1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-16 17:29:55 +00:00

Disallow implicitly sized reads of ESM structs

This commit is contained in:
Evil Eye 2023-06-02 16:54:27 +02:00
parent ec9b4c4563
commit 45ba05c0ed
26 changed files with 79 additions and 67 deletions

View file

@ -97,7 +97,7 @@ namespace ESSImport
void ConvertFMAP::read(ESM::ESMReader& esm) void ConvertFMAP::read(ESM::ESMReader& esm)
{ {
MAPH maph; MAPH maph;
esm.getHNT(maph, "MAPH"); esm.getHNTSized<8>(maph, "MAPH");
std::vector<char> data; std::vector<char> data;
esm.getSubNameIs("MAPD"); esm.getSubNameIs("MAPD");
esm.getSubHeader(); esm.getSubHeader();

View file

@ -48,14 +48,14 @@ namespace ESSImport
if (esm.isNextSub("ACDT")) if (esm.isNextSub("ACDT"))
{ {
mActorData.mHasACDT = true; mActorData.mHasACDT = true;
esm.getHT(mActorData.mACDT); esm.getHTSized<264>(mActorData.mACDT);
} }
mActorData.mHasACSC = false; mActorData.mHasACSC = false;
if (esm.isNextSub("ACSC")) if (esm.isNextSub("ACSC"))
{ {
mActorData.mHasACSC = true; mActorData.mHasACSC = true;
esm.getHT(mActorData.mACSC); esm.getHTSized<112>(mActorData.mACSC);
} }
if (esm.isNextSub("ACSL")) if (esm.isNextSub("ACSL"))
@ -137,7 +137,7 @@ namespace ESSImport
if (esm.isNextSub("ANIS")) if (esm.isNextSub("ANIS"))
{ {
mActorData.mHasANIS = true; mActorData.mHasANIS = true;
esm.getHT(mActorData.mANIS); esm.getHTSized<8>(mActorData.mANIS);
} }
if (esm.isNextSub("LVCR")) if (esm.isNextSub("LVCR"))

View file

@ -16,7 +16,7 @@ namespace ESSImport
} }
else if (esm.getSubSize() == 96) else if (esm.getSubSize() == 96)
{ {
esm.getT(mGMDT); esm.getTSized<96>(mGMDT);
} }
else else
esm.fail("unexpected subrecord size for GAME.GMDT"); esm.fail("unexpected subrecord size for GAME.GMDT");

View file

@ -12,7 +12,7 @@ namespace ESSImport
while (esm.isNextSub("NPCO")) while (esm.isNextSub("NPCO"))
{ {
ContItem contItem; ContItem contItem;
esm.getHT(contItem); esm.getHTSized<36>(contItem);
InventoryItem item; InventoryItem item;
item.mId = contItem.mItem.toString(); item.mId = contItem.mItem.toString();

View file

@ -7,7 +7,7 @@ namespace ESSImport
void NPCC::load(ESM::ESMReader& esm) void NPCC::load(ESM::ESMReader& esm)
{ {
esm.getHNT(mNPDT, "NPDT"); esm.getHNTSized<8>(mNPDT, "NPDT");
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))

View file

@ -19,7 +19,7 @@ namespace ESSImport
mMNAM = esm.getHString(); mMNAM = esm.getHString();
} }
esm.getHNT(mPNAM, "PNAM"); esm.getHNTSized<212>(mPNAM, "PNAM");
if (esm.isNextSub("SNAM")) if (esm.isNextSub("SNAM"))
esm.skipHSub(); esm.skipHSub();
@ -54,7 +54,7 @@ namespace ESSImport
if (esm.isNextSub("ENAM")) if (esm.isNextSub("ENAM"))
{ {
mHasENAM = true; mHasENAM = true;
esm.getHT(mENAM); esm.getHTSized<8>(mENAM);
} }
if (esm.isNextSub("LNAM")) if (esm.isNextSub("LNAM"))
@ -63,7 +63,7 @@ namespace ESSImport
while (esm.isNextSub("FNAM")) while (esm.isNextSub("FNAM"))
{ {
FNAM fnam; FNAM fnam;
esm.getHT(fnam); esm.getHTSized<44>(fnam);
mFactions.push_back(fnam); mFactions.push_back(fnam);
} }
@ -71,7 +71,7 @@ namespace ESSImport
if (esm.isNextSub("AADT")) // Attack animation data? if (esm.isNextSub("AADT")) // Attack animation data?
{ {
mHasAADT = true; mHasAADT = true;
esm.getHT(mAADT); esm.getHTSized<44>(mAADT);
} }
if (esm.isNextSub("KNAM")) if (esm.isNextSub("KNAM"))

