Merge remote branch 'upstream/master'

actorid
Jason Hooks 14 years ago
commit b945ce61a5

@ -1,5 +1,9 @@
project(OpenMW) project(OpenMW)
IF (APPLE)
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
ENDIF (APPLE)
# Sound source selection # Sound source selection
option(USE_AUDIERE "use Audiere for sound" OFF) option(USE_AUDIERE "use Audiere for sound" OFF)
option(USE_FFMPEG "use ffmpeg for sound" OFF) option(USE_FFMPEG "use ffmpeg for sound" OFF)
@ -10,15 +14,15 @@ 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(DPKG_PROGRAM)
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 Morrowind data files") set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
else() else()
if (APPLE) if (APPLE)
# set path inside bundle # set path inside bundle
set(MORROWIND_DATA_FILES "Contents/Resources/data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files")
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of Morrowind data files") set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources files")
else() else()
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 Morrowind data files") set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
endif(APPLE) endif(APPLE)
endif(DPKG_PROGRAM) endif(DPKG_PROGRAM)
@ -123,10 +127,48 @@ source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp) file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp)
set(ESM set(ESM
${COMP_DIR}/esm/load_impl.cpp
${COMP_DIR}/esm/skill.cpp
${COMP_DIR}/esm/attr.cpp ${COMP_DIR}/esm/attr.cpp
${COMP_DIR}/esm/class.cpp ${COMP_DIR}/esm/esm_reader.cpp
${COMP_DIR}/esm/loadland.cpp
${COMP_DIR}/esm/loadacti.cpp
${COMP_DIR}/esm/loadalch.cpp
${COMP_DIR}/esm/loadappa.cpp
${COMP_DIR}/esm/loadarmo.cpp
${COMP_DIR}/esm/loadbody.cpp
${COMP_DIR}/esm/loadbook.cpp
${COMP_DIR}/esm/loadbsgn.cpp
${COMP_DIR}/esm/loadcell.cpp
${COMP_DIR}/esm/loadclas.cpp
${COMP_DIR}/esm/loadclot.cpp
${COMP_DIR}/esm/loadcont.cpp
${COMP_DIR}/esm/loadcrea.cpp
${COMP_DIR}/esm/loaddial.cpp
${COMP_DIR}/esm/loaddoor.cpp
${COMP_DIR}/esm/loadench.cpp
${COMP_DIR}/esm/loadfact.cpp
${COMP_DIR}/esm/loadglob.cpp
${COMP_DIR}/esm/loadgmst.cpp
${COMP_DIR}/esm/loadinfo.cpp
${COMP_DIR}/esm/loadingr.cpp
${COMP_DIR}/esm/loadlevlist.cpp
${COMP_DIR}/esm/loadligh.cpp
${COMP_DIR}/esm/loadlocks.cpp
${COMP_DIR}/esm/loadltex.cpp
${COMP_DIR}/esm/loadmgef.cpp
${COMP_DIR}/esm/loadmisc.cpp
${COMP_DIR}/esm/loadnpc.cpp
${COMP_DIR}/esm/loadpgrd.cpp
${COMP_DIR}/esm/loadrace.cpp
${COMP_DIR}/esm/loadregn.cpp
${COMP_DIR}/esm/loadscpt.cpp
${COMP_DIR}/esm/loadskil.cpp
${COMP_DIR}/esm/loadsndg.cpp
${COMP_DIR}/esm/loadsoun.cpp
${COMP_DIR}/esm/loadspel.cpp
${COMP_DIR}/esm/loadsscr.cpp
${COMP_DIR}/esm/loadstat.cpp
${COMP_DIR}/esm/loadweap.cpp
) )
source_group(components\\esm FILES ${ESM_HEADER} ${ESM}) source_group(components\\esm FILES ${ESM_HEADER} ${ESM})
@ -139,6 +181,18 @@ set(MISC_HEADER
${COMP_DIR}/misc/stringops.hpp) ${COMP_DIR}/misc/stringops.hpp)
source_group(components\\misc FILES ${MISC} ${MISC_HEADER}) source_group(components\\misc FILES ${MISC} ${MISC_HEADER})
set(FILES
${COMP_DIR}/files/path.cpp
${COMP_DIR}/files/multidircollection.cpp
${COMP_DIR}/files/collections.cpp
)
set(FILES_HEADER
${COMP_DIR}/files/path.hpp
${COMP_DIR}/files/multidircollection.hpp
${COMP_DIR}/files/collections.hpp
)
source_group(components\\files FILES ${FILES} ${FILES_HEADER})
file(GLOB COMPILER ${COMP_DIR}/compiler/*.cpp) file(GLOB COMPILER ${COMP_DIR}/compiler/*.cpp)
file(GLOB COMPILER_HEADER ${COMP_DIR}/compiler/*.hpp) file(GLOB COMPILER_HEADER ${COMP_DIR}/compiler/*.hpp)
source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER}) source_group(components\\compiler FILES ${COMPILER} ${COMPILER_HEADER})
@ -148,10 +202,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp)
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8} set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8}
${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET}) ${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER} ${NIFBULLET} ${FILES})
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER} ${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER}
${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER}) ${INTERPRETER_HEADER} ${FILE_FINDER_HEADER} ${NIFBULLET_HEADER} ${FILES_HEADER})
# source directory: libs # source directory: libs
@ -286,25 +340,55 @@ add_definitions(-DCAELUM_STATIC)
# Specify build paths # Specify build paths
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}") if (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${APP_BUNDLE_DIR}/Contents/MacOS")
else (APPLE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}")
endif (APPLE)
# Other files # Other files
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
"${OpenMW_BINARY_DIR}/openmw.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
if (WIN32) if (WIN32)
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.win32 configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.win32
"${OpenMW_BINARY_DIR}/plugins.cfg" COPYONLY) "${OpenMW_BINARY_DIR}/plugins.cfg" COPYONLY)
endif (WIN32) endif (WIN32)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.linux
"${OpenMW_BINARY_DIR}/plugins.cfg") "${OpenMW_BINARY_DIR}/plugins.cfg")
endif() endif()
if (APPLE) if (APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
"${OpenMW_BINARY_DIR}/plugins.cfg") "${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns
"${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY)
# prepare plugins
configure_file(${OGRE_PLUGIN_DIR}/RenderSystem_GL.dylib
"${APP_BUNDLE_DIR}/Contents/Plugins/RenderSystem_GL.dylib" COPYONLY)
configure_file(${OGRE_PLUGIN_DIR}/Plugin_OctreeSceneManager.dylib
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_OctreeSceneManager.dylib" COPYONLY)
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg")
endif (APPLE) endif (APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${OpenMW_BINARY_DIR}/openmw.cfg")
# Compiler settings # Compiler settings
if (CMAKE_COMPILER_IS_GNUCC) if (CMAKE_COMPILER_IS_GNUCC)
@ -315,10 +399,15 @@ endif (CMAKE_COMPILER_IS_GNUCC)
# Apple bundling # Apple bundling
if (APPLE) if (APPLE)
set(MISC_FILES set(MISC_FILES
${OpenMW_BINARY_DIR}/openmw.cfg ${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg
${OpenMW_BINARY_DIR}/plugins.cfg) ${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg)
set(OGRE_PLUGINS
${APP_BUNDLE_DIR}/Contents/Plugins/*)
install(FILES ${MISC_FILES} DESTINATION ../MacOS) install(FILES ${MISC_FILES} DESTINATION ../MacOS)
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ../Resources) install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Plugins" DESTINATION ..)
install(DIRECTORY "${APP_BUNDLE_DIR}/Contents/Resources/resources" DESTINATION ../Resources)
set(CPACK_GENERATOR "Bundle") set(CPACK_GENERATOR "Bundle")
set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/files/mac/Info.plist") set(CPACK_BUNDLE_PLIST "${CMAKE_SOURCE_DIR}/files/mac/Info.plist")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw.icns") set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw.icns")
@ -352,7 +441,7 @@ if(DPKG_PROGRAM)
endif() endif()
#Install global configuration files #Install global configuration files
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg" DESTINATION "../etc/openmw/" 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")
INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "../etc/openmw/" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
#Install resources #Install resources

@ -49,8 +49,8 @@ Getting OpenMW Working
--link-shared,static --prefix=$OMW_LIB_PREFIX install --link-shared,static --prefix=$OMW_LIB_PREFIX install
5. Download [Ogre][] SDK (tested with 1.7.2) and move `lib/Release/Ogre.framework` into 5. Download [Ogre][] SDK (tested with 1.7.2), unpack it and move
`Library/Frameworks`. `lib/Release/Ogre.framework` into `Library/Frameworks`.
6. Download [OIS][] and use the XCode project provided in 6. Download [OIS][] and use the XCode project provided in
`ois/Mac/XCode-2.2`. Be sure to set your build architecture to `ois/Mac/XCode-2.2`. Be sure to set your build architecture to
@ -92,6 +92,7 @@ Getting OpenMW Working
$ cd /path/to/open/build/dir $ cd /path/to/open/build/dir
$ cmake \ $ cmake \
-D CMAKE_OSX_ARCHITECTURES=i386 \ -D CMAKE_OSX_ARCHITECTURES=i386 \
-D OGRESDK=/path/to/ogre/sdk \
-D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \ -D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \
-D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \ -D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \
-D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ -D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
@ -108,32 +109,17 @@ Getting OpenMW Working
You can use -G"Xcode" if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" You can use -G"Xcode" if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles"
if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug if you prefer Eclipse. You also can specify -D CMAKE_BUILD_TYPE=Debug for debug
build. build.
11. In build directory create directory for game resources: 11. Copy your Morrowind `Data Files` directory into the OpenMW build dir
$ cd /path/to/openmw/build/dir
$ mkdir Contents
$ mkdir Contents/Resources
$ mkdir Contents/Plugins
Copy Ogre plugins from Ogre SDK to Plugins subdir:
$ cp /path/to/ogre/sdk/lib/*.dylib Contents/Plugins
Create symlink to resources subdirectory:
$ ln -s resources Contents/Resources/resources
Create symlinks for *.cfg files:
$ ln -s plugins.cfg Contents/MacOS/plugins.cfg
$ ln -s openmw.cfg Contents/MacOS/openmw.cfg
12. Move your Morrowind `Data Files` directory into the `Contents/Resources`
with the name `data` or create symlink: with the name `data` or create symlink:
$ ln -s /path/to/morrowind/data/files Contents/Resources/data $ ln -s /path/to/morrowind/data/files /path/to/openmw/build/dir/data
13. From your build directory run: 12. From your build directory run:
$ ./openmw $ OpenMW.app/Contents/MacOS/openmw
or:
$ open OpenMW.app
Enjoy! Enjoy!
14. Optionally you can create .app bundle:
$ make package
But for now you shold manually copy Contents directory from build directory to bundle
(because there is no plugins and resources in generated .app).
[boost]: http://www.boost.org [boost]: http://www.boost.org

@ -5,7 +5,7 @@ project(OpenMW)
set(GAME set(GAME
main.cpp main.cpp
engine.cpp engine.cpp
path.cpp) )
set(GAME_HEADER set(GAME_HEADER
engine.hpp) engine.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
@ -70,9 +70,17 @@ source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
set(GAMEDIALOGUE_HEADER set(GAMEDIALOGUE_HEADER
mwdialogue/dialoguemanager.hpp mwdialogue/dialoguemanager.hpp
mwdialogue/journal.hpp
mwdialogue/journalentry.hpp
mwdialogue/quest.hpp
mwdialogue/topic.hpp
) )
set(GAMEDIALOGUE set(GAMEDIALOGUE
mwdialogue/dialoguemanager.cpp mwdialogue/dialoguemanager.cpp
mwdialogue/journal.cpp
mwdialogue/journalentry.cpp
mwdialogue/quest.cpp
mwdialogue/topic.cpp
) )
source_group(apps\\openmw\\mwdialogue FILES ${GAMEDIALOGUE_HEADER} ${GAMEDIALOGUE}) source_group(apps\\openmw\\mwdialogue FILES ${GAMEDIALOGUE_HEADER} ${GAMEDIALOGUE})
@ -91,6 +99,7 @@ set(GAMESCRIPT
mwscript/controlextensions.cpp mwscript/controlextensions.cpp
mwscript/extensions.cpp mwscript/extensions.cpp
mwscript/globalscripts.cpp mwscript/globalscripts.cpp
mwscript/dialogueextensions.cpp
) )
set(GAMESCRIPT_HEADER set(GAMESCRIPT_HEADER
mwscript/locals.hpp mwscript/locals.hpp
@ -109,6 +118,7 @@ set(GAMESCRIPT_HEADER
mwscript/extensions.hpp mwscript/extensions.hpp
mwscript/globalscripts.hpp mwscript/globalscripts.hpp
mwscript/ref.hpp mwscript/ref.hpp
mwscript/dialogueextensions.hpp
) )
source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER}) source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER})

@ -15,6 +15,8 @@
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
#include <components/esm/loadregn.hpp> #include <components/esm/loadregn.hpp>
#include <components/esm/esm_reader.hpp> #include <components/esm/esm_reader.hpp>
#include <components/files/path.hpp>
#include <openengine/gui/manager.hpp> #include <openengine/gui/manager.hpp>
#include "mwgui/window_manager.hpp" #include "mwgui/window_manager.hpp"
@ -37,6 +39,7 @@
#include "mwclass/classes.hpp" #include "mwclass/classes.hpp"
#include "mwdialogue/dialoguemanager.hpp" #include "mwdialogue/dialoguemanager.hpp"
#include "mwdialogue/journal.hpp"
#include "mwmechanics/mechanicsmanager.hpp" #include "mwmechanics/mechanicsmanager.hpp"
@ -48,7 +51,6 @@
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include "mwgui/class.hpp" #include "mwgui/class.hpp"
#include "path.hpp"
#include "components/nifbullet/bullet_nif_loader.hpp" #include "components/nifbullet/bullet_nif_loader.hpp"
@ -75,7 +77,7 @@ void OMW::Engine::executeLocalScripts()
} }
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{ {
if(mShowFPS) if(mShowFPS)
{ {
@ -264,6 +266,7 @@ OMW::Engine::Engine()
, mScriptManager (0) , mScriptManager (0)
, mScriptContext (0) , mScriptContext (0)
, mGuiManager (0) , mGuiManager (0)
, mFSStrict (false)
{ {
MWClass::registerClasses(); MWClass::registerClasses();
} }
@ -276,6 +279,7 @@ OMW::Engine::~Engine()
delete mEnvironment.mGlobalScripts; delete mEnvironment.mGlobalScripts;
delete mEnvironment.mMechanicsManager; delete mEnvironment.mMechanicsManager;
delete mEnvironment.mDialogueManager; delete mEnvironment.mDialogueManager;
delete mEnvironment.mJournal;
delete mScriptManager; delete mScriptManager;
delete mScriptContext; delete mScriptContext;
delete mPhysicEngine; delete mPhysicEngine;
@ -285,15 +289,12 @@ OMW::Engine::~Engine()
void OMW::Engine::loadBSA() void OMW::Engine::loadBSA()
{ {
boost::filesystem::directory_iterator end; const Files::MultiDirCollection& bsa = mFileCollections.getCollection (".bsa");
for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter) for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
{ {
if (boost::filesystem::extension (iter->path())==".bsa") std::cout << "Adding " << iter->second.string() << std::endl;
{ addBSA (iter->second.string());
std::cout << "Adding " << iter->path().string() << std::endl;
addBSA(iter->path().string());
}
} }
} }
@ -306,11 +307,20 @@ void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
} }
void OMW::Engine::enableFSStrict()
{
mFSStrict = true;
}
// Set data dir // Set data dir
void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir) void OMW::Engine::setDataDirs (const std::vector<boost::filesystem::path>& dataDirs)
{ {
mDataDir = boost::filesystem::system_complete (dataDir); /// \todo remove mDataDir, once resources system can handle multiple directories
assert (!dataDirs.empty());
mDataDir = dataDirs[0];
mFileCollections = Files::Collections (dataDirs, !mFSStrict);
} }
// Set resource dir // Set resource dir
@ -363,19 +373,14 @@ void OMW::Engine::setNewGame()
void OMW::Engine::go() void OMW::Engine::go()
{ {
assert (!mEnvironment.mWorld); assert (!mEnvironment.mWorld);
assert (!mDataDir.empty());
assert (!mCellName.empty()); assert (!mCellName.empty());
assert (!mMaster.empty()); assert (!mMaster.empty());
test.name = ""; test.name = "";
total = 0; total = 0;
std::string cfgDir = Files::getPath (Files::Path_ConfigGlobal, "openmw", "");
std::string cfgUserDir = Files::getPath (Files::Path_ConfigUser, "openmw", "");
std::cout << "Data directory: " << mDataDir << "\n";
std::string cfgDir = OMW::Path::getPath(OMW::Path::GLOBAL_CFG_PATH, "openmw", "");
std::string cfgUserDir = OMW::Path::getPath(OMW::Path::USER_CFG_PATH, "openmw", "");
std::string plugCfg = "plugins.cfg"; std::string plugCfg = "plugins.cfg";
std::string ogreCfg = "ogre.cfg"; std::string ogreCfg = "ogre.cfg";
ogreCfg.insert(0, cfgUserDir); ogreCfg.insert(0, cfgUserDir);
@ -405,8 +410,8 @@ void OMW::Engine::go()
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader); mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
// Create the world // Create the world
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mDataDir, mMaster, mResDir, mNewGame, mEnvironment); mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
mResDir, mNewGame, mEnvironment);
// Set up the GUI system // Set up the GUI system
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir); mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
@ -444,6 +449,7 @@ void OMW::Engine::go()
mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (mEnvironment); mEnvironment.mMechanicsManager = new MWMechanics::MechanicsManager (mEnvironment);
// Create dialog system // Create dialog system
mEnvironment.mJournal = new MWDialogue::Journal (mEnvironment);
mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment); mEnvironment.mDialogueManager = new MWDialogue::DialogueManager (mEnvironment);
// load cell // load cell

@ -10,6 +10,7 @@
#include <openengine/ogre/renderer.hpp> #include <openengine/ogre/renderer.hpp>
#include <openengine/bullet/physic.hpp> #include <openengine/bullet/physic.hpp>
#include <components/compiler/extensions.hpp> #include <components/compiler/extensions.hpp>
#include <components/files/collections.hpp>
#include "mwworld/environment.hpp" #include "mwworld/environment.hpp"
#include "mwworld/ptr.hpp" #include "mwworld/ptr.hpp"
@ -55,8 +56,6 @@ namespace OMW
class Engine : private Ogre::FrameListener class Engine : private Ogre::FrameListener
{ {
//int nFiles;
boost::filesystem::path mDataDir; boost::filesystem::path mDataDir;
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer mOgre; OEngine::Render::OgreRenderer mOgre;
@ -84,6 +83,9 @@ namespace OMW
MWWorld::Ptr mIgnoreLocalPtr; MWWorld::Ptr mIgnoreLocalPtr;
Files::Collections mFileCollections;
bool mFSStrict;
// not implemented // not implemented
Engine (const Engine&); Engine (const Engine&);
Engine& operator= (const Engine&); Engine& operator= (const Engine&);
@ -98,7 +100,7 @@ namespace OMW
void executeLocalScripts(); void executeLocalScripts();
virtual bool frameStarted(const Ogre::FrameEvent& evt); virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt);
/// Process pending commands /// Process pending commands
@ -108,8 +110,14 @@ namespace OMW
~Engine(); ~Engine();
/// Set data dir /// Enable strict filesystem mode (do not fold case)
void setDataDir (const boost::filesystem::path& dataDir); ///
/// \attention The strict mode must be specified before any path-related settings
/// are given to the engine.
void enableFSStrict();
/// Set data dirs
void setDataDirs (const std::vector<boost::filesystem::path>& dataDirs);
/// Set resource dir /// Set resource dir
void setResourceDir (const boost::filesystem::path& parResDir); void setResourceDir (const boost::filesystem::path& parResDir);

@ -6,8 +6,9 @@
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <components/misc/fileops.hpp> #include <components/misc/fileops.hpp>
#include <components/files/path.hpp>
#include "engine.hpp" #include "engine.hpp"
#include "path.hpp"
#if defined(_WIN32) && !defined(_CONSOLE) #if defined(_WIN32) && !defined(_CONSOLE)
#include <boost/iostreams/concepts.hpp> #include <boost/iostreams/concepts.hpp>
@ -41,29 +42,56 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
desc.add_options() desc.add_options()
("help", "print help message") ("help", "print help message")
("data", bpo::value<std::string>()->default_value ("data"), ("data", bpo::value<std::vector<std::string> >()
"set data directory") ->default_value (std::vector<std::string>(), "data")
->multitoken(),
"set data directories (later directories have higher priority)")
("data-local", bpo::value<std::string>()->default_value (""),
"set local data directory (highest priority)")
("resources", bpo::value<std::string>()->default_value ("resources"), ("resources", bpo::value<std::string>()->default_value ("resources"),
"set resources directory") "set resources directory")
("start", bpo::value<std::string>()->default_value ("Beshara"), ("start", bpo::value<std::string>()->default_value ("Beshara"),
"set initial cell") "set initial cell")
("master", bpo::value<std::string>()->default_value ("Morrowind"), ("master", bpo::value<std::vector<std::string> >()
"master file") ->default_value (std::vector<std::string>(), "")
( "showfps", "show fps counter") ->multitoken(),
( "debug", "debug mode" ) "master file(s)")
( "nosound", "disable all sound" ) ("plugin", bpo::value<std::vector<std::string> >()
( "script-verbose", "verbose script output" ) ->default_value (std::vector<std::string>(), "")
( "new-game", "activate char gen/new game mechanics" ) ->multitoken(),
( "script-all", "compile all scripts (excluding dialogue scripts) at startup") "plugin file(s)")
( "fps", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false), "show fps counter")
( "debug", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false), "debug mode" )
( "nosound", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false), "disable all sound" )
( "script-verbose", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false), "verbose script output" )
( "new-game", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false),
"activate char gen/new game mechanics" )
( "script-all", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false),
"compile all scripts (excluding dialogue scripts) at startup")
( "fs-strict", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false),
"strict file system handling (no case folding)")
; ;
bpo::variables_map variables; bpo::variables_map variables;
std::string cfgFile = OMW::Path::getPath(OMW::Path::GLOBAL_CFG_PATH, "openmw", "openmw.cfg"); //If there is an openmw.cfg in the current path use that as global config
//Otherwise try getPath
std::string cfgFile = "openmw.cfg";
if(!isFile(cfgFile.c_str()))
{
cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg");
}
std::cout << "Using global config file: " << cfgFile << std::endl; std::cout << "Using global config file: " << cfgFile << std::endl;
std::ifstream globalConfigFile(cfgFile.c_str()); std::ifstream globalConfigFile(cfgFile.c_str());
cfgFile = OMW::Path::getPath(OMW::Path::USER_CFG_PATH, "openmw", "openmw.cfg"); cfgFile = Files::getPath (Files::Path_ConfigUser, "openmw", "openmw.cfg");
std::cout << "Using user config file: " << cfgFile << std::endl; std::cout << "Using user config file: " << cfgFile << std::endl;
std::ifstream userConfigFile(cfgFile.c_str()); std::ifstream userConfigFile(cfgFile.c_str());
@ -83,27 +111,63 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
return false; return false;
} }
engine.setDataDir (variables["data"].as<std::string>()); // directory settings
if (variables["fs-strict"].as<bool>()==true)
engine.enableFSStrict();
std::vector<std::string> dataDirs = variables["data"].as<std::vector<std::string> >();
std::vector<boost::filesystem::path> dataDirs2 (dataDirs.begin(), dataDirs.end());
std::string local = variables["data-local"].as<std::string>();
if (!local.empty())
dataDirs.push_back (local);
engine.setDataDirs (dataDirs2);
engine.setResourceDir (variables["resources"].as<std::string>()); engine.setResourceDir (variables["resources"].as<std::string>());
// master and plugin
std::vector<std::string> master = variables["master"].as<std::vector<std::string> >();
if (master.empty())
{
std::cout << "No master file given. Assuming Morrowind.esm" << std::endl;
master.push_back ("Morrowind");
}
if (master.size()>1)
{
std::cout
<< "Ignoring all but the first master file (multiple master files not yet supported)."
<< std::endl;
}
engine.addMaster (master[0]);
std::vector<std::string> plugin = variables["plugin"].as<std::vector<std::string> >();
if (!plugin.empty())
std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl;
// startup-settings
engine.setCell (variables["start"].as<std::string>()); engine.setCell (variables["start"].as<std::string>());
engine.addMaster (variables["master"].as<std::string>());
if (variables.count ("showfps")) if (variables["new-game"].as<bool>()==true)
engine.setNewGame();
// other settings
if (variables["fps"].as<bool>()==true)
engine.showFPS(); engine.showFPS();
if (variables.count ("debug")) if (variables["debug"].as<bool>()==true)
engine.enableDebugMode(); engine.enableDebugMode();
if (variables.count ("nosound")) if (variables["nosound"].as<bool>()==true)
engine.disableSound(); engine.disableSound();
if (variables.count ("script-verbose")) if (variables["script-verbose"].as<bool>()==true)
engine.enableVerboseScripts(); engine.enableVerboseScripts();
if (variables.count ("new-game")) if (variables["script-all"].as<bool>()==true)
engine.setNewGame();
if (variables.count ("script-all"))
engine.setCompileAll (true); engine.setCompileAll (true);
return true; return true;

@ -0,0 +1,95 @@
#include "journal.hpp"
#include "../mwworld/environment.hpp"
namespace MWDialogue
{
Quest& Journal::getQuest (const std::string& id)
{
TQuestContainer::iterator iter = mQuests.find (id);
if (iter==mQuests.end())
{
std::pair<TQuestContainer::iterator, bool> result =
mQuests.insert (std::make_pair (id, Quest (id)));
iter = result.first;
}
return iter->second;
}
Journal::Journal (MWWorld::Environment& environment)
: mEnvironment (environment)
{}
void Journal::addEntry (const std::string& id, int index)
{
StampedJournalEntry entry =
StampedJournalEntry::makeFromQuest (id, index, *mEnvironment.mWorld);
mJournal.push_back (entry);
Quest& quest = getQuest (id);
quest.addEntry (entry, *mEnvironment.mWorld); // we are doing slicing on purpose here
}
void Journal::setJournalIndex (const std::string& id, int index)
{
Quest& quest = getQuest (id);
quest.setIndex (index, *mEnvironment.mWorld);
}
void Journal::addTopic (const std::string& topicId, const std::string& infoId)
{
TTopicContainer::iterator iter = mTopics.find (topicId);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
iter = result.first;
}
iter->second.addEntry (JournalEntry (topicId, infoId), *mEnvironment.mWorld);
}
int Journal::getJournalIndex (const std::string& id) const
{
return 0;
}
Journal::TEntryIter Journal::begin() const
{
return mJournal.begin();
}
Journal::TEntryIter Journal::end() const
{
return mJournal.end();
}
Journal::TQuestIter Journal::questBegin() const
{
return mQuests.begin();
}
Journal::TQuestIter Journal::questEnd() const
{
return mQuests.end();
}
Journal::TTopicIter Journal::topicBegin() const
{
return mTopics.begin();
}
Journal::TTopicIter Journal::topicEnd() const
{
return mTopics.end();
}
}

@ -0,0 +1,78 @@
#ifndef GAME_MMDIALOG_JOURNAL_H
#define GAME_MWDIALOG_JOURNAL_H
#include <string>
#include <deque>
#include <map>
#include "journalentry.hpp"
#include "quest.hpp"
namespace MWWorld
{
struct Environment;
}
namespace MWDialogue
{
/// \brief The player's journal
class Journal
{
public:
typedef std::deque<StampedJournalEntry> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter;
typedef std::map<std::string, Quest> TQuestContainer; // topc, quest
typedef TQuestContainer::const_iterator TQuestIter;
typedef std::map<std::string, Topic> TTopicContainer; // topic-id, topic-content
typedef TTopicContainer::const_iterator TTopicIter;
private:
MWWorld::Environment& mEnvironment;
TEntryContainer mJournal;
TQuestContainer mQuests;
TTopicContainer mTopics;
Quest& getQuest (const std::string& id);
public:
Journal (MWWorld::Environment& environment);
void addEntry (const std::string& id, int index);
///< Add a journal entry.
void setJournalIndex (const std::string& id, int index);
///< Set the journal index without adding an entry.
int getJournalIndex (const std::string& id) const;
///< Get the journal index.
void addTopic (const std::string& topicId, const std::string& infoId);
TEntryIter begin() const;
///< Iterator pointing to the begin of the main journal.
///
/// \note Iterators to main journal entries will never become invalid.
TEntryIter end() const;
///< Iterator pointing past the end of the main journal.
TQuestIter questBegin() const;
///< Iterator pointing to the first quest (sorted by topic ID)
TQuestIter questEnd() const;
///< Iterator pointing past the last quest.
TTopicIter topicBegin() const;
///< Iterator pointing to the first topic (sorted by topic ID)
///
/// \note The topic ID is identical with the user-visible topic string.
TTopicIter topicEnd() const;
///< Iterator pointing past the last topic.
};
}
#endif

@ -0,0 +1,69 @@
#include "journalentry.hpp"
#include <stdexcept>
#include <components/esm_store/store.hpp>
#include "../mwworld/world.hpp"
namespace MWDialogue
{
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: mTopic (topic), mInfoId (infoId)
{}
std::string JournalEntry::getText (const ESMS::ESMStore& store) const
{
const ESM::Dialogue *dialogue = store.dialogs.find (mTopic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->id==mInfoId)
return iter->response;
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic);
}
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index,
const MWWorld::World& world)
{
return JournalEntry (topic, idFromIndex (topic, index, world));
}
std::string JournalEntry::idFromIndex (const std::string& topic, int index,
const MWWorld::World& world)
{
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (topic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->data.disposition==index) /// \todo cleanup info structure
{
iter->id;
}
throw std::runtime_error ("unknown journal index for topic " + topic);
}
StampedJournalEntry::StampedJournalEntry()
: mDay (0), mMonth (0), mDayOfMonth (0)
{}
StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth)
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{}
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index,
const MWWorld::World& world)
{
int day = world.getGlobalVariable ("dayspassed").mLong;
int month = world.getGlobalVariable ("day").mLong;
int dayOfMonth = world.getGlobalVariable ("month").mLong;
return StampedJournalEntry (topic, idFromIndex (topic, index, world), day, month, dayOfMonth);
}
}

@ -0,0 +1,54 @@
#ifndef GAME_MMDIALOGUE_JOURNALENTRY_H
#define GAME_MMDIALOGUE_JOURNALENTRY_H
#include <string>
namespace ESMS
{
struct ESMStore;
}
namespace MWWorld
{
class World;
}
namespace MWDialogue
{
/// \brief A quest or dialogue entry
struct JournalEntry
{
std::string mTopic;
std::string mInfoId;
JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId);
std::string getText (const ESMS::ESMStore& store) const;
static JournalEntry makeFromQuest (const std::string& topic, int index,
const MWWorld::World& world);
static std::string idFromIndex (const std::string& topic, int index,
const MWWorld::World& world);
};
/// \biref A quest entry with a timestamp.
struct StampedJournalEntry : public JournalEntry
{
int mDay;
int mMonth;
int mDayOfMonth;
StampedJournalEntry();
StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth);
static StampedJournalEntry makeFromQuest (const std::string& topic, int index,
const MWWorld::World& world);
};
}
#endif

@ -0,0 +1,86 @@
#include "quest.hpp"
#include <components/esm_store/store.hpp>
#include "../mwworld/world.hpp"
namespace MWDialogue
{
Quest::Quest()
: Topic(), mIndex (0), mFinished (false)
{}
Quest::Quest (const std::string& topic)
: Topic (topic), mIndex (0), mFinished (false)
{}
const std::string Quest::getName (const MWWorld::World& world) const
{
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->questStatus==ESM::DialInfo::QS_Name)
return iter->response;
return "";
}
int Quest::getIndex() const
{
return mIndex;
}
void Quest::setIndex (int index, const MWWorld::World& world)
{
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (mTopic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->data.disposition==index && iter->questStatus!=ESM::DialInfo::QS_Name)
{
mIndex = index;
if (iter->questStatus==ESM::DialInfo::QS_Finished)
mFinished = true;
else if (iter->questStatus==ESM::DialInfo::QS_Restart)
mFinished = false;
return;
}
throw std::runtime_error ("unknown journal index for topic " + mTopic);
}
bool Quest::isFinished() const
{
return mFinished;
}
void Quest::addEntry (const JournalEntry& entry, const MWWorld::World& world)
{
int index = -1;
const ESM::Dialogue *dialogue = world.getStore().dialogs.find (entry.mTopic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->id==entry.mInfoId)
{
index = iter->data.disposition; /// \todo cleanup info structure
break;
}
if (index==-1)
throw std::runtime_error ("unknown journal entry for topic " + mTopic);
setIndex (index, world);
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
if (*iter==entry.mInfoId)
return;
mEntries.push_back (entry.mInfoId);
}
}

@ -0,0 +1,37 @@
#ifndef GAME_MMDIALOG_QUEST_H
#define GAME_MWDIALOG_QUEST_H
#include "topic.hpp"
namespace MWDialogue
{
/// \brief A quest in progress or a compelted quest
class Quest : public Topic
{
int mIndex;
bool mFinished;
public:
Quest();
Quest (const std::string& topic);
const std::string getName (const MWWorld::World& world) const;
///< May be an empty string
int getIndex() const;
void setIndex (int index, const MWWorld::World& world);
///< Calling this function with a non-existant index while throw an exception.
bool isFinished() const;
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
///< Add entry and adjust index accordingly.
///
/// \note Redundant entries are ignored, but the index is still adjusted.
};
}
#endif

@ -0,0 +1,46 @@
#include "topic.hpp"
#include <components/esm_store/store.hpp>
#include "../mwworld/world.hpp"
namespace MWDialogue
{
Topic::Topic()
{}
Topic::Topic (const std::string& topic)
: mTopic (topic)
{}
Topic::~Topic()
{}
void Topic::addEntry (const JournalEntry& entry, const MWWorld::World& world)
{
if (entry.mTopic!=mTopic)
throw std::runtime_error ("topic does not match: " + mTopic);
for (TEntryIter iter = begin(); iter!=end(); ++iter)
if (*iter==entry.mInfoId)
return;
mEntries.push_back (entry.mInfoId);
}
Topic::TEntryIter Topic::begin()
{
return mEntries.begin();
}
Topic::TEntryIter Topic::end()
{
return mEntries.end();
}
JournalEntry Topic::getEntry (const std::string& infoId)
{
return JournalEntry (mTopic, infoId);
}
}

@ -0,0 +1,52 @@
#ifndef GAME_MMDIALOG_TOPIC_H
#define GAME_MWDIALOG_TOPIC_H
#include <string>
#include <vector>
#include "journalentry.hpp"
namespace MWWorld
{
class World;
}
namespace MWDialogue
{
/// \brief Collection of seen responses for a topic
class Topic
{
public:
typedef std::vector<std::string> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter;
protected:
std::string mTopic;
TEntryContainer mEntries; // info-IDs
public:
Topic();
Topic (const std::string& topic);
virtual ~Topic();
virtual void addEntry (const JournalEntry& entry, const MWWorld::World& world);
///< Add entry
///
/// \note Redundant entries are ignored.
TEntryIter begin();
///< Iterator pointing to the begin of the journal for this topic.
TEntryIter end();
///< Iterator pointing past the end of the journal for this topic.
JournalEntry getEntry (const std::string& infoId);
};
}
#endif

@ -119,8 +119,8 @@ void BirthDialog::updateBirths()
{ {
birthList->removeAllItems(); birthList->removeAllItems();
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator it = store.birthSigns.list.begin(); ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator it = store.birthSigns.list.begin();
ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator end = store.birthSigns.list.end(); ESMS::RecListT<ESM::BirthSign>::MapType::const_iterator end = store.birthSigns.list.end();
int index = 0; int index = 0;
@ -149,7 +149,7 @@ void BirthDialog::updateSpells()
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord(0, 0, spellArea->getWidth(), 18); MyGUI::IntCoord coord(0, 0, spellArea->getWidth(), 18);
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
const ESM::BirthSign *birth = store.birthSigns.find(currentBirthId); const ESM::BirthSign *birth = store.birthSigns.find(currentBirthId);
std::string texturePath = std::string("textures\\") + birth->texture; std::string texturePath = std::string("textures\\") + birth->texture;

@ -50,7 +50,7 @@ void GenerateClassResultDialog::setClassId(const std::string &classId)
{ {
currentClassId = classId; currentClassId = classId;
classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds"); classImage->setImageTexture(std::string("textures\\levelup\\") + currentClassId + ".dds");
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
className->setCaption(store.classes.find(currentClassId)->name); className->setCaption(store.classes.find(currentClassId)->name);
} }
@ -196,8 +196,8 @@ void PickClassDialog::updateClasses()
{ {
classList->removeAllItems(); classList->removeAllItems();
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
ESMS::RecListT<ESM::Class>::MapType::const_iterator it = store.classes.list.begin(); ESMS::RecListT<ESM::Class>::MapType::const_iterator it = store.classes.list.begin();
ESMS::RecListT<ESM::Class>::MapType::const_iterator end = store.classes.list.end(); ESMS::RecListT<ESM::Class>::MapType::const_iterator end = store.classes.list.end();
int index = 0; int index = 0;
@ -220,7 +220,7 @@ void PickClassDialog::updateStats()
{ {
if (currentClassId.empty()) if (currentClassId.empty())
return; return;
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
const ESM::Class *klass = store.classes.search(currentClassId); const ESM::Class *klass = store.classes.search(currentClassId);
if (!klass) if (!klass)
return; return;
@ -767,7 +767,7 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager)
{ {
char theIndex = '0'+i; char theIndex = '0'+i;
getWidget(combatSkill[i], std::string("CombatSkill").append(1, theIndex)); getWidget(combatSkill[i], std::string("CombatSkill").append(1, theIndex));
getWidget(magicSkill[i], std::string("MagicSkill").append(1, theIndex)); getWidget(magicSkill[i], std::string("MagicSkill").append(1, theIndex));
getWidget(stealthSkill[i], std::string("StealthSkill").append(1, theIndex)); getWidget(stealthSkill[i], std::string("StealthSkill").append(1, theIndex));
} }
@ -782,8 +782,8 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager)
{combatSkill[6], ESM::Skill::Axe}, {combatSkill[6], ESM::Skill::Axe},
{combatSkill[7], ESM::Skill::Spear}, {combatSkill[7], ESM::Skill::Spear},
{combatSkill[8], ESM::Skill::Athletics} {combatSkill[8], ESM::Skill::Athletics}
}, },
{ {
{magicSkill[0], ESM::Skill::Enchant}, {magicSkill[0], ESM::Skill::Enchant},
{magicSkill[1], ESM::Skill::Destruction}, {magicSkill[1], ESM::Skill::Destruction},
{magicSkill[2], ESM::Skill::Alteration}, {magicSkill[2], ESM::Skill::Alteration},
@ -793,8 +793,8 @@ SelectSkillDialog::SelectSkillDialog(WindowManager& parWindowManager)
{magicSkill[6], ESM::Skill::Restoration}, {magicSkill[6], ESM::Skill::Restoration},
{magicSkill[7], ESM::Skill::Alchemy}, {magicSkill[7], ESM::Skill::Alchemy},
{magicSkill[8], ESM::Skill::Unarmored} {magicSkill[8], ESM::Skill::Unarmored}
}, },
{ {
{stealthSkill[0], ESM::Skill::Security}, {stealthSkill[0], ESM::Skill::Security},
{stealthSkill[1], ESM::Skill::Sneak}, {stealthSkill[1], ESM::Skill::Sneak},
{stealthSkill[2], ESM::Skill::Acrobatics}, {stealthSkill[2], ESM::Skill::Acrobatics},

@ -17,6 +17,8 @@ namespace MWGui
ConsoleInterpreterContext (Console& console, MWWorld::Environment& environment, ConsoleInterpreterContext (Console& console, MWWorld::Environment& environment,
MWWorld::Ptr reference); MWWorld::Ptr reference);
virtual void report (const std::string& message);
}; };
ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console, ConsoleInterpreterContext::ConsoleInterpreterContext (Console& console,
@ -26,6 +28,11 @@ namespace MWGui
mConsole (console) mConsole (console)
{} {}
void ConsoleInterpreterContext::report (const std::string& message)
{
mConsole.printOK (message);
}
bool Console::compile (const std::string& cmd, Compiler::Output& output) bool Console::compile (const std::string& cmd, Compiler::Output& output)
{ {
try try
@ -216,11 +223,11 @@ namespace MWGui
try try
{ {
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr()); ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
Interpreter::Interpreter interpreter (interpreterContext); Interpreter::Interpreter interpreter;
MWScript::installOpcodes (interpreter); MWScript::installOpcodes (interpreter);
std::vector<Interpreter::Type_Code> code; std::vector<Interpreter::Type_Code> code;
output.getCode (code); output.getCode (code);
interpreter.run (&code[0], code.size()); interpreter.run (&code[0], code.size(), interpreterContext);
} }
catch (const std::exception& error) catch (const std::exception& error)
{ {

@ -212,8 +212,8 @@ void RaceDialog::updateRaces()
{ {
raceList->removeAllItems(); raceList->removeAllItems();
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
ESMS::RecListT<ESM::Race>::MapType::const_iterator it = store.races.list.begin(); ESMS::RecListT<ESM::Race>::MapType::const_iterator it = store.races.list.begin();
ESMS::RecListT<ESM::Race>::MapType::const_iterator end = store.races.list.end(); ESMS::RecListT<ESM::Race>::MapType::const_iterator end = store.races.list.end();
int index = 0; int index = 0;
@ -246,7 +246,7 @@ void RaceDialog::updateSkills()
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord1(0, 0, skillList->getWidth(), 18); MyGUI::IntCoord coord1(0, 0, skillList->getWidth(), 18);
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
const ESM::Race *race = store.races.find(currentRaceId); const ESM::Race *race = store.races.find(currentRaceId);
int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE? int count = sizeof(race->data.bonus)/sizeof(race->data.bonus[0]); // TODO: Find a portable macro for this ARRAYSIZE?
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
@ -282,7 +282,7 @@ void RaceDialog::updateSpellPowers()
const int lineHeight = 18; const int lineHeight = 18;
MyGUI::IntCoord coord(0, 0, spellPowerList->getWidth(), 18); MyGUI::IntCoord coord(0, 0, spellPowerList->getWidth(), 18);
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
const ESM::Race *race = store.races.find(currentRaceId); const ESM::Race *race = store.races.find(currentRaceId);
std::vector<std::string>::const_iterator it = race->powers.list.begin(); std::vector<std::string>::const_iterator it = race->powers.list.begin();

@ -323,7 +323,7 @@ void StatsWindow::updateSkillArea()
if (!miscSkills.empty()) if (!miscSkills.empty())
addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2); addSkills(miscSkills, "sSkillClassMisc", "Misc Skills", coord1, coord2);
ESMS::ESMStore &store = mWindowManager.getStore(); const ESMS::ESMStore &store = mWindowManager.getStore();
if (!factions.empty()) if (!factions.empty())
{ {

@ -278,7 +278,7 @@ void MWSpell::setSpellId(const std::string &spellId)
void MWSpell::createEffectWidgets(std::vector<MyGUI::WidgetPtr> &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord) void MWSpell::createEffectWidgets(std::vector<MyGUI::WidgetPtr> &effects, MyGUI::WidgetPtr creator, MyGUI::IntCoord &coord)
{ {
ESMS::ESMStore &store = mWindowManager->getStore(); const ESMS::ESMStore &store = mWindowManager->getStore();
const ESM::Spell *spell = store.spells.search(id); const ESM::Spell *spell = store.spells.search(id);
MYGUI_ASSERT(spell, "spell with id '" << id << "' not found"); MYGUI_ASSERT(spell, "spell with id '" << id << "' not found");
@ -298,7 +298,7 @@ void MWSpell::updateWidgets()
{ {
if (spellNameWidget && mWindowManager) if (spellNameWidget && mWindowManager)
{ {
ESMS::ESMStore &store = mWindowManager->getStore(); const ESMS::ESMStore &store = mWindowManager->getStore();
const ESM::Spell *spell = store.spells.search(id); const ESM::Spell *spell = store.spells.search(id);
if (spell) if (spell)
spellNameWidget->setCaption(spell->name); spellNameWidget->setCaption(spell->name);
@ -363,7 +363,7 @@ void MWSpellEffect::updateWidgets()
if (!mWindowManager) if (!mWindowManager)
return; return;
ESMS::ESMStore &store = mWindowManager->getStore(); const ESMS::ESMStore &store = mWindowManager->getStore();
const ESM::MagicEffect *magicEffect = store.magicEffects.search(effect.effectID); const ESM::MagicEffect *magicEffect = store.magicEffects.search(effect.effectID);
if (textWidget) if (textWidget)
{ {

@ -940,7 +940,7 @@ void WindowManager::onReviewActivateDialog(int parDialog)
}; };
} }
ESMS::ESMStore& WindowManager::getStore() const ESMS::ESMStore& WindowManager::getStore() const
{ {
return environment.mWorld->getStore(); return environment.mWorld->getStore();
} }

@ -257,7 +257,7 @@ namespace MWGui
*/ */
const std::string &getGameSettingString(const std::string &id, const std::string &default_); const std::string &getGameSettingString(const std::string &id, const std::string &default_);
ESMS::ESMStore& getStore(); const ESMS::ESMStore& getStore() const;
private: private:

@ -258,7 +258,7 @@ namespace MWInput
} }
//NOTE: Used to check for movement keys //NOTE: Used to check for movement keys
bool frameStarted(const Ogre::FrameEvent &evt) bool frameRenderingQueued (const Ogre::FrameEvent &evt)
{ {
// Tell OIS to handle all input events // Tell OIS to handle all input events
input.capture(); input.capture();

@ -191,7 +191,7 @@ void MWScene::scaleObject (const std::string& handle, float scale)
} }
void MWScene::toggleCollisionMode() bool MWScene::toggleCollisionMode()
{ {
for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++) for(std::map<std::string,OEngine::Physic::PhysicActor*>::iterator it = eng->PhysicActorMap.begin(); it != eng->PhysicActorMap.end();it++)
{ {
@ -203,6 +203,7 @@ void MWScene::toggleCollisionMode()
act->setGravity(0.); act->setGravity(0.);
act->setVerticalVelocity(0); act->setVerticalVelocity(0);
mFreeFly = true; mFreeFly = true;
return false;
} }
else else
{ {
@ -210,11 +211,15 @@ void MWScene::toggleCollisionMode()
act->enableCollisions(true); act->enableCollisions(true);
act->setGravity(4.); act->setGravity(4.);
act->setVerticalVelocity(0); act->setVerticalVelocity(0);
return true;
} }
} }
return false; // This should never happen, but it shall not bother us now, since
// this part of the code needs a rewrite anyway.
} }
void MWScene::toggleRenderMode (int mode) bool MWScene::toggleRenderMode (int mode)
{ {
switch (mode) switch (mode)
{ {
@ -223,6 +228,8 @@ void MWScene::toggleRenderMode (int mode)
// TODO use a proper function instead of accessing the member variable // TODO use a proper function instead of accessing the member variable
// directly. // directly.
eng->setDebugRenderingMode (!eng->isDebugCreated); eng->setDebugRenderingMode (!eng->isDebugCreated);
break; return eng->isDebugCreated;
} }
return false;
} }

