forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'scrawl/esm_rewrite'
This commit is contained in:
commit
de98ee0062
130 changed files with 3241 additions and 1887 deletions
|
@ -27,7 +27,8 @@ struct ESMData
|
||||||
std::vector<ESM::Header::MasterData> masters;
|
std::vector<ESM::Header::MasterData> masters;
|
||||||
|
|
||||||
std::deque<EsmTool::RecordBase *> mRecords;
|
std::deque<EsmTool::RecordBase *> mRecords;
|
||||||
std::map<ESM::Cell *, std::deque<ESM::CellRef> > mCellRefs;
|
// Value: (Reference, Deleted flag)
|
||||||
|
std::map<ESM::Cell *, std::deque<std::pair<ESM::CellRef, bool> > > mCellRefs;
|
||||||
std::map<int, int> mRecordStats;
|
std::map<int, int> mRecordStats;
|
||||||
|
|
||||||
static const std::set<int> sLabeledRec;
|
static const std::set<int> sLabeledRec;
|
||||||
|
@ -255,7 +256,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||||
while(cell.getNextRef(esm, ref, deleted))
|
while(cell.getNextRef(esm, ref, deleted))
|
||||||
{
|
{
|
||||||
if (save) {
|
if (save) {
|
||||||
info.data.mCellRefs[&cell].push_back(ref);
|
info.data.mCellRefs[&cell].push_back(std::make_pair(ref, deleted));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(quiet) continue;
|
if(quiet) continue;
|
||||||
|
@ -352,30 +353,9 @@ int load(Arguments& info)
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
esm.getRecHeader(flags);
|
esm.getRecHeader(flags);
|
||||||
|
|
||||||
// Is the user interested in this record type?
|
|
||||||
bool interested = true;
|
|
||||||
if (!info.types.empty())
|
|
||||||
{
|
|
||||||
std::vector<std::string>::iterator match;
|
|
||||||
match = std::find(info.types.begin(), info.types.end(),
|
|
||||||
n.toString());
|
|
||||||
if (match == info.types.end()) interested = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string id = esm.getHNOString("NAME");
|
|
||||||
if (id.empty())
|
|
||||||
id = esm.getHNOString("INAM");
|
|
||||||
|
|
||||||
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, id))
|
|
||||||
interested = false;
|
|
||||||
|
|
||||||
if(!quiet && interested)
|
|
||||||
std::cout << "\nRecord: " << n.toString()
|
|
||||||
<< " '" << id << "'\n";
|
|
||||||
|
|
||||||
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
||||||
|
if (record == 0)
|
||||||
if (record == 0) {
|
{
|
||||||
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
|
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
|
||||||
{
|
{
|
||||||
std::cout << "Skipping " << n.toString() << " records." << std::endl;
|
std::cout << "Skipping " << n.toString() << " records." << std::endl;
|
||||||
|
@ -385,29 +365,47 @@ int load(Arguments& info)
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
if (quiet) break;
|
if (quiet) break;
|
||||||
std::cout << " Skipping\n";
|
std::cout << " Skipping\n";
|
||||||
} else {
|
|
||||||
if (record->getType().val == ESM::REC_GMST) {
|
continue;
|
||||||
// preset id for GameSetting record
|
|
||||||
record->cast<ESM::GameSetting>()->get().mId = id;
|
|
||||||
}
|
}
|
||||||
record->setId(id);
|
|
||||||
record->setFlags((int) flags);
|
record->setFlags(static_cast<int>(flags));
|
||||||
record->setPrintPlain(info.plain_given);
|
record->setPrintPlain(info.plain_given);
|
||||||
record->load(esm);
|
record->load(esm);
|
||||||
if (!quiet && interested) record->print();
|
|
||||||
|
|
||||||
if (record->getType().val == ESM::REC_CELL && loadCells && interested) {
|
// Is the user interested in this record type?
|
||||||
|
bool interested = true;
|
||||||
|
if (!info.types.empty())
|
||||||
|
{
|
||||||
|
std::vector<std::string>::iterator match;
|
||||||
|
match = std::find(info.types.begin(), info.types.end(), n.toString());
|
||||||
|
if (match == info.types.end()) interested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.name.empty() && !Misc::StringUtils::ciEqual(info.name, record->getId()))
|
||||||
|
interested = false;
|
||||||
|
|
||||||
|
if(!quiet && interested)
|
||||||
|
{
|
||||||
|
std::cout << "\nRecord: " << n.toString() << " '" << record->getId() << "'\n";
|
||||||
|
record->print();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record->getType().val == ESM::REC_CELL && loadCells && interested)
|
||||||
|
{
|
||||||
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save) {
|
if (save)
|
||||||
|
{
|
||||||
info.data.mRecords.push_back(record);
|
info.data.mRecords.push_back(record);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
delete record;
|
delete record;
|
||||||
}
|
}
|
||||||
++info.data.mRecordStats[n.val];
|
++info.data.mRecordStats[n.val];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} catch(std::exception &e) {
|
} catch(std::exception &e) {
|
||||||
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
std::cout << "\nERROR:\n\n " << e.what() << std::endl;
|
||||||
|
@ -493,28 +491,19 @@ int clone(Arguments& info)
|
||||||
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
|
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
|
||||||
{
|
{
|
||||||
EsmTool::RecordBase *record = *it;
|
EsmTool::RecordBase *record = *it;
|
||||||
|
|
||||||
name.val = record->getType().val;
|
name.val = record->getType().val;
|
||||||
|
|
||||||
esm.startRecord(name.toString(), record->getFlags());
|
esm.startRecord(name.toString(), record->getFlags());
|
||||||
|
|
||||||
// TODO wrap this with std::set
|
|
||||||
if (ESMData::sLabeledRec.count(name.val) > 0) {
|
|
||||||
esm.writeHNCString("NAME", record->getId());
|
|
||||||
} else {
|
|
||||||
esm.writeHNOString("NAME", record->getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
record->save(esm);
|
record->save(esm);
|
||||||
|
|
||||||
if (name.val == ESM::REC_CELL) {
|
if (name.val == ESM::REC_CELL) {
|
||||||
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
||||||
if (!info.data.mCellRefs[ptr].empty()) {
|
if (!info.data.mCellRefs[ptr].empty()) {
|
||||||
typedef std::deque<ESM::CellRef> RefList;
|
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
|
||||||
RefList &refs = info.data.mCellRefs[ptr];
|
RefList &refs = info.data.mCellRefs[ptr];
|
||||||
for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt)
|
for (RefList::iterator refIt = refs.begin(); refIt != refs.end(); ++refIt)
|
||||||
{
|
{
|
||||||
refIt->save(esm);
|
refIt->first.save(esm, refIt->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,6 +405,7 @@ void Record<ESM::Activator>::print()
|
||||||
std::cout << " Name: " << mData.mName << std::endl;
|
std::cout << " Name: " << mData.mName << std::endl;
|
||||||
std::cout << " Model: " << mData.mModel << std::endl;
|
std::cout << " Model: " << mData.mModel << std::endl;
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -419,6 +420,7 @@ void Record<ESM::Potion>::print()
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
|
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
|
||||||
printEffectList(mData.mEffects);
|
printEffectList(mData.mEffects);
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -447,6 +449,7 @@ void Record<ESM::Armor>::print()
|
||||||
if (pit->mFemale != "")
|
if (pit->mFemale != "")
|
||||||
std::cout << " Female Name: " << pit->mFemale << std::endl;
|
std::cout << " Female Name: " << pit->mFemale << std::endl;
|
||||||
}
|
}
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -461,6 +464,7 @@ void Record<ESM::Apparatus>::print()
|
||||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -474,6 +478,7 @@ void Record<ESM::BodyPart>::print()
|
||||||
std::cout << " Part: " << meshPartLabel(mData.mData.mPart)
|
std::cout << " Part: " << meshPartLabel(mData.mData.mPart)
|
||||||
<< " (" << (int)mData.mData.mPart << ")" << std::endl;
|
<< " (" << (int)mData.mData.mPart << ")" << std::endl;
|
||||||
std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl;
|
std::cout << " Vampire: " << (int)mData.mData.mVampire << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -502,6 +507,7 @@ void Record<ESM::Book>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Text: [skipped]" << std::endl;
|
std::cout << " Text: [skipped]" << std::endl;
|
||||||
}
|
}
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -513,6 +519,7 @@ void Record<ESM::BirthSign>::print()
|
||||||
std::vector<std::string>::iterator pit;
|
std::vector<std::string>::iterator pit;
|
||||||
for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit)
|
for (pit = mData.mPowers.mList.begin(); pit != mData.mPowers.mList.end(); ++pit)
|
||||||
std::cout << " Power: " << *pit << std::endl;
|
std::cout << " Power: " << *pit << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -541,6 +548,7 @@ void Record<ESM::Cell>::print()
|
||||||
std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl;
|
std::cout << " Map Color: " << boost::format("0x%08X") % mData.mMapColor << std::endl;
|
||||||
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
|
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
|
||||||
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
|
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -563,6 +571,7 @@ void Record<ESM::Class>::print()
|
||||||
for (int i = 0; i != 5; i++)
|
for (int i = 0; i != 5; i++)
|
||||||
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
|
std::cout << " Major Skill: " << skillLabel(mData.mData.mSkills[i][1])
|
||||||
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
|
<< " (" << mData.mData.mSkills[i][1] << ")" << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -589,6 +598,7 @@ void Record<ESM::Clothing>::print()
|
||||||
if (pit->mFemale != "")
|
if (pit->mFemale != "")
|
||||||
std::cout << " Female Name: " << pit->mFemale << std::endl;
|
std::cout << " Female Name: " << pit->mFemale << std::endl;
|
||||||
}
|
}
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -604,6 +614,7 @@ void Record<ESM::Container>::print()
|
||||||
for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit)
|
for (cit = mData.mInventory.mList.begin(); cit != mData.mInventory.mList.end(); ++cit)
|
||||||
std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount
|
std::cout << " Inventory: Count: " << boost::format("%4d") % cit->mCount
|
||||||
<< " Item: " << cit->mItem.toString() << std::endl;
|
<< " Item: " << cit->mItem.toString() << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -670,6 +681,7 @@ void Record<ESM::Creature>::print()
|
||||||
std::vector<ESM::AIPackage>::iterator pit;
|
std::vector<ESM::AIPackage>::iterator pit;
|
||||||
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
|
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
|
||||||
printAIPackage(*pit);
|
printAIPackage(*pit);
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -677,6 +689,7 @@ void Record<ESM::Dialogue>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Type: " << dialogTypeLabel(mData.mType)
|
std::cout << " Type: " << dialogTypeLabel(mData.mType)
|
||||||
<< " (" << (int)mData.mType << ")" << std::endl;
|
<< " (" << (int)mData.mType << ")" << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
// Sadly, there are no DialInfos, because the loader dumps as it
|
// Sadly, there are no DialInfos, because the loader dumps as it
|
||||||
// loads, rather than loading and then dumping. :-( Anyone mind if
|
// loads, rather than loading and then dumping. :-( Anyone mind if
|
||||||
// I change this?
|
// I change this?
|
||||||
|
@ -693,6 +706,7 @@ void Record<ESM::Door>::print()
|
||||||
std::cout << " Script: " << mData.mScript << std::endl;
|
std::cout << " Script: " << mData.mScript << std::endl;
|
||||||
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
|
std::cout << " OpenSound: " << mData.mOpenSound << std::endl;
|
||||||
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
|
std::cout << " CloseSound: " << mData.mCloseSound << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -704,6 +718,7 @@ void Record<ESM::Enchantment>::print()
|
||||||
std::cout << " Charge: " << mData.mData.mCharge << std::endl;
|
std::cout << " Charge: " << mData.mData.mCharge << std::endl;
|
||||||
std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl;
|
std::cout << " AutoCalc: " << mData.mData.mAutocalc << std::endl;
|
||||||
printEffectList(mData.mEffects);
|
printEffectList(mData.mEffects);
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -737,12 +752,14 @@ void Record<ESM::Faction>::print()
|
||||||
std::map<std::string, int>::iterator rit;
|
std::map<std::string, int>::iterator rit;
|
||||||
for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit)
|
for (rit = mData.mReactions.begin(); rit != mData.mReactions.end(); ++rit)
|
||||||
std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl;
|
std::cout << " Reaction: " << rit->second << " = " << rit->first << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void Record<ESM::Global>::print()
|
void Record<ESM::Global>::print()
|
||||||
{
|
{
|
||||||
std::cout << " " << mData.mValue << std::endl;
|
std::cout << " " << mData.mValue << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -809,6 +826,7 @@ void Record<ESM::DialInfo>::print()
|
||||||
std::cout << " Result Script: [skipped]" << std::endl;
|
std::cout << " Result Script: [skipped]" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -832,6 +850,7 @@ void Record<ESM::Ingredient>::print()
|
||||||
std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i])
|
std::cout << " Attribute: " << attributeLabel(mData.mData.mAttributes[i])
|
||||||
<< " (" << mData.mData.mAttributes[i] << ")" << std::endl;
|
<< " (" << mData.mData.mAttributes[i] << ")" << std::endl;
|
||||||
}
|
}
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -848,6 +867,8 @@ void Record<ESM::Land>::print()
|
||||||
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
|
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
|
||||||
std::cout << " Unknown2: " << data->mUnk2 << std::endl;
|
std::cout << " Unknown2: " << data->mUnk2 << std::endl;
|
||||||
}
|
}
|
||||||
|
mData.unloadData();
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -860,6 +881,7 @@ void Record<ESM::CreatureLevList>::print()
|
||||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
|
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
|
||||||
std::cout << " Creature: Level: " << iit->mLevel
|
std::cout << " Creature: Level: " << iit->mLevel
|
||||||
<< " Creature: " << iit->mId << std::endl;
|
<< " Creature: " << iit->mId << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -872,6 +894,7 @@ void Record<ESM::ItemLevList>::print()
|
||||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
|
for (iit = mData.mList.begin(); iit != mData.mList.end(); ++iit)
|
||||||
std::cout << " Inventory: Level: " << iit->mLevel
|
std::cout << " Inventory: Level: " << iit->mLevel
|
||||||
<< " Item: " << iit->mId << std::endl;
|
<< " Item: " << iit->mId << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -892,6 +915,7 @@ void Record<ESM::Light>::print()
|
||||||
std::cout << " Duration: " << mData.mData.mTime << std::endl;
|
std::cout << " Duration: " << mData.mData.mTime << std::endl;
|
||||||
std::cout << " Radius: " << mData.mData.mRadius << std::endl;
|
std::cout << " Radius: " << mData.mData.mRadius << std::endl;
|
||||||
std::cout << " Color: " << mData.mData.mColor << std::endl;
|
std::cout << " Color: " << mData.mData.mColor << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -906,6 +930,7 @@ void Record<ESM::Lockpick>::print()
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
||||||
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -920,6 +945,7 @@ void Record<ESM::Probe>::print()
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
||||||
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -934,6 +960,7 @@ void Record<ESM::Repair>::print()
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
std::cout << " Quality: " << mData.mData.mQuality << std::endl;
|
||||||
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
std::cout << " Uses: " << mData.mData.mUses << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -942,6 +969,7 @@ void Record<ESM::LandTexture>::print()
|
||||||
std::cout << " Id: " << mData.mId << std::endl;
|
std::cout << " Id: " << mData.mId << std::endl;
|
||||||
std::cout << " Index: " << mData.mIndex << std::endl;
|
std::cout << " Index: " << mData.mIndex << std::endl;
|
||||||
std::cout << " Texture: " << mData.mTexture << std::endl;
|
std::cout << " Texture: " << mData.mTexture << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -992,6 +1020,7 @@ void Record<ESM::Miscellaneous>::print()
|
||||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " Is Key: " << mData.mData.mIsKey << std::endl;
|
std::cout << " Is Key: " << mData.mData.mIsKey << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1077,6 +1106,8 @@ void Record<ESM::NPC>::print()
|
||||||
std::vector<ESM::AIPackage>::iterator pit;
|
std::vector<ESM::AIPackage>::iterator pit;
|
||||||
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
|
for (pit = mData.mAiPackage.mList.begin(); pit != mData.mAiPackage.mList.end(); ++pit)
|
||||||
printAIPackage(*pit);
|
printAIPackage(*pit);
|
||||||
|
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1111,6 +1142,8 @@ void Record<ESM::Pathgrid>::print()
|
||||||
std::cout << " BAD POINT IN EDGE!" << std::endl;
|
std::cout << " BAD POINT IN EDGE!" << std::endl;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1151,6 +1184,8 @@ void Record<ESM::Race>::print()
|
||||||
std::vector<std::string>::iterator sit;
|
std::vector<std::string>::iterator sit;
|
||||||
for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit)
|
for (sit = mData.mPowers.mList.begin(); sit != mData.mPowers.mList.end(); ++sit)
|
||||||
std::cout << " Power: " << *sit << std::endl;
|
std::cout << " Power: " << *sit << std::endl;
|
||||||
|
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1210,6 +1245,8 @@ void Record<ESM::Script>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Script: [skipped]" << std::endl;
|
std::cout << " Script: [skipped]" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1233,6 +1270,7 @@ void Record<ESM::SoundGenerator>::print()
|
||||||
std::cout << " Sound: " << mData.mSound << std::endl;
|
std::cout << " Sound: " << mData.mSound << std::endl;
|
||||||
std::cout << " Type: " << soundTypeLabel(mData.mType)
|
std::cout << " Type: " << soundTypeLabel(mData.mType)
|
||||||
<< " (" << mData.mType << ")" << std::endl;
|
<< " (" << mData.mType << ")" << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1243,6 +1281,7 @@ void Record<ESM::Sound>::print()
|
||||||
if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0)
|
if (mData.mData.mMinRange != 0 && mData.mData.mMaxRange != 0)
|
||||||
std::cout << " Range: " << (int)mData.mData.mMinRange << " - "
|
std::cout << " Range: " << (int)mData.mData.mMinRange << " - "
|
||||||
<< (int)mData.mData.mMaxRange << std::endl;
|
<< (int)mData.mData.mMaxRange << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1254,13 +1293,15 @@ void Record<ESM::Spell>::print()
|
||||||
std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl;
|
std::cout << " Flags: " << spellFlags(mData.mData.mFlags) << std::endl;
|
||||||
std::cout << " Cost: " << mData.mData.mCost << std::endl;
|
std::cout << " Cost: " << mData.mData.mCost << std::endl;
|
||||||
printEffectList(mData.mEffects);
|
printEffectList(mData.mEffects);
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void Record<ESM::StartScript>::print()
|
void Record<ESM::StartScript>::print()
|
||||||
{
|
{
|
||||||
std::cout << "Start Script: " << mData.mId << std::endl;
|
std::cout << " Start Script: " << mData.mId << std::endl;
|
||||||
std::cout << "Start Data: " << mData.mData << std::endl;
|
std::cout << " Start Data: " << mData.mData << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -1301,6 +1342,37 @@ void Record<ESM::Weapon>::print()
|
||||||
if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0)
|
if (mData.mData.mThrust[0] != 0 && mData.mData.mThrust[1] != 0)
|
||||||
std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-"
|
std::cout << " Thrust: " << (int)mData.mData.mThrust[0] << "-"
|
||||||
<< (int)mData.mData.mThrust[1] << std::endl;
|
<< (int)mData.mData.mThrust[1] << std::endl;
|
||||||
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string Record<ESM::Cell>::getId() const
|
||||||
|
{
|
||||||
|
return mData.mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string Record<ESM::Land>::getId() const
|
||||||
|
{
|
||||||
|
return ""; // No ID for Land record
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string Record<ESM::MagicEffect>::getId() const
|
||||||
|
{
|
||||||
|
return ""; // No ID for MagicEffect record
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string Record<ESM::Pathgrid>::getId() const
|
||||||
|
{
|
||||||
|
return ""; // No ID for Pathgrid record
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
std::string Record<ESM::Skill>::getId() const
|
||||||
|
{
|
||||||
|
return ""; // No ID for Skill record
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
|
@ -32,13 +32,7 @@ namespace EsmTool
|
||||||
|
|
||||||
virtual ~RecordBase() {}
|
virtual ~RecordBase() {}
|
||||||
|
|
||||||
const std::string &getId() const {
|
virtual std::string getId() const = 0;
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setId(const std::string &id) {
|
|
||||||
mId = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t getFlags() const {
|
uint32_t getFlags() const {
|
||||||
return mFlags;
|
return mFlags;
|
||||||
|
@ -73,23 +67,38 @@ namespace EsmTool
|
||||||
class Record : public RecordBase
|
class Record : public RecordBase
|
||||||
{
|
{
|
||||||
T mData;
|
T mData;
|
||||||
|
bool mIsDeleted;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Record()
|
||||||
|
: mIsDeleted(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string getId() const {
|
||||||
|
return mData.mId;
|
||||||
|
}
|
||||||
|
|
||||||
T &get() {
|
T &get() {
|
||||||
return mData;
|
return mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void save(ESM::ESMWriter &esm) {
|
void save(ESM::ESMWriter &esm) {
|
||||||
mData.save(esm);
|
mData.save(esm, mIsDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm) {
|
void load(ESM::ESMReader &esm) {
|
||||||
mData.load(esm);
|
mData.load(esm, mIsDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print();
|
void print();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<> std::string Record<ESM::Cell>::getId() const;
|
||||||
|
template<> std::string Record<ESM::Land>::getId() const;
|
||||||
|
template<> std::string Record<ESM::MagicEffect>::getId() const;
|
||||||
|
template<> std::string Record<ESM::Pathgrid>::getId() const;
|
||||||
|
template<> std::string Record<ESM::Skill>::getId() const;
|
||||||
|
|
||||||
template<> void Record<ESM::Activator>::print();
|
template<> void Record<ESM::Activator>::print();
|
||||||
template<> void Record<ESM::Potion>::print();
|
template<> void Record<ESM::Potion>::print();
|
||||||
template<> void Record<ESM::Armor>::print();
|
template<> void Record<ESM::Armor>::print();
|
||||||
|
|
|
@ -158,9 +158,9 @@ namespace ESSImport
|
||||||
void ConvertCell::read(ESM::ESMReader &esm)
|
void ConvertCell::read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
std::string id = esm.getHNString("NAME");
|
bool isDeleted = false;
|
||||||
cell.mName = id;
|
|
||||||
cell.load(esm, false);
|
cell.load(esm, isDeleted, false);
|
||||||
|
|
||||||
// I wonder what 0x40 does?
|
// I wonder what 0x40 does?
|
||||||
if (cell.isExterior() && cell.mData.mFlags & 0x20)
|
if (cell.isExterior() && cell.mData.mFlags & 0x20)
|
||||||
|
@ -169,7 +169,7 @@ namespace ESSImport
|
||||||
}
|
}
|
||||||
|
|
||||||
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
|
// note if the player is in a nameless exterior cell, we will assign the cellId later based on player position
|
||||||
if (id == mContext->mPlayerCellName)
|
if (cell.mName == mContext->mPlayerCellName)
|
||||||
{
|
{
|
||||||
mContext->mPlayer.mCellId = cell.getCellId();
|
mContext->mPlayer.mCellId = cell.getCellId();
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ namespace ESSImport
|
||||||
if (cell.isExterior())
|
if (cell.isExterior())
|
||||||
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
|
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
|
||||||
else
|
else
|
||||||
mIntCells[id] = newcell;
|
mIntCells[cell.mName] = newcell;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)
|
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)
|
||||||
|
|
|
@ -54,6 +54,8 @@ public:
|
||||||
|
|
||||||
void setContext(Context& context) { mContext = &context; }
|
void setContext(Context& context) { mContext = &context; }
|
||||||
|
|
||||||
|
/// @note The load method of ESM records accept the deleted flag as a parameter.
|
||||||
|
/// I don't know can the DELE sub-record appear in saved games, so the deleted flag will be ignored.
|
||||||
virtual void read(ESM::ESMReader& esm)
|
virtual void read(ESM::ESMReader& esm)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -78,10 +80,11 @@ public:
|
||||||
|
|
||||||
virtual void read(ESM::ESMReader& esm)
|
virtual void read(ESM::ESMReader& esm)
|
||||||
{
|
{
|
||||||
std::string id = esm.getHNString("NAME");
|
|
||||||
T record;
|
T record;
|
||||||
record.load(esm);
|
bool isDeleted = false;
|
||||||
mRecords[id] = record;
|
|
||||||
|
record.load(esm, isDeleted);
|
||||||
|
mRecords[record.mId] = record;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void write(ESM::ESMWriter& esm)
|
virtual void write(ESM::ESMWriter& esm)
|
||||||
|
@ -89,7 +92,6 @@ public:
|
||||||
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
|
||||||
{
|
{
|
||||||
esm.startRecord(T::sRecordId);
|
esm.startRecord(T::sRecordId);
|
||||||
esm.writeHNString("NAME", it->first);
|
|
||||||
it->second.save(esm);
|
it->second.save(esm);
|
||||||
esm.endRecord(T::sRecordId);
|
esm.endRecord(T::sRecordId);
|
||||||
}
|
}
|
||||||
|
@ -105,14 +107,15 @@ public:
|
||||||
virtual void read(ESM::ESMReader &esm)
|
virtual void read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
ESM::NPC npc;
|
ESM::NPC npc;
|
||||||
std::string id = esm.getHNString("NAME");
|
bool isDeleted = false;
|
||||||
npc.load(esm);
|
|
||||||
if (id != "player")
|
npc.load(esm, isDeleted);
|
||||||
|
if (npc.mId != "player")
|
||||||
{
|
{
|
||||||
// Handles changes to the NPC struct, but since there is no index here
|
// Handles changes to the NPC struct, but since there is no index here
|
||||||
// it will apply to ALL instances of the class. seems to be the reason for the
|
// it will apply to ALL instances of the class. seems to be the reason for the
|
||||||
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
|
// "feature" in MW where changing AI settings of one guard will change it for all guards of that refID.
|
||||||
mContext->mNpcs[Misc::StringUtils::lowerCase(id)] = npc;
|
mContext->mNpcs[Misc::StringUtils::lowerCase(npc.mId)] = npc;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -142,9 +145,10 @@ public:
|
||||||
{
|
{
|
||||||
// See comment in ConvertNPC
|
// See comment in ConvertNPC
|
||||||
ESM::Creature creature;
|
ESM::Creature creature;
|
||||||
std::string id = esm.getHNString("NAME");
|
bool isDeleted = false;
|
||||||
creature.load(esm);
|
|
||||||
mContext->mCreatures[Misc::StringUtils::lowerCase(id)] = creature;
|
creature.load(esm, isDeleted);
|
||||||
|
mContext->mCreatures[Misc::StringUtils::lowerCase(creature.mId)] = creature;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,18 +161,19 @@ class ConvertGlobal : public DefaultConverter<ESM::Global>
|
||||||
public:
|
public:
|
||||||
virtual void read(ESM::ESMReader &esm)
|
virtual void read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
std::string id = esm.getHNString("NAME");
|
|
||||||
ESM::Global global;
|
ESM::Global global;
|
||||||
global.load(esm);
|
bool isDeleted = false;
|
||||||
if (Misc::StringUtils::ciEqual(id, "gamehour"))
|
|
||||||
|
global.load(esm, isDeleted);
|
||||||
|
if (Misc::StringUtils::ciEqual(global.mId, "gamehour"))
|
||||||
mContext->mHour = global.mValue.getFloat();
|
mContext->mHour = global.mValue.getFloat();
|
||||||
if (Misc::StringUtils::ciEqual(id, "day"))
|
if (Misc::StringUtils::ciEqual(global.mId, "day"))
|
||||||
mContext->mDay = global.mValue.getInteger();
|
mContext->mDay = global.mValue.getInteger();
|
||||||
if (Misc::StringUtils::ciEqual(id, "month"))
|
if (Misc::StringUtils::ciEqual(global.mId, "month"))
|
||||||
mContext->mMonth = global.mValue.getInteger();
|
mContext->mMonth = global.mValue.getInteger();
|
||||||
if (Misc::StringUtils::ciEqual(id, "year"))
|
if (Misc::StringUtils::ciEqual(global.mId, "year"))
|
||||||
mContext->mYear = global.mValue.getInteger();
|
mContext->mYear = global.mValue.getInteger();
|
||||||
mRecords[id] = global;
|
mRecords[global.mId] = global;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,14 +182,14 @@ class ConvertClass : public DefaultConverter<ESM::Class>
|
||||||
public:
|
public:
|
||||||
virtual void read(ESM::ESMReader &esm)
|
virtual void read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
std::string id = esm.getHNString("NAME");
|
|
||||||
ESM::Class class_;
|
ESM::Class class_;
|
||||||
class_.load(esm);
|
bool isDeleted = false;
|
||||||
|
|
||||||
if (id == "NEWCLASSID_CHARGEN")
|
class_.load(esm, isDeleted);
|
||||||
|
if (class_.mId == "NEWCLASSID_CHARGEN")
|
||||||
mContext->mCustomPlayerClassName = class_.mName;
|
mContext->mCustomPlayerClassName = class_.mName;
|
||||||
|
|
||||||
mRecords[id] = class_;
|
mRecords[class_.mId] = class_;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,13 +198,14 @@ class ConvertBook : public DefaultConverter<ESM::Book>
|
||||||
public:
|
public:
|
||||||
virtual void read(ESM::ESMReader &esm)
|
virtual void read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
std::string id = esm.getHNString("NAME");
|
|
||||||
ESM::Book book;
|
ESM::Book book;
|
||||||
book.load(esm);
|
bool isDeleted = false;
|
||||||
if (book.mData.mSkillID == -1)
|
|
||||||
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(id));
|
|
||||||
|
|
||||||
mRecords[id] = book;
|
book.load(esm, isDeleted);
|
||||||
|
if (book.mData.mSkillID == -1)
|
||||||
|
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
|
||||||
|
|
||||||
|
mRecords[book.mId] = book;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -371,11 +377,12 @@ class ConvertFACT : public Converter
|
||||||
public:
|
public:
|
||||||
virtual void read(ESM::ESMReader& esm)
|
virtual void read(ESM::ESMReader& esm)
|
||||||
{
|
{
|
||||||
std::string id = esm.getHNString("NAME");
|
|
||||||
ESM::Faction faction;
|
ESM::Faction faction;
|
||||||
faction.load(esm);
|
bool isDeleted = false;
|
||||||
|
|
||||||
|
faction.load(esm, isDeleted);
|
||||||
|
std::string id = Misc::StringUtils::toLower(faction.mId);
|
||||||
|
|
||||||
Misc::StringUtils::toLower(id);
|
|
||||||
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
|
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
|
||||||
{
|
{
|
||||||
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
|
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
|
||||||
|
|
|
@ -32,7 +32,8 @@ namespace ESSImport
|
||||||
if (esm.isNextSub("MNAM"))
|
if (esm.isNextSub("MNAM"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
ESM::CellRef::loadData(esm);
|
bool isDeleted = false;
|
||||||
|
ESM::CellRef::loadData(esm, isDeleted);
|
||||||
|
|
||||||
mHasACDT = false;
|
mHasACDT = false;
|
||||||
if (esm.isNextSub("ACDT"))
|
if (esm.isNextSub("ACDT"))
|
||||||
|
|
|
@ -394,7 +394,7 @@ namespace ESSImport
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.startRecord(ESM::REC_NPC_);
|
writer.startRecord(ESM::REC_NPC_);
|
||||||
writer.writeHNString("NAME", "player");
|
context.mPlayerBase.mId = "player";
|
||||||
context.mPlayerBase.save(writer);
|
context.mPlayerBase.save(writer);
|
||||||
writer.endRecord(ESM::REC_NPC_);
|
writer.endRecord(ESM::REC_NPC_);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,8 @@ namespace ESSImport
|
||||||
item.mSCRI.load(esm);
|
item.mSCRI.load(esm);
|
||||||
|
|
||||||
// for XSOL and XCHG seen so far, but probably others too
|
// for XSOL and XCHG seen so far, but probably others too
|
||||||
item.ESM::CellRef::loadData(esm);
|
bool isDeleted = false;
|
||||||
|
item.ESM::CellRef::loadData(esm, isDeleted);
|
||||||
|
|
||||||
int charge=-1;
|
int charge=-1;
|
||||||
esm.getHNOT(charge, "XHLT");
|
esm.getHNOT(charge, "XHLT");
|
||||||
|
|
|
@ -99,95 +99,79 @@ int CSMDoc::WriteDialogueCollectionStage::setup()
|
||||||
|
|
||||||
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages)
|
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& messages)
|
||||||
{
|
{
|
||||||
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
|
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
|
||||||
|
|
||||||
CSMWorld::RecordBase::State state = topic.mState;
|
if (topic.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
|
|
||||||
if (state==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
{
|
||||||
// if the topic is deleted, we do not need to bother with INFO records.
|
// if the topic is deleted, we do not need to bother with INFO records.
|
||||||
|
ESM::Dialogue dialogue = topic.get();
|
||||||
/// \todo wrote record with delete flag
|
writer.startRecord(dialogue.sRecordId);
|
||||||
|
dialogue.save(writer, true);
|
||||||
|
writer.endRecord(dialogue.sRecordId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test, if we need to save anything associated info records.
|
// Test, if we need to save anything associated info records.
|
||||||
bool infoModified = false;
|
bool infoModified = false;
|
||||||
|
|
||||||
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
|
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
|
||||||
|
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
{
|
{
|
||||||
CSMWorld::RecordBase::State state = iter->mState;
|
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
|
|
||||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
|
||||||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
|
|
||||||
state==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
{
|
||||||
infoModified = true;
|
infoModified = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
if (topic.isModified() || infoModified)
|
||||||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
|
|
||||||
infoModified)
|
|
||||||
{
|
{
|
||||||
if (infoModified && state != CSMWorld::RecordBase::State_Modified
|
if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified
|
||||||
&& state != CSMWorld::RecordBase::State_ModifiedOnly)
|
&& topic.mState != CSMWorld::RecordBase::State_ModifiedOnly)
|
||||||
{
|
{
|
||||||
mState.getWriter().startRecord (topic.mBase.sRecordId);
|
mState.getWriter().startRecord (topic.mBase.sRecordId);
|
||||||
mState.getWriter().writeHNCString ("NAME", topic.mBase.mId);
|
mState.getWriter().writeHNCString ("NAME", topic.mBase.mId);
|
||||||
topic.mBase.save (mState.getWriter());
|
topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
mState.getWriter().endRecord (topic.mBase.sRecordId);
|
mState.getWriter().endRecord (topic.mBase.sRecordId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mState.getWriter().startRecord (topic.mModified.sRecordId);
|
mState.getWriter().startRecord (topic.mModified.sRecordId);
|
||||||
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
|
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
|
||||||
topic.mModified.save (mState.getWriter());
|
topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
mState.getWriter().endRecord (topic.mModified.sRecordId);
|
mState.getWriter().endRecord (topic.mModified.sRecordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write modified selected info records
|
// write modified selected info records
|
||||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
|
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||||
++iter)
|
|
||||||
{
|
{
|
||||||
CSMWorld::RecordBase::State infoState = iter->mState;
|
if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
|
|
||||||
if (infoState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo wrote record with delete flag
|
|
||||||
}
|
|
||||||
else if (infoState==CSMWorld::RecordBase::State_Modified ||
|
|
||||||
infoState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
ESM::DialInfo info = iter->get();
|
ESM::DialInfo info = iter->get();
|
||||||
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
||||||
|
|
||||||
|
info.mPrev = "";
|
||||||
if (iter!=range.first)
|
if (iter!=range.first)
|
||||||
{
|
{
|
||||||
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
||||||
--prev;
|
--prev;
|
||||||
|
|
||||||
info.mPrev =
|
info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1);
|
||||||
prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
||||||
++next;
|
++next;
|
||||||
|
|
||||||
|
info.mNext = "";
|
||||||
if (next!=range.second)
|
if (next!=range.second)
|
||||||
{
|
{
|
||||||
info.mNext =
|
info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1);
|
||||||
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mState.getWriter().startRecord (info.sRecordId);
|
writer.startRecord (info.sRecordId);
|
||||||
mState.getWriter().writeHNCString ("INAM", info.mId);
|
info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
info.save (mState.getWriter());
|
writer.endRecord (info.sRecordId);
|
||||||
mState.getWriter().endRecord (info.sRecordId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,9 +219,7 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages)
|
||||||
const CSMWorld::Record<CSMWorld::CellRef>& record =
|
const CSMWorld::Record<CSMWorld::CellRef>& record =
|
||||||
mDocument.getData().getReferences().getRecord (i);
|
mDocument.getData().getReferences().getRecord (i);
|
||||||
|
|
||||||
if (record.mState==CSMWorld::RecordBase::State_Deleted ||
|
if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
record.mState==CSMWorld::RecordBase::State_Modified ||
|
|
||||||
record.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
std::string cellId = record.get().mOriginalCell.empty() ?
|
std::string cellId = record.get().mOriginalCell.empty() ?
|
||||||
record.get().mCell : record.get().mOriginalCell;
|
record.get().mCell : record.get().mOriginalCell;
|
||||||
|
@ -279,36 +261,34 @@ int CSMDoc::WriteCellCollectionStage::setup()
|
||||||
|
|
||||||
void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
||||||
{
|
{
|
||||||
const CSMWorld::Record<CSMWorld::Cell>& cell =
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
mDocument.getData().getCells().getRecord (stage);
|
const CSMWorld::Record<CSMWorld::Cell>& cell = mDocument.getData().getCells().getRecord (stage);
|
||||||
|
|
||||||
std::map<std::string, std::deque<int> >::const_iterator references =
|
std::map<std::string, std::deque<int> >::const_iterator references =
|
||||||
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
|
mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId));
|
||||||
|
|
||||||
if (cell.mState==CSMWorld::RecordBase::State_Modified ||
|
if (cell.isModified() ||
|
||||||
cell.mState==CSMWorld::RecordBase::State_ModifiedOnly ||
|
cell.mState == CSMWorld::RecordBase::State_Deleted ||
|
||||||
references!=mState.getSubRecords().end())
|
references!=mState.getSubRecords().end())
|
||||||
{
|
{
|
||||||
bool interior = cell.get().mId.substr (0, 1)!="#";
|
CSMWorld::Cell cellRecord = cell.get();
|
||||||
|
bool interior = cellRecord.mId.substr (0, 1)!="#";
|
||||||
|
|
||||||
// write cell data
|
// write cell data
|
||||||
mState.getWriter().startRecord (cell.mModified.sRecordId);
|
writer.startRecord (cellRecord.sRecordId);
|
||||||
|
|
||||||
mState.getWriter().writeHNOCString ("NAME", cell.get().mName);
|
|
||||||
|
|
||||||
ESM::Cell cell2 = cell.get();
|
|
||||||
|
|
||||||
if (interior)
|
if (interior)
|
||||||
cell2.mData.mFlags |= ESM::Cell::Interior;
|
cellRecord.mData.mFlags |= ESM::Cell::Interior;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell2.mData.mFlags &= ~ESM::Cell::Interior;
|
cellRecord.mData.mFlags &= ~ESM::Cell::Interior;
|
||||||
|
|
||||||
std::istringstream stream (cell.get().mId.c_str());
|
std::istringstream stream (cellRecord.mId.c_str());
|
||||||
char ignore;
|
char ignore;
|
||||||
stream >> ignore >> cell2.mData.mX >> cell2.mData.mY;
|
stream >> ignore >> cellRecord.mData.mX >> cellRecord.mData.mY;
|
||||||
}
|
}
|
||||||
cell2.save (mState.getWriter());
|
|
||||||
|
cellRecord.save (writer, cell.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
|
|
||||||
// write references
|
// write references
|
||||||
if (references!=mState.getSubRecords().end())
|
if (references!=mState.getSubRecords().end())
|
||||||
|
@ -319,24 +299,25 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
||||||
const CSMWorld::Record<CSMWorld::CellRef>& ref =
|
const CSMWorld::Record<CSMWorld::CellRef>& ref =
|
||||||
mDocument.getData().getReferences().getRecord (*iter);
|
mDocument.getData().getReferences().getRecord (*iter);
|
||||||
|
|
||||||
if (ref.mState==CSMWorld::RecordBase::State_Modified ||
|
if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
ref.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
|
CSMWorld::CellRef refRecord = ref.get();
|
||||||
|
|
||||||
// recalculate the ref's cell location
|
// recalculate the ref's cell location
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
if (!interior)
|
if (!interior)
|
||||||
{
|
{
|
||||||
std::pair<int, int> index = ref.get().getCellIndex();
|
std::pair<int, int> index = refRecord.getCellIndex();
|
||||||
stream << "#" << index.first << " " << index.second;
|
stream << "#" << index.first << " " << index.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// An empty mOriginalCell is meant to indicate that it is the same as
|
// An empty mOriginalCell is meant to indicate that it is the same as
|
||||||
// the current cell. It is possible that a moved ref is moved again.
|
// the current cell. It is possible that a moved ref is moved again.
|
||||||
if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell)
|
if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell)
|
||||||
!= stream.str() && !interior)
|
!= stream.str() && !interior)
|
||||||
{
|
{
|
||||||
ESM::MovedCellRef moved;
|
ESM::MovedCellRef moved;
|
||||||
moved.mRefNum = ref.get().mRefNum;
|
moved.mRefNum = refRecord.mRefNum;
|
||||||
|
|
||||||
// Need to fill mTarget with the ref's new position.
|
// Need to fill mTarget with the ref's new position.
|
||||||
std::istringstream istream (stream.str().c_str());
|
std::istringstream istream (stream.str().c_str());
|
||||||
|
@ -344,24 +325,16 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages)
|
||||||
char ignore;
|
char ignore;
|
||||||
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
|
istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1];
|
||||||
|
|
||||||
ref.get().mRefNum.save (mState.getWriter(), false, "MVRF");
|
refRecord.mRefNum.save (writer, false, "MVRF");
|
||||||
mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8);
|
writer.writeHNT ("CNDT", moved.mTarget, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.get().save (mState.getWriter());
|
refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
}
|
|
||||||
else if (ref.mState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mState.getWriter().endRecord (cell.mModified.sRecordId);
|
writer.endRecord (cellRecord.sRecordId);
|
||||||
}
|
|
||||||
else if (cell.mState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,11 +351,11 @@ int CSMDoc::WritePathgridCollectionStage::setup()
|
||||||
|
|
||||||
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
|
void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& messages)
|
||||||
{
|
{
|
||||||
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
|
const CSMWorld::Record<CSMWorld::Pathgrid>& pathgrid =
|
||||||
mDocument.getData().getPathgrids().getRecord (stage);
|
mDocument.getData().getPathgrids().getRecord (stage);
|
||||||
|
|
||||||
if (pathgrid.mState==CSMWorld::RecordBase::State_Modified ||
|
if (pathgrid.isModified() || pathgrid.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
pathgrid.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
CSMWorld::Pathgrid record = pathgrid.get();
|
CSMWorld::Pathgrid record = pathgrid.get();
|
||||||
|
|
||||||
|
@ -395,15 +368,9 @@ void CSMDoc::WritePathgridCollectionStage::perform (int stage, Messages& message
|
||||||
else
|
else
|
||||||
record.mCell = record.mId;
|
record.mCell = record.mId;
|
||||||
|
|
||||||
mState.getWriter().startRecord (record.sRecordId);
|
writer.startRecord (record.sRecordId);
|
||||||
|
record.save (writer, pathgrid.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
record.save (mState.getWriter());
|
writer.endRecord (record.sRecordId);
|
||||||
|
|
||||||
mState.getWriter().endRecord (record.sRecordId);
|
|
||||||
}
|
|
||||||
else if (pathgrid.mState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,26 +387,20 @@ int CSMDoc::WriteLandCollectionStage::setup()
|
||||||
|
|
||||||
void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
|
void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages)
|
||||||
{
|
{
|
||||||
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
const CSMWorld::Record<CSMWorld::Land>& land =
|
const CSMWorld::Record<CSMWorld::Land>& land =
|
||||||
mDocument.getData().getLand().getRecord (stage);
|
mDocument.getData().getLand().getRecord (stage);
|
||||||
|
|
||||||
if (land.mState==CSMWorld::RecordBase::State_Modified ||
|
if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
land.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
const CSMWorld::Land& record = land.get();
|
CSMWorld::Land record = land.get();
|
||||||
|
writer.startRecord (record.sRecordId);
|
||||||
mState.getWriter().startRecord (record.sRecordId);
|
record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
|
|
||||||
record.save (mState.getWriter());
|
|
||||||
|
|
||||||
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
|
if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes))
|
||||||
data->save (mState.getWriter());
|
data->save (mState.getWriter());
|
||||||
|
|
||||||
mState.getWriter().endRecord (record.sRecordId);
|
writer.endRecord (record.sRecordId);
|
||||||
}
|
|
||||||
else if (land.mState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,25 +417,16 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
|
||||||
|
|
||||||
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
|
void CSMDoc::WriteLandTextureCollectionStage::perform (int stage, Messages& messages)
|
||||||
{
|
{
|
||||||
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
|
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture =
|
||||||
mDocument.getData().getLandTextures().getRecord (stage);
|
mDocument.getData().getLandTextures().getRecord (stage);
|
||||||
|
|
||||||
if (landTexture.mState==CSMWorld::RecordBase::State_Modified ||
|
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
landTexture.mState==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
CSMWorld::LandTexture record = landTexture.get();
|
CSMWorld::LandTexture record = landTexture.get();
|
||||||
|
writer.startRecord (record.sRecordId);
|
||||||
mState.getWriter().startRecord (record.sRecordId);
|
record.save (writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
|
writer.endRecord (record.sRecordId);
|
||||||
mState.getWriter().writeHNString("NAME", record.mId);
|
|
||||||
|
|
||||||
record.save (mState.getWriter());
|
|
||||||
|
|
||||||
mState.getWriter().endRecord (record.sRecordId);
|
|
||||||
}
|
|
||||||
else if (landTexture.mState==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,26 +100,17 @@ namespace CSMDoc
|
||||||
if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope)
|
if (CSMWorld::getScopeFromId (mCollection.getRecord (stage).get().mId)!=mScope)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
ESM::ESMWriter& writer = mState.getWriter();
|
||||||
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
|
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
|
||||||
|
typename CollectionT::ESXRecord record = mCollection.getRecord (stage).get();
|
||||||
|
|
||||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
if (state == CSMWorld::RecordBase::State_Modified ||
|
||||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
state == CSMWorld::RecordBase::State_ModifiedOnly ||
|
||||||
|
state == CSMWorld::RecordBase::State_Deleted)
|
||||||
{
|
{
|
||||||
// FIXME: A quick Workaround to support records which should not write
|
writer.startRecord (record.sRecordId);
|
||||||
// NAME, including SKIL, MGEF and SCPT. If there are many more
|
record.save (writer, state == CSMWorld::RecordBase::State_Deleted);
|
||||||
// idcollection records that doesn't use NAME then a more generic
|
writer.endRecord (record.sRecordId);
|
||||||
// solution may be required.
|
|
||||||
uint32_t name = mCollection.getRecord (stage).mModified.sRecordId;
|
|
||||||
mState.getWriter().startRecord (name);
|
|
||||||
|
|
||||||
if(name != ESM::REC_SKIL && name != ESM::REC_MGEF && name != ESM::REC_SCPT)
|
|
||||||
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
|
|
||||||
mCollection.getRecord (stage).mModified.save (mState.getWriter());
|
|
||||||
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
|
|
||||||
}
|
|
||||||
else if (state==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,15 @@
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
void CSMWorld::Cell::load (ESM::ESMReader &esm)
|
void CSMWorld::Cell::load (ESM::ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
mName = mId;
|
ESM::Cell::load (esm, isDeleted, false);
|
||||||
|
|
||||||
ESM::Cell::load (esm, false);
|
mId = mName;
|
||||||
|
if (isExterior())
|
||||||
if (!(mData.mFlags & Interior))
|
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
|
|
||||||
stream << "#" << mData.mX << " " << mData.mY;
|
stream << "#" << mData.mX << " " << mData.mY;
|
||||||
|
|
||||||
mId = stream.str();
|
mId = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
std::string mId;
|
std::string mId;
|
||||||
|
|
||||||
void load (ESM::ESMReader &esm);
|
void load (ESM::ESMReader &esm, bool &isDeleted);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,12 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
||||||
class Collection : public CollectionBase
|
class Collection : public CollectionBase
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef ESXRecordT ESXRecord;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
std::vector<Record<ESXRecordT> > mRecords;
|
std::vector<Record<ESXRecordT> > mRecords;
|
||||||
std::map<std::string, int> mIndex;
|
std::map<std::string, int> mIndex;
|
||||||
std::vector<Column<ESXRecordT> *> mColumns;
|
std::vector<Column<ESXRecordT> *> mColumns;
|
||||||
|
|
|
@ -1010,41 +1010,43 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages)
|
||||||
|
|
||||||
case ESM::REC_DIAL:
|
case ESM::REC_DIAL:
|
||||||
{
|
{
|
||||||
std::string id = mReader->getHNOString ("NAME");
|
|
||||||
|
|
||||||
ESM::Dialogue record;
|
ESM::Dialogue record;
|
||||||
record.mId = id;
|
bool isDeleted = false;
|
||||||
record.load (*mReader);
|
|
||||||
|
|
||||||
if (record.mType==ESM::Dialogue::Journal)
|
record.load (*mReader, isDeleted);
|
||||||
{
|
|
||||||
mJournals.load (record, mBase);
|
|
||||||
mDialogue = &mJournals.getRecord (id).get();
|
|
||||||
}
|
|
||||||
else if (record.mType==ESM::Dialogue::Deleted)
|
|
||||||
{
|
|
||||||
mDialogue = 0; // record vector can be shuffled around which would make pointer
|
|
||||||
// to record invalid
|
|
||||||
|
|
||||||
if (mJournals.tryDelete (id))
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
/// \todo handle info records
|
// record vector can be shuffled around which would make pointer to record invalid
|
||||||
|
mDialogue = 0;
|
||||||
|
|
||||||
|
if (mJournals.tryDelete (record.mId))
|
||||||
|
{
|
||||||
|
mJournalInfos.removeDialogueInfos(record.mId);
|
||||||
}
|
}
|
||||||
else if (mTopics.tryDelete (id))
|
else if (mTopics.tryDelete (record.mId))
|
||||||
{
|
{
|
||||||
/// \todo handle info records
|
mTopicInfos.removeDialogueInfos(record.mId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messages.add (UniversalId::Type_None,
|
messages.add (UniversalId::Type_None,
|
||||||
"Trying to delete dialogue record " + id + " which does not exist",
|
"Trying to delete dialogue record " + record.mId + " which does not exist",
|
||||||
"", CSMDoc::Message::Severity_Warning);
|
"", CSMDoc::Message::Severity_Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (record.mType == ESM::Dialogue::Journal)
|
||||||
|
{
|
||||||
|
mJournals.load (record, mBase);
|
||||||
|
mDialogue = &mJournals.getRecord (record.mId).get();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mTopics.load (record, mBase);
|
mTopics.load (record, mBase);
|
||||||
mDialogue = &mTopics.getRecord (id).get();
|
mDialogue = &mTopics.getRecord (record.mId).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace CSMWorld
|
||||||
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
||||||
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
|
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
|
||||||
{
|
{
|
||||||
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader);
|
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -33,78 +33,47 @@ namespace CSMWorld
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void IdCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
|
void IdCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
|
||||||
ESM::ESMReader& reader)
|
ESM::ESMReader& reader,
|
||||||
|
bool& isDeleted)
|
||||||
{
|
{
|
||||||
record.load (reader);
|
record.load (reader, isDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
|
int IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
|
||||||
{
|
{
|
||||||
std::string id = reader.getHNOString ("NAME");
|
ESXRecordT record;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
if (reader.isNextSub ("DELE"))
|
loadRecord (record, reader, isDeleted);
|
||||||
|
|
||||||
|
std::string id = IdAccessorT().getId (record);
|
||||||
|
int index = this->searchId (id);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
int index = Collection<ESXRecordT, IdAccessorT>::searchId (id);
|
|
||||||
|
|
||||||
reader.skipRecord();
|
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
// deleting a record that does not exist
|
// deleting a record that does not exist
|
||||||
|
|
||||||
// ignore it for now
|
// ignore it for now
|
||||||
|
|
||||||
/// \todo report the problem to the user
|
/// \todo report the problem to the user
|
||||||
}
|
|
||||||
else if (base)
|
|
||||||
{
|
|
||||||
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
|
||||||
record.mState = RecordBase::State_Deleted;
|
|
||||||
this->setRecord (index, record);
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ESXRecordT record;
|
|
||||||
|
|
||||||
// Sometimes id (i.e. NAME of the cell) may be different to the id we stored
|
if (base)
|
||||||
// earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is
|
|
||||||
// missing altogether for scripts or cells.
|
|
||||||
//
|
|
||||||
// In such cases the returned index will be -1. We then try updating the
|
|
||||||
// IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena")
|
|
||||||
// and try getting the index once more after loading the record. The mId of the
|
|
||||||
// record would have changed to "#-4 11" after the load, and searchId() should find
|
|
||||||
// it (if this is a modify)
|
|
||||||
int index = this->searchId (id);
|
|
||||||
|
|
||||||
if (index==-1)
|
|
||||||
IdAccessorT().getId (record) = id;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
record = this->getRecord (index).get();
|
this->removeRows (index, 1);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadRecord (record, reader);
|
Record<ESXRecordT> baseRecord = this->getRecord (index);
|
||||||
|
baseRecord.mState = RecordBase::State_Deleted;
|
||||||
if (index==-1)
|
this->setRecord (index, baseRecord);
|
||||||
{
|
return index;
|
||||||
std::string newId = IdAccessorT().getId(record);
|
|
||||||
int newIndex = this->searchId(newId);
|
|
||||||
if (newIndex != -1 && id != newId)
|
|
||||||
index = newIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return load (record, base, index);
|
return load (record, base, index);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
int IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base,
|
||||||
|
|
|
@ -106,21 +106,20 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int
|
||||||
|
|
||||||
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
||||||
{
|
{
|
||||||
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" +
|
Info info;
|
||||||
reader.getHNOString ("INAM");
|
bool isDeleted = false;
|
||||||
|
|
||||||
if (reader.isNextSub ("DELE"))
|
info.load (reader, isDeleted);
|
||||||
|
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + info.mId;
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
int index = searchId (id);
|
int index = searchId (id);
|
||||||
|
|
||||||
reader.skipRecord();
|
|
||||||
|
|
||||||
if (index==-1)
|
if (index==-1)
|
||||||
{
|
{
|
||||||
// deleting a record that does not exist
|
// deleting a record that does not exist
|
||||||
|
|
||||||
// ignore it for now
|
// ignore it for now
|
||||||
|
|
||||||
/// \todo report the problem to the user
|
/// \todo report the problem to the user
|
||||||
}
|
}
|
||||||
else if (base)
|
else if (base)
|
||||||
|
@ -136,12 +135,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Info record;
|
info.mTopicId = dialogue.mId;
|
||||||
record.mTopicId = dialogue.mId;
|
info.mId = id;
|
||||||
record.mId = id;
|
load (info, base);
|
||||||
record.load (reader);
|
|
||||||
|
|
||||||
load (record, base);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,3 +189,39 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
|
||||||
|
|
||||||
return Range (begin, end);
|
return Range (begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId)
|
||||||
|
{
|
||||||
|
std::string id = Misc::StringUtils::lowerCase(dialogueId);
|
||||||
|
std::vector<int> erasedRecords;
|
||||||
|
|
||||||
|
std::map<std::string, int>::const_iterator current = getIdMap().lower_bound(id);
|
||||||
|
std::map<std::string, int>::const_iterator end = getIdMap().end();
|
||||||
|
for (; current != end; ++current)
|
||||||
|
{
|
||||||
|
Record<Info> record = getRecord(current->second);
|
||||||
|
|
||||||
|
if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId))
|
||||||
|
{
|
||||||
|
if (record.mState == RecordBase::State_ModifiedOnly)
|
||||||
|
{
|
||||||
|
erasedRecords.push_back(current->second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
record.mState = RecordBase::State_Deleted;
|
||||||
|
setRecord(current->second, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!erasedRecords.empty())
|
||||||
|
{
|
||||||
|
removeRows(erasedRecords.back(), 1);
|
||||||
|
erasedRecords.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ namespace CSMWorld
|
||||||
Range getTopicRange (const std::string& topic) const;
|
Range getTopicRange (const std::string& topic) const;
|
||||||
///< Return iterators that point to the beginning and past the end of the range for
|
///< Return iterators that point to the beginning and past the end of the range for
|
||||||
/// the given topic.
|
/// the given topic.
|
||||||
|
|
||||||
|
void removeDialogueInfos(const std::string& dialogueId);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
void Land::load(ESM::ESMReader &esm)
|
void Land::load(ESM::ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
ESM::Land::load(esm);
|
ESM::Land::load(esm, isDeleted);
|
||||||
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << "#" << mX << " " << mY;
|
stream << "#" << mX << " " << mY;
|
||||||
|
|
||||||
mId = stream.str();
|
mId = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,12 @@ namespace CSMWorld
|
||||||
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
|
/// \brief Wrapper for Land record. Encodes X and Y cell index in the ID.
|
||||||
///
|
///
|
||||||
/// \todo Add worldspace support to the Land record.
|
/// \todo Add worldspace support to the Land record.
|
||||||
/// \todo Add a proper copy constructor (currently worked around using shared_ptr)
|
|
||||||
struct Land : public ESM::Land
|
struct Land : public ESM::Land
|
||||||
{
|
{
|
||||||
std::string mId;
|
std::string mId;
|
||||||
|
|
||||||
/// Loads the metadata and ID
|
/// Loads the metadata and ID
|
||||||
void load (ESM::ESMReader &esm);
|
void load (ESM::ESMReader &esm, bool &isDeleted);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,9 @@
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted)
|
||||||
void LandTexture::load(ESM::ESMReader &esm)
|
|
||||||
{
|
{
|
||||||
ESM::LandTexture::load(esm);
|
ESM::LandTexture::load(esm, isDeleted);
|
||||||
|
|
||||||
mPluginIndex = esm.getIndex();
|
mPluginIndex = esm.getIndex();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
int mPluginIndex;
|
int mPluginIndex;
|
||||||
|
|
||||||
void load (ESM::ESMReader &esm);
|
void load (ESM::ESMReader &esm, bool &isDeleted);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,33 +4,28 @@
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, const IdCollection<Cell>& cells)
|
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells)
|
||||||
{
|
{
|
||||||
load (esm);
|
load (esm, isDeleted);
|
||||||
|
|
||||||
// correct ID
|
// correct ID
|
||||||
if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1)
|
if (!mId.empty() && mId[0]!='#' && cells.searchId (mId)==-1)
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
|
|
||||||
stream << "#" << mData.mX << " " << mData.mY;
|
stream << "#" << mData.mX << " " << mData.mY;
|
||||||
|
|
||||||
mId = stream.str();
|
mId = stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm)
|
void CSMWorld::Pathgrid::load (ESM::ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
ESM::Pathgrid::load (esm);
|
ESM::Pathgrid::load (esm, isDeleted);
|
||||||
|
|
||||||
|
mId = mCell;
|
||||||
if (mCell.empty())
|
if (mCell.empty())
|
||||||
{
|
{
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
|
|
||||||
stream << "#" << mData.mX << " " << mData.mY;
|
stream << "#" << mData.mX << " " << mData.mY;
|
||||||
|
|
||||||
mId = stream.str();
|
mId = stream.str();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
mId = mCell;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,8 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
std::string mId;
|
std::string mId;
|
||||||
|
|
||||||
void load (ESM::ESMReader &esm, const IdCollection<Cell>& cells);
|
void load (ESM::ESMReader &esm, bool &isDeleted, const IdCollection<Cell>& cells);
|
||||||
|
void load (ESM::ESMReader &esm, bool &isDeleted);
|
||||||
void load (ESM::ESMReader &esm);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
Cell& cell2 = base ? cell.mBase : cell.mModified;
|
Cell& cell2 = base ? cell.mBase : cell.mModified;
|
||||||
|
|
||||||
CellRef ref;
|
CellRef ref;
|
||||||
|
|
||||||
bool deleted = false;
|
|
||||||
ESM::MovedCellRef mref;
|
ESM::MovedCellRef mref;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
// hack to initialise mindex
|
// hack to initialise mindex
|
||||||
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref))
|
while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref))
|
||||||
{
|
{
|
||||||
// Keep mOriginalCell empty when in modified (as an indicator that the
|
// Keep mOriginalCell empty when in modified (as an indicator that the
|
||||||
// original cell will always be equal the current cell).
|
// original cell will always be equal the current cell).
|
||||||
|
@ -49,17 +48,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
// https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
|
// https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30
|
||||||
ref.mOriginalCell = cell2.mId;
|
ref.mOriginalCell = cell2.mId;
|
||||||
|
|
||||||
if (deleted)
|
|
||||||
{
|
|
||||||
// FIXME: how to mark the record deleted?
|
|
||||||
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell,
|
|
||||||
mCells.getId (cellIndex));
|
|
||||||
|
|
||||||
messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is not always possibe to ignore moved references sub-record and
|
// It is not always possibe to ignore moved references sub-record and
|
||||||
// calculate from coordinates. Some mods may place the ref in positions
|
// calculate from coordinates. Some mods may place the ref in positions
|
||||||
// outside normal bounds, resulting in non sensical cell id's. This often
|
// outside normal bounds, resulting in non sensical cell id's. This often
|
||||||
|
@ -91,7 +79,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleted)
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
if (iter==cache.end())
|
if (iter==cache.end())
|
||||||
{
|
{
|
||||||
|
@ -99,7 +87,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
mCells.getId (cellIndex));
|
mCells.getId (cellIndex));
|
||||||
|
|
||||||
messages.add (id, "Attempt to delete a non-existing reference");
|
messages.add (id, "Attempt to delete a non-existing reference");
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +94,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
||||||
|
|
||||||
Record<CellRef> record = getRecord (index);
|
Record<CellRef> record = getRecord (index);
|
||||||
|
|
||||||
if (record.mState==RecordBase::State_BaseOnly)
|
if (base)
|
||||||
{
|
{
|
||||||
removeRows (index, 1);
|
removeRows (index, 1);
|
||||||
cache.erase (iter);
|
cache.erase (iter);
|
||||||
|
|
|
@ -837,61 +837,7 @@ const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (int index) con
|
||||||
|
|
||||||
void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type)
|
void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::string id = reader.getHNOString ("NAME");
|
mData.load(reader, base, type);
|
||||||
|
|
||||||
int index = searchId (id);
|
|
||||||
|
|
||||||
if (reader.isNextSub ("DELE"))
|
|
||||||
{
|
|
||||||
reader.skipRecord();
|
|
||||||
|
|
||||||
if (index==-1)
|
|
||||||
{
|
|
||||||
// deleting a record that does not exist
|
|
||||||
|
|
||||||
// ignore it for now
|
|
||||||
|
|
||||||
/// \todo report the problem to the user
|
|
||||||
}
|
|
||||||
else if (base)
|
|
||||||
{
|
|
||||||
mData.erase (index, 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mData.getRecord (mData.globalToLocalIndex (index)).mState = RecordBase::State_Deleted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (index==-1)
|
|
||||||
{
|
|
||||||
// new record
|
|
||||||
int index = mData.getAppendIndex (type);
|
|
||||||
mData.appendRecord (type, id, base);
|
|
||||||
|
|
||||||
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
|
|
||||||
|
|
||||||
mData.load (localIndex, reader, base);
|
|
||||||
|
|
||||||
mData.getRecord (localIndex).mState =
|
|
||||||
base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// old record
|
|
||||||
RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index);
|
|
||||||
|
|
||||||
if (!base)
|
|
||||||
if (mData.getRecord (localIndex).mState==RecordBase::State_Erased)
|
|
||||||
throw std::logic_error ("attempt to access a deleted record");
|
|
||||||
|
|
||||||
mData.load (localIndex, reader, base);
|
|
||||||
|
|
||||||
if (!base)
|
|
||||||
mData.getRecord (localIndex).mState = RecordBase::State_Modified;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
|
int CSMWorld::RefIdCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
|
||||||
|
|
|
@ -3,10 +3,20 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
|
|
||||||
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
|
CSMWorld::RefIdDataContainerBase::~RefIdDataContainerBase() {}
|
||||||
|
|
||||||
|
|
||||||
|
std::string CSMWorld::RefIdData::getRecordId(const CSMWorld::RefIdData::LocalIndex &index) const
|
||||||
|
{
|
||||||
|
std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator found =
|
||||||
|
mRecordContainers.find (index.second);
|
||||||
|
|
||||||
|
if (found == mRecordContainers.end())
|
||||||
|
throw std::logic_error ("invalid local index type");
|
||||||
|
|
||||||
|
return found->second->getId(index.first);
|
||||||
|
}
|
||||||
|
|
||||||
CSMWorld::RefIdData::RefIdData()
|
CSMWorld::RefIdData::RefIdData()
|
||||||
{
|
{
|
||||||
mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));
|
mRecordContainers.insert (std::make_pair (UniversalId::Type_Activator, &mActivators));
|
||||||
|
@ -161,15 +171,27 @@ int CSMWorld::RefIdData::getAppendIndex (UniversalId::Type type) const
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdData::load (const LocalIndex& index, ESM::ESMReader& reader, bool base)
|
void CSMWorld::RefIdData::load (ESM::ESMReader& reader, bool base, CSMWorld::UniversalId::Type type)
|
||||||
{
|
{
|
||||||
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
|
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator found =
|
||||||
mRecordContainers.find (index.second);
|
mRecordContainers.find (type);
|
||||||
|
|
||||||
if (iter==mRecordContainers.end())
|
if (found == mRecordContainers.end())
|
||||||
throw std::logic_error ("invalid local index type");
|
throw std::logic_error ("Invalid Referenceable ID type");
|
||||||
|
|
||||||
iter->second->load (index.first, reader, base);
|
int index = found->second->load(reader, base);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
LocalIndex localIndex = LocalIndex(index, type);
|
||||||
|
if (base && getRecord(localIndex).mState == RecordBase::State_Deleted)
|
||||||
|
{
|
||||||
|
erase(localIndex, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mIndex[Misc::StringUtils::lowerCase(getRecordId(localIndex))] = localIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
|
void CSMWorld::RefIdData::erase (const LocalIndex& index, int count)
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include <components/esm/loadmisc.hpp>
|
#include <components/esm/loadmisc.hpp>
|
||||||
#include <components/esm/esmwriter.hpp>
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "record.hpp"
|
#include "record.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
|
||||||
|
@ -49,7 +51,8 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual void insertRecord (RecordBase& record) = 0;
|
virtual void insertRecord (RecordBase& record) = 0;
|
||||||
|
|
||||||
virtual void load (int index, ESM::ESMReader& reader, bool base) = 0;
|
virtual int load (ESM::ESMReader& reader, bool base) = 0;
|
||||||
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
|
|
||||||
virtual void erase (int index, int count) = 0;
|
virtual void erase (int index, int count) = 0;
|
||||||
|
|
||||||
|
@ -73,7 +76,8 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual void insertRecord (RecordBase& record);
|
virtual void insertRecord (RecordBase& record);
|
||||||
|
|
||||||
virtual void load (int index, ESM::ESMReader& reader, bool base);
|
virtual int load (ESM::ESMReader& reader, bool base);
|
||||||
|
///< \return index of a loaded record or -1 if no record was loaded
|
||||||
|
|
||||||
virtual void erase (int index, int count);
|
virtual void erase (int index, int count);
|
||||||
|
|
||||||
|
@ -122,9 +126,58 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::load (int index, ESM::ESMReader& reader, bool base)
|
int RefIdDataContainer<RecordT>::load (ESM::ESMReader& reader, bool base)
|
||||||
{
|
{
|
||||||
(base ? mContainer.at (index).mBase : mContainer.at (index).mModified).load (reader);
|
RecordT record;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
|
record.load(reader, isDeleted);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
int numRecords = static_cast<int>(mContainer.size());
|
||||||
|
for (; index < numRecords; ++index)
|
||||||
|
{
|
||||||
|
if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
if (index == numRecords)
|
||||||
|
{
|
||||||
|
// deleting a record that does not exist
|
||||||
|
// ignore it for now
|
||||||
|
/// \todo report the problem to the user
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the record as Deleted even for a base content file.
|
||||||
|
// RefIdData is responsible for its erasure.
|
||||||
|
mContainer[index].mState = RecordBase::State_Deleted;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (index == numRecords)
|
||||||
|
{
|
||||||
|
appendRecord(record.mId, base);
|
||||||
|
if (base)
|
||||||
|
{
|
||||||
|
mContainer.back().mBase = record;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mContainer.back().mModified = record;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!base)
|
||||||
|
{
|
||||||
|
mContainer[index].setModified(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
|
@ -145,19 +198,14 @@ namespace CSMWorld
|
||||||
template<typename RecordT>
|
template<typename RecordT>
|
||||||
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
void RefIdDataContainer<RecordT>::save (int index, ESM::ESMWriter& writer) const
|
||||||
{
|
{
|
||||||
CSMWorld::RecordBase::State state = mContainer.at (index).mState;
|
Record<RecordT> record = mContainer.at(index);
|
||||||
|
|
||||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
if (record.isModified() || record.mState == RecordBase::State_Deleted)
|
||||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
|
||||||
{
|
{
|
||||||
writer.startRecord (mContainer.at (index).mModified.sRecordId);
|
RecordT esmRecord = record.get();
|
||||||
writer.writeHNCString ("NAME", getId (index));
|
writer.startRecord(esmRecord.sRecordId);
|
||||||
mContainer.at (index).mModified.save (writer);
|
esmRecord.save(writer, record.mState == RecordBase::State_Deleted);
|
||||||
writer.endRecord (mContainer.at (index).mModified.sRecordId);
|
writer.endRecord(esmRecord.sRecordId);
|
||||||
}
|
|
||||||
else if (state==CSMWorld::RecordBase::State_Deleted)
|
|
||||||
{
|
|
||||||
/// \todo write record with delete flag
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +246,8 @@ namespace CSMWorld
|
||||||
void erase (const LocalIndex& index, int count);
|
void erase (const LocalIndex& index, int count);
|
||||||
///< Must not spill over into another type.
|
///< Must not spill over into another type.
|
||||||
|
|
||||||
|
std::string getRecordId(const LocalIndex &index) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RefIdData();
|
RefIdData();
|
||||||
|
@ -221,7 +271,7 @@ namespace CSMWorld
|
||||||
|
|
||||||
int getAppendIndex (UniversalId::Type type) const;
|
int getAppendIndex (UniversalId::Type type) const;
|
||||||
|
|
||||||
void load (const LocalIndex& index, ESM::ESMReader& reader, bool base);
|
void load (ESM::ESMReader& reader, bool base, UniversalId::Type type);
|
||||||
|
|
||||||
int getSize() const;
|
int getSize() const;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
const IdCollection<Cell>& mCells;
|
const IdCollection<Cell>& mCells;
|
||||||
|
|
||||||
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader);
|
virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -29,9 +29,10 @@ namespace CSMWorld
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
void SubCellCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
|
void SubCellCollection<ESXRecordT, IdAccessorT>::loadRecord (ESXRecordT& record,
|
||||||
ESM::ESMReader& reader)
|
ESM::ESMReader& reader,
|
||||||
|
bool& isDeleted)
|
||||||
{
|
{
|
||||||
record.load (reader, mCells);
|
record.load (reader, isDeleted, mCells);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT, typename IdAccessorT>
|
template<typename ESXRecordT, typename IdAccessorT>
|
||||||
|
|
|
@ -475,7 +475,7 @@ namespace MWWorld
|
||||||
switch (store.find (ref.mRefID))
|
switch (store.find (ref.mRefID))
|
||||||
{
|
{
|
||||||
case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break;
|
case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break;
|
||||||
case ESM::REC_ALCH: mPotions.load(ref, deleted, store); break;
|
case ESM::REC_ALCH: mPotions.load(ref, deleted,store); break;
|
||||||
case ESM::REC_APPA: mAppas.load(ref, deleted, store); break;
|
case ESM::REC_APPA: mAppas.load(ref, deleted, store); break;
|
||||||
case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break;
|
case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break;
|
||||||
case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break;
|
case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break;
|
||||||
|
|
|
@ -96,33 +96,21 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
|
||||||
throw std::runtime_error(error.str());
|
throw std::runtime_error(error.str());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Load it
|
RecordId id = it->second->load(esm);
|
||||||
std::string id = esm.getHNOString("NAME");
|
if (id.mIsDeleted)
|
||||||
// ... unless it got deleted! This means that the following record
|
{
|
||||||
// has been deleted, and trying to load it using standard assumptions
|
it->second->eraseStatic(id.mId);
|
||||||
// on the structure will (probably) fail.
|
|
||||||
if (esm.isNextSub("DELE")) {
|
|
||||||
esm.skipRecord();
|
|
||||||
it->second->eraseStatic(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
it->second->load(esm, id);
|
|
||||||
|
|
||||||
// DELE can also occur after the usual subrecords
|
|
||||||
if (esm.isNextSub("DELE")) {
|
|
||||||
esm.skipRecord();
|
|
||||||
it->second->eraseStatic(id);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n.val==ESM::REC_DIAL) {
|
if (n.val==ESM::REC_DIAL) {
|
||||||
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id));
|
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id.mId));
|
||||||
} else {
|
} else {
|
||||||
dialogue = 0;
|
dialogue = 0;
|
||||||
}
|
}
|
||||||
// Insert the reference into the global lookup
|
// Insert the reference into the global lookup
|
||||||
if (!id.empty() && isCacheableRecord(n.val)) {
|
if (!id.mId.empty() && isCacheableRecord(n.val)) {
|
||||||
mIds[Misc::StringUtils::lowerCase (id)] = n.val;
|
mIds[Misc::StringUtils::lowerCase (id.mId)] = n.val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener->setProgress(static_cast<size_t>(esm.getFileOffset() / (float)esm.getFileSize() * 1000));
|
listener->setProgress(static_cast<size_t>(esm.getFileOffset() / (float)esm.getFileSize() * 1000));
|
||||||
|
@ -195,13 +183,12 @@ void ESMStore::setUp()
|
||||||
case ESM::REC_LEVC:
|
case ESM::REC_LEVC:
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string id = reader.getHNString ("NAME");
|
RecordId id = mStores[type]->read (reader);
|
||||||
mStores[type]->read (reader, id);
|
|
||||||
|
|
||||||
// FIXME: there might be stale dynamic IDs in mIds from an earlier savegame
|
// FIXME: there might be stale dynamic IDs in mIds from an earlier savegame
|
||||||
// that really should be cleared instead of just overwritten
|
// that really should be cleared instead of just overwritten
|
||||||
|
|
||||||
mIds[id] = type;
|
mIds[id.mId] = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type==ESM::REC_NPC_)
|
if (type==ESM::REC_NPC_)
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
Globals::Collection::const_iterator Globals::find (const std::string& name) const
|
Globals::Collection::const_iterator Globals::find (const std::string& name) const
|
||||||
{
|
{
|
||||||
Collection::const_iterator iter = mVariables.find (name);
|
Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));
|
||||||
|
|
||||||
if (iter==mVariables.end())
|
if (iter==mVariables.end())
|
||||||
throw std::runtime_error ("unknown global variable: " + name);
|
throw std::runtime_error ("unknown global variable: " + name);
|
||||||
|
@ -23,7 +23,7 @@ namespace MWWorld
|
||||||
|
|
||||||
Globals::Collection::iterator Globals::find (const std::string& name)
|
Globals::Collection::iterator Globals::find (const std::string& name)
|
||||||
{
|
{
|
||||||
Collection::iterator iter = mVariables.find (name);
|
Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));
|
||||||
|
|
||||||
if (iter==mVariables.end())
|
if (iter==mVariables.end())
|
||||||
throw std::runtime_error ("unknown global variable: " + name);
|
throw std::runtime_error ("unknown global variable: " + name);
|
||||||
|
@ -40,28 +40,28 @@ namespace MWWorld
|
||||||
for (MWWorld::Store<ESM::Global>::iterator iter = globals.begin(); iter!=globals.end();
|
for (MWWorld::Store<ESM::Global>::iterator iter = globals.begin(); iter!=globals.end();
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
mVariables.insert (std::make_pair (iter->mId, iter->mValue));
|
mVariables.insert (std::make_pair (Misc::StringUtils::lowerCase (iter->mId), *iter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::Variant& Globals::operator[] (const std::string& name) const
|
const ESM::Variant& Globals::operator[] (const std::string& name) const
|
||||||
{
|
{
|
||||||
return find (name)->second;
|
return find (Misc::StringUtils::lowerCase (name))->second.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::Variant& Globals::operator[] (const std::string& name)
|
ESM::Variant& Globals::operator[] (const std::string& name)
|
||||||
{
|
{
|
||||||
return find (name)->second;
|
return find (Misc::StringUtils::lowerCase (name))->second.mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
char Globals::getType (const std::string& name) const
|
char Globals::getType (const std::string& name) const
|
||||||
{
|
{
|
||||||
Collection::const_iterator iter = mVariables.find (name);
|
Collection::const_iterator iter = mVariables.find (Misc::StringUtils::lowerCase (name));
|
||||||
|
|
||||||
if (iter==mVariables.end())
|
if (iter==mVariables.end())
|
||||||
return ' ';
|
return ' ';
|
||||||
|
|
||||||
switch (iter->second.getType())
|
switch (iter->second.mValue.getType())
|
||||||
{
|
{
|
||||||
case ESM::VT_Short: return 's';
|
case ESM::VT_Short: return 's';
|
||||||
case ESM::VT_Long: return 'l';
|
case ESM::VT_Long: return 'l';
|
||||||
|
@ -81,8 +81,7 @@ namespace MWWorld
|
||||||
for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter)
|
for (Collection::const_iterator iter (mVariables.begin()); iter!=mVariables.end(); ++iter)
|
||||||
{
|
{
|
||||||
writer.startRecord (ESM::REC_GLOB);
|
writer.startRecord (ESM::REC_GLOB);
|
||||||
writer.writeHNString ("NAME", iter->first);
|
iter->second.save (writer);
|
||||||
iter->second.write (writer, ESM::Variant::Format_Global);
|
|
||||||
writer.endRecord (ESM::REC_GLOB);
|
writer.endRecord (ESM::REC_GLOB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,14 +90,17 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
if (type==ESM::REC_GLOB)
|
if (type==ESM::REC_GLOB)
|
||||||
{
|
{
|
||||||
std::string id = reader.getHNString ("NAME");
|
ESM::Global global;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
Collection::iterator iter = mVariables.find (Misc::StringUtils::lowerCase (id));
|
// This readRecord() method is used when reading a saved game.
|
||||||
|
// Deleted globals can't appear there, so isDeleted will be ignored here.
|
||||||
|
global.load(reader, isDeleted);
|
||||||
|
Misc::StringUtils::toLower(global.mId);
|
||||||
|
|
||||||
|
Collection::iterator iter = mVariables.find (global.mId);
|
||||||
if (iter!=mVariables.end())
|
if (iter!=mVariables.end())
|
||||||
iter->second.read (reader, ESM::Variant::Format_Global);
|
iter->second = global;
|
||||||
else
|
|
||||||
reader.skipRecord();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <components/interpreter/types.hpp>
|
#include <components/interpreter/types.hpp>
|
||||||
#include <components/esm/variant.hpp>
|
#include <components/esm/loadglob.hpp>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef std::map<std::string, ESM::Variant> Collection;
|
typedef std::map<std::string, ESM::Global> Collection;
|
||||||
|
|
||||||
Collection mVariables; // type, value
|
Collection mVariables; // type, value
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ namespace
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
RecordId::RecordId(const std::string &id, bool isDeleted)
|
||||||
|
: mId(id), mIsDeleted(isDeleted)
|
||||||
|
{}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
IndexedStore<T>::IndexedStore()
|
IndexedStore<T>::IndexedStore()
|
||||||
{
|
{
|
||||||
|
@ -60,7 +64,9 @@ namespace MWWorld
|
||||||
void IndexedStore<T>::load(ESM::ESMReader &esm)
|
void IndexedStore<T>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
T record;
|
T record;
|
||||||
record.load(esm);
|
bool isDeleted = false;
|
||||||
|
|
||||||
|
record.load(esm, isDeleted);
|
||||||
|
|
||||||
// Try to overwrite existing record
|
// Try to overwrite existing record
|
||||||
std::pair<typename Static::iterator, bool> ret = mStatic.insert(std::make_pair(record.mIndex, record));
|
std::pair<typename Static::iterator, bool> ret = mStatic.insert(std::make_pair(record.mIndex, record));
|
||||||
|
@ -178,16 +184,21 @@ namespace MWWorld
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Store<T>::load(ESM::ESMReader &esm, const std::string &id)
|
RecordId Store<T>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
std::string idLower = Misc::StringUtils::lowerCase(id);
|
T record;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
std::pair<typename Static::iterator, bool> inserted = mStatic.insert(std::make_pair(idLower, T()));
|
record.load(esm, isDeleted);
|
||||||
|
Misc::StringUtils::toLower(record.mId);
|
||||||
|
|
||||||
|
std::pair<typename Static::iterator, bool> inserted = mStatic.insert(std::make_pair(record.mId, record));
|
||||||
if (inserted.second)
|
if (inserted.second)
|
||||||
mShared.push_back(&inserted.first->second);
|
mShared.push_back(&inserted.first->second);
|
||||||
|
else
|
||||||
|
inserted.first->second = record;
|
||||||
|
|
||||||
inserted.first->second.mId = idLower;
|
return RecordId(record.mId, isDeleted);
|
||||||
inserted.first->second.load(esm);
|
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Store<T>::setUp()
|
void Store<T>::setUp()
|
||||||
|
@ -309,20 +320,21 @@ namespace MWWorld
|
||||||
++iter)
|
++iter)
|
||||||
{
|
{
|
||||||
writer.startRecord (T::sRecordId);
|
writer.startRecord (T::sRecordId);
|
||||||
writer.writeHNString ("NAME", iter->second.mId);
|
|
||||||
iter->second.save (writer);
|
iter->second.save (writer);
|
||||||
writer.endRecord (T::sRecordId);
|
writer.endRecord (T::sRecordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Store<T>::read(ESM::ESMReader& reader, const std::string& id)
|
RecordId Store<T>::read(ESM::ESMReader& reader)
|
||||||
{
|
{
|
||||||
T record;
|
T record;
|
||||||
record.mId = id;
|
bool isDeleted = false;
|
||||||
record.load (reader);
|
|
||||||
insert (record);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
record.load (reader, isDeleted);
|
||||||
|
insert (record);
|
||||||
|
|
||||||
|
return RecordId(record.mId, isDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
// LandTexture
|
// LandTexture
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
@ -361,11 +373,12 @@ namespace MWWorld
|
||||||
assert(plugin < mStatic.size());
|
assert(plugin < mStatic.size());
|
||||||
return mStatic[plugin].size();
|
return mStatic[plugin].size();
|
||||||
}
|
}
|
||||||
void Store<ESM::LandTexture>::load(ESM::ESMReader &esm, const std::string &id, size_t plugin)
|
RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm, size_t plugin)
|
||||||
{
|
{
|
||||||
ESM::LandTexture lt;
|
ESM::LandTexture lt;
|
||||||
lt.load(esm);
|
bool isDeleted = false;
|
||||||
lt.mId = id;
|
|
||||||
|
lt.load(esm, isDeleted);
|
||||||
|
|
||||||
// Make sure we have room for the structure
|
// Make sure we have room for the structure
|
||||||
if (plugin >= mStatic.size()) {
|
if (plugin >= mStatic.size()) {
|
||||||
|
@ -377,10 +390,12 @@ namespace MWWorld
|
||||||
|
|
||||||
// Store it
|
// Store it
|
||||||
ltexl[lt.mIndex] = lt;
|
ltexl[lt.mIndex] = lt;
|
||||||
|
|
||||||
|
return RecordId(lt.mId, isDeleted);
|
||||||
}
|
}
|
||||||
void Store<ESM::LandTexture>::load(ESM::ESMReader &esm, const std::string &id)
|
RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
load(esm, id, esm.getIndex());
|
return load(esm, esm.getIndex());
|
||||||
}
|
}
|
||||||
Store<ESM::LandTexture>::iterator Store<ESM::LandTexture>::begin(size_t plugin) const
|
Store<ESM::LandTexture>::iterator Store<ESM::LandTexture>::begin(size_t plugin) const
|
||||||
{
|
{
|
||||||
|
@ -393,7 +408,6 @@ namespace MWWorld
|
||||||
return mStatic[plugin].end();
|
return mStatic[plugin].end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Land
|
// Land
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
Store<ESM::Land>::~Store()
|
Store<ESM::Land>::~Store()
|
||||||
|
@ -440,10 +454,12 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
void Store<ESM::Land>::load(ESM::ESMReader &esm, const std::string &id)
|
RecordId Store<ESM::Land>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
ESM::Land *ptr = new ESM::Land();
|
ESM::Land *ptr = new ESM::Land();
|
||||||
ptr->load(esm);
|
bool isDeleted = false;
|
||||||
|
|
||||||
|
ptr->load(esm, isDeleted);
|
||||||
|
|
||||||
// Same area defined in multiple plugins? -> last plugin wins
|
// Same area defined in multiple plugins? -> last plugin wins
|
||||||
// Can't use search() because we aren't sorted yet - is there any other way to speed this up?
|
// Can't use search() because we aren't sorted yet - is there any other way to speed this up?
|
||||||
|
@ -458,6 +474,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
mStatic.push_back(ptr);
|
mStatic.push_back(ptr);
|
||||||
|
|
||||||
|
return RecordId("", isDeleted);
|
||||||
}
|
}
|
||||||
void Store<ESM::Land>::setUp()
|
void Store<ESM::Land>::setUp()
|
||||||
{
|
{
|
||||||
|
@ -600,7 +618,7 @@ namespace MWWorld
|
||||||
mSharedExt.push_back(&(it->second));
|
mSharedExt.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
|
RecordId Store<ESM::Cell>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
|
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
|
||||||
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
|
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
|
||||||
|
@ -608,13 +626,13 @@ namespace MWWorld
|
||||||
// are not available until both cells have been loaded at least partially!
|
// are not available until both cells have been loaded at least partially!
|
||||||
|
|
||||||
// All cells have a name record, even nameless exterior cells.
|
// All cells have a name record, even nameless exterior cells.
|
||||||
std::string idLower = Misc::StringUtils::lowerCase(id);
|
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
cell.mName = id;
|
bool isDeleted = false;
|
||||||
|
|
||||||
// Load the (x,y) coordinates of the cell, if it is an exterior cell,
|
// Load the (x,y) coordinates of the cell, if it is an exterior cell,
|
||||||
// so we can find the cell we need to merge with
|
// so we can find the cell we need to merge with
|
||||||
cell.loadData(esm);
|
cell.loadNameAndData(esm, isDeleted);
|
||||||
|
std::string idLower = Misc::StringUtils::lowerCase(cell.mName);
|
||||||
|
|
||||||
if(cell.mData.mFlags & ESM::Cell::Interior)
|
if(cell.mData.mFlags & ESM::Cell::Interior)
|
||||||
{
|
{
|
||||||
|
@ -682,6 +700,8 @@ namespace MWWorld
|
||||||
mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell;
|
mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return RecordId(cell.mName, isDeleted);
|
||||||
}
|
}
|
||||||
Store<ESM::Cell>::iterator Store<ESM::Cell>::intBegin() const
|
Store<ESM::Cell>::iterator Store<ESM::Cell>::intBegin() const
|
||||||
{
|
{
|
||||||
|
@ -837,10 +857,12 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
mCells = &cells;
|
mCells = &cells;
|
||||||
}
|
}
|
||||||
void Store<ESM::Pathgrid>::load(ESM::ESMReader &esm, const std::string &id)
|
RecordId Store<ESM::Pathgrid>::load(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
ESM::Pathgrid pathgrid;
|
ESM::Pathgrid pathgrid;
|
||||||
pathgrid.load(esm);
|
bool isDeleted = false;
|
||||||
|
|
||||||
|
pathgrid.load(esm, isDeleted);
|
||||||
|
|
||||||
// Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell.
|
// Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell.
|
||||||
// For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name.
|
// For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name.
|
||||||
|
@ -862,6 +884,8 @@ namespace MWWorld
|
||||||
if (!ret.second)
|
if (!ret.second)
|
||||||
ret.first->second = pathgrid;
|
ret.first->second = pathgrid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return RecordId("", isDeleted);
|
||||||
}
|
}
|
||||||
size_t Store<ESM::Pathgrid>::getSize() const
|
size_t Store<ESM::Pathgrid>::getSize() const
|
||||||
{
|
{
|
||||||
|
@ -1013,51 +1037,29 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
|
inline RecordId Store<ESM::Dialogue>::load(ESM::ESMReader &esm) {
|
||||||
std::string idLower = Misc::StringUtils::lowerCase(id);
|
// The original letter case of a dialogue ID is saved, because it's printed
|
||||||
|
ESM::Dialogue dialogue;
|
||||||
|
bool isDeleted = false;
|
||||||
|
|
||||||
std::map<std::string, ESM::Dialogue>::iterator it = mStatic.find(idLower);
|
dialogue.loadId(esm);
|
||||||
if (it == mStatic.end()) {
|
|
||||||
it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first;
|
|
||||||
it->second.mId = id; // don't smash case here, as this line is printed
|
|
||||||
}
|
|
||||||
|
|
||||||
it->second.load(esm);
|
std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId);
|
||||||
}
|
std::map<std::string, ESM::Dialogue>::iterator found = mStatic.find(idLower);
|
||||||
|
if (found == mStatic.end())
|
||||||
|
|
||||||
// Script
|
|
||||||
//=========================================================================
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
|
|
||||||
ESM::Script scpt;
|
|
||||||
scpt.load(esm);
|
|
||||||
Misc::StringUtils::toLower(scpt.mId);
|
|
||||||
|
|
||||||
std::pair<typename Static::iterator, bool> inserted = mStatic.insert(std::make_pair(scpt.mId, scpt));
|
|
||||||
if (inserted.second)
|
|
||||||
mShared.push_back(&inserted.first->second);
|
|
||||||
else
|
|
||||||
inserted.first->second = scpt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// StartScript
|
|
||||||
//=========================================================================
|
|
||||||
|
|
||||||
template <>
|
|
||||||
void Store<ESM::StartScript>::load(ESM::ESMReader &esm, const std::string &id)
|
|
||||||
{
|
{
|
||||||
ESM::StartScript s;
|
dialogue.loadData(esm, isDeleted);
|
||||||
s.load(esm);
|
mStatic.insert(std::make_pair(idLower, dialogue));
|
||||||
s.mId = Misc::StringUtils::toLower(s.mId);
|
|
||||||
std::pair<typename Static::iterator, bool> inserted = mStatic.insert(std::make_pair(s.mId, s));
|
|
||||||
if (inserted.second)
|
|
||||||
mShared.push_back(&inserted.first->second);
|
|
||||||
else
|
|
||||||
inserted.first->second = s;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found->second.loadData(esm, isDeleted);
|
||||||
|
dialogue = found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecordId(dialogue.mId, isDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class MWWorld::Store<ESM::Activator>;
|
template class MWWorld::Store<ESM::Activator>;
|
||||||
|
@ -1082,7 +1084,7 @@ template class MWWorld::Store<ESM::Global>;
|
||||||
template class MWWorld::Store<ESM::Ingredient>;
|
template class MWWorld::Store<ESM::Ingredient>;
|
||||||
template class MWWorld::Store<ESM::ItemLevList>;
|
template class MWWorld::Store<ESM::ItemLevList>;
|
||||||
//template class MWWorld::Store<ESM::Land>;
|
//template class MWWorld::Store<ESM::Land>;
|
||||||
template class MWWorld::Store<ESM::LandTexture>;
|
//template class MWWorld::Store<ESM::LandTexture>;
|
||||||
template class MWWorld::Store<ESM::Light>;
|
template class MWWorld::Store<ESM::Light>;
|
||||||
template class MWWorld::Store<ESM::Lockpick>;
|
template class MWWorld::Store<ESM::Lockpick>;
|
||||||
//template class MWWorld::Store<ESM::MagicEffect>;
|
//template class MWWorld::Store<ESM::MagicEffect>;
|
||||||
|
|
|
@ -19,6 +19,14 @@ namespace Loading
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
struct RecordId
|
||||||
|
{
|
||||||
|
std::string mId;
|
||||||
|
bool mIsDeleted;
|
||||||
|
|
||||||
|
RecordId(const std::string &id = "", bool isDeleted = false);
|
||||||
|
};
|
||||||
|
|
||||||
struct StoreBase
|
struct StoreBase
|
||||||
{
|
{
|
||||||
virtual ~StoreBase() {}
|
virtual ~StoreBase() {}
|
||||||
|
@ -28,14 +36,14 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual size_t getSize() const = 0;
|
virtual size_t getSize() const = 0;
|
||||||
virtual int getDynamicSize() const { return 0; }
|
virtual int getDynamicSize() const { return 0; }
|
||||||
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
|
virtual RecordId load(ESM::ESMReader &esm) = 0;
|
||||||
|
|
||||||
virtual bool eraseStatic(const std::string &id) {return false;}
|
virtual bool eraseStatic(const std::string &id) {return false;}
|
||||||
virtual void clearDynamic() {}
|
virtual void clearDynamic() {}
|
||||||
|
|
||||||
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {}
|
virtual void write (ESM::ESMWriter& writer, Loading::Listener& progress) const {}
|
||||||
|
|
||||||
virtual void read (ESM::ESMReader& reader, const std::string& id) {}
|
virtual RecordId read (ESM::ESMReader& reader) { return RecordId(); }
|
||||||
///< Read into dynamic storage
|
///< Read into dynamic storage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,9 +188,9 @@ namespace MWWorld
|
||||||
bool erase(const std::string &id);
|
bool erase(const std::string &id);
|
||||||
bool erase(const T &item);
|
bool erase(const T &item);
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id);
|
RecordId load(ESM::ESMReader &esm);
|
||||||
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
||||||
void read(ESM::ESMReader& reader, const std::string& id);
|
RecordId read(ESM::ESMReader& reader);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -205,8 +213,8 @@ namespace MWWorld
|
||||||
size_t getSize() const;
|
size_t getSize() const;
|
||||||
size_t getSize(size_t plugin) const;
|
size_t getSize(size_t plugin) const;
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id, size_t plugin);
|
RecordId load(ESM::ESMReader &esm, size_t plugin);
|
||||||
void load(ESM::ESMReader &esm, const std::string &id);
|
RecordId load(ESM::ESMReader &esm);
|
||||||
|
|
||||||
iterator begin(size_t plugin) const;
|
iterator begin(size_t plugin) const;
|
||||||
iterator end(size_t plugin) const;
|
iterator end(size_t plugin) const;
|
||||||
|
@ -231,7 +239,7 @@ namespace MWWorld
|
||||||
ESM::Land *search(int x, int y) const;
|
ESM::Land *search(int x, int y) const;
|
||||||
ESM::Land *find(int x, int y) const;
|
ESM::Land *find(int x, int y) const;
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id);
|
RecordId load(ESM::ESMReader &esm);
|
||||||
void setUp();
|
void setUp();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -281,7 +289,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id);
|
RecordId load(ESM::ESMReader &esm);
|
||||||
|
|
||||||
iterator intBegin() const;
|
iterator intBegin() const;
|
||||||
iterator intEnd() const;
|
iterator intEnd() const;
|
||||||
|
@ -323,7 +331,7 @@ namespace MWWorld
|
||||||
Store();
|
Store();
|
||||||
|
|
||||||
void setCells(Store<ESM::Cell>& cells);
|
void setCells(Store<ESM::Cell>& cells);
|
||||||
void load(ESM::ESMReader &esm, const std::string &id);
|
RecordId load(ESM::ESMReader &esm);
|
||||||
size_t getSize() const;
|
size_t getSize() const;
|
||||||
|
|
||||||
void setUp();
|
void setUp();
|
||||||
|
|
|
@ -4,8 +4,11 @@ if (GTEST_FOUND)
|
||||||
include_directories(${GTEST_INCLUDE_DIRS})
|
include_directories(${GTEST_INCLUDE_DIRS})
|
||||||
|
|
||||||
file(GLOB UNITTEST_SRC_FILES
|
file(GLOB UNITTEST_SRC_FILES
|
||||||
components/misc/test_*.cpp
|
../openmw/mwworld/store.cpp
|
||||||
mwdialogue/test_*.cpp
|
../openmw/mwworld/esmstore.cpp
|
||||||
|
mwworld/test_store.cpp
|
||||||
|
|
||||||
|
mwdialogue/test_keywordsearch.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
source_group(apps\\openmw_test_suite FILES openmw_test_suite.cpp ${UNITTEST_SRC_FILES})
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include "components/misc/stringops.hpp"
|
|
||||||
|
|
||||||
struct StringOpsTest : public ::testing::Test
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
virtual void SetUp()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void TearDown()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
315
apps/openmw_test_suite/mwworld/test_store.cpp
Normal file
315
apps/openmw_test_suite/mwworld/test_store.cpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/esm/esmwriter.hpp>
|
||||||
|
#include <components/loadinglistener/loadinglistener.hpp>
|
||||||
|
|
||||||
|
#include "apps/openmw/mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
static Loading::Listener dummyListener;
|
||||||
|
|
||||||
|
/// Base class for tests of ESMStore that rely on external content files to produce the test results
|
||||||
|
struct ContentFileTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void SetUp()
|
||||||
|
{
|
||||||
|
readContentFiles();
|
||||||
|
|
||||||
|
// load the content files
|
||||||
|
std::vector<ESM::ESMReader> readerList;
|
||||||
|
readerList.resize(mContentFiles.size());
|
||||||
|
|
||||||
|
int index=0;
|
||||||
|
for (std::vector<boost::filesystem::path>::const_iterator it = mContentFiles.begin(); it != mContentFiles.end(); ++it)
|
||||||
|
{
|
||||||
|
ESM::ESMReader lEsm;
|
||||||
|
lEsm.setEncoder(NULL);
|
||||||
|
lEsm.setIndex(index);
|
||||||
|
lEsm.setGlobalReaderList(&readerList);
|
||||||
|
lEsm.open(it->string());
|
||||||
|
readerList[index] = lEsm;
|
||||||
|
mEsmStore.load(readerList[index], &dummyListener);
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
mEsmStore.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// read absolute path to content files from openmw.cfg
|
||||||
|
void readContentFiles()
|
||||||
|
{
|
||||||
|
boost::program_options::variables_map variables;
|
||||||
|
|
||||||
|
boost::program_options::options_description desc("Allowed options");
|
||||||
|
desc.add_options()
|
||||||
|
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing())
|
||||||
|
("content", boost::program_options::value<std::vector<std::string> >()->default_value(std::vector<std::string>(), "")
|
||||||
|
->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
|
||||||
|
("data-local", boost::program_options::value<std::string>()->default_value(""));
|
||||||
|
|
||||||
|
boost::program_options::notify(variables);
|
||||||
|
|
||||||
|
mConfigurationManager.readConfiguration(variables, desc, true);
|
||||||
|
|
||||||
|
Files::PathContainer dataDirs, dataLocal;
|
||||||
|
if (!variables["data"].empty()) {
|
||||||
|
dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
|
if (!local.empty()) {
|
||||||
|
dataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
mConfigurationManager.processPaths (dataDirs);
|
||||||
|
mConfigurationManager.processPaths (dataLocal, true);
|
||||||
|
|
||||||
|
if (!dataLocal.empty())
|
||||||
|
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
||||||
|
|
||||||
|
Files::Collections collections (dataDirs, true);
|
||||||
|
|
||||||
|
std::vector<std::string> contentFiles = variables["content"].as<std::vector<std::string> >();
|
||||||
|
for (std::vector<std::string>::iterator it = contentFiles.begin(); it != contentFiles.end(); ++it)
|
||||||
|
mContentFiles.push_back(collections.getPath(*it));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Files::ConfigurationManager mConfigurationManager;
|
||||||
|
MWWorld::ESMStore mEsmStore;
|
||||||
|
std::vector<boost::filesystem::path> mContentFiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Print results of the dialogue merging process, i.e. the resulting linked list.
|
||||||
|
TEST_F(ContentFileTest, dialogue_merging_test)
|
||||||
|
{
|
||||||
|
if (mContentFiles.empty())
|
||||||
|
{
|
||||||
|
std::cout << "No content files found, skipping test" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string file = "test_dialogue_merging.txt";
|
||||||
|
|
||||||
|
boost::filesystem::ofstream stream;
|
||||||
|
stream.open(file);
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::Dialogue>& dialStore = mEsmStore.get<ESM::Dialogue>();
|
||||||
|
for (MWWorld::Store<ESM::Dialogue>::iterator it = dialStore.begin(); it != dialStore.end(); ++it)
|
||||||
|
{
|
||||||
|
const ESM::Dialogue& dial = *it;
|
||||||
|
stream << "Dialogue: " << dial.mId << std::endl;
|
||||||
|
|
||||||
|
for (ESM::Dialogue::InfoContainer::const_iterator infoIt = dial.mInfo.begin(); infoIt != dial.mInfo.end(); ++infoIt)
|
||||||
|
{
|
||||||
|
const ESM::DialInfo& info = *infoIt;
|
||||||
|
stream << info.mId << std::endl;
|
||||||
|
}
|
||||||
|
stream << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "dialogue_merging_test successful, results printed to " << file << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: here we don't test records that don't use string names (e.g. Land, Pathgrid, Cell)
|
||||||
|
#define RUN_TEST_FOR_TYPES(func, arg1, arg2) \
|
||||||
|
func<ESM::Activator>(arg1, arg2); \
|
||||||
|
func<ESM::Apparatus>(arg1, arg2); \
|
||||||
|
func<ESM::Armor>(arg1, arg2); \
|
||||||
|
func<ESM::BirthSign>(arg1, arg2); \
|
||||||
|
func<ESM::BodyPart>(arg1, arg2); \
|
||||||
|
func<ESM::Book>(arg1, arg2); \
|
||||||
|
func<ESM::Class>(arg1, arg2); \
|
||||||
|
func<ESM::Clothing>(arg1, arg2); \
|
||||||
|
func<ESM::Container>(arg1, arg2); \
|
||||||
|
func<ESM::Creature>(arg1, arg2); \
|
||||||
|
func<ESM::CreatureLevList>(arg1, arg2); \
|
||||||
|
func<ESM::Dialogue>(arg1, arg2); \
|
||||||
|
func<ESM::Door>(arg1, arg2); \
|
||||||
|
func<ESM::Enchantment>(arg1, arg2); \
|
||||||
|
func<ESM::Faction>(arg1, arg2); \
|
||||||
|
func<ESM::GameSetting>(arg1, arg2); \
|
||||||
|
func<ESM::Global>(arg1, arg2); \
|
||||||
|
func<ESM::Ingredient>(arg1, arg2); \
|
||||||
|
func<ESM::ItemLevList>(arg1, arg2); \
|
||||||
|
func<ESM::Light>(arg1, arg2); \
|
||||||
|
func<ESM::Lockpick>(arg1, arg2); \
|
||||||
|
func<ESM::Miscellaneous>(arg1, arg2); \
|
||||||
|
func<ESM::NPC>(arg1, arg2); \
|
||||||
|
func<ESM::Potion>(arg1, arg2); \
|
||||||
|
func<ESM::Probe>(arg1, arg2); \
|
||||||
|
func<ESM::Race>(arg1, arg2); \
|
||||||
|
func<ESM::Region>(arg1, arg2); \
|
||||||
|
func<ESM::Repair>(arg1, arg2); \
|
||||||
|
func<ESM::Script>(arg1, arg2); \
|
||||||
|
func<ESM::Sound>(arg1, arg2); \
|
||||||
|
func<ESM::SoundGenerator>(arg1, arg2); \
|
||||||
|
func<ESM::Spell>(arg1, arg2); \
|
||||||
|
func<ESM::StartScript>(arg1, arg2); \
|
||||||
|
func<ESM::Weapon>(arg1, arg2);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void printRecords(MWWorld::ESMStore& esmStore, std::ostream& outStream)
|
||||||
|
{
|
||||||
|
const MWWorld::Store<T>& store = esmStore.get<T>();
|
||||||
|
outStream << store.getSize() << " " << T::getRecordType() << " records" << std::endl;
|
||||||
|
|
||||||
|
for (typename MWWorld::Store<T>::iterator it = store.begin(); it != store.end(); ++it)
|
||||||
|
{
|
||||||
|
const T& record = *it;
|
||||||
|
outStream << record.mId << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
outStream << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print some basic diagnostics about the loaded content files, e.g. number of records and names of those records
|
||||||
|
/// Also used to test the iteration order of records
|
||||||
|
TEST_F(ContentFileTest, content_diagnostics_test)
|
||||||
|
{
|
||||||
|
if (mContentFiles.empty())
|
||||||
|
{
|
||||||
|
std::cout << "No content files found, skipping test" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string file = "test_content_diagnostics.txt";
|
||||||
|
|
||||||
|
boost::filesystem::ofstream stream;
|
||||||
|
stream.open(file);
|
||||||
|
|
||||||
|
RUN_TEST_FOR_TYPES(printRecords, mEsmStore, stream);
|
||||||
|
|
||||||
|
std::cout << "diagnostics_test successful, results printed to " << file << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
/// Print results of autocalculated NPC spell lists. Also serves as test for attribute/skill autocalculation which the spell autocalculation heavily relies on
|
||||||
|
/// - even incorrect rounding modes can completely change the resulting spell lists.
|
||||||
|
/*
|
||||||
|
TEST_F(ContentFileTest, autocalc_test)
|
||||||
|
{
|
||||||
|
if (mContentFiles.empty())
|
||||||
|
{
|
||||||
|
std::cout << "No content files found, skipping test" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Base class for tests of ESMStore that do not rely on external content files
|
||||||
|
struct StoreTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
MWWorld::ESMStore mEsmStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Create an ESM file in-memory containing the specified record.
|
||||||
|
/// @param deleted Write record with deleted flag?
|
||||||
|
template <typename T>
|
||||||
|
Files::IStreamPtr getEsmFile(T record, bool deleted)
|
||||||
|
{
|
||||||
|
ESM::ESMWriter writer;
|
||||||
|
std::stringstream* stream = new std::stringstream;
|
||||||
|
writer.setFormat(0);
|
||||||
|
writer.save(*stream);
|
||||||
|
writer.startRecord(T::sRecordId);
|
||||||
|
record.save(writer, deleted);
|
||||||
|
writer.endRecord(T::sRecordId);
|
||||||
|
|
||||||
|
return Files::IStreamPtr(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests deletion of records.
|
||||||
|
TEST_F(StoreTest, delete_test)
|
||||||
|
{
|
||||||
|
const std::string recordId = "foobar";
|
||||||
|
|
||||||
|
typedef ESM::Apparatus RecordType;
|
||||||
|
|
||||||
|
RecordType record;
|
||||||
|
record.blank();
|
||||||
|
record.mId = recordId;
|
||||||
|
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
std::vector<ESM::ESMReader> readerList;
|
||||||
|
readerList.push_back(reader);
|
||||||
|
reader.setGlobalReaderList(&readerList);
|
||||||
|
|
||||||
|
// master file inserts a record
|
||||||
|
Files::IStreamPtr file = getEsmFile(record, false);
|
||||||
|
reader.open(file, "filename");
|
||||||
|
mEsmStore.load(reader, &dummyListener);
|
||||||
|
mEsmStore.setUp();
|
||||||
|
|
||||||
|
ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 1);
|
||||||
|
|
||||||
|
// now a plugin deletes it
|
||||||
|
file = getEsmFile(record, true);
|
||||||
|
reader.open(file, "filename");
|
||||||
|
mEsmStore.load(reader, &dummyListener);
|
||||||
|
mEsmStore.setUp();
|
||||||
|
|
||||||
|
ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 0);
|
||||||
|
|
||||||
|
// now another plugin inserts it again
|
||||||
|
// expected behaviour is the record to reappear rather than staying deleted
|
||||||
|
file = getEsmFile(record, false);
|
||||||
|
reader.open(file, "filename");
|
||||||
|
mEsmStore.load(reader, &dummyListener);
|
||||||
|
mEsmStore.setUp();
|
||||||
|
|
||||||
|
ASSERT_TRUE (mEsmStore.get<RecordType>().getSize() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests overwriting of records.
|
||||||
|
TEST_F(StoreTest, overwrite_test)
|
||||||
|
{
|
||||||
|
const std::string recordId = "foobar";
|
||||||
|
const std::string recordIdUpper = "Foobar";
|
||||||
|
|
||||||
|
typedef ESM::Apparatus RecordType;
|
||||||
|
|
||||||
|
RecordType record;
|
||||||
|
record.blank();
|
||||||
|
record.mId = recordId;
|
||||||
|
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
std::vector<ESM::ESMReader> readerList;
|
||||||
|
readerList.push_back(reader);
|
||||||
|
reader.setGlobalReaderList(&readerList);
|
||||||
|
|
||||||
|
// master file inserts a record
|
||||||
|
Files::IStreamPtr file = getEsmFile(record, false);
|
||||||
|
reader.open(file, "filename");
|
||||||
|
mEsmStore.load(reader, &dummyListener);
|
||||||
|
mEsmStore.setUp();
|
||||||
|
|
||||||
|
// now a plugin overwrites it with changed data
|
||||||
|
record.mId = recordIdUpper; // change id to uppercase, to test case smashing while we're at it
|
||||||
|
record.mModel = "the_new_model";
|
||||||
|
file = getEsmFile(record, false);
|
||||||
|
reader.open(file, "filename");
|
||||||
|
mEsmStore.load(reader, &dummyListener);
|
||||||
|
mEsmStore.setUp();
|
||||||
|
|
||||||
|
// verify that changes were actually applied
|
||||||
|
const RecordType* overwrittenRec = mEsmStore.get<RecordType>().search(recordId);
|
||||||
|
|
||||||
|
ASSERT_TRUE (overwrittenRec != NULL);
|
||||||
|
|
||||||
|
ASSERT_TRUE (overwrittenRec && overwrittenRec->mModel == "the_new_model");
|
||||||
|
}
|
|
@ -24,10 +24,10 @@ void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum)
|
void ESM::CellRef::load (ESMReader& esm, bool &isDeleted, bool wideRefNum)
|
||||||
{
|
{
|
||||||
loadId(esm, wideRefNum);
|
loadId(esm, wideRefNum);
|
||||||
loadData(esm);
|
loadData(esm, isDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)
|
void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)
|
||||||
|
@ -39,71 +39,98 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum)
|
||||||
if (esm.isNextSub ("NAM0"))
|
if (esm.isNextSub ("NAM0"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
|
blank();
|
||||||
|
|
||||||
mRefNum.load (esm, wideRefNum);
|
mRefNum.load (esm, wideRefNum);
|
||||||
|
|
||||||
mRefID = esm.getHNString ("NAME");
|
mRefID = esm.getHNString ("NAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::CellRef::loadData(ESMReader &esm)
|
void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
// Again, UNAM sometimes appears after NAME and sometimes later.
|
isDeleted = false;
|
||||||
// Or perhaps this UNAM means something different?
|
|
||||||
mReferenceBlocked = -1;
|
|
||||||
esm.getHNOT (mReferenceBlocked, "UNAM");
|
|
||||||
|
|
||||||
mScale = 1.0;
|
bool isLoaded = false;
|
||||||
esm.getHNOT (mScale, "XSCL");
|
while (!isLoaded && esm.hasMoreSubs())
|
||||||
|
|
||||||
mOwner = esm.getHNOString ("ANAM");
|
|
||||||
mGlobalVariable = esm.getHNOString ("BNAM");
|
|
||||||
mSoul = esm.getHNOString ("XSOL");
|
|
||||||
|
|
||||||
mFaction = esm.getHNOString ("CNAM");
|
|
||||||
mFactionRank = -2;
|
|
||||||
esm.getHNOT (mFactionRank, "INDX");
|
|
||||||
|
|
||||||
mGoldValue = 1;
|
|
||||||
mChargeInt = -1;
|
|
||||||
mEnchantmentCharge = -1;
|
|
||||||
|
|
||||||
esm.getHNOT (mEnchantmentCharge, "XCHG");
|
|
||||||
|
|
||||||
esm.getHNOT (mChargeInt, "INTV");
|
|
||||||
|
|
||||||
esm.getHNOT (mGoldValue, "NAM9");
|
|
||||||
|
|
||||||
// Present for doors that teleport you to another cell.
|
|
||||||
if (esm.isNextSub ("DODT"))
|
|
||||||
{
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::FourCC<'U','N','A','M'>::value:
|
||||||
|
esm.getHT(mReferenceBlocked);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'X','S','C','L'>::value:
|
||||||
|
esm.getHT(mScale);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'A','N','A','M'>::value:
|
||||||
|
mOwner = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'B','N','A','M'>::value:
|
||||||
|
mGlobalVariable = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'X','S','O','L'>::value:
|
||||||
|
mSoul = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'C','N','A','M'>::value:
|
||||||
|
mFaction = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'I','N','D','X'>::value:
|
||||||
|
esm.getHT(mFactionRank);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'X','C','H','G'>::value:
|
||||||
|
esm.getHT(mEnchantmentCharge);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'I','N','T','V'>::value:
|
||||||
|
esm.getHT(mChargeInt);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'N','A','M','9'>::value:
|
||||||
|
esm.getHT(mGoldValue);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','O','D','T'>::value:
|
||||||
|
esm.getHT(mDoorDest);
|
||||||
mTeleport = true;
|
mTeleport = true;
|
||||||
esm.getHT (mDoorDest);
|
break;
|
||||||
mDestCell = esm.getHNOString ("DNAM");
|
case ESM::FourCC<'D','N','A','M'>::value:
|
||||||
|
mDestCell = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'F','L','T','V'>::value:
|
||||||
|
esm.getHT(mLockLevel);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'K','N','A','M'>::value:
|
||||||
|
mKey = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'T','N','A','M'>::value:
|
||||||
|
mTrap = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
esm.getHT(mPos, 24);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'N','A','M','0'>::value:
|
||||||
|
esm.skipHSub();
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.cacheSubName();
|
||||||
|
isLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
mTeleport = false;
|
|
||||||
|
|
||||||
mLockLevel = 0; //Set to 0 to indicate no lock
|
|
||||||
esm.getHNOT (mLockLevel, "FLTV");
|
|
||||||
|
|
||||||
mKey = esm.getHNOString ("KNAM");
|
|
||||||
mTrap = esm.getHNOString ("TNAM");
|
|
||||||
|
|
||||||
esm.getHNOT (mReferenceBlocked, "UNAM");
|
|
||||||
if (esm.isNextSub("FLTV")) // no longer used
|
|
||||||
esm.skipHSub();
|
|
||||||
|
|
||||||
esm.getHNOT(mPos, "DATA", 24);
|
|
||||||
|
|
||||||
if (esm.isNextSub("NAM0"))
|
|
||||||
esm.skipHSub();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const
|
void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool isDeleted) const
|
||||||
{
|
{
|
||||||
mRefNum.save (esm, wideRefNum);
|
mRefNum.save (esm, wideRefNum);
|
||||||
|
|
||||||
esm.writeHNCString("NAME", mRefID);
|
esm.writeHNCString("NAME", mRefID);
|
||||||
|
|
||||||
|
if (isDeleted) {
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mScale != 1.0) {
|
if (mScale != 1.0) {
|
||||||
esm.writeHNT("XSCL", mScale);
|
esm.writeHNT("XSCL", mScale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ namespace ESM
|
||||||
class CellRef
|
class CellRef
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Reference number
|
// Reference number
|
||||||
// Note: Currently unused for items in containers
|
// Note: Currently unused for items in containers
|
||||||
RefNum mRefNum;
|
RefNum mRefNum;
|
||||||
|
@ -100,14 +99,14 @@ namespace ESM
|
||||||
Position mPos;
|
Position mPos;
|
||||||
|
|
||||||
/// Calls loadId and loadData
|
/// Calls loadId and loadData
|
||||||
void load (ESMReader& esm, bool wideRefNum = false);
|
void load (ESMReader& esm, bool &isDeleted, bool wideRefNum = false);
|
||||||
|
|
||||||
void loadId (ESMReader& esm, bool wideRefNum = false);
|
void loadId (ESMReader& esm, bool wideRefNum = false);
|
||||||
|
|
||||||
/// Implicitly called by load
|
/// Implicitly called by load
|
||||||
void loadData (ESMReader& esm);
|
void loadData (ESMReader& esm, bool &isDeleted);
|
||||||
|
|
||||||
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false) const;
|
void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,15 +6,48 @@
|
||||||
|
|
||||||
unsigned int ESM::DebugProfile::sRecordId = REC_DBGP;
|
unsigned int ESM::DebugProfile::sRecordId = REC_DBGP;
|
||||||
|
|
||||||
void ESM::DebugProfile::load (ESMReader& esm)
|
void ESM::DebugProfile::load (ESMReader& esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
mDescription = esm.getHNString ("DESC");
|
isDeleted = false;
|
||||||
mScriptText = esm.getHNString ("SCRP");
|
|
||||||
esm.getHNT (mFlags, "FLAG");
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','E','S','C'>::value:
|
||||||
|
mDescription = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'S','C','R','P'>::value:
|
||||||
|
mScriptText = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'F','L','A','G'>::value:
|
||||||
|
esm.getHT(mFlags);
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::DebugProfile::save (ESMWriter& esm) const
|
void ESM::DebugProfile::save (ESMWriter& esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString ("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString ("DESC", mDescription);
|
esm.writeHNCString ("DESC", mDescription);
|
||||||
esm.writeHNCString ("SCRP", mScriptText);
|
esm.writeHNCString ("SCRP", mScriptText);
|
||||||
esm.writeHNT ("FLAG", mFlags);
|
esm.writeHNT ("FLAG", mFlags);
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace ESM
|
||||||
|
|
||||||
unsigned int mFlags;
|
unsigned int mFlags;
|
||||||
|
|
||||||
void load (ESMReader& esm);
|
void load (ESMReader& esm, bool &isDeleted);
|
||||||
void save (ESMWriter& esm) const;
|
void save (ESMWriter& esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
/// Set record to default state (does not touch the ID).
|
/// Set record to default state (does not touch the ID).
|
||||||
void blank();
|
void blank();
|
||||||
|
|
|
@ -133,5 +133,12 @@ enum RecNameInts
|
||||||
REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files
|
REC_DBGP = FourCC<'D','B','G','P'>::value ///< only used in project files
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Common subrecords
|
||||||
|
enum SubRecNameInts
|
||||||
|
{
|
||||||
|
SREC_DELE = ESM::FourCC<'D','E','L','E'>::value,
|
||||||
|
SREC_NAME = ESM::FourCC<'N','A','M','E'>::value
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -187,6 +187,11 @@ bool ESMReader::peekNextSub(const char *name)
|
||||||
return mCtx.subName == name;
|
return mCtx.subName == name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::cacheSubName()
|
||||||
|
{
|
||||||
|
mCtx.subCached = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||||
// slightly.
|
// slightly.
|
||||||
void ESMReader::getSubName()
|
void ESMReader::getSubName()
|
||||||
|
@ -276,6 +281,7 @@ void ESMReader::skipRecord()
|
||||||
{
|
{
|
||||||
skip(mCtx.leftRec);
|
skip(mCtx.leftRec);
|
||||||
mCtx.leftRec = 0;
|
mCtx.leftRec = 0;
|
||||||
|
mCtx.subCached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMReader::getRecHeader(uint32_t &flags)
|
void ESMReader::getRecHeader(uint32_t &flags)
|
||||||
|
|
|
@ -185,6 +185,9 @@ public:
|
||||||
|
|
||||||
bool peekNextSub(const char* name);
|
bool peekNextSub(const char* name);
|
||||||
|
|
||||||
|
// Store the current subrecord name for the next call of getSubName()
|
||||||
|
void cacheSubName();
|
||||||
|
|
||||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||||
// slightly.
|
// slightly.
|
||||||
void getSubName();
|
void getSubName();
|
||||||
|
|
|
@ -6,14 +6,46 @@
|
||||||
|
|
||||||
unsigned int ESM::Filter::sRecordId = REC_FILT;
|
unsigned int ESM::Filter::sRecordId = REC_FILT;
|
||||||
|
|
||||||
void ESM::Filter::load (ESMReader& esm)
|
void ESM::Filter::load (ESMReader& esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
mFilter = esm.getHNString ("FILT");
|
isDeleted = false;
|
||||||
mDescription = esm.getHNString ("DESC");
|
|
||||||
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
uint32_t name = esm.retSubName().val;
|
||||||
|
switch (name)
|
||||||
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'F','I','L','T'>::value:
|
||||||
|
mFilter = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','E','S','C'>::value:
|
||||||
|
mDescription = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::Filter::save (ESMWriter& esm) const
|
void ESM::Filter::save (ESMWriter& esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString ("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString ("FILT", mFilter);
|
esm.writeHNCString ("FILT", mFilter);
|
||||||
esm.writeHNCString ("DESC", mDescription);
|
esm.writeHNCString ("DESC", mDescription);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ namespace ESM
|
||||||
|
|
||||||
std::string mFilter;
|
std::string mFilter;
|
||||||
|
|
||||||
void load (ESMReader& esm);
|
void load (ESMReader& esm, bool &isDeleted);
|
||||||
void save (ESMWriter& esm) const;
|
void save (ESMWriter& esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,14 +8,20 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Activator::sRecordId = REC_ACTI;
|
unsigned int Activator::sRecordId = REC_ACTI;
|
||||||
|
|
||||||
void Activator::load(ESMReader &esm)
|
void Activator::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -25,13 +31,29 @@ namespace ESM
|
||||||
case ESM::FourCC<'S','C','R','I'>::value:
|
case ESM::FourCC<'S','C','R','I'>::value:
|
||||||
mScript = esm.getHString();
|
mScript = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
}
|
}
|
||||||
void Activator::save(ESMWriter &esm) const
|
void Activator::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNOCString("SCRI", mScript);
|
esm.writeHNOCString("SCRI", mScript);
|
||||||
|
|
|
@ -17,8 +17,8 @@ struct Activator
|
||||||
|
|
||||||
std::string mId, mName, mScript, mModel;
|
std::string mId, mName, mScript, mModel;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,16 +8,23 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Potion::sRecordId = REC_ALCH;
|
unsigned int Potion::sRecordId = REC_ALCH;
|
||||||
|
|
||||||
void Potion::load(ESMReader &esm)
|
void Potion::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mEffects.mList.clear();
|
mEffects.mList.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -37,15 +44,31 @@ namespace ESM
|
||||||
case ESM::FourCC<'E','N','A','M'>::value:
|
case ESM::FourCC<'E','N','A','M'>::value:
|
||||||
mEffects.add(esm);
|
mEffects.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
esm.fail("Missing ALDT");
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
|
esm.fail("Missing ALDT subrecord");
|
||||||
}
|
}
|
||||||
void Potion::save(ESMWriter &esm) const
|
void Potion::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("TEXT", mIcon);
|
esm.writeHNOCString("TEXT", mIcon);
|
||||||
esm.writeHNOCString("SCRI", mScript);
|
esm.writeHNOCString("SCRI", mScript);
|
||||||
|
|
|
@ -33,8 +33,8 @@ struct Potion
|
||||||
std::string mId, mName, mModel, mIcon, mScript;
|
std::string mId, mName, mModel, mIcon, mScript;
|
||||||
EffectList mEffects;
|
EffectList mEffects;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Apparatus::sRecordId = REC_APPA;
|
unsigned int Apparatus::sRecordId = REC_APPA;
|
||||||
|
|
||||||
void Apparatus::load(ESMReader &esm)
|
void Apparatus::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -33,22 +39,38 @@ void Apparatus::load(ESMReader &esm)
|
||||||
case ESM::FourCC<'I','T','E','X'>::value:
|
case ESM::FourCC<'I','T','E','X'>::value:
|
||||||
mIcon = esm.getHString();
|
mIcon = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
esm.fail("Missing AADT");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Apparatus::save(ESMWriter &esm) const
|
if (!hasName)
|
||||||
{
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
|
esm.fail("Missing AADT subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Apparatus::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNCString("FNAM", mName);
|
esm.writeHNCString("FNAM", mName);
|
||||||
esm.writeHNT("AADT", mData, 16);
|
esm.writeHNT("AADT", mData, 16);
|
||||||
esm.writeHNOCString("SCRI", mScript);
|
esm.writeHNOCString("SCRI", mScript);
|
||||||
esm.writeHNCString("ITEX", mIcon);
|
esm.writeHNCString("ITEX", mIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Apparatus::blank()
|
void Apparatus::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,8 +38,8 @@ struct Apparatus
|
||||||
AADTstruct mData;
|
AADTstruct mData;
|
||||||
std::string mId, mModel, mIcon, mScript, mName;
|
std::string mId, mModel, mIcon, mScript, mName;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -38,16 +38,23 @@ namespace ESM
|
||||||
|
|
||||||
unsigned int Armor::sRecordId = REC_ARMO;
|
unsigned int Armor::sRecordId = REC_ARMO;
|
||||||
|
|
||||||
void Armor::load(ESMReader &esm)
|
void Armor::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mParts.mParts.clear();
|
mParts.mParts.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -70,16 +77,32 @@ namespace ESM
|
||||||
case ESM::FourCC<'I','N','D','X'>::value:
|
case ESM::FourCC<'I','N','D','X'>::value:
|
||||||
mParts.add(esm);
|
mParts.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing CTDT subrecord");
|
esm.fail("Missing CTDT subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Armor::save(ESMWriter &esm) const
|
void Armor::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNOCString("SCRI", mScript);
|
esm.writeHNOCString("SCRI", mScript);
|
||||||
|
|
|
@ -96,8 +96,8 @@ struct Armor
|
||||||
|
|
||||||
std::string mId, mName, mModel, mIcon, mScript, mEnchant;
|
std::string mId, mName, mModel, mIcon, mScript, mEnchant;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,16 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int BodyPart::sRecordId = REC_BODY;
|
unsigned int BodyPart::sRecordId = REC_BODY;
|
||||||
|
|
||||||
|
void BodyPart::load(ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
void BodyPart::load(ESMReader &esm)
|
bool hasName = false;
|
||||||
{
|
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -28,20 +33,36 @@ void BodyPart::load(ESMReader &esm)
|
||||||
esm.getHT(mData, 4);
|
esm.getHT(mData, 4);
|
||||||
hasData = true;
|
hasData = true;
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasData)
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing BYDT subrecord");
|
esm.fail("Missing BYDT subrecord");
|
||||||
}
|
}
|
||||||
void BodyPart::save(ESMWriter &esm) const
|
|
||||||
{
|
void BodyPart::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mRace);
|
esm.writeHNOCString("FNAM", mRace);
|
||||||
esm.writeHNT("BYDT", mData, 4);
|
esm.writeHNT("BYDT", mData, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BodyPart::blank()
|
void BodyPart::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,8 +60,8 @@ struct BodyPart
|
||||||
BYDTstruct mData;
|
BYDTstruct mData;
|
||||||
std::string mId, mModel, mRace;
|
std::string mId, mModel, mRace;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Book::sRecordId = REC_BOOK;
|
unsigned int Book::sRecordId = REC_BOOK;
|
||||||
|
|
||||||
void Book::load(ESMReader &esm)
|
void Book::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -39,15 +45,31 @@ namespace ESM
|
||||||
case ESM::FourCC<'T','E','X','T'>::value:
|
case ESM::FourCC<'T','E','X','T'>::value:
|
||||||
mText = esm.getHString();
|
mText = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing BKDT subrecord");
|
esm.fail("Missing BKDT subrecord");
|
||||||
}
|
}
|
||||||
void Book::save(ESMWriter &esm) const
|
void Book::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -28,8 +28,8 @@ struct Book
|
||||||
std::string mName, mModel, mIcon, mScript, mEnchant, mText;
|
std::string mName, mModel, mIcon, mScript, mEnchant, mText;
|
||||||
std::string mId;
|
std::string mId;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,22 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int BirthSign::sRecordId = REC_BSGN;
|
unsigned int BirthSign::sRecordId = REC_BSGN;
|
||||||
|
|
||||||
void BirthSign::load(ESMReader &esm)
|
void BirthSign::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mPowers.mList.clear();
|
mPowers.mList.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'F','N','A','M'>::value:
|
case ESM::FourCC<'F','N','A','M'>::value:
|
||||||
mName = esm.getHString();
|
mName = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -29,20 +36,35 @@ void BirthSign::load(ESMReader &esm)
|
||||||
case ESM::FourCC<'N','P','C','S'>::value:
|
case ESM::FourCC<'N','P','C','S'>::value:
|
||||||
mPowers.add(esm);
|
mPowers.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void BirthSign::save(ESMWriter &esm) const
|
if (!hasName)
|
||||||
{
|
esm.fail("Missing NAME subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BirthSign::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNOCString("TNAM", mTexture);
|
esm.writeHNOCString("TNAM", mTexture);
|
||||||
esm.writeHNOCString("DESC", mDescription);
|
esm.writeHNOCString("DESC", mDescription);
|
||||||
|
|
||||||
mPowers.save(esm);
|
mPowers.save(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BirthSign::blank()
|
void BirthSign::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,8 +22,8 @@ struct BirthSign
|
||||||
// List of powers and abilities that come with this birth sign.
|
// List of powers and abilities that come with this birth sign.
|
||||||
SpellList mPowers;
|
SpellList mPowers;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID/index).
|
///< Set record to default state (does not touch the ID/index).
|
||||||
|
|
|
@ -52,79 +52,109 @@ namespace ESM
|
||||||
return ref.mRefNum == refNum;
|
return ref.mRefNum == refNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::load(ESMReader &esm, bool saveContext)
|
void Cell::load(ESMReader &esm, bool &isDeleted, bool saveContext)
|
||||||
{
|
{
|
||||||
loadData(esm);
|
loadNameAndData(esm, isDeleted);
|
||||||
loadCell(esm, saveContext);
|
loadCell(esm, saveContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::loadCell(ESMReader &esm, bool saveContext)
|
void Cell::loadNameAndData(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
mRefNumCounter = 0;
|
isDeleted = false;
|
||||||
|
|
||||||
if (mData.mFlags & Interior)
|
blank();
|
||||||
|
|
||||||
|
bool hasData = false;
|
||||||
|
bool isLoaded = false;
|
||||||
|
while (!isLoaded && esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
// Interior cells
|
esm.getSubName();
|
||||||
if (esm.isNextSub("INTV"))
|
switch (esm.retSubName().val)
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mName = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
esm.getHT(mData, 12);
|
||||||
|
hasData = true;
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.cacheSubName();
|
||||||
|
isLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasData)
|
||||||
|
esm.fail("Missing DATA subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cell::loadCell(ESMReader &esm, bool saveContext)
|
||||||
|
{
|
||||||
|
bool isLoaded = false;
|
||||||
|
while (!isLoaded && esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::FourCC<'I','N','T','V'>::value:
|
||||||
int waterl;
|
int waterl;
|
||||||
esm.getHT(waterl);
|
esm.getHT(waterl);
|
||||||
mWater = (float) waterl;
|
mWater = static_cast<float>(waterl);
|
||||||
mWaterInt = true;
|
mWaterInt = true;
|
||||||
}
|
break;
|
||||||
else if (esm.isNextSub("WHGT"))
|
case ESM::FourCC<'W','H','G','T'>::value:
|
||||||
{
|
|
||||||
esm.getHT(mWater);
|
esm.getHT(mWater);
|
||||||
}
|
mWaterInt = false;
|
||||||
|
break;
|
||||||
// Quasi-exterior cells have a region (which determines the
|
case ESM::FourCC<'A','M','B','I'>::value:
|
||||||
// weather), pure interior cells have ambient lighting
|
|
||||||
// instead.
|
|
||||||
if (mData.mFlags & QuasiEx)
|
|
||||||
mRegion = esm.getHNOString("RGNN");
|
|
||||||
else if (esm.isNextSub("AMBI"))
|
|
||||||
esm.getHT(mAmbi);
|
esm.getHT(mAmbi);
|
||||||
}
|
break;
|
||||||
else
|
case ESM::FourCC<'R','G','N','N'>::value:
|
||||||
{
|
mRegion = esm.getHString();
|
||||||
// Exterior cells
|
break;
|
||||||
mRegion = esm.getHNOString("RGNN");
|
case ESM::FourCC<'N','A','M','5'>::value:
|
||||||
|
esm.getHT(mMapColor);
|
||||||
mMapColor = 0;
|
break;
|
||||||
esm.getHNOT(mMapColor, "NAM5");
|
case ESM::FourCC<'N','A','M','0'>::value:
|
||||||
}
|
|
||||||
if (esm.isNextSub("NAM0")) {
|
|
||||||
esm.getHT(mRefNumCounter);
|
esm.getHT(mRefNumCounter);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.cacheSubName();
|
||||||
|
isLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveContext) {
|
if (saveContext)
|
||||||
|
{
|
||||||
mContextList.push_back(esm.getContext());
|
mContextList.push_back(esm.getContext());
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void Cell::loadData(ESMReader &esm)
|
|
||||||
{
|
|
||||||
// Ignore this for now, it might mean we should delete the entire
|
|
||||||
// cell?
|
|
||||||
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
|
|
||||||
if (esm.isNextSub("DELE")) {
|
|
||||||
esm.skipHSub();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.getHNT(mData, "DATA", 12);
|
void Cell::postLoad(ESMReader &esm)
|
||||||
}
|
{
|
||||||
|
|
||||||
void Cell::postLoad(ESMReader &esm)
|
|
||||||
{
|
|
||||||
// Save position of the cell references and move on
|
// Save position of the cell references and move on
|
||||||
mContextList.push_back(esm.getContext());
|
mContextList.push_back(esm.getContext());
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::save(ESMWriter &esm) const
|
void Cell::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNOCString("NAME", mName);
|
||||||
esm.writeHNT("DATA", mData, 12);
|
esm.writeHNT("DATA", mData, 12);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mData.mFlags & Interior)
|
if (mData.mFlags & Interior)
|
||||||
{
|
{
|
||||||
if (mWaterInt) {
|
if (mWaterInt) {
|
||||||
|
@ -149,15 +179,15 @@ void Cell::save(ESMWriter &esm) const
|
||||||
|
|
||||||
if (mRefNumCounter != 0)
|
if (mRefNumCounter != 0)
|
||||||
esm.writeHNT("NAM0", mRefNumCounter);
|
esm.writeHNT("NAM0", mRefNumCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::restore(ESMReader &esm, int iCtx) const
|
void Cell::restore(ESMReader &esm, int iCtx) const
|
||||||
{
|
{
|
||||||
esm.restoreContext(mContextList.at (iCtx));
|
esm.restoreContext(mContextList.at (iCtx));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cell::getDescription() const
|
std::string Cell::getDescription() const
|
||||||
{
|
{
|
||||||
if (mData.mFlags & Interior)
|
if (mData.mFlags & Interior)
|
||||||
{
|
{
|
||||||
return mName;
|
return mName;
|
||||||
|
@ -168,10 +198,12 @@ std::string Cell::getDescription() const
|
||||||
stream << mData.mX << ", " << mData.mY;
|
stream << mData.mX << ", " << mData.mY;
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref)
|
|
||||||
{
|
|
||||||
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
||||||
if (!esm.hasMoreSubs())
|
if (!esm.hasMoreSubs())
|
||||||
return false;
|
return false;
|
||||||
|
@ -194,31 +226,26 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ref.load (esm);
|
if (esm.peekNextSub("FRMR"))
|
||||||
|
{
|
||||||
|
ref.load (esm, isDeleted);
|
||||||
|
|
||||||
// Identify references belonging to a parent file and adapt the ID accordingly.
|
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||||
adjustRefNum (ref.mRefNum, esm);
|
adjustRefNum (ref.mRefNum, esm);
|
||||||
|
|
||||||
if (esm.isNextSub("DELE"))
|
|
||||||
{
|
|
||||||
esm.skipHSub();
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
deleted = false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
||||||
{
|
{
|
||||||
esm.getHT(mref.mRefNum.mIndex);
|
esm.getHT(mref.mRefNum.mIndex);
|
||||||
esm.getHNOT(mref.mTarget, "CNDT");
|
esm.getHNOT(mref.mTarget, "CNDT");
|
||||||
|
|
||||||
adjustRefNum (mref.mRefNum, esm);
|
adjustRefNum (mref.mRefNum, esm);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::blank()
|
void Cell::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -116,11 +116,11 @@ struct Cell
|
||||||
|
|
||||||
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||||
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||||
void load(ESMReader &esm, bool saveContext = true); // Load everything (except references)
|
void load(ESMReader &esm, bool &isDeleted, bool saveContext = true); // Load everything (except references)
|
||||||
void loadData(ESMReader &esm); // Load DATAstruct only
|
void loadNameAndData(ESMReader &esm, bool &isDeleted); // Load NAME and DATAstruct
|
||||||
void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except DATAstruct and references
|
void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references
|
||||||
|
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
bool isExterior() const
|
bool isExterior() const
|
||||||
{
|
{
|
||||||
|
@ -160,7 +160,10 @@ struct Cell
|
||||||
*/
|
*/
|
||||||
/// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef.
|
/// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef.
|
||||||
static bool getNextRef(ESMReader &esm,
|
static bool getNextRef(ESMReader &esm,
|
||||||
CellRef &ref, bool& deleted, bool ignoreMoves = false, MovedCellRef *mref = 0);
|
CellRef &ref,
|
||||||
|
bool &isDeleted,
|
||||||
|
bool ignoreMoves = false,
|
||||||
|
MovedCellRef *mref = 0);
|
||||||
|
|
||||||
/* This fetches an MVRF record, which is used to track moved references.
|
/* This fetches an MVRF record, which is used to track moved references.
|
||||||
* Since they are comparably rare, we use a separate method for this.
|
* Since they are comparably rare, we use a separate method for this.
|
||||||
|
|
|
@ -22,7 +22,6 @@ namespace ESM
|
||||||
"sSpecializationStealth"
|
"sSpecializationStealth"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
int& Class::CLDTstruct::getSkill (int index, bool major)
|
int& Class::CLDTstruct::getSkill (int index, bool major)
|
||||||
{
|
{
|
||||||
if (index<0 || index>=5)
|
if (index<0 || index>=5)
|
||||||
|
@ -39,15 +38,21 @@ namespace ESM
|
||||||
return mSkills[index][major ? 1 : 0];
|
return mSkills[index][major ? 1 : 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Class::load(ESMReader &esm)
|
void Class::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'F','N','A','M'>::value:
|
case ESM::FourCC<'F','N','A','M'>::value:
|
||||||
mName = esm.getHString();
|
mName = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -60,15 +65,31 @@ namespace ESM
|
||||||
case ESM::FourCC<'D','E','S','C'>::value:
|
case ESM::FourCC<'D','E','S','C'>::value:
|
||||||
mDescription = esm.getHString();
|
mDescription = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing CLDT subrecord");
|
esm.fail("Missing CLDT subrecord");
|
||||||
}
|
}
|
||||||
void Class::save(ESMWriter &esm) const
|
void Class::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNT("CLDT", mData, 60);
|
esm.writeHNT("CLDT", mData, 60);
|
||||||
esm.writeHNOString("DESC", mDescription);
|
esm.writeHNOString("DESC", mDescription);
|
||||||
|
|
|
@ -73,8 +73,8 @@ struct Class
|
||||||
std::string mId, mName, mDescription;
|
std::string mId, mName, mDescription;
|
||||||
CLDTstruct mData;
|
CLDTstruct mData;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID/index).
|
///< Set record to default state (does not touch the ID/index).
|
||||||
|
|
|
@ -8,16 +8,23 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Clothing::sRecordId = REC_CLOT;
|
unsigned int Clothing::sRecordId = REC_CLOT;
|
||||||
|
|
||||||
void Clothing::load(ESMReader &esm)
|
void Clothing::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mParts.mParts.clear();
|
mParts.mParts.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -40,16 +47,32 @@ namespace ESM
|
||||||
case ESM::FourCC<'I','N','D','X'>::value:
|
case ESM::FourCC<'I','N','D','X'>::value:
|
||||||
mParts.add(esm);
|
mParts.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing CTDT subrecord");
|
esm.fail("Missing CTDT subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clothing::save(ESMWriter &esm) const
|
void Clothing::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNT("CTDT", mData, 12);
|
esm.writeHNT("CTDT", mData, 12);
|
||||||
|
|
|
@ -48,8 +48,8 @@ struct Clothing
|
||||||
|
|
||||||
std::string mId, mName, mModel, mIcon, mEnchant, mScript;
|
std::string mId, mName, mModel, mIcon, mEnchant, mScript;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -24,17 +24,24 @@ namespace ESM
|
||||||
|
|
||||||
unsigned int Container::sRecordId = REC_CONT;
|
unsigned int Container::sRecordId = REC_CONT;
|
||||||
|
|
||||||
void Container::load(ESMReader &esm)
|
void Container::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mInventory.mList.clear();
|
mInventory.mList.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasWeight = false;
|
bool hasWeight = false;
|
||||||
bool hasFlags = false;
|
bool hasFlags = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -59,18 +66,34 @@ namespace ESM
|
||||||
case ESM::FourCC<'N','P','C','O'>::value:
|
case ESM::FourCC<'N','P','C','O'>::value:
|
||||||
mInventory.add(esm);
|
mInventory.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasWeight)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasWeight && !isDeleted)
|
||||||
esm.fail("Missing CNDT subrecord");
|
esm.fail("Missing CNDT subrecord");
|
||||||
if (!hasFlags)
|
if (!hasFlags && !isDeleted)
|
||||||
esm.fail("Missing FLAG subrecord");
|
esm.fail("Missing FLAG subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container::save(ESMWriter &esm) const
|
void Container::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNT("CNDT", mWeight, 4);
|
esm.writeHNT("CNDT", mWeight, 4);
|
||||||
|
|
|
@ -52,8 +52,8 @@ struct Container
|
||||||
int mFlags;
|
int mFlags;
|
||||||
InventoryList mInventory;
|
InventoryList mInventory;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,8 +8,10 @@ namespace ESM {
|
||||||
|
|
||||||
unsigned int Creature::sRecordId = REC_CREA;
|
unsigned int Creature::sRecordId = REC_CREA;
|
||||||
|
|
||||||
void Creature::load(ESMReader &esm)
|
void Creature::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mPersistent = (esm.getRecordFlags() & 0x0400) != 0;
|
mPersistent = (esm.getRecordFlags() & 0x0400) != 0;
|
||||||
|
|
||||||
mAiPackage.mList.clear();
|
mAiPackage.mList.clear();
|
||||||
|
@ -19,14 +21,19 @@ namespace ESM {
|
||||||
|
|
||||||
mScale = 1.f;
|
mScale = 1.f;
|
||||||
mHasAI = false;
|
mHasAI = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasNpdt = false;
|
bool hasNpdt = false;
|
||||||
bool hasFlags = false;
|
bool hasFlags = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -72,18 +79,34 @@ namespace ESM {
|
||||||
case AI_CNDT:
|
case AI_CNDT:
|
||||||
mAiPackage.add(esm);
|
mAiPackage.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasNpdt)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasNpdt && !isDeleted)
|
||||||
esm.fail("Missing NPDT subrecord");
|
esm.fail("Missing NPDT subrecord");
|
||||||
if (!hasFlags)
|
if (!hasFlags && !isDeleted)
|
||||||
esm.fail("Missing FLAG subrecord");
|
esm.fail("Missing FLAG subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::save(ESMWriter &esm) const
|
void Creature::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("CNAM", mOriginal);
|
esm.writeHNOCString("CNAM", mOriginal);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
|
|
|
@ -91,7 +91,6 @@ struct Creature
|
||||||
InventoryList mInventory;
|
InventoryList mInventory;
|
||||||
SpellList mSpells;
|
SpellList mSpells;
|
||||||
|
|
||||||
|
|
||||||
bool mHasAI;
|
bool mHasAI;
|
||||||
AIData mAiData;
|
AIData mAiData;
|
||||||
AIPackageList mAiPackage;
|
AIPackageList mAiPackage;
|
||||||
|
@ -99,8 +98,8 @@ struct Creature
|
||||||
|
|
||||||
const std::vector<Transport::Dest>& getTransport() const;
|
const std::vector<Transport::Dest>& getTransport() const;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -10,68 +10,96 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Dialogue::sRecordId = REC_DIAL;
|
unsigned int Dialogue::sRecordId = REC_DIAL;
|
||||||
|
|
||||||
void Dialogue::load(ESMReader &esm)
|
void Dialogue::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
esm.getSubNameIs("DATA");
|
loadId(esm);
|
||||||
|
loadData(esm, isDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dialogue::loadId(ESMReader &esm)
|
||||||
|
{
|
||||||
|
mId = esm.getHNString("NAME");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dialogue::loadData(ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
int si = esm.getSubSize();
|
int size = esm.getSubSize();
|
||||||
if (si == 1)
|
if (size == 1)
|
||||||
|
{
|
||||||
esm.getT(mType);
|
esm.getT(mType);
|
||||||
else if (si == 4)
|
|
||||||
{
|
|
||||||
// These are just markers, their values are not used.
|
|
||||||
int i;
|
|
||||||
esm.getT(i);
|
|
||||||
esm.getHNT(i, "DELE");
|
|
||||||
mType = Deleted;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esm.fail("Unknown sub record size");
|
{
|
||||||
}
|
esm.skip(size);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
mType = Unknown;
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Dialogue::save(ESMWriter &esm) const
|
void Dialogue::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
if (mType != Deleted)
|
esm.writeHNCString("NAME", mId);
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
esm.writeHNT("DATA", mType);
|
esm.writeHNT("DATA", mType);
|
||||||
else
|
|
||||||
{
|
|
||||||
esm.writeHNT("DATA", (int)1);
|
|
||||||
esm.writeHNT("DELE", (int)1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialogue::blank()
|
void Dialogue::blank()
|
||||||
{
|
{
|
||||||
mInfo.clear();
|
mInfo.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialogue::readInfo(ESMReader &esm, bool merge)
|
void Dialogue::readInfo(ESMReader &esm, bool merge)
|
||||||
{
|
|
||||||
const std::string& id = esm.getHNOString("INAM");
|
|
||||||
|
|
||||||
if (!merge || mInfo.empty())
|
|
||||||
{
|
{
|
||||||
ESM::DialInfo info;
|
ESM::DialInfo info;
|
||||||
info.mId = id;
|
info.loadId(esm);
|
||||||
info.load(esm);
|
|
||||||
mLookup[id] = mInfo.insert(mInfo.end(), info);
|
bool isDeleted = false;
|
||||||
|
if (!merge || mInfo.empty())
|
||||||
|
{
|
||||||
|
info.loadData(esm, isDeleted);
|
||||||
|
mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::Dialogue::InfoContainer::iterator it = mInfo.end();
|
InfoContainer::iterator it = mInfo.end();
|
||||||
|
|
||||||
std::map<std::string, ESM::Dialogue::InfoContainer::iterator>::iterator lookup;
|
LookupMap::iterator lookup;
|
||||||
|
lookup = mLookup.find(info.mId);
|
||||||
|
|
||||||
lookup = mLookup.find(id);
|
|
||||||
|
|
||||||
ESM::DialInfo info;
|
|
||||||
if (lookup != mLookup.end())
|
if (lookup != mLookup.end())
|
||||||
{
|
{
|
||||||
it = lookup->second;
|
it = lookup->second.first;
|
||||||
|
|
||||||
// Merge with existing record. Only the subrecords that are present in
|
// Merge with existing record. Only the subrecords that are present in
|
||||||
// the new record will be overwritten.
|
// the new record will be overwritten.
|
||||||
it->load(esm);
|
it->loadData(esm, isDeleted);
|
||||||
info = *it;
|
info = *it;
|
||||||
|
|
||||||
// Since the record merging may have changed the next/prev linked list connection, we need to re-insert the record
|
// Since the record merging may have changed the next/prev linked list connection, we need to re-insert the record
|
||||||
|
@ -80,51 +108,52 @@ void Dialogue::readInfo(ESMReader &esm, bool merge)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info.mId = id;
|
info.loadData(esm, isDeleted);
|
||||||
info.load(esm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.mNext.empty())
|
if (info.mNext.empty())
|
||||||
{
|
{
|
||||||
mLookup[id] = mInfo.insert(mInfo.end(), info);
|
mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.end(), info), isDeleted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (info.mPrev.empty())
|
if (info.mPrev.empty())
|
||||||
{
|
{
|
||||||
mLookup[id] = mInfo.insert(mInfo.begin(), info);
|
mLookup[info.mId] = std::make_pair(mInfo.insert(mInfo.begin(), info), isDeleted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup = mLookup.find(info.mPrev);
|
lookup = mLookup.find(info.mPrev);
|
||||||
if (lookup != mLookup.end())
|
if (lookup != mLookup.end())
|
||||||
{
|
{
|
||||||
it = lookup->second;
|
it = lookup->second.first;
|
||||||
|
|
||||||
mLookup[id] = mInfo.insert(++it, info);
|
mLookup[info.mId] = std::make_pair(mInfo.insert(++it, info), isDeleted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup = mLookup.find(info.mNext);
|
lookup = mLookup.find(info.mNext);
|
||||||
if (lookup != mLookup.end())
|
if (lookup != mLookup.end())
|
||||||
{
|
{
|
||||||
it = lookup->second;
|
it = lookup->second.first;
|
||||||
|
|
||||||
mLookup[id] = mInfo.insert(it, info);
|
mLookup[info.mId] = std::make_pair(mInfo.insert(it, info), isDeleted);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "Failed to insert info " << id << std::endl;
|
std::cerr << "Failed to insert info " << info.mId << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialogue::clearDeletedInfos()
|
void Dialogue::clearDeletedInfos()
|
||||||
{
|
|
||||||
for (InfoContainer::iterator it = mInfo.begin(); it != mInfo.end(); )
|
|
||||||
{
|
{
|
||||||
if (it->mQuestStatus == DialInfo::QS_Deleted)
|
LookupMap::const_iterator current = mLookup.begin();
|
||||||
it = mInfo.erase(it);
|
LookupMap::const_iterator end = mLookup.end();
|
||||||
else
|
for (; current != end; ++current)
|
||||||
++it;
|
{
|
||||||
|
if (current->second.second)
|
||||||
|
{
|
||||||
|
mInfo.erase(current->second.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mLookup.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ struct Dialogue
|
||||||
Greeting = 2,
|
Greeting = 2,
|
||||||
Persuasion = 3,
|
Persuasion = 3,
|
||||||
Journal = 4,
|
Journal = 4,
|
||||||
Deleted = -1
|
Unknown = -1 // Used for deleted dialogues
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string mId;
|
std::string mId;
|
||||||
|
@ -39,17 +39,24 @@ struct Dialogue
|
||||||
|
|
||||||
typedef std::list<DialInfo> InfoContainer;
|
typedef std::list<DialInfo> InfoContainer;
|
||||||
|
|
||||||
typedef std::map<std::string, InfoContainer::iterator> LookupMap;
|
// Parameters: Info ID, (Info iterator, Deleted flag)
|
||||||
|
typedef std::map<std::string, std::pair<InfoContainer::iterator, bool> > LookupMap;
|
||||||
|
|
||||||
InfoContainer mInfo;
|
InfoContainer mInfo;
|
||||||
|
|
||||||
// This is only used during the loading phase to speed up DialInfo merging.
|
// This is only used during the loading phase to speed up DialInfo merging.
|
||||||
LookupMap mLookup;
|
LookupMap mLookup;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
///< Loads all sub-records of Dialogue record
|
||||||
|
void loadId(ESMReader &esm);
|
||||||
|
///< Loads NAME sub-record of Dialogue record
|
||||||
|
void loadData(ESMReader &esm, bool &isDeleted);
|
||||||
|
///< Loads all sub-records of Dialogue record, except NAME sub-record
|
||||||
|
|
||||||
/// Remove all INFOs marked as QS_Deleted from mInfos.
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
|
/// Remove all INFOs that are deleted
|
||||||
void clearDeletedInfos();
|
void clearDeletedInfos();
|
||||||
|
|
||||||
/// Read the next info record
|
/// Read the next info record
|
||||||
|
|
|
@ -8,14 +8,20 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Door::sRecordId = REC_DOOR;
|
unsigned int Door::sRecordId = REC_DOOR;
|
||||||
|
|
||||||
void Door::load(ESMReader &esm)
|
void Door::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -31,14 +37,30 @@ namespace ESM
|
||||||
case ESM::FourCC<'A','N','A','M'>::value:
|
case ESM::FourCC<'A','N','A','M'>::value:
|
||||||
mCloseSound = esm.getHString();
|
mCloseSound = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Door::save(ESMWriter &esm) const
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Door::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNOCString("SCRI", mScript);
|
esm.writeHNOCString("SCRI", mScript);
|
||||||
|
|
|
@ -17,8 +17,8 @@ struct Door
|
||||||
|
|
||||||
std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound;
|
std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,16 +8,22 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Enchantment::sRecordId = REC_ENCH;
|
unsigned int Enchantment::sRecordId = REC_ENCH;
|
||||||
|
|
||||||
void Enchantment::load(ESMReader &esm)
|
void Enchantment::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
mEffects.mList.clear();
|
mEffects.mList.clear();
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'E','N','D','T'>::value:
|
case ESM::FourCC<'E','N','D','T'>::value:
|
||||||
esm.getHT(mData, 16);
|
esm.getHT(mData, 16);
|
||||||
hasData = true;
|
hasData = true;
|
||||||
|
@ -25,20 +31,35 @@ void Enchantment::load(ESMReader &esm)
|
||||||
case ESM::FourCC<'E','N','A','M'>::value:
|
case ESM::FourCC<'E','N','A','M'>::value:
|
||||||
mEffects.add(esm);
|
mEffects.add(esm);
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
esm.fail("Missing ENDT subrecord");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Enchantment::save(ESMWriter &esm) const
|
if (!hasName)
|
||||||
{
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
|
esm.fail("Missing ENDT subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enchantment::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNT("ENDT", mData, 16);
|
esm.writeHNT("ENDT", mData, 16);
|
||||||
mEffects.save(esm);
|
mEffects.save(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Enchantment::blank()
|
void Enchantment::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,8 +42,8 @@ struct Enchantment
|
||||||
ENDTstruct mData;
|
ENDTstruct mData;
|
||||||
EffectList mEffects;
|
EffectList mEffects;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -26,20 +26,26 @@ namespace ESM
|
||||||
return mSkills[index];
|
return mSkills[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Faction::load(ESMReader &esm)
|
void Faction::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mReactions.clear();
|
mReactions.clear();
|
||||||
for (int i=0;i<10;++i)
|
for (int i=0;i<10;++i)
|
||||||
mRanks[i].clear();
|
mRanks[i].clear();
|
||||||
|
|
||||||
int rankCounter=0;
|
int rankCounter = 0;
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'F','N','A','M'>::value:
|
case ESM::FourCC<'F','N','A','M'>::value:
|
||||||
mName = esm.getHString();
|
mName = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -62,15 +68,32 @@ void Faction::load(ESMReader &esm)
|
||||||
mReactions[faction] = reaction;
|
mReactions[faction] = reaction;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing FADT subrecord");
|
esm.fail("Missing FADT subrecord");
|
||||||
}
|
}
|
||||||
void Faction::save(ESMWriter &esm) const
|
|
||||||
{
|
void Faction::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
|
@ -88,7 +111,7 @@ void Faction::save(ESMWriter &esm) const
|
||||||
esm.writeHNString("ANAM", it->first);
|
esm.writeHNString("ANAM", it->first);
|
||||||
esm.writeHNT("INTV", it->second);
|
esm.writeHNT("INTV", it->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Faction::blank()
|
void Faction::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,8 +62,8 @@ struct Faction
|
||||||
// Name of faction ranks (may be empty for NPC factions)
|
// Name of faction ranks (may be empty for NPC factions)
|
||||||
std::string mRanks[10];
|
std::string mRanks[10];
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID/index).
|
///< Set record to default state (does not touch the ID/index).
|
||||||
|
|
|
@ -1,20 +1,43 @@
|
||||||
#include "loadglob.hpp"
|
#include "loadglob.hpp"
|
||||||
|
|
||||||
|
#include "esmreader.hpp"
|
||||||
|
#include "esmwriter.hpp"
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Global::sRecordId = REC_GLOB;
|
unsigned int Global::sRecordId = REC_GLOB;
|
||||||
|
|
||||||
void Global::load (ESMReader &esm)
|
void Global::load (ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
mId = esm.getHNString ("NAME");
|
||||||
|
|
||||||
|
if (esm.isNextSub ("DELE"))
|
||||||
|
{
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mValue.read (esm, ESM::Variant::Format_Global);
|
mValue.read (esm, ESM::Variant::Format_Global);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Global::save (ESMWriter &esm) const
|
void Global::save (ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString ("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString ("DELE", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
mValue.write (esm, ESM::Variant::Format_Global);
|
mValue.write (esm, ESM::Variant::Format_Global);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Global::blank()
|
void Global::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,8 +24,8 @@ struct Global
|
||||||
std::string mId;
|
std::string mId;
|
||||||
Variant mValue;
|
Variant mValue;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
#include "loadgmst.hpp"
|
#include "loadgmst.hpp"
|
||||||
|
|
||||||
|
#include "esmreader.hpp"
|
||||||
|
#include "esmwriter.hpp"
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int GameSetting::sRecordId = REC_GMST;
|
unsigned int GameSetting::sRecordId = REC_GMST;
|
||||||
|
|
||||||
void GameSetting::load (ESMReader &esm)
|
void GameSetting::load (ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false; // GameSetting record can't be deleted now (may be changed in the future)
|
||||||
|
|
||||||
|
mId = esm.getHNString("NAME");
|
||||||
mValue.read (esm, ESM::Variant::Format_Gmst);
|
mValue.read (esm, ESM::Variant::Format_Gmst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameSetting::save (ESMWriter &esm) const
|
void GameSetting::save (ESMWriter &esm, bool /*isDeleted*/) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
mValue.write (esm, ESM::Variant::Format_Gmst);
|
mValue.write (esm, ESM::Variant::Format_Gmst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct GameSetting
|
||||||
|
|
||||||
Variant mValue;
|
Variant mValue;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
|
|
||||||
/// \todo remove the get* functions (redundant, since mValue has equivalent functions now).
|
/// \todo remove the get* functions (redundant, since mValue has equivalent functions now).
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ struct GameSetting
|
||||||
std::string getString() const;
|
std::string getString() const;
|
||||||
///< Throwns an exception if GMST is not of type string.
|
///< Throwns an exception if GMST is not of type string.
|
||||||
|
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,8 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int DialInfo::sRecordId = REC_INFO;
|
unsigned int DialInfo::sRecordId = REC_INFO;
|
||||||
|
|
||||||
void DialInfo::load(ESMReader &esm)
|
void DialInfo::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
loadId(esm);
|
||||||
|
loadData(esm, isDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialInfo::loadId(ESMReader &esm)
|
||||||
|
{
|
||||||
|
mId = esm.getHNString("INAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialInfo::loadData(ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
mQuestStatus = QS_None;
|
mQuestStatus = QS_None;
|
||||||
mFactionLess = false;
|
mFactionLess = false;
|
||||||
|
|
||||||
|
@ -19,118 +32,90 @@ void DialInfo::load(ESMReader &esm)
|
||||||
// Since there's no way to mark selects as "deleted", we have to clear the SelectStructs from all previous loadings
|
// Since there's no way to mark selects as "deleted", we have to clear the SelectStructs from all previous loadings
|
||||||
mSelects.clear();
|
mSelects.clear();
|
||||||
|
|
||||||
// Not present if deleted
|
while (esm.hasMoreSubs())
|
||||||
if (esm.isNextSub("DATA")) {
|
{
|
||||||
esm.getHT(mData, 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!esm.hasMoreSubs())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// What follows is somewhat spaghetti-ish, but it's worth if for
|
|
||||||
// an extra speedup. INFO is by far the most common record type.
|
|
||||||
|
|
||||||
// subName is a reference to the original, so it changes whenever
|
|
||||||
// a new sub name is read. esm.isEmptyOrGetName() will get the
|
|
||||||
// next name for us, or return true if there are no more records.
|
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
const NAME &subName = esm.retSubName();
|
switch (esm.retSubName().val)
|
||||||
|
|
||||||
if (subName.val == REC_ONAM)
|
|
||||||
{
|
{
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
esm.getHT(mData, 12);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'O','N','A','M'>::value:
|
||||||
mActor = esm.getHString();
|
mActor = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'R','N','A','M'>::value:
|
||||||
}
|
|
||||||
if (subName.val == REC_RNAM)
|
|
||||||
{
|
|
||||||
mRace = esm.getHString();
|
mRace = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'C','N','A','M'>::value:
|
||||||
}
|
|
||||||
if (subName.val == REC_CNAM)
|
|
||||||
{
|
|
||||||
mClass = esm.getHString();
|
mClass = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'F','N','A','M'>::value:
|
||||||
}
|
|
||||||
|
|
||||||
if (subName.val == REC_FNAM)
|
|
||||||
{
|
{
|
||||||
mFaction = esm.getHString();
|
mFaction = esm.getHString();
|
||||||
if (mFaction == "FFFF")
|
if (mFaction == "FFFF")
|
||||||
|
{
|
||||||
mFactionLess = true;
|
mFactionLess = true;
|
||||||
if (esm.isEmptyOrGetName())
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (subName.val == REC_ANAM)
|
break;
|
||||||
{
|
}
|
||||||
|
case ESM::FourCC<'A','N','A','M'>::value:
|
||||||
mCell = esm.getHString();
|
mCell = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'D','N','A','M'>::value:
|
||||||
}
|
|
||||||
if (subName.val == REC_DNAM)
|
|
||||||
{
|
|
||||||
mPcFaction = esm.getHString();
|
mPcFaction = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'S','N','A','M'>::value:
|
||||||
}
|
|
||||||
if (subName.val == REC_SNAM)
|
|
||||||
{
|
|
||||||
mSound = esm.getHString();
|
mSound = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::SREC_NAME:
|
||||||
}
|
|
||||||
if (subName.val == REC_NAME)
|
|
||||||
{
|
|
||||||
mResponse = esm.getHString();
|
mResponse = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'S','C','V','R'>::value:
|
||||||
}
|
|
||||||
|
|
||||||
while (subName.val == REC_SCVR)
|
|
||||||
{
|
{
|
||||||
SelectStruct ss;
|
SelectStruct ss;
|
||||||
|
|
||||||
ss.mSelectRule = esm.getHString();
|
ss.mSelectRule = esm.getHString();
|
||||||
|
ss.mValue.read(esm, Variant::Format_Info);
|
||||||
ss.mValue.read (esm, Variant::Format_Info);
|
|
||||||
|
|
||||||
mSelects.push_back(ss);
|
mSelects.push_back(ss);
|
||||||
|
break;
|
||||||
if (esm.isEmptyOrGetName())
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
case ESM::FourCC<'B','N','A','M'>::value:
|
||||||
if (subName.val == REC_BNAM)
|
|
||||||
{
|
|
||||||
mResultScript = esm.getHString();
|
mResultScript = esm.getHString();
|
||||||
if (esm.isEmptyOrGetName())
|
break;
|
||||||
return;
|
case ESM::FourCC<'Q','S','T','N'>::value:
|
||||||
|
mQuestStatus = QS_Name;
|
||||||
|
esm.skipRecord();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'Q','S','T','F'>::value:
|
||||||
|
mQuestStatus = QS_Finished;
|
||||||
|
esm.skipRecord();
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'Q','S','T','R'>::value:
|
||||||
|
mQuestStatus = QS_Restart;
|
||||||
|
esm.skipRecord();
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subName.val == REC_QSTN)
|
void DialInfo::save(ESMWriter &esm, bool isDeleted) const
|
||||||
mQuestStatus = QS_Name;
|
{
|
||||||
else if (subName.val == REC_QSTF)
|
esm.writeHNCString("INAM", mId);
|
||||||
mQuestStatus = QS_Finished;
|
|
||||||
else if (subName.val == REC_QSTR)
|
|
||||||
mQuestStatus = QS_Restart;
|
|
||||||
else if (subName.val == REC_DELE)
|
|
||||||
mQuestStatus = QS_Deleted;
|
|
||||||
else
|
|
||||||
esm.fail(
|
|
||||||
"Don't know what to do with " + subName.toString()
|
|
||||||
+ " in INFO " + mId);
|
|
||||||
|
|
||||||
if (mQuestStatus != QS_None)
|
|
||||||
// Skip rest of record
|
|
||||||
esm.skipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DialInfo::save(ESMWriter &esm) const
|
|
||||||
{
|
|
||||||
esm.writeHNCString("PNAM", mPrev);
|
esm.writeHNCString("PNAM", mPrev);
|
||||||
esm.writeHNCString("NNAM", mNext);
|
esm.writeHNCString("NNAM", mNext);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNT("DATA", mData, 12);
|
esm.writeHNT("DATA", mData, 12);
|
||||||
esm.writeHNOCString("ONAM", mActor);
|
esm.writeHNOCString("ONAM", mActor);
|
||||||
esm.writeHNOCString("RNAM", mRace);
|
esm.writeHNOCString("RNAM", mRace);
|
||||||
|
@ -154,10 +139,9 @@ void DialInfo::save(ESMWriter &esm) const
|
||||||
case QS_Name: esm.writeHNT("QSTN",'\1'); break;
|
case QS_Name: esm.writeHNT("QSTN",'\1'); break;
|
||||||
case QS_Finished: esm.writeHNT("QSTF", '\1'); break;
|
case QS_Finished: esm.writeHNT("QSTF", '\1'); break;
|
||||||
case QS_Restart: esm.writeHNT("QSTR", '\1'); break;
|
case QS_Restart: esm.writeHNT("QSTR", '\1'); break;
|
||||||
case QS_Deleted: esm.writeHNT("DELE", '\1'); break;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialInfo::blank()
|
void DialInfo::blank()
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,8 +59,7 @@ struct DialInfo
|
||||||
QS_None = 0,
|
QS_None = 0,
|
||||||
QS_Name = 1,
|
QS_Name = 1,
|
||||||
QS_Finished = 2,
|
QS_Finished = 2,
|
||||||
QS_Restart = 3,
|
QS_Restart = 3
|
||||||
QS_Deleted
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rules for when to include this item in the final list of options
|
// Rules for when to include this item in the final list of options
|
||||||
|
@ -106,8 +105,14 @@ struct DialInfo
|
||||||
REC_DELE = 0x454c4544
|
REC_DELE = 0x454c4544
|
||||||
};
|
};
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
///< Loads all sub-records of Info record
|
||||||
|
void loadId(ESMReader &esm);
|
||||||
|
///< Loads only Id of Info record (INAM sub-record)
|
||||||
|
void loadData(ESMReader &esm, bool &isDeleted);
|
||||||
|
///< Loads all sub-records of Info record, except INAM sub-record
|
||||||
|
|
||||||
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Ingredient::sRecordId = REC_INGR;
|
unsigned int Ingredient::sRecordId = REC_INGR;
|
||||||
|
|
||||||
void Ingredient::load(ESMReader &esm)
|
void Ingredient::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -33,12 +39,19 @@ namespace ESM
|
||||||
case ESM::FourCC<'I','T','E','X'>::value:
|
case ESM::FourCC<'I','T','E','X'>::value:
|
||||||
mIcon = esm.getHString();
|
mIcon = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasData)
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing IRDT subrecord");
|
esm.fail("Missing IRDT subrecord");
|
||||||
|
|
||||||
// horrible hack to fix broken data in records
|
// horrible hack to fix broken data in records
|
||||||
|
@ -65,8 +78,16 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ingredient::save(ESMWriter &esm) const
|
void Ingredient::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNT("IRDT", mData, 56);
|
esm.writeHNT("IRDT", mData, 56);
|
||||||
|
|
|
@ -31,8 +31,8 @@ struct Ingredient
|
||||||
IRDTstruct mData;
|
IRDTstruct mData;
|
||||||
std::string mId, mName, mModel, mIcon, mScript;
|
std::string mId, mName, mModel, mIcon, mScript;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -10,8 +10,8 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Land::sRecordId = REC_LAND;
|
unsigned int Land::sRecordId = REC_LAND;
|
||||||
|
|
||||||
void Land::LandData::save(ESMWriter &esm) const
|
void Land::LandData::save(ESMWriter &esm) const
|
||||||
{
|
{
|
||||||
if (mDataTypes & Land::DATA_VNML) {
|
if (mDataTypes & Land::DATA_VNML) {
|
||||||
esm.writeHNT("VNML", mNormals, sizeof(mNormals));
|
esm.writeHNT("VNML", mNormals, sizeof(mNormals));
|
||||||
}
|
}
|
||||||
|
@ -53,19 +53,9 @@ void Land::LandData::save(ESMWriter &esm) const
|
||||||
transposeTextureData(mTextures, vtex);
|
transposeTextureData(mTextures, vtex);
|
||||||
esm.writeHNT("VTEX", vtex, sizeof(vtex));
|
esm.writeHNT("VTEX", vtex, sizeof(vtex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out)
|
Land::Land()
|
||||||
{
|
|
||||||
int readPos = 0; //bit ugly, but it works
|
|
||||||
for ( int y1 = 0; y1 < 4; y1++ )
|
|
||||||
for ( int x1 = 0; x1 < 4; x1++ )
|
|
||||||
for ( int y2 = 0; y2 < 4; y2++)
|
|
||||||
for ( int x2 = 0; x2 < 4; x2++ )
|
|
||||||
out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
Land::Land()
|
|
||||||
: mFlags(0)
|
: mFlags(0)
|
||||||
, mX(0)
|
, mX(0)
|
||||||
, mY(0)
|
, mY(0)
|
||||||
|
@ -74,73 +64,122 @@ Land::Land()
|
||||||
, mDataTypes(0)
|
, mDataTypes(0)
|
||||||
, mDataLoaded(false)
|
, mDataLoaded(false)
|
||||||
, mLandData(NULL)
|
, mLandData(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Land::~Land()
|
void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out)
|
||||||
{
|
{
|
||||||
|
int readPos = 0; //bit ugly, but it works
|
||||||
|
for ( int y1 = 0; y1 < 4; y1++ )
|
||||||
|
for ( int x1 = 0; x1 < 4; x1++ )
|
||||||
|
for ( int y2 = 0; y2 < 4; y2++)
|
||||||
|
for ( int x2 = 0; x2 < 4; x2++ )
|
||||||
|
out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
Land::~Land()
|
||||||
|
{
|
||||||
delete mLandData;
|
delete mLandData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Land::load(ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
void Land::load(ESMReader &esm)
|
|
||||||
{
|
|
||||||
mEsm = &esm;
|
mEsm = &esm;
|
||||||
mPlugin = mEsm->getIndex();
|
mPlugin = mEsm->getIndex();
|
||||||
|
|
||||||
// Get the grid location
|
bool hasLocation = false;
|
||||||
esm.getSubNameIs("INTV");
|
bool isLoaded = false;
|
||||||
|
while (!isLoaded && esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::FourCC<'I','N','T','V'>::value:
|
||||||
esm.getSubHeaderIs(8);
|
esm.getSubHeaderIs(8);
|
||||||
esm.getT<int>(mX);
|
esm.getT<int>(mX);
|
||||||
esm.getT<int>(mY);
|
esm.getT<int>(mY);
|
||||||
|
hasLocation = true;
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
esm.getHT(mFlags);
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.cacheSubName();
|
||||||
|
isLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
esm.getHNT(mFlags, "DATA");
|
if (!hasLocation)
|
||||||
|
esm.fail("Missing INTV subrecord");
|
||||||
|
|
||||||
// Store the file position
|
|
||||||
mContext = esm.getContext();
|
mContext = esm.getContext();
|
||||||
|
|
||||||
// Skip these here. Load the actual data when the cell is loaded.
|
// Skip the land data here. Load it when the cell is loaded.
|
||||||
if (esm.isNextSub("VNML"))
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.skipHSubSize(12675);
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::FourCC<'V','N','M','L'>::value:
|
||||||
|
esm.skipHSub();
|
||||||
mDataTypes |= DATA_VNML;
|
mDataTypes |= DATA_VNML;
|
||||||
}
|
break;
|
||||||
if (esm.isNextSub("VHGT"))
|
case ESM::FourCC<'V','H','G','T'>::value:
|
||||||
{
|
esm.skipHSub();
|
||||||
esm.skipHSubSize(4232);
|
|
||||||
mDataTypes |= DATA_VHGT;
|
mDataTypes |= DATA_VHGT;
|
||||||
}
|
break;
|
||||||
if (esm.isNextSub("WNAM"))
|
case ESM::FourCC<'W','N','A','M'>::value:
|
||||||
{
|
esm.skipHSub();
|
||||||
esm.skipHSubSize(81);
|
|
||||||
mDataTypes |= DATA_WNAM;
|
mDataTypes |= DATA_WNAM;
|
||||||
}
|
break;
|
||||||
if (esm.isNextSub("VCLR"))
|
case ESM::FourCC<'V','C','L','R'>::value:
|
||||||
{
|
esm.skipHSub();
|
||||||
esm.skipHSubSize(12675);
|
|
||||||
mDataTypes |= DATA_VCLR;
|
mDataTypes |= DATA_VCLR;
|
||||||
}
|
break;
|
||||||
if (esm.isNextSub("VTEX"))
|
case ESM::FourCC<'V','T','E','X'>::value:
|
||||||
{
|
esm.skipHSub();
|
||||||
esm.skipHSubSize(512);
|
|
||||||
mDataTypes |= DATA_VTEX;
|
mDataTypes |= DATA_VTEX;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mDataLoaded = 0;
|
mDataLoaded = 0;
|
||||||
mLandData = NULL;
|
mLandData = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Land::save(ESMWriter &esm) const
|
void Land::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
esm.startSubRecord("INTV");
|
esm.startSubRecord("INTV");
|
||||||
esm.writeT(mX);
|
esm.writeT(mX);
|
||||||
esm.writeT(mY);
|
esm.writeT(mY);
|
||||||
esm.endRecord("INTV");
|
esm.endRecord("INTV");
|
||||||
|
|
||||||
esm.writeHNT("DATA", mFlags);
|
esm.writeHNT("DATA", mFlags);
|
||||||
}
|
|
||||||
|
|
||||||
void Land::loadData(int flags) const
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLandData)
|
||||||
|
{
|
||||||
|
mLandData->save(esm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Land::loadData(int flags) const
|
||||||
|
{
|
||||||
// Try to load only available data
|
// Try to load only available data
|
||||||
flags = flags & mDataTypes;
|
flags = flags & mDataTypes;
|
||||||
// Return if all required data is loaded
|
// Return if all required data is loaded
|
||||||
|
@ -189,20 +228,20 @@ void Land::loadData(int flags) const
|
||||||
LandData::transposeTextureData(vtex, mLandData->mTextures);
|
LandData::transposeTextureData(vtex, mLandData->mTextures);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Land::unloadData()
|
void Land::unloadData()
|
||||||
{
|
{
|
||||||
if (mDataLoaded)
|
if (mDataLoaded)
|
||||||
{
|
{
|
||||||
delete mLandData;
|
delete mLandData;
|
||||||
mLandData = NULL;
|
mLandData = NULL;
|
||||||
mDataLoaded = 0;
|
mDataLoaded = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const
|
bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const
|
||||||
{
|
{
|
||||||
if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) {
|
if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) {
|
||||||
mEsm->getHExact(ptr, size);
|
mEsm->getHExact(ptr, size);
|
||||||
mDataLoaded |= dataFlag;
|
mDataLoaded |= dataFlag;
|
||||||
|
@ -210,12 +249,12 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size) const
|
||||||
}
|
}
|
||||||
mEsm->skipHSubSize(size);
|
mEsm->skipHSubSize(size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Land::isDataLoaded(int flags) const
|
bool Land::isDataLoaded(int flags) const
|
||||||
{
|
{
|
||||||
return (mDataLoaded & flags) == (flags & mDataTypes);
|
return (mDataLoaded & flags) == (flags & mDataTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Land::Land (const Land& land)
|
Land::Land (const Land& land)
|
||||||
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
|
: mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin),
|
||||||
|
|
|
@ -106,8 +106,8 @@ struct Land
|
||||||
static void transposeTextureData(const uint16_t *in, uint16_t *out);
|
static void transposeTextureData(const uint16_t *in, uint16_t *out);
|
||||||
};
|
};
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank() {}
|
void blank() {}
|
||||||
|
|
||||||
|
|
|
@ -6,25 +6,32 @@
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
void LevelledListBase::load(ESMReader &esm, bool &isDeleted)
|
||||||
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
void LevelledListBase::load(ESMReader &esm)
|
bool hasName = false;
|
||||||
|
bool hasList = false;
|
||||||
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getHNT(mFlags, "DATA");
|
esm.getSubName();
|
||||||
esm.getHNT(mChanceNone, "NNAM");
|
switch (esm.retSubName().val)
|
||||||
|
|
||||||
if (esm.isNextSub("INDX"))
|
|
||||||
{
|
{
|
||||||
int len;
|
case ESM::SREC_NAME:
|
||||||
esm.getHT(len);
|
mId = esm.getHString();
|
||||||
mList.resize(len);
|
hasName = true;
|
||||||
}
|
break;
|
||||||
else
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
esm.getHT(mFlags);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'N','N','A','M'>::value:
|
||||||
|
esm.getHT(mChanceNone);
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'I','N','D','X'>::value:
|
||||||
{
|
{
|
||||||
// Original engine ignores rest of the record, even if there are items following
|
int length = 0;
|
||||||
mList.clear();
|
esm.getHT(length);
|
||||||
esm.skipRecord();
|
mList.resize(length);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this levelled list was already loaded by a previous content file,
|
// 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,
|
// we overwrite the list. Merging lists should probably be left to external tools,
|
||||||
|
@ -32,16 +39,51 @@ namespace ESM
|
||||||
// will be flawed in some way. For a proper fix the ESM format would have to be changed
|
// 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
|
// to actually track list changes instead of including the whole list for every file
|
||||||
// that does something with that list.
|
// that does something with that list.
|
||||||
|
|
||||||
for (size_t i = 0; i < mList.size(); i++)
|
for (size_t i = 0; i < mList.size(); i++)
|
||||||
{
|
{
|
||||||
LevelItem &li = mList[i];
|
LevelItem &li = mList[i];
|
||||||
li.mId = esm.getHNString(mRecName);
|
li.mId = esm.getHNString(mRecName);
|
||||||
esm.getHNT(li.mLevel, "INTV");
|
esm.getHNT(li.mLevel, "INTV");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasList = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
void LevelledListBase::save(ESMWriter &esm) const
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
{
|
{
|
||||||
|
if (!hasList)
|
||||||
|
{
|
||||||
|
// Original engine ignores rest of the record, even if there are items following
|
||||||
|
mList.clear();
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelledListBase::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNT("DATA", mFlags);
|
esm.writeHNT("DATA", mFlags);
|
||||||
esm.writeHNT("NNAM", mChanceNone);
|
esm.writeHNT("NNAM", mChanceNone);
|
||||||
esm.writeHNT<int>("INDX", mList.size());
|
esm.writeHNT<int>("INDX", mList.size());
|
||||||
|
|
|
@ -36,8 +36,8 @@ struct LevelledListBase
|
||||||
|
|
||||||
std::vector<LevelItem> mList;
|
std::vector<LevelItem> mList;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Light::sRecordId = REC_LIGH;
|
unsigned int Light::sRecordId = REC_LIGH;
|
||||||
|
|
||||||
void Light::load(ESMReader &esm)
|
void Light::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -36,15 +42,31 @@ namespace ESM
|
||||||
case ESM::FourCC<'S','N','A','M'>::value:
|
case ESM::FourCC<'S','N','A','M'>::value:
|
||||||
mSound = esm.getHString();
|
mSound = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing LHDT subrecord");
|
esm.fail("Missing LHDT subrecord");
|
||||||
}
|
}
|
||||||
void Light::save(ESMWriter &esm) const
|
void Light::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
esm.writeHNOCString("ITEX", mIcon);
|
esm.writeHNOCString("ITEX", mIcon);
|
||||||
|
|
|
@ -47,8 +47,8 @@ struct Light
|
||||||
|
|
||||||
std::string mSound, mScript, mModel, mIcon, mName, mId;
|
std::string mSound, mScript, mModel, mIcon, mName, mId;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Lockpick::sRecordId = REC_LOCK;
|
unsigned int Lockpick::sRecordId = REC_LOCK;
|
||||||
|
|
||||||
void Lockpick::load(ESMReader &esm)
|
void Lockpick::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
bool hasData = true;
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -33,16 +39,32 @@ namespace ESM
|
||||||
case ESM::FourCC<'I','T','E','X'>::value:
|
case ESM::FourCC<'I','T','E','X'>::value:
|
||||||
mIcon = esm.getHString();
|
mIcon = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
esm.fail("Unknown subrecord");
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing LKDT subrecord");
|
esm.fail("Missing LKDT subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lockpick::save(ESMWriter &esm) const
|
void Lockpick::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
esm.writeHNCString("MODL", mModel);
|
esm.writeHNCString("MODL", mModel);
|
||||||
esm.writeHNOCString("FNAM", mName);
|
esm.writeHNOCString("FNAM", mName);
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ struct Lockpick
|
||||||
Data mData;
|
Data mData;
|
||||||
std::string mId, mName, mModel, mIcon, mScript;
|
std::string mId, mName, mModel, mIcon, mScript;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
|
@ -8,21 +8,58 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int LandTexture::sRecordId = REC_LTEX;
|
unsigned int LandTexture::sRecordId = REC_LTEX;
|
||||||
|
|
||||||
void LandTexture::load(ESMReader &esm)
|
void LandTexture::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
esm.getHNT(mIndex, "INTV");
|
isDeleted = false;
|
||||||
mTexture = esm.getHNString("DATA");
|
|
||||||
}
|
bool hasName = false;
|
||||||
void LandTexture::save(ESMWriter &esm) const
|
bool hasIndex = false;
|
||||||
{
|
while (esm.hasMoreSubs())
|
||||||
|
{
|
||||||
|
esm.getSubName();
|
||||||
|
switch (esm.retSubName().val)
|
||||||
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'I','N','T','V'>::value:
|
||||||
|
esm.getHT(mIndex);
|
||||||
|
hasIndex = true;
|
||||||
|
break;
|
||||||
|
case ESM::FourCC<'D','A','T','A'>::value:
|
||||||
|
mTexture = esm.getHString();
|
||||||
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasIndex)
|
||||||
|
esm.fail("Missing INTV subrecord");
|
||||||
|
}
|
||||||
|
void LandTexture::save(ESMWriter &esm, bool isDeleted) const
|
||||||
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
esm.writeHNT("INTV", mIndex);
|
esm.writeHNT("INTV", mIndex);
|
||||||
esm.writeHNCString("DATA", mTexture);
|
esm.writeHNCString("DATA", mTexture);
|
||||||
}
|
|
||||||
|
|
||||||
void LandTexture::blank()
|
if (isDeleted)
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LandTexture::blank()
|
||||||
|
{
|
||||||
mTexture.clear();
|
mTexture.clear();
|
||||||
mIndex = -1;
|
mIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,11 +34,11 @@ struct LandTexture
|
||||||
std::string mId, mTexture;
|
std::string mId, mTexture;
|
||||||
int mIndex;
|
int mIndex;
|
||||||
|
|
||||||
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
|
||||||
void save(ESMWriter &esm) const;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -189,8 +189,10 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int MagicEffect::sRecordId = REC_MGEF;
|
unsigned int MagicEffect::sRecordId = REC_MGEF;
|
||||||
|
|
||||||
void MagicEffect::load(ESMReader &esm)
|
void MagicEffect::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false; // MagicEffect record can't be deleted now (may be changed in the future)
|
||||||
|
|
||||||
esm.getHNT(mIndex, "INDX");
|
esm.getHNT(mIndex, "INDX");
|
||||||
|
|
||||||
mId = indexToId (mIndex);
|
mId = indexToId (mIndex);
|
||||||
|
@ -209,8 +211,7 @@ void MagicEffect::load(ESMReader &esm)
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
case ESM::FourCC<'I','T','E','X'>::value:
|
case ESM::FourCC<'I','T','E','X'>::value:
|
||||||
mIcon = esm.getHString();
|
mIcon = esm.getHString();
|
||||||
|
@ -250,7 +251,7 @@ void MagicEffect::load(ESMReader &esm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void MagicEffect::save(ESMWriter &esm) const
|
void MagicEffect::save(ESMWriter &esm, bool /*isDeleted*/) const
|
||||||
{
|
{
|
||||||
esm.writeHNT("INDX", mIndex);
|
esm.writeHNT("INDX", mIndex);
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,8 @@ struct MagicEffect
|
||||||
// sMagicCreature04ID/05ID.
|
// sMagicCreature04ID/05ID.
|
||||||
int mIndex;
|
int mIndex;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
/// Set record to default state (does not touch the ID/index).
|
/// Set record to default state (does not touch the ID/index).
|
||||||
void blank();
|
void blank();
|
||||||
|
|
|
@ -8,15 +8,21 @@ namespace ESM
|
||||||
{
|
{
|
||||||
unsigned int Miscellaneous::sRecordId = REC_MISC;
|
unsigned int Miscellaneous::sRecordId = REC_MISC;
|
||||||
|
|
||||||
void Miscellaneous::load(ESMReader &esm)
|
void Miscellaneous::load(ESMReader &esm, bool &isDeleted)
|
||||||
{
|
{
|
||||||
|
isDeleted = false;
|
||||||
|
|
||||||
|
bool hasName = false;
|
||||||
bool hasData = false;
|
bool hasData = false;
|
||||||
while (esm.hasMoreSubs())
|
while (esm.hasMoreSubs())
|
||||||
{
|
{
|
||||||
esm.getSubName();
|
esm.getSubName();
|
||||||
uint32_t name = esm.retSubName().val;
|
switch (esm.retSubName().val)
|
||||||
switch (name)
|
|
||||||
{
|
{
|
||||||
|
case ESM::SREC_NAME:
|
||||||
|
mId = esm.getHString();
|
||||||
|
hasName = true;
|
||||||
|
break;
|
||||||
case ESM::FourCC<'M','O','D','L'>::value:
|
case ESM::FourCC<'M','O','D','L'>::value:
|
||||||
mModel = esm.getHString();
|
mModel = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
@ -33,14 +39,32 @@ namespace ESM
|
||||||
case ESM::FourCC<'I','T','E','X'>::value:
|
case ESM::FourCC<'I','T','E','X'>::value:
|
||||||
mIcon = esm.getHString();
|
mIcon = esm.getHString();
|
||||||
break;
|
break;
|
||||||
|
case ESM::SREC_DELE:
|
||||||
|
esm.skipHSub();
|
||||||
|
isDeleted = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
esm.fail("Unknown subrecord");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasData)
|
|
||||||
|
if (!hasName)
|
||||||
|
esm.fail("Missing NAME subrecord");
|
||||||
|
if (!hasData && !isDeleted)
|
||||||
esm.fail("Missing MCDT subrecord");
|
esm.fail("Missing MCDT subrecord");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Miscellaneous::save(ESMWriter &esm) const
|
void Miscellaneous::save(ESMWriter &esm, bool isDeleted) const
|
||||||
{
|
{
|
||||||
|
esm.writeHNCString("NAME", mId);
|
||||||
|
|
||||||
|
if (isDeleted)
|
||||||
|
{
|
||||||
|
esm.writeHNCString("DELE", "");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -32,8 +32,8 @@ struct Miscellaneous
|
||||||
|
|
||||||
std::string mId, mName, mModel, mIcon, mScript;
|
std::string mId, mName, mModel, mIcon, mScript;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm, bool &isDeleted);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm, bool isDeleted = false) const;
|
||||||
|
|
||||||
void blank();
|
void blank();
|
||||||
///< Set record to default state (does not touch the ID).
|
///< Set record to default state (does not touch the ID).
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue