mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 17:09:41 +00:00
Merge branch 'esmstuff' into 'master'
Use more decomposition, string_view, and implicit sizes in ESM code See merge request OpenMW/openmw!3975
This commit is contained in:
commit
e8038d353f
12 changed files with 124 additions and 142 deletions
|
@ -897,9 +897,6 @@ namespace EsmTool
|
||||||
if (const ESM::Land::LandData* data = mData.getLandData(mData.mDataTypes))
|
if (const ESM::Land::LandData* data = mData.getLandData(mData.mDataTypes))
|
||||||
{
|
{
|
||||||
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
|
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
|
||||||
// Lots of missing members.
|
|
||||||
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
|
|
||||||
std::cout << " Unknown2: " << static_cast<unsigned>(data->mUnk2) << std::endl;
|
|
||||||
}
|
}
|
||||||
mData.unloadData();
|
mData.unloadData();
|
||||||
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
std::cout << " Deleted: " << mIsDeleted << std::endl;
|
||||||
|
|
|
@ -232,7 +232,7 @@ namespace ESSImport
|
||||||
esm.skip(4);
|
esm.skip(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.getExact(nam8, 32);
|
esm.getT(nam8);
|
||||||
|
|
||||||
newcell.mFogOfWar.reserve(16 * 16);
|
newcell.mFogOfWar.reserve(16 * 16);
|
||||||
for (int x = 0; x < 16; ++x)
|
for (int x = 0; x < 16; ++x)
|
||||||
|
|
|
@ -1,10 +1,30 @@
|
||||||
#include "importcellref.hpp"
|
#include "importcellref.hpp"
|
||||||
|
|
||||||
#include <components/esm3/esmreader.hpp>
|
#include <components/esm3/esmreader.hpp>
|
||||||
|
#include <components/misc/concepts.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
{
|
{
|
||||||
|
template <Misc::SameAsWithoutCvref<ACDT> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mUnknown, v.mFlags, v.mBreathMeter, v.mUnknown2, v.mDynamic, v.mUnknown3, v.mAttributes, v.mMagicEffects,
|
||||||
|
v.mUnknown4, v.mGoldPool, v.mCountDown, v.mUnknown5);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<ACSC> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mUnknown1, v.mFlags, v.mUnknown2, v.mCorpseClearCountdown, v.mUnknown3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<ANIS> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
f(v.mGroupIndex, v.mUnknown, v.mTime);
|
||||||
|
}
|
||||||
|
|
||||||
void CellRef::load(ESM::ESMReader& esm)
|
void CellRef::load(ESM::ESMReader& esm)
|
||||||
{
|
{
|
||||||
|
@ -45,14 +65,9 @@ namespace ESSImport
|
||||||
bool isDeleted = false;
|
bool isDeleted = false;
|
||||||
ESM::CellRef::loadData(esm, isDeleted);
|
ESM::CellRef::loadData(esm, isDeleted);
|
||||||
|
|
||||||
mActorData.mHasACDT
|
mActorData.mHasACDT = esm.getOptionalComposite("ACDT", mActorData.mACDT);
|
||||||
= esm.getHNOT("ACDT", mActorData.mACDT.mUnknown, mActorData.mACDT.mFlags, mActorData.mACDT.mBreathMeter,
|
|
||||||
mActorData.mACDT.mUnknown2, mActorData.mACDT.mDynamic, mActorData.mACDT.mUnknown3,
|
|
||||||
mActorData.mACDT.mAttributes, mActorData.mACDT.mMagicEffects, mActorData.mACDT.mUnknown4,
|
|
||||||
mActorData.mACDT.mGoldPool, mActorData.mACDT.mCountDown, mActorData.mACDT.mUnknown5);
|
|
||||||
|
|
||||||
mActorData.mHasACSC = esm.getHNOT("ACSC", mActorData.mACSC.mUnknown1, mActorData.mACSC.mFlags,
|
mActorData.mHasACSC = esm.getOptionalComposite("ACSC", mActorData.mACSC);
|
||||||
mActorData.mACSC.mUnknown2, mActorData.mACSC.mCorpseClearCountdown, mActorData.mACSC.mUnknown3);
|
|
||||||
|
|
||||||
if (esm.isNextSub("ACSL"))
|
if (esm.isNextSub("ACSL"))
|
||||||
esm.skipHSubSize(112);
|
esm.skipHSubSize(112);
|
||||||
|
@ -127,8 +142,7 @@ namespace ESSImport
|
||||||
if (esm.isNextSub("ND3D"))
|
if (esm.isNextSub("ND3D"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
mActorData.mHasANIS
|
mActorData.mHasANIS = esm.getOptionalComposite("ANIS", mActorData.mANIS);
|
||||||
= esm.getHNOT("ANIS", mActorData.mANIS.mGroupIndex, mActorData.mANIS.mUnknown, mActorData.mANIS.mTime);
|
|
||||||
|
|
||||||
if (esm.isNextSub("LVCR"))
|
if (esm.isNextSub("LVCR"))
|
||||||
{
|
{
|
||||||
|
@ -146,7 +160,7 @@ namespace ESSImport
|
||||||
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
|
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
|
||||||
// alarmvoi0000.ess
|
// alarmvoi0000.ess
|
||||||
for (int i = 0; i < 2; ++i)
|
for (int i = 0; i < 2; ++i)
|
||||||
esm.getHNOT("DATA", mPos.pos, mPos.rot);
|
esm.getOptionalComposite("DATA", mPos);
|
||||||
|
|
||||||
mDeleted = 0;
|
mDeleted = 0;
|
||||||
if (esm.isNextSub("DELE"))
|
if (esm.isNextSub("DELE"))
|
||||||
|
|
|
@ -135,7 +135,7 @@ namespace ESSImport
|
||||||
sub.mFileOffset = esm.getFileOffset();
|
sub.mFileOffset = esm.getFileOffset();
|
||||||
sub.mName = esm.retSubName().toString();
|
sub.mName = esm.retSubName().toString();
|
||||||
sub.mData.resize(esm.getSubSize());
|
sub.mData.resize(esm.getSubSize());
|
||||||
esm.getExact(&sub.mData[0], sub.mData.size());
|
esm.getExact(sub.mData.data(), sub.mData.size());
|
||||||
rec.mSubrecords.push_back(sub);
|
rec.mSubrecords.push_back(sub);
|
||||||
}
|
}
|
||||||
file.mRecords.push_back(rec);
|
file.mRecords.push_back(rec);
|
||||||
|
|
|
@ -38,7 +38,7 @@ std::string ESM::loadLuaBinaryData(ESMReader& esm)
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
data.resize(esm.getSubSize());
|
data.resize(esm.getSubSize());
|
||||||
esm.getExact(data.data(), static_cast<int>(data.size()));
|
esm.getExact(data.data(), data.size());
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
if (isNextSub(name))
|
if (isNextSub(name))
|
||||||
return getHString();
|
return getHString();
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::RefId ESMReader::getHNORefId(NAME name)
|
ESM::RefId ESMReader::getHNORefId(NAME name)
|
||||||
|
@ -244,21 +244,6 @@ namespace ESM
|
||||||
skipHString();
|
skipHString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMReader::getHExact(void* p, std::size_t size)
|
|
||||||
{
|
|
||||||
getSubHeader();
|
|
||||||
if (size != mCtx.leftSub)
|
|
||||||
reportSubSizeMismatch(size, mCtx.leftSub);
|
|
||||||
getExact(p, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the given number of bytes from a named subrecord
|
|
||||||
void ESMReader::getHNExact(void* p, std::size_t size, NAME name)
|
|
||||||
{
|
|
||||||
getSubNameIs(name);
|
|
||||||
getHExact(p, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
FormId ESMReader::getFormId(bool wide, NAME tag)
|
FormId ESMReader::getFormId(bool wide, NAME tag)
|
||||||
{
|
{
|
||||||
FormId res;
|
FormId res;
|
||||||
|
@ -316,7 +301,7 @@ namespace ESM
|
||||||
|
|
||||||
// reading the subrecord data anyway.
|
// reading the subrecord data anyway.
|
||||||
const std::size_t subNameSize = decltype(mCtx.subName)::sCapacity;
|
const std::size_t subNameSize = decltype(mCtx.subName)::sCapacity;
|
||||||
getExact(mCtx.subName.mData, static_cast<int>(subNameSize));
|
getExact(mCtx.subName.mData, subNameSize);
|
||||||
mCtx.leftRec -= static_cast<std::uint32_t>(subNameSize);
|
mCtx.leftRec -= static_cast<std::uint32_t>(subNameSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,7 +491,7 @@ namespace ESM
|
||||||
case RefIdType::Generated:
|
case RefIdType::Generated:
|
||||||
{
|
{
|
||||||
std::uint64_t generated{};
|
std::uint64_t generated{};
|
||||||
getExact(&generated, sizeof(std::uint64_t));
|
getT(generated);
|
||||||
return RefId::generated(generated);
|
return RefId::generated(generated);
|
||||||
}
|
}
|
||||||
case RefIdType::Index:
|
case RefIdType::Index:
|
||||||
|
@ -514,14 +499,14 @@ namespace ESM
|
||||||
RecNameInts recordType{};
|
RecNameInts recordType{};
|
||||||
getExact(&recordType, sizeof(std::uint32_t));
|
getExact(&recordType, sizeof(std::uint32_t));
|
||||||
std::uint32_t index{};
|
std::uint32_t index{};
|
||||||
getExact(&index, sizeof(std::uint32_t));
|
getT(index);
|
||||||
return RefId::index(recordType, index);
|
return RefId::index(recordType, index);
|
||||||
}
|
}
|
||||||
case RefIdType::ESM3ExteriorCell:
|
case RefIdType::ESM3ExteriorCell:
|
||||||
{
|
{
|
||||||
int32_t x, y;
|
int32_t x, y;
|
||||||
getExact(&x, sizeof(std::int32_t));
|
getT(x);
|
||||||
getExact(&y, sizeof(std::int32_t));
|
getT(y);
|
||||||
return RefId::esm3ExteriorCell(x, y);
|
return RefId::esm3ExteriorCell(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,7 +514,7 @@ namespace ESM
|
||||||
fail("Unsupported RefIdType: " + std::to_string(static_cast<unsigned>(refIdType)));
|
fail("Unsupported RefIdType: " + std::to_string(static_cast<unsigned>(refIdType)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void ESMReader::fail(const std::string& msg)
|
[[noreturn]] void ESMReader::fail(std::string_view msg)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
|
|
|
@ -237,12 +237,6 @@ namespace ESM
|
||||||
|
|
||||||
void skipHRefId();
|
void skipHRefId();
|
||||||
|
|
||||||
// Read the given number of bytes from a subrecord
|
|
||||||
void getHExact(void* p, std::size_t size);
|
|
||||||
|
|
||||||
// Read the given number of bytes from a named subrecord
|
|
||||||
void getHNExact(void* p, std::size_t size, NAME name);
|
|
||||||
|
|
||||||
ESM::FormId getFormId(bool wide = false, NAME tag = "FRMR");
|
ESM::FormId getFormId(bool wide = false, NAME tag = "FRMR");
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
|
@ -354,7 +348,7 @@ namespace ESM
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used for error handling
|
/// Used for error handling
|
||||||
[[noreturn]] void fail(const std::string& msg);
|
[[noreturn]] void fail(std::string_view msg);
|
||||||
|
|
||||||
/// Sets font encoder for ESM strings
|
/// Sets font encoder for ESM strings
|
||||||
void setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; }
|
void setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; }
|
||||||
|
|
|
@ -97,14 +97,14 @@ namespace ESM
|
||||||
mHeader.mData.type = type;
|
mHeader.mData.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::setAuthor(const std::string& auth)
|
void ESMWriter::setAuthor(std::string_view auth)
|
||||||
{
|
{
|
||||||
mHeader.mData.author.assign(auth);
|
mHeader.mData.author = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::setDescription(const std::string& desc)
|
void ESMWriter::setDescription(std::string_view desc)
|
||||||
{
|
{
|
||||||
mHeader.mData.desc.assign(desc);
|
mHeader.mData.desc = desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::setRecordCount(int count)
|
void ESMWriter::setRecordCount(int count)
|
||||||
|
@ -122,7 +122,7 @@ namespace ESM
|
||||||
mHeader.mMaster.clear();
|
mHeader.mMaster.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::addMaster(const std::string& name, uint64_t size)
|
void ESMWriter::addMaster(std::string_view name, uint64_t size)
|
||||||
{
|
{
|
||||||
Header::MasterData d;
|
Header::MasterData d;
|
||||||
d.name = name;
|
d.name = name;
|
||||||
|
@ -208,14 +208,14 @@ namespace ESM
|
||||||
endRecord(NAME(name));
|
endRecord(NAME(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::writeHNString(NAME name, const std::string& data)
|
void ESMWriter::writeHNString(NAME name, std::string_view data)
|
||||||
{
|
{
|
||||||
startSubRecord(name);
|
startSubRecord(name);
|
||||||
writeHString(data);
|
writeHString(data);
|
||||||
endRecord(name);
|
endRecord(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::writeHNString(NAME name, const std::string& data, size_t size)
|
void ESMWriter::writeHNString(NAME name, std::string_view data, size_t size)
|
||||||
{
|
{
|
||||||
assert(data.size() <= size);
|
assert(data.size() <= size);
|
||||||
startSubRecord(name);
|
startSubRecord(name);
|
||||||
|
@ -278,9 +278,9 @@ namespace ESM
|
||||||
write(string.c_str(), string.size());
|
write(string.c_str(), string.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::writeHString(const std::string& data)
|
void ESMWriter::writeHString(std::string_view data)
|
||||||
{
|
{
|
||||||
if (data.size() == 0)
|
if (data.empty())
|
||||||
write("\0", 1);
|
write("\0", 1);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -291,7 +291,7 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMWriter::writeHCString(const std::string& data)
|
void ESMWriter::writeHCString(std::string_view data)
|
||||||
{
|
{
|
||||||
writeHString(data);
|
writeHString(data);
|
||||||
if (data.size() > 0 && data[data.size() - 1] != '\0')
|
if (data.size() > 0 && data[data.size() - 1] != '\0')
|
||||||
|
|
|
@ -38,8 +38,8 @@ namespace ESM
|
||||||
void setVersion(unsigned int ver = 0x3fa66666);
|
void setVersion(unsigned int ver = 0x3fa66666);
|
||||||
void setType(int type);
|
void setType(int type);
|
||||||
void setEncoder(ToUTF8::Utf8Encoder* encoding);
|
void setEncoder(ToUTF8::Utf8Encoder* encoding);
|
||||||
void setAuthor(const std::string& author);
|
void setAuthor(std::string_view author);
|
||||||
void setDescription(const std::string& desc);
|
void setDescription(std::string_view desc);
|
||||||
void setHeader(const Header& value) { mHeader = value; }
|
void setHeader(const Header& value) { mHeader = value; }
|
||||||
|
|
||||||
// Set the record count for writing it in the file header
|
// Set the record count for writing it in the file header
|
||||||
|
@ -54,7 +54,7 @@ namespace ESM
|
||||||
|
|
||||||
void clearMaster();
|
void clearMaster();
|
||||||
|
|
||||||
void addMaster(const std::string& name, uint64_t size);
|
void addMaster(std::string_view name, uint64_t size);
|
||||||
|
|
||||||
void save(std::ostream& file);
|
void save(std::ostream& file);
|
||||||
///< Start saving a file by writing the TES3 header.
|
///< Start saving a file by writing the TES3 header.
|
||||||
|
@ -62,20 +62,20 @@ namespace ESM
|
||||||
void close();
|
void close();
|
||||||
///< \note Does not close the stream.
|
///< \note Does not close the stream.
|
||||||
|
|
||||||
void writeHNString(NAME name, const std::string& data);
|
void writeHNString(NAME name, std::string_view data);
|
||||||
void writeHNString(NAME name, const std::string& data, size_t size);
|
void writeHNString(NAME name, std::string_view data, size_t size);
|
||||||
void writeHNCString(NAME name, const std::string& data)
|
void writeHNCString(NAME name, std::string_view data)
|
||||||
{
|
{
|
||||||
startSubRecord(name);
|
startSubRecord(name);
|
||||||
writeHCString(data);
|
writeHCString(data);
|
||||||
endRecord(name);
|
endRecord(name);
|
||||||
}
|
}
|
||||||
void writeHNOString(NAME name, const std::string& data)
|
void writeHNOString(NAME name, std::string_view data)
|
||||||
{
|
{
|
||||||
if (!data.empty())
|
if (!data.empty())
|
||||||
writeHNString(name, data);
|
writeHNString(name, data);
|
||||||
}
|
}
|
||||||
void writeHNOCString(NAME name, const std::string& data)
|
void writeHNOCString(NAME name, std::string_view data)
|
||||||
{
|
{
|
||||||
if (!data.empty())
|
if (!data.empty())
|
||||||
writeHNCString(name, data);
|
writeHNCString(name, data);
|
||||||
|
@ -140,6 +140,7 @@ namespace ESM
|
||||||
// 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.
|
||||||
void writeHNT(NAME name, const std::string& data) = delete;
|
void writeHNT(NAME name, const std::string& data) = delete;
|
||||||
|
void writeHNT(NAME name, std::string_view data) = delete;
|
||||||
|
|
||||||
void writeT(NAME data) = delete;
|
void writeT(NAME data) = delete;
|
||||||
|
|
||||||
|
@ -181,8 +182,8 @@ namespace ESM
|
||||||
void endRecord(NAME name);
|
void endRecord(NAME name);
|
||||||
void endRecord(uint32_t name);
|
void endRecord(uint32_t name);
|
||||||
void writeMaybeFixedSizeString(const std::string& data, std::size_t size);
|
void writeMaybeFixedSizeString(const std::string& data, std::size_t size);
|
||||||
void writeHString(const std::string& data);
|
void writeHString(std::string_view data);
|
||||||
void writeHCString(const std::string& data);
|
void writeHCString(std::string_view data);
|
||||||
|
|
||||||
void writeMaybeFixedSizeRefId(RefId value, std::size_t size);
|
void writeMaybeFixedSizeRefId(RefId value, std::size_t size);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
|
#ifndef OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
|
||||||
#define OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
|
#define OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
|
@ -22,24 +23,20 @@ namespace ESM
|
||||||
// Initial reference height for the first vertex, only needed for filling mHeights
|
// Initial reference height for the first vertex, only needed for filling mHeights
|
||||||
float mHeightOffset = 0;
|
float mHeightOffset = 0;
|
||||||
// Height in world space for each vertex
|
// Height in world space for each vertex
|
||||||
float mHeights[sLandNumVerts];
|
std::array<float, sLandNumVerts> mHeights;
|
||||||
float mMinHeight = 0;
|
float mMinHeight = 0;
|
||||||
float mMaxHeight = 0;
|
float mMaxHeight = 0;
|
||||||
|
|
||||||
// 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage.
|
// 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage.
|
||||||
std::int8_t mNormals[sLandNumVerts * 3];
|
std::array<std::int8_t, 3 * sLandNumVerts> mNormals;
|
||||||
|
|
||||||
// 2D array of texture indices. An index can be used to look up an LandTexture,
|
// 2D array of texture indices. An index can be used to look up an LandTexture,
|
||||||
// but to do so you must subtract 1 from the index first!
|
// but to do so you must subtract 1 from the index first!
|
||||||
// An index of 0 indicates the default texture.
|
// An index of 0 indicates the default texture.
|
||||||
std::uint16_t mTextures[sLandNumTextures];
|
std::array<std::uint16_t, sLandNumTextures> mTextures;
|
||||||
|
|
||||||
// 24-bit RGB color for each vertex
|
// 24-bit RGB color for each vertex
|
||||||
std::uint8_t mColours[3 * sLandNumVerts];
|
std::array<std::uint8_t, 3 * sLandNumVerts> mColours;
|
||||||
|
|
||||||
// ???
|
|
||||||
std::uint16_t mUnk1 = 0;
|
|
||||||
std::uint8_t mUnk2 = 0;
|
|
||||||
|
|
||||||
int mDataLoaded = 0;
|
int mDataLoaded = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "components/esm/defs.hpp"
|
#include <components/esm/defs.hpp>
|
||||||
|
#include <components/misc/concepts.hpp>
|
||||||
|
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
@ -13,27 +15,43 @@ namespace ESM
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct VHGT
|
||||||
|
{
|
||||||
|
float mHeightOffset;
|
||||||
|
std::int8_t mHeightData[LandRecordData::sLandNumVerts];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <Misc::SameAsWithoutCvref<VHGT> T>
|
||||||
|
void decompose(T&& v, const auto& f)
|
||||||
|
{
|
||||||
|
char padding[3] = { 0, 0, 0 };
|
||||||
|
f(v.mHeightOffset, v.mHeightData, padding);
|
||||||
|
}
|
||||||
|
|
||||||
void transposeTextureData(const std::uint16_t* in, std::uint16_t* out)
|
void transposeTextureData(const std::uint16_t* in, std::uint16_t* out)
|
||||||
{
|
{
|
||||||
int readPos = 0; // bit ugly, but it works
|
size_t readPos = 0; // bit ugly, but it works
|
||||||
for (int y1 = 0; y1 < 4; y1++)
|
for (size_t y1 = 0; y1 < 4; y1++)
|
||||||
for (int x1 = 0; x1 < 4; x1++)
|
for (size_t x1 = 0; x1 < 4; x1++)
|
||||||
for (int y2 = 0; y2 < 4; y2++)
|
for (size_t y2 = 0; y2 < 4; y2++)
|
||||||
for (int x2 = 0; x2 < 4; x2++)
|
for (size_t x2 = 0; x2 < 4; x2++)
|
||||||
out[(y1 * 4 + y2) * 16 + (x1 * 4 + x2)] = in[readPos++];
|
out[(y1 * 4 + y2) * 16 + (x1 * 4 + x2)] = in[readPos++];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads data and marks it as loaded. Return true if data is actually loaded from reader, false otherwise
|
// Loads data and marks it as loaded. Return true if data is actually loaded from reader, false otherwise
|
||||||
// including the case when data is already loaded.
|
// including the case when data is already loaded.
|
||||||
bool condLoad(ESMReader& reader, int flags, int& targetFlags, int dataFlag, void* ptr, std::size_t size)
|
bool condLoad(ESMReader& reader, int flags, int& targetFlags, int dataFlag, auto& in)
|
||||||
{
|
{
|
||||||
if ((targetFlags & dataFlag) == 0 && (flags & dataFlag) != 0)
|
if ((targetFlags & dataFlag) == 0 && (flags & dataFlag) != 0)
|
||||||
{
|
{
|
||||||
reader.getHExact(ptr, size);
|
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(in)>, VHGT>)
|
||||||
|
reader.getSubComposite(in);
|
||||||
|
else
|
||||||
|
reader.getHT(in);
|
||||||
targetFlags |= dataFlag;
|
targetFlags |= dataFlag;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
reader.skipHSubSize(size);
|
reader.skipHSub();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +68,7 @@ namespace ESM
|
||||||
switch (esm.retSubName().toInt())
|
switch (esm.retSubName().toInt())
|
||||||
{
|
{
|
||||||
case fourCC("INTV"):
|
case fourCC("INTV"):
|
||||||
esm.getSubHeader();
|
esm.getHT(mX, mY);
|
||||||
if (esm.getSubSize() != 8)
|
|
||||||
esm.fail("Subrecord size is not equal to 8");
|
|
||||||
esm.getT(mX);
|
|
||||||
esm.getT(mY);
|
|
||||||
hasLocation = true;
|
hasLocation = true;
|
||||||
break;
|
break;
|
||||||
case fourCC("DATA"):
|
case fourCC("DATA"):
|
||||||
|
@ -94,7 +108,7 @@ namespace ESM
|
||||||
mDataTypes |= DATA_VHGT;
|
mDataTypes |= DATA_VHGT;
|
||||||
break;
|
break;
|
||||||
case fourCC("WNAM"):
|
case fourCC("WNAM"):
|
||||||
esm.getHExact(mWnam.data(), mWnam.size());
|
esm.getHT(mWnam);
|
||||||
mDataTypes |= DATA_WNAM;
|
mDataTypes |= DATA_WNAM;
|
||||||
break;
|
break;
|
||||||
case fourCC("VCLR"):
|
case fourCC("VCLR"):
|
||||||
|
@ -137,12 +151,10 @@ namespace ESM
|
||||||
{
|
{
|
||||||
VHGT offsets;
|
VHGT offsets;
|
||||||
offsets.mHeightOffset = mLandData->mHeights[0] / HEIGHT_SCALE;
|
offsets.mHeightOffset = mLandData->mHeights[0] / HEIGHT_SCALE;
|
||||||
offsets.mUnk1 = mLandData->mUnk1;
|
|
||||||
offsets.mUnk2 = mLandData->mUnk2;
|
|
||||||
|
|
||||||
float prevY = mLandData->mHeights[0];
|
float prevY = mLandData->mHeights[0];
|
||||||
int number = 0; // avoid multiplication
|
size_t number = 0; // avoid multiplication
|
||||||
for (int i = 0; i < LAND_SIZE; ++i)
|
for (unsigned i = 0; i < LandRecordData::sLandSize; ++i)
|
||||||
{
|
{
|
||||||
float diff = (mLandData->mHeights[number] - prevY) / HEIGHT_SCALE;
|
float diff = (mLandData->mHeights[number] - prevY) / HEIGHT_SCALE;
|
||||||
offsets.mHeightData[number]
|
offsets.mHeightData[number]
|
||||||
|
@ -151,7 +163,7 @@ namespace ESM
|
||||||
float prevX = prevY = mLandData->mHeights[number];
|
float prevX = prevY = mLandData->mHeights[number];
|
||||||
++number;
|
++number;
|
||||||
|
|
||||||
for (int j = 1; j < LAND_SIZE; ++j)
|
for (unsigned j = 1; j < LandRecordData::sLandSize; ++j)
|
||||||
{
|
{
|
||||||
diff = (mLandData->mHeights[number] - prevX) / HEIGHT_SCALE;
|
diff = (mLandData->mHeights[number] - prevX) / HEIGHT_SCALE;
|
||||||
offsets.mHeightData[number]
|
offsets.mHeightData[number]
|
||||||
|
@ -161,7 +173,7 @@ namespace ESM
|
||||||
++number;
|
++number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
esm.writeHNT("VHGT", offsets, sizeof(VHGT));
|
esm.writeNamedComposite("VHGT", offsets);
|
||||||
}
|
}
|
||||||
if (mDataTypes & Land::DATA_WNAM)
|
if (mDataTypes & Land::DATA_WNAM)
|
||||||
{
|
{
|
||||||
|
@ -169,13 +181,15 @@ namespace ESM
|
||||||
std::int8_t wnam[LAND_GLOBAL_MAP_LOD_SIZE];
|
std::int8_t wnam[LAND_GLOBAL_MAP_LOD_SIZE];
|
||||||
constexpr float max = std::numeric_limits<std::int8_t>::max();
|
constexpr float max = std::numeric_limits<std::int8_t>::max();
|
||||||
constexpr float min = std::numeric_limits<std::int8_t>::min();
|
constexpr float min = std::numeric_limits<std::int8_t>::min();
|
||||||
constexpr float vertMult = static_cast<float>(Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;
|
constexpr float vertMult
|
||||||
for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)
|
= static_cast<float>(LandRecordData::sLandSize - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;
|
||||||
|
for (unsigned row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)
|
||||||
{
|
{
|
||||||
for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)
|
for (unsigned col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)
|
||||||
{
|
{
|
||||||
float height = mLandData->mHeights[static_cast<int>(row * vertMult) * Land::LAND_SIZE
|
float height
|
||||||
+ static_cast<int>(col * vertMult)];
|
= mLandData->mHeights[static_cast<size_t>(row * vertMult) * LandRecordData::sLandSize
|
||||||
|
+ static_cast<size_t>(col * vertMult)];
|
||||||
height /= height > 0 ? 128.f : 16.f;
|
height /= height > 0 ? 128.f : 16.f;
|
||||||
height = std::clamp(height, min, max);
|
height = std::clamp(height, min, max);
|
||||||
wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast<std::int8_t>(height);
|
wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast<std::int8_t>(height);
|
||||||
|
@ -189,8 +203,8 @@ namespace ESM
|
||||||
}
|
}
|
||||||
if (mDataTypes & Land::DATA_VTEX)
|
if (mDataTypes & Land::DATA_VTEX)
|
||||||
{
|
{
|
||||||
uint16_t vtex[LAND_NUM_TEXTURES];
|
uint16_t vtex[LandRecordData::sLandNumTextures];
|
||||||
transposeTextureData(mLandData->mTextures, vtex);
|
transposeTextureData(mLandData->mTextures.data(), vtex);
|
||||||
esm.writeHNT("VTEX", vtex);
|
esm.writeHNT("VTEX", vtex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,25 +214,23 @@ namespace ESM
|
||||||
{
|
{
|
||||||
setPlugin(0);
|
setPlugin(0);
|
||||||
|
|
||||||
std::fill(std::begin(mWnam), std::end(mWnam), 0);
|
mWnam.fill(0);
|
||||||
|
|
||||||
if (mLandData == nullptr)
|
if (mLandData == nullptr)
|
||||||
mLandData = std::make_unique<LandData>();
|
mLandData = std::make_unique<LandData>();
|
||||||
|
|
||||||
mLandData->mHeightOffset = 0;
|
mLandData->mHeightOffset = 0;
|
||||||
std::fill(std::begin(mLandData->mHeights), std::end(mLandData->mHeights), 0.0f);
|
mLandData->mHeights.fill(0);
|
||||||
mLandData->mMinHeight = 0;
|
mLandData->mMinHeight = 0;
|
||||||
mLandData->mMaxHeight = 0;
|
mLandData->mMaxHeight = 0;
|
||||||
for (int i = 0; i < LAND_NUM_VERTS; ++i)
|
for (size_t i = 0; i < LandRecordData::sLandNumVerts; ++i)
|
||||||
{
|
{
|
||||||
mLandData->mNormals[i * 3 + 0] = 0;
|
mLandData->mNormals[i * 3 + 0] = 0;
|
||||||
mLandData->mNormals[i * 3 + 1] = 0;
|
mLandData->mNormals[i * 3 + 1] = 0;
|
||||||
mLandData->mNormals[i * 3 + 2] = 127;
|
mLandData->mNormals[i * 3 + 2] = 127;
|
||||||
}
|
}
|
||||||
std::fill(std::begin(mLandData->mTextures), std::end(mLandData->mTextures), 0);
|
mLandData->mTextures.fill(0);
|
||||||
std::fill(std::begin(mLandData->mColours), std::end(mLandData->mColours), 255);
|
mLandData->mColours.fill(255);
|
||||||
mLandData->mUnk1 = 0;
|
|
||||||
mLandData->mUnk2 = 0;
|
|
||||||
mLandData->mDataLoaded
|
mLandData->mDataLoaded
|
||||||
= Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | Land::DATA_VCLR | Land::DATA_VTEX;
|
= Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | Land::DATA_VCLR | Land::DATA_VTEX;
|
||||||
mDataTypes = mLandData->mDataLoaded;
|
mDataTypes = mLandData->mDataLoaded;
|
||||||
|
@ -259,32 +271,32 @@ namespace ESM
|
||||||
|
|
||||||
if (reader.isNextSub("VNML"))
|
if (reader.isNextSub("VNML"))
|
||||||
{
|
{
|
||||||
condLoad(reader, flags, data.mDataLoaded, DATA_VNML, data.mNormals, sizeof(data.mNormals));
|
condLoad(reader, flags, data.mDataLoaded, DATA_VNML, data.mNormals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.isNextSub("VHGT"))
|
if (reader.isNextSub("VHGT"))
|
||||||
{
|
{
|
||||||
VHGT vhgt;
|
VHGT vhgt;
|
||||||
if (condLoad(reader, flags, data.mDataLoaded, DATA_VHGT, &vhgt, sizeof(vhgt)))
|
if (condLoad(reader, flags, data.mDataLoaded, DATA_VHGT, vhgt))
|
||||||
{
|
{
|
||||||
data.mMinHeight = std::numeric_limits<float>::max();
|
data.mMinHeight = std::numeric_limits<float>::max();
|
||||||
data.mMaxHeight = -std::numeric_limits<float>::max();
|
data.mMaxHeight = -std::numeric_limits<float>::max();
|
||||||
float rowOffset = vhgt.mHeightOffset;
|
float rowOffset = vhgt.mHeightOffset;
|
||||||
for (int y = 0; y < LAND_SIZE; y++)
|
for (unsigned y = 0; y < LandRecordData::sLandSize; y++)
|
||||||
{
|
{
|
||||||
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
|
rowOffset += vhgt.mHeightData[y * LandRecordData::sLandSize];
|
||||||
|
|
||||||
data.mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE;
|
data.mHeights[y * LandRecordData::sLandSize] = rowOffset * HEIGHT_SCALE;
|
||||||
if (rowOffset * HEIGHT_SCALE > data.mMaxHeight)
|
if (rowOffset * HEIGHT_SCALE > data.mMaxHeight)
|
||||||
data.mMaxHeight = rowOffset * HEIGHT_SCALE;
|
data.mMaxHeight = rowOffset * HEIGHT_SCALE;
|
||||||
if (rowOffset * HEIGHT_SCALE < data.mMinHeight)
|
if (rowOffset * HEIGHT_SCALE < data.mMinHeight)
|
||||||
data.mMinHeight = rowOffset * HEIGHT_SCALE;
|
data.mMinHeight = rowOffset * HEIGHT_SCALE;
|
||||||
|
|
||||||
float colOffset = rowOffset;
|
float colOffset = rowOffset;
|
||||||
for (int x = 1; x < LAND_SIZE; x++)
|
for (unsigned x = 1; x < LandRecordData::sLandSize; x++)
|
||||||
{
|
{
|
||||||
colOffset += vhgt.mHeightData[y * LAND_SIZE + x];
|
colOffset += vhgt.mHeightData[y * LandRecordData::sLandSize + x];
|
||||||
data.mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE;
|
data.mHeights[x + y * LandRecordData::sLandSize] = colOffset * HEIGHT_SCALE;
|
||||||
|
|
||||||
if (colOffset * HEIGHT_SCALE > data.mMaxHeight)
|
if (colOffset * HEIGHT_SCALE > data.mMaxHeight)
|
||||||
data.mMaxHeight = colOffset * HEIGHT_SCALE;
|
data.mMaxHeight = colOffset * HEIGHT_SCALE;
|
||||||
|
@ -292,8 +304,6 @@ namespace ESM
|
||||||
data.mMinHeight = colOffset * HEIGHT_SCALE;
|
data.mMinHeight = colOffset * HEIGHT_SCALE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.mUnk1 = vhgt.mUnk1;
|
|
||||||
data.mUnk2 = vhgt.mUnk2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,13 +311,13 @@ namespace ESM
|
||||||
reader.skipHSub();
|
reader.skipHSub();
|
||||||
|
|
||||||
if (reader.isNextSub("VCLR"))
|
if (reader.isNextSub("VCLR"))
|
||||||
condLoad(reader, flags, data.mDataLoaded, DATA_VCLR, data.mColours, 3 * LAND_NUM_VERTS);
|
condLoad(reader, flags, data.mDataLoaded, DATA_VCLR, data.mColours);
|
||||||
if (reader.isNextSub("VTEX"))
|
if (reader.isNextSub("VTEX"))
|
||||||
{
|
{
|
||||||
uint16_t vtex[LAND_NUM_TEXTURES];
|
uint16_t vtex[LandRecordData::sLandNumTextures];
|
||||||
if (condLoad(reader, flags, data.mDataLoaded, DATA_VTEX, vtex, sizeof(vtex)))
|
if (condLoad(reader, flags, data.mDataLoaded, DATA_VTEX, vtex))
|
||||||
{
|
{
|
||||||
transposeTextureData(vtex, data.mTextures);
|
transposeTextureData(vtex, data.mTextures.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,19 +86,9 @@ namespace ESM
|
||||||
// total number of textures per land
|
// total number of textures per land
|
||||||
static constexpr int LAND_NUM_TEXTURES = LandRecordData::sLandNumTextures;
|
static constexpr int LAND_NUM_TEXTURES = LandRecordData::sLandNumTextures;
|
||||||
|
|
||||||
static constexpr int LAND_GLOBAL_MAP_LOD_SIZE = 81;
|
static constexpr unsigned LAND_GLOBAL_MAP_LOD_SIZE = 81;
|
||||||
|
|
||||||
static constexpr int LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;
|
static constexpr unsigned LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct VHGT
|
|
||||||
{
|
|
||||||
float mHeightOffset;
|
|
||||||
std::int8_t mHeightData[LAND_NUM_VERTS];
|
|
||||||
std::uint16_t mUnk1;
|
|
||||||
std::uint8_t mUnk2;
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
using LandData = ESM::LandRecordData;
|
using LandData = ESM::LandRecordData;
|
||||||
|
|
||||||
|
@ -128,16 +118,10 @@ namespace ESM
|
||||||
const LandData* getLandData(int flags) const;
|
const LandData* getLandData(int flags) const;
|
||||||
|
|
||||||
/// Return land data without loading first anything. Can return a 0-pointer.
|
/// Return land data without loading first anything. Can return a 0-pointer.
|
||||||
const LandData* getLandData() const
|
const LandData* getLandData() const { return mLandData.get(); }
|
||||||
{
|
|
||||||
return mLandData.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return land data without loading first anything. Can return a 0-pointer.
|
/// Return land data without loading first anything. Can return a 0-pointer.
|
||||||
LandData* getLandData()
|
LandData* getLandData() { return mLandData.get(); }
|
||||||
{
|
|
||||||
return mLandData.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \attention Must not be called on objects that aren't fully loaded.
|
/// \attention Must not be called on objects that aren't fully loaded.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Reference in a new issue