Skip to content

Commit

Permalink
feat: index parent esms and handle overwritten records
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeBryssinck committed Jan 20, 2022
1 parent dee225a commit 7866a42
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 32 deletions.
35 changes: 29 additions & 6 deletions Code/components/es_loader/ESLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ bool ESLoader::LoadLoadOrder()
switch (extensionType)
{
case 'm':
m_masterFiles[line] = standardId;
case 'p':
plugin.m_standardId = standardId;
standardId += 0x01;
Expand All @@ -76,6 +77,7 @@ bool ESLoader::LoadLoadOrder()
void ESLoader::LoadFiles()
{
Map<uint32_t, CLMT> climates{};
Map<uint32_t, NPC> npcs{};

for (Plugin& plugin : m_loadOrder)
{
Expand All @@ -86,7 +88,7 @@ void ESLoader::LoadFiles()
continue;
}

TESFile pluginFile{};
TESFile pluginFile(m_masterFiles);
if (plugin.IsLite())
pluginFile.Setup(plugin.m_liteId);
else
Expand All @@ -101,13 +103,24 @@ void ESLoader::LoadFiles()

const Map<uint32_t, CLMT>& pluginClimates = pluginFile.GetClimates();
climates.insert(pluginClimates.begin(), pluginClimates.end());
spdlog::info("Climate count in {}: {}", plugin.m_filename, pluginClimates.size());
//spdlog::info("Climate count in {}: {}", plugin.m_filename, pluginClimates.size());

const Map<uint32_t, NPC>& pluginNpcs = pluginFile.GetNPCs();
auto npc = pluginNpcs.find(0x13480);
if (npc != std::end(pluginNpcs))
spdlog::info("Found Faendal! {}", npc->second.m_baseStats.IsRespawn());
for (auto& [formId, npc] : pluginNpcs)
{
npcs[formId] = npc;
}
npcs.insert(pluginNpcs.begin(), pluginNpcs.end());
}

spdlog::info("All climates:");
for (auto& [formId, climate] : climates)
spdlog::info("All NPCs:");
for (auto& [formId, npc] : npcs)
{
spdlog::info("\t{} ({:X})", climate.m_editorId, formId);
if (formId == 0x13480)
spdlog::info("\tIsRespawn? {} ({:X})", npc.m_baseStats.IsRespawn(), formId);
}
}

Expand All @@ -124,10 +137,20 @@ fs::path ESLoader::GetPath(String& aFilename)
return fs::path();
}

String ESLoader::LoadZString(Buffer::Reader& aReader) noexcept
String ESLoader::ReadZString(Buffer::Reader& aReader) noexcept
{
String zstring = String(reinterpret_cast<const char*>(aReader.GetDataAtPosition()));
aReader.Advance(zstring.size() + 1);
return zstring;
}

String ESLoader::ReadWString(Buffer::Reader& aReader) noexcept
{
// TODO: docs don't mention wstring being an actual wide string, test this
uint16_t stringLength = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&stringLength), 2);
String wstring = String(reinterpret_cast<const char*>(aReader.GetDataAtPosition()), stringLength);
aReader.Advance(stringLength);
return wstring;
}

4 changes: 3 additions & 1 deletion Code/components/es_loader/ESLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class ESLoader
ESLoader() = delete;
ESLoader(String aDirectory);

static String LoadZString(Buffer::Reader& aReader) noexcept;
static String ReadZString(Buffer::Reader& aReader) noexcept;
static String ReadWString(Buffer::Reader& aReader) noexcept;

private:
void FindFiles();
Expand All @@ -37,4 +38,5 @@ class ESLoader

