Don't rely on subrecord order when reading (Fixes )

This commit is contained in:
scrawl 2015-02-10 23:16:25 +01:00
parent bf1839b370
commit 9009889d24
17 changed files with 574 additions and 253 deletions

View file

@ -11,14 +11,10 @@ namespace ESM
mServices = 0; mServices = 0;
} }
void AIPackageList::load(ESMReader &esm) void AIPackageList::add(ESMReader &esm)
{ {
mList.clear();
while (esm.hasMoreSubs()) {
// initialize every iteration
AIPackage pack; AIPackage pack;
esm.getSubName(); if (esm.retSubName() == AI_CNDT) {
if (esm.retSubName() == 0x54444e43) { // CNDT
mList.back().mCellName = esm.getHString(); mList.back().mCellName = esm.getHString();
} else if (esm.retSubName() == AI_Wander) { } else if (esm.retSubName() == AI_Wander) {
pack.mType = AI_Wander; pack.mType = AI_Wander;
@ -42,6 +38,29 @@ namespace ESM
} else { // not AI package related data, so leave } else { // not AI package related data, so leave
return; return;
} }
}
void AIPackageList::load(ESMReader &esm)
{
mList.clear();
while (esm.hasMoreSubs()) {
// initialize every iteration
esm.getSubName();
switch (esm.retSubName().val)
{
case AI_Wander:
case AI_Activate:
case AI_Escort:
case AI_Follow:
case AI_Travel:
case AI_CNDT:
add(esm);
break;
default:
return;
}
} }
} }

View file

@ -63,7 +63,8 @@ namespace ESM
AI_Travel = 0x545f4941, AI_Travel = 0x545f4941,
AI_Follow = 0x465f4941, AI_Follow = 0x465f4941,
AI_Escort = 0x455f4941, AI_Escort = 0x455f4941,
AI_Activate = 0x415f4941 AI_Activate = 0x415f4941,
AI_CNDT = 0x54444e43
}; };
/// \note Used for storaging packages in a single container /// \note Used for storaging packages in a single container
@ -90,11 +91,12 @@ namespace ESM
{ {
std::vector<AIPackage> mList; std::vector<AIPackage> mList;
/// \note This breaks consistency of subrecords reading: /// Add a single AIPackage, assumes subrecord name was already read
/// after calling it subrecord name is already read, so void add(ESMReader &esm);
/// it needs to use retSubName() if needed. But, hey, there
/// is only one field left (XSCL) and only two records uses AI /// TODO: remove this method. The ESM format does not guarantee that all AI packages follow one another
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };
} }

View file

@ -7,44 +7,79 @@
namespace ESM namespace ESM
{ {
void PartReferenceList::load(ESMReader &esm) void PartReferenceList::add(ESMReader &esm)
{
mParts.clear();
while (esm.isNextSub("INDX"))
{ {
PartReference pr; PartReference pr;
esm.getHT(pr.mPart); // The INDX byte esm.getHT(pr.mPart); // The INDX byte
pr.mMale = esm.getHNOString("BNAM"); pr.mMale = esm.getHNOString("BNAM");
pr.mFemale = esm.getHNOString("CNAM"); pr.mFemale = esm.getHNOString("CNAM");
mParts.push_back(pr); mParts.push_back(pr);
}
}
void PartReferenceList::save(ESMWriter &esm) const }
{
void PartReferenceList::load(ESMReader &esm)
{
mParts.clear();
while (esm.isNextSub("INDX"))
{
add(esm);
}
}
void PartReferenceList::save(ESMWriter &esm) const
{
for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it) for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it)
{ {
esm.writeHNT("INDX", it->mPart); esm.writeHNT("INDX", it->mPart);
esm.writeHNOString("BNAM", it->mMale); esm.writeHNOString("BNAM", it->mMale);
esm.writeHNOString("CNAM", it->mFemale); esm.writeHNOString("CNAM", it->mFemale);
} }
} }
unsigned int Armor::sRecordId = REC_ARMO; unsigned int Armor::sRecordId = REC_ARMO;
void Armor::load(ESMReader &esm) void Armor::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mParts.mParts.clear();
mName = esm.getHNOString("FNAM"); bool hasData = false;
mScript = esm.getHNOString("SCRI"); while (esm.hasMoreSubs())
esm.getHNT(mData, "AODT", 24); {
mIcon = esm.getHNOString("ITEX"); esm.getSubName();
mParts.load(esm); uint32_t name = esm.retSubName().val;
mEnchant = esm.getHNOString("ENAM"); 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 void Armor::save(ESMWriter &esm) const
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
@ -52,7 +87,7 @@ void Armor::save(ESMWriter &esm) const
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
mParts.save(esm); mParts.save(esm);
esm.writeHNOCString("ENAM", mEnchant); esm.writeHNOCString("ENAM", mEnchant);
} }
void Armor::blank() void Armor::blank()
{ {

View file

@ -55,6 +55,10 @@ struct PartReferenceList
{ {
std::vector<PartReference> mParts; 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 load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,18 +8,46 @@ namespace ESM
{ {
unsigned int Book::sRecordId = REC_BOOK; unsigned int Book::sRecordId = REC_BOOK;
void Book::load(ESMReader &esm) void Book::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = false;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
esm.getHNT(mData, "BKDT", 20); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
mIcon = esm.getHNOString("ITEX"); uint32_t name = esm.retSubName().val;
mText = esm.getHNOString("TEXT"); switch (name)
mEnchant = esm.getHNOString("ENAM"); {
} case ESM::FourCC<'M','O','D','L'>::value:
void Book::save(ESMWriter &esm) const 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.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("BKDT", mData, 20); esm.writeHNT("BKDT", mData, 20);
@ -27,7 +55,7 @@ void Book::save(ESMWriter &esm) const
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
esm.writeHNOString("TEXT", mText); esm.writeHNOString("TEXT", mText);
esm.writeHNOCString("ENAM", mEnchant); esm.writeHNOCString("ENAM", mEnchant);
} }
void Book::blank() void Book::blank()
{ {

View file

@ -10,17 +10,42 @@ namespace ESM
void Clothing::load(ESMReader &esm) void Clothing::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mParts.mParts.clear();
mName = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "CTDT", 12); while (esm.hasMoreSubs())
{
mScript = esm.getHNOString("SCRI"); esm.getSubName();
mIcon = esm.getHNOString("ITEX"); uint32_t name = esm.retSubName().val;
switch (name)
mParts.load(esm); {
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
mEnchant = esm.getHNOString("ENAM"); 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 void Clothing::save(ESMWriter &esm) const

View file

@ -7,14 +7,19 @@
namespace ESM namespace ESM
{ {
void InventoryList::add(ESMReader &esm)
{
ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
}
void InventoryList::load(ESMReader &esm) void InventoryList::load(ESMReader &esm)
{ {
mList.clear(); mList.clear();
ContItem ci;
while (esm.isNextSub("NPCO")) while (esm.isNextSub("NPCO"))
{ {
esm.getHT(ci, 36); add(esm);
mList.push_back(ci);
} }
} }

View file

@ -26,6 +26,10 @@ struct InventoryList
{ {
std::vector<ContItem> mList; std::vector<ContItem> mList;
/// Load one item, assumes subrecord name is already read
void add(ESMReader &esm);
/// TODO: remove this method, the ESM format doesn't guarantee that all ContItems follow one another
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,23 +8,43 @@ namespace ESM
{ {
unsigned int Door::sRecordId = REC_DOOR; unsigned int Door::sRecordId = REC_DOOR;
void Door::load(ESMReader &esm) void Door::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); while (esm.hasMoreSubs())
mName = esm.getHNOString("FNAM"); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
mOpenSound = esm.getHNOString("SNAM"); uint32_t name = esm.retSubName().val;
mCloseSound = esm.getHNOString("ANAM"); 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 void Door::save(ESMWriter &esm) const
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("SNAM", mOpenSound); esm.writeHNOCString("SNAM", mOpenSound);
esm.writeHNOCString("ANAM", mCloseSound); esm.writeHNOCString("ANAM", mCloseSound);
} }
void Door::blank() void Door::blank()
{ {

View file

@ -204,20 +204,51 @@ void MagicEffect::load(ESMReader &esm)
mData.mFlags |= HardcodedFlags[mIndex]; mData.mFlags |= HardcodedFlags[mIndex];
} }
mIcon = esm.getHNOString("ITEX"); // vanilla MW accepts the _SND subrecords before or after DESC... I hope
mParticle = esm.getHNOString("PTEX"); // this isn't true for other records, or we have to do a mass-refactor
while (esm.hasMoreSubs())
mBoltSound = esm.getHNOString("BSND"); {
mCastSound = esm.getHNOString("CSND"); esm.getSubName();
mHitSound = esm.getHNOString("HSND"); uint32_t name = esm.retSubName().val;
mAreaSound = esm.getHNOString("ASND"); switch (name)
{
mCasting = esm.getHNOString("CVFX"); case ESM::FourCC<'I','T','E','X'>::value:
mBolt = esm.getHNOString("BVFX"); mIcon = esm.getHString();
mHit = esm.getHNOString("HVFX"); break;
mArea = esm.getHNOString("AVFX"); case ESM::FourCC<'P','T','E','X'>::value:
mParticle = esm.getHString();
mDescription = esm.getHNOString("DESC"); 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 " + esm.retSubName().toString());
}
}
} }
void MagicEffect::save(ESMWriter &esm) const void MagicEffect::save(ESMWriter &esm) const
{ {

View file

@ -8,22 +8,45 @@ namespace ESM
{ {
unsigned int Miscellaneous::sRecordId = REC_MISC; unsigned int Miscellaneous::sRecordId = REC_MISC;
void Miscellaneous::load(ESMReader &esm) void Miscellaneous::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = false;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
esm.getHNT(mData, "MCDT", 12); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
mIcon = esm.getHNOString("ITEX"); uint32_t name = esm.retSubName().val;
} switch (name)
void Miscellaneous::save(ESMWriter &esm) const {
{ 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.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("MCDT", mData, 12); esm.writeHNT("MCDT", mData, 12);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
} }
void Miscellaneous::blank() void Miscellaneous::blank()
{ {

View file

@ -8,22 +8,50 @@ namespace ESM
{ {
unsigned int NPC::sRecordId = REC_NPC_; unsigned int NPC::sRecordId = REC_NPC_;
void NPC::load(ESMReader &esm) void NPC::load(ESMReader &esm)
{ {
mPersistent = esm.getRecordFlags() & 0x0400; mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNOString("MODL"); mSpells.mList.clear();
mName = esm.getHNOString("FNAM"); mInventory.mList.clear();
mTransport.clear();
mAiPackage.mList.clear();
mRace = esm.getHNString("RNAM"); bool hasNpdt = false;
mClass = esm.getHNString("CNAM"); bool hasFlags = false;
mFaction = esm.getHNString("ANAM"); mHasAI = false;
mHead = esm.getHNString("BNAM"); while (esm.hasMoreSubs())
mHair = esm.getHNString("KNAM"); {
esm.getSubName();
mScript = esm.getHNOString("SCRI"); uint32_t name = esm.retSubName().val;
switch (name)
esm.getSubNameIs("NPDT"); {
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(); esm.getSubHeader();
if (esm.getSubSize() == 52) if (esm.getSubSize() == 52)
{ {
@ -37,34 +65,50 @@ void NPC::load(ESMReader &esm)
} }
else else
esm.fail("NPC_NPDT must be 12 or 52 bytes long"); esm.fail("NPC_NPDT must be 12 or 52 bytes long");
break;
esm.getHNT(mFlags, "FLAG"); case ESM::FourCC<'F','L','A','G'>::value:
hasFlags = true;
mInventory.load(esm); esm.getHT(mFlags);
mSpells.load(esm); break;
case ESM::FourCC<'N','P','C','S'>::value:
if (esm.isNextSub("AIDT")) 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)); esm.getHExact(&mAiData, sizeof(mAiData));
mHasAI= true; mHasAI= true;
} break;
else case ESM::FourCC<'D','O','D','T'>::value:
mHasAI = false; {
mTransport.clear();
while (esm.isNextSub("DODT") || esm.isNextSub("DNAM")) {
if (esm.retSubName() == 0x54444f44) { // DODT struct
Dest dodt; Dest dodt;
esm.getHExact(&dodt.mPos, 24); esm.getHExact(&dodt.mPos, 24);
mTransport.push_back(dodt); mTransport.push_back(dodt);
} else if (esm.retSubName() == 0x4d414e44) { // DNAM struct break;
}
case ESM::FourCC<'D','N','A','M'>::value:
mTransport.back().mCellName = esm.getHString(); mTransport.back().mCellName = esm.getHString();
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 " + esm.retSubName().toString());
} }
} }
mAiPackage.load(esm); if (!hasNpdt)
} esm.fail("Missing NPDT subrecord");
void NPC::save(ESMWriter &esm) const if (!hasFlags)
{ esm.fail("Missing FLAG subrecord");
}
void NPC::save(ESMWriter &esm) const
{
esm.writeHNOCString("MODL", mModel); esm.writeHNOCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNCString("RNAM", mRace); esm.writeHNCString("RNAM", mRace);
@ -93,7 +137,7 @@ void NPC::save(ESMWriter &esm) const
esm.writeHNOCString("DNAM", it->mCellName); esm.writeHNOCString("DNAM", it->mCellName);
} }
mAiPackage.save(esm); mAiPackage.save(esm);
} }
bool NPC::isMale() const { bool NPC::isMale() const {
return (mFlags & Female) == 0; return (mFlags & Female) == 0;

View file

@ -20,10 +20,34 @@ namespace ESM
void Race::load(ESMReader &esm) void Race::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); mPowers.mList.clear();
esm.getHNT(mData, "RADT", 140);
mPowers.load(esm); bool hasData = false;
mDescription = esm.getHNOString("DESC"); 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 " + esm.retSubName().toString());
}
}
if (!hasData)
esm.fail("Missing RADT subrecord");
} }
void Race::save(ESMWriter &esm) const void Race::save(ESMWriter &esm) const
{ {

View file

@ -10,9 +10,30 @@ namespace ESM
void Spell::load(ESMReader &esm) void Spell::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "SPDT", 12); while (esm.hasMoreSubs())
mEffects.load(esm); {
esm.getSubName();
uint32_t val = esm.retSubName().val;
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 void Spell::save(ESMWriter &esm) const

View file

@ -8,24 +8,50 @@ namespace ESM
{ {
unsigned int Weapon::sRecordId = REC_WEAP; unsigned int Weapon::sRecordId = REC_WEAP;
void Weapon::load(ESMReader &esm) void Weapon::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = false;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
esm.getHNT(mData, "WPDT", 32); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
mIcon = esm.getHNOString("ITEX"); uint32_t name = esm.retSubName().val;
mEnchant = esm.getHNOString("ENAM"); switch (name)
} {
void Weapon::save(ESMWriter &esm) const 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.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("WPDT", mData, 32); esm.writeHNT("WPDT", mData, 32);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
esm.writeHNOCString("ENAM", mEnchant); esm.writeHNOCString("ENAM", mEnchant);
} }
void Weapon::blank() void Weapon::blank()
{ {

View file

@ -9,10 +9,15 @@ void SpellList::load(ESMReader &esm)
{ {
mList.clear(); mList.clear();
while (esm.isNextSub("NPCS")) { while (esm.isNextSub("NPCS")) {
mList.push_back(esm.getHString()); add(esm);
} }
} }
void SpellList::add(ESMReader &esm)
{
mList.push_back(esm.getHString());
}
void SpellList::save(ESMWriter &esm) const void SpellList::save(ESMWriter &esm) const
{ {
for (std::vector<std::string>::const_iterator it = mList.begin(); it != mList.end(); ++it) { for (std::vector<std::string>::const_iterator it = mList.begin(); it != mList.end(); ++it) {

View file

@ -19,6 +19,11 @@ namespace ESM
/// Is this spell ID in mList? /// Is this spell ID in mList?
bool exists(const std::string& spell) const; bool exists(const std::string& spell) const;
/// Load one spell, assumes the subrecord name was already read
void add(ESMReader &esm);
/// Load all spells
/// TODO: remove this method, the ESM format doesn't guarantee that all spell subrecords follow one another
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };