Write AiSequence and Script data field by field via decompose function

Use the same function to load and save to have single place with field order
definition. Use concepts for overload over different types.
This commit is contained in:
elsid 2024-01-12 02:15:54 +01:00
parent 3592dc4c88
commit 6451750890
No known key found for this signature in database
GPG key ID: 4DE04C198CBA7625
8 changed files with 104 additions and 28 deletions

View file

@ -422,7 +422,6 @@ namespace ESM
std::copy(std::begin(idle), std::end(idle), record.mData.mIdle); std::copy(std::begin(idle), std::end(idle), record.mData.mIdle);
record.mData.mShouldRepeat = 12; record.mData.mShouldRepeat = 12;
record.mDurationData.mRemainingDuration = 13; record.mDurationData.mRemainingDuration = 13;
record.mDurationData.mUnused = 14;
record.mStoredInitialActorPosition = true; record.mStoredInitialActorPosition = true;
constexpr float initialActorPosition[3] = { 15, 16, 17 }; constexpr float initialActorPosition[3] = { 15, 16, 17 };
static_assert(std::size(initialActorPosition) == std::size(record.mInitialActorPosition.mValues)); static_assert(std::size(initialActorPosition) == std::size(record.mInitialActorPosition.mValues));
@ -438,7 +437,6 @@ namespace ESM
EXPECT_THAT(result.mData.mIdle, ElementsAreArray(record.mData.mIdle)); EXPECT_THAT(result.mData.mIdle, ElementsAreArray(record.mData.mIdle));
EXPECT_EQ(result.mData.mShouldRepeat, record.mData.mShouldRepeat); EXPECT_EQ(result.mData.mShouldRepeat, record.mData.mShouldRepeat);
EXPECT_EQ(result.mDurationData.mRemainingDuration, record.mDurationData.mRemainingDuration); EXPECT_EQ(result.mDurationData.mRemainingDuration, record.mDurationData.mRemainingDuration);
EXPECT_EQ(result.mDurationData.mUnused, record.mDurationData.mUnused);
EXPECT_EQ(result.mStoredInitialActorPosition, record.mStoredInitialActorPosition); EXPECT_EQ(result.mStoredInitialActorPosition, record.mStoredInitialActorPosition);
EXPECT_THAT(result.mInitialActorPosition.mValues, ElementsAreArray(record.mInitialActorPosition.mValues)); EXPECT_THAT(result.mInitialActorPosition.mValues, ElementsAreArray(record.mInitialActorPosition.mValues));
} }

View file

@ -0,0 +1,10 @@
#ifndef OPENMW_COMPONENTS_ESM_DECOMPOSE_H
#define OPENMW_COMPONENTS_ESM_DECOMPOSE_H
namespace ESM
{
template <class T>
void decompose(T&& value, const auto& apply) = delete;
}
#endif

View file

@ -3,32 +3,58 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include <components/misc/concepts.hpp>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
namespace ESM namespace ESM
{ {
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderData> T>
void decompose(T&& v, const auto& f)
{
f(v.mDistance, v.mDuration, v.mTimeOfDay, v.mIdle, v.mShouldRepeat);
}
template <Misc::SameAsWithoutCvref<AiSequence::AiWanderDuration> T>
void decompose(T&& v, const auto& f)
{
std::uint32_t unused = 0;
f(v.mRemainingDuration, unused);
}
template <Misc::SameAsWithoutCvref<AiSequence::AiTravelData> T>
void decompose(T&& v, const auto& f)
{
f(v.mX, v.mY, v.mZ);
}
template <Misc::SameAsWithoutCvref<AiSequence::AiEscortData> T>
void decompose(T&& v, const auto& f)
{
f(v.mX, v.mY, v.mZ, v.mDuration);
}
namespace AiSequence namespace AiSequence
{ {
void AiWander::load(ESMReader& esm) void AiWander::load(ESMReader& esm)
{ {
esm.getHNT("DATA", mData.mDistance, mData.mDuration, mData.mTimeOfDay, mData.mIdle, mData.mShouldRepeat); esm.getNamedComposite("DATA", mData);
esm.getHNT("STAR", mDurationData.mRemainingDuration, mDurationData.mUnused); // was mStartTime esm.getNamedComposite("STAR", mDurationData); // was mStartTime
mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues); mStoredInitialActorPosition = esm.getHNOT("POS_", mInitialActorPosition.mValues);
} }
void AiWander::save(ESMWriter& esm) const void AiWander::save(ESMWriter& esm) const
{ {
esm.writeHNT("DATA", mData); esm.writeNamedComposite("DATA", mData);
esm.writeHNT("STAR", mDurationData); esm.writeNamedComposite("STAR", mDurationData); // was mStartTime
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
esm.writeHNT("POS_", mInitialActorPosition); esm.writeHNT("POS_", mInitialActorPosition.mValues);
} }
void AiTravel::load(ESMReader& esm) void AiTravel::load(ESMReader& esm)
{ {
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ); esm.getNamedComposite("DATA", mData);
esm.getHNT(mHidden, "HIDD"); esm.getHNT(mHidden, "HIDD");
mRepeat = false; mRepeat = false;
esm.getHNOT(mRepeat, "REPT"); esm.getHNOT(mRepeat, "REPT");
@ -36,7 +62,7 @@ namespace ESM
void AiTravel::save(ESMWriter& esm) const void AiTravel::save(ESMWriter& esm) const
{ {
esm.writeHNT("DATA", mData); esm.writeNamedComposite("DATA", mData);
esm.writeHNT("HIDD", mHidden); esm.writeHNT("HIDD", mHidden);
if (mRepeat) if (mRepeat)
esm.writeHNT("REPT", mRepeat); esm.writeHNT("REPT", mRepeat);
@ -44,7 +70,7 @@ namespace ESM
void AiEscort::load(ESMReader& esm) void AiEscort::load(ESMReader& esm)
{ {
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration); esm.getNamedComposite("DATA", mData);
mTargetId = esm.getHNRefId("TARG"); mTargetId = esm.getHNRefId("TARG");
mTargetActorId = -1; mTargetActorId = -1;
esm.getHNOT(mTargetActorId, "TAID"); esm.getHNOT(mTargetActorId, "TAID");
@ -64,7 +90,7 @@ namespace ESM
void AiEscort::save(ESMWriter& esm) const void AiEscort::save(ESMWriter& esm) const
{ {
esm.writeHNT("DATA", mData); esm.writeNamedComposite("DATA", mData);
esm.writeHNRefId("TARG", mTargetId); esm.writeHNRefId("TARG", mTargetId);
esm.writeHNT("TAID", mTargetActorId); esm.writeHNT("TAID", mTargetActorId);
esm.writeHNT("DURA", mRemainingDuration); esm.writeHNT("DURA", mRemainingDuration);
@ -76,7 +102,7 @@ namespace ESM
void AiFollow::load(ESMReader& esm) void AiFollow::load(ESMReader& esm)
{ {
esm.getHNT("DATA", mData.mX, mData.mY, mData.mZ, mData.mDuration); esm.getNamedComposite("DATA", mData);
mTargetId = esm.getHNRefId("TARG"); mTargetId = esm.getHNRefId("TARG");
mTargetActorId = -1; mTargetActorId = -1;
esm.getHNOT(mTargetActorId, "TAID"); esm.getHNOT(mTargetActorId, "TAID");
@ -101,7 +127,7 @@ namespace ESM
void AiFollow::save(ESMWriter& esm) const void AiFollow::save(ESMWriter& esm) const
{ {
esm.writeHNT("DATA", mData); esm.writeNamedComposite("DATA", mData);
esm.writeHNRefId("TARG", mTargetId); esm.writeHNRefId("TARG", mTargetId);
esm.writeHNT("TAID", mTargetActorId); esm.writeHNT("TAID", mTargetActorId);
esm.writeHNT("DURA", mRemainingDuration); esm.writeHNT("DURA", mRemainingDuration);

View file

@ -36,32 +36,31 @@ namespace ESM
virtual ~AiPackage() {} virtual ~AiPackage() {}
}; };
#pragma pack(push, 1)
struct AiWanderData struct AiWanderData
{ {
int16_t mDistance; int16_t mDistance;
int16_t mDuration; int16_t mDuration;
unsigned char mTimeOfDay; std::uint8_t mTimeOfDay;
unsigned char mIdle[8]; std::uint8_t mIdle[8];
unsigned char mShouldRepeat; std::uint8_t mShouldRepeat;
}; };
struct AiWanderDuration struct AiWanderDuration
{ {
float mRemainingDuration; float mRemainingDuration;
int32_t mUnused;
}; };
struct AiTravelData struct AiTravelData
{ {
float mX, mY, mZ; float mX, mY, mZ;
}; };
struct AiEscortData struct AiEscortData
{ {
float mX, mY, mZ; float mX, mY, mZ;
int16_t mDuration; int16_t mDuration;
}; };
#pragma pack(pop)
struct AiWander : AiPackage struct AiWander : AiPackage
{ {
AiWanderData mData; AiWanderData mData;

View file

@ -12,8 +12,10 @@
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
#include "components/esm/decompose.hpp"
#include "components/esm/esmcommon.hpp" #include "components/esm/esmcommon.hpp"
#include "components/esm/refid.hpp" #include "components/esm/refid.hpp"
#include "loadtes3.hpp" #include "loadtes3.hpp"
namespace ESM namespace ESM
@ -177,6 +179,16 @@ namespace ESM
(getT(args), ...); (getT(args), ...);
} }
void getNamedComposite(NAME name, auto& value)
{
decompose(value, [&](auto&... args) { getHNT(name, args...); });
}
void getComposite(auto& value)
{
decompose(value, [&](auto&... args) { (getT(args), ...); });
}
template <typename T, typename = std::enable_if_t<IsReadable<T>>> template <typename T, typename = std::enable_if_t<IsReadable<T>>>
void skipHT() void skipHT()
{ {

View file

@ -5,6 +5,7 @@
#include <list> #include <list>
#include <type_traits> #include <type_traits>
#include "components/esm/decompose.hpp"
#include "components/esm/esmcommon.hpp" #include "components/esm/esmcommon.hpp"
#include "components/esm/refid.hpp" #include "components/esm/refid.hpp"
@ -121,6 +122,20 @@ namespace ESM
endRecord(name); endRecord(name);
} }
void writeNamedComposite(NAME name, const auto& value)
{
decompose(value, [&](const auto&... args) {
startSubRecord(name);
(writeT(args), ...);
endRecord(name);
});
}
void writeComposite(const auto& value)
{
decompose(value, [&](const auto&... args) { (writeT(args), ...); });
}
// Prevent using writeHNT with strings. This already happened by accident and results in // Prevent using writeHNT with strings. This already happened by accident and results in
// state being discarded without any error on writing or reading it. :( // state being discarded without any error on writing or reading it. :(
// writeHNString and friends must be used instead. // writeHNString and friends must be used instead.
@ -132,7 +147,7 @@ namespace ESM
void writeHNT(NAME name, const T (&data)[size], int) = delete; void writeHNT(NAME name, const T (&data)[size], int) = delete;
template <typename T> template <typename T>
void writeHNT(NAME name, const T& data, int size) void writeHNT(NAME name, const T& data, std::size_t size)
{ {
startSubRecord(name); startSubRecord(name);
writeT(data, size); writeT(data, size);

View file

@ -4,12 +4,19 @@
#include <sstream> #include <sstream>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/concepts.hpp>
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
namespace ESM namespace ESM
{ {
template <Misc::SameAsWithoutCvref<Script::SCHDstruct> T>
void decompose(T&& v, const auto& f)
{
f(v.mNumShorts, v.mNumLongs, v.mNumFloats, v.mScriptDataSize, v.mStringTableSize);
}
void Script::loadSCVR(ESMReader& esm) void Script::loadSCVR(ESMReader& esm)
{ {
uint32_t s = mData.mStringTableSize; uint32_t s = mData.mStringTableSize;
@ -99,11 +106,7 @@ namespace ESM
{ {
esm.getSubHeader(); esm.getSubHeader();
mId = esm.getMaybeFixedRefIdSize(32); mId = esm.getMaybeFixedRefIdSize(32);
esm.getT(mData.mNumShorts); esm.getComposite(mData);
esm.getT(mData.mNumLongs);
esm.getT(mData.mNumFloats);
esm.getT(mData.mScriptDataSize);
esm.getT(mData.mStringTableSize);
hasHeader = true; hasHeader = true;
break; break;
@ -157,7 +160,7 @@ namespace ESM
esm.startSubRecord("SCHD"); esm.startSubRecord("SCHD");
esm.writeMaybeFixedSizeRefId(mId, 32); esm.writeMaybeFixedSizeRefId(mId, 32);
esm.writeT(mData, 20); esm.writeComposite(mData);
esm.endRecord("SCHD"); esm.endRecord("SCHD");
if (isDeleted) if (isDeleted)

View file

@ -0,0 +1,13 @@
#ifndef OPENMW_COMPONENTS_MISC_CONCEPTS_H
#define OPENMW_COMPONENTS_MISC_CONCEPTS_H
#include <concepts>
#include <type_traits>
namespace Misc
{
template <class T, class U>
concept SameAsWithoutCvref = std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<U>>;
}
#endif