diff --git a/Code/components/es_loader/ESLoaderTest.cpp b/Code/components/es_loader/ESLoaderTest.cpp index b9662ca40..e2cbe7ab0 100644 --- a/Code/components/es_loader/ESLoaderTest.cpp +++ b/Code/components/es_loader/ESLoaderTest.cpp @@ -111,4 +111,13 @@ TEST_F(ESLoaderTest, GetGameSettingFloatBribeScale) EXPECT_EQ(bribeSetting.m_value.m_float, 0.5f); } +TEST_F(ESLoaderTest, GetWorldTamriel) +{ + const auto& pCollection = GetCollection(); + + const WRLD& tamrielWorld = pCollection->GetWorldById(60); + + EXPECT_EQ(tamrielWorld.m_editorId, "Tamriel"); +} + } // namespace diff --git a/Code/components/es_loader/RecordCollection.h b/Code/components/es_loader/RecordCollection.h index 9761a5c4f..2ac22f77c 100644 --- a/Code/components/es_loader/RecordCollection.h +++ b/Code/components/es_loader/RecordCollection.h @@ -5,6 +5,7 @@ #include "Records/NPC.h" #include "Records/REFR.h" #include "Records/GMST.h" +#include "Records/WRLD.h" class RecordCollection { @@ -43,6 +44,10 @@ class RecordCollection { return m_gameSettings[aFormId]; } + WRLD& GetWorldById(uint32_t aFormId) noexcept + { + return m_worlds[aFormId]; + } private: Map m_allRecords{}; @@ -51,4 +56,5 @@ class RecordCollection Map m_npcs{}; Map m_containers{}; Map m_gameSettings{}; + Map m_worlds{}; }; diff --git a/Code/components/es_loader/Records/Chunks.cpp b/Code/components/es_loader/Records/Chunks.cpp index 7554a0bb6..fee217fe5 100644 --- a/Code/components/es_loader/Records/Chunks.cpp +++ b/Code/components/es_loader/Records/Chunks.cpp @@ -166,4 +166,16 @@ MAST::MAST(Buffer::Reader& aReader) m_masterName = ESLoader::ReadZString(aReader); } +WCTR::WCTR(Buffer::Reader& aReader) +{ + aReader.ReadBytes(reinterpret_cast(&m_x), 2); + aReader.ReadBytes(reinterpret_cast(&m_y), 2); +} + +DNAM::DNAM(Buffer::Reader& aReader) +{ + aReader.ReadBytes(reinterpret_cast(&m_landLevel), 4); + aReader.ReadBytes(reinterpret_cast(&m_waterLevel), 4); +} + } // namespace diff --git a/Code/components/es_loader/Records/Chunks.h b/Code/components/es_loader/Records/Chunks.h index 3edc58376..6d1b3482b 100644 --- a/Code/components/es_loader/Records/Chunks.h +++ b/Code/components/es_loader/Records/Chunks.h @@ -310,4 +310,26 @@ struct TypedValue }; }; +struct WCTR +{ + WCTR() + { + } + WCTR(Buffer::Reader& aReader); + + int16_t m_x; + int16_t m_y; +}; + +struct DNAM +{ + DNAM() + { + } + DNAM(Buffer::Reader& aReader); + + float m_landLevel; + float m_waterLevel; +}; + } // namespace diff --git a/Code/components/es_loader/Records/TESFileRecordTypes.inl b/Code/components/es_loader/Records/TESFileRecordTypes.inl index 7f74d19ce..bcbfc20ab 100644 --- a/Code/components/es_loader/Records/TESFileRecordTypes.inl +++ b/Code/components/es_loader/Records/TESFileRecordTypes.inl @@ -1,17 +1,21 @@ #pragma once +#define EXPAND_TO_INTEGER(name) ((#name)[0]) | ((#name)[1]) << 8 | ((#name)[2]) << 16 | ((#name)[3]) << 24 +#define DECLARE_FORM_TYPE(name) name = EXPAND_TO_INTEGER(name) + enum class FormEnum : uint32_t { EMPTY_ID = 0, - TES4 = 0x34534554, - GRUP = 0x50555247, - REFR = 0x52464552, - ACHR = 0x52484341, - CELL = 0x4C4C4543, - CLMT = 0x544D4C43, - CONT = 0x544E4F43, - NPC_ = 0x5F43504E, - GMST = 0x54534D47, + DECLARE_FORM_TYPE(TES4), + DECLARE_FORM_TYPE(GRUP), + DECLARE_FORM_TYPE(REFR), + DECLARE_FORM_TYPE(ACHR), + DECLARE_FORM_TYPE(CELL), + DECLARE_FORM_TYPE(CLMT), + DECLARE_FORM_TYPE(CONT), + DECLARE_FORM_TYPE(NPC_), + DECLARE_FORM_TYPE(GMST), + DECLARE_FORM_TYPE(WRLD) }; enum class ChunkId : uint32_t diff --git a/Code/components/es_loader/Records/WRLD.cpp b/Code/components/es_loader/Records/WRLD.cpp new file mode 100644 index 000000000..e5df3e6d4 --- /dev/null +++ b/Code/components/es_loader/Records/WRLD.cpp @@ -0,0 +1,36 @@ +#include "WRLD.h" + +#include + +void WRLD::ParseChunks(WRLD& aSourceRecord, Map& aParentToFormIdPrefix) noexcept +{ + aSourceRecord.IterateChunks([&](ChunkId aChunkId, Buffer::Reader& aReader) { + switch (aChunkId) + { + case ChunkId::EDID_ID: + m_editorId = ESLoader::ReadZString(aReader); + break; + case ChunkId::WCTR_ID: + m_centerCell = Chunks::WCTR(aReader); + break; + case ChunkId::CNAM_ID: + aReader.ReadBytes(reinterpret_cast(&m_climateId), sizeof(m_climateId)); + break; + case ChunkId::DNAM_ID: + m_landData = Chunks::DNAM(aReader); + break; + case ChunkId::WNAM_ID: { + uint32_t id = 0; + aReader.ReadBytes(reinterpret_cast(&id), sizeof(id)); + m_parentId = id; + } + break; + case ChunkId::ZNAM_ID: + aReader.ReadBytes(reinterpret_cast(&m_musicId), sizeof(m_musicId)); + break; + case ChunkId::NAMA_ID: + aReader.ReadBytes(reinterpret_cast(&m_lodMultiplier), sizeof(m_lodMultiplier)); + break; + } + }); +} diff --git a/Code/components/es_loader/Records/WRLD.h b/Code/components/es_loader/Records/WRLD.h new file mode 100644 index 000000000..5186b6d55 --- /dev/null +++ b/Code/components/es_loader/Records/WRLD.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Record.h" +#include "Chunks.h" + +// https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/WRLD +class WRLD : public Record +{ +public: + static constexpr FormEnum kType = FormEnum::WRLD; + + String m_editorId = ""; + std::optional m_centerCell; + std::optional m_climateId; + std::optional m_landData; + std::optional m_parentId; + uint32_t m_musicId; + float m_lodMultiplier; + + void ParseChunks(WRLD& aSourceRecord, Map& aParentToFormIdPrefix) noexcept; +}; diff --git a/Code/components/es_loader/TESFile.cpp b/Code/components/es_loader/TESFile.cpp index 48cd38559..709a1225c 100644 --- a/Code/components/es_loader/TESFile.cpp +++ b/Code/components/es_loader/TESFile.cpp @@ -128,6 +128,10 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader, RecordCollection& aReco aRecordCollection.m_gameSettings[parsedRecord.GetFormId()] = parsedRecord; break; } + case FormEnum::WRLD: { + WRLD parsedRecord = CopyAndParseRecord(pRecord); + aRecordCollection.m_worlds[parsedRecord.GetFormId()] = parsedRecord; + } } //pRecord->DiscoverChunks(); @@ -136,7 +140,7 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader, RecordCollection& aReco { Record record; record.CopyRecordData(*pRecord); - record.SetBaseId(TESFile::GetFormIdPrefix(pRecord->GetFormId(), m_parentToFormIdPrefix)); + record.SetBaseId(GetFormIdPrefix(pRecord->GetFormId(), m_parentToFormIdPrefix)); aRecordCollection.m_allRecords[pRecord->GetFormId()] = *pRecord; } @@ -163,8 +167,8 @@ T TESFile::CopyAndParseRecord(Record* pRecordHeader) uint32_t TESFile::GetFormIdPrefix(uint32_t aFormId, Map& aParentToFormIdPrefix) noexcept { - uint8_t baseId = (uint8_t)(aFormId >> 24); - auto masterId = aParentToFormIdPrefix.find(baseId); + auto baseId = (uint8_t)(aFormId >> 24); + const auto masterId = aParentToFormIdPrefix.find(baseId); if (masterId == std::end(aParentToFormIdPrefix)) {