String m_directory = "";
Vector<Plugin> m_loadOrder{};
Map<String, uint8_t> m_masterFiles{};
};
6 changes: 3 additions & 3 deletions Code/components/es_loader/Records/CLMT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ CLMT CLMT::ParseChunks() noexcept
switch (aChunkId)
{
case ChunkId::EDID_ID:
clmt.m_editorId = ESLoader::LoadZString(aReader);
clmt.m_editorId = ESLoader::ReadZString(aReader);
break;
case ChunkId::WLST_ID:
clmt.m_weatherList = Chunks::WLST(aReader);
break;
case ChunkId::FNAM_ID:
clmt.m_sunTexture = ESLoader::LoadZString(aReader);
clmt.m_sunTexture = ESLoader::ReadZString(aReader);
break;
case ChunkId::GNAM_ID:
clmt.m_glareTexture = ESLoader::LoadZString(aReader);
clmt.m_glareTexture = ESLoader::ReadZString(aReader);
break;
case ChunkId::TNAM_ID:
clmt.m_timing = Chunks::TNAM(aReader);
Expand Down
4 changes: 2 additions & 2 deletions Code/components/es_loader/Records/CONT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ CONT CONT::ParseChunks() noexcept
switch (aChunkId)
{
case ChunkId::EDID_ID:
cont.m_editorId = ESLoader::LoadZString(aReader);
cont.m_editorId = ESLoader::ReadZString(aReader);
break;
case ChunkId::FULL_ID:
cont.m_name = ESLoader::LoadZString(aReader);
cont.m_name = ESLoader::ReadZString(aReader);
break;
case ChunkId::CNTO_ID:
Chunks::CNTO cnto(aReader);
Expand Down
18 changes: 8 additions & 10 deletions Code/components/es_loader/Records/Chunks.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Chunks.h"

#include "Record.h"
#include <ESLoader.h>

namespace Chunks
{
Expand All @@ -17,11 +18,7 @@ PrimaryScripts::PrimaryScripts(Buffer::Reader& aReader)
{
Script script;

// TODO: ESLoader::ReadZString(Reader&);
uint32_t nameLength = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&nameLength), 2);
script.m_name = String(reinterpret_cast<const char*>(aReader.GetDataAtPosition()), nameLength);
aReader.Advance(nameLength);
script.m_name = ESLoader::ReadWString(aReader);

aReader.ReadBytes(&script.m_status, 1);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&script.m_propertyCount), 2);
Expand All @@ -30,11 +27,7 @@ PrimaryScripts::PrimaryScripts(Buffer::Reader& aReader)
{
ScriptProperty scriptProperty;

// TODO: ESLoader::ReadZString(Reader&);
uint32_t propNameLength = 0;
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&propNameLength), 2);
scriptProperty.m_name = String(reinterpret_cast<const char*>(aReader.GetDataAtPosition()), propNameLength);
aReader.Advance(propNameLength);
scriptProperty.m_name = ESLoader::ReadWString(aReader);

aReader.ReadBytes(reinterpret_cast<uint8_t*>(&scriptProperty.m_type), 1);
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&scriptProperty.m_status), 1);
Expand Down Expand Up @@ -151,4 +144,9 @@ ACBS::ACBS(Buffer::Reader& aReader)
aReader.ReadBytes(reinterpret_cast<uint8_t*>(&m_bleedoutOverride), 2);
}

MAST::MAST(Buffer::Reader& aReader)
{
m_masterName = ESLoader::ReadZString(aReader);
}

} // namespace
8 changes: 8 additions & 0 deletions Code/components/es_loader/Records/Chunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,12 @@ struct ACBS

static_assert(sizeof(ACBS) == 0x18);

struct MAST
{
MAST(){}
MAST(Buffer::Reader& aReader);

String m_masterName = "";
};

} // namespace
21 changes: 21 additions & 0 deletions Code/components/es_loader/Records/TES4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "TES4.h"

#include <ESLoader.h>

TES4 TES4::ParseChunks() noexcept
{
TES4 tes4;
tes4.CopyRecordData(*this);

IterateChunks([&](ChunkId aChunkId, Buffer::Reader& aReader) {
switch (aChunkId)
{
case ChunkId::MAST_ID:
Chunks::MAST mast(aReader);
tes4.m_masterFiles.push_back(mast);
break;
}
});

return tes4;
}
16 changes: 16 additions & 0 deletions Code/components/es_loader/Records/TES4.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "Record.h"
#include "Chunks.h"

// https://en.uesp.net/wiki/Skyrim_Mod:Mod_File_Format/TES4
class TES4 : public Record
{
public:
static constexpr FormEnum kType = FormEnum::CONT;

// Master files
Vector<Chunks::MAST> m_masterFiles{};

TES4 ParseChunks() noexcept;
};
34 changes: 28 additions & 6 deletions Code/components/es_loader/TESFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
#include <filesystem>
#include <fstream>

