mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-06 21:45:35 +00:00
Adjust FormId in FormIdRefId and for items in containers when loading saves
This commit is contained in:
parent
d2f16774d9
commit
9a9f9d7081
14 changed files with 57 additions and 44 deletions
|
@ -132,7 +132,7 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void write(ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
|
virtual void write(ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
|
||||||
|
|
||||||
virtual void readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap) = 0;
|
virtual void readRecord(ESM::ESMReader& reader, uint32_t type) = 0;
|
||||||
|
|
||||||
virtual void useDeathCamera() = 0;
|
virtual void useDeathCamera() = 0;
|
||||||
|
|
||||||
|
|
|
@ -240,20 +240,13 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GlobalScripts::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
bool GlobalScripts::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||||
{
|
{
|
||||||
if (type == ESM::REC_GSCR)
|
if (type == ESM::REC_GSCR)
|
||||||
{
|
{
|
||||||
ESM::GlobalScript script;
|
ESM::GlobalScript script;
|
||||||
script.load(reader);
|
script.load(reader);
|
||||||
|
|
||||||
if (script.mTargetRef.hasContentFile())
|
|
||||||
{
|
|
||||||
auto iter = contentFileMap.find(script.mTargetRef.mContentFile);
|
|
||||||
if (iter != contentFileMap.end())
|
|
||||||
script.mTargetRef.mContentFile = iter->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iter = mScripts.find(script.mId);
|
auto iter = mScripts.find(script.mId);
|
||||||
|
|
||||||
if (iter == mScripts.end())
|
if (iter == mScripts.end())
|
||||||
|
|
|
@ -78,7 +78,7 @@ namespace MWScript
|
||||||
|
|
||||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||||
|
|
||||||
bool readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);
|
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||||
///< Records for variables that do not exist are dropped silently.
|
///< Records for variables that do not exist are dropped silently.
|
||||||
///
|
///
|
||||||
/// \return Known type?
|
/// \return Known type?
|
||||||
|
|
|
@ -412,6 +412,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||||
"to the newest OpenMW version to load this file.");
|
"to the newest OpenMW version to load this file.");
|
||||||
|
|
||||||
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
|
||||||
|
reader.setContentFileMapping(&contentFileMap);
|
||||||
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
|
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
|
||||||
|
|
||||||
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
Loading::Listener& listener = *MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||||
|
@ -485,7 +486,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||||
case ESM::REC_CREA:
|
case ESM::REC_CREA:
|
||||||
case ESM::REC_CONT:
|
case ESM::REC_CONT:
|
||||||
case ESM::REC_RAND:
|
case ESM::REC_RAND:
|
||||||
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
|
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESM::REC_CAM_:
|
case ESM::REC_CAM_:
|
||||||
|
@ -494,8 +495,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
|
||||||
|
|
||||||
case ESM::REC_GSCR:
|
case ESM::REC_GSCR:
|
||||||
|
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(reader, n.toInt());
|
||||||
reader, n.toInt(), contentFileMap);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ESM::REC_GMAP:
|
case ESM::REC_GMAP:
|
||||||
|
|
|
@ -211,7 +211,7 @@ namespace
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void readReferenceCollection(ESM::ESMReader& reader, MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref,
|
void readReferenceCollection(ESM::ESMReader& reader, MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref,
|
||||||
const std::map<int, int>& contentFileMap, const MWWorld::ESMStore& esmStore, MWWorld::CellStore* cellstore)
|
const MWWorld::ESMStore& esmStore, MWWorld::CellStore* cellstore)
|
||||||
{
|
{
|
||||||
using StateType = typename RecordToState<T>::StateType;
|
using StateType = typename RecordToState<T>::StateType;
|
||||||
StateType state;
|
StateType state;
|
||||||
|
@ -219,15 +219,8 @@ namespace
|
||||||
state.load(reader);
|
state.load(reader);
|
||||||
|
|
||||||
// If the reference came from a content file, make sure this content file is loaded
|
// If the reference came from a content file, make sure this content file is loaded
|
||||||
if (state.mRef.mRefNum.hasContentFile())
|
if (!reader.applyContentFileMapping(state.mRef.mRefNum))
|
||||||
{
|
return; // content file has been removed -> skip
|
||||||
std::map<int, int>::const_iterator iter = contentFileMap.find(state.mRef.mRefNum.mContentFile);
|
|
||||||
|
|
||||||
if (iter == contentFileMap.end())
|
|
||||||
return; // content file has been removed -> skip
|
|
||||||
|
|
||||||
state.mRef.mRefNum.mContentFile = iter->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MWWorld::LiveCellRef<T>::checkState(state))
|
if (!MWWorld::LiveCellRef<T>::checkState(state))
|
||||||
return; // not valid anymore with current content files -> skip
|
return; // not valid anymore with current content files -> skip
|
||||||
|
@ -1017,8 +1010,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::readReferences(
|
void CellStore::readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback)
|
||||||
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)
|
|
||||||
{
|
{
|
||||||
mHasState = true;
|
mHasState = true;
|
||||||
|
|
||||||
|
@ -1047,12 +1039,12 @@ namespace MWWorld
|
||||||
if (type != 0)
|
if (type != 0)
|
||||||
{
|
{
|
||||||
bool foundCorrespondingStore = false;
|
bool foundCorrespondingStore = false;
|
||||||
Misc::tupleForEach(this->mCellStoreImp->mRefLists,
|
Misc::tupleForEach(
|
||||||
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore, type](auto&& x) {
|
this->mCellStoreImp->mRefLists, [&reader, this, &cref, &foundCorrespondingStore, type](auto&& x) {
|
||||||
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
|
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
|
||||||
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore](auto& store) {
|
[&reader, this, &cref, &foundCorrespondingStore](auto& store) {
|
||||||
foundCorrespondingStore = true;
|
foundCorrespondingStore = true;
|
||||||
readReferenceCollection(reader, store, cref, contentFileMap, mStore, this);
|
readReferenceCollection(reader, store, cref, mStore, this);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1066,14 +1058,14 @@ namespace MWWorld
|
||||||
reader.cacheSubName();
|
reader.cacheSubName();
|
||||||
ESM::RefNum refnum = reader.getFormId(true, "MVRF");
|
ESM::RefNum refnum = reader.getFormId(true, "MVRF");
|
||||||
ESM::RefId movedToId = reader.getCellId();
|
ESM::RefId movedToId = reader.getCellId();
|
||||||
if (refnum.hasContentFile())
|
if (!reader.applyContentFileMapping(refnum))
|
||||||
{
|
{
|
||||||
auto iter = contentFileMap.find(refnum.mContentFile);
|
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex
|
||||||
if (iter != contentFileMap.end())
|
<< " (content file no longer exists)";
|
||||||
refnum.mContentFile = iter->second;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for the reference. It might no longer exist if its content file was removed.
|
// Search for the reference. It might no longer exist if its content file was changed.
|
||||||
Ptr movedRef = MWBase::Environment::get().getWorldModel()->getPtr(refnum);
|
Ptr movedRef = MWBase::Environment::get().getWorldModel()->getPtr(refnum);
|
||||||
if (movedRef.isEmpty())
|
if (movedRef.isEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
@ -328,8 +328,7 @@ namespace MWWorld
|
||||||
|
|
||||||
/// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved
|
/// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved
|
||||||
/// references)
|
/// references)
|
||||||
void readReferences(
|
void readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback);
|
||||||
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback);
|
|
||||||
|
|
||||||
void respawn();
|
void respawn();
|
||||||
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.
|
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.
|
||||||
|
|
|
@ -486,7 +486,7 @@ namespace MWWorld
|
||||||
writer.endRecord(ESM::REC_CAM_);
|
writer.endRecord(ESM::REC_CAM_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
void World::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
@ -522,8 +522,7 @@ namespace MWWorld
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
|
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
|
||||||
&& !mWeatherManager->readRecord(reader, type)
|
&& !mWeatherManager->readRecord(reader, type) && !mWorldModel.readRecord(reader, type)
|
||||||
&& !mWorldModel.readRecord(reader, type, contentFileMap)
|
|
||||||
&& !mProjectileManager->readRecord(reader, type))
|
&& !mProjectileManager->readRecord(reader, type))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("unknown record in saved game");
|
throw std::runtime_error("unknown record in saved game");
|
||||||
|
|
|
@ -219,7 +219,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override;
|
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const override;
|
||||||
|
|
||||||
void readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap) override;
|
void readRecord(ESM::ESMReader& reader, uint32_t type) override;
|
||||||
|
|
||||||
// switch to POV before showing player's death animation
|
// switch to POV before showing player's death animation
|
||||||
void useDeathCamera() override;
|
void useDeathCamera() override;
|
||||||
|
|
|
@ -446,7 +446,7 @@ public:
|
||||||
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override { return mWorldModel.findCell(cellId); }
|
MWWorld::CellStore* getCellStore(const ESM::RefId& cellId) override { return mWorldModel.findCell(cellId); }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap)
|
bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type)
|
||||||
{
|
{
|
||||||
if (type == ESM::REC_CSTA)
|
if (type == ESM::REC_CSTA)
|
||||||
{
|
{
|
||||||
|
@ -472,7 +472,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
|
||||||
|
|
||||||
GetCellStoreCallback callback(*this);
|
GetCellStoreCallback callback(*this);
|
||||||
|
|
||||||
cellStore->readReferences(reader, contentFileMap, &callback);
|
cellStore->readReferences(reader, &callback);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||||
|
|
||||||
bool readRecord(ESM::ESMReader& reader, uint32_t type, const std::map<int, int>& contentFileMap);
|
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last
|
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last
|
||||||
|
|
|
@ -87,6 +87,18 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ESMReader::applyContentFileMapping(FormId& id)
|
||||||
|
{
|
||||||
|
if (mContentFileMapping && id.hasContentFile())
|
||||||
|
{
|
||||||
|
auto iter = mContentFileMapping->find(id.mContentFile);
|
||||||
|
if (iter == mContentFileMapping->end())
|
||||||
|
return false;
|
||||||
|
id.mContentFile = iter->second;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ESM::RefId ESMReader::getCellId()
|
ESM::RefId ESMReader::getCellId()
|
||||||
{
|
{
|
||||||
if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion)
|
if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion)
|
||||||
|
@ -485,7 +497,10 @@ namespace ESM
|
||||||
{
|
{
|
||||||
FormId formId{};
|
FormId formId{};
|
||||||
getTSized<8>(formId);
|
getTSized<8>(formId);
|
||||||
return RefId::formIdRefId(formId);
|
if (applyContentFileMapping(formId))
|
||||||
|
return RefId::formIdRefId(formId);
|
||||||
|
else
|
||||||
|
return RefId(); // content file was removed from load order
|
||||||
}
|
}
|
||||||
case RefIdType::Generated:
|
case RefIdType::Generated:
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <istream>
|
#include <istream>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -112,6 +113,13 @@ namespace ESM
|
||||||
void resolveParentFileIndices(ReadersCache& readers);
|
void resolveParentFileIndices(ReadersCache& readers);
|
||||||
const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; }
|
const std::vector<int>& getParentFileIndices() const { return mCtx.parentFileIndices; }
|
||||||
|
|
||||||
|
// Used only when loading saves to adjust FormIds if load order was changes.
|
||||||
|
void setContentFileMapping(const std::map<int, int>* mapping) { mContentFileMapping = mapping; }
|
||||||
|
const std::map<int, int>* getContentFileMapping();
|
||||||
|
|
||||||
|
// Returns false if content file not found.
|
||||||
|
bool applyContentFileMapping(FormId& id);
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
* Medium-level reading shortcuts
|
* Medium-level reading shortcuts
|
||||||
|
@ -368,6 +376,8 @@ namespace ESM
|
||||||
ToUTF8::Utf8Encoder* mEncoder;
|
ToUTF8::Utf8Encoder* mEncoder;
|
||||||
|
|
||||||
size_t mFileSize;
|
size_t mFileSize;
|
||||||
|
|
||||||
|
const std::map<int, int>* mContentFileMapping = nullptr;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,6 +19,7 @@ namespace ESM
|
||||||
mTargetId = esm.getHNORefId("TARG");
|
mTargetId = esm.getHNORefId("TARG");
|
||||||
if (esm.peekNextSub("FRMR"))
|
if (esm.peekNextSub("FRMR"))
|
||||||
mTargetRef = esm.getFormId(true, "FRMR");
|
mTargetRef = esm.getFormId(true, "FRMR");
|
||||||
|
esm.applyContentFileMapping(mTargetRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalScript::save(ESMWriter& esm) const
|
void GlobalScript::save(ESMWriter& esm) const
|
||||||
|
|
|
@ -47,6 +47,10 @@ namespace ESM
|
||||||
state.mRef.loadId(esm, true);
|
state.mRef.loadId(esm, true);
|
||||||
state.load(esm);
|
state.load(esm);
|
||||||
|
|
||||||
|
// Update content file index if load order was changed.
|
||||||
|
if (!esm.applyContentFileMapping(state.mRef.mRefNum))
|
||||||
|
state.mRef.mRefNum = FormId(); // content file removed; unset refnum, but keep object.
|
||||||
|
|
||||||
if (state.mCount == 0)
|
if (state.mCount == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue