void PathGrid::load(ESMReader &esm) { esm.getHNT(data, "DATA", 12); cell = esm.getHNString("NAME"); // Remember this file position context = esm.getContext(); // Check that the sizes match up. Size = 16 * s2 (path points?) if (esm.isNextSub("PGRP")) { esm.skipHSub(); int size = esm.getSubSize(); if (size != 16 * data.s2) esm.fail("Path grid table size mismatch"); } // Size varies. Path grid chances? Connections? Multiples of 4 // suggest either int or two shorts, or perhaps a float. Study // it later. if (esm.isNextSub("PGRC")) { esm.skipHSub(); int size = esm.getSubSize(); if (size % 4 != 0) esm.fail("PGRC size not a multiple of 4"); } }
void ESM::CellRef::loadData(ESMReader &esm) { // Again, UNAM sometimes appears after NAME and sometimes later. // Or perhaps this UNAM means something different? mReferenceBlocked = -1; esm.getHNOT (mReferenceBlocked, "UNAM"); mScale = 1.0; esm.getHNOT (mScale, "XSCL"); mOwner = esm.getHNOString ("ANAM"); mGlobalVariable = esm.getHNOString ("BNAM"); mSoul = esm.getHNOString ("XSOL"); mFaction = esm.getHNOString ("CNAM"); mFactionRank = -2; esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; mChargeInt = -1; mEnchantmentCharge = -1; esm.getHNOT (mEnchantmentCharge, "XCHG"); esm.getHNOT (mChargeInt, "INTV"); esm.getHNOT (mGoldValue, "NAM9"); // Present for doors that teleport you to another cell. if (esm.isNextSub ("DODT")) { mTeleport = true; esm.getHT (mDoorDest); mDestCell = esm.getHNOString ("DNAM"); } else mTeleport = false; mLockLevel = 0; //Set to 0 to indicate no lock esm.getHNOT (mLockLevel, "FLTV"); mKey = esm.getHNOString ("KNAM"); mTrap = esm.getHNOString ("TNAM"); esm.getHNOT (mReferenceBlocked, "UNAM"); if (esm.isNextSub("FLTV")) // no longer used esm.skipHSub(); esm.getHNOT(mPos, "DATA", 24); if (esm.isNextSub("NAM0")) esm.skipHSub(); }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { esm.skipRecord(); // skip MVRF esm.skipRecord(); // skip CNDT // That should be it, I haven't seen any other fields yet. } ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); if (esm.isNextSub("DELE")) { esm.skipHSub(); deleted = true; } else deleted = false; return true; }
void ESM::DebugProfile::load (ESMReader& esm, bool &isDeleted) { isDeleted = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mId = esm.getHString(); break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::FourCC<'S','C','R','P'>::value: mScriptText = esm.getHString(); break; case ESM::FourCC<'F','L','A','G'>::value: esm.getHT(mFlags); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } }
void ESM::ObjectState::load (ESMReader &esm) { mVersion = esm.getFormat(); bool isDeleted; mRef.loadData(esm, isDeleted); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); if (mHasLocals) mLocals.load (esm); mEnabled = 1; esm.getHNOT (mEnabled, "ENAB"); mCount = 1; esm.getHNOT (mCount, "COUN"); esm.getHNOT (mPosition, "POS_", 24); if (esm.isNextSub("LROT")) esm.skipHSub(); // local rotation, no longer used // obsolete int unused; esm.getHNOT(unused, "LTIM"); // FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files mHasCustomState = true; esm.getHNOT (mHasCustomState, "HCUS"); }
void StartScript::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasData = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'D','A','T','A'>::value: mData = esm.getHString(); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME"); if (!hasData && !isDeleted) esm.fail("Missing DATA"); }
void Faction::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mReactions.clear(); for (int i=0;i<10;++i) mRanks[i].clear(); int rankCounter = 0; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: if (rankCounter >= 10) esm.fail("Rank out of range"); mRanks[rankCounter++] = esm.getHString(); break; case ESM::FourCC<'F','A','D','T'>::value: esm.getHT(mData, 240); if (mData.mIsHidden > 1) esm.fail("Unknown flag!"); hasData = true; break; case ESM::FourCC<'A','N','A','M'>::value: { std::string faction = esm.getHString(); int reaction; esm.getHNT(reaction, "INTV"); mReactions[faction] = reaction; break; } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing FADT subrecord"); }
void Cell::loadData(ESMReader &esm) { // Ignore this for now, it might mean we should delete the entire // cell? // TODO: treat the special case "another plugin moved this ref, but we want to delete it"! if (esm.isNextSub("DELE")) { esm.skipHSub(); } esm.getHNT(mData, "DATA", 12); }
void Book::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'B','K','D','T'>::value: esm.getHT(mData, 20); hasData = true; break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'I','T','E','X'>::value: mIcon = esm.getHString(); break; case ESM::FourCC<'E','N','A','M'>::value: mEnchant = esm.getHString(); break; case ESM::FourCC<'T','E','X','T'>::value: mText = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing BKDT subrecord"); }
void Cell::loadNameAndData(ESMReader &esm, bool &isDeleted) { isDeleted = false; blank(); bool hasData = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mName = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } if (!hasData) esm.fail("Missing DATA subrecord"); mCellId.mPaged = !(mData.mFlags & Interior); if (mCellId.mPaged) { mCellId.mWorldspace = ESM::CellId::sDefaultWorldspace; mCellId.mIndex.mX = mData.mX; mCellId.mIndex.mY = mData.mY; } else { mCellId.mWorldspace = Misc::StringUtils::lowerCase (mName); mCellId.mIndex.mX = 0; mCellId.mIndex.mY = 0; } }
void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, // because any item can theoretically be moved by a script. if (esm.isNextSub ("NAM0")) esm.skipHSub(); if (wideRefNum) esm.getHNT (mRefNum, "FRMR", 8); else esm.getHNT (mRefNum.mIndex, "FRMR"); mRefID = esm.getHNString ("NAME"); }
void printRaw(ESMReader &esm) { while(esm.hasMoreRecs()) { NAME n = esm.getRecName(); cout << "Record: " << n.toString() << endl; esm.getRecHeader(); while(esm.hasMoreSubs()) { uint64_t offs = esm.getOffset(); esm.getSubName(); esm.skipHSub(); n = esm.retSubName(); cout << " " << n.toString() << " - " << esm.getSubSize() << " bytes @ 0x" << hex << offs << "\n"; } } }
void Race::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPowers.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','A','D','T'>::value: esm.getHT(mData, 140); hasData = true; break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::FourCC<'N','P','C','S'>::value: mPowers.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing RADT subrecord"); }
void Spell::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEffects.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','P','D','T'>::value: esm.getHT(mData, 12); hasData = true; break; case ESM::FourCC<'E','N','A','M'>::value: ENAMstruct s; esm.getHT(s, 24); mEffects.mList.push_back(s); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing SPDT subrecord"); }
void Door::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'S','N','A','M'>::value: mOpenSound = esm.getHString(); break; case ESM::FourCC<'A','N','A','M'>::value: mCloseSound = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); }
void Class::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'C','L','D','T'>::value: esm.getHT(mData, 60); if (mData.mIsPlayable > 1) esm.fail("Unknown bool value"); hasData = true; break; case ESM::FourCC<'D','E','S','C'>::value: mDescription = esm.getHString(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing CLDT subrecord"); }
void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, // because any item can theoretically be moved by a script. if (esm.isNextSub ("NAM0")) esm.skipHSub(); blank(); mRefNum.load (esm, wideRefNum); mRefID = esm.getHNOString ("NAME"); if (mRefID.empty()) { std::ios::fmtflags f(std::cerr.flags()); std::cerr << "Warning: got CellRef with empty RefId in " << esm.getName() << " 0x" << std::hex << esm.getFileOffset() << std::endl; std::cerr.flags(f); } }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { if (ignoreMoves) { esm.getHT (mref->mRefNum.mIndex); esm.getHNOT (mref->mTarget, "CNDT"); adjustRefNum (mref->mRefNum, esm); } else { // skip rest of cell record (moved references), they are handled elsewhere esm.skipRecord(); // skip MVRF, CNDT return false; } } ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); if (esm.isNextSub("DELE")) { esm.skipHSub(); deleted = true; } else deleted = false; return true; }
void Enchantment::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mEffects.mList.clear(); bool hasName = false; bool hasData = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'E','N','D','T'>::value: esm.getHT(mData, 16); hasData = true; break; case ESM::FourCC<'E','N','A','M'>::value: mEffects.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasData && !isDeleted) esm.fail("Missing ENDT subrecord"); }
void Cell::load(ESMReader &esm) { // Ignore this for now, it might mean we should delete the entire // cell? if (esm.isNextSub("DELE")) esm.skipHSub(); esm.getHNT(data, "DATA", 12); // Water level water = 0; if (data.flags & Interior) { // Interior cells if (esm.isNextSub("INTV") || esm.isNextSub("WHGT")) esm.getHT(water); // Quasi-exterior cells have a region (which determines the // weather), pure interior cells have ambient lighting // instead. if (data.flags & QuasiEx) region = esm.getHNOString("RGNN"); else esm.getHNT(ambi, "AMBI", 16); } else { // Exterior cells region = esm.getHNOString("RGNN"); esm.getHNOT(mapColor, "NAM5"); } // Save position of the cell references and move on context = esm.getContext(); esm.skipRecord(); }
void ESM::CreatureStats::load (ESMReader &esm) { for (int i=0; i<8; ++i) mAttributes[i].load (esm); for (int i=0; i<3; ++i) mDynamic[i].load (esm); mGoldPool = 0; esm.getHNOT (mGoldPool, "GOLD"); mTradeTime.mDay = 0; mTradeTime.mHour = 0; esm.getHNOT (mTradeTime, "TIME"); mDead = false; esm.getHNOT (mDead, "DEAD"); mDied = false; esm.getHNOT (mDied, "DIED"); mMurdered = false; esm.getHNOT (mMurdered, "MURD"); mFriendlyHits = 0; esm.getHNOT (mFriendlyHits, "FRHT"); mTalkedTo = false; esm.getHNOT (mTalkedTo, "TALK"); mAlarmed = false; esm.getHNOT (mAlarmed, "ALRM"); mAttacked = false; esm.getHNOT (mAttacked, "ATKD"); if (esm.isNextSub("HOST")) esm.skipHSub(); // Hostile, no longer used mAttackingOrSpell = false; esm.getHNOT (mAttackingOrSpell, "ATCK"); mKnockdown = false; esm.getHNOT (mKnockdown, "KNCK"); mKnockdownOneFrame = false; esm.getHNOT (mKnockdownOneFrame, "KNC1"); mKnockdownOverOneFrame = false; esm.getHNOT (mKnockdownOverOneFrame, "KNCO"); mHitRecovery = false; esm.getHNOT (mHitRecovery, "HITR"); mBlock = false; esm.getHNOT (mBlock, "BLCK"); mMovementFlags = 0; esm.getHNOT (mMovementFlags, "MOVE"); mAttackStrength = 0; esm.getHNOT (mAttackStrength, "ASTR"); mFallHeight = 0; esm.getHNOT (mFallHeight, "FALL"); mLastHitObject = esm.getHNOString ("LHIT"); mLastHitAttemptObject = esm.getHNOString ("LHAT"); mRecalcDynamicStats = false; esm.getHNOT (mRecalcDynamicStats, "CALC"); mDrawState = 0; esm.getHNOT (mDrawState, "DRAW"); mLevel = 1; esm.getHNOT (mLevel, "LEVL"); mActorId = -1; esm.getHNOT (mActorId, "ACID"); mDeathAnimation = 0; esm.getHNOT (mDeathAnimation, "DANM"); mSpells.load(esm); mActiveSpells.load(esm); mAiSequence.load(esm); mMagicEffects.load(esm); while (esm.isNextSub("SUMM")) { int magicEffect; esm.getHT(magicEffect); std::string source = esm.getHNOString("SOUR"); int actorId; esm.getHNT (actorId, "ACID"); mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId; } while (esm.isNextSub("GRAV")) { int actorId; esm.getHT(actorId); mSummonGraveyard.push_back(actorId); } mHasAiSettings = false; esm.getHNOT(mHasAiSettings, "AISE"); if (mHasAiSettings) { for (int i=0; i<4; ++i) mAiSettings[i].load(esm); } }
void LevelledListBase::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool hasName = false; bool hasList = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mFlags); break; case ESM::FourCC<'N','N','A','M'>::value: esm.getHT(mChanceNone); break; case ESM::FourCC<'I','N','D','X'>::value: { int length = 0; esm.getHT(length); mList.resize(length); // If this levelled list was already loaded by a previous content file, // we overwrite the list. Merging lists should probably be left to external tools, // with the limited amount of information there is in the records, all merging methods // will be flawed in some way. For a proper fix the ESM format would have to be changed // to actually track list changes instead of including the whole list for every file // that does something with that list. for (size_t i = 0; i < mList.size(); i++) { LevelItem &li = mList[i]; li.mId = esm.getHNString(mRecName); esm.getHNT(li.mLevel, "INTV"); } hasList = true; break; } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: { if (!hasList) { // Original engine ignores rest of the record, even if there are items following mList.clear(); esm.skipRecord(); } else { esm.fail("Unknown subrecord"); } break; } } } if (!hasName) esm.fail("Missing NAME subrecord"); }
void NPC::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mSpells.mList.clear(); mInventory.mList.clear(); mTransport.mList.clear(); mAiPackage.mList.clear(); mHasAI = false; bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: mRace = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mClass = esm.getHString(); break; case ESM::FourCC<'A','N','A','M'>::value: mFaction = esm.getHString(); break; case ESM::FourCC<'B','N','A','M'>::value: mHead = esm.getHString(); break; case ESM::FourCC<'K','N','A','M'>::value: mHair = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'N','P','D','T'>::value: hasNpdt = true; esm.getSubHeader(); if (esm.getSubSize() == 52) { mNpdtType = NPC_DEFAULT; esm.getExact(&mNpdt52, 52); } else if (esm.getSubSize() == 12) { mNpdtType = NPC_WITH_AUTOCALCULATED_STATS; esm.getExact(&mNpdt12, 12); } else esm.fail("NPC_NPDT must be 12 or 52 bytes long"); break; case ESM::FourCC<'F','L','A','G'>::value: hasFlags = true; esm.getHT(mFlags); break; case ESM::FourCC<'N','P','C','S'>::value: mSpells.add(esm); break; case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; case ESM::FourCC<'A','I','D','T'>::value: esm.getHExact(&mAiData, sizeof(mAiData)); mHasAI= true; break; case ESM::FourCC<'D','O','D','T'>::value: case ESM::FourCC<'D','N','A','M'>::value: mTransport.add(esm); break; case AI_Wander: case AI_Activate: case AI_Escort: case AI_Follow: case AI_Travel: case AI_CNDT: mAiPackage.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); }
void Land::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPlugin = esm.getIndex(); bool hasLocation = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'I','N','T','V'>::value: esm.getSubHeaderIs(8); esm.getT<int>(mX); esm.getT<int>(mY); hasLocation = true; break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mFlags); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } if (!hasLocation) esm.fail("Missing INTV subrecord"); mContext = esm.getContext(); // Skip the land data here. Load it when the cell is loaded. while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().val) { case ESM::FourCC<'V','N','M','L'>::value: esm.skipHSub(); mDataTypes |= DATA_VNML; break; case ESM::FourCC<'V','H','G','T'>::value: esm.skipHSub(); mDataTypes |= DATA_VHGT; break; case ESM::FourCC<'W','N','A','M'>::value: esm.skipHSub(); mDataTypes |= DATA_WNAM; break; case ESM::FourCC<'V','C','L','R'>::value: esm.skipHSub(); mDataTypes |= DATA_VCLR; break; case ESM::FourCC<'V','T','E','X'>::value: esm.skipHSub(); mDataTypes |= DATA_VTEX; break; default: esm.fail("Unknown subrecord"); break; } } mDataLoaded = 0; mLandData = NULL; }
// Load a record and add it to the list void load(ESMReader &esm) { esm.getSubNameIs("DATA"); esm.skipHSub(); script = esm.getHNString("NAME"); }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { esm.skipRecord(); // skip MVRF esm.skipRecord(); // skip CNDT // That should be it, I haven't seen any other fields yet. } // NAM0 sometimes appears here, sometimes further on ref.mNam0 = 0; if (esm.isNextSub("NAM0")) { esm.getHT(ref.mNam0); //esm.getHNOT(NAM0, "NAM0"); } esm.getHNT(ref.mRefnum, "FRMR"); ref.mRefID = esm.getHNString("NAME"); // Identify references belonging to a parent file and adapt the ID accordingly. int local = (ref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; if (local) { // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. ref.mRefnum &= 0x00ffffff; // delete old plugin ID const std::vector<Header::MasterData> &masters = esm.getGameFiles(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } else { // This is an addition by the present plugin. Set the corresponding plugin index. ref.mRefnum |= global << 24; // insert global plugin ID } // getHNOT will not change the existing value if the subrecord is // missing ref.mScale = 1.0; esm.getHNOT(ref.mScale, "XSCL"); // TODO: support loading references from saves, there are tons of keys not recognized yet. // The following is just an incomplete list. if (esm.isNextSub("ACTN")) esm.skipHSub(); if (esm.isNextSub("STPR")) esm.skipHSub(); if (esm.isNextSub("ACDT")) esm.skipHSub(); if (esm.isNextSub("ACSC")) esm.skipHSub(); if (esm.isNextSub("ACSL")) esm.skipHSub(); if (esm.isNextSub("CHRD")) esm.skipHSub(); else if (esm.isNextSub("CRED")) // ??? esm.skipHSub(); ref.mOwner = esm.getHNOString("ANAM"); ref.mGlob = esm.getHNOString("BNAM"); ref.mSoul = esm.getHNOString("XSOL"); ref.mFaction = esm.getHNOString("CNAM"); ref.mFactIndex = -2; esm.getHNOT(ref.mFactIndex, "INDX"); ref.mGoldValue = 1; ref.mCharge = -1; ref.mEnchantmentCharge = -1; esm.getHNOT(ref.mEnchantmentCharge, "XCHG"); esm.getHNOT(ref.mCharge, "INTV"); esm.getHNOT(ref.mGoldValue, "NAM9"); // Present for doors that teleport you to another cell. if (esm.isNextSub("DODT")) { ref.mTeleport = true; esm.getHT(ref.mDoorDest); ref.mDestCell = esm.getHNOString("DNAM"); } else { ref.mTeleport = false; } // Integer, despite the name suggesting otherwise ref.mLockLevel = -1; esm.getHNOT(ref.mLockLevel, "FLTV"); ref.mKey = esm.getHNOString("KNAM"); ref.mTrap = esm.getHNOString("TNAM"); ref.mReferenceBlocked = -1; ref.mFltv = 0; esm.getHNOT(ref.mReferenceBlocked, "UNAM"); esm.getHNOT(ref.mFltv, "FLTV"); esm.getHNOT(ref.mPos, "DATA", 24); // Number of references in the cell? Maximum once in each cell, // but not always at the beginning, and not always right. In other // words, completely useless. // Update: Well, maybe not completely useless. This might actually be // number_of_references + number_of_references_moved_here_Across_boundaries, // and could be helpful for collecting these weird moved references. if (esm.isNextSub("NAM0")) { esm.getHT(ref.mNam0); //esm.getHNOT(NAM0, "NAM0"); } if (esm.isNextSub("DELE")) { esm.skipHSub(); ref.mDeleted = 2; // Deleted, will not respawn. // TODO: find out when references do respawn. } else ref.mDeleted = 0; return true; }
void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) { isDeleted = false; bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::FourCC<'U','N','A','M'>::value: esm.getHT(mReferenceBlocked); break; case ESM::FourCC<'X','S','C','L'>::value: esm.getHT(mScale); break; case ESM::FourCC<'A','N','A','M'>::value: mOwner = esm.getHString(); break; case ESM::FourCC<'B','N','A','M'>::value: mGlobalVariable = esm.getHString(); break; case ESM::FourCC<'X','S','O','L'>::value: mSoul = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mFaction = esm.getHString(); break; case ESM::FourCC<'I','N','D','X'>::value: esm.getHT(mFactionRank); break; case ESM::FourCC<'X','C','H','G'>::value: esm.getHT(mEnchantmentCharge); break; case ESM::FourCC<'I','N','T','V'>::value: esm.getHT(mChargeInt); break; case ESM::FourCC<'N','A','M','9'>::value: esm.getHT(mGoldValue); break; case ESM::FourCC<'D','O','D','T'>::value: esm.getHT(mDoorDest); mTeleport = true; break; case ESM::FourCC<'D','N','A','M'>::value: mDestCell = esm.getHString(); break; case ESM::FourCC<'F','L','T','V'>::value: esm.getHT(mLockLevel); break; case ESM::FourCC<'K','N','A','M'>::value: mKey = esm.getHString(); break; case ESM::FourCC<'T','N','A','M'>::value: mTrap = esm.getHString(); break; case ESM::FourCC<'D','A','T','A'>::value: esm.getHT(mPos, 24); break; case ESM::FourCC<'N','A','M','0'>::value: esm.skipHSub(); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.cacheSubName(); isLoaded = true; break; } } }
void Script::load(ESMReader &esm) { SCHD data; esm.getHNT(data, "SCHD", 52); mData = data.mData; mId = data.mName.toString(); // List of local variables if (esm.isNextSub("SCVR")) { int s = mData.mStringTableSize; std::vector<char> tmp (s); esm.getHExact (&tmp[0], s); // Set up the list of variable names mVarNames.resize(mData.mNumShorts + mData.mNumLongs + mData.mNumFloats); // The tmp buffer is a null-byte separated string list, we // just have to pick out one string at a time. char* str = &tmp[0]; for (size_t i = 0; i < mVarNames.size(); i++) { // Support '\r' terminated strings like vanilla. See Bug #1324. char *termsym = strchr(str, '\r'); if(termsym) *termsym = '\0'; mVarNames[i] = std::string(str); str += mVarNames[i].size() + 1; if (str - &tmp[0] > s) { // Apparently SCVR subrecord is not used and variable names are // determined on the fly from the script text. Therefore don't throw // an exeption, just log an error and continue. std::stringstream ss; ss << "ESM Error: " << "String table overflow"; ss << "\n File: " << esm.getName(); ss << "\n Record: " << esm.getContext().recName.toString(); ss << "\n Subrecord: " << "SCVR"; ss << "\n Offset: 0x" << std::hex << esm.getFileOffset(); std::cerr << ss.str() << std::endl; break; } } } // Script mData mScriptData.resize(mData.mScriptDataSize); esm.getHNExact(&mScriptData[0], mScriptData.size(), "SCDT"); // Script text mScriptText = esm.getHNOString("SCTX"); // NOTE: A minor hack/workaround... // // MAO_Containers.esp from Morrowind Acoustic Overhaul has SCVR records // at the end (see Bug #1849). Since OpenMW does not use SCVR subrecords // for variable names just skip these as a quick fix. An alternative // solution would be to decode and validate SCVR subrecords even if they // appear here. if (esm.isNextSub("SCVR")) { esm.skipHSub(); } }
void NPC::load(ESMReader &esm, bool &isDeleted) { isDeleted = false; mPersistent = (esm.getRecordFlags() & 0x0400) != 0; mSpells.mList.clear(); mInventory.mList.clear(); mTransport.mList.clear(); mAiPackage.mList.clear(); mAiData.blank(); mAiData.mHello = mAiData.mFight = mAiData.mFlee = 30; bool hasName = false; bool hasNpdt = false; bool hasFlags = false; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().intval) { case ESM::SREC_NAME: mId = esm.getHString(); hasName = true; break; case ESM::FourCC<'M','O','D','L'>::value: mModel = esm.getHString(); break; case ESM::FourCC<'F','N','A','M'>::value: mName = esm.getHString(); break; case ESM::FourCC<'R','N','A','M'>::value: mRace = esm.getHString(); break; case ESM::FourCC<'C','N','A','M'>::value: mClass = esm.getHString(); break; case ESM::FourCC<'A','N','A','M'>::value: mFaction = esm.getHString(); break; case ESM::FourCC<'B','N','A','M'>::value: mHead = esm.getHString(); break; case ESM::FourCC<'K','N','A','M'>::value: mHair = esm.getHString(); break; case ESM::FourCC<'S','C','R','I'>::value: mScript = esm.getHString(); break; case ESM::FourCC<'N','P','D','T'>::value: hasNpdt = true; esm.getSubHeader(); if (esm.getSubSize() == 52) { mNpdtType = NPC_DEFAULT; esm.getExact(&mNpdt, 52); } else if (esm.getSubSize() == 12) { //Reading into temporary NPDTstruct12 object NPDTstruct12 npdt12; mNpdtType = NPC_WITH_AUTOCALCULATED_STATS; esm.getExact(&npdt12, 12); //Clearing the mNdpt struct to initialize all values blankNpdt(); //Swiching to an internal representation mNpdt.mLevel = npdt12.mLevel; mNpdt.mDisposition = npdt12.mDisposition; mNpdt.mReputation = npdt12.mReputation; mNpdt.mRank = npdt12.mRank; mNpdt.mGold = npdt12.mGold; } else esm.fail("NPC_NPDT must be 12 or 52 bytes long"); break; case ESM::FourCC<'F','L','A','G'>::value: hasFlags = true; esm.getHT(mFlags); break; case ESM::FourCC<'N','P','C','S'>::value: mSpells.add(esm); break; case ESM::FourCC<'N','P','C','O'>::value: mInventory.add(esm); break; case ESM::FourCC<'A','I','D','T'>::value: esm.getHExact(&mAiData, sizeof(mAiData)); break; case ESM::FourCC<'D','O','D','T'>::value: case ESM::FourCC<'D','N','A','M'>::value: mTransport.add(esm); break; case AI_Wander: case AI_Activate: case AI_Escort: case AI_Follow: case AI_Travel: case AI_CNDT: mAiPackage.add(esm); break; case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; break; default: esm.fail("Unknown subrecord"); break; } } if (!hasName) esm.fail("Missing NAME subrecord"); if (!hasNpdt && !isDeleted) esm.fail("Missing NPDT subrecord"); if (!hasFlags && !isDeleted) esm.fail("Missing FLAG subrecord"); }