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

Adjust FormId in FormIdRefId and for items in containers when loading saves

This commit is contained in:
Petr Mikheev 2023-07-30 01:22:47 +02:00
parent d2f16774d9
commit 9a9f9d7081
14 changed files with 57 additions and 44 deletions

View file

@ -132,7 +132,7 @@ namespace MWBase
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;

View file

@ -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)
{
ESM::GlobalScript script;
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);
if (iter == mScripts.end())

View file

@ -78,7 +78,7 @@ namespace MWScript
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.
///
/// \return Known type?

View file

@ -412,6 +412,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
"to the newest OpenMW version to load this file.");
std::map<int, int> contentFileMap = buildContentFileIndexMap(reader);
reader.setContentFileMapping(&contentFileMap);
MWBase::Environment::get().getLuaManager()->setContentFileMapping(contentFileMap);
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_CONT:
case ESM::REC_RAND:
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt(), contentFileMap);
MWBase::Environment::get().getWorld()->readRecord(reader, n.toInt());
break;
case ESM::REC_CAM_:
@ -494,8 +495,7 @@ void MWState::StateManager::loadGame(const Character* character, const std::file
case ESM::REC_GSCR:
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(
reader, n.toInt(), contentFileMap);
MWBase::Environment::get().getScriptManager()->getGlobalScripts().readRecord(reader, n.toInt());
break;
case ESM::REC_GMAP:

View file

@ -211,7 +211,7 @@ namespace
template <typename T>
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;
StateType state;
@ -219,15 +219,8 @@ namespace
state.load(reader);
// If the reference came from a content file, make sure this content file is loaded
if (state.mRef.mRefNum.hasContentFile())
{
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 (!reader.applyContentFileMapping(state.mRef.mRefNum))
return; // content file has been removed -> skip
if (!MWWorld::LiveCellRef<T>::checkState(state))
return; // not valid anymore with current content files -> skip
@ -1017,8 +1010,7 @@ namespace MWWorld
}
}
void CellStore::readReferences(
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)
void CellStore::readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback)
{
mHasState = true;
@ -1047,12 +1039,12 @@ namespace MWWorld
if (type != 0)
{
bool foundCorrespondingStore = false;
Misc::tupleForEach(this->mCellStoreImp->mRefLists,
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore, type](auto&& x) {
Misc::tupleForEach(
this->mCellStoreImp->mRefLists, [&reader, this, &cref, &foundCorrespondingStore, type](auto&& x) {
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore](auto& store) {
[&reader, this, &cref, &foundCorrespondingStore](auto& store) {
foundCorrespondingStore = true;
readReferenceCollection(reader, store, cref, contentFileMap, mStore, this);
readReferenceCollection(reader, store, cref, mStore, this);
});
});
@ -1066,14 +1058,14 @@ namespace MWWorld
reader.cacheSubName();
ESM::RefNum refnum = reader.getFormId(true, "MVRF");
ESM::RefId movedToId = reader.getCellId();
if (refnum.hasContentFile())
if (!reader.applyContentFileMapping(refnum))
{
auto iter = contentFileMap.find(refnum.mContentFile);
if (iter != contentFileMap.end())
refnum.mContentFile = iter->second;
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex
<< " (content file no longer exists)";
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);
if (movedRef.isEmpty())
{

View file

@ -328,8 +328,7 @@ namespace MWWorld
/// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved
/// references)
void readReferences(
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback);
void readReferences(ESM::ESMReader& reader, GetCellStoreCallback* callback);
void respawn();
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.

View file

@ -486,7 +486,7 @@ namespace MWWorld
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)
{
@ -522,8 +522,7 @@ namespace MWWorld
break;
default:
if (!mStore.readRecord(reader, type) && !mGlobalVariables.readRecord(reader, type)
&& !mWeatherManager->readRecord(reader, type)
&& !mWorldModel.readRecord(reader, type, contentFileMap)
&& !mWeatherManager->readRecord(reader, type) && !mWorldModel.readRecord(reader, type)
&& !mProjectileManager->readRecord(reader, type))
{
throw std::runtime_error("unknown record in saved game");

View file

@ -219,7 +219,7 @@ namespace MWWorld
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
void useDeathCamera() override;

View file

@ -446,7 +446,7 @@ public:
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)
{
@ -472,7 +472,7 @@ bool MWWorld::WorldModel::readRecord(ESM::ESMReader& reader, uint32_t type, cons
GetCellStoreCallback callback(*this);
cellStore->readReferences(reader, contentFileMap, &callback);
cellStore->readReferences(reader, &callback);
return true;
}

View file

@ -99,7 +99,7 @@ namespace MWWorld
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:
PtrRegistry mPtrRegistry; // defined before mCells because during destruction it should be the last

View file

@ -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()
{
if (mHeader.mFormatVersion <= ESM::MaxUseEsmCellIdFormatVersion)
@ -485,7 +497,10 @@ namespace ESM
{
FormId 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:
{

View file

@ -4,6 +4,7 @@
#include <cstdint>
#include <filesystem>
#include <istream>
#include <map>
#include <memory>
#include <type_traits>
#include <vector>
@ -112,6 +113,13 @@ namespace ESM
void resolveParentFileIndices(ReadersCache& readers);
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
@ -368,6 +376,8 @@ namespace ESM
ToUTF8::Utf8Encoder* mEncoder;
size_t mFileSize;
const std::map<int, int>* mContentFileMapping = nullptr;
};
}
#endif

View file

@ -19,6 +19,7 @@ namespace ESM
mTargetId = esm.getHNORefId("TARG");
if (esm.peekNextSub("FRMR"))
mTargetRef = esm.getFormId(true, "FRMR");
esm.applyContentFileMapping(mTargetRef);
}
void GlobalScript::save(ESMWriter& esm) const

View file

@ -47,6 +47,10 @@ namespace ESM
state.mRef.loadId(esm, true);
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)
continue;