View file

@ -10,7 +10,7 @@ namespace ESSImport
while (esm.isNextSub("PNAM")) while (esm.isNextSub("PNAM"))
{ {
PNAM pnam; PNAM pnam;
esm.getHT(pnam); esm.getHTSized<184>(pnam);
mProjectiles.push_back(pnam); mProjectiles.push_back(pnam);
} }
} }

View file

@ -7,7 +7,7 @@ namespace ESSImport
void SCPT::load(ESM::ESMReader& esm) void SCPT::load(ESM::ESMReader& esm)
{ {
esm.getHNT(mSCHD, "SCHD"); esm.getHNTSized<52>(mSCHD, "SCHD");
mSCRI.load(esm); mSCRI.load(esm);

View file

@ -11,13 +11,13 @@ namespace ESSImport
{ {
ActiveSpell spell; ActiveSpell spell;
esm.getHT(spell.mIndex); esm.getHT(spell.mIndex);
esm.getHNT(spell.mSPDT, "SPDT"); esm.getHNTSized<160>(spell.mSPDT, "SPDT");
spell.mTarget = esm.getHNOString("TNAM"); spell.mTarget = esm.getHNOString("TNAM");
while (esm.isNextSub("NPDT")) while (esm.isNextSub("NPDT"))
{ {
ActiveEffect effect; ActiveEffect effect;
esm.getHT(effect.mNPDT); esm.getHTSized<56>(effect.mNPDT);
// Effect-specific subrecords can follow: // Effect-specific subrecords can follow:
// - INAM for disintegration and bound effects // - INAM for disintegration and bound effects

View file

@ -61,7 +61,7 @@ namespace ESM
if (esm.isNextSub("WORS")) if (esm.isNextSub("WORS"))
{ {
esm.getHT(params.mWorsenings); esm.getHT(params.mWorsenings);
esm.getHNT(params.mNextWorsening, "TIME"); esm.getHNTSized<8>(params.mNextWorsening, "TIME");
} }
else else
params.mWorsenings = -1; params.mWorsenings = -1;

View file

@ -13,13 +13,13 @@ namespace ESM
void AiWander::load(ESMReader& esm) void AiWander::load(ESMReader& esm)
{ {
esm.getHNT(mData, "DATA"); esm.getHNTSized<14>(mData, "DATA");
esm.getHNT(mDurationData, "STAR"); // was mStartTime esm.getHNTSized<8>(mDurationData, "STAR"); // was mStartTime
mStoredInitialActorPosition = false; mStoredInitialActorPosition = false;
if (esm.isNextSub("POS_")) if (esm.isNextSub("POS_"))
{ {
mStoredInitialActorPosition = true; mStoredInitialActorPosition = true;
esm.getHT(mInitialActorPosition); esm.getHTSized<12>(mInitialActorPosition);
} }
} }
@ -33,7 +33,7 @@ namespace ESM
void AiTravel::load(ESMReader& esm) void AiTravel::load(ESMReader& esm)
{ {
esm.getHNT(mData, "DATA"); esm.getHNTSized<12>(mData, "DATA");
esm.getHNOT(mHidden, "HIDD"); esm.getHNOT(mHidden, "HIDD");
mRepeat = false; mRepeat = false;
esm.getHNOT(mRepeat, "REPT"); esm.getHNOT(mRepeat, "REPT");
@ -49,7 +49,7 @@ namespace ESM
void AiEscort::load(ESMReader& esm) void AiEscort::load(ESMReader& esm)
{ {
esm.getHNT(mData, "DATA"); esm.getHNTSized<14>(mData, "DATA");
mTargetId = esm.getHNRefId("TARG"); mTargetId = esm.getHNRefId("TARG");
mTargetActorId = -1; mTargetActorId = -1;
esm.getHNOT(mTargetActorId, "TAID"); esm.getHNOT(mTargetActorId, "TAID");
@ -81,7 +81,7 @@ namespace ESM
void AiFollow::load(ESMReader& esm) void AiFollow::load(ESMReader& esm)
{ {
esm.getHNT(mData, "DATA"); esm.getHNTSized<14>(mData, "DATA");
mTargetId = esm.getHNRefId("TARG"); mTargetId = esm.getHNRefId("TARG");
mTargetActorId = -1; mTargetActorId = -1;
esm.getHNOT(mTargetActorId, "TAID"); esm.getHNOT(mTargetActorId, "TAID");

View file

@ -110,9 +110,13 @@ namespace ESM
getHTOrSkip(cellRef.mGoldValue); getHTOrSkip(cellRef.mGoldValue);
break; break;
case fourCC("DODT"): case fourCC("DODT"):
getHTOrSkip(cellRef.mDoorDest);
if constexpr (load) if constexpr (load)
{
esm.getHTSized<24>(cellRef.mDoorDest);
cellRef.mTeleport = true; cellRef.mTeleport = true;
}
else
esm.skipHTSized<24, ESM::Position>();
break; break;
case fourCC("DNAM"): case fourCC("DNAM"):
getHStringOrSkip(cellRef.mDestCell); getHStringOrSkip(cellRef.mDestCell);

View file

@ -16,7 +16,7 @@ namespace ESM
mLastRespawn.mDay = 0; mLastRespawn.mDay = 0;
mLastRespawn.mHour = 0; mLastRespawn.mHour = 0;
esm.getHNOT(mLastRespawn, "RESP"); esm.getHNOTSized<8>(mLastRespawn, "RESP");
} }
void CellState::save(ESMWriter& esm) const void CellState::save(ESMWriter& esm) const

View file

@ -21,7 +21,7 @@ namespace ESM
mTradeTime.mDay = 0; mTradeTime.mDay = 0;
mTradeTime.mHour = 0; mTradeTime.mHour = 0;
esm.getHNOT(mTradeTime, "TIME"); esm.getHNOTSized<8>(mTradeTime, "TIME");
int flags = 0; int flags = 0;
mDead = false; mDead = false;
@ -108,7 +108,7 @@ namespace ESM
mTimeOfDeath.mDay = 0; mTimeOfDeath.mDay = 0;
mTimeOfDeath.mHour = 0; mTimeOfDeath.mHour = 0;
esm.getHNOT(mTimeOfDeath, "DTIM"); esm.getHNOTSized<8>(mTimeOfDeath, "DTIM");
mSpells.load(esm); mSpells.load(esm);
mActiveSpells.load(esm); mActiveSpells.load(esm);
@ -164,7 +164,7 @@ namespace ESM
CorprusStats stats; CorprusStats stats;
esm.getHNT(stats.mWorsenings, "WORS"); esm.getHNT(stats.mWorsenings, "WORS");
esm.getHNT(stats.mNextWorsening, "TIME"); esm.getHNTSized<8>(stats.mNextWorsening, "TIME");
mCorprusSpells[id] = stats; mCorprusSpells[id] = stats;
} }

View file

@ -484,7 +484,7 @@ namespace ESM
case RefIdType::FormId: case RefIdType::FormId:
{ {
FormId formId{}; FormId formId{};
getT(formId); getTSized<8>(formId);
return RefId::formIdRefId(formId); return RefId::formIdRefId(formId);
} }
case RefIdType::Generated: case RefIdType::Generated:

View file

@ -5,6 +5,7 @@
#include <filesystem> #include <filesystem>
#include <istream> #include <istream>
#include <memory> #include <memory>
#include <type_traits>
#include <vector> #include <vector>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -15,6 +16,9 @@
namespace ESM namespace ESM
{ {
template <class T>
constexpr bool IsReadable
= std::is_arithmetic_v<T> || std::is_enum_v<T> || (std::is_array_v<T> && IsReadable<std::remove_extent_t<T>>);
class ReadersCache; class ReadersCache;
@ -100,19 +104,17 @@ namespace ESM
ESM::RefId getCellId(); ESM::RefId getCellId();
// Read data of a given type, stored in a subrecord of a given name // Read data of a given type, stored in a subrecord of a given name
template <typename X> template <typename X, typename = std::enable_if_t<IsReadable<X>>>
void getHNT(X& x, NAME name) void getHNT(X& x, NAME name)
{ {
getSubNameIs(name); getHNTSized<sizeof(X)>(x, name);
getHT(x);
} }
// Optional version of getHNT // Optional version of getHNT
template <typename X> template <typename X, typename = std::enable_if_t<IsReadable<X>>>
void getHNOT(X& x, NAME name) void getHNOT(X& x, NAME name)
{ {
if (isNextSub(name)) getHNOTSized<sizeof(X)>(x, name);
getHT(x);
} }
// Version with extra size checking, to make sure the compiler // Version with extra size checking, to make sure the compiler
@ -120,34 +122,28 @@ namespace ESM
template <std::size_t size, typename X> template <std::size_t size, typename X>
void getHNTSized(X& x, NAME name) void getHNTSized(X& x, NAME name)
{ {
static_assert(sizeof(X) == size); getSubNameIs(name);
getHNT(x, name); getHTSized<size>(x);
} }
template <std::size_t size, typename X> template <std::size_t size, typename X>
void getHNOTSized(X& x, NAME name) void getHNOTSized(X& x, NAME name)
{ {
static_assert(sizeof(X) == size); if (isNextSub(name))
getHNOT(x, name); getHTSized<size>(x);
} }
// Get data of a given type/size, including subrecord header // Get data of a given type/size, including subrecord header
template <typename X> template <typename X, typename = std::enable_if_t<IsReadable<X>>>
void getHT(X& x) void getHT(X& x)
{ {
getSubHeader(); getHTSized<sizeof(X)>(x);
if (mCtx.leftSub != sizeof(X))
reportSubSizeMismatch(sizeof(X), mCtx.leftSub);
getT(x);
} }
template <typename T> template <typename T, typename = std::enable_if_t<IsReadable<T>>>
void skipHT() void skipHT()
{ {
getSubHeader(); skipHTSized<sizeof(T), T>();
if (mCtx.leftSub != sizeof(T))
reportSubSizeMismatch(sizeof(T), mCtx.leftSub);
skipT<T>();
} }
// Version with extra size checking, to make sure the compiler // Version with extra size checking, to make sure the compiler
@ -155,15 +151,27 @@ namespace ESM
template <std::size_t size, typename X> template <std::size_t size, typename X>
void getHTSized(X& x) void getHTSized(X& x)
{ {
static_assert(sizeof(X) == size); getSubHeader();
getHT(x); if (mCtx.leftSub != size)
reportSubSizeMismatch(size, mCtx.leftSub);
getTSized<size>(x);
} }
template <std::size_t size, typename T> template <std::size_t size, typename T>
void skipHTSized() void skipHTSized()
{ {
static_assert(sizeof(T) == size); static_assert(sizeof(T) == size);
skipHT<T>(); getSubHeader();
if (mCtx.leftSub != size)
reportSubSizeMismatch(size, mCtx.leftSub);
skip(size);
}
template <std::size_t size, typename X>
void getTSized(X& x)
{
static_assert(sizeof(X) == size);
getExact(&x, size);
} }
// Read a string by the given name if it is the next record. // Read a string by the given name if it is the next record.
@ -266,13 +274,13 @@ namespace ESM
* *
*************************************************************************/ *************************************************************************/
template <typename X> template <typename X, typename = std::enable_if_t<IsReadable<X>>>
void getT(X& x) void getT(X& x)
{ {
getExact(&x, sizeof(X)); getExact(&x, sizeof(X));
} }
template <typename T> template <typename T, typename = std::enable_if_t<IsReadable<T>>>
void skipT() void skipT()
{ {
skip(sizeof(T)); skip(sizeof(T));
@ -283,7 +291,7 @@ namespace ESM
mEsm->read(static_cast<char*>(x), static_cast<std::streamsize>(size)); mEsm->read(static_cast<char*>(x), static_cast<std::streamsize>(size));
} }
void getName(NAME& name) { getT(name); } void getName(NAME& name) { getTSized<4>(name); }
void getUint(uint32_t& u) { getT(u); } void getUint(uint32_t& u) { getT(u); }
std::string getMaybeFixedStringSize(std::size_t size); std::string getMaybeFixedStringSize(std::size_t size);

View file

@ -58,7 +58,7 @@ namespace ESM
void FogState::load(ESMReader& esm) void FogState::load(ESMReader& esm)
{ {
esm.getHNOT(mBounds, "BOUN"); esm.getHNOTSized<16>(mBounds, "BOUN");
esm.getHNOT(mNorthMarkerAngle, "ANGL"); esm.getHNOT(mNorthMarkerAngle, "ANGL");
const FormatVersion dataFormat = esm.getFormatVersion(); const FormatVersion dataFormat = esm.getFormatVersion();
while (esm.isNextSub("FTEX")) while (esm.isNextSub("FTEX"))

View file

@ -8,7 +8,7 @@ namespace ESM
void GlobalMap::load(ESMReader& esm) void GlobalMap::load(ESMReader& esm)
{ {
esm.getHNT(mBounds, "BNDS"); esm.getHNTSized<16>(mBounds, "BNDS");
esm.getSubNameIs("DATA"); esm.getSubNameIs("DATA");
esm.getSubHeader(); esm.getSubHeader();

View file

@ -28,7 +28,7 @@ namespace ESM
mName = esm.getHString(); mName = esm.getHString();
break; break;
case fourCC("AADT"): case fourCC("AADT"):
esm.getHT(mData); esm.getHTSized<16>(mData);
hasData = true; hasData = true;
break; break;
case fourCC("SCRI"): case fourCC("SCRI"):

View file

@ -144,7 +144,7 @@ namespace ESM
mWater = waterLevel; mWater = waterLevel;
break; break;
case fourCC("AMBI"): case fourCC("AMBI"):
esm.getHT(mAmbi); esm.getHTSized<16>(mAmbi);
mHasAmbi = true; mHasAmbi = true;
break; break;
case fourCC("RGNN"): case fourCC("RGNN"):

View file

@ -29,7 +29,7 @@ namespace ESM
// Cold weather not included before 1.3 // Cold weather not included before 1.3
if (esm.getSubSize() == sizeof(mData)) if (esm.getSubSize() == sizeof(mData))
{ {
esm.getT(mData); esm.getTSized<10>(mData);
} }
else if (esm.getSubSize() == sizeof(mData) - 2) else if (esm.getSubSize() == sizeof(mData) - 2)
{ {

View file

@ -99,7 +99,7 @@ namespace ESM
{ {
esm.getSubHeader(); esm.getSubHeader();
mId = esm.getMaybeFixedRefIdSize(32); mId = esm.getMaybeFixedRefIdSize(32);
esm.getT(mData); esm.getTSized<20>(mData);
hasHeader = true; hasHeader = true;
break; break;

View file

@ -45,7 +45,7 @@ namespace ESM
if (esm.isNextSub("GMDT")) if (esm.isNextSub("GMDT"))
{ {
esm.getHT(mGameData); esm.getHTSized<124>(mGameData);
} }
if (esm.isNextSub("SCRD")) if (esm.isNextSub("SCRD"))
{ {

View file

@ -35,7 +35,7 @@ namespace ESM
if (esm.isNextSub("POS_")) if (esm.isNextSub("POS_"))
{ {
std::array<float, 6> pos; std::array<float, 6> pos;
esm.getHT(pos); esm.getHTSized<24>(pos);
memcpy(mPosition.pos, pos.data(), sizeof(float) * 3); memcpy(mPosition.pos, pos.data(), sizeof(float) * 3);
memcpy(mPosition.rot, pos.data() + 3, sizeof(float) * 3); memcpy(mPosition.rot, pos.data() + 3, sizeof(float) * 3);
} }

View file

@ -17,8 +17,8 @@ namespace ESM
void BaseProjectileState::load(ESMReader& esm) void BaseProjectileState::load(ESMReader& esm)
{ {
mId = esm.getHNRefId("ID__"); mId = esm.getHNRefId("ID__");
esm.getHNT(mPosition, "VEC3"); esm.getHNTSized<12>(mPosition, "VEC3");
esm.getHNT(mOrientation, "QUAT"); esm.getHNTSized<16>(mOrientation, "QUAT");
esm.getHNT(mActorId, "ACTO"); esm.getHNT(mActorId, "ACTO");
} }
@ -64,7 +64,7 @@ namespace ESM
BaseProjectileState::load(esm); BaseProjectileState::load(esm);
mBowId = esm.getHNRefId("BOW_"); mBowId = esm.getHNRefId("BOW_");
esm.getHNT(mVelocity, "VEL_"); esm.getHNTSized<12>(mVelocity, "VEL_");
mAttackStrength = 1.f; mAttackStrength = 1.f;
esm.getHNOT(mAttackStrength, "STR_"); esm.getHNOT(mAttackStrength, "STR_");

View file

@ -79,7 +79,7 @@ namespace ESM
CorprusStats stats; CorprusStats stats;
esm.getHNT(stats.mWorsenings, "WORS"); esm.getHNT(stats.mWorsenings, "WORS");
esm.getHNT(stats.mNextWorsening, "TIME"); esm.getHNTSized<8>(stats.mNextWorsening, "TIME");
mCorprusSpells[id] = stats; mCorprusSpells[id] = stats;
} }
@ -88,7 +88,7 @@ namespace ESM
{ {
ESM::RefId id = esm.getRefId(); ESM::RefId id = esm.getRefId();
TimeStamp time; TimeStamp time;
esm.getHNT(time, "TIME"); esm.getHNTSized<8>(time, "TIME");
mUsedPowers[id] = time; mUsedPowers[id] = time;
} }