mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-05-09 12:07:51 +03:00
Merge branch 'master' into moveref
Conflicts: apps/opencs/model/world/columns.hpp apps/opencs/model/world/commands.cpp apps/opencs/model/world/commands.hpp apps/opencs/model/world/ref.cpp apps/opencs/view/world/dialoguesubview.cpp apps/opencs/view/world/dialoguesubview.hpp apps/opencs/view/world/util.cpp components/esm/cellref.hpp
This commit is contained in:
commit
f1a58994c6
889 changed files with 24666 additions and 10682 deletions
|
@ -11,38 +11,34 @@ namespace ESM
|
|||
mServices = 0;
|
||||
}
|
||||
|
||||
void AIPackageList::load(ESMReader &esm)
|
||||
void AIPackageList::add(ESMReader &esm)
|
||||
{
|
||||
mList.clear();
|
||||
while (esm.hasMoreSubs()) {
|
||||
// initialize every iteration
|
||||
AIPackage pack;
|
||||
esm.getSubName();
|
||||
if (esm.retSubName() == 0x54444e43) { // CNDT
|
||||
mList.back().mCellName = esm.getHString();
|
||||
} else if (esm.retSubName() == AI_Wander) {
|
||||
pack.mType = AI_Wander;
|
||||
esm.getHExact(&pack.mWander, 14);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Travel) {
|
||||
pack.mType = AI_Travel;
|
||||
esm.getHExact(&pack.mTravel, 16);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Escort ||
|
||||
esm.retSubName() == AI_Follow)
|
||||
{
|
||||
pack.mType =
|
||||
(esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow;
|
||||
esm.getHExact(&pack.mTarget, 48);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Activate) {
|
||||
pack.mType = AI_Activate;
|
||||
esm.getHExact(&pack.mActivate, 33);
|
||||
mList.push_back(pack);
|
||||
} else { // not AI package related data, so leave
|
||||
return;
|
||||
}
|
||||
AIPackage pack;
|
||||
if (esm.retSubName() == AI_CNDT) {
|
||||
mList.back().mCellName = esm.getHString();
|
||||
} else if (esm.retSubName() == AI_Wander) {
|
||||
pack.mType = AI_Wander;
|
||||
esm.getHExact(&pack.mWander, 14);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Travel) {
|
||||
pack.mType = AI_Travel;
|
||||
esm.getHExact(&pack.mTravel, 16);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Escort ||
|
||||
esm.retSubName() == AI_Follow)
|
||||
{
|
||||
pack.mType =
|
||||
(esm.retSubName() == AI_Escort) ? AI_Escort : AI_Follow;
|
||||
esm.getHExact(&pack.mTarget, 48);
|
||||
mList.push_back(pack);
|
||||
} else if (esm.retSubName() == AI_Activate) {
|
||||
pack.mType = AI_Activate;
|
||||
esm.getHExact(&pack.mActivate, 33);
|
||||
mList.push_back(pack);
|
||||
} else { // not AI package related data, so leave
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPackageList::save(ESMWriter &esm) const
|
||||
|
|
|
@ -63,7 +63,8 @@ namespace ESM
|
|||
AI_Travel = 0x545f4941,
|
||||
AI_Follow = 0x465f4941,
|
||||
AI_Escort = 0x455f4941,
|
||||
AI_Activate = 0x415f4941
|
||||
AI_Activate = 0x415f4941,
|
||||
AI_CNDT = 0x54444e43
|
||||
};
|
||||
|
||||
/// \note Used for storaging packages in a single container
|
||||
|
@ -90,11 +91,9 @@ namespace ESM
|
|||
{
|
||||
std::vector<AIPackage> mList;
|
||||
|
||||
/// \note This breaks consistency of subrecords reading:
|
||||
/// after calling it subrecord name is already read, so
|
||||
/// it needs to use retSubName() if needed. But, hey, there
|
||||
/// is only one field left (XSCL) and only two records uses AI
|
||||
void load(ESMReader &esm);
|
||||
/// Add a single AIPackage, assumes subrecord name was already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -28,4 +28,4 @@ namespace ESM
|
|||
bool operator!= (const CellId& left, const CellId& right);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,12 @@ void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
|
|||
|
||||
|
||||
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum, bool ignoreRefNum)
|
||||
{
|
||||
loadId(esm, wideRefNum);
|
||||
loadData(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.
|
||||
|
@ -38,7 +44,10 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum, bool ignoreRefNum)
|
|||
mRefNum.load (esm, wideRefNum);
|
||||
|
||||
mRefID = esm.getHNString ("NAME");
|
||||
}
|
||||
|
||||
void ESM::CellRef::loadData(ESMReader &esm)
|
||||
{
|
||||
// Again, UNAM sometimes appears after NAME and sometimes later.
|
||||
// Or perhaps this UNAM means something different?
|
||||
mReferenceBlocked = -1;
|
||||
|
@ -56,12 +65,12 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum, bool ignoreRefNum)
|
|||
esm.getHNOT (mFactionRank, "INDX");
|
||||
|
||||
mGoldValue = 1;
|
||||
mCharge = -1;
|
||||
mChargeInt = -1;
|
||||
mEnchantmentCharge = -1;
|
||||
|
||||
esm.getHNOT (mEnchantmentCharge, "XCHG");
|
||||
|
||||
esm.getHNOT (mCharge, "INTV");
|
||||
esm.getHNOT (mChargeInt, "INTV");
|
||||
|
||||
esm.getHNOT (mGoldValue, "NAM9");
|
||||
|
||||
|
@ -113,8 +122,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
|
|||
if (mEnchantmentCharge != -1)
|
||||
esm.writeHNT("XCHG", mEnchantmentCharge);
|
||||
|
||||
if (mCharge != -1)
|
||||
esm.writeHNT("INTV", mCharge);
|
||||
if (mChargeInt != -1)
|
||||
esm.writeHNT("INTV", mChargeInt);
|
||||
|
||||
if (mGoldValue != 1) {
|
||||
esm.writeHNT("NAM9", mGoldValue);
|
||||
|
@ -145,8 +154,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
|
|||
|
||||
void ESM::CellRef::blank()
|
||||
{
|
||||
mRefNum.mIndex = 0;
|
||||
mRefNum.mContentFile = -1;
|
||||
mRefNum.unset();
|
||||
mRefID.clear();
|
||||
mScale = 1;
|
||||
mOwner.clear();
|
||||
|
@ -154,14 +162,14 @@ void ESM::CellRef::blank()
|
|||
mSoul.clear();
|
||||
mFaction.clear();
|
||||
mFactionRank = -2;
|
||||
mCharge = 0;
|
||||
mEnchantmentCharge = 0;
|
||||
mChargeInt = -1;
|
||||
mEnchantmentCharge = -1;
|
||||
mGoldValue = 0;
|
||||
mDestCell.clear();
|
||||
mLockLevel = 0;
|
||||
mKey.clear();
|
||||
mTrap.clear();
|
||||
mReferenceBlocked = 0;
|
||||
mReferenceBlocked = -1;
|
||||
mTeleport = false;
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
|
|
|
@ -13,12 +13,21 @@ namespace ESM
|
|||
|
||||
struct RefNum
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
int mIndex;
|
||||
int mContentFile; // -1 no content file
|
||||
|
||||
void load (ESMReader& esm, bool wide = false);
|
||||
|
||||
void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const;
|
||||
=======
|
||||
unsigned int mIndex;
|
||||
int mContentFile;
|
||||
|
||||
enum { RefNum_NoContentFile = -1 };
|
||||
inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; }
|
||||
inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; }
|
||||
>>>>>>> master
|
||||
};
|
||||
|
||||
/* Cell reference. This represents ONE object (of many) inside the
|
||||
|
@ -59,7 +68,13 @@ namespace ESM
|
|||
|
||||
// For weapon or armor, this is the remaining item health.
|
||||
// For tools (lockpicks, probes, repair hammer) it is the remaining uses.
|
||||
int mCharge;
|
||||
// For lights it is remaining time.
|
||||
// This could be -1 if the charge was not touched yet (i.e. full).
|
||||
union
|
||||
{
|
||||
int mChargeInt; // Used by everything except lights
|
||||
float mChargeFloat; // Used only by lights
|
||||
};
|
||||
|
||||
// Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).
|
||||
float mEnchantmentCharge;
|
||||
|
@ -89,8 +104,14 @@ namespace ESM
|
|||
// Position and rotation of this object within the cell
|
||||
Position mPos;
|
||||
|
||||
/// Calls loadId and loadData
|
||||
void load (ESMReader& esm, bool wideRefNum = false);
|
||||
|
||||
void loadId (ESMReader& esm, bool wideRefNum = false);
|
||||
|
||||
/// Implicitly called by load
|
||||
void loadData (ESMReader& esm);
|
||||
|
||||
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const;
|
||||
|
||||
void blank();
|
||||
|
|
|
@ -13,4 +13,4 @@ void ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const
|
|||
ObjectState::save (esm, inInventory);
|
||||
|
||||
mInventory.save (esm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,16 +5,28 @@ void ESM::CreatureState::load (ESMReader &esm)
|
|||
{
|
||||
ObjectState::load (esm);
|
||||
|
||||
mInventory.load (esm);
|
||||
if (mHasCustomState)
|
||||
{
|
||||
mInventory.load (esm);
|
||||
|
||||
mCreatureStats.load (esm);
|
||||
mCreatureStats.load (esm);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const
|
||||
{
|
||||
ObjectState::save (esm, inInventory);
|
||||
|
||||
mInventory.save (esm);
|
||||
if (mHasCustomState)
|
||||
{
|
||||
mInventory.save (esm);
|
||||
|
||||
mCreatureStats.save (esm);
|
||||
}
|
||||
mCreatureStats.save (esm);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::CreatureState::blank()
|
||||
{
|
||||
ObjectState::blank();
|
||||
mCreatureStats.blank();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ namespace ESM
|
|||
InventoryState mInventory;
|
||||
CreatureStats mCreatureStats;
|
||||
|
||||
/// Initialize to default state
|
||||
void blank();
|
||||
|
||||
virtual void load (ESMReader &esm);
|
||||
virtual void save (ESMWriter &esm, bool inInventory = false) const;
|
||||
};
|
||||
|
|
|
@ -24,8 +24,8 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
mMurdered = false;
|
||||
esm.getHNOT (mMurdered, "MURD");
|
||||
|
||||
mFriendlyHits = 0;
|
||||
esm.getHNOT (mFriendlyHits, "FRHT");
|
||||
if (esm.isNextSub("FRHT"))
|
||||
esm.skipHSub(); // Friendly hits, no longer used
|
||||
|
||||
mTalkedTo = false;
|
||||
esm.getHNOT (mTalkedTo, "TALK");
|
||||
|
@ -94,9 +94,10 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
|||
{
|
||||
int magicEffect;
|
||||
esm.getHT(magicEffect);
|
||||
std::string source = esm.getHNOString("SOUR");
|
||||
int actorId;
|
||||
esm.getHNT (actorId, "ACID");
|
||||
mSummonedCreatureMap[magicEffect] = actorId;
|
||||
mSummonedCreatureMap[std::make_pair(magicEffect, source)] = actorId;
|
||||
}
|
||||
|
||||
while (esm.isNextSub("GRAV"))
|
||||
|
@ -139,9 +140,6 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
if (mMurdered)
|
||||
esm.writeHNT ("MURD", mMurdered);
|
||||
|
||||
if (mFriendlyHits)
|
||||
esm.writeHNT ("FRHT", mFriendlyHits);
|
||||
|
||||
if (mTalkedTo)
|
||||
esm.writeHNT ("TALK", mTalkedTo);
|
||||
|
||||
|
@ -204,9 +202,10 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
mAiSequence.save(esm);
|
||||
mMagicEffects.save(esm);
|
||||
|
||||
for (std::map<int, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it)
|
||||
for (std::map<std::pair<int, std::string>, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it)
|
||||
{
|
||||
esm.writeHNT ("SUMM", it->first);
|
||||
esm.writeHNT ("SUMM", it->first.first);
|
||||
esm.writeHNString ("SOUR", it->first.second);
|
||||
esm.writeHNT ("ACID", it->second);
|
||||
}
|
||||
|
||||
|
@ -216,6 +215,37 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
|||
}
|
||||
|
||||
esm.writeHNT("AISE", mHasAiSettings);
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i].save(esm);
|
||||
if (mHasAiSettings)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i].save(esm);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::CreatureStats::blank()
|
||||
{
|
||||
mTradeTime.mHour = 0;
|
||||
mTradeTime.mDay = 0;
|
||||
mGoldPool = 0;
|
||||
mActorId = -1;
|
||||
mHasAiSettings = false;
|
||||
mDead = false;
|
||||
mDied = false;
|
||||
mMurdered = false;
|
||||
mTalkedTo = false;
|
||||
mAlarmed = false;
|
||||
mAttacked = false;
|
||||
mAttackingOrSpell = false;
|
||||
mKnockdown = false;
|
||||
mKnockdownOneFrame = false;
|
||||
mKnockdownOverOneFrame = false;
|
||||
mHitRecovery = false;
|
||||
mBlock = false;
|
||||
mMovementFlags = 0;
|
||||
mAttackStrength = 0.f;
|
||||
mFallHeight = 0.f;
|
||||
mRecalcDynamicStats = false;
|
||||
mDrawState = 0;
|
||||
mDeathAnimation = 0;
|
||||
mLevel = 1;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace ESM
|
|||
bool mHasAiSettings;
|
||||
StatState<int> mAiSettings[4];
|
||||
|
||||
std::map<int, int> mSummonedCreatureMap;
|
||||
std::map<std::pair<int, std::string>, int> mSummonedCreatureMap;
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
||||
ESM::TimeStamp mTradeTime;
|
||||
|
@ -42,7 +42,6 @@ namespace ESM
|
|||
bool mDead;
|
||||
bool mDied;
|
||||
bool mMurdered;
|
||||
int mFriendlyHits;
|
||||
bool mTalkedTo;
|
||||
bool mAlarmed;
|
||||
bool mAttacked;
|
||||
|
@ -66,6 +65,9 @@ namespace ESM
|
|||
SpellState mSpells;
|
||||
ActiveSpells mActiveSpells;
|
||||
|
||||
/// Initialize to default state
|
||||
void blank();
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
};
|
||||
|
|
26
components/esm/custommarkerstate.cpp
Normal file
26
components/esm/custommarkerstate.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "custommarkerstate.hpp"
|
||||
|
||||
#include "esmwriter.hpp"
|
||||
#include "esmreader.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void CustomMarker::save(ESM::ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("POSX", mWorldX);
|
||||
esm.writeHNT("POSY", mWorldY);
|
||||
mCell.save(esm);
|
||||
if (!mNote.empty())
|
||||
esm.writeHNString("NOTE", mNote);
|
||||
}
|
||||
|
||||
void CustomMarker::load(ESM::ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mWorldX, "POSX");
|
||||
esm.getHNT(mWorldY, "POSY");
|
||||
mCell.load(esm);
|
||||
mNote = esm.getHNOString("NOTE");
|
||||
}
|
||||
|
||||
}
|
30
components/esm/custommarkerstate.hpp
Normal file
30
components/esm/custommarkerstate.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef OPENMW_ESM_CUSTOMMARKERSTATE_H
|
||||
#define OPENMW_ESM_CUSTOMMARKERSTATE_H
|
||||
|
||||
#include "cellid.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
// format 0, saved games only
|
||||
struct CustomMarker
|
||||
{
|
||||
float mWorldX;
|
||||
float mWorldY;
|
||||
|
||||
ESM::CellId mCell;
|
||||
|
||||
std::string mNote;
|
||||
|
||||
bool operator == (const CustomMarker& other)
|
||||
{
|
||||
return mNote == other.mNote && mCell == other.mCell && mWorldX == other.mWorldX && mWorldY == other.mWorldY;
|
||||
}
|
||||
|
||||
void load (ESM::ESMReader& reader);
|
||||
void save (ESM::ESMWriter& writer) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -96,15 +96,17 @@ enum RecNameInts
|
|||
REC_WEAP = 0x50414557,
|
||||
|
||||
// format 0 - saved games
|
||||
REC_SAVE = 0x45564153,
|
||||
REC_JOUR = 0x524f55a4,
|
||||
REC_QUES = 0x53455551,
|
||||
REC_GSCR = 0x52435347,
|
||||
REC_PLAY = 0x59414c50,
|
||||
REC_CSTA = 0x41545343,
|
||||
REC_GMAP = 0x50414d47,
|
||||
REC_DIAS = 0x53414944,
|
||||
REC_WTHR = 0x52485457,
|
||||
REC_SAVE = FourCC<'S','A','V','E'>::value,
|
||||
REC_JOUR_LEGACY = FourCC<0xa4,'U','O','R'>::value, // "\xa4UOR", rather than "JOUR", little oversight when magic numbers were
|
||||
// calculated by hand, needs to be supported for older files now
|
||||
REC_JOUR = FourCC<'J','O','U','R'>::value,
|
||||
REC_QUES = FourCC<'Q','U','E','S'>::value,
|
||||
REC_GSCR = FourCC<'G','S','C','R'>::value,
|
||||
REC_PLAY = FourCC<'P','L','A','Y'>::value,
|
||||
REC_CSTA = FourCC<'C','S','T','A'>::value,
|
||||
REC_GMAP = FourCC<'G','M','A','P'>::value,
|
||||
REC_DIAS = FourCC<'D','I','A','S'>::value,
|
||||
REC_WTHR = FourCC<'W','T','H','R'>::value,
|
||||
REC_KEYS = FourCC<'K','E','Y','S'>::value,
|
||||
REC_DYNA = FourCC<'D','Y','N','A'>::value,
|
||||
REC_ASPL = FourCC<'A','S','P','L'>::value,
|
||||
|
@ -114,9 +116,11 @@ enum RecNameInts
|
|||
REC_DCOU = FourCC<'D','C','O','U'>::value,
|
||||
REC_MARK = FourCC<'M','A','R','K'>::value,
|
||||
REC_ENAB = FourCC<'E','N','A','B'>::value,
|
||||
REC_CAM_ = FourCC<'C','A','M','_'>::value,
|
||||
REC_STLN = FourCC<'S','T','L','N'>::value,
|
||||
|
||||
// format 1
|
||||
REC_FILT = 0x544C4946,
|
||||
REC_FILT = FourCC<'F','I','L','T'>::value,
|
||||
REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files
|
||||
};
|
||||
|
||||
|
|
|
@ -13,13 +13,20 @@ void ESM::DialogueState::load (ESMReader &esm)
|
|||
{
|
||||
std::string faction = esm.getHString();
|
||||
|
||||
while (esm.isNextSub ("REAC"))
|
||||
while (esm.isNextSub("REA2"))
|
||||
{
|
||||
std::string faction2 = esm.getHString();
|
||||
int reaction;
|
||||
esm.getHNT(reaction, "INTV");
|
||||
mChangedFactionReaction[faction][faction2] = reaction;
|
||||
}
|
||||
|
||||
mModFactionReaction[faction][faction2] = reaction;
|
||||
// no longer used
|
||||
while (esm.isNextSub ("REAC"))
|
||||
{
|
||||
esm.skipHSub();
|
||||
esm.getSubName();
|
||||
esm.skipHSub();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,15 +39,15 @@ void ESM::DialogueState::save (ESMWriter &esm) const
|
|||
esm.writeHNString ("TOPI", *iter);
|
||||
}
|
||||
|
||||
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mModFactionReaction.begin();
|
||||
iter != mModFactionReaction.end(); ++iter)
|
||||
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mChangedFactionReaction.begin();
|
||||
iter != mChangedFactionReaction.end(); ++iter)
|
||||
{
|
||||
esm.writeHNString ("FACT", iter->first);
|
||||
|
||||
for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin();
|
||||
reactIter != iter->second.end(); ++reactIter)
|
||||
{
|
||||
esm.writeHNString ("REAC", reactIter->first);
|
||||
esm.writeHNString ("REA2", reactIter->first);
|
||||
esm.writeHNT ("INTV", reactIter->second);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@ namespace ESM
|
|||
|
||||
struct DialogueState
|
||||
{
|
||||
// must be lower case topic IDs
|
||||
std::vector<std::string> mKnownTopics;
|
||||
|
||||
std::map<std::string, std::map<std::string, int> > mModFactionReaction;
|
||||
// must be lower case faction IDs
|
||||
std::map<std::string, std::map<std::string, int> > mChangedFactionReaction;
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
|
|
|
@ -8,13 +8,18 @@ namespace ESM {
|
|||
void EffectList::load(ESMReader &esm)
|
||||
{
|
||||
mList.clear();
|
||||
ENAMstruct s;
|
||||
while (esm.isNextSub("ENAM")) {
|
||||
esm.getHT(s, 24);
|
||||
mList.push_back(s);
|
||||
add(esm);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectList::add(ESMReader &esm)
|
||||
{
|
||||
ENAMstruct s;
|
||||
esm.getHT(s, 24);
|
||||
mList.push_back(s);
|
||||
}
|
||||
|
||||
void EffectList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {
|
||||
|
|
|
@ -29,11 +29,15 @@ namespace ESM
|
|||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/// EffectList, ENAM subrecord
|
||||
struct EffectList
|
||||
{
|
||||
|
||||
std::vector<ENAMstruct> mList;
|
||||
|
||||
/// Load one effect, assumes subrecord name was already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
/// Load all effects
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <cstring>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libs/platform/string.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ template <int LEN>
|
|||
union NAME_T
|
||||
{
|
||||
char name[LEN];
|
||||
int32_t val;
|
||||
uint32_t val;
|
||||
|
||||
bool operator==(const char *str) const
|
||||
{
|
||||
|
@ -40,8 +40,8 @@ union NAME_T
|
|||
}
|
||||
bool operator!=(const std::string &str) const { return !((*this)==str); }
|
||||
|
||||
bool operator==(int v) const { return v == val; }
|
||||
bool operator!=(int v) const { return v != val; }
|
||||
bool operator==(uint32_t v) const { return v == val; }
|
||||
bool operator!=(uint32_t v) const { return v != val; }
|
||||
|
||||
std::string toString() const { return std::string(name, strnlen(name, LEN)); }
|
||||
|
||||
|
@ -53,18 +53,6 @@ typedef NAME_T<32> NAME32;
|
|||
typedef NAME_T<64> NAME64;
|
||||
typedef NAME_T<256> NAME256;
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
// Data that is only present in save game files
|
||||
struct SaveData
|
||||
{
|
||||
float pos[6]; // Player position and rotation
|
||||
NAME64 cell; // Cell name
|
||||
float unk2; // Unknown value - possibly game time?
|
||||
NAME32 player; // Player name
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
/* This struct defines a file 'context' which can be saved and later
|
||||
restored by an ESMReader instance. It will save the position within
|
||||
a file, and when restored will let you read from that position as
|
||||
|
|
|
@ -123,7 +123,7 @@ std::string ESMReader::getHString()
|
|||
// Skip the following zero byte
|
||||
mCtx.leftRec--;
|
||||
char c;
|
||||
mEsm->read(&c, 1);
|
||||
getExact(&c, 1);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,11 @@ void ESMReader::getHExact(void*p, int size)
|
|||
{
|
||||
getSubHeader();
|
||||
if (size != static_cast<int> (mCtx.leftSub))
|
||||
fail("getHExact() size mismatch");
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "getHExact(): size mismatch (requested " << size << ", got " << mCtx.leftSub << ")";
|
||||
fail(error.str());
|
||||
}
|
||||
getExact(p, size);
|
||||
}
|
||||
|
||||
|
@ -182,7 +186,7 @@ void ESMReader::getSubName()
|
|||
}
|
||||
|
||||
// reading the subrecord data anyway.
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
getExact(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
}
|
||||
|
||||
|
@ -190,7 +194,7 @@ bool ESMReader::isEmptyOrGetName()
|
|||
{
|
||||
if (mCtx.leftRec)
|
||||
{
|
||||
mEsm->read(mCtx.subName.name, 4);
|
||||
getExact(mCtx.subName.name, 4);
|
||||
mCtx.leftRec -= 4;
|
||||
return false;
|
||||
}
|
||||
|
@ -210,6 +214,17 @@ void ESMReader::skipHSubSize(int size)
|
|||
fail("skipHSubSize() mismatch");
|
||||
}
|
||||
|
||||
void ESMReader::skipHSubUntil(const char *name)
|
||||
{
|
||||
while (hasMoreSubs() && !isNextSub(name))
|
||||
{
|
||||
mCtx.subCached = false;
|
||||
skipHSub();
|
||||
}
|
||||
if (hasMoreSubs())
|
||||
mCtx.subCached = true;
|
||||
}
|
||||
|
||||
void ESMReader::getSubHeader()
|
||||
{
|
||||
if (mCtx.leftRec < 4)
|
||||
|
@ -279,9 +294,16 @@ void ESMReader::getRecHeader(uint32_t &flags)
|
|||
|
||||
void ESMReader::getExact(void*x, int size)
|
||||
{
|
||||
int t = mEsm->read(x, size);
|
||||
if (t != size)
|
||||
fail("Read error");
|
||||
try
|
||||
{
|
||||
int t = mEsm->read(x, size);
|
||||
if (t != size)
|
||||
fail("Read error");
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
fail(std::string("Read error: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::string ESMReader::getString(int size)
|
||||
|
@ -299,8 +321,7 @@ std::string ESMReader::getString(int size)
|
|||
char *ptr = &mBuffer[0];
|
||||
getExact(ptr, size);
|
||||
|
||||
if (size>0 && ptr[size-1]==0)
|
||||
--size;
|
||||
size = strnlen(ptr, size);
|
||||
|
||||
// Convert to UTF8 and return
|
||||
if (mEncoder)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define OPENMW_ESM_READER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libs/platform/string.h>
|
||||
#include <string.h>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
@ -32,10 +32,11 @@ public:
|
|||
|
||||
int getVer() const { return mHeader.mData.version; }
|
||||
int getRecordCount() const { return mHeader.mData.records; }
|
||||
float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; }
|
||||
float getFVer() const { return (mHeader.mData.version == VER_12) ? 1.2f : 1.3f; }
|
||||
const std::string getAuthor() const { return mHeader.mData.author.toString(); }
|
||||
const std::string getDesc() const { return mHeader.mData.desc.toString(); }
|
||||
const std::vector<Header::MasterData> &getGameFiles() const { return mHeader.mMaster; }
|
||||
const Header& getHeader() const { return mHeader; }
|
||||
int getFormat() const;
|
||||
const NAME &retSubName() const { return mCtx.subName; }
|
||||
uint32_t getSubSize() const { return mCtx.leftSub; }
|
||||
|
@ -136,7 +137,11 @@ public:
|
|||
{
|
||||
getSubHeader();
|
||||
if (mCtx.leftSub != sizeof(X))
|
||||
fail("getHT(): subrecord size mismatch");
|
||||
{
|
||||
std::stringstream error;
|
||||
error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")";
|
||||
fail(error.str());
|
||||
}
|
||||
getT(x);
|
||||
}
|
||||
|
||||
|
@ -194,6 +199,9 @@ public:
|
|||
// Skip sub record and check its size
|
||||
void skipHSubSize(int size);
|
||||
|
||||
// Skip all subrecords until the given subrecord or no more subrecords remaining
|
||||
void skipHSubUntil(const char* name);
|
||||
|
||||
/* Sub-record header. This updates leftRec beyond the current
|
||||
sub-record as well. leftSub contains size of current sub-record.
|
||||
*/
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
ESMWriter::ESMWriter()
|
||||
|
|
|
@ -4,11 +4,14 @@
|
|||
#include <iosfwd>
|
||||
#include <list>
|
||||
|
||||
#include <components/to_utf8/to_utf8.hpp>
|
||||
|
||||
#include "esmcommon.hpp"
|
||||
#include "loadtes3.hpp"
|
||||
|
||||
namespace ToUTF8
|
||||
{
|
||||
class Utf8Encoder;
|
||||
}
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMWriter
|
||||
|
|
|
@ -26,4 +26,4 @@ void ESM::GlobalScript::save (ESMWriter &esm) const
|
|||
esm.writeHNT ("RUN_", mRunning);
|
||||
|
||||
esm.writeHNOString ("TARG", mTargetId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace ESM
|
|||
|
||||
struct GlobalScript
|
||||
{
|
||||
std::string mId;
|
||||
std::string mId; /// \note must be lowercase
|
||||
Locals mLocals;
|
||||
int mRunning;
|
||||
std::string mTargetId; // for targeted scripts
|
||||
|
|
|
@ -4,52 +4,33 @@
|
|||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
void read (ESM::ESMReader &esm, ESM::ObjectState& state, int& slot)
|
||||
{
|
||||
slot = -1;
|
||||
esm.getHNOT (slot, "SLOT");
|
||||
|
||||
state.load (esm);
|
||||
}
|
||||
|
||||
void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot)
|
||||
{
|
||||
esm.writeHNT ("IOBJ", type);
|
||||
|
||||
if (slot!=-1)
|
||||
esm.writeHNT ("SLOT", slot);
|
||||
|
||||
state.save (esm, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::InventoryState::load (ESMReader &esm)
|
||||
{
|
||||
int index = 0;
|
||||
while (esm.isNextSub ("IOBJ"))
|
||||
{
|
||||
unsigned int id = 0;
|
||||
esm.getHT (id);
|
||||
int unused; // no longer used
|
||||
esm.getHT(unused);
|
||||
|
||||
if (id==ESM::REC_LIGH)
|
||||
ObjectState state;
|
||||
|
||||
// obsolete
|
||||
if (esm.isNextSub("SLOT"))
|
||||
{
|
||||
LightState state;
|
||||
int slot;
|
||||
read (esm, state, slot);
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
mLights.push_back (std::make_pair (state, slot));
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectState state;
|
||||
int slot;
|
||||
read (esm, state, slot);
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
mItems.push_back (std::make_pair (state, std::make_pair (id, slot)));
|
||||
esm.getHT(slot);
|
||||
mEquipmentSlots[index] = slot;
|
||||
}
|
||||
|
||||
state.mRef.loadId(esm, true);
|
||||
state.load (esm);
|
||||
|
||||
if (state.mCount == 0)
|
||||
continue;
|
||||
|
||||
mItems.push_back (state);
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
while (esm.isNextSub("LEVM"))
|
||||
|
@ -74,16 +55,30 @@ void ESM::InventoryState::load (ESMReader &esm)
|
|||
}
|
||||
mPermanentMagicEffectMagnitudes[id] = params;
|
||||
}
|
||||
|
||||
while (esm.isNextSub("EQUI"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
int index;
|
||||
esm.getT(index);
|
||||
int slot;
|
||||
esm.getT(slot);
|
||||
mEquipmentSlots[index] = slot;
|
||||
}
|
||||
|
||||
mSelectedEnchantItem = -1;
|
||||
esm.getHNOT(mSelectedEnchantItem, "SELE");
|
||||
}
|
||||
|
||||
void ESM::InventoryState::save (ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<std::pair<ObjectState, std::pair<unsigned int, int> > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter)
|
||||
write (esm, iter->first, iter->second.first, iter->second.second);
|
||||
for (std::vector<ObjectState>::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter)
|
||||
{
|
||||
int unused = 0;
|
||||
esm.writeHNT ("IOBJ", unused);
|
||||
|
||||
for (std::vector<std::pair<LightState, int> >::const_iterator iter (mLights.begin());
|
||||
iter!=mLights.end(); ++iter)
|
||||
write (esm, iter->first, ESM::REC_LIGH, iter->second);
|
||||
iter->save (esm, true);
|
||||
}
|
||||
|
||||
for (std::map<std::string, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it)
|
||||
{
|
||||
|
@ -102,4 +97,15 @@ void ESM::InventoryState::save (ESMWriter &esm) const
|
|||
esm.writeHNT ("MULT", pIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<int, int>::const_iterator it = mEquipmentSlots.begin(); it != mEquipmentSlots.end(); ++it)
|
||||
{
|
||||
esm.startSubRecord("EQUI");
|
||||
esm.writeT(it->first);
|
||||
esm.writeT(it->second);
|
||||
esm.endRecord("EQUI");
|
||||
}
|
||||
|
||||
if (mSelectedEnchantItem != -1)
|
||||
esm.writeHNT ("SELE", mSelectedEnchantItem);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <map>
|
||||
|
||||
#include "objectstate.hpp"
|
||||
#include "lightstate.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -16,17 +15,19 @@ namespace ESM
|
|||
/// \brief State for inventories and containers
|
||||
struct InventoryState
|
||||
{
|
||||
// anything but lights (type, slot)
|
||||
std::vector<std::pair<ObjectState, std::pair<unsigned int, int> > > mItems;
|
||||
std::vector<ObjectState> mItems;
|
||||
|
||||
// lights (slot)
|
||||
std::vector<std::pair<LightState, int> > mLights;
|
||||
// <Index in mItems, equipment slot>
|
||||
std::map<int, int> mEquipmentSlots;
|
||||
|
||||
std::map<std::string, int> mLevelledItemMap;
|
||||
|
||||
typedef std::map<std::string, std::vector<std::pair<float, float> > > TEffectMagnitudes;
|
||||
TEffectMagnitudes mPermanentMagicEffectMagnitudes;
|
||||
|
||||
int mSelectedEnchantItem; // For inventories only
|
||||
|
||||
InventoryState() : mSelectedEnchantItem(-1) {}
|
||||
virtual ~InventoryState() {}
|
||||
|
||||
virtual void load (ESMReader &esm);
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
|
||||
#include "lightstate.hpp"
|
||||
|
||||
#include "esmreader.hpp"
|
||||
#include "esmwriter.hpp"
|
||||
|
||||
void ESM::LightState::load (ESMReader &esm)
|
||||
{
|
||||
ObjectState::load (esm);
|
||||
|
||||
mTime = 0;
|
||||
esm.getHNOT (mTime, "LTIM");
|
||||
}
|
||||
|
||||
void ESM::LightState::save (ESMWriter &esm, bool inInventory) const
|
||||
{
|
||||
ObjectState::save (esm, inInventory);
|
||||
|
||||
if (mTime)
|
||||
esm.writeHNT ("LTIM", mTime);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef OPENMW_ESM_LIGHTSTATE_H
|
||||
#define OPENMW_ESM_LIGHTSTATE_H
|
||||
|
||||
#include "objectstate.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
// format 0, saved games only
|
||||
|
||||
struct LightState : public ObjectState
|
||||
{
|
||||
float mTime;
|
||||
|
||||
virtual void load (ESMReader &esm);
|
||||
virtual void save (ESMWriter &esm, bool inInventory = false) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -8,18 +8,34 @@ namespace ESM
|
|||
{
|
||||
unsigned int Activator::sRecordId = REC_ACTI;
|
||||
|
||||
void Activator::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
}
|
||||
void Activator::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
}
|
||||
void Activator::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Activator::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
}
|
||||
|
||||
void Activator::blank()
|
||||
{
|
||||
|
|
|
@ -8,24 +8,51 @@ namespace ESM
|
|||
{
|
||||
unsigned int Potion::sRecordId = REC_ALCH;
|
||||
|
||||
void Potion::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNOString("MODL");
|
||||
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "ALDT", 12);
|
||||
mEffects.load(esm);
|
||||
}
|
||||
void Potion::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("TEXT", mIcon);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("ALDT", mData, 12);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
void Potion::load(ESMReader &esm)
|
||||
{
|
||||
mEffects.mList.clear();
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'M','O','D','L'>::value:
|
||||
mModel = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'T','E','X','T'>::value: // not ITEX here for some reason
|
||||
mIcon = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'S','C','R','I'>::value:
|
||||
mScript = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'F','N','A','M'>::value:
|
||||
mName = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'A','L','D','T'>::value:
|
||||
esm.getHT(mData, 12);
|
||||
hasData = true;
|
||||
break;
|
||||
case ESM::FourCC<'E','N','A','M'>::value:
|
||||
mEffects.add(esm);
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing ALDT");
|
||||
}
|
||||
void Potion::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("TEXT", mIcon);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("ALDT", mData, 12);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
|
||||
void Potion::blank()
|
||||
{
|
||||
|
|
|
@ -10,25 +10,35 @@ namespace ESM
|
|||
|
||||
void Apparatus::load(ESMReader &esm)
|
||||
{
|
||||
// we will not treat duplicated subrecords as errors here
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
NAME subName = esm.retSubName();
|
||||
|
||||
if (subName == "MODL")
|
||||
mModel = esm.getHString();
|
||||
else if (subName == "FNAM")
|
||||
mName = esm.getHString();
|
||||
else if (subName == "AADT")
|
||||
esm.getHT(mData);
|
||||
else if (subName == "SCRI")
|
||||
mScript = esm.getHString();
|
||||
else if (subName == "ITEX")
|
||||
mIcon = esm.getHString();
|
||||
else
|
||||
esm.fail("wrong subrecord type " + subName.toString() + " for APPA record");
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'A','A','D','T'>::value:
|
||||
esm.getHT(mData);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing AADT");
|
||||
}
|
||||
|
||||
void Apparatus::save(ESMWriter &esm) const
|
||||
|
|
|
@ -7,52 +7,87 @@
|
|||
namespace ESM
|
||||
{
|
||||
|
||||
void PartReferenceList::load(ESMReader &esm)
|
||||
{
|
||||
mParts.clear();
|
||||
while (esm.isNextSub("INDX"))
|
||||
void PartReferenceList::add(ESMReader &esm)
|
||||
{
|
||||
PartReference pr;
|
||||
esm.getHT(pr.mPart); // The INDX byte
|
||||
pr.mMale = esm.getHNOString("BNAM");
|
||||
pr.mFemale = esm.getHNOString("CNAM");
|
||||
mParts.push_back(pr);
|
||||
}
|
||||
}
|
||||
|
||||
void PartReferenceList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)
|
||||
}
|
||||
|
||||
void PartReferenceList::load(ESMReader &esm)
|
||||
{
|
||||
esm.writeHNT("INDX", it->mPart);
|
||||
esm.writeHNOString("BNAM", it->mMale);
|
||||
esm.writeHNOString("CNAM", it->mFemale);
|
||||
mParts.clear();
|
||||
while (esm.isNextSub("INDX"))
|
||||
{
|
||||
add(esm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Armor::sRecordId = REC_ARMO;
|
||||
void PartReferenceList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("INDX", it->mPart);
|
||||
esm.writeHNOString("BNAM", it->mMale);
|
||||
esm.writeHNOString("CNAM", it->mFemale);
|
||||
}
|
||||
}
|
||||
|
||||
void Armor::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
esm.getHNT(mData, "AODT", 24);
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mParts.load(esm);
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
unsigned int Armor::sRecordId = REC_ARMO;
|
||||
|
||||
void Armor::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("AODT", mData, 24);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
mParts.save(esm);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
void Armor::load(ESMReader &esm)
|
||||
{
|
||||
mParts.mParts.clear();
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'A','O','D','T'>::value:
|
||||
esm.getHT(mData, 24);
|
||||
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<'I','N','D','X'>::value:
|
||||
mParts.add(esm);
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing CTDT subrecord");
|
||||
}
|
||||
|
||||
void Armor::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("AODT", mData, 24);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
mParts.save(esm);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Armor::blank()
|
||||
{
|
||||
|
|
|
@ -55,6 +55,10 @@ struct PartReferenceList
|
|||
{
|
||||
std::vector<PartReference> mParts;
|
||||
|
||||
/// Load one part, assumes the subrecord name was already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
/// TODO: remove this method. The ESM format does not guarantee that all Part subrecords follow one another.
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
|
|
@ -11,9 +11,30 @@ namespace ESM
|
|||
|
||||
void BodyPart::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mRace = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "BYDT", 4);
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'M','O','D','L'>::value:
|
||||
mModel = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'F','N','A','M'>::value:
|
||||
mRace = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'B','Y','D','T'>::value:
|
||||
esm.getHT(mData, 4);
|
||||
hasData = true;
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasData)
|
||||
esm.fail("Missing BYDT subrecord");
|
||||
}
|
||||
void BodyPart::save(ESMWriter &esm) const
|
||||
{
|
||||
|
|
|
@ -8,26 +8,54 @@ namespace ESM
|
|||
{
|
||||
unsigned int Book::sRecordId = REC_BOOK;
|
||||
|
||||
void Book::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "BKDT", 20);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mText = esm.getHNOString("TEXT");
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void Book::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("BKDT", mData, 20);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNOString("TEXT", mText);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
void Book::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing BKDT subrecord");
|
||||
}
|
||||
void Book::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("BKDT", mData, 20);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNOString("TEXT", mText);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Book::blank()
|
||||
{
|
||||
|
|
|
@ -10,11 +10,29 @@ namespace ESM
|
|||
|
||||
void BirthSign::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mTexture = esm.getHNOString("TNAM");
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
|
||||
mPowers.load(esm);
|
||||
mPowers.mList.clear();
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'F','N','A','M'>::value:
|
||||
mName = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'T','N','A','M'>::value:
|
||||
mTexture = esm.getHString();
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BirthSign::save(ESMWriter &esm) const
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace ESM
|
|||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
class CellId;
|
||||
struct CellId;
|
||||
|
||||
/* Moved cell reference tracking object. This mainly stores the target cell
|
||||
of the reference, so we can easily know where it has been moved when another
|
||||
|
@ -137,7 +137,7 @@ struct Cell
|
|||
|
||||
bool hasWater() const
|
||||
{
|
||||
return (mData.mFlags&HasWater);
|
||||
return (mData.mFlags&HasWater) != 0;
|
||||
}
|
||||
|
||||
// Restore the given reader to the stored position. Will try to open
|
||||
|
|
|
@ -10,17 +10,17 @@ namespace ESM
|
|||
{
|
||||
unsigned int Class::sRecordId = REC_CLAS;
|
||||
|
||||
const Class::Specialization Class::sSpecializationIds[3] = {
|
||||
Class::Combat,
|
||||
Class::Magic,
|
||||
Class::Stealth
|
||||
};
|
||||
const Class::Specialization Class::sSpecializationIds[3] = {
|
||||
Class::Combat,
|
||||
Class::Magic,
|
||||
Class::Stealth
|
||||
};
|
||||
|
||||
const char *Class::sGmstSpecializationIds[3] = {
|
||||
"sSpecializationCombat",
|
||||
"sSpecializationMagic",
|
||||
"sSpecializationStealth"
|
||||
};
|
||||
const char *Class::sGmstSpecializationIds[3] = {
|
||||
"sSpecializationCombat",
|
||||
"sSpecializationMagic",
|
||||
"sSpecializationStealth"
|
||||
};
|
||||
|
||||
|
||||
int& Class::CLDTstruct::getSkill (int index, bool major)
|
||||
|
@ -39,22 +39,40 @@ const char *Class::sGmstSpecializationIds[3] = {
|
|||
return mSkills[index][major ? 1 : 0];
|
||||
}
|
||||
|
||||
void Class::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "CLDT", 60);
|
||||
|
||||
if (mData.mIsPlayable > 1)
|
||||
esm.fail("Unknown bool value");
|
||||
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
}
|
||||
void Class::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CLDT", mData, 60);
|
||||
esm.writeHNOString("DESC", mDescription);
|
||||
}
|
||||
void Class::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing CLDT subrecord");
|
||||
}
|
||||
void Class::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CLDT", mData, 60);
|
||||
esm.writeHNOString("DESC", mDescription);
|
||||
}
|
||||
|
||||
void Class::blank()
|
||||
{
|
||||
|
|
|
@ -10,17 +10,42 @@ namespace ESM
|
|||
|
||||
void Clothing::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "CTDT", 12);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
|
||||
mParts.load(esm);
|
||||
|
||||
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
mParts.mParts.clear();
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'C','T','D','T'>::value:
|
||||
esm.getHT(mData, 12);
|
||||
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<'I','N','D','X'>::value:
|
||||
mParts.add(esm);
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing CTDT subrecord");
|
||||
}
|
||||
|
||||
void Clothing::save(ESMWriter &esm) const
|
||||
|
|
|
@ -7,55 +7,79 @@
|
|||
namespace ESM
|
||||
{
|
||||
|
||||
void InventoryList::load(ESMReader &esm)
|
||||
{
|
||||
mList.clear();
|
||||
ContItem ci;
|
||||
while (esm.isNextSub("NPCO"))
|
||||
void InventoryList::add(ESMReader &esm)
|
||||
{
|
||||
ContItem ci;
|
||||
esm.getHT(ci, 36);
|
||||
mList.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryList::save(ESMWriter &esm) const
|
||||
{
|
||||
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
|
||||
void InventoryList::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("NPCO", *it, 36);
|
||||
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("NPCO", *it, 36);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Container::sRecordId = REC_CONT;
|
||||
|
||||
void Container::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mWeight, "CNDT", 4);
|
||||
esm.getHNT(mFlags, "FLAG", 4);
|
||||
void Container::load(ESMReader &esm)
|
||||
{
|
||||
mInventory.mList.clear();
|
||||
bool hasWeight = false;
|
||||
bool hasFlags = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'C','N','D','T'>::value:
|
||||
esm.getHT(mWeight, 4);
|
||||
hasWeight = true;
|
||||
break;
|
||||
case ESM::FourCC<'F','L','A','G'>::value:
|
||||
esm.getHT(mFlags, 4);
|
||||
if (mFlags & 0xf4)
|
||||
esm.fail("Unknown flags");
|
||||
if (!(mFlags & 0x8))
|
||||
esm.fail("Flag 8 not set");
|
||||
hasFlags = true;
|
||||
break;
|
||||
case ESM::FourCC<'S','C','R','I'>::value:
|
||||
mScript = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'N','P','C','O'>::value:
|
||||
mInventory.add(esm);
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasWeight)
|
||||
esm.fail("Missing CNDT subrecord");
|
||||
if (!hasFlags)
|
||||
esm.fail("Missing FLAG subrecord");
|
||||
}
|
||||
|
||||
if (mFlags & 0xf4)
|
||||
esm.fail("Unknown flags");
|
||||
if (!(mFlags & 0x8))
|
||||
esm.fail("Flag 8 not set");
|
||||
void Container::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CNDT", mWeight, 4);
|
||||
esm.writeHNT("FLAG", mFlags, 4);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
|
||||
mInventory.load(esm);
|
||||
}
|
||||
|
||||
void Container::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("CNDT", mWeight, 4);
|
||||
esm.writeHNT("FLAG", mFlags, 4);
|
||||
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
|
||||
mInventory.save(esm);
|
||||
}
|
||||
mInventory.save(esm);
|
||||
}
|
||||
|
||||
void Container::blank()
|
||||
{
|
||||
|
@ -63,7 +87,7 @@ void Container::save(ESMWriter &esm) const
|
|||
mModel.clear();
|
||||
mScript.clear();
|
||||
mWeight = 0;
|
||||
mFlags = 0;
|
||||
mFlags = 0x8; // set default flag value
|
||||
mInventory.mList.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,14 @@ struct ContItem
|
|||
NAME32 mItem;
|
||||
};
|
||||
|
||||
/// InventoryList, NPCO subrecord
|
||||
struct InventoryList
|
||||
{
|
||||
std::vector<ContItem> mList;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
/// Load one item, assumes subrecord name is already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -8,55 +8,100 @@ namespace ESM {
|
|||
|
||||
unsigned int Creature::sRecordId = REC_CREA;
|
||||
|
||||
void Creature::load(ESMReader &esm)
|
||||
{
|
||||
mPersistent = esm.getRecordFlags() & 0x0400;
|
||||
|
||||
mModel = esm.getHNString("MODL");
|
||||
mOriginal = esm.getHNOString("CNAM");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getHNT(mData, "NPDT", 96);
|
||||
|
||||
esm.getHNT(mFlags, "FLAG");
|
||||
mScale = 1.0;
|
||||
esm.getHNOT(mScale, "XSCL");
|
||||
|
||||
mInventory.load(esm);
|
||||
mSpells.load(esm);
|
||||
|
||||
if (esm.isNextSub("AIDT"))
|
||||
void Creature::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHExact(&mAiData, sizeof(mAiData));
|
||||
mHasAI = true;
|
||||
}
|
||||
else
|
||||
mPersistent = (esm.getRecordFlags() & 0x0400) != 0;
|
||||
|
||||
mAiPackage.mList.clear();
|
||||
mInventory.mList.clear();
|
||||
mSpells.mList.clear();
|
||||
mTransport.mList.clear();
|
||||
|
||||
mScale = 1.f;
|
||||
mHasAI = false;
|
||||
|
||||
mAiPackage.load(esm);
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void Creature::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("CNAM", mOriginal);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("NPDT", mData, 96);
|
||||
esm.writeHNT("FLAG", mFlags);
|
||||
if (mScale != 1.0) {
|
||||
esm.writeHNT("XSCL", mScale);
|
||||
bool hasNpdt = false;
|
||||
bool hasFlags = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'M','O','D','L'>::value:
|
||||
mModel = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'C','N','A','M'>::value:
|
||||
mOriginal = 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<'N','P','D','T'>::value:
|
||||
esm.getHT(mData, 96);
|
||||
hasNpdt = true;
|
||||
break;
|
||||
case ESM::FourCC<'F','L','A','G'>::value:
|
||||
esm.getHT(mFlags);
|
||||
hasFlags = true;
|
||||
break;
|
||||
case ESM::FourCC<'X','S','C','L'>::value:
|
||||
esm.getHT(mScale);
|
||||
break;
|
||||
case ESM::FourCC<'N','P','C','O'>::value:
|
||||
mInventory.add(esm);
|
||||
break;
|
||||
case ESM::FourCC<'N','P','C','S'>::value:
|
||||
mSpells.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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasNpdt)
|
||||
esm.fail("Missing NPDT subrecord");
|
||||
if (!hasFlags)
|
||||
esm.fail("Missing FLAG subrecord");
|
||||
}
|
||||
|
||||
mInventory.save(esm);
|
||||
mSpells.save(esm);
|
||||
if (mHasAI) {
|
||||
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
|
||||
void Creature::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("CNAM", mOriginal);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNT("NPDT", mData, 96);
|
||||
esm.writeHNT("FLAG", mFlags);
|
||||
if (mScale != 1.0) {
|
||||
esm.writeHNT("XSCL", mScale);
|
||||
}
|
||||
|
||||
mInventory.save(esm);
|
||||
mSpells.save(esm);
|
||||
if (mHasAI) {
|
||||
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
|
||||
}
|
||||
mTransport.save(esm);
|
||||
mAiPackage.save(esm);
|
||||
}
|
||||
mAiPackage.save(esm);
|
||||
}
|
||||
|
||||
void Creature::blank()
|
||||
{
|
||||
|
@ -81,5 +126,11 @@ void Creature::save(ESMWriter &esm) const
|
|||
mAiData.blank();
|
||||
mAiData.mServices = 0;
|
||||
mAiPackage.mList.clear();
|
||||
mTransport.mList.clear();
|
||||
}
|
||||
|
||||
const std::vector<Transport::Dest>& Creature::getTransport() const
|
||||
{
|
||||
return mTransport.mList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "loadcont.hpp"
|
||||
#include "spelllist.hpp"
|
||||
#include "aipackage.hpp"
|
||||
#include "transport.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -92,6 +93,9 @@ struct Creature
|
|||
bool mHasAI;
|
||||
AIData mAiData;
|
||||
AIPackageList mAiPackage;
|
||||
Transport mTransport;
|
||||
|
||||
const std::vector<Transport::Dest>& getTransport() const;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef OPENMW_ESM_CREC_H
|
||||
#define OPENMW_ESM_CREC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
// TODO create implementation files and remove this one
|
||||
#include "esmreader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/* These two are only used in save games. They are not decoded yet.
|
||||
*/
|
||||
|
||||
/// Changes a creature
|
||||
struct LoadCREC
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void save(ESMWriter &esm) const
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// Changes an item list / container
|
||||
struct LoadCNTC
|
||||
{
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.skipRecord();
|
||||
}
|
||||
|
||||
void save(ESMWriter &esm) const
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -8,23 +8,43 @@ namespace ESM
|
|||
{
|
||||
unsigned int Door::sRecordId = REC_DOOR;
|
||||
|
||||
void Door::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mOpenSound = esm.getHNOString("SNAM");
|
||||
mCloseSound = esm.getHNOString("ANAM");
|
||||
}
|
||||
void Door::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Door::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("SNAM", mOpenSound);
|
||||
esm.writeHNOCString("ANAM", mCloseSound);
|
||||
}
|
||||
void Door::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("SNAM", mOpenSound);
|
||||
esm.writeHNOCString("ANAM", mCloseSound);
|
||||
}
|
||||
|
||||
void Door::blank()
|
||||
{
|
||||
|
|
|
@ -10,8 +10,28 @@ namespace ESM
|
|||
|
||||
void Enchantment::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mData, "ENDT", 16);
|
||||
mEffects.load(esm);
|
||||
mEffects.mList.clear();
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing ENDT subrecord");
|
||||
}
|
||||
|
||||
void Enchantment::save(ESMWriter &esm) const
|
||||
|
|
|
@ -28,27 +28,46 @@ namespace ESM
|
|||
|
||||
void Faction::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mReactions.clear();
|
||||
for (int i=0;i<10;++i)
|
||||
mRanks[i].clear();
|
||||
|
||||
// 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
|
||||
int rankCounter=0;
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
std::string faction = esm.getHNString("ANAM");
|
||||
int reaction;
|
||||
esm.getHNT(reaction, "INTV");
|
||||
mReactions[faction] = reaction;
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
}
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing FADT subrecord");
|
||||
}
|
||||
void Faction::save(ESMWriter &esm) const
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ class ESMReader;
|
|||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Game setting, with automatic cleaning of "dirty" entries.
|
||||
* Game setting
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -32,7 +32,11 @@ struct DialInfo
|
|||
struct DATAstruct
|
||||
{
|
||||
int mUnknown1;
|
||||
int mDisposition;
|
||||
union
|
||||
{
|
||||
int mDisposition; // Used for dialogue responses
|
||||
int mJournalIndex; // Used for journal entries
|
||||
};
|
||||
signed char mRank; // Rank of NPC
|
||||
signed char mGender; // See Gender enum
|
||||
signed char mPCrank; // Player rank
|
||||
|
|
|
@ -8,45 +8,71 @@ namespace ESM
|
|||
{
|
||||
unsigned int Ingredient::sRecordId = REC_INGR;
|
||||
|
||||
void Ingredient::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "IRDT", 56);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
// horrible hack to fix broken data in records
|
||||
for (int i=0; i<4; ++i)
|
||||
void Ingredient::load(ESMReader &esm)
|
||||
{
|
||||
if (mData.mEffectID[i] != 85 &&
|
||||
mData.mEffectID[i] != 22 &&
|
||||
mData.mEffectID[i] != 17 &&
|
||||
mData.mEffectID[i] != 79 &&
|
||||
mData.mEffectID[i] != 74)
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
mData.mAttributes[i] = -1;
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'I','R','D','T'>::value:
|
||||
esm.getHT(mData, 56);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
|
||||
// is this relevant in cycle from 0 to 4?
|
||||
if (mData.mEffectID[i] != 89 &&
|
||||
mData.mEffectID[i] != 26 &&
|
||||
mData.mEffectID[i] != 21 &&
|
||||
mData.mEffectID[i] != 83 &&
|
||||
mData.mEffectID[i] != 78)
|
||||
if (!hasData)
|
||||
esm.fail("Missing IRDT subrecord");
|
||||
|
||||
// horrible hack to fix broken data in records
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
mData.mSkills[i] = -1;
|
||||
if (mData.mEffectID[i] != 85 &&
|
||||
mData.mEffectID[i] != 22 &&
|
||||
mData.mEffectID[i] != 17 &&
|
||||
mData.mEffectID[i] != 79 &&
|
||||
mData.mEffectID[i] != 74)
|
||||
{
|
||||
mData.mAttributes[i] = -1;
|
||||
}
|
||||
|
||||
// is this relevant in cycle from 0 to 4?
|
||||
if (mData.mEffectID[i] != 89 &&
|
||||
mData.mEffectID[i] != 26 &&
|
||||
mData.mEffectID[i] != 21 &&
|
||||
mData.mEffectID[i] != 83 &&
|
||||
mData.mEffectID[i] != 78)
|
||||
{
|
||||
mData.mSkills[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ingredient::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("IRDT", mData, 56);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
void Ingredient::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("IRDT", mData, 56);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
|
||||
void Ingredient::blank()
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace ESM
|
|||
void Land::LandData::save(ESMWriter &esm)
|
||||
{
|
||||
if (mDataTypes & Land::DATA_VNML) {
|
||||
esm.writeHNT("VNML", mNormals, sizeof(VNML));
|
||||
esm.writeHNT("VNML", mNormals, sizeof(mNormals));
|
||||
}
|
||||
if (mDataTypes & Land::DATA_VHGT) {
|
||||
VHGT offsets;
|
||||
|
@ -72,7 +72,6 @@ Land::Land()
|
|||
, mDataLoaded(false)
|
||||
, mLandData(NULL)
|
||||
, mPlugin(0)
|
||||
, mHasData(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -97,8 +96,6 @@ void Land::load(ESMReader &esm)
|
|||
// Store the file position
|
||||
mContext = esm.getContext();
|
||||
|
||||
mHasData = false;
|
||||
|
||||
// Skip these here. Load the actual data when the cell is loaded.
|
||||
if (esm.isNextSub("VNML"))
|
||||
{
|
||||
|
@ -126,10 +123,6 @@ void Land::load(ESMReader &esm)
|
|||
mDataTypes |= DATA_VTEX;
|
||||
}
|
||||
|
||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||
// landscape. (Though Morrowind seems to accept terrain without VTEX/VCLR entries)
|
||||
mHasData = mDataTypes & (DATA_VNML|DATA_VHGT|DATA_WNAM);
|
||||
|
||||
mDataLoaded = 0;
|
||||
mLandData = NULL;
|
||||
}
|
||||
|
@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const
|
|||
esm.writeHNT("DATA", mFlags);
|
||||
}
|
||||
|
||||
/// \todo remove memory allocation when only defaults needed
|
||||
void Land::loadData(int flags)
|
||||
{
|
||||
// Try to load only available data
|
||||
int actual = flags & mDataTypes;
|
||||
flags = flags & mDataTypes;
|
||||
// Return if all required data is loaded
|
||||
if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) {
|
||||
if ((mDataLoaded & flags) == flags) {
|
||||
return;
|
||||
}
|
||||
// Create storage if nothing is loaded
|
||||
|
@ -160,15 +152,13 @@ void Land::loadData(int flags)
|
|||
}
|
||||
mEsm->restoreContext(mContext);
|
||||
|
||||
memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals));
|
||||
|
||||
if (mEsm->isNextSub("VNML")) {
|
||||
condLoad(actual, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
|
||||
condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
|
||||
}
|
||||
|
||||
if (mEsm->isNextSub("VHGT")) {
|
||||
static VHGT vhgt;
|
||||
if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(vhgt))) {
|
||||
if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
|
||||
float rowOffset = vhgt.mHeightOffset;
|
||||
for (int y = 0; y < LAND_SIZE; y++) {
|
||||
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
|
||||
|
@ -184,30 +174,18 @@ void Land::loadData(int flags)
|
|||
mLandData->mUnk1 = vhgt.mUnk1;
|
||||
mLandData->mUnk2 = vhgt.mUnk2;
|
||||
}
|
||||
} else if ((flags & DATA_VHGT) && (mDataLoaded & DATA_VHGT) == 0) {
|
||||
for (int i = 0; i < LAND_NUM_VERTS; ++i) {
|
||||
mLandData->mHeights[i] = -256.0f * HEIGHT_SCALE;
|
||||
}
|
||||
mDataLoaded |= DATA_VHGT;
|
||||
}
|
||||
|
||||
if (mEsm->isNextSub("WNAM")) {
|
||||
condLoad(actual, DATA_WNAM, mLandData->mWnam, 81);
|
||||
}
|
||||
if (mEsm->isNextSub("VCLR")) {
|
||||
mLandData->mUsingColours = true;
|
||||
condLoad(actual, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
|
||||
} else {
|
||||
mLandData->mUsingColours = false;
|
||||
condLoad(flags, DATA_WNAM, mLandData->mWnam, 81);
|
||||
}
|
||||
if (mEsm->isNextSub("VCLR"))
|
||||
condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
|
||||
if (mEsm->isNextSub("VTEX")) {
|
||||
static uint16_t vtex[LAND_NUM_TEXTURES];
|
||||
if (condLoad(actual, DATA_VTEX, vtex, sizeof(vtex))) {
|
||||
if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) {
|
||||
LandData::transposeTextureData(vtex, mLandData->mTextures);
|
||||
}
|
||||
} else if ((flags & DATA_VTEX) && (mDataLoaded & DATA_VTEX) == 0) {
|
||||
memset(mLandData->mTextures, 0, sizeof(mLandData->mTextures));
|
||||
mDataLoaded |= DATA_VTEX;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,4 +210,9 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Land::isDataLoaded(int flags) const
|
||||
{
|
||||
return (mDataLoaded & flags) == (flags & mDataTypes);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ struct Land
|
|||
ESMReader* mEsm;
|
||||
ESM_Context mContext;
|
||||
|
||||
bool mHasData;
|
||||
int mDataTypes;
|
||||
int mDataLoaded;
|
||||
|
||||
|
@ -81,16 +80,12 @@ struct Land
|
|||
VNML mNormals[LAND_NUM_VERTS * 3];
|
||||
uint16_t mTextures[LAND_NUM_TEXTURES];
|
||||
|
||||
bool mUsingColours;
|
||||
char mColours[3 * LAND_NUM_VERTS];
|
||||
int mDataTypes;
|
||||
|
||||
// WNAM appears to contain the global map image for this cell. Probably a palette-based format,
|
||||
// since there's only 1 byte for each pixel.
|
||||
// Currently unused (global map is drawn on the fly in OpenMW, takes ~1/2 second at startup for Morrowind.esm).
|
||||
// The problem with using the original data is that we would need to exactly replicate the TES CS's algorithm
|
||||
// for drawing the global map in OpenCS, in order to get seamless edges when creating landmass mods.
|
||||
uint8_t mWnam[81];
|
||||
// low-LOD heightmap (used for rendering the global map)
|
||||
signed char mWnam[81];
|
||||
|
||||
short mUnk1;
|
||||
uint8_t mUnk2;
|
||||
|
||||
|
@ -116,10 +111,8 @@ struct Land
|
|||
void unloadData();
|
||||
|
||||
/// Check if given data type is loaded
|
||||
/// \todo reimplement this
|
||||
bool isDataLoaded(int flags) {
|
||||
return (mDataLoaded & flags) == flags;
|
||||
}
|
||||
/// @note We only check data types that *can* be loaded (present in mDataTypes)
|
||||
bool isDataLoaded(int flags) const;
|
||||
|
||||
private:
|
||||
Land(const Land& land);
|
||||
|
|
|
@ -7,47 +7,53 @@
|
|||
namespace ESM
|
||||
{
|
||||
|
||||
void LeveledListBase::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mFlags, "DATA");
|
||||
esm.getHNT(mChanceNone, "NNAM");
|
||||
|
||||
if (esm.isNextSub("INDX"))
|
||||
void LevelledListBase::load(ESMReader &esm)
|
||||
{
|
||||
int len;
|
||||
esm.getHT(len);
|
||||
mList.resize(len);
|
||||
esm.getHNT(mFlags, "DATA");
|
||||
esm.getHNT(mChanceNone, "NNAM");
|
||||
|
||||
if (esm.isNextSub("INDX"))
|
||||
{
|
||||
int len;
|
||||
esm.getHT(len);
|
||||
mList.resize(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Original engine ignores rest of the record, even if there are items following
|
||||
mList.clear();
|
||||
esm.skipRecord();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// TODO: Merge with an existing lists here. This can be done
|
||||
// simply by adding the lists together, making sure that they are
|
||||
// sorted by level. A better way might be to exclude repeated
|
||||
// items. Also, some times we don't want to merge lists, just
|
||||
// overwrite. Figure out a way to give the user this option.
|
||||
|
||||
for (size_t i = 0; i < mList.size(); i++)
|
||||
void LevelledListBase::save(ESMWriter &esm) const
|
||||
{
|
||||
LevelItem &li = mList[i];
|
||||
li.mId = esm.getHNString(mRecName);
|
||||
esm.getHNT(li.mLevel, "INTV");
|
||||
}
|
||||
}
|
||||
void LeveledListBase::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mFlags);
|
||||
esm.writeHNT("NNAM", mChanceNone);
|
||||
esm.writeHNT<int>("INDX", mList.size());
|
||||
esm.writeHNT("DATA", mFlags);
|
||||
esm.writeHNT("NNAM", mChanceNone);
|
||||
esm.writeHNT<int>("INDX", mList.size());
|
||||
|
||||
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
|
||||
{
|
||||
esm.writeHNCString(mRecName, it->mId);
|
||||
esm.writeHNT("INTV", it->mLevel);
|
||||
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
|
||||
{
|
||||
esm.writeHNCString(mRecName, it->mId);
|
||||
esm.writeHNT("INTV", it->mLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LeveledListBase::blank()
|
||||
void LevelledListBase::blank()
|
||||
{
|
||||
mFlags = 0;
|
||||
mChanceNone = 0;
|
||||
|
|
|
@ -11,14 +11,14 @@ class ESMReader;
|
|||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* Leveled lists. Since these have identical layout, I only bothered
|
||||
* Levelled lists. Since these have identical layout, I only bothered
|
||||
* to implement it once.
|
||||
*
|
||||
* We should later implement the ability to merge leveled lists from
|
||||
* We should later implement the ability to merge levelled lists from
|
||||
* several files.
|
||||
*/
|
||||
|
||||
struct LeveledListBase
|
||||
struct LevelledListBase
|
||||
{
|
||||
int mFlags;
|
||||
unsigned char mChanceNone; // Chance that none are selected (0-100)
|
||||
|
@ -43,7 +43,7 @@ struct LeveledListBase
|
|||
///< Set record to default state (does not touch the ID).
|
||||
};
|
||||
|
||||
struct CreatureLevList: LeveledListBase
|
||||
struct CreatureLevList: LevelledListBase
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
|
@ -61,7 +61,7 @@ struct CreatureLevList: LeveledListBase
|
|||
}
|
||||
};
|
||||
|
||||
struct ItemLevList: LeveledListBase
|
||||
struct ItemLevList: LevelledListBase
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
|
@ -72,7 +72,7 @@ struct ItemLevList: LeveledListBase
|
|||
// list is instantiated, instead of
|
||||
// giving several identical items
|
||||
// (used when a container has more
|
||||
// than one instance of one leveled
|
||||
// than one instance of one levelled
|
||||
// list.)
|
||||
AllLevels = 0x02 // Calculate from all levels <= player
|
||||
// level, not just the closest below
|
||||
|
|
|
@ -8,25 +8,50 @@ namespace ESM
|
|||
{
|
||||
unsigned int Light::sRecordId = REC_LIGH;
|
||||
|
||||
void Light::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNOString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
assert(sizeof(mData) == 24);
|
||||
esm.getHNT(mData, "LHDT", 24);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mSound = esm.getHNOString("SNAM");
|
||||
}
|
||||
void Light::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNT("LHDT", mData, 24);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("SNAM", mSound);
|
||||
}
|
||||
void Light::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'I','T','E','X'>::value:
|
||||
mIcon = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'L','H','D','T'>::value:
|
||||
esm.getHT(mData, 24);
|
||||
hasData = true;
|
||||
break;
|
||||
case ESM::FourCC<'S','C','R','I'>::value:
|
||||
mScript = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'S','N','A','M'>::value:
|
||||
mSound = esm.getHString();
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing LHDT subrecord");
|
||||
}
|
||||
void Light::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNT("LHDT", mData, 24);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("SNAM", mSound);
|
||||
}
|
||||
|
||||
void Light::blank()
|
||||
{
|
||||
|
|
|
@ -37,7 +37,7 @@ struct Light
|
|||
int mValue;
|
||||
int mTime; // Duration
|
||||
int mRadius;
|
||||
int mColor; // 4-byte rgba value
|
||||
unsigned int mColor; // 4-byte rgba value
|
||||
int mFlags;
|
||||
}; // Size = 24 bytes
|
||||
|
||||
|
|
|
@ -8,26 +8,48 @@ namespace ESM
|
|||
{
|
||||
unsigned int Lockpick::sRecordId = REC_LOCK;
|
||||
|
||||
void Lockpick::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
void Lockpick::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = true;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'L','K','D','T'>::value:
|
||||
esm.getHT(mData, 16);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing LKDT subrecord");
|
||||
}
|
||||
|
||||
esm.getHNT(mData, "LKDT", 16);
|
||||
void Lockpick::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
}
|
||||
|
||||
void Lockpick::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
|
||||
esm.writeHNT("LKDT", mData, 16);
|
||||
esm.writeHNOString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
esm.writeHNT("LKDT", mData, 16);
|
||||
esm.writeHNOString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
|
||||
void Lockpick::blank()
|
||||
{
|
||||
|
|
|
@ -191,33 +191,64 @@ namespace ESM
|
|||
|
||||
void MagicEffect::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
|
||||
mId = indexToId (mIndex);
|
||||
|
||||
esm.getHNT(mData, "MEDT", 36);
|
||||
if (esm.getFormat() == 0)
|
||||
{
|
||||
// don't allow mods to change fixed flags in the legacy format
|
||||
mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);
|
||||
if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)
|
||||
esm.getHNT(mData, "MEDT", 36);
|
||||
if (esm.getFormat() == 0)
|
||||
{
|
||||
// don't allow mods to change fixed flags in the legacy format
|
||||
mData.mFlags &= (AllowSpellmaking | AllowEnchanting | NegativeLight);
|
||||
if (mIndex>=0 && mIndex<NumberOfHardcodedFlags)
|
||||
mData.mFlags |= HardcodedFlags[mIndex];
|
||||
}
|
||||
}
|
||||
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mParticle = esm.getHNOString("PTEX");
|
||||
|
||||
mBoltSound = esm.getHNOString("BSND");
|
||||
mCastSound = esm.getHNOString("CSND");
|
||||
mHitSound = esm.getHNOString("HSND");
|
||||
mAreaSound = esm.getHNOString("ASND");
|
||||
|
||||
mCasting = esm.getHNOString("CVFX");
|
||||
mBolt = esm.getHNOString("BVFX");
|
||||
mHit = esm.getHNOString("HVFX");
|
||||
mArea = esm.getHNOString("AVFX");
|
||||
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
// vanilla MW accepts the _SND subrecords before or after DESC... I hope
|
||||
// this isn't true for other records, or we have to do a mass-refactor
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'I','T','E','X'>::value:
|
||||
mIcon = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'P','T','E','X'>::value:
|
||||
mParticle = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'B','S','N','D'>::value:
|
||||
mBoltSound = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'C','S','N','D'>::value:
|
||||
mCastSound = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'H','S','N','D'>::value:
|
||||
mHitSound = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'A','S','N','D'>::value:
|
||||
mAreaSound = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'C','V','F','X'>::value:
|
||||
mCasting = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'B','V','F','X'>::value:
|
||||
mBolt = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'H','V','F','X'>::value:
|
||||
mHit = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'A','V','F','X'>::value:
|
||||
mArea = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'D','E','S','C'>::value:
|
||||
mDescription = esm.getHString();
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
}
|
||||
void MagicEffect::save(ESMWriter &esm) const
|
||||
{
|
||||
|
|
|
@ -57,9 +57,9 @@ struct MagicEffect
|
|||
// Glow color for enchanted items with this effect
|
||||
int mRed, mGreen, mBlue;
|
||||
|
||||
float mUnknown1;
|
||||
float mUnknown1; // Called "Size X" in CS
|
||||
float mSpeed; // Speed of fired projectile
|
||||
float mUnknown2;
|
||||
float mUnknown2; // Called "Size Cap" in CS
|
||||
}; // 36 bytes
|
||||
|
||||
static const std::map<short,std::string> sNames;
|
||||
|
|
|
@ -8,22 +8,45 @@ namespace ESM
|
|||
{
|
||||
unsigned int Miscellaneous::sRecordId = REC_MISC;
|
||||
|
||||
void Miscellaneous::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "MCDT", 12);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
}
|
||||
void Miscellaneous::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("MCDT", mData, 12);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
void Miscellaneous::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'M','C','D','T'>::value:
|
||||
esm.getHT(mData, 12);
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing MCDT subrecord");
|
||||
}
|
||||
|
||||
void Miscellaneous::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("MCDT", mData, 12);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
|
||||
void Miscellaneous::blank()
|
||||
{
|
||||
|
|
|
@ -8,93 +8,128 @@ namespace ESM
|
|||
{
|
||||
unsigned int NPC::sRecordId = REC_NPC_;
|
||||
|
||||
void NPC::load(ESMReader &esm)
|
||||
{
|
||||
mPersistent = esm.getRecordFlags() & 0x0400;
|
||||
|
||||
mModel = esm.getHNOString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
|
||||
mRace = esm.getHNString("RNAM");
|
||||
mClass = esm.getHNString("CNAM");
|
||||
mFaction = esm.getHNString("ANAM");
|
||||
mHead = esm.getHNString("BNAM");
|
||||
mHair = esm.getHNString("KNAM");
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
|
||||
esm.getSubNameIs("NPDT");
|
||||
esm.getSubHeader();
|
||||
if (esm.getSubSize() == 52)
|
||||
void NPC::load(ESMReader &esm)
|
||||
{
|
||||
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");
|
||||
mPersistent = (esm.getRecordFlags() & 0x0400) != 0;
|
||||
|
||||
esm.getHNT(mFlags, "FLAG");
|
||||
mSpells.mList.clear();
|
||||
mInventory.mList.clear();
|
||||
mTransport.mList.clear();
|
||||
mAiPackage.mList.clear();
|
||||
|
||||
mInventory.load(esm);
|
||||
mSpells.load(esm);
|
||||
|
||||
if (esm.isNextSub("AIDT"))
|
||||
{
|
||||
esm.getHExact(&mAiData, sizeof(mAiData));
|
||||
mHasAI= true;
|
||||
}
|
||||
else
|
||||
bool hasNpdt = false;
|
||||
bool hasFlags = false;
|
||||
mHasAI = false;
|
||||
|
||||
mTransport.clear();
|
||||
while (esm.isNextSub("DODT") || esm.isNextSub("DNAM")) {
|
||||
if (esm.retSubName() == 0x54444f44) { // DODT struct
|
||||
Dest dodt;
|
||||
esm.getHExact(&dodt.mPos, 24);
|
||||
mTransport.push_back(dodt);
|
||||
} else if (esm.retSubName() == 0x4d414e44) { // DNAM struct
|
||||
mTransport.back().mCellName = esm.getHString();
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasNpdt)
|
||||
esm.fail("Missing NPDT subrecord");
|
||||
if (!hasFlags)
|
||||
esm.fail("Missing FLAG subrecord");
|
||||
}
|
||||
mAiPackage.load(esm);
|
||||
}
|
||||
void NPC::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNCString("RNAM", mRace);
|
||||
esm.writeHNCString("CNAM", mClass);
|
||||
esm.writeHNCString("ANAM", mFaction);
|
||||
esm.writeHNCString("BNAM", mHead);
|
||||
esm.writeHNCString("KNAM", mHair);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
void NPC::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNCString("RNAM", mRace);
|
||||
esm.writeHNCString("CNAM", mClass);
|
||||
esm.writeHNCString("ANAM", mFaction);
|
||||
esm.writeHNCString("BNAM", mHead);
|
||||
esm.writeHNCString("KNAM", mHair);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
|
||||
if (mNpdtType == NPC_DEFAULT)
|
||||
esm.writeHNT("NPDT", mNpdt52, 52);
|
||||
else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS)
|
||||
esm.writeHNT("NPDT", mNpdt12, 12);
|
||||
if (mNpdtType == NPC_DEFAULT)
|
||||
esm.writeHNT("NPDT", mNpdt52, 52);
|
||||
else if (mNpdtType == NPC_WITH_AUTOCALCULATED_STATS)
|
||||
esm.writeHNT("NPDT", mNpdt12, 12);
|
||||
|
||||
esm.writeHNT("FLAG", mFlags);
|
||||
esm.writeHNT("FLAG", mFlags);
|
||||
|
||||
mInventory.save(esm);
|
||||
mSpells.save(esm);
|
||||
if (mHasAI) {
|
||||
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
|
||||
mInventory.save(esm);
|
||||
mSpells.save(esm);
|
||||
if (mHasAI) {
|
||||
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
|
||||
}
|
||||
|
||||
mTransport.save(esm);
|
||||
|
||||
mAiPackage.save(esm);
|
||||
}
|
||||
|
||||
typedef std::vector<Dest>::const_iterator DestIter;
|
||||
for (DestIter it = mTransport.begin(); it != mTransport.end(); ++it) {
|
||||
esm.writeHNT("DODT", it->mPos, sizeof(it->mPos));
|
||||
esm.writeHNOCString("DNAM", it->mCellName);
|
||||
}
|
||||
mAiPackage.save(esm);
|
||||
}
|
||||
|
||||
bool NPC::isMale() const {
|
||||
return (mFlags & Female) == 0;
|
||||
}
|
||||
|
@ -133,7 +168,7 @@ void NPC::save(ESMWriter &esm) const
|
|||
mSpells.mList.clear();
|
||||
mAiData.blank();
|
||||
mHasAI = false;
|
||||
mTransport.clear();
|
||||
mTransport.mList.clear();
|
||||
mAiPackage.mList.clear();
|
||||
mName.clear();
|
||||
mModel.clear();
|
||||
|
@ -144,4 +179,19 @@ void NPC::save(ESMWriter &esm) const
|
|||
mHair.clear();
|
||||
mHead.clear();
|
||||
}
|
||||
|
||||
int NPC::getFactionRank() const
|
||||
{
|
||||
if (mFaction.empty())
|
||||
return -1;
|
||||
else if (mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
return mNpdt12.mRank;
|
||||
else // NPC_DEFAULT
|
||||
return mNpdt52.mRank;
|
||||
}
|
||||
|
||||
const std::vector<Transport::Dest>& NPC::getTransport() const
|
||||
{
|
||||
return mTransport.mList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "aipackage.hpp"
|
||||
#include "spelllist.hpp"
|
||||
#include "loadskil.hpp"
|
||||
#include "transport.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
|
@ -98,18 +99,14 @@ struct NPC
|
|||
char mUnknown1, mUnknown2, mUnknown3;
|
||||
int mGold;
|
||||
}; // 12 bytes
|
||||
|
||||
struct Dest
|
||||
{
|
||||
Position mPos;
|
||||
std::string mCellName;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
unsigned char mNpdtType;
|
||||
NPDTstruct52 mNpdt52;
|
||||
NPDTstruct12 mNpdt12; //for autocalculated characters
|
||||
|
||||
int getFactionRank() const; /// wrapper for mNpdt*, -1 = no rank
|
||||
|
||||
int mFlags;
|
||||
|
||||
bool mPersistent;
|
||||
|
@ -120,7 +117,10 @@ struct NPC
|
|||
AIData mAiData;
|
||||
bool mHasAI;
|
||||
|
||||
std::vector<Dest> mTransport;
|
||||
Transport mTransport;
|
||||
|
||||
const std::vector<Transport::Dest>& getTransport() const;
|
||||
|
||||
AIPackageList mAiPackage;
|
||||
|
||||
std::string mId, mName, mModel, mRace, mClass, mFaction, mScript;
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
#ifndef OPENMW_ESM_NPCC_H
|
||||
#define OPENMW_ESM_NPCC_H
|
||||
|
||||
#include <string>
|
||||
|
||||
// TODO: create implementation files to remove this
|
||||
#include "esmreader.hpp"
|
||||
|
||||
namespace ESM {
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/*
|
||||
* NPC change information (found in savegame files only). We can't
|
||||
* read these yet.
|
||||
*
|
||||
* Some general observations about savegames:
|
||||
*
|
||||
* Magical items/potions/spells/etc are added normally as new ALCH,
|
||||
* SPEL, etc. records, with unique numeric identifiers.
|
||||
*
|
||||
* Books with ability enhancements are listed in the save if they have
|
||||
* been read.
|
||||
*
|
||||
* GLOB records set global variables.
|
||||
*
|
||||
* SCPT records do not define new scripts, but assign values to the
|
||||
* variables of existing ones.
|
||||
*
|
||||
* STLN - stolen items, ONAM is the owner
|
||||
*
|
||||
* GAME - contains a GMDT (game data) of unknown format
|
||||
*
|
||||
* VFXM, SPLM, KLST - no clue
|
||||
*
|
||||
* PCDT - seems to contain a lot of DNAMs, strings?
|
||||
*
|
||||
* FMAP - MAPH and MAPD, probably map data.
|
||||
*
|
||||
* JOUR - the entire journal in html
|
||||
*
|
||||
* QUES - seems to contain all the quests in the game, not just the
|
||||
* ones you have done or begun.
|
||||
*
|
||||
* REGN - lists all regions in the game, even unvisited ones.
|
||||
*
|
||||
* The DIAL/INFO blocks contain changes to characters' dialog status.
|
||||
*
|
||||
* Dammit there's a lot of stuff in there! Should really have
|
||||
* suspected as much. The strategy further is to completely ignore
|
||||
* save files for the time being.
|
||||
*
|
||||
* Several records have a "change" variant, like NPCC, CNTC
|
||||
* (contents), and CREC (creature.) These seem to alter specific
|
||||
* instances of creatures, npcs, etc. I have not identified most of
|
||||
* their subrecords yet.
|
||||
*
|
||||
* Several NPCC records have names that begin with "chargen ", I don't
|
||||
* know if it means something special yet.
|
||||
*
|
||||
* The CNTC blocks seem to be instances of leveled lists. When a
|
||||
* container is supposed to contain this leveled list of this type,
|
||||
* but is referenced elsewhere in the file by an INDX, the CNTC with
|
||||
* the corresponding leveled list identifier and INDX will determine
|
||||
* the container contents instead.
|
||||
*
|
||||
* Some classes of objects seem to be altered, and these include an
|
||||
* INDX, which is probably an index used by specific references other
|
||||
* places within the save file. I guess this means 'use this class for
|
||||
* these objects, not the general class.' All the indices I have
|
||||
* encountered so far are zero, but they have been for different
|
||||
* classes (different containers, really) so possibly we start from
|
||||
* zero for each class. This looks like a mess, but is probably still
|
||||
* easier than to duplicate everything. I think WRITING this format
|
||||
* will be harder than reading it.
|
||||
*/
|
||||
|
||||
struct LoadNPCC
|
||||
{
|
||||
static unsigned int sRecordId;
|
||||
|
||||
std::string mId;
|
||||
|
||||
void load(ESMReader &esm)
|
||||
{
|
||||
esm.skipRecord();
|
||||
}
|
||||
void save(ESMWriter &esm) const
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
|
@ -10,22 +10,22 @@ namespace ESM
|
|||
|
||||
Pathgrid::Point& Pathgrid::Point::operator=(const float rhs[3])
|
||||
{
|
||||
mX = rhs[0];
|
||||
mY = rhs[1];
|
||||
mZ = rhs[2];
|
||||
mX = static_cast<int>(rhs[0]);
|
||||
mY = static_cast<int>(rhs[1]);
|
||||
mZ = static_cast<int>(rhs[2]);
|
||||
mAutogenerated = 0;
|
||||
mConnectionNum = 0;
|
||||
mUnknown = 0;
|
||||
return *this;
|
||||
}
|
||||
Pathgrid::Point::Point(const float rhs[3])
|
||||
: mAutogenerated(0),
|
||||
: mX(static_cast<int>(rhs[0])),
|
||||
mY(static_cast<int>(rhs[1])),
|
||||
mZ(static_cast<int>(rhs[2])),
|
||||
mAutogenerated(0),
|
||||
mConnectionNum(0),
|
||||
mUnknown(0)
|
||||
{
|
||||
mX = rhs[0];
|
||||
mY = rhs[1];
|
||||
mZ = rhs[2];
|
||||
}
|
||||
Pathgrid::Point::Point():mX(0),mY(0),mZ(0),mAutogenerated(0),
|
||||
mConnectionNum(0),mUnknown(0)
|
||||
|
|
|
@ -8,26 +8,48 @@ namespace ESM
|
|||
{
|
||||
unsigned int Probe::sRecordId = REC_PROB;
|
||||
|
||||
void Probe::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
void Probe::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = true;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'P','B','D','T'>::value:
|
||||
esm.getHT(mData, 16);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing PBDT subrecord");
|
||||
}
|
||||
|
||||
esm.getHNT(mData, "PBDT", 16);
|
||||
void Probe::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
}
|
||||
|
||||
void Probe::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
|
||||
esm.writeHNT("PBDT", mData, 16);
|
||||
esm.writeHNOString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
esm.writeHNT("PBDT", mData, 16);
|
||||
esm.writeHNOString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
}
|
||||
|
||||
void Probe::blank()
|
||||
{
|
||||
|
|
|
@ -15,15 +15,39 @@ namespace ESM
|
|||
|
||||
int Race::MaleFemaleF::getValue (bool male) const
|
||||
{
|
||||
return male ? mMale : mFemale;
|
||||
return static_cast<int>(male ? mMale : mFemale);
|
||||
}
|
||||
|
||||
void Race::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "RADT", 140);
|
||||
mPowers.load(esm);
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
mPowers.mList.clear();
|
||||
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing RADT subrecord");
|
||||
}
|
||||
void Race::save(ESMWriter &esm) const
|
||||
{
|
||||
|
|
|
@ -10,13 +10,35 @@ namespace ESM
|
|||
|
||||
void Repair::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
|
||||
esm.getHNT(mData, "RIDT", 16);
|
||||
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
bool hasData = true;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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','I','D','T'>::value:
|
||||
esm.getHT(mData, 16);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing RIDT subrecord");
|
||||
}
|
||||
|
||||
void Repair::save(ESMWriter &esm) const
|
||||
|
|
|
@ -7,25 +7,9 @@
|
|||
namespace ESM
|
||||
{
|
||||
|
||||
struct SCHD
|
||||
{
|
||||
NAME32 mName;
|
||||
Script::SCHDstruct mData;
|
||||
};
|
||||
|
||||
unsigned int Script::sRecordId = REC_SCPT;
|
||||
|
||||
void Script::load(ESMReader &esm)
|
||||
{
|
||||
SCHD data;
|
||||
esm.getHNT(data, "SCHD", 52);
|
||||
mData = data.mData;
|
||||
mId = data.mName.toString();
|
||||
|
||||
mVarNames.clear();
|
||||
|
||||
// List of local variables
|
||||
if (esm.isNextSub("SCVR"))
|
||||
void Script::loadSCVR(ESMReader &esm)
|
||||
{
|
||||
int s = mData.mStringTableSize;
|
||||
|
||||
|
@ -72,58 +56,70 @@ void Script::load(ESMReader &esm)
|
|||
}
|
||||
}
|
||||
|
||||
// Script mData
|
||||
if (esm.isNextSub("SCDT"))
|
||||
void Script::load(ESMReader &esm)
|
||||
{
|
||||
mScriptData.resize(mData.mScriptDataSize);
|
||||
esm.getHExact(&mScriptData[0], mScriptData.size());
|
||||
}
|
||||
SCHD data;
|
||||
esm.getHNT(data, "SCHD", 52);
|
||||
mData = data.mData;
|
||||
mId = data.mName.toString();
|
||||
|
||||
// Script text
|
||||
mScriptText = esm.getHNOString("SCTX");
|
||||
mVarNames.clear();
|
||||
|
||||
// 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 Script::save(ESMWriter &esm) const
|
||||
{
|
||||
std::string varNameString;
|
||||
if (!mVarNames.empty())
|
||||
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
|
||||
varNameString.append(*it);
|
||||
|
||||
SCHD data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
data.mData = mData;
|
||||
memcpy(data.mName.name, mId.c_str(), mId.size());
|
||||
|
||||
esm.writeHNT("SCHD", data, 52);
|
||||
|
||||
if (!mVarNames.empty())
|
||||
{
|
||||
esm.startSubRecord("SCVR");
|
||||
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.writeHCString(*it);
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'S','C','V','R'>::value:
|
||||
// list of local variables
|
||||
loadSCVR(esm);
|
||||
break;
|
||||
case ESM::FourCC<'S','C','D','T'>::value:
|
||||
// compiled script
|
||||
mScriptData.resize(mData.mScriptDataSize);
|
||||
esm.getHExact(&mScriptData[0], mScriptData.size());
|
||||
break;
|
||||
case ESM::FourCC<'S','C','T','X'>::value:
|
||||
mScriptText = esm.getHString();
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
esm.endRecord("SCVR");
|
||||
}
|
||||
|
||||
esm.startSubRecord("SCDT");
|
||||
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
|
||||
esm.endRecord("SCDT");
|
||||
void Script::save(ESMWriter &esm) const
|
||||
{
|
||||
std::string varNameString;
|
||||
if (!mVarNames.empty())
|
||||
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
|
||||
varNameString.append(*it);
|
||||
|
||||
esm.writeHNOString("SCTX", mScriptText);
|
||||
}
|
||||
SCHD data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
data.mData = mData;
|
||||
memcpy(data.mName.name, mId.c_str(), mId.size());
|
||||
|
||||
esm.writeHNT("SCHD", data, 52);
|
||||
|
||||
if (!mVarNames.empty())
|
||||
{
|
||||
esm.startSubRecord("SCVR");
|
||||
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
|
||||
{
|
||||
esm.writeHCString(*it);
|
||||
}
|
||||
esm.endRecord("SCVR");
|
||||
}
|
||||
|
||||
esm.startSubRecord("SCDT");
|
||||
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
|
||||
esm.endRecord("SCDT");
|
||||
|
||||
esm.writeHNOString("SCTX", mScriptText);
|
||||
}
|
||||
|
||||
void Script::blank()
|
||||
{
|
||||
|
|
|
@ -26,6 +26,11 @@ public:
|
|||
/// Data from script-precompling in the editor.
|
||||
/// \warning Do not use them. OpenCS currently does not precompile scripts.
|
||||
int mNumShorts, mNumLongs, mNumFloats, mScriptDataSize, mStringTableSize;
|
||||
};
|
||||
struct SCHD
|
||||
{
|
||||
NAME32 mName;
|
||||
Script::SCHDstruct mData;
|
||||
}; // 52 bytes
|
||||
|
||||
std::string mId;
|
||||
|
@ -48,6 +53,9 @@ public:
|
|||
|
||||
void blank();
|
||||
///< Set record to default state (does not touch the ID/index).
|
||||
|
||||
private:
|
||||
void loadSCVR(ESMReader &esm);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -129,23 +129,47 @@ namespace ESM
|
|||
|
||||
unsigned int Skill::sRecordId = REC_SKIL;
|
||||
|
||||
void Skill::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mIndex, "INDX");
|
||||
esm.getHNT(mData, "SKDT", 24);
|
||||
mDescription = esm.getHNOString("DESC");
|
||||
void Skill::load(ESMReader &esm)
|
||||
{
|
||||
bool hasIndex = false;
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'I','N','D','X'>::value:
|
||||
esm.getHT(mIndex);
|
||||
hasIndex = true;
|
||||
break;
|
||||
case ESM::FourCC<'S','K','D','T'>::value:
|
||||
esm.getHT(mData, 24);
|
||||
hasData = true;
|
||||
break;
|
||||
case ESM::FourCC<'D','E','S','C'>::value:
|
||||
mDescription = esm.getHString();
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasIndex)
|
||||
esm.fail("Missing INDX");
|
||||
if (!hasData)
|
||||
esm.fail("Missing SKDT");
|
||||
|
||||
// create an ID from the index and the name (only used in the editor and likely to change in the
|
||||
// future)
|
||||
mId = indexToId (mIndex);
|
||||
}
|
||||
// create an ID from the index and the name (only used in the editor and likely to change in the
|
||||
// future)
|
||||
mId = indexToId (mIndex);
|
||||
}
|
||||
|
||||
void Skill::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("INDX", mIndex);
|
||||
esm.writeHNT("SKDT", mData, 24);
|
||||
esm.writeHNOString("DESC", mDescription);
|
||||
}
|
||||
void Skill::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("INDX", mIndex);
|
||||
esm.writeHNT("SKDT", mData, 24);
|
||||
esm.writeHNOString("DESC", mDescription);
|
||||
}
|
||||
|
||||
void Skill::blank()
|
||||
{
|
||||
|
|
|
@ -8,19 +8,38 @@ namespace ESM
|
|||
{
|
||||
unsigned int SoundGenerator::sRecordId = REC_SNDG;
|
||||
|
||||
void SoundGenerator::load(ESMReader &esm)
|
||||
{
|
||||
esm.getHNT(mType, "DATA", 4);
|
||||
|
||||
mCreature = esm.getHNOString("CNAM");
|
||||
mSound = esm.getHNOString("SNAM");
|
||||
}
|
||||
void SoundGenerator::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mType, 4);
|
||||
esm.writeHNOCString("CNAM", mCreature);
|
||||
esm.writeHNOCString("SNAM", mSound);
|
||||
}
|
||||
void SoundGenerator::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'D','A','T','A'>::value:
|
||||
esm.getHT(mType, 4);
|
||||
hasData = true;
|
||||
break;
|
||||
case ESM::FourCC<'C','N','A','M'>::value:
|
||||
mCreature = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'S','N','A','M'>::value:
|
||||
mSound = esm.getHString();
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing DATA");
|
||||
}
|
||||
void SoundGenerator::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNT("DATA", mType, 4);
|
||||
esm.writeHNOCString("CNAM", mCreature);
|
||||
esm.writeHNOCString("SNAM", mSound);
|
||||
}
|
||||
|
||||
void SoundGenerator::blank()
|
||||
{
|
||||
|
|
|
@ -8,22 +8,35 @@ namespace ESM
|
|||
{
|
||||
unsigned int Sound::sRecordId = REC_SOUN;
|
||||
|
||||
void Sound::load(ESMReader &esm)
|
||||
{
|
||||
mSound = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "DATA", 3);
|
||||
/*
|
||||
cout << "vol=" << (int)data.volume
|
||||
<< " min=" << (int)data.minRange
|
||||
<< " max=" << (int)data.maxRange
|
||||
<< endl;
|
||||
*/
|
||||
}
|
||||
void Sound::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mSound);
|
||||
esm.writeHNT("DATA", mData, 3);
|
||||
}
|
||||
void Sound::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'F','N','A','M'>::value:
|
||||
mSound = esm.getHString();
|
||||
break;
|
||||
case ESM::FourCC<'D','A','T','A'>::value:
|
||||
esm.getHT(mData, 3);
|
||||
hasData = true;
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing DATA");
|
||||
}
|
||||
|
||||
void Sound::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mSound);
|
||||
esm.writeHNT("DATA", mData, 3);
|
||||
}
|
||||
|
||||
void Sound::blank()
|
||||
{
|
||||
|
|
|
@ -8,19 +8,41 @@ namespace ESM
|
|||
{
|
||||
unsigned int Spell::sRecordId = REC_SPEL;
|
||||
|
||||
void Spell::load(ESMReader &esm)
|
||||
{
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "SPDT", 12);
|
||||
mEffects.load(esm);
|
||||
}
|
||||
void Spell::load(ESMReader &esm)
|
||||
{
|
||||
mEffects.mList.clear();
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t val = esm.retSubName().val;
|
||||
|
||||
void Spell::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("SPDT", mData, 12);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
switch (val)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing SPDT subrecord");
|
||||
}
|
||||
|
||||
void Spell::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("SPDT", mData, 12);
|
||||
mEffects.save(esm);
|
||||
}
|
||||
|
||||
void Spell::blank()
|
||||
{
|
||||
|
|
|
@ -8,15 +8,41 @@ namespace ESM
|
|||
{
|
||||
unsigned int StartScript::sRecordId = REC_SSCR;
|
||||
|
||||
void StartScript::load(ESMReader &esm)
|
||||
{
|
||||
mData = esm.getHNString("DATA");
|
||||
mScript = esm.getHNString("NAME");
|
||||
}
|
||||
void StartScript::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNString("DATA", mData);
|
||||
esm.writeHNString("NAME", mScript);
|
||||
}
|
||||
void StartScript::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
bool hasName = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
case ESM::FourCC<'D','A','T','A'>::value:
|
||||
mData = esm.getHString();
|
||||
hasData = true;
|
||||
break;
|
||||
case ESM::FourCC<'N','A','M','E'>::value:
|
||||
mId = esm.getHString();
|
||||
hasName = true;
|
||||
break;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing DATA");
|
||||
if (!hasName)
|
||||
esm.fail("Missing NAME");
|
||||
}
|
||||
void StartScript::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNString("DATA", mData);
|
||||
esm.writeHNString("NAME", mId);
|
||||
}
|
||||
|
||||
void StartScript::blank()
|
||||
{
|
||||
mData.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,13 @@ struct StartScript
|
|||
static unsigned int sRecordId;
|
||||
|
||||
std::string mData;
|
||||
std::string mId, mScript;
|
||||
std::string mId;
|
||||
|
||||
// Load a record and add it to the list
|
||||
void load(ESMReader &esm);
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
void blank();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,15 +8,16 @@ namespace ESM
|
|||
{
|
||||
unsigned int Static::sRecordId = REC_STAT;
|
||||
|
||||
void Static::load(ESMReader &esm)
|
||||
{
|
||||
mPersistent = esm.getRecordFlags() & 0x0400;
|
||||
mModel = esm.getHNString("MODL");
|
||||
}
|
||||
void Static::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
}
|
||||
void Static::load(ESMReader &esm)
|
||||
{
|
||||
mPersistent = (esm.getRecordFlags() & 0x0400) != 0;
|
||||
|
||||
mModel = esm.getHNString("MODL");
|
||||
}
|
||||
void Static::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
}
|
||||
|
||||
void Static::blank()
|
||||
{
|
||||
|
|
|
@ -45,6 +45,25 @@ void ESM::Header::load (ESMReader &esm)
|
|||
m.size = esm.getHNLong ("DATA");
|
||||
mMaster.push_back (m);
|
||||
}
|
||||
|
||||
if (esm.isNextSub("GMDT"))
|
||||
{
|
||||
esm.getHT(mGameData);
|
||||
}
|
||||
if (esm.isNextSub("SCRD"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
mSCRD.resize(esm.getSubSize());
|
||||
if (mSCRD.size())
|
||||
esm.getExact(&mSCRD[0], mSCRD.size());
|
||||
}
|
||||
if (esm.isNextSub("SCRS"))
|
||||
{
|
||||
esm.getSubHeader();
|
||||
mSCRS.resize(esm.getSubSize());
|
||||
if (mSCRS.size())
|
||||
esm.getExact(&mSCRS[0], mSCRS.size());
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::Header::save (ESMWriter &esm)
|
||||
|
|
|
@ -39,6 +39,20 @@ namespace ESM
|
|||
int index; // Position of the parent file in the global list of loaded files
|
||||
};
|
||||
|
||||
struct GMDT
|
||||
{
|
||||
float mCurrentHealth;
|
||||
float mMaximumHealth;
|
||||
float mHour;
|
||||
unsigned char unknown1[12];
|
||||
NAME64 mCurrentCell;
|
||||
unsigned char unknown2[4];
|
||||
NAME32 mPlayerName;
|
||||
};
|
||||
GMDT mGameData; // Used in .ess savegames only
|
||||
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
|
||||
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
|
||||
|
||||
Data mData;
|
||||
int mFormat;
|
||||
std::vector<MasterData> mMaster;
|
||||
|
|
|
@ -8,24 +8,50 @@ namespace ESM
|
|||
{
|
||||
unsigned int Weapon::sRecordId = REC_WEAP;
|
||||
|
||||
void Weapon::load(ESMReader &esm)
|
||||
{
|
||||
mModel = esm.getHNString("MODL");
|
||||
mName = esm.getHNOString("FNAM");
|
||||
esm.getHNT(mData, "WPDT", 32);
|
||||
mScript = esm.getHNOString("SCRI");
|
||||
mIcon = esm.getHNOString("ITEX");
|
||||
mEnchant = esm.getHNOString("ENAM");
|
||||
}
|
||||
void Weapon::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("WPDT", mData, 32);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
void Weapon::load(ESMReader &esm)
|
||||
{
|
||||
bool hasData = false;
|
||||
while (esm.hasMoreSubs())
|
||||
{
|
||||
esm.getSubName();
|
||||
uint32_t name = esm.retSubName().val;
|
||||
switch (name)
|
||||
{
|
||||
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<'W','P','D','T'>::value:
|
||||
esm.getHT(mData, 32);
|
||||
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;
|
||||
default:
|
||||
esm.fail("Unknown subrecord");
|
||||
}
|
||||
}
|
||||
if (!hasData)
|
||||
esm.fail("Missing WPDT subrecord");
|
||||
}
|
||||
void Weapon::save(ESMWriter &esm) const
|
||||
{
|
||||
esm.writeHNCString("MODL", mModel);
|
||||
esm.writeHNOCString("FNAM", mName);
|
||||
esm.writeHNT("WPDT", mData, 32);
|
||||
esm.writeHNOCString("SCRI", mScript);
|
||||
esm.writeHNOCString("ITEX", mIcon);
|
||||
esm.writeHNOCString("ENAM", mEnchant);
|
||||
}
|
||||
|
||||
void Weapon::blank()
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ void ESM::Locals::load (ESMReader &esm)
|
|||
std::string id = esm.getHString();
|
||||
|
||||
Variant value;
|
||||
value.read (esm, Variant::Format_Info);
|
||||
value.read (esm, Variant::Format_Local);
|
||||
|
||||
mVariables.push_back (std::make_pair (id, value));
|
||||
}
|
||||
|
@ -23,6 +23,6 @@ void ESM::Locals::save (ESMWriter &esm) const
|
|||
iter!=mVariables.end(); ++iter)
|
||||
{
|
||||
esm.writeHNString ("LOCA", iter->first);
|
||||
iter->second.write (esm, Variant::Format_Info);
|
||||
iter->second.write (esm, Variant::Format_Local);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,20 +5,34 @@ void ESM::NpcState::load (ESMReader &esm)
|
|||
{
|
||||
ObjectState::load (esm);
|
||||
|
||||
mInventory.load (esm);
|
||||
if (mHasCustomState)
|
||||
{
|
||||
mInventory.load (esm);
|
||||
|
||||
mNpcStats.load (esm);
|
||||
mNpcStats.load (esm);
|
||||
|
||||
mCreatureStats.load (esm);
|
||||
mCreatureStats.load (esm);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const
|
||||
{
|
||||
ObjectState::save (esm, inInventory);
|
||||
|
||||
mInventory.save (esm);
|
||||
if (mHasCustomState)
|
||||
{
|
||||
mInventory.save (esm);
|
||||
|
||||
mNpcStats.save (esm);
|
||||
mNpcStats.save (esm);
|
||||
|
||||
mCreatureStats.save (esm);
|
||||
}
|
||||
mCreatureStats.save (esm);
|
||||
}
|
||||
}
|
||||
|
||||
void ESM::NpcState::blank()
|
||||
{
|
||||
ObjectState::blank();
|
||||
mNpcStats.blank();
|
||||
mCreatureStats.blank();
|
||||
mHasCustomState = true;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ namespace ESM
|
|||
NpcStats mNpcStats;
|
||||
CreatureStats mCreatureStats;
|
||||
|
||||
/// Initialize to default state
|
||||
void blank();
|
||||
|
||||
virtual void load (ESMReader &esm);
|
||||
virtual void save (ESMWriter &esm, bool inInventory = false) const;
|
||||
};
|
||||
|
|
|
@ -57,12 +57,13 @@ void ESM::NpcStats::load (ESMReader &esm)
|
|||
mWerewolfKills = 0;
|
||||
esm.getHNOT (mWerewolfKills, "WKIL");
|
||||
|
||||
mProfit = 0;
|
||||
esm.getHNOT (mProfit, "PROF");
|
||||
// No longer used
|
||||
if (esm.isNextSub("PROF"))
|
||||
esm.skipHSub(); // int profit
|
||||
|
||||
// No longer used. Now part of CreatureStats.
|
||||
float attackStrength = 0;
|
||||
esm.getHNOT (attackStrength, "ASTR");
|
||||
if (esm.isNextSub("ASTR"))
|
||||
esm.skipHSub(); // attackStrength
|
||||
|
||||
mLevelProgress = 0;
|
||||
esm.getHNOT (mLevelProgress, "LPRO");
|
||||
|
@ -75,8 +76,9 @@ void ESM::NpcStats::load (ESMReader &esm)
|
|||
mTimeToStartDrowning = 0;
|
||||
esm.getHNOT (mTimeToStartDrowning, "DRTI");
|
||||
|
||||
mLastDrowningHit = 0;
|
||||
esm.getHNOT (mLastDrowningHit, "DRLH");
|
||||
// No longer used
|
||||
float lastDrowningHit = 0;
|
||||
esm.getHNOT (lastDrowningHit, "DRLH");
|
||||
|
||||
// No longer used
|
||||
float levelHealthBonus = 0;
|
||||
|
@ -131,9 +133,6 @@ void ESM::NpcStats::save (ESMWriter &esm) const
|
|||
if (mWerewolfKills)
|
||||
esm.writeHNT ("WKIL", mWerewolfKills);
|
||||
|
||||
if (mProfit)
|
||||
esm.writeHNT ("PROF", mProfit);
|
||||
|
||||
if (mLevelProgress)
|
||||
esm.writeHNT ("LPRO", mLevelProgress);
|
||||
|
||||
|
@ -146,9 +145,20 @@ void ESM::NpcStats::save (ESMWriter &esm) const
|
|||
if (mTimeToStartDrowning)
|
||||
esm.writeHNT ("DRTI", mTimeToStartDrowning);
|
||||
|
||||
if (mLastDrowningHit)
|
||||
esm.writeHNT ("DRLH", mLastDrowningHit);
|
||||
|
||||
if (mCrimeId != -1)
|
||||
esm.writeHNT ("CRID", mCrimeId);
|
||||
}
|
||||
|
||||
void ESM::NpcStats::blank()
|
||||
{
|
||||
mIsWerewolf = false;
|
||||
mDisposition = 0;
|
||||
mBounty = 0;
|
||||
mReputation = 0;
|
||||
mWerewolfKills = 0;
|
||||
mLevelProgress = 0;
|
||||
for (int i=0; i<8; ++i)
|
||||
mSkillIncrease[i] = 0;
|
||||
mTimeToStartDrowning = 20;
|
||||
mCrimeId = -1;
|
||||
}
|
||||
|
|
|
@ -34,20 +34,21 @@ namespace ESM
|
|||
StatState<int> mWerewolfAttributes[8];
|
||||
bool mIsWerewolf;
|
||||
|
||||
std::map<std::string, Faction> mFactions;
|
||||
std::map<std::string, Faction> mFactions; // lower case IDs
|
||||
int mDisposition;
|
||||
Skill mSkills[27];
|
||||
int mBounty;
|
||||
int mReputation;
|
||||
int mWerewolfKills;
|
||||
int mProfit;
|
||||
int mLevelProgress;
|
||||
int mSkillIncrease[8];
|
||||
std::vector<std::string> mUsedIds;
|
||||
std::vector<std::string> mUsedIds; // lower case IDs
|
||||
float mTimeToStartDrowning;
|
||||
float mLastDrowningHit;
|
||||
int mCrimeId;
|
||||
|
||||
/// Initialize to default state
|
||||
void blank();
|
||||
|
||||
void load (ESMReader &esm);
|
||||
void save (ESMWriter &esm) const;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
void ESM::ObjectState::load (ESMReader &esm)
|
||||
{
|
||||
mRef.load (esm, true);
|
||||
mRef.loadData(esm);
|
||||
|
||||
mHasLocals = 0;
|
||||
esm.getHNOT (mHasLocals, "HLOC");
|
||||
|
@ -23,6 +23,14 @@ void ESM::ObjectState::load (ESMReader &esm)
|
|||
esm.getHNOT (mPosition, "POS_", 24);
|
||||
|
||||
esm.getHNOT (mLocalRotation, "LROT", 12);
|
||||
|
||||
// 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 ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
|
||||
|
@ -46,6 +54,24 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
|
|||
esm.writeHNT ("POS_", mPosition, 24);
|
||||
esm.writeHNT ("LROT", mLocalRotation, 12);
|
||||
}
|
||||
|
||||
if (!mHasCustomState)
|
||||
esm.writeHNT ("HCUS", false);
|
||||
}
|
||||
|
||||
ESM::ObjectState::~ObjectState() {}
|
||||
void ESM::ObjectState::blank()
|
||||
{
|
||||
mRef.blank();
|
||||
mHasLocals = 0;
|
||||
mEnabled = false;
|
||||
mCount = 1;
|
||||
for (int i=0;i<3;++i)
|
||||
{
|
||||
mPosition.pos[i] = 0;
|
||||
mPosition.rot[i] = 0;
|
||||
mLocalRotation[i] = 0;
|
||||
}
|
||||
mHasCustomState = true;
|
||||
}
|
||||
|
||||
ESM::ObjectState::~ObjectState() {}
|
||||
|
|
|
@ -26,11 +26,22 @@ namespace ESM
|
|||
ESM::Position mPosition;
|
||||
float mLocalRotation[3];
|
||||
|
||||
// Is there any class-specific state following the ObjectState
|
||||
bool mHasCustomState;
|
||||
|
||||
ObjectState() : mHasCustomState(true)
|
||||
{}
|
||||
|
||||
/// @note Does not load the CellRef ID, it should already be loaded before calling this method
|
||||
virtual void load (ESMReader &esm);
|
||||
|
||||
virtual void save (ESMWriter &esm, bool inInventory = false) const;
|
||||
|
||||
/// Initialize to default state
|
||||
void blank();
|
||||
|
||||
virtual ~ObjectState();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
void ESM::Player::load (ESMReader &esm)
|
||||
{
|
||||
mObject.mRef.loadId(esm, true);
|
||||
mObject.load (esm);
|
||||
|
||||
mCellId.load (esm);
|
||||
|
|
|
@ -16,4 +16,4 @@ void ESM::QuestState::save (ESMWriter &esm) const
|
|||
esm.writeHNString ("YETO", mTopic);
|
||||
esm.writeHNT ("QSTA", mState);
|
||||
esm.writeHNT ("QFIN", mFinished);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace ESM
|
|||
|
||||
struct QuestState
|
||||
{
|
||||
std::string mTopic;
|
||||
std::string mTopic; // lower case id
|
||||
int mState;
|
||||
unsigned char mFinished;
|
||||
|
||||
|
@ -21,4 +21,4 @@ namespace ESM
|
|||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "loadclot.hpp"
|
||||
#include "loadcont.hpp"
|
||||
#include "loadcrea.hpp"
|
||||
#include "loadcrec.hpp"
|
||||
#include "loadinfo.hpp"
|
||||
#include "loaddial.hpp"
|
||||
#include "loaddoor.hpp"
|
||||
|
@ -33,7 +32,6 @@
|
|||
#include "loadmgef.hpp"
|
||||
#include "loadmisc.hpp"
|
||||
#include "loadnpc.hpp"
|
||||
#include "loadnpcc.hpp"
|
||||
#include "loadpgrd.hpp"
|
||||
#include "loadrace.hpp"
|
||||
#include "loadregn.hpp"
|
||||
|
|
|
@ -5,12 +5,9 @@
|
|||
|
||||
namespace ESM {
|
||||
|
||||
void SpellList::load(ESMReader &esm)
|
||||
void SpellList::add(ESMReader &esm)
|
||||
{
|
||||
mList.clear();
|
||||
while (esm.isNextSub("NPCS")) {
|
||||
mList.push_back(esm.getHString());
|
||||
}
|
||||
mList.push_back(esm.getHString());
|
||||
}
|
||||
|
||||
void SpellList::save(ESMWriter &esm) const
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace ESM
|
|||
|
||||
/** A list of references to spells and spell effects. This is shared
|
||||
between the records BSGN, NPC and RACE.
|
||||
NPCS subrecord.
|
||||
*/
|
||||
struct SpellList
|
||||
{
|
||||
|
@ -19,7 +20,9 @@ namespace ESM
|
|||
/// Is this spell ID in mList?
|
||||
bool exists(const std::string& spell) const;
|
||||
|
||||
void load(ESMReader &esm);
|
||||
/// Load one spell, assumes the subrecord name was already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
void save(ESMWriter &esm) const;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace ESM
|
|||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
// NOTE: spell ids must be lower case
|
||||
struct SpellState
|
||||
{
|
||||
struct CorprusStats
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace ESM
|
|||
// mDamage was changed to a float; ensure backwards compatibility
|
||||
T oldDamage = 0;
|
||||
esm.getHNOT(oldDamage, "STDA");
|
||||
mDamage = oldDamage;
|
||||
mDamage = static_cast<float>(oldDamage);
|
||||
|
||||
esm.getHNOT (mDamage, "STDF");
|
||||
|
||||
|
|
47
components/esm/stolenitems.cpp
Normal file
47
components/esm/stolenitems.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "stolenitems.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void StolenItems::write(ESMWriter &esm) const
|
||||
{
|
||||
for (StolenItemsMap::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
|
||||
{
|
||||
esm.writeHNString("NAME", it->first);
|
||||
for (std::map<std::pair<std::string, bool>, int>::const_iterator ownerIt = it->second.begin();
|
||||
ownerIt != it->second.end(); ++ownerIt)
|
||||
{
|
||||
if (ownerIt->first.second)
|
||||
esm.writeHNString("FNAM", ownerIt->first.first);
|
||||
else
|
||||
esm.writeHNString("ONAM", ownerIt->first.first);
|
||||
esm.writeHNT("COUN", ownerIt->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StolenItems::load(ESMReader &esm)
|
||||
{
|
||||
while (esm.isNextSub("NAME"))
|
||||
{
|
||||
std::string itemid = esm.getHString();
|
||||
|
||||
std::map<std::pair<std::string, bool>, int> ownerMap;
|
||||
while (esm.isNextSub("FNAM") || esm.isNextSub("ONAM"))
|
||||
{
|
||||
std::string subname = esm.retSubName().toString();
|
||||
std::string owner = esm.getHString();
|
||||
bool isFaction = (subname == "FNAM");
|
||||
int count;
|
||||
esm.getHNT(count, "COUN");
|
||||
ownerMap.insert(std::make_pair(std::make_pair(owner, isFaction), count));
|
||||
}
|
||||
|
||||
mStolenItems[itemid] = ownerMap;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
components/esm/stolenitems.hpp
Normal file
24
components/esm/stolenitems.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_STOLENITEMS_H
|
||||
#define OPENMW_COMPONENTS_ESM_STOLENITEMS_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
// format 0, saved games only
|
||||
struct StolenItems
|
||||
{
|
||||
typedef std::map<std::string, std::map<std::pair<std::string, bool>, int> > StolenItemsMap;
|
||||
StolenItemsMap mStolenItems;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
void write(ESM::ESMWriter& esm) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
33
components/esm/transport.cpp
Normal file
33
components/esm/transport.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "transport.hpp"
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
void Transport::add(ESMReader &esm)
|
||||
{
|
||||
if (esm.retSubName().val == ESM::FourCC<'D','O','D','T'>::value)
|
||||
{
|
||||
Dest dodt;
|
||||
esm.getHExact(&dodt.mPos, 24);
|
||||
mList.push_back(dodt);
|
||||
}
|
||||
else if (esm.retSubName().val == ESM::FourCC<'D','N','A','M'>::value)
|
||||
{
|
||||
mList.back().mCellName = esm.getHString();
|
||||
}
|
||||
}
|
||||
|
||||
void Transport::save(ESMWriter &esm) const
|
||||
{
|
||||
typedef std::vector<Dest>::const_iterator DestIter;
|
||||
for (DestIter it = mList.begin(); it != mList.end(); ++it)
|
||||
{
|
||||
esm.writeHNT("DODT", it->mPos, sizeof(it->mPos));
|
||||
esm.writeHNOCString("DNAM", it->mCellName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
36
components/esm/transport.hpp
Normal file
36
components/esm/transport.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_TRANSPORT_H
|
||||
#define OPENMW_COMPONENTS_ESM_TRANSPORT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "defs.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
||||
class ESMReader;
|
||||
class ESMWriter;
|
||||
|
||||
/// List of travel service destination. Shared by CREA and NPC_ records.
|
||||
struct Transport
|
||||
{
|
||||
|
||||
struct Dest
|
||||
{
|
||||
Position mPos;
|
||||
std::string mCellName;
|
||||
};
|
||||
|
||||
std::vector<Dest> mList;
|
||||
|
||||
/// Load one destination, assumes the subrecord name was already read
|
||||
void add(ESMReader &esm);
|
||||
|
||||
void save(ESMWriter &esm) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -13,6 +13,7 @@ namespace
|
|||
const uint32_t STRV = ESM::FourCC<'S','T','R','V'>::value;
|
||||
const uint32_t INTV = ESM::FourCC<'I','N','T','V'>::value;
|
||||
const uint32_t FLTV = ESM::FourCC<'F','L','T','V'>::value;
|
||||
const uint32_t STTV = ESM::FourCC<'S','T','T','V'>::value;
|
||||
}
|
||||
|
||||
ESM::Variant::Variant() : mType (VT_None), mData (0) {}
|
||||
|
@ -141,7 +142,7 @@ void ESM::Variant::read (ESMReader& esm, Format format)
|
|||
esm.fail ("invalid subrecord: " + name.toString());
|
||||
}
|
||||
}
|
||||
else // info
|
||||
else if (format == Format_Info)
|
||||
{
|
||||
esm.getSubName();
|
||||
NAME name = esm.retSubName();
|
||||
|
@ -157,6 +158,26 @@ void ESM::Variant::read (ESMReader& esm, Format format)
|
|||
else
|
||||
esm.fail ("invalid subrecord: " + name.toString());
|
||||
}
|
||||
else if (format == Format_Local)
|
||||
{
|
||||
esm.getSubName();
|
||||
NAME name = esm.retSubName();
|
||||
|
||||
if (name==INTV)
|
||||
{
|
||||
type = VT_Int;
|
||||
}
|
||||
else if (name==FLTV)
|
||||
{
|
||||
type = VT_Float;
|
||||
}
|
||||
else if (name==STTV)
|
||||
{
|
||||
type = VT_Short;
|
||||
}
|
||||
else
|
||||
esm.fail ("invalid subrecord: " + name.toString());
|
||||
}
|
||||
|
||||
setType (type);
|
||||
|
||||
|
@ -179,6 +200,9 @@ void ESM::Variant::write (ESMWriter& esm, Format format) const
|
|||
if (format==Format_Info)
|
||||
throw std::runtime_error ("can not serialise variant of type none to info format");
|
||||
|
||||
if (format==Format_Local)
|
||||
throw std::runtime_error ("can not serialise variant of type none to local format");
|
||||
|
||||
// nothing to do here for GMST format
|
||||
}
|
||||
else
|
||||
|
|
|
@ -33,7 +33,8 @@ namespace ESM
|
|||
{
|
||||
Format_Global,
|
||||
Format_Gmst,
|
||||
Format_Info // also used for local variables in saved game files
|
||||
Format_Info,
|
||||
Format_Local // local script variables in save game files
|
||||
};
|
||||
|
||||
Variant();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue