1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-01 15:06:41 +00:00

Merge branch 'theeditorisjustanotherengine' into 'master'

Use ESM::ReadersCache in the editor

Closes #7896

See merge request OpenMW/openmw!4111
This commit is contained in:
Dave Corley 2024-05-21 06:35:47 +00:00
commit c87eaefd17
12 changed files with 100 additions and 123 deletions

View file

@ -168,6 +168,7 @@
Bug #7872: Region sounds use wrong odds Bug #7872: Region sounds use wrong odds
Bug #7886: Equip and unequip animations can't share the animation track section Bug #7886: Equip and unequip animations can't share the animation track section
Bug #7887: Editor: Mismatched reported script data size and actual data size causes a crash during save Bug #7887: Editor: Mismatched reported script data size and actual data size causes a crash during save
Bug #7896: Editor: Loading cellrefs incorrectly transforms Refnums, causing load failures
Bug #7898: Editor: Invalid reference scales are allowed Bug #7898: Editor: Invalid reference scales are allowed
Bug #7899: Editor: Doors can't be unlocked Bug #7899: Editor: Doors can't be unlocked
Bug #7901: Editor: Teleport-related fields shouldn't be editable if a ref does not teleport Bug #7901: Editor: Teleport-related fields shouldn't be editable if a ref does not teleport

View file

@ -17,13 +17,6 @@
#include "document.hpp" #include "document.hpp"
CSMDoc::Loader::Stage::Stage()
: mFile(0)
, mRecordsLoaded(0)
, mRecordsLeft(false)
{
}
CSMDoc::Loader::Loader() CSMDoc::Loader::Loader()
: mShouldStop(false) : mShouldStop(false)
{ {
@ -105,7 +98,7 @@ void CSMDoc::Loader::load()
if (iter->second.mFile < size) // start loading the files if (iter->second.mFile < size) // start loading the files
{ {
std::filesystem::path path = document->getContentFiles()[iter->second.mFile]; const std::filesystem::path& path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading(path, iter->second.mFile != editedIndex, /*project*/ false); int steps = document->getData().startLoading(path, iter->second.mFile != editedIndex, /*project*/ false);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;

View file

@ -23,11 +23,9 @@ namespace CSMDoc
struct Stage struct Stage
{ {
int mFile; int mFile = 0;
int mRecordsLoaded; int mRecordsLoaded = 0;
bool mRecordsLeft; bool mRecordsLeft = false;
Stage();
}; };
QMutex mMutex; QMutex mMutex;

View file

@ -305,9 +305,8 @@ void CSMDoc::WriteCellCollectionStage::writeReferences(
{ {
CSMWorld::CellRef refRecord = ref.get(); CSMWorld::CellRef refRecord = ref.get();
// Check for uninitialized content file // -1 is the current file, saved indices are 1-based
if (!refRecord.mRefNum.hasContentFile()) refRecord.mRefNum.mContentFile++;
refRecord.mRefNum.mContentFile = 0;
// recalculate the ref's cell location // recalculate the ref's cell location
std::ostringstream stream; std::ostringstream stream;

View file

@ -117,7 +117,7 @@ void CSMTools::MergeReferencesStage::perform(int stage, CSMDoc::Messages& messag
ref.mOriginalCell = ref.mCell; ref.mOriginalCell = ref.mCell;
ref.mRefNum.mIndex = mIndex[ref.mCell]++; ref.mRefNum.mIndex = mIndex[ref.mCell]++;
ref.mRefNum.mContentFile = 0; ref.mRefNum.mContentFile = -1;
ref.mNew = false; ref.mNew = false;
mState.mTarget->getData().getReferences().appendRecord(std::make_unique<CSMWorld::Record<CSMWorld::CellRef>>( mState.mTarget->getData().getReferences().appendRecord(std::make_unique<CSMWorld::Record<CSMWorld::CellRef>>(

View file

@ -137,9 +137,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
: mEncoder(encoding) : mEncoder(encoding)
, mPathgrids(mCells) , mPathgrids(mCells)
, mRefs(mCells) , mRefs(mCells)
, mReader(nullptr)
, mDialogue(nullptr) , mDialogue(nullptr)
, mReaderIndex(1) , mReaderIndex(0)
, mDataPaths(dataPaths) , mDataPaths(dataPaths)
, mArchives(archives) , mArchives(archives)
, mVFS(std::make_unique<VFS::Manager>()) , mVFS(std::make_unique<VFS::Manager>())
@ -688,8 +687,6 @@ CSMWorld::Data::~Data()
{ {
for (std::vector<QAbstractItemModel*>::iterator iter(mModels.begin()); iter != mModels.end(); ++iter) for (std::vector<QAbstractItemModel*>::iterator iter(mModels.begin()); iter != mModels.end(); ++iter)
delete *iter; delete *iter;
delete mReader;
} }
std::shared_ptr<Resource::ResourceSystem> CSMWorld::Data::getResourceSystem() std::shared_ptr<Resource::ResourceSystem> CSMWorld::Data::getResourceSystem()
@ -1068,17 +1065,11 @@ int CSMWorld::Data::startLoading(const std::filesystem::path& path, bool base, b
{ {
Log(Debug::Info) << "Loading content file " << path; Log(Debug::Info) << "Loading content file " << path;
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading
std::shared_ptr<ESM::ESMReader> ptr(mReader);
mReaders.push_back(ptr);
mReader = nullptr;
mDialogue = nullptr; mDialogue = nullptr;
mReader = new ESM::ESMReader; ESM::ReadersCache::BusyItem reader = mReaders.get(mReaderIndex++);
mReader->setEncoder(&mEncoder); reader->setEncoder(&mEncoder);
mReader->setIndex((project || !base) ? 0 : mReaderIndex++); reader->open(path);
mReader->open(path);
mBase = base; mBase = base;
mProject = project; mProject = project;
@ -1087,13 +1078,13 @@ int CSMWorld::Data::startLoading(const std::filesystem::path& path, bool base, b
{ {
MetaData metaData; MetaData metaData;
metaData.mId = ESM::RefId::stringRefId("sys::meta"); metaData.mId = ESM::RefId::stringRefId("sys::meta");
metaData.load(*mReader); metaData.load(*reader);
mMetaData.setRecord(0, mMetaData.setRecord(0,
std::make_unique<Record<MetaData>>(Record<MetaData>(RecordBase::State_ModifiedOnly, nullptr, &metaData))); std::make_unique<Record<MetaData>>(Record<MetaData>(RecordBase::State_ModifiedOnly, nullptr, &metaData)));
} }
return mReader->getRecordCount(); return reader->getRecordCount();
} }
void CSMWorld::Data::loadFallbackEntries() void CSMWorld::Data::loadFallbackEntries()
@ -1140,24 +1131,17 @@ void CSMWorld::Data::loadFallbackEntries()
bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages) bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
{ {
if (!mReader) if (mReaderIndex == 0)
throw std::logic_error("can't continue loading, because no load has been started"); throw std::logic_error("can't continue loading, because no load has been started");
ESM::ReadersCache::BusyItem reader = mReaders.get(mReaderIndex - 1);
if (!reader->isOpen())
throw std::logic_error("can't continue loading, because no load has been started");
reader->setEncoder(&mEncoder);
reader->setIndex(static_cast<int>(mReaderIndex - 1));
reader->resolveParentFileIndices(mReaders);
if (!mReader->hasMoreRecs()) if (!reader->hasMoreRecs())
{ {
if (mBase)
{
// Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand
// loading. We don't store non-base reader, because everything going into modified will be fully loaded
// during the initial loading process.
std::shared_ptr<ESM::ESMReader> ptr(mReader);
mReaders.push_back(ptr);
}
else
delete mReader;
mReader = nullptr;
mDialogue = nullptr; mDialogue = nullptr;
loadFallbackEntries(); loadFallbackEntries();
@ -1165,76 +1149,76 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
return true; return true;
} }
ESM::NAME n = mReader->getRecName(); ESM::NAME n = reader->getRecName();
mReader->getRecHeader(); reader->getRecHeader();
bool unhandledRecord = false; bool unhandledRecord = false;
switch (n.toInt()) switch (n.toInt())
{ {
case ESM::REC_GLOB: case ESM::REC_GLOB:
mGlobals.load(*mReader, mBase); mGlobals.load(*reader, mBase);
break; break;
case ESM::REC_GMST: case ESM::REC_GMST:
mGmsts.load(*mReader, mBase); mGmsts.load(*reader, mBase);
break; break;
case ESM::REC_SKIL: case ESM::REC_SKIL:
mSkills.load(*mReader, mBase); mSkills.load(*reader, mBase);
break; break;
case ESM::REC_CLAS: case ESM::REC_CLAS:
mClasses.load(*mReader, mBase); mClasses.load(*reader, mBase);
break; break;
case ESM::REC_FACT: case ESM::REC_FACT:
mFactions.load(*mReader, mBase); mFactions.load(*reader, mBase);
break; break;
case ESM::REC_RACE: case ESM::REC_RACE:
mRaces.load(*mReader, mBase); mRaces.load(*reader, mBase);
break; break;
case ESM::REC_SOUN: case ESM::REC_SOUN:
mSounds.load(*mReader, mBase); mSounds.load(*reader, mBase);
break; break;
case ESM::REC_SCPT: case ESM::REC_SCPT:
mScripts.load(*mReader, mBase); mScripts.load(*reader, mBase);
break; break;
case ESM::REC_REGN: case ESM::REC_REGN:
mRegions.load(*mReader, mBase); mRegions.load(*reader, mBase);
break; break;
case ESM::REC_BSGN: case ESM::REC_BSGN:
mBirthsigns.load(*mReader, mBase); mBirthsigns.load(*reader, mBase);
break; break;
case ESM::REC_SPEL: case ESM::REC_SPEL:
mSpells.load(*mReader, mBase); mSpells.load(*reader, mBase);
break; break;
case ESM::REC_ENCH: case ESM::REC_ENCH:
mEnchantments.load(*mReader, mBase); mEnchantments.load(*reader, mBase);
break; break;
case ESM::REC_BODY: case ESM::REC_BODY:
mBodyParts.load(*mReader, mBase); mBodyParts.load(*reader, mBase);
break; break;
case ESM::REC_SNDG: case ESM::REC_SNDG:
mSoundGens.load(*mReader, mBase); mSoundGens.load(*reader, mBase);
break; break;
case ESM::REC_MGEF: case ESM::REC_MGEF:
mMagicEffects.load(*mReader, mBase); mMagicEffects.load(*reader, mBase);
break; break;
case ESM::REC_PGRD: case ESM::REC_PGRD:
mPathgrids.load(*mReader, mBase); mPathgrids.load(*reader, mBase);
break; break;
case ESM::REC_SSCR: case ESM::REC_SSCR:
mStartScripts.load(*mReader, mBase); mStartScripts.load(*reader, mBase);
break; break;
case ESM::REC_LTEX: case ESM::REC_LTEX:
mLandTextures.load(*mReader, mBase); mLandTextures.load(*reader, mBase);
break; break;
case ESM::REC_LAND: case ESM::REC_LAND:
mLand.load(*mReader, mBase); mLand.load(*reader, mBase);
break; break;
case ESM::REC_CELL: case ESM::REC_CELL:
{ {
int index = mCells.load(*mReader, mBase); int index = mCells.load(*reader, mBase);
if (index < 0 || index >= mCells.getSize()) if (index < 0 || index >= mCells.getSize())
{ {
// log an error and continue loading the refs to the last loaded cell // log an error and continue loading the refs to the last loaded cell
@ -1243,69 +1227,69 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
index = mCells.getSize() - 1; index = mCells.getSize() - 1;
} }
mRefs.load(*mReader, index, mBase, mRefLoadCache[mCells.getId(index)], messages); mRefs.load(*reader, index, mBase, mRefLoadCache[mCells.getId(index)], messages);
break; break;
} }
case ESM::REC_ACTI: case ESM::REC_ACTI:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Activator); mReferenceables.load(*reader, mBase, UniversalId::Type_Activator);
break; break;
case ESM::REC_ALCH: case ESM::REC_ALCH:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Potion); mReferenceables.load(*reader, mBase, UniversalId::Type_Potion);
break; break;
case ESM::REC_APPA: case ESM::REC_APPA:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Apparatus); mReferenceables.load(*reader, mBase, UniversalId::Type_Apparatus);
break; break;
case ESM::REC_ARMO: case ESM::REC_ARMO:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Armor); mReferenceables.load(*reader, mBase, UniversalId::Type_Armor);
break; break;
case ESM::REC_BOOK: case ESM::REC_BOOK:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Book); mReferenceables.load(*reader, mBase, UniversalId::Type_Book);
break; break;
case ESM::REC_CLOT: case ESM::REC_CLOT:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Clothing); mReferenceables.load(*reader, mBase, UniversalId::Type_Clothing);
break; break;
case ESM::REC_CONT: case ESM::REC_CONT:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Container); mReferenceables.load(*reader, mBase, UniversalId::Type_Container);
break; break;
case ESM::REC_CREA: case ESM::REC_CREA:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Creature); mReferenceables.load(*reader, mBase, UniversalId::Type_Creature);
break; break;
case ESM::REC_DOOR: case ESM::REC_DOOR:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Door); mReferenceables.load(*reader, mBase, UniversalId::Type_Door);
break; break;
case ESM::REC_INGR: case ESM::REC_INGR:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Ingredient); mReferenceables.load(*reader, mBase, UniversalId::Type_Ingredient);
break; break;
case ESM::REC_LEVC: case ESM::REC_LEVC:
mReferenceables.load(*mReader, mBase, UniversalId::Type_CreatureLevelledList); mReferenceables.load(*reader, mBase, UniversalId::Type_CreatureLevelledList);
break; break;
case ESM::REC_LEVI: case ESM::REC_LEVI:
mReferenceables.load(*mReader, mBase, UniversalId::Type_ItemLevelledList); mReferenceables.load(*reader, mBase, UniversalId::Type_ItemLevelledList);
break; break;
case ESM::REC_LIGH: case ESM::REC_LIGH:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Light); mReferenceables.load(*reader, mBase, UniversalId::Type_Light);
break; break;
case ESM::REC_LOCK: case ESM::REC_LOCK:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Lockpick); mReferenceables.load(*reader, mBase, UniversalId::Type_Lockpick);
break; break;
case ESM::REC_MISC: case ESM::REC_MISC:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Miscellaneous); mReferenceables.load(*reader, mBase, UniversalId::Type_Miscellaneous);
break; break;
case ESM::REC_NPC_: case ESM::REC_NPC_:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Npc); mReferenceables.load(*reader, mBase, UniversalId::Type_Npc);
break; break;
case ESM::REC_PROB: case ESM::REC_PROB:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Probe); mReferenceables.load(*reader, mBase, UniversalId::Type_Probe);
break; break;
case ESM::REC_REPA: case ESM::REC_REPA:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Repair); mReferenceables.load(*reader, mBase, UniversalId::Type_Repair);
break; break;
case ESM::REC_STAT: case ESM::REC_STAT:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Static); mReferenceables.load(*reader, mBase, UniversalId::Type_Static);
break; break;
case ESM::REC_WEAP: case ESM::REC_WEAP:
mReferenceables.load(*mReader, mBase, UniversalId::Type_Weapon); mReferenceables.load(*reader, mBase, UniversalId::Type_Weapon);
break; break;
case ESM::REC_DIAL: case ESM::REC_DIAL:
@ -1313,7 +1297,7 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
ESM::Dialogue record; ESM::Dialogue record;
bool isDeleted = false; bool isDeleted = false;
record.load(*mReader, isDeleted); record.load(*reader, isDeleted);
if (isDeleted) if (isDeleted)
{ {
@ -1359,14 +1343,14 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
messages.add(UniversalId::Type_None, "Found info record not following a dialogue record", "", messages.add(UniversalId::Type_None, "Found info record not following a dialogue record", "",
CSMDoc::Message::Severity_Error); CSMDoc::Message::Severity_Error);
mReader->skipRecord(); reader->skipRecord();
break; break;
} }
if (mDialogue->mType == ESM::Dialogue::Journal) if (mDialogue->mType == ESM::Dialogue::Journal)
mJournalInfos.load(*mReader, mBase, *mDialogue, mJournalInfoOrder); mJournalInfos.load(*reader, mBase, *mDialogue, mJournalInfoOrder);
else else
mTopicInfos.load(*mReader, mBase, *mDialogue, mTopicInfoOrder); mTopicInfos.load(*reader, mBase, *mDialogue, mTopicInfoOrder);
break; break;
} }
@ -1379,7 +1363,7 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
break; break;
} }
mFilters.load(*mReader, mBase); mFilters.load(*reader, mBase);
break; break;
case ESM::REC_DBGP: case ESM::REC_DBGP:
@ -1390,7 +1374,7 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
break; break;
} }
mDebugProfiles.load(*mReader, mBase); mDebugProfiles.load(*reader, mBase);
break; break;
case ESM::REC_SELG: case ESM::REC_SELG:
@ -1401,7 +1385,7 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
break; break;
} }
mSelectionGroups.load(*mReader, mBase); mSelectionGroups.load(*reader, mBase);
break; break;
default: default:
@ -1414,7 +1398,7 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages)
messages.add( messages.add(
UniversalId::Type_None, "Unsupported record type: " + n.toString(), "", CSMDoc::Message::Severity_Error); UniversalId::Type_None, "Unsupported record type: " + n.toString(), "", CSMDoc::Message::Severity_Error);
mReader->skipRecord(); reader->skipRecord();
} }
return false; return false;
@ -1424,6 +1408,8 @@ void CSMWorld::Data::finishLoading()
{ {
mTopicInfos.sort(mTopicInfoOrder); mTopicInfos.sort(mTopicInfoOrder);
mJournalInfos.sort(mJournalInfoOrder); mJournalInfos.sort(mJournalInfoOrder);
// Release file locks so we can actually write to the file we're editing
mReaders.clear();
} }
bool CSMWorld::Data::hasId(const std::string& id) const bool CSMWorld::Data::hasId(const std::string& id) const

View file

@ -33,6 +33,7 @@
#include <components/esm3/loadsoun.hpp> #include <components/esm3/loadsoun.hpp>
#include <components/esm3/loadspel.hpp> #include <components/esm3/loadspel.hpp>
#include <components/esm3/loadsscr.hpp> #include <components/esm3/loadsscr.hpp>
#include <components/esm3/readerscache.hpp>
#include <components/esm3/selectiongroup.hpp> #include <components/esm3/selectiongroup.hpp>
#include <components/files/multidircollection.hpp> #include <components/files/multidircollection.hpp>
#include <components/misc/algorithm.hpp> #include <components/misc/algorithm.hpp>
@ -122,12 +123,12 @@ namespace CSMWorld
std::unique_ptr<ActorAdapter> mActorAdapter; std::unique_ptr<ActorAdapter> mActorAdapter;
std::vector<QAbstractItemModel*> mModels; std::vector<QAbstractItemModel*> mModels;
std::map<UniversalId::Type, QAbstractItemModel*> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel*> mModelIndex;
ESM::ESMReader* mReader; ESM::ReadersCache mReaders;
const ESM::Dialogue* mDialogue; // last loaded dialogue const ESM::Dialogue* mDialogue; // last loaded dialogue
bool mBase; bool mBase;
bool mProject; bool mProject;
std::map<ESM::RefId, std::map<unsigned int, unsigned int>> mRefLoadCache; std::map<ESM::RefId, std::map<ESM::RefNum, unsigned int>> mRefLoadCache;
int mReaderIndex; std::size_t mReaderIndex;
Files::PathContainer mDataPaths; Files::PathContainer mDataPaths;
std::vector<std::string> mArchives; std::vector<std::string> mArchives;
@ -135,14 +136,11 @@ namespace CSMWorld
ResourcesManager mResourcesManager; ResourcesManager mResourcesManager;
std::shared_ptr<Resource::ResourceSystem> mResourceSystem; std::shared_ptr<Resource::ResourceSystem> mResourceSystem;
std::vector<std::shared_ptr<ESM::ESMReader>> mReaders;
InfoOrderByTopic mJournalInfoOrder; InfoOrderByTopic mJournalInfoOrder;
InfoOrderByTopic mTopicInfoOrder; InfoOrderByTopic mTopicInfoOrder;
// not implemented Data(const Data&) = delete;
Data(const Data&); Data& operator=(const Data&) = delete;
Data& operator=(const Data&);
void addModel(QAbstractItemModel* model, UniversalId::Type type, bool update = true); void addModel(QAbstractItemModel* model, UniversalId::Type type, bool update = true);

View file

@ -8,8 +8,6 @@ CSMWorld::CellRef::CellRef()
: mNew(true) : mNew(true)
, mIdNum(0) , mIdNum(0)
{ {
mRefNum.mIndex = 0;
mRefNum.mContentFile = 0;
} }
std::pair<int, int> CSMWorld::CellRef::getCellIndex() const std::pair<int, int> CSMWorld::CellRef::getCellIndex() const

View file

@ -8,6 +8,7 @@
#include <apps/opencs/model/world/collection.hpp> #include <apps/opencs/model/world/collection.hpp>
#include <components/esm3/cellref.hpp> #include <components/esm3/cellref.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
@ -47,7 +48,7 @@ namespace CSMWorld
} }
void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool base, void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool base,
std::map<unsigned int, unsigned int>& cache, CSMDoc::Messages& messages) std::map<ESM::RefNum, unsigned int>& cache, CSMDoc::Messages& messages)
{ {
Record<Cell> cell = mCells.getRecord(cellIndex); Record<Cell> cell = mCells.getRecord(cellIndex);
@ -64,6 +65,8 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b
if (!ESM::Cell::getNextRef(reader, ref, isDeleted, mref, isMoved)) if (!ESM::Cell::getNextRef(reader, ref, isDeleted, mref, isMoved))
break; break;
if (!base && reader.getIndex() == ref.mRefNum.mContentFile)
ref.mRefNum.mContentFile = -1;
// Keep mOriginalCell empty when in modified (as an indicator that the // Keep mOriginalCell empty when in modified (as an indicator that the
// original cell will always be equal the current cell). // original cell will always be equal the current cell).
ref.mOriginalCell = base ? cell2.mId : ESM::RefId(); ref.mOriginalCell = base ? cell2.mId : ESM::RefId();
@ -102,16 +105,7 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b
else else
ref.mCell = cell2.mId; ref.mCell = cell2.mId;
if (ref.mRefNum.mContentFile != -1 && !base) auto iter = cache.find(ref.mRefNum);
{
ref.mRefNum.mContentFile = ref.mRefNum.mIndex >> 24;
ref.mRefNum.mIndex &= 0x00ffffff;
}
unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff)
| (ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24;
std::map<unsigned int, unsigned int>::iterator iter = cache.find(refNum);
if (isMoved) if (isMoved)
{ {
@ -181,7 +175,7 @@ void CSMWorld::RefCollection::load(ESM::ESMReader& reader, int cellIndex, bool b
ref.mIdNum = mNextId; // FIXME: fragile ref.mIdNum = mNextId; // FIXME: fragile
ref.mId = ESM::RefId::stringRefId(getNewId()); ref.mId = ESM::RefId::stringRefId(getNewId());
cache.emplace(refNum, ref.mIdNum); cache.emplace(ref.mRefNum, ref.mIdNum);
auto record = std::make_unique<Record<CellRef>>(); auto record = std::make_unique<Record<CellRef>>();
record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
@ -305,10 +299,10 @@ void CSMWorld::RefCollection::cloneRecord(
copy->get().mId = destination; copy->get().mId = destination;
copy->get().mIdNum = extractIdNum(destination.getRefIdString()); copy->get().mIdNum = extractIdNum(destination.getRefIdString());
if (copy->get().mRefNum.mContentFile != 0) if (copy->get().mRefNum.hasContentFile())
{ {
mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(copy.get())->get().mIdNum, index)); mRefIndex.insert(std::make_pair(static_cast<Record<CellRef>*>(copy.get())->get().mIdNum, index));
copy->get().mRefNum.mContentFile = 0; copy->get().mRefNum.mContentFile = -1;
copy->get().mRefNum.mIndex = index; copy->get().mRefNum.mIndex = index;
} }
else else

View file

@ -55,7 +55,7 @@ namespace CSMWorld
{ {
} }
void load(ESM::ESMReader& reader, int cellIndex, bool base, std::map<unsigned int, unsigned int>& cache, void load(ESM::ESMReader& reader, int cellIndex, bool base, std::map<ESM::RefNum, unsigned int>& cache,
CSMDoc::Messages& messages); CSMDoc::Messages& messages);
///< Load a sequence of references. ///< Load a sequence of references.

View file

@ -86,4 +86,12 @@ namespace ESM
it->mState = State::Closed; it->mState = State::Closed;
} }
} }
void ReadersCache::clear()
{
mIndex.clear();
mBusyItems.clear();
mFreeItems.clear();
mClosedItems.clear();
}
} }

View file

@ -55,6 +55,8 @@ namespace ESM
BusyItem get(std::size_t index); BusyItem get(std::size_t index);
void clear();
private: private:
const std::size_t mCapacity; const std::size_t mCapacity;
std::map<std::size_t, std::list<Item>::iterator> mIndex; std::map<std::size_t, std::list<Item>::iterator> mIndex;