void AiFollow::load(ESMReader &esm) { esm.getHNT (mData, "DATA"); mTargetId = esm.getHNString("TARG"); mTargetActorId = -1; esm.getHNOT (mTargetActorId, "TAID"); esm.getHNT (mRemainingDuration, "DURA"); mCellId = esm.getHNOString ("CELL"); esm.getHNT (mAlwaysFollow, "ALWY"); mCommanded = false; esm.getHNOT (mCommanded, "CMND"); mActive = false; esm.getHNOT (mActive, "ACTV"); }
void Clothing::load(ESMReader &esm) { model = esm.getHNString("MODL"); name = esm.getHNOString("FNAM"); esm.getHNT(data, "CTDT", 12); script = esm.getHNOString("SCRI"); icon = esm.getHNOString("ITEX"); parts.load(esm); enchant = esm.getHNOString("ENAM"); }
void Tool::load(ESMReader &esm) { model = esm.getHNString("MODL"); name = esm.getHNString("FNAM"); esm.getSubName(); NAME n = esm.retSubName(); // The data name varies, RIDT for repair items, LKDT for lock // picks, PBDT for probes esm.getHT(data, 16); if (n == "RIDT") { // Swap t.data.quality and t.data.uses for repair items (sigh) float tmp = *((float*) &data.uses); data.uses = *((int*) &data.quality); data.quality = tmp; } script = esm.getHNOString("SCRI"); icon = esm.getHNOString("ITEX"); }
void NPC::load(ESMReader &esm, const std::string& id) { mId = id; npdt52.gold = -10; model = esm.getHNOString("MODL"); name = esm.getHNOString("FNAM"); race = esm.getHNString("RNAM"); cls = esm.getHNString("CNAM"); faction = esm.getHNString("ANAM"); head = esm.getHNString("BNAM"); hair = esm.getHNString("KNAM"); script = esm.getHNOString("SCRI"); esm.getSubNameIs("NPDT"); esm.getSubHeader(); if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52); else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12); else esm.fail("NPC_NPDT must be 12 or 52 bytes long"); esm.getHNT(flags, "FLAG"); inventory.load(esm); spells.load(esm); if(esm.isNextSub("AIDT")) { esm.getHExact(&AI, sizeof(AI)); hasAI = true; } else hasAI = false; esm.skipRecord(); }
void Faction::load(ESMReader &esm) { mName = esm.getHNString("FNAM"); // Read rank names. These are optional. int i = 0; while (esm.isNextSub("RNAM") && i < 10) mRanks[i++] = esm.getHString(); // Main data struct esm.getHNT(mData, "FADT", 240); if (mData.mIsHidden > 1) esm.fail("Unknown flag!"); // Read faction response values while (esm.hasMoreSubs()) { Reaction r; r.mFaction = esm.getHNString("ANAM"); esm.getHNT(r.mReaction, "INTV"); mReactions.push_back(r); } }
void Container::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); mName = esm.getHNOString("FNAM"); esm.getHNT(mWeight, "CNDT", 4); esm.getHNT(mFlags, "FLAG", 4); if (mFlags & 0xf4) esm.fail("Unknown flags"); if (!(mFlags & 0x8)) esm.fail("Flag 8 not set"); mScript = esm.getHNOString("SCRI"); mInventory.load(esm); }
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 Creature::load(ESMReader &esm, const std::string& id) { mId = id; model = esm.getHNString("MODL"); original = esm.getHNOString("CNAM"); name = esm.getHNOString("FNAM"); script = esm.getHNOString("SCRI"); esm.getHNT(data, "NPDT", 96); esm.getHNT(flags, "FLAG"); scale = 1.0; esm.getHNOT(scale, "XSCL"); inventory.load(esm); if (esm.isNextSub("AIDT")) { esm.getHExact(&AI, sizeof(AI)); hasAI = true; } else hasAI = false; // More subrecords: // AI_W - wander (14 bytes, i don't understand it) // short distance // byte duration // byte timeOfDay // byte idle[10] // // Rest is optional: // AI_T - travel? // AI_F - follow? // AI_E - escort? // AI_A - activate? esm.skipRecord(); }
void Region::load(ESMReader &esm) { mName = esm.getHNString("FNAM"); if (esm.getVer() == VER_12) esm.getHNExact(&mData, sizeof(mData) - 2, "WEAT"); else if (esm.getVer() == VER_13) esm.getHNExact(&mData, sizeof(mData), "WEAT"); else esm.fail("Don't know what to do in this version"); mSleepList = esm.getHNOString("BNAM"); esm.getHNT(mMapColor, "CNAM"); while (esm.hasMoreSubs()) { SoundRef sr; esm.getHNT(sr, "SNAM", 33); mSoundList.push_back(sr); } }
void ESM::DebugProfile::load (ESMReader& esm) { mDescription = esm.getHNString ("DESC"); mScriptText = esm.getHNString ("SCRP"); esm.getHNT (mFlags, "FLAG"); }
void AiActivate::load(ESMReader &esm) { mTargetId = esm.getHNString("TARG"); }
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 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 Pathgrid::load(ESMReader &esm) { esm.getHNT(mData, "DATA", 12); mCell = esm.getHNString("NAME"); // keep track of total connections so we can reserve edge vector size int edgeCount = 0; if (esm.isNextSub("PGRP")) { esm.getSubHeader(); int size = esm.getSubSize(); // Check that the sizes match up. Size = 16 * s2 (path points) if (size != static_cast<int> (sizeof(Point) * mData.mS2)) esm.fail("Path point subrecord size mismatch"); else { int pointCount = mData.mS2; mPoints.reserve(pointCount); for (int i = 0; i < pointCount; ++i) { Point p; esm.getExact(&p, sizeof(Point)); mPoints.push_back(p); edgeCount += p.mConnectionNum; } } } if (esm.isNextSub("PGRC")) { esm.getSubHeader(); int size = esm.getSubSize(); if (size % sizeof(int) != 0) esm.fail("PGRC size not a multiple of 4"); else { int rawConnNum = size / sizeof(int); std::vector<int> rawConnections; rawConnections.reserve(rawConnNum); for (int i = 0; i < rawConnNum; ++i) { int currentValue; esm.getT(currentValue); rawConnections.push_back(currentValue); } std::vector<int>::const_iterator rawIt = rawConnections.begin(); int pointIndex = 0; mEdges.reserve(edgeCount); for(PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it, ++pointIndex) { unsigned char connectionNum = (*it).mConnectionNum; for (int i = 0; i < connectionNum; ++i) { Edge edge; edge.mV0 = pointIndex; edge.mV1 = *rawIt; ++rawIt; mEdges.push_back(edge); } } } } }
void DialInfo::load(ESMReader &esm) { mId = esm.getHNString("INAM"); mPrev = esm.getHNString("PNAM"); mNext = esm.getHNString("NNAM"); // Not present if deleted if (esm.isNextSub("DATA")) { esm.getHT(mData, 12); } // What follows is somewhat spaghetti-ish, but it's worth if for // an extra speedup. INFO is by far the most common record type. // subName is a reference to the original, so it changes whenever // a new sub name is read. esm.isEmptyOrGetName() will get the // next name for us, or return true if there are no more records. esm.getSubName(); const NAME &subName = esm.retSubName(); if (subName.val == REC_ONAM) { mActor = esm.getHString(); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_RNAM) { mRace = esm.getHString(); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_CNAM) { mClass = esm.getHString(); if (esm.isEmptyOrGetName()) return; } mFactionLess = false; if (subName.val == REC_FNAM) { mNpcFaction = esm.getHString(); if (mNpcFaction == "FFFF") mFactionLess = true; if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_ANAM) { mCell = esm.getHString(); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_DNAM) { mPcFaction = esm.getHString(); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_SNAM) { mSound = esm.getHString(); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_NAME) { mResponse = esm.getHString(); if (esm.isEmptyOrGetName()) return; } while (subName.val == REC_SCVR) { SelectStruct ss; ss.mSelectRule = esm.getHString(); esm.isEmptyOrGetName(); if (subName.val == REC_INTV) { ss.mType = VT_Int; esm.getHT(ss.mI); } else if (subName.val == REC_FLTV) { ss.mType = VT_Float; esm.getHT(ss.mF); } else esm.fail( "INFO.SCVR must precede INTV or FLTV, not " + subName.toString()); mSelects.push_back(ss); if (esm.isEmptyOrGetName()) return; } if (subName.val == REC_BNAM) { mResultScript = esm.getHString(); if (esm.isEmptyOrGetName()) return; } mQuestStatus = QS_None; if (subName.val == REC_QSTN) mQuestStatus = QS_Name; else if (subName.val == REC_QSTF) mQuestStatus = QS_Finished; else if (subName.val == REC_QSTR) mQuestStatus = QS_Restart; else if (subName.val == REC_DELE) mQuestStatus = QS_Deleted; else esm.fail( "Don't know what to do with " + subName.toString() + " in INFO " + mId); if (mQuestStatus != QS_None) // Skip rest of record esm.skipRecord(); }
// Load a record and add it to the list void load(ESMReader &esm) { esm.getSubNameIs("DATA"); esm.skipHSub(); script = esm.getHNString("NAME"); }
void BodyPart::load(ESMReader &esm) { model = esm.getHNString("MODL"); name = esm.getHNString("FNAM"); esm.getHNT(data, "BYDT", 4); }
void Activator::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); mName = esm.getHNString("FNAM"); mScript = esm.getHNOString("SCRI"); }
void load(ESMReader &esm) { model = esm.getHNString("MODL"); name = esm.getHNString("FNAM"); script = esm.getHNOString("SCRI"); }
void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); mRace = esm.getHNOString("FNAM"); esm.getHNT(mData, "BYDT", 4); }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref) { if (!esm.hasMoreSubs()) return false; // 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. { int i; esm.getHNOT(i, "NAM0"); } esm.getHNT(ref.refnum, "FRMR"); ref.refID = esm.getHNString("NAME"); // getHNOT will not change the existing value if the subrecord is // missing ref.scale = 1.0; esm.getHNOT(ref.scale, "XSCL"); ref.owner = esm.getHNOString("ANAM"); ref.glob = esm.getHNOString("BNAM"); ref.soul = esm.getHNOString("XSOL"); ref.faction = esm.getHNOString("CNAM"); ref.factIndex = -1; esm.getHNOT(ref.factIndex, "INDX"); ref.charge = -1.0; esm.getHNOT(ref.charge, "XCHG"); ref.intv = 0; ref.nam9 = 0; esm.getHNOT(ref.intv, "INTV"); esm.getHNOT(ref.nam9, "NAM9"); // Present for doors that teleport you to another cell. if (esm.isNextSub("DODT")) { ref.teleport = true; esm.getHT(ref.doorDest); ref.destCell = esm.getHNOString("DNAM"); } else ref.teleport = false; // Integer, despite the name suggesting otherwise ref.lockLevel = 0; esm.getHNOT(ref.lockLevel, "FLTV"); ref.key = esm.getHNOString("KNAM"); ref.trap = esm.getHNOString("TNAM"); ref.unam = 0; ref.fltv = 0; esm.getHNOT(ref.unam, "UNAM"); esm.getHNOT(ref.fltv, "FLTV"); esm.getHNT(ref.pos, "DATA", 24); return true; }