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:
cc9cii 2015-04-25 06:52:53 +10:00
commit f1a58994c6
889 changed files with 24666 additions and 10682 deletions

View file

@ -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

View file

@ -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;
};
}

View file

@ -28,4 +28,4 @@ namespace ESM
bool operator!= (const CellId& left, const CellId& right);
}
#endif
#endif

View file

@ -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)

View file

@ -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();

View file

@ -13,4 +13,4 @@ void ESM::ContainerState::save (ESMWriter &esm, bool inInventory) const
ObjectState::save (esm, inInventory);
mInventory.save (esm);
}
}

View file

@ -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();
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;
};

View 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");
}
}

View 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

View file

@ -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
};

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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) {

View file

@ -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;
};

View file

@ -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

View file

@ -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)

View file

@ -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.
*/

View file

@ -4,6 +4,8 @@
#include <fstream>
#include <stdexcept>
#include <components/to_utf8/to_utf8.hpp>
namespace ESM
{
ESMWriter::ESMWriter()

View file

@ -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

View file

@ -26,4 +26,4 @@ void ESM::GlobalScript::save (ESMWriter &esm) const
esm.writeHNT ("RUN_", mRunning);
esm.writeHNOString ("TARG", mTargetId);
}
}

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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()
{

View file

@ -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()
{

View file

@ -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

View file

@ -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()
{

View file

@ -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;
};

View file

@ -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
{

View file

@ -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()
{

View file

@ -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

View file

@ -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

View file

@ -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()
{

View file

@ -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

View file

@ -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();
}
}

View file

@ -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;
};

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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

View file

@ -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()
{

View file

@ -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

View file

@ -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
{

View file

@ -12,7 +12,7 @@ class ESMReader;
class ESMWriter;
/*
* Game setting, with automatic cleaning of "dirty" entries.
* Game setting
*
*/

View file

@ -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

View file

@ -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()
{

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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()
{

View file

@ -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

View file

@ -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()
{

View file

@ -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
{

View file

@ -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;

View file

@ -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()
{

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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

View file

@ -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)

View file

@ -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()
{

View file

@ -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
{

View file

@ -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

View file

@ -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()
{

View file

@ -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

View file

@ -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()
{

View file

@ -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()
{

View file

@ -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()
{

View file

@ -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()
{

View file

@ -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();
}
}

View file

@ -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();
};
}

View file

@ -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()
{

View file

@ -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)

View file

@ -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;

View file

@ -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()
{

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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() {}

View file

@ -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

View file

@ -6,6 +6,7 @@
void ESM::Player::load (ESMReader &esm)
{
mObject.mRef.loadId(esm, true);
mObject.load (esm);
mCellId.load (esm);

View file

@ -16,4 +16,4 @@ void ESM::QuestState::save (ESMWriter &esm) const
esm.writeHNString ("YETO", mTopic);
esm.writeHNT ("QSTA", mState);
esm.writeHNT ("QFIN", mFinished);
}
}

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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;
};
}

View file

@ -12,6 +12,7 @@ namespace ESM
class ESMReader;
class ESMWriter;
// NOTE: spell ids must be lower case
struct SpellState
{
struct CorprusStats

View file

@ -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");

View 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;
}
}
}

View 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

View 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);
}
}
}

View 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

View file

@ -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

View file

@ -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