@ -91,12 +91,14 @@ namespace MWRender
/// Toggle collision mode for player. If disabled player object should ignore /// Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity. /// collisions and gravity.
void toggleCollisionMode(); /// \return Resulting mode
bool toggleCollisionMode();
/// Toggle render mode /// Toggle render mode
/// \todo Using an int instead of a enum here to avoid cyclic includes. Will be fixed /// \todo Using an int instead of a enum here to avoid cyclic includes. Will be fixed
/// when the mw*-refactoring is done. /// when the mw*-refactoring is done.
void toggleRenderMode (int mode); /// \return Resulting mode
bool toggleRenderMode (int mode);
}; };
} }

@ -102,10 +102,42 @@ namespace MWScript
} }
}; };
class OpGetPCCell : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
std::string name = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
const ESM::Cell *cell = context.getWorld().getPlayer().getPlayer().getCell()->cell;
std::string current = cell->name;
if (!(cell->data.flags & ESM::Cell::Interior) && current.empty())
{
const ESM::Region *region =
context.getWorld().getStore().regions.find (cell->region);
current = region->name;
}
bool match = current.length()>=name.length() &&
current.substr (0, name.length())==name;
runtime.push (match ? 1 : 0);
}
};
const int opcodeCellChanged = 0x2000000; const int opcodeCellChanged = 0x2000000;
const int opcodeCOC = 0x2000026; const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x200008e; const int opcodeCOE = 0x200008e;
const int opcodeGetInterior = 0x2000131; const int opcodeGetInterior = 0x2000131;
const int opcodeGetPCCell = 0x2000136;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
@ -115,6 +147,7 @@ namespace MWScript
extensions.registerInstruction ("coe", "ll", opcodeCOE); extensions.registerInstruction ("coe", "ll", opcodeCOE);
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior); extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -123,6 +156,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeCOC, new OpCOC); interpreter.installSegment5 (opcodeCOC, new OpCOC);
interpreter.installSegment5 (opcodeCOE, new OpCOE); interpreter.installSegment5 (opcodeCOE, new OpCOE);
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior); interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
} }
} }
} }

