mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-29 21:45:32 +00:00
OpenMW Integration.
Read the ESM/ESP records but do nothing with them for the moment.
This commit is contained in:
parent
5ad440cb45
commit
6ec6b9bc2a
8 changed files with 319 additions and 17 deletions
|
@ -580,6 +580,7 @@ add_subdirectory (extern/shiny)
|
|||
add_subdirectory (extern/ogre-ffmpeg-videoplayer)
|
||||
add_subdirectory (extern/oics)
|
||||
add_subdirectory (extern/sdl4ogre)
|
||||
add_subdirectory (extern/esm4)
|
||||
add_subdirectory (extern/murmurhash)
|
||||
add_subdirectory (extern/BSAOpt)
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ target_link_libraries(openmw
|
|||
${OGRE_LIBRARIES}
|
||||
${OGRE_STATIC_PLUGINS}
|
||||
${SHINY_LIBRARIES}
|
||||
${ESM4_LIBRARIES}
|
||||
${BSAOPTHASH_LIBRARIES}
|
||||
${ZLIB_LIBRARY}
|
||||
${OPENAL_LIBRARY}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "esmstore.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esm4reader.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -42,8 +43,22 @@ void EsmLoader::load(const boost::filesystem::path& filepath, std::vector<std::v
|
|||
else
|
||||
tesVerIndex = 3;
|
||||
|
||||
// do nothing for now
|
||||
return;
|
||||
lEsm->close();
|
||||
delete lEsm;
|
||||
ESM::ESM4Reader *esm = new ESM::ESM4Reader(isTes4); // NOTE: TES4 headers are 4 bytes shorter
|
||||
esm->setEncoder(mEncoder);
|
||||
|
||||
index = contentFiles[tesVerIndex].size();
|
||||
contentFiles[tesVerIndex].push_back(filepath.filename().string());
|
||||
esm->setIndex(index);
|
||||
|
||||
esm->reader().setModIndex(index);
|
||||
esm->openTes4File(filepath.string());
|
||||
esm->reader().updateModIndicies(contentFiles[tesVerIndex]);
|
||||
// FIXME: this does not work well (copies the base class pointer)
|
||||
//i.e. have to check TES4/TES5 versions each time before use within EsmStore::load,
|
||||
//static casting as required
|
||||
mEsm[tesVerIndex].push_back(esm);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <components/loadinglistener/loadinglistener.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/esm4reader.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -82,6 +84,16 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
|
|||
// Loop through all records
|
||||
while(esm.hasMoreRecs())
|
||||
{
|
||||
if (isTes4 || isTes5 || isFONV)
|
||||
{
|
||||
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
|
||||
reader.checkGroupStatus();
|
||||
|
||||
loadTes4Group(esm);
|
||||
listener->setProgress(static_cast<size_t>(esm.getFileOffset() / (float)esm.getFileSize() * 1000));
|
||||
continue;
|
||||
}
|
||||
|
||||
ESM::NAME n = esm.getRecName();
|
||||
esm.getRecHeader();
|
||||
|
||||
|
@ -136,6 +148,138 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
|
|||
}
|
||||
}
|
||||
|
||||
// Can't use ESM4::Reader& as the parameter here because we need esm.hasMoreRecs() for
|
||||
// checking an empty group followed by EOF
|
||||
void ESMStore::loadTes4Group (ESM::ESMReader &esm)
|
||||
{
|
||||
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
|
||||
|
||||
reader.getRecordHeader();
|
||||
const ESM4::RecordHeader& hdr = reader.hdr();
|
||||
|
||||
if (hdr.record.typeId != ESM4::REC_GRUP)
|
||||
return loadTes4Record(esm);
|
||||
|
||||
switch (hdr.group.type)
|
||||
{
|
||||
case ESM4::Grp_RecordType:
|
||||
{
|
||||
// FIXME: rewrite to workaround reliability issue
|
||||
if (hdr.group.label.value == ESM4::REC_NAVI || hdr.group.label.value == ESM4::REC_WRLD ||
|
||||
hdr.group.label.value == ESM4::REC_REGN || hdr.group.label.value == ESM4::REC_STAT ||
|
||||
hdr.group.label.value == ESM4::REC_ANIO || hdr.group.label.value == ESM4::REC_CONT ||
|
||||
hdr.group.label.value == ESM4::REC_MISC || hdr.group.label.value == ESM4::REC_ACTI ||
|
||||
hdr.group.label.value == ESM4::REC_ARMO || hdr.group.label.value == ESM4::REC_NPC_ ||
|
||||
hdr.group.label.value == ESM4::REC_FLOR || hdr.group.label.value == ESM4::REC_GRAS ||
|
||||
hdr.group.label.value == ESM4::REC_TREE || hdr.group.label.value == ESM4::REC_LIGH ||
|
||||
hdr.group.label.value == ESM4::REC_BOOK || hdr.group.label.value == ESM4::REC_FURN ||
|
||||
hdr.group.label.value == ESM4::REC_SOUN || hdr.group.label.value == ESM4::REC_WEAP ||
|
||||
hdr.group.label.value == ESM4::REC_DOOR || hdr.group.label.value == ESM4::REC_AMMO ||
|
||||
hdr.group.label.value == ESM4::REC_CLOT || hdr.group.label.value == ESM4::REC_ALCH ||
|
||||
hdr.group.label.value == ESM4::REC_APPA || hdr.group.label.value == ESM4::REC_INGR ||
|
||||
hdr.group.label.value == ESM4::REC_SGST || hdr.group.label.value == ESM4::REC_SLGM ||
|
||||
hdr.group.label.value == ESM4::REC_KEYM || hdr.group.label.value == ESM4::REC_HAIR ||
|
||||
hdr.group.label.value == ESM4::REC_EYES || hdr.group.label.value == ESM4::REC_CELL ||
|
||||
hdr.group.label.value == ESM4::REC_CREA || hdr.group.label.value == ESM4::REC_LVLC ||
|
||||
hdr.group.label.value == ESM4::REC_LVLI || hdr.group.label.value == ESM4::REC_MATO ||
|
||||
hdr.group.label.value == ESM4::REC_IDLE || hdr.group.label.value == ESM4::REC_LTEX ||
|
||||
hdr.group.label.value == ESM4::REC_RACE || hdr.group.label.value == ESM4::REC_SBSP
|
||||
)
|
||||
{
|
||||
reader.saveGroupStatus();
|
||||
loadTes4Group(esm);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Skip groups that are of no interest (for now).
|
||||
// GMST GLOB CLAS FACT SKIL MGEF SCPT ENCH SPEL BSGN WTHR CLMT DIAL
|
||||
// QUST PACK CSTY LSCR LVSP WATR EFSH
|
||||
|
||||
// FIXME: The label field of a group is not reliable, so we will need to check here as well
|
||||
//std::cout << "skipping group... " << ESM4::printLabel(hdr.group.label, hdr.group.type) << std::endl;
|
||||
reader.skipGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ESM4::Grp_CellChild:
|
||||
case ESM4::Grp_WorldChild:
|
||||
case ESM4::Grp_TopicChild:
|
||||
case ESM4::Grp_CellPersistentChild:
|
||||
{
|
||||
reader.adjustGRUPFormId(); // not needed or even shouldn't be done? (only labels anyway)
|
||||
reader.saveGroupStatus();
|
||||
//#if 0
|
||||
// Below test shows that Oblivion.esm does not have any persistent cell child
|
||||
// groups under exterior world sub-block group. Haven't checked other files yet.
|
||||
if (reader.grp(0).type == ESM4::Grp_CellPersistentChild &&
|
||||
reader.grp(1).type == ESM4::Grp_CellChild &&
|
||||
!(reader.grp(2).type == ESM4::Grp_WorldChild || reader.grp(2).type == ESM4::Grp_InteriorSubCell))
|
||||
std::cout << "Unexpected persistent child group in exterior subcell" << std::endl;
|
||||
//#endif
|
||||
if (!esm.hasMoreRecs())
|
||||
return; // may have been an empty group followed by EOF
|
||||
|
||||
loadTes4Group(esm);
|
||||
|
||||
break;
|
||||
}
|
||||
case ESM4::Grp_CellTemporaryChild:
|
||||
case ESM4::Grp_CellVisibleDistChild:
|
||||
{
|
||||
// NOTE: preload strategy and persistent records
|
||||
//
|
||||
// Current strategy defers loading of "temporary" or "visible when distant"
|
||||
// references and other records (land and pathgrid) until they are needed.
|
||||
//
|
||||
// The "persistent" records need to be loaded up front, however. This is to allow,
|
||||
// for example, doors to work. A door reference will have a FormId of the
|
||||
// destination door FormId. But we have no way of knowing to which cell the
|
||||
// destination FormId belongs until that cell and that reference is loaded.
|
||||
//
|
||||
// For worldspaces the persistent records are usully (always?) stored in a dummy
|
||||
// cell under a "world child" group. It may be possible to skip the whole "cell
|
||||
// child" group without scanning for persistent records. See above short test.
|
||||
reader.skipGroup();
|
||||
break;
|
||||
}
|
||||
case ESM4::Grp_ExteriorCell:
|
||||
case ESM4::Grp_ExteriorSubCell:
|
||||
case ESM4::Grp_InteriorCell:
|
||||
case ESM4::Grp_InteriorSubCell:
|
||||
{
|
||||
reader.saveGroupStatus();
|
||||
loadTes4Group(esm);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.skipGroup();
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ESMStore::loadTes4Record (ESM::ESMReader& esm)
|
||||
{
|
||||
// Assumes that the reader has just read the record header only.
|
||||
ESM4::Reader& reader = static_cast<ESM::ESM4Reader*>(&esm)->reader();
|
||||
const ESM4::RecordHeader& hdr = reader.hdr();
|
||||
|
||||
switch (hdr.record.typeId)
|
||||
{
|
||||
|
||||
// FIXME: removed for now
|
||||
|
||||
default:
|
||||
reader.skipRecordData();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ESMStore::setUp()
|
||||
{
|
||||
std::map<int, StoreBase *>::iterator it = mStores.begin();
|
||||
|
@ -213,7 +357,7 @@ void ESMStore::setUp()
|
|||
if (type==ESM::REC_NPC_)
|
||||
{
|
||||
// NPC record will always be last and we know that there can be only one
|
||||
// dynamic NPC record (player) -> We are done here with dynamic record laoding
|
||||
// dynamic NPC record (player) -> We are done here with dynamic record loading
|
||||
setUp();
|
||||
|
||||
const ESM::NPC *player = mNpcs.find ("player");
|
||||
|
|
|
@ -6,6 +6,12 @@
|
|||
#include <components/esm/records.hpp>
|
||||
#include "store.hpp"
|
||||
|
||||
namespace ESM4
|
||||
{
|
||||
class Reader;
|
||||
union RecordHeader;
|
||||
}
|
||||
|
||||
namespace Loading
|
||||
{
|
||||
class Listener;
|
||||
|
@ -73,6 +79,9 @@ namespace MWWorld
|
|||
|
||||
unsigned int mDynamicCount;
|
||||
|
||||
void loadTes4Group (ESM::ESMReader& esm);
|
||||
void loadTes4Record (ESM::ESMReader& esm);
|
||||
|
||||
public:
|
||||
/// \todo replace with SharedIterator<StoreBase>
|
||||
typedef std::map<int, StoreBase *>::const_iterator iterator;
|
||||
|
|
|
@ -6,16 +6,16 @@ set (VERSION_HPP ${CMAKE_CURRENT_SOURCE_DIR}/version/version.hpp)
|
|||
if (GIT_CHECKOUT)
|
||||
add_custom_target (git-version
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
|
||||
-DVERSION_HPP_IN=${VERSION_HPP_IN}
|
||||
-DVERSION_HPP=${VERSION_HPP}
|
||||
-DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR}
|
||||
-DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR}
|
||||
-DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE}
|
||||
-DOPENMW_VERSION=${OPENMW_VERSION}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake
|
||||
VERBATIM)
|
||||
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
|
||||
-DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
|
||||
-DVERSION_HPP_IN=${VERSION_HPP_IN}
|
||||
-DVERSION_HPP=${VERSION_HPP}
|
||||
-DOPENMW_VERSION_MAJOR=${OPENMW_VERSION_MAJOR}
|
||||
-DOPENMW_VERSION_MINOR=${OPENMW_VERSION_MINOR}
|
||||
-DOPENMW_VERSION_RELEASE=${OPENMW_VERSION_RELEASE}
|
||||
-DOPENMW_VERSION=${OPENMW_VERSION}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/GitVersion.cmake
|
||||
VERBATIM)
|
||||
else (GIT_CHECKOUT)
|
||||
configure_file(${VERSION_HPP_IN} ${VERSION_HPP})
|
||||
endif (GIT_CHECKOUT)
|
||||
|
@ -62,7 +62,7 @@ add_component_dir (esm
|
|||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
||||
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
|
||||
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
|
||||
aisequence magiceffects util custommarkerstate stolenitems transport
|
||||
aisequence magiceffects util custommarkerstate stolenitems transport esm4reader
|
||||
)
|
||||
|
||||
add_component_dir (esmterrain
|
||||
|
@ -165,10 +165,10 @@ include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
|||
|
||||
add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR})
|
||||
|
||||
target_link_libraries(components
|
||||
${Boost_LIBRARIES}
|
||||
target_link_libraries(components
|
||||
${Boost_LIBRARIES}
|
||||
${OGRE_LIBRARIES}
|
||||
${OENGINE_LIBRARY}
|
||||
${OENGINE_LIBRARY}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
|
94
components/esm/esm4reader.cpp
Normal file
94
components/esm/esm4reader.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include "esm4reader.hpp"
|
||||
|
||||
ESM::ESM4Reader::ESM4Reader(bool oldHeader)
|
||||
{
|
||||
// TES4 header size is 4 bytes smaller than TES5 header
|
||||
mReader.setRecHeaderSize(oldHeader ? sizeof(ESM4::RecordHeader)-4 : sizeof(ESM4::RecordHeader));
|
||||
}
|
||||
|
||||
ESM::ESM4Reader::~ESM4Reader()
|
||||
{
|
||||
}
|
||||
|
||||
void ESM::ESM4Reader::openTes4File(const std::string &name)
|
||||
{
|
||||
mCtx.filename = name;
|
||||
// WARNING: may throw
|
||||
mCtx.leftFile = mReader.openTes4File(name);
|
||||
mReader.registerForUpdates(this); // for updating mCtx.leftFile
|
||||
|
||||
mReader.getRecordHeader();
|
||||
if (mReader.hdr().record.typeId == ESM4::REC_TES4)
|
||||
{
|
||||
mReader.loadHeader();
|
||||
mCtx.leftFile -= mReader.hdr().record.dataSize;
|
||||
|
||||
// Hack: copy over values to TES3 header for getVer() and getRecordCount() to work
|
||||
mHeader.mData.version = mReader.esmVersion();
|
||||
mHeader.mData.records = mReader.numRecords();
|
||||
|
||||
mReader.buildLStringIndex(); // for localised strings in Skyrim
|
||||
}
|
||||
else
|
||||
fail("Unknown file format");
|
||||
}
|
||||
|
||||
ESM4::ReaderContext ESM::ESM4Reader::getESM4Context()
|
||||
{
|
||||
return mReader.getContext();
|
||||
}
|
||||
|
||||
void ESM::ESM4Reader::restoreESM4Context(const ESM4::ReaderContext& ctx)
|
||||
{
|
||||
// Reopen the file if necessary
|
||||
if (mCtx.filename != ctx.filename)
|
||||
openTes4File(ctx.filename);
|
||||
|
||||
// mCtx.leftFile is the only thing used in the old context. Strictly speaking, updating it
|
||||
// with the correct value is not really necessary since we're not going to load the rest of
|
||||
// the file (most likely to load a CELL or LAND then be done with it).
|
||||
mCtx.leftFile = mReader.getFileSize() - mReader.getFileOffset();
|
||||
|
||||
// restore group stack, load the header, etc.
|
||||
mReader.restoreContext(ctx);
|
||||
}
|
||||
|
||||
void ESM::ESM4Reader::restoreCellChildrenContext(const ESM4::ReaderContext& ctx)
|
||||
{
|
||||
// Reopen the file if necessary
|
||||
if (mCtx.filename != ctx.filename)
|
||||
openTes4File(ctx.filename);
|
||||
|
||||
mReader.restoreContext(ctx); // restore group stack, load the CELL header, etc.
|
||||
if (mReader.hdr().record.typeId != ESM4::REC_CELL) // FIXME: testing only
|
||||
fail("Restore Cell Children failed");
|
||||
mReader.skipRecordData(); // skip the CELL record
|
||||
|
||||
mReader.getRecordHeader(); // load the header for cell child group (hopefully)
|
||||
// this is a hack to load only the cell child group...
|
||||
if (mReader.hdr().group.typeId == ESM4::REC_GRUP && mReader.hdr().group.type == ESM4::Grp_CellChild)
|
||||
{
|
||||
mCtx.leftFile = mReader.hdr().group.groupSize - ctx.recHeaderSize;
|
||||
return;
|
||||
}
|
||||
|
||||
// But some cells may have no child groups...
|
||||
// Suspect "ICMarketDistrict" 7 18 is one, followed by cell record 00165F2C "ICMarketDistrict" 6 17
|
||||
if (mReader.hdr().group.typeId != ESM4::REC_GRUP && mReader.hdr().record.typeId == ESM4::REC_CELL)
|
||||
{
|
||||
mCtx.leftFile = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Maybe the group is completed
|
||||
// See "ICMarketDistrict" 9 15 which is followed by a exterior sub-cell block
|
||||
ESM4::ReaderContext tempCtx = mReader.getContext();
|
||||
if (!tempCtx.groupStack.empty() && tempCtx.groupStack.back().second == 0)
|
||||
{
|
||||
mCtx.leftFile = 0;
|
||||
return;
|
||||
}
|
||||
else
|
||||
fail("Restore Cell Children failed");
|
||||
|
||||
}
|
38
components/esm/esm4reader.hpp
Normal file
38
components/esm/esm4reader.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef COMPONENT_ESM_4READER_H
|
||||
#define COMPONENT_ESM_4READER_H
|
||||
|
||||
#include <extern/esm4/tes4.hpp>
|
||||
#include <extern/esm4/reader.hpp>
|
||||
|
||||
#include "esmreader.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
// Wrapper class for integrating into OpenCS
|
||||
class ESM4Reader : public ESMReader, public ESM4::ReaderObserver
|
||||
{
|
||||
ESM4::Reader mReader;
|
||||
|
||||
public:
|
||||
|
||||
ESM4Reader(bool oldHeader = true);
|
||||
virtual ~ESM4Reader();
|
||||
|
||||
ESM4::Reader& reader() { return mReader; }
|
||||
|
||||
// Added for use with OpenMW (loading progress bar)
|
||||
inline size_t getFileSize() { return mReader.getFileSize(); }
|
||||
inline size_t getFileOffset() { return mReader.getFileOffset(); }
|
||||
|
||||
// Added for loading Cell/Land
|
||||
ESM4::ReaderContext getESM4Context();
|
||||
void restoreESM4Context(const ESM4::ReaderContext& ctx);
|
||||
void restoreCellChildrenContext(const ESM4::ReaderContext& ctx);
|
||||
|
||||
void openTes4File(const std::string &name);
|
||||
|
||||
// callback from mReader to ensure hasMoreRecs() can reliably track to EOF
|
||||
inline void update(std::size_t size) { mCtx.leftFile -= size; }
|
||||
};
|
||||
}
|
||||
#endif // COMPONENT_ESM_4READER_H
|
Loading…
Reference in a new issue