#include "Records/Record.h"
#include "Records/REFR.h"
#include "Records/CLMT.h"
#include "Records/NPC.h"
TESFile::TESFile(Map<String, uint8_t> aMasterFiles)
: m_masterFiles(aMasterFiles)
{
}

void TESFile::Setup(uint8_t aStandardId)
{
Expand Down Expand Up @@ -69,8 +69,8 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader) noexcept

if (type == static_cast<uint32_t>(FormEnum::GRUP))
{
const size_t endOfGroup = aReader.GetBytePosition() + size;
aReader.Advance(sizeof(Group));
const size_t endOfGroup = aReader.GetBytePosition() + size - 0x18;

while (aReader.GetBytePosition() < endOfGroup)
{
Expand All @@ -83,6 +83,18 @@ bool TESFile::ReadGroupOrRecord(Buffer::Reader& aReader) noexcept

switch (pRecord->GetType())
{
case FormEnum::TES4: {
TES4 fileHeader = CopyAndParseRecord<TES4>(pRecord);

uint8_t parentId = 0;
for (const Chunks::MAST& master : fileHeader.m_masterFiles)
{
m_parentToMaster[parentId] = m_masterFiles[master.m_masterName];
parentId++;
}

break;
}
//case FormEnum::ACHR:
case FormEnum::REFR: {
REFR parsedRecord = CopyAndParseRecord<REFR>(pRecord);
Expand Down Expand Up @@ -114,6 +126,16 @@ T TESFile::CopyAndParseRecord(Record* pRecordHeader)
{
T* pRecord = reinterpret_cast<T*>(pRecordHeader);
T parsedRecord = pRecord->ParseChunks();
parsedRecord.SetBaseId(GetFormIdPrefix());
uint8_t baseId = (uint8_t)(pRecord->GetFormId() >> 24);
parsedRecord.SetBaseId(GetFormIdPrefix(baseId));
return parsedRecord;
}

uint32_t TESFile::GetFormIdPrefix(uint8_t aCurrentPrefix) const noexcept
{
auto masterId = m_parentToMaster.find(aCurrentPrefix);
if (masterId != std::end(m_parentToMaster))
return ((uint32_t)masterId->second) << 24;
else
return m_formIdPrefix;
}
15 changes: 11 additions & 4 deletions Code/components/es_loader/TESFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include <Records/REFR.h>
#include <Records/CLMT.h>
#include <Records/NPC.h>
#include <Records/TES4.h>

class TESFile
{
public:
TESFile() = default;
TESFile(Map<String, uint8_t> aMasterFiles);

void Setup(uint8_t aStandardId);
void Setup(uint16_t aLiteId);
Expand All @@ -23,10 +25,6 @@ class TESFile
{
return IsLite() ? m_liteId : m_standardId;
}
[[nodiscard]] uint32_t GetFormIdPrefix() const noexcept
{
return m_formIdPrefix;
}
[[nodiscard]] String GetFilename() const noexcept
{
return m_filename;
Expand All @@ -40,12 +38,18 @@ class TESFile
{
return m_climates;
}
const Map<uint32_t, NPC>& GetNPCs() const noexcept
{
return m_npcs;
}

NPC& GetNpcById(uint32_t aFormId) noexcept
{
return m_npcs[aFormId];
}

[[nodiscard]] uint32_t GetFormIdPrefix(uint8_t aCurrentPrefix) const noexcept;

private:
bool ReadGroupOrRecord(Buffer::Reader& aReader) noexcept;

Expand All @@ -63,6 +67,9 @@ class TESFile
};
uint32_t m_formIdPrefix = 0;

Map<String, uint8_t> m_masterFiles{};
Map<uint8_t, uint8_t> m_parentToMaster{};

Map<uint32_t, REFR> m_objectReferences{};
Map<uint32_t, CLMT> m_climates{};
Map<uint32_t, NPC> m_npcs{};
Expand Down

0 comments on commit 7866a42

Please sign in to comment.