@ -46,7 +46,9 @@ namespace MWScript
InterpreterContext& context InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext()); = static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleCollisionMode(); bool enabled = context.getWorld().toggleCollisionMode();
context.report (enabled ? "Collsion -> On" : "Collision -> Off");
} }
}; };
@ -74,6 +76,7 @@ namespace MWScript
} }
extensions.registerInstruction ("togglecollision", "", opcodeToggleCollision); extensions.registerInstruction ("togglecollision", "", opcodeToggleCollision);
extensions.registerInstruction ("tcl", "", opcodeToggleCollision);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)

@ -0,0 +1,94 @@
#include "dialogueextensions.hpp"
#include <components/compiler/extensions.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/runtime.hpp>
#include <components/interpreter/opcodes.hpp>
#include "../mwdialogue/journal.hpp"
#include "interpretercontext.hpp"
namespace MWScript
{
namespace Dialogue
{
class OpJournal : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Integer index = runtime[0].mInteger;
runtime.pop();
context.getEnvironment().mJournal->addEntry (quest, index);
}
};
class OpSetJournalIndex : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
Interpreter::Type_Integer index = runtime[0].mInteger;
runtime.pop();
context.getEnvironment().mJournal->setJournalIndex (quest, index);
}
};
class OpGetJournalIndex : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWScript::InterpreterContext& context
= static_cast<MWScript::InterpreterContext&> (runtime.getContext());
std::string quest = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
int index = context.getEnvironment().mJournal->getJournalIndex (quest);
runtime.push (index);
}
};
const int opcodeJournal = 0x2000133;
const int opcodeSetJournalIndex = 0x2000134;
const int opcodeGetJournalIndex = 0x2000135;
void registerExtensions (Compiler::Extensions& extensions)
{
extensions.registerInstruction ("journal", "cl", opcodeJournal);
extensions.registerInstruction ("setjournalindex", "cl", opcodeSetJournalIndex);
extensions.registerFunction ("getjournalindex", 'l', "c", opcodeGetJournalIndex);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5 (opcodeJournal, new OpJournal);
interpreter.installSegment5 (opcodeSetJournalIndex, new OpSetJournalIndex);
interpreter.installSegment5 (opcodeGetJournalIndex, new OpGetJournalIndex);
}
}
}

@ -0,0 +1,25 @@
#ifndef GAME_SCRIPT_DIALOGUEEXTENSIONS_H
#define GAME_SCRIPT_DIALOGUEEXTENSIONS_H
namespace Compiler
{
class Extensions;
}
namespace Interpreter
{
class Interpreter;
}
namespace MWScript
{
/// \brief Dialogue/Journal-related script functionality
namespace Dialogue
{
void registerExtensions (Compiler::Extensions& extensions);
void installOpcodes (Interpreter::Interpreter& interpreter);
}
}
#endif

@ -104,4 +104,8 @@ op 0x2000115-0x200012f: ModSKill, explicit reference
op 0x2000130: ToggleCollision op 0x2000130: ToggleCollision
op 0x2000131: GetInterior op 0x2000131: GetInterior
op 0x2000132: ToggleCollsionDebug op 0x2000132: ToggleCollsionDebug
opcodes 0x2000133-0x3ffffff unused op 0x2000133: Journal
op 0x2000134: SetJournalIndex
op 0x2000135: GetJournalIndex
op 0x2000136: GetPCCell
opcodes 0x2000137-0x3ffffff unused

@ -13,6 +13,7 @@
#include "containerextensions.hpp" #include "containerextensions.hpp"
#include "aiextensions.hpp" #include "aiextensions.hpp"
#include "controlextensions.hpp" #include "controlextensions.hpp"
#include "dialogueextensions.hpp"
namespace MWScript namespace MWScript
{ {
@ -27,6 +28,7 @@ namespace MWScript
Container::registerExtensions (extensions); Container::registerExtensions (extensions);
Ai::registerExtensions (extensions); Ai::registerExtensions (extensions);
Control::registerExtensions (extensions); Control::registerExtensions (extensions);
Dialogue::registerExtensions (extensions);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -41,5 +43,6 @@ namespace MWScript
Container::installOpcodes (interpreter); Container::installOpcodes (interpreter);
Ai::installOpcodes (interpreter); Ai::installOpcodes (interpreter);
Control::installOpcodes (interpreter); Control::installOpcodes (interpreter);
Dialogue::installOpcodes (interpreter);
} }
} }

@ -110,6 +110,11 @@ namespace MWScript
mEnvironment.mWindowManager->messageBox (message, buttons); mEnvironment.mWindowManager->messageBox (message, buttons);
} }
void InterpreterContext::report (const std::string& message)
{
messageBox (message);
}
bool InterpreterContext::menuMode() bool InterpreterContext::menuMode()
{ {
return mEnvironment.mWindowManager->isGuiMode(); return mEnvironment.mWindowManager->isGuiMode();
@ -260,6 +265,11 @@ namespace MWScript
mEnvironment.mWorld->disable (ref); mEnvironment.mWorld->disable (ref);
} }
MWWorld::Environment& InterpreterContext::getEnvironment()
{
return mEnvironment;
}
MWGui::WindowManager& InterpreterContext::getWindowManager() MWGui::WindowManager& InterpreterContext::getWindowManager()
{ {
return *mEnvironment.mWindowManager; return *mEnvironment.mWindowManager;

@ -57,9 +57,13 @@ namespace MWScript
virtual void setLocalFloat (int index, float value); virtual void setLocalFloat (int index, float value);
using Interpreter::Context::messageBox; using Interpreter::Context::messageBox;
virtual void messageBox (const std::string& message, virtual void messageBox (const std::string& message,
const std::vector<std::string>& buttons); const std::vector<std::string>& buttons);
virtual void report (const std::string& message);
///< By default echo via messageBox.
virtual bool menuMode(); virtual bool menuMode();
virtual int getGlobalShort (const std::string& name) const; virtual int getGlobalShort (const std::string& name) const;
@ -106,6 +110,10 @@ namespace MWScript
virtual void disable (const std::string& id = ""); virtual void disable (const std::string& id = "");
MWWorld::Environment& getEnvironment();
/// \todo remove the following functions (extentions should use getEnvironment instead)
MWWorld::World& getWorld(); MWWorld::World& getWorld();
MWSound::SoundManager& getSoundManager(); MWSound::SoundManager& getSoundManager();

@ -99,7 +99,11 @@ namespace MWScript
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleRenderMode (MWWorld::World::Render_CollisionDebug); bool enabled =
context.getWorld().toggleRenderMode (MWWorld::World::Render_CollisionDebug);
context.report (enabled ?
"Collsion Mesh Rendering -> On" : "Collision Mesh Rendering -> Off");
} }
}; };

@ -12,8 +12,6 @@
#include <components/compiler/scanner.hpp> #include <components/compiler/scanner.hpp>
#include <components/compiler/context.hpp> #include <components/compiler/context.hpp>
#include <components/interpreter/interpreter.hpp>
#include "extensions.hpp" #include "extensions.hpp"
namespace MWScript namespace MWScript
@ -21,7 +19,8 @@ namespace MWScript
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose, ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
Compiler::Context& compilerContext) Compiler::Context& compilerContext)
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext) mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
mOpcodesInstalled (false)
{} {}
bool ScriptManager::compile (const std::string& name) bool ScriptManager::compile (const std::string& name)
@ -99,9 +98,13 @@ namespace MWScript
if (!iter->second.empty()) if (!iter->second.empty())
try try
{ {
Interpreter::Interpreter interpreter (interpreterContext); if (!mOpcodesInstalled)
installOpcodes (interpreter); {
interpreter.run (&iter->second[0], iter->second.size()); installOpcodes (mInterpreter);
mOpcodesInstalled = true;
}
mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

@ -8,6 +8,7 @@
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/fileparser.hpp> #include <components/compiler/fileparser.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/types.hpp> #include <components/interpreter/types.hpp>
namespace ESMS namespace ESMS
@ -35,6 +36,8 @@ namespace MWScript
bool mVerbose; bool mVerbose;
Compiler::Context& mCompilerContext; Compiler::Context& mCompilerContext;
Compiler::FileParser mParser; Compiler::FileParser mParser;
Interpreter::Interpreter mInterpreter;
bool mOpcodesInstalled;
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts; std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;

@ -16,68 +16,70 @@ namespace MWScript
class OpToggleSky : public Interpreter::Opcode0 class OpToggleSky : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleSky(); bool enabled = context.getWorld().toggleSky();
}
}; context.report (enabled ? "Sky -> On" : "Sky -> Off");
}
};
class OpTurnMoonWhite : public Interpreter::Opcode0 class OpTurnMoonWhite : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().setMoonColour (false); context.getWorld().setMoonColour (false);
} }
}; };
class OpTurnMoonRed : public Interpreter::Opcode0 class OpTurnMoonRed : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().setMoonColour (true); context.getWorld().setMoonColour (true);
} }
}; };
class OpGetMasserPhase : public Interpreter::Opcode0 class OpGetMasserPhase : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
runtime.push (context.getWorld().getMasserPhase()); runtime.push (context.getWorld().getMasserPhase());
} }
}; };
class OpGetSecundaPhase : public Interpreter::Opcode0 class OpGetSecundaPhase : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context = InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext()); static_cast<InterpreterContext&> (runtime.getContext());
runtime.push (context.getWorld().getSecundaPhase()); runtime.push (context.getWorld().getSecundaPhase());
} }
}; };
const int opcodeToggleSky = 0x2000021; const int opcodeToggleSky = 0x2000021;
const int opcodeTurnMoonWhite = 0x2000022; const int opcodeTurnMoonWhite = 0x2000022;
const int opcodeTurnMoonRed = 0x2000023; const int opcodeTurnMoonRed = 0x2000023;
@ -93,7 +95,7 @@ namespace MWScript
extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase); extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase);
extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase); extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
interpreter.installSegment5 (opcodeToggleSky, new OpToggleSky); interpreter.installSegment5 (opcodeToggleSky, new OpToggleSky);
@ -101,7 +103,6 @@ namespace MWScript
interpreter.installSegment5 (opcodeTurnMoonRed, new OpTurnMoonRed); interpreter.installSegment5 (opcodeTurnMoonRed, new OpTurnMoonRed);
interpreter.installSegment5 (opcodeGetMasserPhase, new OpGetMasserPhase); interpreter.installSegment5 (opcodeGetMasserPhase, new OpGetMasserPhase);
interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase); interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase);
} }
} }
} }

@ -24,6 +24,7 @@ namespace MWMechanics
namespace MWDialogue namespace MWDialogue
{ {
class DialogueManager; class DialogueManager;
class Journal;
} }
namespace MWInput namespace MWInput
@ -41,7 +42,7 @@ namespace MWWorld
public: public:
Environment() Environment()
: mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0), : mWorld (0), mSoundManager (0), mGlobalScripts (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mFrameDuration (0), mMechanicsManager (0), mDialogueManager (0), mJournal (0), mFrameDuration (0),
mInputManager (0) mInputManager (0)
{} {}
@ -51,6 +52,7 @@ namespace MWWorld
MWGui::WindowManager *mWindowManager; MWGui::WindowManager *mWindowManager;
MWMechanics::MechanicsManager *mMechanicsManager; MWMechanics::MechanicsManager *mMechanicsManager;
MWDialogue::DialogueManager *mDialogueManager; MWDialogue::DialogueManager *mDialogueManager;
MWDialogue::Journal *mJournal;
float mFrameDuration; float mFrameDuration;
// For setting GUI mode // For setting GUI mode

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
#include <components/files/collections.hpp>
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/interior.hpp" #include "../mwrender/interior.hpp"
@ -406,15 +407,16 @@ namespace MWWorld
mCellChanged = true; mCellChanged = true;
} }
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir, World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment) bool newGame, Environment& environment)
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0), : mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
mSky (false), mCellChanged (false), mEnvironment (environment) mSky (false), mCellChanged (false), mEnvironment (environment)
{ {
mPhysEngine = physEng; mPhysEngine = physEng;
boost::filesystem::path masterPath (dataDir);
masterPath /= master; boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master));
std::cout << "Loading ESM " << masterPath.string() << "\n"; std::cout << "Loading ESM " << masterPath.string() << "\n";
@ -460,7 +462,7 @@ namespace MWWorld
return *mPlayer; return *mPlayer;
} }
ESMS::ESMStore& World::getStore() const ESMS::ESMStore& World::getStore() const
{ {
return mStore; return mStore;
} }
@ -480,6 +482,11 @@ namespace MWWorld
return (*mGlobalVariables)[name]; return (*mGlobalVariables)[name];
} }
Globals::Data World::getGlobalVariable (const std::string& name) const
{
return (*mGlobalVariables)[name];
}
char World::getGlobalVariableType (const std::string& name) const char World::getGlobalVariableType (const std::string& name) const
{ {
return mGlobalVariables->getType (name); return mGlobalVariables->getType (name);
@ -645,12 +652,13 @@ namespace MWWorld
mSkyManager->setDate (mGlobalVariables->getInt ("day"), month); mSkyManager->setDate (mGlobalVariables->getInt ("day"), month);
} }
void World::toggleSky() bool World::toggleSky()
{ {
if (mSky) if (mSky)
{ {
mSky = false; mSky = false;
mSkyManager->disable(); mSkyManager->disable();
return false;
} }
else else
{ {
@ -660,6 +668,7 @@ namespace MWWorld
mSkyManager->setDate (mGlobalVariables->getInt ("day"), mSkyManager->setDate (mGlobalVariables->getInt ("day"),
mGlobalVariables->getInt ("month")); mGlobalVariables->getInt ("month"));
mSkyManager->enable(); mSkyManager->enable();
return true;
} }
} }
@ -853,13 +862,13 @@ namespace MWWorld
mScene.doPhysics (duration, *this, actors); mScene.doPhysics (duration, *this, actors);
} }
void World::toggleCollisionMode() bool World::toggleCollisionMode()
{ {
mScene.toggleCollisionMode(); return mScene.toggleCollisionMode();
} }
void World::toggleRenderMode (RenderMode mode) bool World::toggleRenderMode (RenderMode mode)
{ {
mScene.toggleRenderMode (mode); return mScene.toggleRenderMode (mode);
} }
} }

@ -26,6 +26,11 @@ namespace ESM
struct Position; struct Position;
} }
namespace Files
{
class Collections;
}
namespace Render namespace Render
{ {
class OgreRenderer; class OgreRenderer;
@ -107,7 +112,8 @@ namespace MWWorld
/// interior cell. /// interior cell.
public: public:
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, const boost::filesystem::path& dataDir, World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& master, const boost::filesystem::path& resDir, bool newGame,
Environment& environment); Environment& environment);
@ -115,7 +121,7 @@ namespace MWWorld
MWWorld::Player& getPlayer(); MWWorld::Player& getPlayer();
ESMS::ESMStore& getStore(); const ESMS::ESMStore& getStore() const;
const ScriptList& getLocalScripts() const; const ScriptList& getLocalScripts() const;
///< Names and local variable state of all local scripts in active cells. ///< Names and local variable state of all local scripts in active cells.
@ -125,6 +131,8 @@ namespace MWWorld
Globals::Data& getGlobalVariable (const std::string& name); Globals::Data& getGlobalVariable (const std::string& name);
Globals::Data getGlobalVariable (const std::string& name) const;
char getGlobalVariableType (const std::string& name) const; char getGlobalVariableType (const std::string& name) const;
///< Return ' ', if there is no global variable with this name. ///< Return ' ', if there is no global variable with this name.
@ -147,7 +155,8 @@ namespace MWWorld
void setDay (int day); void setDay (int day);
void toggleSky(); bool toggleSky();
///< \return Resulting mode
int getMasserPhase() const; int getMasserPhase() const;
@ -185,12 +194,14 @@ namespace MWWorld
float duration); float duration);
///< Run physics simulation and modify \a world accordingly. ///< Run physics simulation and modify \a world accordingly.
void toggleCollisionMode(); bool toggleCollisionMode();
///< Toggle collision mode for player. If disabled player object should ignore ///< Toggle collision mode for player. If disabled player object should ignore
/// collisions and gravity. /// collisions and gravity.
///< \return Resulting mode
void toggleRenderMode (RenderMode mode); bool toggleRenderMode (RenderMode mode);
///< Toggle a render mode. ///< Toggle a render mode.
///< \return Resulting mode
}; };
} }

@ -1,25 +0,0 @@
#ifndef PATH__HPP
#define PATH__HPP
#include <OgrePlatform.h>
#include <string>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OSX/macUtils.h>
#endif
namespace OMW
{
class Path
{
public:
enum PathTypeEnum
{
USER_CFG_PATH,
GLOBAL_CFG_PATH
};
static std::string getPath(PathTypeEnum parType, const std::string parApp, const std::string parFile);
};
}
#endif

@ -12,9 +12,9 @@
# For details see the accompanying COPYING-CMAKE-SCRIPTS file. # For details see the accompanying COPYING-CMAKE-SCRIPTS file.
CMAKE_POLICY(PUSH) CMAKE_POLICY(PUSH)
IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR) IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR)
SET(OGRE_FIND_QUIETLY TRUE) # Already in cache, be silent SET(OGRE_FIND_QUIETLY TRUE) # Already in cache, be silent
ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR) ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR)
IF (WIN32) #Windows IF (WIN32) #Windows
MESSAGE(STATUS "Looking for OGRE") MESSAGE(STATUS "Looking for OGRE")
@ -47,7 +47,16 @@ IF (UNIX AND NOT APPLE)
PKG_CHECK_MODULES(OGRE OGRE) PKG_CHECK_MODULES(OGRE OGRE)
ENDIF (UNIX AND NOT APPLE) ENDIF (UNIX AND NOT APPLE)
# on OS X we need Ogre SDK because framework doesn't include all libs, just Ogre Main lib
IF (APPLE) IF (APPLE)
IF (OGRESDK)
MESSAGE(STATUS "Using Ogre SDK")
SET(OGRE_LIB_DIR ${OGRESDK}/lib)
ELSE (OGRESDK)
MESSAGE(FATAL_ERROR "Path to Ogre SDK not specified. Specify OGRESDK.")
ENDIF (OGRESDK)
FIND_PATH(OGRE_INCLUDE_DIR Ogre.h FIND_PATH(OGRE_INCLUDE_DIR Ogre.h
PATHS PATHS
/Library/Frameworks /Library/Frameworks
@ -71,7 +80,12 @@ SET(OGRE_LIB_DIR ${OGRE_LIB_DIR} CACHE PATH "")
if(OGRE_LIB_DIR) if(OGRE_LIB_DIR)
CMAKE_POLICY(SET CMP0009 NEW) CMAKE_POLICY(SET CMP0009 NEW)
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so") IF (NOT APPLE)
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so")
ENDIF (NOT APPLE)
IF (APPLE)
FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.dylib")
ENDIF (APPLE)
FOREACH (OGRE_PLUGINS_FILE ${OGRE_PLUGINS}) FOREACH (OGRE_PLUGINS_FILE ${OGRE_PLUGINS})
STRING(REGEX REPLACE "/[^/]*$" "" OGRE_PLUGIN_DIR ${OGRE_PLUGINS_FILE}) STRING(REGEX REPLACE "/[^/]*$" "" OGRE_PLUGIN_DIR ${OGRE_PLUGINS_FILE})
ENDFOREACH(OGRE_PLUGINS_FILE) ENDFOREACH(OGRE_PLUGINS_FILE)
@ -85,6 +99,7 @@ IF (OGRE_FOUND)
IF (NOT OGRE_FIND_QUIETLY) IF (NOT OGRE_FIND_QUIETLY)
MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}") MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}")
MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}") MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}")
MESSAGE(STATUS " plugins : ${OGRE_PLUGIN_DIR}")
ENDIF (NOT OGRE_FIND_QUIETLY) ENDIF (NOT OGRE_FIND_QUIETLY)
ELSE (OGRE_FOUND) ELSE (OGRE_FOUND)
IF (OGRE_FIND_REQUIRED) IF (OGRE_FIND_REQUIRED)

@ -125,6 +125,11 @@ namespace
code.push_back (Compiler::Generator::segment3 (0, buttons)); code.push_back (Compiler::Generator::segment3 (0, buttons));
} }
void opReport (Compiler::Generator::CodeContainer& code)
{
code.push_back (Compiler::Generator::segment5 (58));
}
void opFetchLocalShort (Compiler::Generator::CodeContainer& code) void opFetchLocalShort (Compiler::Generator::CodeContainer& code)
{ {
code.push_back (Compiler::Generator::segment5 (21)); code.push_back (Compiler::Generator::segment5 (21));
@ -516,6 +521,14 @@ namespace Compiler
opMessageBox (code, buttons); opMessageBox (code, buttons);
} }
void report (CodeContainer& code, Literals& literals, const std::string& message)
{
int index = literals.addString (message);
opPushInt (code, index);
opReport (code);
}
void fetchLocal (CodeContainer& code, char localType, int localIndex) void fetchLocal (CodeContainer& code, char localType, int localIndex)
{ {
opPushInt (code, localIndex); opPushInt (code, localIndex);

@ -81,6 +81,8 @@ namespace Compiler
void message (CodeContainer& code, Literals& literals, const std::string& message, void message (CodeContainer& code, Literals& literals, const std::string& message,
int buttons); int buttons);
void report (CodeContainer& code, Literals& literals, const std::string& message);
void fetchLocal (CodeContainer& code, char localType, int localIndex); void fetchLocal (CodeContainer& code, char localType, int localIndex);
void jump (CodeContainer& code, int offset); void jump (CodeContainer& code, int offset);

@ -30,12 +30,12 @@ namespace Compiler
{ {
case 'l': case 'l':
Generator::message (mCode, mLiterals, "%g", 0); Generator::report (mCode, mLiterals, "%g");
break; break;
case 'f': case 'f':
Generator::message (mCode, mLiterals, "%f", 0); Generator::report (mCode, mLiterals, "%f");
break; break;
default: default:

@ -3,13 +3,14 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
// Pixel color value. Standard four-byte rr,gg,bb,aa format. // Pixel color value. Standard four-byte rr,gg,bb,aa format.
typedef int32_t Color; typedef int32_t Color;
enum VarType enum VarType
{ {
VT_Unknown, VT_Unknown,
VT_None, VT_None,
VT_Short, VT_Short,
@ -18,79 +19,79 @@ enum VarType
VT_Float, VT_Float,
VT_String, VT_String,
VT_Ignored VT_Ignored
}; };
enum Specialization enum Specialization
{ {
SPC_Combat = 0, SPC_Combat = 0,
SPC_Magic = 1, SPC_Magic = 1,
SPC_Stealth = 2 SPC_Stealth = 2
}; };
enum RangeType enum RangeType
{ {
RT_Self = 0, RT_Self = 0,
RT_Touch = 1, RT_Touch = 1,
RT_Target = 2 RT_Target = 2
}; };
/** A list of references to spells and spell effects. This is shared /** A list of references to spells and spell effects. This is shared
between the records BSGN, NPC and RACE. between the records BSGN, NPC and RACE.
*/ */
struct SpellList struct SpellList
{ {
std::vector<std::string> list; std::vector<std::string> list;
void load(ESMReader &esm) void load(ESMReader &esm)
{ {
while(esm.isNextSub("NPCS")) while (esm.isNextSub("NPCS"))
list.push_back(esm.getHString()); list.push_back(esm.getHString());
} }
}; };
/** Defines a spell effect. Shared between SPEL (Spells), ALCH /** Defines a spell effect. Shared between SPEL (Spells), ALCH
(Potions) and ENCH (Item enchantments) records (Potions) and ENCH (Item enchantments) records
*/ */
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
// Position and rotation // Position and rotation
struct Position struct Position
{ {
float pos[3]; float pos[3];
float rot[3]; float rot[3];
}; };
struct ENAMstruct struct ENAMstruct
{ {
// Magical effect, hard-coded ID // Magical effect, hard-coded ID
short effectID; short effectID;
// Which skills/attributes are affected (for restore/drain spells // Which skills/attributes are affected (for restore/drain spells
// etc.) // etc.)
signed char skill, attribute; // -1 if N/A signed char skill, attribute; // -1 if N/A
// Other spell parameters // Other spell parameters
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum) int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
int area, duration, magnMin, magnMax; int area, duration, magnMin, magnMax;
// Struct size should be 24 bytes // Struct size should be 24 bytes
}; };
#pragma pack(pop) #pragma pack(pop)
struct EffectList struct EffectList
{ {
std::vector<ENAMstruct> list; std::vector<ENAMstruct> list;
void load(ESMReader &esm) void load(ESMReader &esm)
{ {
ENAMstruct s; ENAMstruct s;
while(esm.isNextSub("ENAM")) while (esm.isNextSub("ENAM"))
{ {
esm.getHT(s, 24); esm.getHT(s, 24);
list.push_back(s); list.push_back(s);
} }
} }
}; };
} }

@ -0,0 +1,345 @@
#include "esm_reader.hpp"
namespace ESM
{
ESM_Context ESMReader::getContext()
{
// Update the file position before returning
mCtx.filePos = mEsm->tell();
return mCtx;
}
void ESMReader::restoreContext(const ESM_Context &rc)
{
// Reopen the file if necessary
if (mCtx.filename != rc.filename)
openRaw(rc.filename);
// Copy the data
mCtx = rc;
// Make sure we seek to the right place
mEsm->seek(mCtx.filePos);
}
void ESMReader::close()
{
mEsm.reset();
mCtx.filename.clear();
mCtx.leftFile = 0;
mCtx.leftRec = 0;
mCtx.leftSub = 0;
mCtx.subCached = false;
mCtx.recName.val = 0;
mCtx.subName.val = 0;
}
void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
{
close();
mEsm = _esm;
mCtx.filename = name;
mCtx.leftFile = mEsm->size();
// Flag certain files for special treatment, based on the file
// name.
const char *cstr = mCtx.filename.c_str();
if (iends(cstr, "Morrowind.esm"))
mSpf = SF_Morrowind;
else if (iends(cstr, "Tribunal.esm"))
mSpf = SF_Tribunal;
else if (iends(cstr, "Bloodmoon.esm"))
mSpf = SF_Bloodmoon;
else
mSpf = SF_Other;
}
void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
{
openRaw(_esm, name);
if (getRecName() != "TES3")
fail("Not a valid Morrowind file");
getRecHeader();
// Get the header
getHNT(mCtx.header, "HEDR", 300);
if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13)
fail("Unsupported file format version");
while (isNextSub("MAST"))
{
MasterData m;
m.name = getHString();
m.size = getHNLong("DATA");
mMasters.push_back(m);
}
if (mCtx.header.type == FT_ESS)
{
// Savegame-related data
// Player position etc
getHNT(mSaveData, "GMDT", 124);
/* Image properties, five ints. Is always:
Red-mask: 0xff0000
Blue-mask: 0x00ff00
Green-mask: 0x0000ff
Alpha-mask: 0x000000
Bpp: 32
*/
getSubNameIs("SCRD");
skipHSubSize(20);
/* Savegame screenshot:
128x128 pixels * 4 bytes per pixel
*/
getSubNameIs("SCRS");
skipHSubSize(65536);
}
}
void ESMReader::open(const std::string &file)
{
using namespace Mangle::Stream;
open(StreamPtr(new FileStream(file)), file);
}
void ESMReader::openRaw(const std::string &file)
{
using namespace Mangle::Stream;
openRaw(StreamPtr(new FileStream(file)), file);
}
int64_t ESMReader::getHNLong(const char *name)
{
int64_t val;
getHNT(val, name);
return val;
}
std::string ESMReader::getHNOString(const char* name)
{
if (isNextSub(name))
return getHString();
return "";
}
std::string ESMReader::getHNString(const char* name)
{
getSubNameIs(name);
return getHString();
}
std::string ESMReader::getHString()
{
getSubHeader();
// Hack to make MultiMark.esp load. Zero-length strings do not
// occur in any of the official mods, but MultiMark makes use of
// them. For some reason, they break the rules, and contain a byte
// (value 0) even if the header says there is no data. If
// Morrowind accepts it, so should we.
if (mCtx.leftSub == 0)
{
// Skip the following zero byte
mCtx.leftRec--;
char c;
mEsm->read(&c, 1);
return "";
}
return getString(mCtx.leftSub);
}
void ESMReader::getHExact(void*p, int size)
{
getSubHeader();
if (size != static_cast<int> (mCtx.leftSub))
fail("getHExact() size mismatch");
getExact(p, size);
}
// Read the given number of bytes from a named subrecord
void ESMReader::getHNExact(void*p, int size, const char* name)
{
getSubNameIs(name);
getHExact(p, size);
}
// Get the next subrecord name and check if it matches the parameter
void ESMReader::getSubNameIs(const char* name)
{
getSubName();
if (mCtx.subName != name)
fail(
"Expected subrecord " + std::string(name) + " but got "
+ mCtx.subName.toString());
}
bool ESMReader::isNextSub(const char* name)
{
if (!mCtx.leftRec)
return false;
getSubName();
// If the name didn't match, then mark the it as 'cached' so it's
// available for the next call to getSubName.
mCtx.subCached = (mCtx.subName != name);
// If subCached is false, then subName == name.
return !mCtx.subCached;
}
// Read subrecord name. This gets called a LOT, so I've optimized it
// slightly.
void ESMReader::getSubName()
{
// If the name has already been read, do nothing
if (mCtx.subCached)
{
mCtx.subCached = false;
return;
}
// reading the subrecord data anyway.
mEsm->read(mCtx.subName.name, 4);
mCtx.leftRec -= 4;
}
bool ESMReader::isEmptyOrGetName()
{
if (mCtx.leftRec)
{
mEsm->read(mCtx.subName.name, 4);
mCtx.leftRec -= 4;
return false;
}
return true;
}
void ESMReader::skipHSub()
{
getSubHeader();
skip(mCtx.leftSub);
}
void ESMReader::skipHSubSize(int size)
{
skipHSub();
if (static_cast<int> (mCtx.leftSub) != size)
fail("skipHSubSize() mismatch");
}
void ESMReader::getSubHeader()
{
if (mCtx.leftRec < 4)
fail("End of record while reading sub-record header");
// Get subrecord size
getT(mCtx.leftSub);
// Adjust number of record bytes left
mCtx.leftRec -= mCtx.leftSub + 4;
}
void ESMReader::getSubHeaderIs(int size)
{
getSubHeader();
if (size != static_cast<int> (mCtx.leftSub))
fail("getSubHeaderIs(): Sub header mismatch");
}
NAME ESMReader::getRecName()
{
if (!hasMoreRecs())
fail("No more records, getRecName() failed");
getName(mCtx.recName);
mCtx.leftFile -= 4;
// Make sure we don't carry over any old cached subrecord
// names. This can happen in some cases when we skip parts of a
// record.
mCtx.subCached = false;
return mCtx.recName;
}
void ESMReader::skipRecord()
{
skip(mCtx.leftRec);
mCtx.leftRec = 0;
}
void ESMReader::skipHRecord()
{
if (!mCtx.leftFile)
return;
getRecHeader();
skipRecord();
}
void ESMReader::getRecHeader(uint32_t &flags)
{
// General error checking
if (mCtx.leftFile < 12)
fail("End of file while reading record header");
if (mCtx.leftRec)
fail("Previous record contains unread bytes");
getUint(mCtx.leftRec);
getUint(flags);// This header entry is always zero
getUint(flags);
mCtx.leftFile -= 12;
// Check that sizes add up
if (mCtx.leftFile < mCtx.leftRec)
fail("Record size is larger than rest of file");
// Adjust number of bytes mCtx.left in file
mCtx.leftFile -= mCtx.leftRec;
}
/*************************************************************************
*
* Lowest level data reading and misc methods
*
*************************************************************************/
void ESMReader::getExact(void*x, int size)
{
int t = mEsm->read(x, size);
if (t != size)
fail("Read error");
}
std::string ESMReader::getString(int size)
{
char *ptr = ToUTF8::getBuffer(size);
mEsm->read(ptr, size);
// Convert to UTF8 and return
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
}
void ESMReader::fail(const std::string &msg)
{
using namespace std;
stringstream ss;
ss << "ESM Error: " << msg;
ss << "\n File: " << mCtx.filename;
ss << "\n Record: " << mCtx.recName.toString();
ss << "\n Subrecord: " << mCtx.subName.toString();
if (mEsm != NULL)
ss << "\n Offset: 0x" << hex << mEsm->tell();
throw std::runtime_error(ss.str());
}
}

@ -171,122 +171,27 @@ public:
/** Save the current file position and information in a ESM_Context /** Save the current file position and information in a ESM_Context
struct struct
*/ */
ESM_Context getContext() ESM_Context getContext();
{
// Update the file position before returning
mCtx.filePos = mEsm->tell();
return mCtx;
}
/** Restore a previously saved context */ /** Restore a previously saved context */
void restoreContext(const ESM_Context &rc) void restoreContext(const ESM_Context &rc);
{
// Reopen the file if necessary
if(mCtx.filename != rc.filename)
openRaw(rc.filename);
// Copy the data
mCtx = rc;
// Make sure we seek to the right place
mEsm->seek(mCtx.filePos);
}
/** Close the file, resets all information. After calling close() /** Close the file, resets all information. After calling close()
the structure may be reused to load a new file. the structure may be reused to load a new file.
*/ */
void close() void close();
{
mEsm.reset();
mCtx.filename.clear();
mCtx.leftFile = 0;
mCtx.leftRec = 0;
mCtx.leftSub = 0;
mCtx.subCached = false;
mCtx.recName.val = 0;
mCtx.subName.val = 0;
}
/// Raw opening. Opens the file and sets everything up but doesn't /// Raw opening. Opens the file and sets everything up but doesn't
/// parse the header. /// parse the header.
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name) void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name);
{
close();
mEsm = _esm;
mCtx.filename = name;
mCtx.leftFile = mEsm->size();
// Flag certain files for special treatment, based on the file
// name.
const char *cstr = mCtx.filename.c_str();
if(iends(cstr, "Morrowind.esm")) mSpf = SF_Morrowind;
else if(iends(cstr, "Tribunal.esm")) mSpf = SF_Tribunal;
else if(iends(cstr, "Bloodmoon.esm")) mSpf = SF_Bloodmoon;
else mSpf = SF_Other;
}
/// Load ES file from a new stream, parses the header. Closes the /// Load ES file from a new stream, parses the header. Closes the
/// currently open file first, if any. /// currently open file first, if any.
void open(Mangle::Stream::StreamPtr _esm, const std::string &name) void open(Mangle::Stream::StreamPtr _esm, const std::string &name);
{
openRaw(_esm, name);
if(getRecName() != "TES3")
fail("Not a valid Morrowind file");
getRecHeader();
// Get the header
getHNT(mCtx.header, "HEDR", 300);
if(mCtx.header.version != VER_12 &&
mCtx.header.version != VER_13)
fail("Unsupported file format version");
while(isNextSub("MAST"))
{
MasterData m;
m.name = getHString();
m.size = getHNLong("DATA");
mMasters.push_back(m);
}
if(mCtx.header.type == FT_ESS)
{
// Savegame-related data
// Player position etc
getHNT(mSaveData, "GMDT", 124);
/* Image properties, five ints. Is always:
Red-mask: 0xff0000
Blue-mask: 0x00ff00
Green-mask: 0x0000ff
Alpha-mask: 0x000000
Bpp: 32
*/
getSubNameIs("SCRD");
skipHSubSize(20);
/* Savegame screenshot:
128x128 pixels * 4 bytes per pixel
*/
getSubNameIs("SCRS");
skipHSubSize(65536);
}
}
void open(const std::string &file) void open(const std::string &file);
{
using namespace Mangle::Stream;
open(StreamPtr(new FileStream(file)), file);
}
void openRaw(const std::string &file) void openRaw(const std::string &file);
{
using namespace Mangle::Stream;
openRaw(StreamPtr(new FileStream(file)), file);
}
/************************************************************************* /*************************************************************************
* *
@ -306,8 +211,8 @@ public:
template <typename X> template <typename X>
void getHNOT(X &x, const char* name) void getHNOT(X &x, const char* name)
{ {
if(isNextSub(name)) if(isNextSub(name))
getHT(x); getHT(x);
} }
// Version with extra size checking, to make sure the compiler // Version with extra size checking, to make sure the compiler
@ -315,26 +220,21 @@ public:
template <typename X> template <typename X>
void getHNT(X &x, const char* name, int size) void getHNT(X &x, const char* name, int size)
{ {
assert(sizeof(X) == size); assert(sizeof(X) == size);
getSubNameIs(name); getSubNameIs(name);
getHT(x); getHT(x);
} }
int64_t getHNLong(const char *name) int64_t getHNLong(const char *name);
{
int64_t val;
getHNT(val, name);
return val;
}
// Get data of a given type/size, including subrecord header // Get data of a given type/size, including subrecord header
template <typename X> template <typename X>
void getHT(X &x) void getHT(X &x)
{ {
getSubHeader(); getSubHeader();
if(mCtx.leftSub != sizeof(X)) if (mCtx.leftSub != sizeof(X))
fail("getHT(): subrecord size mismatch"); fail("getHT(): subrecord size mismatch");
getT(x); getT(x);
} }
// Version with extra size checking, to make sure the compiler // Version with extra size checking, to make sure the compiler
@ -342,62 +242,24 @@ public:
template <typename X> template <typename X>
void getHT(X &x, int size) void getHT(X &x, int size)
{ {
assert(sizeof(X) == size); assert(sizeof(X) == size);
getHT(x); getHT(x);
} }
// Read a string by the given name if it is the next record. // Read a string by the given name if it is the next record.
std::string getHNOString(const char* name) std::string getHNOString(const char* name);
{
if(isNextSub(name))
return getHString();
return "";
}
// Read a string with the given sub-record name // Read a string with the given sub-record name
std::string getHNString(const char* name) std::string getHNString(const char* name);
{
getSubNameIs(name);
return getHString();
}
// Read a string, including the sub-record header (but not the name) // Read a string, including the sub-record header (but not the name)
std::string getHString() std::string getHString();
{
getSubHeader();
// Hack to make MultiMark.esp load. Zero-length strings do not
// occur in any of the official mods, but MultiMark makes use of
// them. For some reason, they break the rules, and contain a byte
// (value 0) even if the header says there is no data. If
// Morrowind accepts it, so should we.
if(mCtx.leftSub == 0)
{
// Skip the following zero byte
mCtx.leftRec--;
char c;
mEsm->read(&c,1);
return "";
}
return getString(mCtx.leftSub);
}
// Read the given number of bytes from a subrecord // Read the given number of bytes from a subrecord
void getHExact(void*p, int size) void getHExact(void*p, int size);
{
getSubHeader();
if(size !=static_cast<int> (mCtx.leftSub))
fail("getHExact() size mismatch");
getExact(p,size);
}
// Read the given number of bytes from a named subrecord // Read the given number of bytes from a named subrecord
void getHNExact(void*p, int size, const char* name) void getHNExact(void*p, int size, const char* name);
{
getSubNameIs(name);
getHExact(p,size);
}
/************************************************************************* /*************************************************************************
* *
@ -406,100 +268,37 @@ public:
*************************************************************************/ *************************************************************************/
// Get the next subrecord name and check if it matches the parameter // Get the next subrecord name and check if it matches the parameter
void getSubNameIs(const char* name) void getSubNameIs(const char* name);
{
getSubName();
if(mCtx.subName != name)
fail("Expected subrecord " + std::string(name) + " but got " + mCtx.subName.toString());
}
/** Checks if the next sub record name matches the parameter. If it /** Checks if the next sub record name matches the parameter. If it
does, it is read into 'subName' just as if getSubName() was does, it is read into 'subName' just as if getSubName() was
called. If not, the read name will still be available for future called. If not, the read name will still be available for future
calls to getSubName(), isNextSub() and getSubNameIs(). calls to getSubName(), isNextSub() and getSubNameIs().
*/ */
bool isNextSub(const char* name) bool isNextSub(const char* name);
{
if(!mCtx.leftRec) return false;
getSubName();
// If the name didn't match, then mark the it as 'cached' so it's
// available for the next call to getSubName.
mCtx.subCached = (mCtx.subName != name);
// If subCached is false, then subName == name.
return !mCtx.subCached;
}
// Read subrecord name. This gets called a LOT, so I've optimized it // Read subrecord name. This gets called a LOT, so I've optimized it
// slightly. // slightly.
void getSubName() void getSubName();
{
// If the name has already been read, do nothing
if(mCtx.subCached)
{
mCtx.subCached = false;
return;
}
// Don't bother with error checking, we will catch an EOF upon
// reading the subrecord data anyway.
mEsm->read(mCtx.subName.name, 4);
mCtx.leftRec -= 4;
}
// This is specially optimized for LoadINFO. // This is specially optimized for LoadINFO.
bool isEmptyOrGetName() bool isEmptyOrGetName();
{
if(mCtx.leftRec)
{
mEsm->read(mCtx.subName.name, 4);
mCtx.leftRec -= 4;
return false;
}
return true;
}
// Skip current sub record, including header (but not including // Skip current sub record, including header (but not including
// name.) // name.)
void skipHSub() void skipHSub();
{
getSubHeader();
skip(mCtx.leftSub);
}
// Skip sub record and check its size // Skip sub record and check its size
void skipHSubSize(int size) void skipHSubSize(int size);
{
skipHSub();
if(static_cast<int> (mCtx.leftSub) != size)
fail("skipHSubSize() mismatch");
}
/* Sub-record header. This updates leftRec beyond the current /* Sub-record header. This updates leftRec beyond the current
sub-record as well. leftSub contains size of current sub-record. sub-record as well. leftSub contains size of current sub-record.
*/ */
void getSubHeader() void getSubHeader();
{
if(mCtx.leftRec < 4)
fail("End of record while reading sub-record header");
// Get subrecord size
getT(mCtx.leftSub);
// Adjust number of record bytes left
mCtx.leftRec -= mCtx.leftSub + 4;
}
/** Get sub header and check the size /** Get sub header and check the size
*/ */
void getSubHeaderIs(int size) void getSubHeaderIs(int size);
{
getSubHeader();
if(size != static_cast<int> (mCtx.leftSub))
fail("getSubHeaderIs(): Sub header mismatch");
}
/************************************************************************* /*************************************************************************
* *
@ -508,62 +307,21 @@ public:
*************************************************************************/ *************************************************************************/
// Get the next record name // Get the next record name
NAME getRecName() NAME getRecName();
{
if(!hasMoreRecs())
fail("No more records, getRecName() failed");
getName(mCtx.recName);
mCtx.leftFile -= 4;
// Make sure we don't carry over any old cached subrecord
// names. This can happen in some cases when we skip parts of a
// record.
mCtx.subCached = false;
return mCtx.recName;
}
// Skip the rest of this record. Assumes the name and header have // Skip the rest of this record. Assumes the name and header have
// already been read // already been read
void skipRecord() void skipRecord();
{
skip(mCtx.leftRec);
mCtx.leftRec = 0;
}
// Skip an entire record, including the header (but not the name) // Skip an entire record, including the header (but not the name)
void skipHRecord() void skipHRecord();
{
if(!mCtx.leftFile) return;
getRecHeader();
skipRecord();
}
/* Read record header. This updatesleftFile BEYOND the data that /* Read record header. This updatesleftFile BEYOND the data that
follows the header, ie beyond the entire record. You should use follows the header, ie beyond the entire record. You should use
leftRec to orient yourself inside the record itself. leftRec to orient yourself inside the record itself.
*/ */
void getRecHeader() { uint32_t u; getRecHeader(u); } void getRecHeader() { uint32_t u; getRecHeader(u); }
void getRecHeader(uint32_t &flags) void getRecHeader(uint32_t &flags);
{
// General error checking
if(mCtx.leftFile < 12)
fail("End of file while reading record header");
if(mCtx.leftRec)
fail("Previous record contains unread bytes");
getUint(mCtx.leftRec);
getUint(flags);// This header entry is always zero
getUint(flags);
mCtx.leftFile -= 12;
// Check that sizes add up
if(mCtx.leftFile < mCtx.leftRec)
fail("Record size is larger than rest of file");
// Adjust number of bytes mCtx.left in file
mCtx.leftFile -= mCtx.leftRec;
}
bool hasMoreRecs() { return mCtx.leftFile > 0; } bool hasMoreRecs() { return mCtx.leftFile > 0; }
bool hasMoreSubs() { return mCtx.leftRec > 0; } bool hasMoreSubs() { return mCtx.leftRec > 0; }
@ -578,44 +336,19 @@ public:
template <typename X> template <typename X>
void getT(X &x) { getExact(&x, sizeof(X)); } void getT(X &x) { getExact(&x, sizeof(X)); }
void getExact(void*x, int size) void getExact(void*x, int size);
{
int t = mEsm->read(x, size);
if(t != size)
fail("Read error");
}
void getName(NAME &name) { getT(name); } void getName(NAME &name) { getT(name); }
void getUint(uint32_t &u) { getT(u); } void getUint(uint32_t &u) { getT(u); }
// Read the next 'size' bytes and return them as a string. Converts // Read the next 'size' bytes and return them as a string. Converts
// them from native encoding to UTF8 in the process. // them from native encoding to UTF8 in the process.
std::string getString(int size) std::string getString(int size);
{
char *ptr = ToUTF8::getBuffer(size);
mEsm->read(ptr,size);
// Convert to UTF8 and return
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
}
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); } void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
uint64_t getOffset() { return mEsm->tell(); } uint64_t getOffset() { return mEsm->tell(); }
/// Used for error handling /// Used for error handling
void fail(const std::string &msg) void fail(const std::string &msg);
{
using namespace std;
stringstream ss;
ss << "ESM Error: " << msg;
ss << "\n File: " << mCtx.filename;
ss << "\n Record: " << mCtx.recName.toString();
ss << "\n Subrecord: " << mCtx.subName.toString();
if(mEsm != NULL)
ss << "\n Offset: 0x" << hex << mEsm->tell();
throw std::runtime_error(ss.str());
}
private: private:
Mangle::Stream::StreamPtr mEsm; Mangle::Stream::StreamPtr mEsm;

@ -1,175 +0,0 @@
#include "records.hpp"
/** Implementation for some of the load() functions. Most are found in
the header files themselves, but this is a bit irritating to
compile if you're changing the functions often, as virtually the
entire engine depends on these headers.
*/
/*
#include <iostream>
using namespace std;
*/
namespace ESM
{
void NPC::load(ESMReader &esm, const std::string& id)
{
mId = id;
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if(esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else hasAI = false;
esm.skipRecord();
}
void DialInfo::load(ESMReader &esm)
{
id = esm.getHNString("INAM");
prev = esm.getHNString("PNAM");
next = esm.getHNString("NNAM");
// Not present if deleted
if(esm.isNextSub("DATA"))
esm.getHT(data, 12);
// What follows is somewhat spaghetti-ish, but it's worth if for
// an extra speedup. INFO is by far the most common record type.
// subName is a reference to the original, so it changes whenever
// a new sub name is read. esm.isEmptyOrGetName() will get the
// next name for us, or return true if there are no more records.
esm.getSubName();
const NAME &subName = esm.retSubName();
if(subName.val == REC_ONAM)
{
actor = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_RNAM)
{
race = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_CNAM)
{
clas = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
factionLess = false;
if(subName.val == REC_FNAM)
{
npcFaction = esm.getHString();
if(npcFaction == "FFFF") factionLess = true;
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_ANAM)
{
cell = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_DNAM)
{
pcFaction = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_SNAM)
{
sound = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_NAME)
{
response = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
while(subName.val == REC_SCVR)
{
SelectStruct ss;
ss.selectRule = esm.getHString();
esm.isEmptyOrGetName();
if(subName.val == REC_INTV)
{
ss.type = VT_Int;
esm.getHT(ss.i);
}
else if(subName.val == REC_FLTV)
{
ss.type = VT_Float;
esm.getHT(ss.f);
}
else
esm.fail("INFO.SCVR must precede INTV or FLTV, not "
+ subName.toString());
selects.push_back(ss);
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_BNAM)
{
resultScript = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
questStatus = QS_None;
if (subName.val == REC_QSTN) questStatus = QS_Name;
else if(subName.val == REC_QSTF) questStatus = QS_Finished;
else if(subName.val == REC_QSTR) questStatus = QS_Restart;
else if(subName.val == REC_DELE) questStatus = QS_Deleted;
else
esm.fail("Don't know what to do with " + subName.toString() + " in INFO " + id);
if(questStatus != QS_None)
// Skip rest of record
esm.skipRecord();
}
void Sound::load(ESMReader &esm)
{
sound = esm.getHNString("FNAM");
esm.getHNT(data, "DATA", 3);
/*
cout << "vol=" << (int)data.volume
<< " min=" << (int)data.minRange
<< " max=" << (int)data.maxRange
<< endl;
*/
}
}

@ -0,0 +1,11 @@
#include "loadacti.hpp"
namespace ESM
{
void Activator::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
script = esm.getHNOString("SCRI");
}
}

@ -3,18 +3,14 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct Activator struct Activator
{ {
std::string name, script, model; std::string name, script, model;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
script = esm.getHNOString("SCRI");
}
}; };
} }
#endif #endif

@ -0,0 +1,14 @@
#include "loadalch.hpp"
namespace ESM
{
void Potion::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
script = esm.getHNOString("SCRI");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "ALDT", 12);
effects.load(esm);
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp" #include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Alchemy item (potions) * Alchemy item (potions)
@ -12,26 +13,18 @@ namespace ESM {
struct Potion struct Potion
{ {
struct ALDTstruct struct ALDTstruct
{ {
float weight; float weight;
int value; int value;
int autoCalc; int autoCalc;
}; };
ALDTstruct data; ALDTstruct data;
std::string name, model, icon, script; std::string name, model, icon, script;
EffectList effects; EffectList effects;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
script = esm.getHNOString("SCRI");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "ALDT", 12);
effects.load(esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,13 @@
#include "loadappa.hpp"
namespace ESM
{
void Apparatus::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "AADT", 16);
script = esm.getHNOString("SCRI");
icon = esm.getHNString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Alchemist apparatus * Alchemist apparatus
@ -11,33 +12,26 @@ namespace ESM {
struct Apparatus struct Apparatus
{ {
enum AppaType enum AppaType
{ {
MortarPestle = 0, MortarPestle = 0,
Albemic = 1, Albemic = 1,
Calcinator = 2, Calcinator = 2,
Retort = 3 Retort = 3
}; };
struct AADTstruct struct AADTstruct
{ {
int type; int type;
float quality; float quality;
float weight; float weight;
int value; int value;
}; };
AADTstruct data; AADTstruct data;
std::string model, icon, script, name; std::string model, icon, script, name;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "AADT", 16);
script = esm.getHNOString("SCRI");
icon = esm.getHNString("ITEX");
}
}; };
} }
#endif #endif

@ -0,0 +1,28 @@
#include "loadarmo.hpp"
namespace ESM
{
void PartReferenceList::load(ESMReader &esm)
{
while (esm.isNextSub("INDX"))
{
PartReference pr;
esm.getHT(pr.part); // The INDX byte
pr.male = esm.getHNOString("BNAM");
pr.female = esm.getHNOString("CNAM");
}
}
void Armor::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "AODT", 24);
icon = esm.getHNOString("ITEX");
parts.load(esm);
enchant = esm.getHNOString("ENAM");
}
}

@ -3,102 +3,85 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
enum PartReferenceType enum PartReferenceType
{ {
PRT_Head = 0, PRT_Head = 0,
PRT_Hair = 1, PRT_Hair = 1,
PRT_Neck = 2, PRT_Neck = 2,
PRT_Cuirass = 3, PRT_Cuirass = 3,
PRT_Groin = 4, PRT_Groin = 4,
PRT_Skirt = 5, PRT_Skirt = 5,
PRT_RHand = 6, PRT_RHand = 6,
PRT_LHand = 7, PRT_LHand = 7,
PRT_RWrist = 8, PRT_RWrist = 8,
PRT_LWrist = 9, PRT_LWrist = 9,
PRT_Shield = 10, PRT_Shield = 10,
PRT_RForearm = 11, PRT_RForearm = 11,
PRT_LForearm = 12, PRT_LForearm = 12,
PRT_RUpperarm = 13, PRT_RUpperarm = 13,
PRT_LUpperarm = 14, PRT_LUpperarm = 14,
PRT_RFoot = 15, PRT_RFoot = 15,
PRT_LFoot = 16, PRT_LFoot = 16,
PRT_RAnkle = 17, PRT_RAnkle = 17,
PRT_LAnkle = 18, PRT_LAnkle = 18,
PRT_RKnee = 19, PRT_RKnee = 19,
PRT_LKnee = 20, PRT_LKnee = 20,
PRT_RLeg = 21, PRT_RLeg = 21,
PRT_LLeg = 22, PRT_LLeg = 22,
PRT_RPauldron = 23, PRT_RPauldron = 23,
PRT_LPauldron = 24, PRT_LPauldron = 24,
PRT_Weapon = 25, PRT_Weapon = 25,
PRT_Tail = 26 PRT_Tail = 26
}; };
// Reference to body parts // Reference to body parts
struct PartReference struct PartReference
{ {
char part; char part;
std::string male, female; std::string male, female;
}; };
// A list of references to body parts // A list of references to body parts
struct PartReferenceList struct PartReferenceList
{ {
std::vector<PartReference> parts; std::vector<PartReference> parts;
void load(ESMReader &esm) void load(ESMReader &esm);
{
while(esm.isNextSub("INDX"))
{
PartReference pr;
esm.getHT(pr.part); // The INDX byte
pr.male = esm.getHNOString("BNAM");
pr.female = esm.getHNOString("CNAM");
}
}
}; };
struct Armor struct Armor
{ {
enum Type enum Type
{ {
Helmet = 0, Helmet = 0,
Cuirass = 1, Cuirass = 1,
LPauldron = 2, LPauldron = 2,
RPauldron = 3, RPauldron = 3,
Greaves = 4, Greaves = 4,
Boots = 5, Boots = 5,
LGauntlet = 6, LGauntlet = 6,
RGauntlet = 7, RGauntlet = 7,
Shield = 8, Shield = 8,
LBracer = 9, LBracer = 9,
RBracer = 10 RBracer = 10
}; };
struct AODTstruct struct AODTstruct
{ {
int type; int type;
float weight; float weight;
int value, health, enchant, armor; int value, health, enchant, armor;
}; };
AODTstruct data; AODTstruct data;
PartReferenceList parts; PartReferenceList parts;
std::string name, model, icon, script, enchant; std::string name, model, icon, script, enchant;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "AODT", 24);
icon = esm.getHNOString("ITEX");
parts.load(esm);
enchant = esm.getHNOString("ENAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,13 @@
#include "loadbody.hpp"
namespace ESM
{
void BodyPart::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "BYDT", 4);
}
}

@ -3,59 +3,55 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct BodyPart struct BodyPart
{ {
enum MeshPart enum MeshPart
{ {
MP_Head = 0, MP_Head = 0,
MP_Hair = 1, MP_Hair = 1,
MP_Neck = 2, MP_Neck = 2,
MP_Chest = 3, MP_Chest = 3,
MP_Groin = 4, MP_Groin = 4,
MP_Hand = 5, MP_Hand = 5,
MP_Wrist = 6, MP_Wrist = 6,
MP_Forearm = 7, MP_Forearm = 7,
MP_Upperarm = 8, MP_Upperarm = 8,
MP_Foot = 9, MP_Foot = 9,
MP_Ankle = 10, MP_Ankle = 10,
MP_Knee = 11, MP_Knee = 11,
MP_Upperleg = 12, MP_Upperleg = 12,
MP_Clavicle = 13, MP_Clavicle = 13,
MP_Tail = 14 MP_Tail = 14
}; };
enum Flags enum Flags
{ {
BPF_Female = 1, BPF_Female = 1,
BPF_Playable = 2 BPF_Playable = 2
}; };
enum MeshType enum MeshType
{ {
MT_Skin = 0, MT_Skin = 0,
MT_Clothing = 1, MT_Clothing = 1,
MT_Armor = 2 MT_Armor = 2
}; };
struct BYDTstruct struct BYDTstruct
{ {
char part; char part;
char vampire; char vampire;
char flags; char flags;
char type; char type;
}; };
BYDTstruct data; BYDTstruct data;
std::string model, name; std::string model, name;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "BYDT", 4);
}
}; };
} }
#endif #endif

@ -0,0 +1,17 @@
#include "loadbook.hpp"
namespace ESM
{
void Book::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "BKDT", 20);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
text = esm.getHNOString("TEXT");
enchant = esm.getHNOString("ENAM");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Books, magic scrolls, notes and so on * Books, magic scrolls, notes and so on
@ -11,25 +12,16 @@ namespace ESM {
struct Book struct Book
{ {
struct BKDTstruct struct BKDTstruct
{ {
float weight; float weight;
int value, isScroll, skillID, enchant; int value, isScroll, skillID, enchant;
}; };
BKDTstruct data; BKDTstruct data;
std::string name, model, icon, script, enchant, text; std::string name, model, icon, script, enchant, text;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "BKDT", 20);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
text = esm.getHNOString("TEXT");
enchant = esm.getHNOString("ENAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,15 @@
#include "loadbsgn.hpp"
namespace ESM
{
void BirthSign::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
texture = esm.getHNOString("TNAM");
description = esm.getHNOString("DESC");
powers.load(esm);
}
}

@ -4,23 +4,17 @@
#include "defs.hpp" #include "defs.hpp"
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct BirthSign struct BirthSign
{ {
std::string name, description, texture; std::string name, description, texture;
// List of powers and abilities that come with this birth sign.
SpellList powers;
void load(ESMReader &esm) // List of powers and abilities that come with this birth sign.
{ SpellList powers;
name = esm.getHNString("FNAM");
texture = esm.getHNOString("TNAM");
description = esm.getHNOString("DESC");
powers.load(esm); void load(ESMReader &esm);
};
}; };
} }
#endif #endif

@ -0,0 +1,113 @@
#include "loadcell.hpp"
namespace ESM
{
void Cell::load(ESMReader &esm)
{
// Ignore this for now, it might mean we should delete the entire
// cell?
if (esm.isNextSub("DELE"))
esm.skipHSub();
esm.getHNT(data, "DATA", 12);
// Water level
water = 0;
if (data.flags & Interior)
{
// Interior cells
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
esm.getHT(water);
// Quasi-exterior cells have a region (which determines the
// weather), pure interior cells have ambient lighting
// instead.
if (data.flags & QuasiEx)
region = esm.getHNOString("RGNN");
else
esm.getHNT(ambi, "AMBI", 16);
}
else
{
// Exterior cells
region = esm.getHNOString("RGNN");
esm.getHNOT(mapColor, "NAM5");
}
// Save position of the cell references and move on
context = esm.getContext();
esm.skipRecord();
}
void Cell::restore(ESMReader &esm) const
{
esm.restoreContext(context);
}
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
{
if (!esm.hasMoreSubs())
return false;
// Number of references in the cell? Maximum once in each cell,
// but not always at the beginning, and not always right. In other
// words, completely useless.
{
int i;
esm.getHNOT(i, "NAM0");
}
esm.getHNT(ref.refnum, "FRMR");
ref.refID = esm.getHNString("NAME");
// getHNOT will not change the existing value if the subrecord is
// missing
ref.scale = 1.0;
esm.getHNOT(ref.scale, "XSCL");
ref.owner = esm.getHNOString("ANAM");
ref.glob = esm.getHNOString("BNAM");
ref.soul = esm.getHNOString("XSOL");
ref.faction = esm.getHNOString("CNAM");
ref.factIndex = -1;
esm.getHNOT(ref.factIndex, "INDX");
ref.charge = -1.0;
esm.getHNOT(ref.charge, "XCHG");
ref.intv = 0;
ref.nam9 = 0;
esm.getHNOT(ref.intv, "INTV");
esm.getHNOT(ref.nam9, "NAM9");
// Present for doors that teleport you to another cell.
if (esm.isNextSub("DODT"))
{
ref.teleport = true;
esm.getHT(ref.doorDest);
ref.destCell = esm.getHNOString("DNAM");
}
else
ref.teleport = false;
// Integer, despite the name suggesting otherwise
ref.lockLevel = 0;
esm.getHNOT(ref.lockLevel, "FLTV");
ref.key = esm.getHNOString("KNAM");
ref.trap = esm.getHNOString("TNAM");
ref.unam = 0;
ref.fltv = 0;
esm.getHNOT(ref.unam, "UNAM");
esm.getHNOT(ref.fltv, "FLTV");
esm.getHNT(ref.pos, "DATA", 24);
return true;
}
}

@ -117,51 +117,14 @@ struct Cell
int water; // Water level int water; // Water level
int mapColor; int mapColor;
void load(ESMReader &esm) void load(ESMReader &esm);
{
// Ignore this for now, it might mean we should delete the entire
// cell?
if(esm.isNextSub("DELE")) esm.skipHSub();
esm.getHNT(data, "DATA", 12);
// Water level
water = 0;
if(data.flags & Interior)
{
// Interior cells
if(esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
esm.getHT(water);
// Quasi-exterior cells have a region (which determines the
// weather), pure interior cells have ambient lighting
// instead.
if(data.flags & QuasiEx)
region = esm.getHNOString("RGNN");
else
esm.getHNT(ambi, "AMBI", 16);
}
else
{
// Exterior cells
region = esm.getHNOString("RGNN");
esm.getHNOT(mapColor, "NAM5");
}
// Save position of the cell references and move on
context = esm.getContext();
esm.skipRecord();
}
// Restore the given reader to the stored position. Will try to open // Restore the given reader to the stored position. Will try to open
// the file matching the stored file name. If you want to read from // the file matching the stored file name. If you want to read from
// somewhere other than the file system, you need to pre-open the // somewhere other than the file system, you need to pre-open the
// ESMReader, and the filename must match the stored filename // ESMReader, and the filename must match the stored filename
// exactly. // exactly.
void restore(ESMReader &esm) const void restore(ESMReader &esm) const;
{ esm.restoreContext(context); }
/* Get the next reference in this cell, if any. Returns false when /* Get the next reference in this cell, if any. Returns false when
there are no more references in the cell. there are no more references in the cell.
@ -169,66 +132,7 @@ struct Cell
All fields of the CellRef struct are overwritten. You can safely All fields of the CellRef struct are overwritten. You can safely
reuse one memory location without blanking it between calls. reuse one memory location without blanking it between calls.
*/ */
static bool getNextRef(ESMReader &esm, CellRef &ref) static bool getNextRef(ESMReader &esm, CellRef &ref);
{
if(!esm.hasMoreSubs()) return false;
// Number of references in the cell? Maximum once in each cell,
// but not always at the beginning, and not always right. In other
// words, completely useless.
{
int i;
esm.getHNOT(i, "NAM0");
}
esm.getHNT(ref.refnum, "FRMR");
ref.refID = esm.getHNString("NAME");
// getHNOT will not change the existing value if the subrecord is
// missing
ref.scale = 1.0;
esm.getHNOT(ref.scale, "XSCL");
ref.owner = esm.getHNOString("ANAM");
ref.glob = esm.getHNOString("BNAM");
ref.soul = esm.getHNOString("XSOL");
ref.faction = esm.getHNOString("CNAM");
ref.factIndex = -1;
esm.getHNOT(ref.factIndex, "INDX");
ref.charge = -1.0;
esm.getHNOT(ref.charge, "XCHG");
ref.intv = 0;
ref.nam9 = 0;
esm.getHNOT(ref.intv, "INTV");
esm.getHNOT(ref.nam9, "NAM9");
// Present for doors that teleport you to another cell.
if(esm.isNextSub("DODT"))
{
ref.teleport = true;
esm.getHT(ref.doorDest);
ref.destCell = esm.getHNOString("DNAM");
}
else ref.teleport = false;
// Integer, despite the name suggesting otherwise
ref.lockLevel = 0;
esm.getHNOT(ref.lockLevel, "FLTV");
ref.key = esm.getHNOString("KNAM");
ref.trap = esm.getHNOString("TNAM");
ref.unam = 0;
ref.fltv = 0;
esm.getHNOT(ref.unam, "UNAM");
esm.getHNOT(ref.fltv, "FLTV");
esm.getHNT(ref.pos, "DATA", 24);
return true;
}
}; };
} }
#endif #endif

