void Land::loadData(int flags) const { OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mMutex); // Try to load only available data flags = flags & mDataTypes; // Return if all required data is loaded if ((mDataLoaded & flags) == flags) { return; } // Create storage if nothing is loaded if (mLandData == NULL) { mLandData = new LandData; mLandData->mDataTypes = mDataTypes; } ESM::ESMReader reader; reader.restoreContext(mContext); if (reader.isNextSub("VNML")) { condLoad(reader, flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); } if (reader.isNextSub("VHGT")) { static VHGT vhgt; if (condLoad(reader, flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; mLandData->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; float colOffset = rowOffset; for (int x = 1; x < LAND_SIZE; x++) { colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; mLandData->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; } } mLandData->mUnk1 = vhgt.mUnk1; mLandData->mUnk2 = vhgt.mUnk2; } } if (reader.isNextSub("WNAM")) { condLoad(reader, flags, DATA_WNAM, mLandData->mWnam, 81); } if (reader.isNextSub("VCLR")) condLoad(reader, flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); if (reader.isNextSub("VTEX")) { static uint16_t vtex[LAND_NUM_TEXTURES]; if (condLoad(reader, flags, DATA_VTEX, vtex, sizeof(vtex))) { LandData::transposeTextureData(vtex, mLandData->mTextures); } } }
void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) { //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; ESM::MovedCellRef cMRef; cell->getNextMVRF(esm, cMRef); MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(mEsmStore->get<ESM::Cell>()); ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. bool deleted = false; cell->getNextRef(esm, ref, deleted); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefNum); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(ref); else *iter = ref; } }
void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type) { std::string id = reader.getHNOString ("NAME"); int index = searchId (id); if (reader.isNextSub ("DELE")) { reader.skipRecord(); if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user } else if (base) { mData.erase (index, 1); } else { mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted; } } else { if (index==-1) { // new record int index = mData.getAppendIndex (type); mData.appendRecord (type, id); RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); mData.load (localIndex, reader, base); mData.getRecord (localIndex).mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; } else { // old record RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); if (!base) if (mData.getRecord (localIndex).mState==RecordBase::State_Erased) throw std::logic_error ("attempt to access a deleted record"); mData.load (localIndex, reader, base); if (!base) mData.getRecord (localIndex).mState = RecordBase::State_Modified; } } }
void IdCollection<ESXRecordT>::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); if (reader.isNextSub ("DELE")) { int index = searchId (id); reader.skipRecord(); if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user } else if (base) { removeRows (index, 1); } else { mRecords[index].mState = RecordBase::State_Deleted; } } else { ESXRecordT record; record.mId = id; record.load (reader); int index = searchId (record.mId); if (index==-1) { // new record Record<ESXRecordT> record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; appendRecord (record2); } else { // old record Record<ESXRecordT>& record2 = mRecords[index]; if (base) record2.mBase = record; else record2.setModified (record); } } }
virtual void read(ESM::ESMReader &esm) { std::string itemid = esm.getHNString("NAME"); Misc::StringUtils::lowerCaseInPlace(itemid); while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM")) { if (esm.retSubName().toString() == "FNAM") { std::string factionid = esm.getHString(); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(factionid), true)); } else { std::string ownerid = esm.getHString(); mStolenItems[itemid].insert(std::make_pair(Misc::StringUtils::lowerCase(ownerid), false)); } } }
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); if (reader.isNextSub ("DELE")) { int index = Collection<ESXRecordT, IdAccessorT>::searchId (id); reader.skipRecord(); if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user } else if (base) { Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1); } else { Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index); record.mState = RecordBase::State_Deleted; this->setRecord (index, record); } } else { ESXRecordT record; int index = this->searchId (id); if (index==-1) IdAccessorT().getId (record) = id; else { record = this->getRecord (index).get(); } record.load (reader); load (record, base, index); } }
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + reader.getHNOString ("INAM"); if (reader.isNextSub ("DELE")) { int index = searchId (id); reader.skipRecord(); if (index==-1) { // deleting a record that does not exist // ignore it for now /// \todo report the problem to the user } else if (base) { removeRows (index, 1); } else { Record<Info> record = getRecord (index); record.mState = RecordBase::State_Deleted; setRecord (index, record); } } else { Info record; record.mTopicId = dialogue.mId; record.mId = id; record.load (reader); load (record, base); } }
void CellStore::readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap) { while (reader.isNextSub ("OBJE")) { unsigned int id = 0; reader.getHT (id); switch (id) { case ESM::REC_ACTI: readReferenceCollection<ESM::ObjectState> (reader, mActivators, contentFileMap); break; case ESM::REC_ALCH: readReferenceCollection<ESM::ObjectState> (reader, mPotions, contentFileMap); break; case ESM::REC_APPA: readReferenceCollection<ESM::ObjectState> (reader, mAppas, contentFileMap); break; case ESM::REC_ARMO: readReferenceCollection<ESM::ObjectState> (reader, mArmors, contentFileMap); break; case ESM::REC_BOOK: readReferenceCollection<ESM::ObjectState> (reader, mBooks, contentFileMap); break; case ESM::REC_CLOT: readReferenceCollection<ESM::ObjectState> (reader, mClothes, contentFileMap); break; case ESM::REC_CONT: readReferenceCollection<ESM::ContainerState> (reader, mContainers, contentFileMap); break; case ESM::REC_CREA: readReferenceCollection<ESM::CreatureState> (reader, mCreatures, contentFileMap); break; case ESM::REC_DOOR: readReferenceCollection<ESM::ObjectState> (reader, mDoors, contentFileMap); break; case ESM::REC_INGR: readReferenceCollection<ESM::ObjectState> (reader, mIngreds, contentFileMap); break; case ESM::REC_LEVC: readReferenceCollection<ESM::ObjectState> (reader, mCreatureLists, contentFileMap); break; case ESM::REC_LEVI: readReferenceCollection<ESM::ObjectState> (reader, mItemLists, contentFileMap); break; case ESM::REC_LIGH: readReferenceCollection<ESM::LightState> (reader, mLights, contentFileMap); break; case ESM::REC_LOCK: readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, contentFileMap); break; case ESM::REC_MISC: readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, contentFileMap); break; case ESM::REC_NPC_: readReferenceCollection<ESM::NpcState> (reader, mNpcs, contentFileMap); break; case ESM::REC_PROB: readReferenceCollection<ESM::ObjectState> (reader, mProbes, contentFileMap); break; case ESM::REC_REPA: readReferenceCollection<ESM::ObjectState> (reader, mRepairs, contentFileMap); break; case ESM::REC_STAT: readReferenceCollection<ESM::ObjectState> (reader, mStatics, contentFileMap); break; case ESM::REC_WEAP: readReferenceCollection<ESM::ObjectState> (reader, mWeapons, contentFileMap); break; default: throw std::runtime_error ("unknown type in cell reference section"); } } }
void CellStore::readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; while (reader.isNextSub ("OBJE")) { unsigned int unused; reader.getHT (unused); // load the RefID first so we know what type of object it is ESM::CellRef cref; cref.loadId(reader, true); int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; reader.skipHSubUntil("OBJE"); continue; } switch (type) { case ESM::REC_ACTI: readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap); break; case ESM::REC_BODY: readReferenceCollection<ESM::ObjectState> (reader, mBodyParts, cref, contentFileMap); break; default: throw std::runtime_error ("unknown type in cell reference section"); } } // Do another update here to make sure objects referred to by MVRF tags can be found // This update is only needed for old saves that used the old copy&delete way of moving objects updateMergedRefs(); while (reader.isNextSub("MVRF")) { reader.cacheSubName(); ESM::RefNum refnum; ESM::CellId movedTo; refnum.load(reader, true, "MVRF"); movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. SearchByRefNumVisitor visitor(refnum); forEachInternal(visitor); if (!visitor.mFound) { std::cerr << "Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; continue; } MWWorld::LiveCellRefBase* movedRef = visitor.mFound; CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == NULL) { std::cerr << "Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); continue; } if (otherCell == this) { // Should never happen unless someone's tampering with files. std::cerr << "Found invalid moved ref, ignoring" << std::endl; continue; } moveTo(MWWorld::Ptr(movedRef, this), otherCell); } }
void PCDT::load(ESM::ESMReader &esm) { while (esm.isNextSub("DNAM")) { mKnownDialogueTopics.push_back(esm.getHString()); } mHasMark = false; if (esm.isNextSub("MNAM")) { mHasMark = true; mMNAM = esm.getHString(); } esm.getHNT(mPNAM, "PNAM"); if (esm.isNextSub("SNAM")) esm.skipHSub(); if (esm.isNextSub("NAM9")) esm.skipHSub(); // Rest state. You shouldn't even be able to save during rest, but skip just in case. if (esm.isNextSub("RNAM")) /* int hoursLeft; float x, y, z; // resting position */ esm.skipHSub(); // 16 bytes mBounty = 0; esm.getHNOT(mBounty, "CNAM"); mBirthsign = esm.getHNOString("BNAM"); // Holds the names of the last used Alchemy apparatus. Don't need to import this ATM, // because our GUI auto-selects the best apparatus. if (esm.isNextSub("NAM0")) esm.skipHSub(); if (esm.isNextSub("NAM1")) esm.skipHSub(); if (esm.isNextSub("NAM2")) esm.skipHSub(); if (esm.isNextSub("NAM3")) esm.skipHSub(); mHasENAM = false; if (esm.isNextSub("ENAM")) { mHasENAM = true; esm.getHT(mENAM); } if (esm.isNextSub("LNAM")) esm.skipHSub(); while (esm.isNextSub("FNAM")) { FNAM fnam; esm.getHT(fnam); mFactions.push_back(fnam); } mHasAADT = false; if (esm.isNextSub("AADT")) // Attack animation data? { mHasAADT = true; esm.getHT(mAADT); } if (esm.isNextSub("KNAM")) esm.skipHSub(); // assigned Quick Keys, I think if (esm.isNextSub("ANIS")) esm.skipHSub(); // 16 bytes if (esm.isNextSub("WERE")) { // some werewolf data, 152 bytes // maybe current skills and attributes for werewolf form esm.getSubHeader(); esm.skip(152); } }
void ActorData::load(ESM::ESMReader &esm) { if (esm.isNextSub("ACTN")) esm.skipHSub(); if (esm.isNextSub("STPR")) esm.skipHSub(); if (esm.isNextSub("MNAM")) esm.skipHSub(); ESM::CellRef::loadData(esm); // FIXME: not all actors have this, add flag esm.getHNOT(mACDT, "ACDT"); ACSC acsc; esm.getHNOT(acsc, "ACSC"); esm.getHNOT(acsc, "ACSL"); if (esm.isNextSub("CSTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? if (esm.isNextSub("LSTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? // unsure at which point between LSTN and TGTN if (esm.isNextSub("CSHN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? // unsure if before or after CSTN/LSTN if (esm.isNextSub("LSHN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? while (esm.isNextSub("TGTN")) esm.skipHSub(); // "PlayerSaveGame", link to some object? while (esm.isNextSub("FGTN")) esm.getHString(); // fight target? // unsure at which point between TGTN and CRED if (esm.isNextSub("AADT")) { // occured when a creature was in the middle of its attack, 44 bytes esm.skipHSub(); } // unsure at which point between FGTN and CHRD if (esm.isNextSub("PWPC")) esm.skipHSub(); if (esm.isNextSub("PWPS")) esm.skipHSub(); // unsure at which point between LSTN and CHRD if (esm.isNextSub("APUD")) esm.skipHSub(); // 40 bytes, starts with string "ancestor guardian". maybe spellcasting in progress? if (esm.isNextSub("WNAM")) { std::string id = esm.getHString(); if (esm.isNextSub("XNAM")) mSelectedEnchantItem = esm.getHString(); else mSelectedSpell = id; if (esm.isNextSub("YNAM")) esm.skipHSub(); // 4 byte, 0 } // FIXME: not all actors have this, add flag if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); if (esm.isNextSub("CRED")) // creature only esm.getHExact(mCombatStats, 3*2*sizeof(int)); mSCRI.load(esm); if (esm.isNextSub("ND3D")) esm.skipHSub(); if (esm.isNextSub("ANIS")) esm.skipHSub(); }
void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) { listener->setProgressRange(1000); std::set<std::string> missing; ESM::Dialogue *dialogue = 0; /// \todo Move this to somewhere else. ESMReader? // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles(); std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]); std::string fname = mast.name; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); if (fname == fnamecandidate) { index = i; break; } } if (index == (int)~0) { // Tried to load a parent file that has not been loaded yet. This is bad, // the launcher should have taken care of this. std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name + ", but it has not been loaded yet. Please check your load order."; esm.fail(fstring); } mast.index = index; } // Loop through all records while(esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); esm.getRecHeader(); // Look up the record type. std::map<int, StoreBase *>::iterator it = mStores.find(n.val); if (it == mStores.end()) { if (n.val == ESM::REC_INFO) { std::string id = esm.getHNOString("INAM"); if (dialogue) { dialogue->mInfo.push_back(ESM::DialInfo()); dialogue->mInfo.back().mId = id; dialogue->mInfo.back().load(esm); } else { std::cerr << "error: info record without dialog" << std::endl; esm.skipRecord(); } } else if (n.val == ESM::REC_MGEF) { mMagicEffects.load (esm); } else if (n.val == ESM::REC_SKIL) { mSkills.load (esm); } else { // Not found (this would be an error later) esm.skipRecord(); missing.insert(n.toString()); } } else { // Load it std::string id = esm.getHNOString("NAME"); // ... unless it got deleted! This means that the following record // has been deleted, and trying to load it using standard assumptions // on the structure will (probably) fail. if (esm.isNextSub("DELE")) { esm.skipRecord(); it->second->eraseStatic(id); continue; } it->second->load(esm, id); if (n.val==ESM::REC_DIAL) { dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id)); } else { dialogue = 0; } // Insert the reference into the global lookup if (!id.empty() && isCacheableRecord(n.val)) { mIds[Misc::StringUtils::lowerCase (id)] = n.val; } } listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000); } /* This information isn't needed on screen. But keep the code around for debugging purposes later. cout << "\n" << mStores.size() << " record types:\n"; for(RecListList::iterator it = mStores.begin(); it != mStores.end(); it++) cout << " " << toStr(it->first) << ": " << it->second->getSize() << endl; cout << "\nNot implemented yet: "; for(set<string>::iterator it = missing.begin(); it != missing.end(); it++ ) cout << *it << " "; cout << endl; */ }
void Inventory::load(ESM::ESMReader &esm) { while (esm.isNextSub("NPCO")) { ESM::ContItem contItem; esm.getHT(contItem); InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; item.mRelativeEquipmentSlot = -1; unsigned int itemCount = std::abs(item.mCount); bool separateStacks = false; for (unsigned int i=0;i<itemCount;++i) { bool newStack = esm.isNextSub("XIDX"); if (newStack) { unsigned int idx; esm.getHT(idx); separateStacks = true; item.mCount = 1; } item.mSCRI.load(esm); // for XSOL and XCHG seen so far, but probably others too bool isDeleted = false; item.ESM::CellRef::loadData(esm, isDeleted); int charge=-1; esm.getHNOT(charge, "XHLT"); item.mChargeInt = charge; if (newStack) mItems.push_back(item); } if (!separateStacks) mItems.push_back(item); } // equipped items while (esm.isNextSub("WIDX")) { // note: same item can be equipped 2 items (e.g. 2 rings) // and will be *stacked* in the NPCO list, unlike openmw! // this is currently not handled properly. esm.getSubHeader(); int itemIndex; // index of the item in the NPCO list esm.getT(itemIndex); if (itemIndex < 0 || itemIndex >= int(mItems.size())) esm.fail("equipment item index out of range"); // appears to be a relative index for only the *possible* slots this item can be equipped in, // i.e. 0 most of the time int slotIndex; esm.getT(slotIndex); mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } }
void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) { listener->setProgressRange(1000); ESM::Dialogue *dialogue = 0; /// \todo Move this to somewhere else. ESMReader? // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles(); std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]); std::string fname = mast.name; int index = ~0; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) { index = i; break; } } if (index == (int)~0) { // Tried to load a parent file that has not been loaded yet. This is bad, // the launcher should have taken care of this. std::string fstring = "File " + esm.getName() + " asks for parent file " + masters[j].name + ", but it has not been loaded yet. Please check your load order."; esm.fail(fstring); } mast.index = index; } // Loop through all records while(esm.hasMoreRecs()) { ESM::NAME n = esm.getRecName(); esm.getRecHeader(); // Look up the record type. std::map<int, StoreBase *>::iterator it = mStores.find(n.val); if (it == mStores.end()) { if (n.val == ESM::REC_INFO) { if (dialogue) { dialogue->readInfo(esm, esm.getIndex() != 0); } else { std::cerr << "error: info record without dialog" << std::endl; esm.skipRecord(); } } else if (n.val == ESM::REC_MGEF) { mMagicEffects.load (esm); } else if (n.val == ESM::REC_SKIL) { mSkills.load (esm); } else if (n.val==ESM::REC_FILT || ESM::REC_DBGP) { // ignore project file only records esm.skipRecord(); } else { std::stringstream error; error << "Unknown record: " << n.toString(); throw std::runtime_error(error.str()); } } else { // Load it std::string id = esm.getHNOString("NAME"); // ... unless it got deleted! This means that the following record // has been deleted, and trying to load it using standard assumptions // on the structure will (probably) fail. if (esm.isNextSub("DELE")) { esm.skipRecord(); it->second->eraseStatic(id); continue; } it->second->load(esm, id); // DELE can also occur after the usual subrecords if (esm.isNextSub("DELE")) { esm.skipRecord(); it->second->eraseStatic(id); continue; } if (n.val==ESM::REC_DIAL) { dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id)); } else { dialogue = 0; } // Insert the reference into the global lookup if (!id.empty() && isCacheableRecord(n.val)) { mIds[Misc::StringUtils::lowerCase (id)] = n.val; } } listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000); } }
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id) { // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded! So first, proceed as usual. // All cells have a name record, even nameless exterior cells. std::string idLower = Misc::StringUtils::lowerCase(id); ESM::Cell *cell = new ESM::Cell; cell->mName = id; //First part of cell loading cell->preLoad(esm); //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; ESM::MovedCellRef cMRef; cell->getNextMVRF(esm, cMRef); MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(mEsmStore->get<ESM::Cell>()); ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. cell->getNextRef(esm, ref); std::string lowerCase; std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), (int(*)(int)) std::tolower); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(ref); else *iter = ref; } //Second part of cell loading cell->postLoad(esm); if(cell->mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower)); if (oldcell) { // push the new references on the list of references to manage oldcell->mContextList.push_back(cell->mContextList.at(0)); // copy list into new cell cell->mContextList = oldcell->mContextList; // have new cell replace old cell *oldcell = *cell; } else mInt[idLower] = *cell; } else { // Store exterior cells by grid position, try to merge with existing parent data. ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell->getGridX(), cell->getGridY())); if (oldcell) { // push the new references on the list of references to manage oldcell->mContextList.push_back(cell->mContextList.at(0)); // copy list into new cell cell->mContextList = oldcell->mContextList; // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); it++) { // remove reference from current leased ref tracker and add it to new cell ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum); if (itold != oldcell->mMovedRefs.end()) { ESM::MovedCellRef target0 = *itold; ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(target0.mTarget[0], target0.mTarget[1])); ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum); wipecell->mLeasedRefs.erase(it_lease); *itold = *it; } } cell->mMovedRefs = oldcell->mMovedRefs; // have new cell replace old cell *oldcell = *cell; } else mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; } delete cell; }