@ -1,6 +1,7 @@
#include "loadclas.hpp" #include "loadclas.hpp"
using namespace ESM; namespace ESM
{
const Class::Specialization Class::specializationIds[3] = { const Class::Specialization Class::specializationIds[3] = {
Class::Combat, Class::Combat,
@ -13,3 +14,16 @@ const char *Class::gmstSpecializationIds[3] = {
"sSpecializationMagic", "sSpecializationMagic",
"sSpecializationStealth" "sSpecializationStealth"
}; };
void Class::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
esm.getHNT(data, "CLDT", 60);
if (data.isPlayable > 1)
esm.fail("Unknown bool value");
description = esm.getHNOString("DESC");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Character class definitions * Character class definitions
@ -13,62 +14,53 @@ namespace ESM {
// class // class
struct Class struct Class
{ {
enum AutoCalc enum AutoCalc
{ {
Weapon = 0x00001, Weapon = 0x00001,
Armor = 0x00002, Armor = 0x00002,
Clothing = 0x00004, Clothing = 0x00004,
Books = 0x00008, Books = 0x00008,
Ingredient = 0x00010, Ingredient = 0x00010,
Lockpick = 0x00020, Lockpick = 0x00020,
Probe = 0x00040, Probe = 0x00040,
Lights = 0x00080, Lights = 0x00080,
Apparatus = 0x00100, Apparatus = 0x00100,
Repair = 0x00200, Repair = 0x00200,
Misc = 0x00400, Misc = 0x00400,
Spells = 0x00800, Spells = 0x00800,
MagicItems = 0x01000, MagicItems = 0x01000,
Potions = 0x02000, Potions = 0x02000,
Training = 0x04000, Training = 0x04000,
Spellmaking = 0x08000, Spellmaking = 0x08000,
Enchanting = 0x10000, Enchanting = 0x10000,
RepairItem = 0x20000 RepairItem = 0x20000
}; };
enum Specialization enum Specialization
{ {
Combat = 0, Combat = 0,
Magic = 1, Magic = 1,
Stealth = 2 Stealth = 2
}; };
static const Specialization specializationIds[3]; static const Specialization specializationIds[3];
static const char *gmstSpecializationIds[3]; static const char *gmstSpecializationIds[3];
struct CLDTstruct
{
int attribute[2]; // Attributes that get class bonus
int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
int skills[5][2]; // Minor and major skills.
int isPlayable; // 0x0001 - Playable class
// I have no idea how to autocalculate these items...
int calc;
}; // 60 bytes
std::string name, description; struct CLDTstruct
CLDTstruct data;
void load(ESMReader &esm)
{ {
name = esm.getHNString("FNAM"); int attribute[2]; // Attributes that get class bonus
esm.getHNT(data, "CLDT", 60); int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
int skills[5][2]; // Minor and major skills.
int isPlayable; // 0x0001 - Playable class
// I have no idea how to autocalculate these items...
int calc;
}; // 60 bytes
if(data.isPlayable > 1) std::string name, description;
esm.fail("Unknown bool value"); CLDTstruct data;
description = esm.getHNOString("DESC"); void load(ESMReader &esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,20 @@
#include "loadclot.hpp"
namespace ESM
{
void Clothing::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "CTDT", 12);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
parts.load(esm);
enchant = esm.getHNOString("ENAM");
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "loadarmo.hpp" #include "loadarmo.hpp"
namespace ESM { namespace ESM
{
/* /*
* Clothing * Clothing
@ -12,46 +13,34 @@ namespace ESM {
struct Clothing struct Clothing
{ {
enum Type enum Type
{ {
Pants = 0, Pants = 0,
Shoes = 1, Shoes = 1,
Shirt = 2, Shirt = 2,
Belt = 3, Belt = 3,
Robe = 4, Robe = 4,
RGlove = 5, RGlove = 5,
LGlove = 6, LGlove = 6,
Skirt = 7, Skirt = 7,
Ring = 8, Ring = 8,
Amulet = 9 Amulet = 9
}; };
struct CTDTstruct struct CTDTstruct
{ {
int type; int type;
float weight; float weight;
short value; short value;
short enchant; short enchant;
}; };
CTDTstruct data; CTDTstruct data;
PartReferenceList parts;
std::string name, model, icon, enchant, script;
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "CTDT", 12);
script = esm.getHNOString("SCRI"); PartReferenceList parts;
icon = esm.getHNOString("ITEX");
parts.load(esm); std::string name, model, icon, enchant, script;
enchant = esm.getHNOString("ENAM"); void load(ESMReader &esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,33 @@
#include "loadcont.hpp"
namespace ESM
{
void InventoryList::load(ESMReader &esm)
{
ContItem ci;
while (esm.isNextSub("NPCO"))
{
esm.getHT(ci, 36);
list.push_back(ci);
}
}
void Container::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(weight, "CNDT", 4);
esm.getHNT(flags, "FLAG", 4);
if (flags & 0xf4)
esm.fail("Unknown flags");
if (!(flags & 0x8))
esm.fail("Flag 8 not set");
script = esm.getHNOString("SCRI");
inventory.load(esm);
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Container definition * Container definition
@ -11,54 +12,33 @@ namespace ESM {
struct ContItem struct ContItem
{ {
int count; int count;
NAME32 item; NAME32 item;
}; };
struct InventoryList struct InventoryList
{ {
std::vector<ContItem> list; std::vector<ContItem> list;
void load(ESMReader &esm) void load(ESMReader &esm);
{
ContItem ci;
while(esm.isNextSub("NPCO"))
{
esm.getHT(ci, 36);
list.push_back(ci);
}
}
}; };
struct Container struct Container
{ {
enum Flags enum Flags
{ {
Organic = 1, // Objects cannot be placed in this container Organic = 1, // Objects cannot be placed in this container
Respawn = 2, // Respawns after 4 months Respawn = 2, // Respawns after 4 months
Unknown = 8 Unknown = 8
}; };
std::string name, model, script; std::string name, model, script;
float weight; // Not sure, might be max total weight allowed?
int flags;
InventoryList inventory;
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(weight, "CNDT", 4);
esm.getHNT(flags, "FLAG", 4);
if(flags & 0xf4) esm.fail("Unknown flags");
if(!(flags & 0x8)) esm.fail("Flag 8 not set");
script = esm.getHNOString("SCRI"); float weight; // Not sure, might be max total weight allowed?
int flags;
InventoryList inventory;
inventory.load(esm); void load(ESMReader &esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,40 @@
#include "loadcrea.hpp"
namespace ESM {
void Creature::load(ESMReader &esm, const std::string& id)
{
mId = id;
model = esm.getHNString("MODL");
original = esm.getHNOString("CNAM");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "NPDT", 96);
esm.getHNT(flags, "FLAG");
scale = 1.0;
esm.getHNOT(scale, "XSCL");
inventory.load(esm);
// More subrecords:
// AIDT - data (12 bytes, unknown)
// AI_W - wander (14 bytes, i don't understand it)
// short distance
// byte duration
// byte timeOfDay
// byte idle[10]
//
// Rest is optional:
// AI_T - travel?
// AI_F - follow?
// AI_E - escort?
// AI_A - activate?
esm.skipRecord();
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "loadcont.hpp" #include "loadcont.hpp"
namespace ESM { namespace ESM
{
/* /*
* Creature definition * Creature definition
@ -13,91 +14,56 @@ namespace ESM {
struct Creature struct Creature
{ {
// Default is 0x48? // Default is 0x48?
enum Flags enum Flags
{ {
Biped = 0x001, Biped = 0x001, Respawn = 0x002, Weapon = 0x004, // Has weapon and shield
Respawn = 0x002, None = 0x008, // ??
Weapon = 0x004, // Has weapon and shield Swims = 0x010,
None = 0x008, // ?? Flies = 0x020, // Don't know what happens if several
Swims = 0x010, Walks = 0x040, // of these are set
Flies = 0x020, // Don't know what happens if several Essential = 0x080,
Walks = 0x040, // of these are set Skeleton = 0x400, // Does not have normal blood
Essential = 0x080, Metal = 0x800
Skeleton = 0x400, // Does not have normal blood // Has 'golden' blood
Metal = 0x800 // Has 'golden' blood
}; };
enum Type enum Type
{ {
Creatures = 0, Creatures = 0,
Deadra = 1, Deadra = 1,
Undead = 2, Undead = 2,
Humanoid = 3 Humanoid = 3
}; };
struct NPDTstruct struct NPDTstruct
{ {
int type; int type;
// For creatures we obviously have to use ints, not shorts and // For creatures we obviously have to use ints, not shorts and
// bytes like we use for NPCs.... this file format just makes so // bytes like we use for NPCs.... this file format just makes so
// much sense! (Still, _much_ easier to decode than the NIFs.) // much sense! (Still, _much_ easier to decode than the NIFs.)
int level; int level;
int strength, intelligence, willpower, agility, speed, endurance, int strength, intelligence, willpower, agility, speed, endurance,
personality, luck, health, mana, fatigue; // Stats personality, luck, health, mana, fatigue; // Stats
int soul; // The creatures soul value (used with soul gems.) int soul; // The creatures soul value (used with soul gems.)
int combat, magic, stealth; // Don't know yet. int combat, magic, stealth; // Don't know yet.
int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3
int gold; int gold;
}; // 96 bytes }; // 96 bytes
NPDTstruct data; NPDTstruct data;
int flags; int flags;
float scale; float scale;
std::string model, name, script, std::string model, name, script, original; // Base creature that this is a modification of
original; // Base creature that this is a modification of
// Defined in loadcont.hpp // Defined in loadcont.hpp
InventoryList inventory; InventoryList inventory;
std::string mId; std::string mId;
void load(ESMReader &esm, const std::string& id) void load(ESMReader &esm, const std::string& id);
{
mId = id;
model = esm.getHNString("MODL");
original = esm.getHNOString("CNAM");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "NPDT", 96);
esm.getHNT(flags, "FLAG");
scale = 1.0;
esm.getHNOT(scale, "XSCL");
inventory.load(esm);
// More subrecords:
// AIDT - data (12 bytes, unknown)
// AI_W - wander (14 bytes, i don't understand it)
// short distance
// byte duration
// byte timeOfDay
// byte idle[10]
//
// Rest is optional:
// AI_T - travel?
// AI_F - follow?
// AI_E - escort?
// AI_A - activate?
esm.skipRecord();
}
}; };
} }
#endif #endif

@ -0,0 +1,25 @@
#include "loaddial.hpp"
namespace ESM
{
void Dialogue::load(ESMReader &esm)
{
esm.getSubNameIs("DATA");
esm.getSubHeader();
int si = esm.getSubSize();
if (si == 1)
esm.getT(type);
else if (si == 4)
{
// These are just markers, their values are not used.
int i;
esm.getT(i);
esm.getHNT(i, "DELE");
type = Deleted;
}
else
esm.fail("Unknown sub record size");
}
}

@ -6,7 +6,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "loadinfo.hpp" #include "loadinfo.hpp"
namespace ESM { namespace ESM
{
/* /*
* Dialogue topic and journal entries. The actual data is contained in * Dialogue topic and journal entries. The actual data is contained in
@ -15,36 +16,20 @@ namespace ESM {
struct Dialogue struct Dialogue
{ {
enum Type enum Type
{ {
Topic = 0, Topic = 0,
Voice = 1, Voice = 1,
Greeting = 2, Greeting = 2,
Persuasion = 3, Persuasion = 3,
Journal = 4, Journal = 4,
Deleted = -1 Deleted = -1
}; };
char type; char type;
std::vector<DialInfo> mInfo; std::vector<DialInfo> mInfo;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getSubNameIs("DATA");
esm.getSubHeader();
int si = esm.getSubSize();
if(si == 1)
esm.getT(type);
else if(si == 4)
{
// These are just markers, their values are not used.
int i;
esm.getT(i);
esm.getHNT(i,"DELE");
type = Deleted;
}
else esm.fail("Unknown sub record size");
}
}; };
} }
#endif #endif

@ -0,0 +1,15 @@
#include "loaddoor.hpp"
namespace ESM
{
void Door::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
openSound = esm.getHNOString("SNAM");
closeSound = esm.getHNOString("ANAM");
}
}

@ -3,20 +3,14 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct Door struct Door
{ {
std::string name, model, script, openSound, closeSound; std::string name, model, script, openSound, closeSound;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
openSound = esm.getHNOString("SNAM");
closeSound = esm.getHNOString("ANAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,12 @@
#include "loadench.hpp"
namespace ESM
{
void Enchantment::load(ESMReader &esm)
{
esm.getHNT(data, "ENDT", 16);
effects.load(esm);
}
}

@ -2,8 +2,10 @@
#define _ESM_ENCH_H #define _ESM_ENCH_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Enchantments * Enchantments
@ -11,31 +13,27 @@ namespace ESM {
struct Enchantment struct Enchantment
{ {
enum Type enum Type
{ {
CastOnce = 0, CastOnce = 0,
WhenStrikes = 1, WhenStrikes = 1,
WhenUsed = 2, WhenUsed = 2,
ConstantEffect = 3 ConstantEffect = 3
}; };
struct ENDTstruct struct ENDTstruct
{ {
int type; int type;
int cost; int cost;
int charge; int charge;
int autocalc; // Guessing this is 1 if we are supposed to auto int autocalc; // Guessing this is 1 if we are supposed to auto
// calculate // calculate
}; };
ENDTstruct data; ENDTstruct data;
EffectList effects; EffectList effects;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(data, "ENDT", 16);
effects.load(esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,31 @@
#include "loadfact.hpp"
namespace ESM
{
void Faction::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
// Read rank names. These are optional.
int i = 0;
while (esm.isNextSub("RNAM") && i < 10)
ranks[i++] = esm.getHString();
// Main data struct
esm.getHNT(data, "FADT", 240);
if (data.isHidden > 1)
esm.fail("Unknown flag!");
// Read faction response values
while (esm.hasMoreSubs())
{
Reaction r;
r.faction = esm.getHNString("ANAM");
esm.getHNT(r.reaction, "INTV");
reactions.push_back(r);
}
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Faction definitions * Faction definitions
@ -12,67 +13,46 @@ namespace ESM {
// Requirements for each rank // Requirements for each rank
struct RankData struct RankData
{ {
int attribute1, attribute2; // Attribute level int attribute1, attribute2; // Attribute level
int skill1, skill2; // Skill level (faction skills given in int skill1, skill2; // Skill level (faction skills given in
// skillID below.) You need one skill at // skillID below.) You need one skill at
// level 'skill1' and two skills at level // level 'skill1' and two skills at level
// 'skill2' to advance to this rank. // 'skill2' to advance to this rank.
int factReaction; // Reaction from faction members int factReaction; // Reaction from faction members
}; };
struct Faction struct Faction
{ {
std::string id, name; std::string id, name;
struct FADTstruct
{
// Which attributes we like
int attribute1, attribute2;
RankData rankData[10];
int skillID[6]; // IDs of skills this faction require
int unknown; // Always -1?
int isHidden; // 1 - hidden from player
}; // 240 bytes
FADTstruct data;
struct Reaction struct FADTstruct
{ {
std::string faction; // Which attributes we like
int reaction; int attribute1, attribute2;
};
std::vector<Reaction> reactions; RankData rankData[10];
// Name of faction ranks (may be empty for NPC factions) int skillID[6]; // IDs of skills this faction require
std::string ranks[10]; int unknown; // Always -1?
int isHidden; // 1 - hidden from player
}; // 240 bytes
void load(ESMReader &esm) FADTstruct data;
{
name = esm.getHNString("FNAM");
// Read rank names. These are optional. struct Reaction
int i = 0; {
while(esm.isNextSub("RNAM") && i<10) ranks[i++] = esm.getHString(); std::string faction;
int reaction;
};
// Main data struct std::vector<Reaction> reactions;
esm.getHNT(data, "FADT", 240);
if(data.isHidden > 1) esm.fail("Unknown flag!"); // Name of faction ranks (may be empty for NPC factions)
std::string ranks[10];
// Read faction response values void load(ESMReader &esm);
while(esm.hasMoreSubs())
{
Reaction r;
r.faction = esm.getHNString("ANAM");
esm.getHNT(r.reaction, "INTV");
reactions.push_back(r);
}
}
}; };
} }
#endif #endif

@ -0,0 +1,24 @@
#include "loadglob.hpp"
namespace ESM
{
void Global::load(ESMReader &esm)
{
VarType t;
std::string tmp = esm.getHNString("FNAM");
if (tmp == "s")
t = VT_Short;
else if (tmp == "l")
t = VT_Int;
else if (tmp == "f")
t = VT_Float;
else
esm.fail("Illegal global variable type " + tmp);
type = t;
// Note: Both floats and longs are represented as floats.
esm.getHNT(value, "FLTV");
}
}

@ -2,8 +2,10 @@
#define _ESM_GLOB_H #define _ESM_GLOB_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Global script variables * Global script variables
@ -11,22 +13,10 @@ namespace ESM {
struct Global struct Global
{ {
unsigned value; unsigned value;
VarType type; VarType type;
void load(ESMReader &esm)
{
VarType t;
std::string tmp = esm.getHNString("FNAM");
if(tmp == "s") t = VT_Short;
else if(tmp == "l") t = VT_Int;
else if(tmp == "f") t = VT_Float;
else esm.fail("Illegal global variable type " + tmp);
type = t;
// Note: Both floats and longs are represented as floats. void load(ESMReader &esm);
esm.getHNT(value, "FLTV");
}
}; };
} }
#endif #endif

@ -0,0 +1,172 @@
#include "loadgmst.hpp"
namespace ESM
{
// Some handy macros
#define cI(s,x) { if(id == (s)) return (i == (x)); }
#define cF(s,x) { if(id == (s)) return (f == (x)); }
#define cS(s,x) { if(id == (s)) return (str == (x)); }
bool GameSetting::isDirtyTribunal()
{
/*
Here, id contains the game setting name, and we check the
setting for certain values. If it matches, this is a "dirty"
entry. The correct entry (as defined in Tribunal and Bloodmoon
esms) are given in the comments. Many of the values are correct,
and are marked as 'same'. We still ignore them though, as they
are still in the wrong file and might override custom values
from other mods.
*/
// Strings
cS("sProfitValue", "Profit Value"); // 'Profit:'
cS("sEditNote", "Edit Note"); // same
cS("sDeleteNote", "Delete Note?"); // same
cS("sMaxSale", "Max Sale"); // 'Seller Max'
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon'
cS("sTeleportDisabled",
"Teleportation magic does not work here.");// same
cS("sLevitateDisabled",
"Levitation magic does not work here."); // same
cS("sCompanionShare", "Companion Share"); // 'Share'
cS("sCompanionWarningButtonOne",
"Let the mercenary quit."); // same
cS("sCompanionWarningButtonTwo",
"Return to Companion Share display."); // same
cS("sCompanionWarningMessage",
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value.");
// 'Your mercenary is poorer now than when he contracted with
// you. Your mercenary will quit if you do not give him gold
// or goods to bring his Profit to a positive value.'
// [The difference here is "Profit Value" -> "Profit"]
// Strings that matches the id
cS("sEffectSummonFabricant", id);// 'Summon Fabricant'
return false;
}
// Bloodmoon variant
bool GameSetting::isDirtyBloodmoon()
{
// Strings
cS("sWerewolfPopup", "Werewolf"); // same
cS("sWerewolfRestMessage",
"You cannot rest in werewolf form."); // same
cS("sWerewolfRefusal",
"You cannot do this as a werewolf."); // same
cS("sWerewolfAlarmMessage",
"You have been detected changing from a werewolf state.");
// 'You have been detected as a known werewolf.'
// Strings that matches the id
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon'
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon'
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
cS("sMagicCreature04ID", id); // same
cS("sMagicCreature05ID", id); // same
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
cS("sEffectSummonCreature02", id); // 'Calf Bear'
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
cS("sEffectSummonCreature04", id); // same
cS("sEffectSummonCreature05", id); // same
// Integers
cI("iWereWolfBounty", 10000); // 1000
cI("iWereWolfFightMod", 100); // same
cI("iWereWolfFleeMod", 100); // same
cI("iWereWolfLevelToAttack", 20); // same
// Floats
cF("fFleeDistance", 3000); // same
cF("fCombatDistanceWerewolfMod", 0.3); // same
cF("fWereWolfFatigue", 400); // same
cF("fWereWolfEnchant", 1); // 0
cF("fWereWolfArmorer", 1); // 0
cF("fWereWolfBlock", 1); // 0
cF("fWereWolfSneak", 1); // 95
cF("fWereWolfDestruction", 1); // 0
cF("fWereWolfEndurance", 150); // same
cF("fWereWolfConjuration", 1); // 0
cF("fWereWolfRestoration", 1); // 0
cF("fWereWolfAthletics", 150); // 50
cF("fWereWolfLuck", 1); // 25
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
cF("fWereWolfMediumArmor", 1); // 0
cF("fWereWolfShortBlade", 1); // 0
cF("fWereWolfAcrobatics", 150); // 80
cF("fWereWolfSpeechcraft", 1); // 0
cF("fWereWolfAlteration", 1); // 0
cF("fWereWolfIllusion", 1); // 0
cF("fWereWolfLongBlade", 1); // 0
cF("fWereWolfMarksman", 1); // 0
cF("fWereWolfHandtoHand", 100); // same
cF("fWereWolfIntellegence", 1); // 0
cF("fWereWolfAlchemy", 1); // 0
cF("fWereWolfUnarmored", 100); // same
cF("fWereWolfAxe", 1); // 0
cF("fWereWolfRunMult", 1.5); // 1.3
cF("fWereWolfMagicka", 100); // same
cF("fWereWolfAgility", 150); // same
cF("fWereWolfBluntWeapon", 1); // 0
cF("fWereWolfSecurity", 1); // 0
cF("fWereWolfPersonality", 1); // 0
cF("fWereWolfMerchantile", 1); // 0
cF("fWereWolfHeavyArmor", 1); // 0
cF("fWereWolfSpear", 1); // 0
cF("fWereWolfStrength", 150); // same
cF("fWereWolfHealth", 2); // same
cF("fWereWolfMysticism", 1); // 0
cF("fWereWolfLightArmor", 1); // 0
cF("fWereWolfWillPower", 1); // 0
cF("fWereWolfSpeed", 150); // 90
return false;
}
void GameSetting::load(ESMReader &esm)
{
assert(id != "");
dirty = false;
// We are apparently allowed to be empty
if (!esm.hasMoreSubs())
{
type = VT_None;
return;
}
// Load some data
esm.getSubName();
NAME n = esm.retSubName();
if (n == "STRV")
{
str = esm.getHString();
type = VT_String;
}
else if (n == "INTV")
{
esm.getHT(i);
type = VT_Int;
}
else if (n == "FLTV")
{
esm.getHT(f);
type = VT_Float;
}
else
esm.fail("Unwanted subrecord type");
int spf = esm.getSpecial();
// Check if this is one of the dirty values mentioned above. If it
// is, we set the dirty flag. This will ONLY work if you've set
// the 'id' string correctly before calling load().
if ((spf != SF_Tribunal && isDirtyTribunal()) || (spf != SF_Bloodmoon
&& isDirtyBloodmoon()))
dirty = true;
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp" #include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Game setting, with automatic cleaning of "dirty" entries. * Game setting, with automatic cleaning of "dirty" entries.
@ -13,238 +14,75 @@ namespace ESM {
struct GameSetting struct GameSetting
{ {
std::string id; std::string id;
// One of these is used depending on the variable type // One of these is used depending on the variable type
std::string str; std::string str;
int i; int i;
float f; float f;
VarType type; VarType type;
// Set to true if this is a 'dirty' entry which should be ignored // Set to true if this is a 'dirty' entry which should be ignored
bool dirty; bool dirty;
/*
These functions check if this game setting is one of the "dirty"
GMST records found in many mods. These are due to a serious bug in
the official TES3 editor. It only occurs in the newer editor
versions that came with Tribunal and Bloodmoon, and only if a
modder tries to make a mod without loading the corresponding
expansion master file. For example, if you have Tribunal installed
and try to make a mod without loading Tribunal.esm, the editor
will insert these GMST records as a replacement for the entries it
cannot find in the ESMs.
The values of these "dirty" records differ in general from their
values as defined in Tribunal.esm and Bloodmoon.esm, and are
always set to the same "default" values. Most of these values are
nonsensical, ie. changing the "Seller Max" string to "Max Sale",
or change the stats of werewolves to useless values like 1. Some
of them break certain spell effects.
It is most likely that these values are just leftover values from
an early stage of development that are inserted as default values
by the editor code. They are supposed to be overridden when the
correct esm file is loaded. When it isn't loaded however, you get
stuck with the initial value, and this gets written to every mod
by the editor, for some reason.
Bethesda themselves have fallen for this bug. If you install both
Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
some of the dirty settings from Tribunal. In other words, this bug
affects the game EVEN IF YOU DO NOT USE ANY MODS!
The guys at Bethesda are well aware of this bug (and many others),
as the mod community and fan base complained about them for a long
time. But unfortunately it was never fixed.
There are several tools available to help modders remove these
records from their files, but not all modders use them, and they
really shouldn't have to. In this file we choose instead to reject
all the corrupt values at load time.
These functions checks if the current game setting is one of the
"dirty" ones as described above. TODO: I have not checked this
against other sources yet, do that later. Currently recognizes 22
values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
(setting values other than the default "dirty" ones) are not
affected and will work correctly.
*/
// Some handy macros
#define cI(s,x) { if(id == (s)) return (i == (x)); }
#define cF(s,x) { if(id == (s)) return (f == (x)); }
#define cS(s,x) { if(id == (s)) return (str == (x)); }
/*
Checks for dirty tribunal values. These will be ignored if found
in any file except when they are found in "Tribunal.esm".
*/
bool isDirtyTribunal()
{
/* /*
Here, id contains the game setting name, and we check the These functions check if this game setting is one of the "dirty"
setting for certain values. If it matches, this is a "dirty" GMST records found in many mods. These are due to a serious bug in
entry. The correct entry (as defined in Tribunal and Bloodmoon the official TES3 editor. It only occurs in the newer editor
esms) are given in the comments. Many of the values are correct, versions that came with Tribunal and Bloodmoon, and only if a
and are marked as 'same'. We still ignore them though, as they modder tries to make a mod without loading the corresponding
are still in the wrong file and might override custom values expansion master file. For example, if you have Tribunal installed
from other mods. and try to make a mod without loading Tribunal.esm, the editor
*/ will insert these GMST records as a replacement for the entries it
cannot find in the ESMs.
// Strings
cS("sProfitValue", "Profit Value"); // 'Profit:' The values of these "dirty" records differ in general from their
cS("sEditNote", "Edit Note"); // same values as defined in Tribunal.esm and Bloodmoon.esm, and are
cS("sDeleteNote", "Delete Note?"); // same always set to the same "default" values. Most of these values are
cS("sMaxSale", "Max Sale"); // 'Seller Max' nonsensical, ie. changing the "Seller Max" string to "Max Sale",
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon' or change the stats of werewolves to useless values like 1. Some
cS("sTeleportDisabled", of them break certain spell effects.
"Teleportation magic does not work here.");// same
cS("sLevitateDisabled", It is most likely that these values are just leftover values from
"Levitation magic does not work here."); // same an early stage of development that are inserted as default values
cS("sCompanionShare", "Companion Share"); // 'Share' by the editor code. They are supposed to be overridden when the
cS("sCompanionWarningButtonOne", correct esm file is loaded. When it isn't loaded however, you get
"Let the mercenary quit."); // same stuck with the initial value, and this gets written to every mod
cS("sCompanionWarningButtonTwo", by the editor, for some reason.
"Return to Companion Share display."); // same
cS("sCompanionWarningMessage", Bethesda themselves have fallen for this bug. If you install both
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value."); Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
// 'Your mercenary is poorer now than when he contracted with dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
// you. Your mercenary will quit if you do not give him gold some of the dirty settings from Tribunal. In other words, this bug
// or goods to bring his Profit to a positive value.' affects the game EVEN IF YOU DO NOT USE ANY MODS!
// [The difference here is "Profit Value" -> "Profit"]
The guys at Bethesda are well aware of this bug (and many others),
// Strings that matches the id as the mod community and fan base complained about them for a long
cS("sEffectSummonFabricant", id);// 'Summon Fabricant' time. But unfortunately it was never fixed.
return false;
} There are several tools available to help modders remove these
records from their files, but not all modders use them, and they
// Bloodmoon variant really shouldn't have to. In this file we choose instead to reject
bool isDirtyBloodmoon() all the corrupt values at load time.
{
// Strings These functions checks if the current game setting is one of the
cS("sWerewolfPopup", "Werewolf"); // same "dirty" ones as described above. TODO: I have not checked this
cS("sWerewolfRestMessage", against other sources yet, do that later. Currently recognizes 22
"You cannot rest in werewolf form."); // same values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
cS("sWerewolfRefusal", (setting values other than the default "dirty" ones) are not
"You cannot do this as a werewolf."); // same affected and will work correctly.
cS("sWerewolfAlarmMessage", */
"You have been detected changing from a werewolf state.");
// 'You have been detected as a known werewolf.' /*
Checks for dirty tribunal values. These will be ignored if found
// Strings that matches the id in any file except when they are found in "Tribunal.esm".
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon' */
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon' bool isDirtyTribunal();
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
cS("sMagicCreature04ID", id); // same // Bloodmoon variant
cS("sMagicCreature05ID", id); // same bool isDirtyBloodmoon();
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
cS("sEffectSummonCreature02", id); // 'Calf Bear' void load(ESMReader &esm);
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
cS("sEffectSummonCreature04", id); // same
cS("sEffectSummonCreature05", id); // same
// Integers
cI("iWereWolfBounty", 10000); // 1000
cI("iWereWolfFightMod", 100); // same
cI("iWereWolfFleeMod", 100); // same
cI("iWereWolfLevelToAttack", 20); // same
// Floats
cF("fFleeDistance", 3000); // same
cF("fCombatDistanceWerewolfMod", 0.3); // same
cF("fWereWolfFatigue", 400); // same
cF("fWereWolfEnchant", 1); // 0
cF("fWereWolfArmorer", 1); // 0
cF("fWereWolfBlock", 1); // 0
cF("fWereWolfSneak", 1); // 95
cF("fWereWolfDestruction", 1); // 0
cF("fWereWolfEndurance", 150); // same
cF("fWereWolfConjuration", 1); // 0
cF("fWereWolfRestoration", 1); // 0
cF("fWereWolfAthletics", 150); // 50
cF("fWereWolfLuck", 1); // 25
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
cF("fWereWolfMediumArmor", 1); // 0
cF("fWereWolfShortBlade", 1); // 0
cF("fWereWolfAcrobatics", 150); // 80
cF("fWereWolfSpeechcraft", 1); // 0
cF("fWereWolfAlteration", 1); // 0
cF("fWereWolfIllusion", 1); // 0
cF("fWereWolfLongBlade", 1); // 0
cF("fWereWolfMarksman", 1); // 0
cF("fWereWolfHandtoHand", 100); // same
cF("fWereWolfIntellegence", 1); // 0
cF("fWereWolfAlchemy", 1); // 0
cF("fWereWolfUnarmored", 100); // same
cF("fWereWolfAxe", 1); // 0
cF("fWereWolfRunMult", 1.5); // 1.3
cF("fWereWolfMagicka", 100); // same
cF("fWereWolfAgility", 150); // same
cF("fWereWolfBluntWeapon", 1); // 0
cF("fWereWolfSecurity", 1); // 0
cF("fWereWolfPersonality", 1); // 0
cF("fWereWolfMerchantile", 1); // 0
cF("fWereWolfHeavyArmor", 1); // 0
cF("fWereWolfSpear", 1); // 0
cF("fWereWolfStrength", 150); // same
cF("fWereWolfHealth", 2); // same
cF("fWereWolfMysticism", 1); // 0
cF("fWereWolfLightArmor", 1); // 0
cF("fWereWolfWillPower", 1); // 0
cF("fWereWolfSpeed", 150); // 90
return false;
}
#undef cI
#undef cF
#undef cS
void load(ESMReader &esm)
{
assert(id != "");
dirty = false;
// We are apparently allowed to be empty
if(!esm.hasMoreSubs())
{
type = VT_None;
return;
}
// Load some data
esm.getSubName();
NAME n = esm.retSubName();
if(n == "STRV")
{
str = esm.getHString();
type = VT_String;
}
else if(n == "INTV")
{
esm.getHT(i);
type = VT_Int;
}
else if(n == "FLTV")
{
esm.getHT(f);
type = VT_Float;
}
else
esm.fail("Unwanted subrecord type");
int spf = esm.getSpecial();
// Check if this is one of the dirty values mentioned above. If it
// is, we set the dirty flag. This will ONLY work if you've set
// the 'id' string correctly before calling load().
if( ( spf != SF_Tribunal && isDirtyTribunal() ) ||
( spf != SF_Bloodmoon && isDirtyBloodmoon() ) )
dirty = true;
}
}; };
} }
#endif #endif

@ -0,0 +1,133 @@
#include "loadinfo.hpp"
namespace ESM
{
void DialInfo::load(ESMReader &esm)
{
id = esm.getHNString("INAM");
prev = esm.getHNString("PNAM");
next = esm.getHNString("NNAM");
// Not present if deleted
if (esm.isNextSub("DATA"))
esm.getHT(data, 12);
// What follows is somewhat spaghetti-ish, but it's worth if for
// an extra speedup. INFO is by far the most common record type.
// subName is a reference to the original, so it changes whenever
// a new sub name is read. esm.isEmptyOrGetName() will get the
// next name for us, or return true if there are no more records.
esm.getSubName();
const NAME &subName = esm.retSubName();
if (subName.val == REC_ONAM)
{
actor = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_RNAM)
{
race = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_CNAM)
{
clas = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
factionLess = false;
if (subName.val == REC_FNAM)
{
npcFaction = esm.getHString();
if (npcFaction == "FFFF")
factionLess = true;
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_ANAM)
{
cell = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_DNAM)
{
pcFaction = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_SNAM)
{
sound = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_NAME)
{
response = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
while (subName.val == REC_SCVR)
{
SelectStruct ss;
ss.selectRule = esm.getHString();
esm.isEmptyOrGetName();
if (subName.val == REC_INTV)
{
ss.type = VT_Int;
esm.getHT(ss.i);
}
else if (subName.val == REC_FLTV)
{
ss.type = VT_Float;
esm.getHT(ss.f);
}
else
esm.fail(
"INFO.SCVR must precede INTV or FLTV, not "
+ subName.toString());
selects.push_back(ss);
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_BNAM)
{
resultScript = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
questStatus = QS_None;
if (subName.val == REC_QSTN)
questStatus = QS_Name;
else if (subName.val == REC_QSTF)
questStatus = QS_Finished;
else if (subName.val == REC_QSTR)
questStatus = QS_Restart;
else if (subName.val == REC_DELE)
questStatus = QS_Deleted;
else
esm.fail(
"Don't know what to do with " + subName.toString()
+ " in INFO " + id);
if (questStatus != QS_None)
// Skip rest of record
esm.skipRecord();
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp" #include "defs.hpp"
namespace ESM { namespace ESM
{
// NOT DONE // NOT DONE
@ -15,88 +16,88 @@ namespace ESM {
struct DialInfo struct DialInfo
{ {
enum Gender enum Gender
{
Male = 0,
Female = 1,
NA = -1
};
struct DATAstruct
{
int unknown1;
int disposition;
char rank; // Rank of NPC
char gender; // See Gender enum
char PCrank; // Player rank
char unknown2;
}; // 12 bytes
DATAstruct data;
// The rules for whether or not we will select this dialog item.
struct SelectStruct
{ {
Male = 0, std::string selectRule; // This has a complicated format
Female = 1, float f; // Only one of 'f' or 'i' is used
NA = -1 int i;
VarType type;
}; };
struct DATAstruct // Journal quest indices (introduced with the quest system in Tribunal)
{ enum QuestStatus
int unknown1;
int disposition;
char rank; // Rank of NPC
char gender; // See Gender enum
char PCrank; // Player rank
char unknown2;
}; // 12 bytes
DATAstruct data;
// The rules for whether or not we will select this dialog item.
struct SelectStruct
{
std::string selectRule; // This has a complicated format
float f; // Only one of 'f' or 'i' is used
int i;
VarType type;
};
// Journal quest indices (introduced with the quest system in Tribunal)
enum QuestStatus
{ {
QS_None, QS_None,
QS_Name, QS_Name,
QS_Finished, QS_Finished,
QS_Restart, QS_Restart,
QS_Deleted QS_Deleted
}; };
// Rules for when to include this item in the final list of options // Rules for when to include this item in the final list of options
// visible to the player. // visible to the player.
std::vector<SelectStruct> selects; std::vector<SelectStruct> selects;
// Id of this, previous and next INFO items // Id of this, previous and next INFO items
std::string id, prev, next, std::string id, prev, next,
// Various references used in determining when to select this item. // Various references used in determining when to select this item.
actor, race, clas, npcFaction, pcFaction, cell, actor, race, clas, npcFaction, pcFaction, cell,
// Sound and text associated with this item // Sound and text associated with this item
sound, response, sound, response,
// Result script (uncomiled) to run whenever this dialog item is // Result script (uncomiled) to run whenever this dialog item is
// selected // selected
resultScript; resultScript;
// ONLY include this item the NPC is not part of any faction. // ONLY include this item the NPC is not part of any faction.
bool factionLess; bool factionLess;
// Status of this quest item // Status of this quest item
QuestStatus questStatus; QuestStatus questStatus;
// Hexadecimal versions of the various subrecord names. // Hexadecimal versions of the various subrecord names.
enum SubNames enum SubNames
{ {
REC_ONAM = 0x4d414e4f, REC_ONAM = 0x4d414e4f,
REC_RNAM = 0x4d414e52, REC_RNAM = 0x4d414e52,
REC_CNAM = 0x4d414e43, REC_CNAM = 0x4d414e43,
REC_FNAM = 0x4d414e46, REC_FNAM = 0x4d414e46,
REC_ANAM = 0x4d414e41, REC_ANAM = 0x4d414e41,
REC_DNAM = 0x4d414e44, REC_DNAM = 0x4d414e44,
REC_SNAM = 0x4d414e53, REC_SNAM = 0x4d414e53,
REC_NAME = 0x454d414e, REC_NAME = 0x454d414e,
REC_SCVR = 0x52564353, REC_SCVR = 0x52564353,
REC_INTV = 0x56544e49, REC_INTV = 0x56544e49,
REC_FLTV = 0x56544c46, REC_FLTV = 0x56544c46,
REC_BNAM = 0x4d414e42, REC_BNAM = 0x4d414e42,
REC_QSTN = 0x4e545351, REC_QSTN = 0x4e545351,
REC_QSTF = 0x46545351, REC_QSTF = 0x46545351,
REC_QSTR = 0x52545351, REC_QSTR = 0x52545351,
REC_DELE = 0x454c4544 REC_DELE = 0x454c4544
}; };
void load(ESMReader &esm); void load(ESMReader &esm);
}; };
/* /*

@ -0,0 +1,15 @@
#include "loadingr.hpp"
namespace ESM
{
void Ingredient::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "IRDT", 56);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Alchemy ingredient * Alchemy ingredient
@ -11,26 +12,19 @@ namespace ESM {
struct Ingredient struct Ingredient
{ {
struct IRDTstruct struct IRDTstruct
{ {
float weight; float weight;
int value; int value;
int effectID[4]; // Effect, 0 or -1 means none int effectID[4]; // Effect, 0 or -1 means none
int skills[4]; // SkillEnum related to effect int skills[4]; // SkillEnum related to effect
int attributes[4]; // Attribute related to effect int attributes[4]; // Attribute related to effect
}; };
IRDTstruct data; IRDTstruct data;
std::string name, model, icon, script; std::string name, model, icon, script;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "IRDT", 56);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}; };
} }
#endif #endif

@ -0,0 +1,32 @@
#include "loadland.hpp"
namespace ESM
{
void Land::load(ESMReader &esm)
{
// Get the grid location
esm.getSubNameIs("INTV");
esm.getSubHeaderIs(8);
esm.getT<int>(X);
esm.getT<int>(Y);
esm.getHNT(flags, "DATA");
// Store the file position
context = esm.getContext();
hasData = false;
int cnt = 0;
// Skip these here. Load the actual data when the cell is loaded.
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
// We need all three of VNML, VHGT and VTEX in order to use the
// landscape.
hasData = (cnt == 3);
}
}

@ -3,51 +3,25 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Landscape data. * Landscape data.
*/ */
struct Land struct Land
{ {
int flags; // Only first four bits seem to be used, don't know what int flags; // Only first four bits seem to be used, don't know what
// they mean. // they mean.
int X, Y; // Map coordinates. int X, Y; // Map coordinates.
// File context. This allows the ESM reader to be 'reset' to this
// location later when we are ready to load the full data set.
ESM_Context context;
bool hasData;
void load(ESMReader &esm)
{
// Get the grid location
esm.getSubNameIs("INTV");
esm.getSubHeaderIs(8);
esm.getT<int>(X);
esm.getT<int>(Y);
esm.getHNT(flags, "DATA");
// Store the file position
context = esm.getContext();
hasData = false; // File context. This allows the ESM reader to be 'reset' to this
int cnt = 0; // location later when we are ready to load the full data set.
ESM_Context context;
// Skip these here. Load the actual data when the cell is loaded. bool hasData;
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
// We need all three of VNML, VHGT and VTEX in order to use the void load(ESMReader &esm);
// landscape.
hasData = (cnt == 3);
}
}; };
} }
#endif #endif

@ -0,0 +1,34 @@
#include "loadlevlist.hpp"
namespace ESM
{
void LeveledListBase::load(ESMReader &esm)
{
esm.getHNT(flags, "DATA");
esm.getHNT(chanceNone, "NNAM");
if (esm.isNextSub("INDX"))
{
int len;
esm.getHT(len);
list.resize(len);
}
else
return;
// TODO: Merge with an existing lists here. This can be done
// simply by adding the lists together, making sure that they are
// sorted by level. A better way might be to exclude repeated
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for (size_t i = 0; i < list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);
esm.getHNT(li.level, "INTV");
}
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Leveled lists. Since these have identical layout, I only bothered * Leveled lists. Since these have identical layout, I only bothered
@ -15,65 +16,50 @@ namespace ESM {
struct LeveledListBase struct LeveledListBase
{ {
enum Flags enum Flags
{ {
AllLevels = 0x01, // Calculate from all levels <= player AllLevels = 0x01, // Calculate from all levels <= player
// level, not just the closest below // level, not just the closest below
// player. // player.
Each = 0x02 // Select a new item each time this Each = 0x02 // Select a new item each time this
// list is instantiated, instead of // list is instantiated, instead of
// giving several identical items // giving several identical items
}; // (used when a container has more }; // (used when a container has more
// than one instance of one leveled // than one instance of one leveled
// list.) // list.)
int flags; int flags;
unsigned char chanceNone; // Chance that none are selected (0-255?) unsigned char chanceNone; // Chance that none are selected (0-255?)
// Record name used to read references. Must be set before load() is
// called.
const char *recName;
struct LevelItem
{
std::string id;
short level;
};
std::vector<LevelItem> list; // Record name used to read references. Must be set before load() is
// called.
const char *recName;
void load(ESMReader &esm) struct LevelItem
{ {
esm.getHNT(flags, "DATA"); std::string id;
esm.getHNT(chanceNone, "NNAM"); short level;
};
if(esm.isNextSub("INDX")) std::vector<LevelItem> list;
{
int len;
esm.getHT(len);
list.resize(len);
}
else return;
// TODO: Merge with an existing lists here. This can be done void load(ESMReader &esm);
// simply by adding the lists together, making sure that they are
// sorted by level. A better way might be to exclude repeated
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for(size_t i=0; i<list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);
esm.getHNT(li.level, "INTV");
}
}
}; };
struct CreatureLevList : LeveledListBase struct CreatureLevList: LeveledListBase
{ CreatureLevList() { recName = "CNAM"; } }; {
CreatureLevList()
{
recName = "CNAM";
}
};
struct ItemLevList : LeveledListBase struct ItemLevList: LeveledListBase
{ ItemLevList() { recName = "INAM"; } }; {
ItemLevList()
{
recName = "INAM";
}
};
} }
#endif #endif

@ -0,0 +1,17 @@
#include "loadligh.hpp"
namespace ESM
{
void Light::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
icon = esm.getHNOString("ITEX");
assert(sizeof(data) == 24);
esm.getHNT(data, "LHDT", 24);
script = esm.getHNOString("SCRI");
sound = esm.getHNOString("SNAM");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Lights. Includes static light sources and also carryable candles * Lights. Includes static light sources and also carryable candles
@ -12,43 +13,34 @@ namespace ESM {
struct Light struct Light
{ {
enum Flags enum Flags
{ {
Dynamic = 0x001, Dynamic = 0x001,
Carry = 0x002, // Can be carried Carry = 0x002, // Can be carried
Negative = 0x004, // Negative light? Negative = 0x004, // Negative light?
Flicker = 0x008, Flicker = 0x008,
Fire = 0x010, Fire = 0x010,
OffDefault = 0x020, // Off by default OffDefault = 0x020, // Off by default
FlickerSlow = 0x040, FlickerSlow = 0x040,
Pulse = 0x080, Pulse = 0x080,
PulseSlow = 0x100 PulseSlow = 0x100
}; };
struct LHDTstruct struct LHDTstruct
{ {
float weight; float weight;
int value; int value;
int time; // Duration int time; // Duration
int radius; int radius;
int color; // 4-byte rgba value int color; // 4-byte rgba value
int flags; int flags;
}; // Size = 24 bytes }; // Size = 24 bytes
LHDTstruct data; LHDTstruct data;
std::string sound, script, model, icon, name; std::string sound, script, model, icon, name;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
icon = esm.getHNOString("ITEX");
assert(sizeof(data) == 24);
esm.getHNT(data, "LHDT", 24);
script = esm.getHNOString("SCRI");
sound = esm.getHNOString("SNAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,30 @@
#include "loadlocks.hpp"
namespace ESM
{
void Tool::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getSubName();
NAME n = esm.retSubName();
// The data name varies, RIDT for repair items, LKDT for lock
// picks, PBDT for probes
esm.getHT(data, 16);
if (n == "RIDT")
{
// Swap t.data.quality and t.data.uses for repair items (sigh)
float tmp = *((float*) &data.uses);
data.uses = *((int*) &data.quality);
data.quality = tmp;
}
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* This file covers lockpicks (LOCK), probes (PROB) and armor repair * This file covers lockpicks (LOCK), probes (PROB) and armor repair
@ -12,51 +13,29 @@ namespace ESM {
struct Tool struct Tool
{ {
struct Data struct Data
{ {
float weight; float weight;
int value; int value;
float quality; // And when I say nearly identical structure, I
int uses; // mean perfectly identical except that these two
// variables are swaped for repair items. Don't ask
// me why.
}; // Size = 16
Data data;
std::string name, model, icon, script;
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getSubName();
NAME n = esm.retSubName();
// The data name varies, RIDT for repair items, LKDT for lock
// picks, PBDT for probes
esm.getHT(data, 16); float quality; // And when I say nearly identical structure, I
int uses; // mean perfectly identical except that these two
// variables are swaped for repair items. Don't ask
// me why.
}; // Size = 16
if(n == "RIDT") Data data;
{ std::string name, model, icon, script;
// Swap t.data.quality and t.data.uses for repair items (sigh)
float tmp = *((float*)&data.uses);
data.uses = *((int*)&data.quality);
data.quality = tmp;
}
script = esm.getHNOString("SCRI"); void load(ESMReader &esm);
icon = esm.getHNOString("ITEX");
}
}; };
struct Probe : Tool struct Probe: Tool
{ {
}; };
struct Repair : Tool struct Repair: Tool
{ {
}; };

@ -0,0 +1,12 @@
#include "loadltex.hpp"
namespace ESM
{
void LandTexture::load(ESMReader &esm)
{
esm.getHNT(index, "INTV");
texture = esm.getHNString("DATA");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Texture used for texturing landscape. * Texture used for texturing landscape.
@ -23,14 +24,10 @@ namespace ESM {
struct LandTexture struct LandTexture
{ {
std::string id, texture; std::string id, texture;
int index; int index;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(index, "INTV");
texture = esm.getHNString("DATA");
}
}; };
} }
#endif #endif

@ -0,0 +1,27 @@
#include "loadmgef.hpp"
namespace ESM
{
void MagicEffect::load(ESMReader &esm)
{
esm.getHNT(index, "INDX");
esm.getHNT(data, "MEDT", 36);
icon = esm.getHNOString("ITEX");
particle = esm.getHNOString("PTEX");
boltSound = esm.getHNOString("BSND");
castSound = esm.getHNOString("CSND");
hitSound = esm.getHNOString("HSND");
areaSound = esm.getHNOString("ASND");
casting = esm.getHNOString("CVFX");
bolt = esm.getHNOString("BVFX");
hit = esm.getHNOString("HVFX");
area = esm.getHNOString("AVFX");
description = esm.getHNOString("DESC");
}
}

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

Loading…
Cancel
Save