From 6031a245dac78846cb47b9f1662552fe67913d2c Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Fri, 29 Nov 2019 10:09:56 +0000 Subject: [PATCH 1/6] Friendly::Gff now stores a FileType. --- FileFormats/Gff/Gff_Friendly.cpp | 22 +++++++++++++++++----- FileFormats/Gff/Gff_Friendly.hpp | 4 ++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/FileFormats/Gff/Gff_Friendly.cpp b/FileFormats/Gff/Gff_Friendly.cpp index e3648f0..5cffdbe 100644 --- a/FileFormats/Gff/Gff_Friendly.cpp +++ b/FileFormats/Gff/Gff_Friendly.cpp @@ -213,7 +213,9 @@ Gff::Gff() : m_TopLevelStruct() { } Gff::Gff(Raw::Gff const& rawGff) : m_TopLevelStruct(rawGff.m_Structs[0], rawGff) -{ } +{ + memcpy(m_FileType, rawGff.m_Header.m_FileType, sizeof(m_FileType)); +} GffStruct& Gff::GetTopLevelStruct() { @@ -225,10 +227,20 @@ GffStruct const& Gff::GetTopLevelStruct() const return m_TopLevelStruct; } +char* Gff::GetFileType() +{ + return m_FileType; +} + +const char* Gff::GetFileType() const +{ + return m_FileType; +} + struct GffCreator { public: - std::unique_ptr Create(const GffStruct& topLevelStruct); + std::unique_ptr Create(const GffStruct& topLevelStruct, const char* fleType); private: template @@ -262,16 +274,16 @@ struct GffCreator bool Gff::WriteToFile(char const* path) const { - return GffCreator().Create(m_TopLevelStruct)->WriteToFile(path); + return GffCreator().Create(m_TopLevelStruct, m_FileType)->WriteToFile(path); } -std::unique_ptr GffCreator::Create(const GffStruct& topLevelStruct) +std::unique_ptr GffCreator::Create(const GffStruct& topLevelStruct, const char* fileType) { m_RawGff = std::make_unique(); InsertIntoRawGff(topLevelStruct); Raw::GffHeader* header = &m_RawGff->m_Header; - std::memcpy(header->m_FileType, "UTC ", 4); + std::memcpy(header->m_FileType, fileType, 4); std::memcpy(header->m_FileVersion, "V3.2", 4); header->m_StructOffset = sizeof(Raw::GffHeader); diff --git a/FileFormats/Gff/Gff_Friendly.hpp b/FileFormats/Gff/Gff_Friendly.hpp index f4b9a77..2e4094c 100644 --- a/FileFormats/Gff/Gff_Friendly.hpp +++ b/FileFormats/Gff/Gff_Friendly.hpp @@ -174,10 +174,14 @@ class Gff GffStruct& GetTopLevelStruct(); GffStruct const& GetTopLevelStruct() const; + char* GetFileType(); + const char* GetFileType() const; + bool WriteToFile(char const* path) const; private: GffStruct m_TopLevelStruct; + char m_FileType[4]; }; } From e43082f8afc604bf2d211c3a0cd0b1bccac4d294 Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Fri, 29 Nov 2019 16:51:52 +0000 Subject: [PATCH 2/6] Raw/Friendly::Erf now support saving and editing. Added erf-packer which can be used to produce ERF files. --- FileFormats/Erf/Erf_Friendly.cpp | 93 ++++++++++++++++++++++++++++++++ FileFormats/Erf/Erf_Friendly.hpp | 12 +++++ FileFormats/Erf/Erf_Raw.cpp | 28 ++++++++++ FileFormats/Erf/Erf_Raw.hpp | 4 +- README | 1 + Tools/CMakeLists.txt | 4 ++ Tools/Tool_ErfPacker.cpp | 75 ++++++++++++++++++++++++++ 7 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 Tools/Tool_ErfPacker.cpp diff --git a/FileFormats/Erf/Erf_Friendly.cpp b/FileFormats/Erf/Erf_Friendly.cpp index dfbdab3..7ef9009 100644 --- a/FileFormats/Erf/Erf_Friendly.cpp +++ b/FileFormats/Erf/Erf_Friendly.cpp @@ -6,6 +6,9 @@ namespace FileFormats::Erf::Friendly { +Erf::Erf() +{ } + Erf::Erf(Raw::Erf const& rawErf) { ConstructInternal(rawErf); @@ -16,16 +19,106 @@ Erf::Erf(Raw::Erf&& rawErf) : m_RawErf(std::forward(rawErf)) ConstructInternal(m_RawErf.value()); } +std::vector& Erf::GetDescriptions() +{ + return m_Descriptions; +} + std::vector const& Erf::GetDescriptions() const { return m_Descriptions; } +std::vector& Erf::GetResources() +{ + return m_Resources; +} + std::vector const& Erf::GetResources() const { return m_Resources; } +char* Erf::GetFileType() +{ + return m_FileType; +} + +const char* Erf::GetFileType() const +{ + return m_FileType; +} + +bool Erf::WriteToFile(char const* path) const +{ + Raw::Erf raw_erf; + + std::memcpy(raw_erf.m_Header.m_FileType, m_FileType, 4); + std::memcpy(raw_erf.m_Header.m_Version, "V1.0", 4); + + raw_erf.m_Header.m_LanguageCount = (std::uint32_t)m_Descriptions.size(); + raw_erf.m_Header.m_EntryCount = (std::uint32_t)m_Resources.size(); + + raw_erf.m_Header.m_LocalizedStringSize = 0; + for (const Raw::ErfLocalisedString& str : m_Descriptions) + { + raw_erf.m_Header.m_LocalizedStringSize += sizeof(str.m_LanguageId) + + sizeof(std::uint32_t) // string size + + (std::uint32_t)str.m_String.size(); // string + } + + std::uint32_t offset_str_table = sizeof(raw_erf.m_Header); + std::uint32_t offset_keys = offset_str_table + raw_erf.m_Header.m_LocalizedStringSize; + std::uint32_t offset_resources = offset_keys + (raw_erf.m_Header.m_EntryCount * sizeof(Raw::ErfKey)); + std::uint32_t offset_to_res_data = offset_resources + (raw_erf.m_Header.m_EntryCount * sizeof(Raw::ErfResource)); + + raw_erf.m_Header.m_OffsetToLocalizedString = offset_str_table; + raw_erf.m_Header.m_OffsetToKeyList = offset_keys; + raw_erf.m_Header.m_OffsetToResourceList = offset_resources; + raw_erf.m_Header.m_BuildYear = 666; + raw_erf.m_Header.m_BuildDay = 333; + raw_erf.m_Header.m_DescriptionStrRef = ~0u; + std::memset(raw_erf.m_Header.m_Reserved, 0, sizeof(raw_erf.m_Header.m_Reserved)); + + raw_erf.m_LocalisedStrings = m_Descriptions; + + { + uint32_t total_data_size = 0; + + for (const ErfResource& friendly_res : m_Resources) + { + total_data_size += (std::uint32_t)friendly_res.m_DataBlock->GetDataLength(); + } + + std::unique_ptr data = std::make_unique(); + data->m_Data.resize(total_data_size); + raw_erf.m_ResourceData = std::move(data); + } + + for (std::uint32_t i = 0, offset_in_res_data = 0; i < raw_erf.m_Header.m_EntryCount; ++i) + { + const ErfResource& friendly_res = m_Resources[i]; + + Raw::ErfKey key; + std::memset(key.m_ResRef, 0, sizeof(key.m_ResRef)); + std::memcpy(key.m_ResRef, friendly_res.m_ResRef.c_str(), friendly_res.m_ResRef.size()); + key.m_ResId = i; + key.m_ResType = friendly_res.m_ResType; + std::memset(key.m_Reserved, 0, sizeof(key.m_Reserved)); + raw_erf.m_Keys.emplace_back(std::move(key)); + + Raw::ErfResource res; + res.m_OffsetToResource = offset_to_res_data + offset_in_res_data; + res.m_ResourceSize = (std::uint32_t)friendly_res.m_DataBlock->GetDataLength(); + raw_erf.m_Resources.emplace_back(std::move(res)); + + std::memcpy((void*)(raw_erf.m_ResourceData->GetData() + offset_in_res_data), friendly_res.m_DataBlock->GetData(), res.m_ResourceSize); + offset_in_res_data += res.m_ResourceSize; + } + + return raw_erf.WriteToFile(path); +} + void Erf::ConstructInternal(Raw::Erf const& rawErf) { // First - copy in the descriptions. This one is simple. diff --git a/FileFormats/Erf/Erf_Friendly.hpp b/FileFormats/Erf/Erf_Friendly.hpp index 3e89b78..17871e4 100644 --- a/FileFormats/Erf/Erf_Friendly.hpp +++ b/FileFormats/Erf/Erf_Friendly.hpp @@ -25,6 +25,8 @@ struct ErfResource class Erf { public: + Erf(); + // This constructs a friendly Erf from a raw Erf. Erf(Raw::Erf const& rawBif); @@ -33,9 +35,17 @@ class Erf // the memory usage significantly. Erf(Raw::Erf&& rawBif); + std::vector& GetDescriptions(); std::vector const& GetDescriptions() const; + + std::vector& GetResources(); std::vector const& GetResources() const; + char* GetFileType(); + const char* GetFileType() const; + + bool WriteToFile(char const* path) const; + private: std::optional m_RawErf; @@ -46,6 +56,8 @@ class Erf // A vector of resources contained within this ERF. std::vector m_Resources; + + char m_FileType[4]; }; } diff --git a/FileFormats/Erf/Erf_Raw.cpp b/FileFormats/Erf/Erf_Raw.cpp index d09b369..5f9ff98 100644 --- a/FileFormats/Erf/Erf_Raw.cpp +++ b/FileFormats/Erf/Erf_Raw.cpp @@ -93,6 +93,34 @@ bool Erf::ReadFromFile(char const* path, Erf* out) return true; } +bool Erf::WriteToFile(char const* path) const +{ + ASSERT(path); + + FILE* outFile = std::fopen(path, "wb"); + + if (outFile) + { + std::fwrite(&m_Header, sizeof(m_Header), 1, outFile); + + for (const ErfLocalisedString& str : m_LocalisedStrings) + { + std::fwrite(&str.m_LanguageId, sizeof(str.m_LanguageId), 1, outFile); + uint32_t len = (std::uint32_t)str.m_String.size(); + std::fwrite(&len, sizeof(len), 1, outFile); + std::fwrite(str.m_String.c_str(), len, 1, outFile); + } + + std::fwrite(m_Keys.data(), m_Keys.size() * sizeof(ErfKey), 1, outFile); + std::fwrite(m_Resources.data(), m_Resources.size() * sizeof(ErfResource), 1, outFile); + std::fwrite(m_ResourceData->GetData(), m_ResourceData->GetDataLength(), 1, outFile); + + std::fclose(outFile); + return true; + } + + return false; +} bool Erf::ConstructInternal(std::byte const* bytes) { diff --git a/FileFormats/Erf/Erf_Raw.hpp b/FileFormats/Erf/Erf_Raw.hpp index b837343..a8c9a24 100644 --- a/FileFormats/Erf/Erf_Raw.hpp +++ b/FileFormats/Erf/Erf_Raw.hpp @@ -121,6 +121,9 @@ struct Erf // Constructs an Erf from a file. The file with be memory mapped so memory usage will be ideal. static bool ReadFromFile(char const* path, Erf* out); + // Writes the raw ERF to disk. + bool WriteToFile(char const* path) const; + private: // This is an RAII wrapper around the various methods of loading a BIF that we have. @@ -133,7 +136,6 @@ struct Erf void ReadLocalisedStrings(std::byte const* data); void ReadKeys(std::byte const* data); void ReadResources(std::byte const* data); - void ReadResourceData(std::byte const* data, std::size_t bytesCount); }; } \ No newline at end of file diff --git a/README b/README index 8960168..92e65ae 100644 --- a/README +++ b/README @@ -20,3 +20,4 @@ There are some tools in the Tools subdirectory: - generate_placeable_blueprints allows the user to generate a series of blueprints from placeables defined in 2da using a base blueprint - key_bif_extractor allows extracting all resources in a KEY from their BIFs. - erf_extractor allows extracting all resources from an ERF. +- erf_packer allows you to pack resources into an ERF (like nwhak). diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt index 0e85968..8b69ab8 100644 --- a/Tools/CMakeLists.txt +++ b/Tools/CMakeLists.txt @@ -6,6 +6,10 @@ add_executable(erf_extractor Tool_ErfExtractor.cpp) target_link_libraries(erf_extractor FileFormats) set_target_properties(erf_extractor PROPERTIES FOLDER "Tools") +add_executable(erf_packer Tool_ErfPacker.cpp) +target_link_libraries(erf_packer FileFormats) +set_target_properties(erf_packer PROPERTIES FOLDER "Tools") + add_executable(key_bif_extractor Tool_KeyBifExtractor.cpp) target_link_libraries(key_bif_extractor FileFormats) set_target_properties(key_bif_extractor PROPERTIES FOLDER "Tools") diff --git a/Tools/Tool_ErfPacker.cpp b/Tools/Tool_ErfPacker.cpp new file mode 100644 index 0000000..3b9501a --- /dev/null +++ b/Tools/Tool_ErfPacker.cpp @@ -0,0 +1,75 @@ +#include "FileFormats/Erf.hpp" +#include "Utility/Assert.hpp" + +#include +#include + +namespace +{ + +int pack_erf(const char* out_path, const std::vector& files) +{ + using namespace FileFormats::Erf; + + Friendly::Erf erf; + + Raw::ErfLocalisedString desc; + desc.m_LanguageId = 0; + desc.m_String = "ERFPacked\nhttps://github.com/Liareth/NWNFileFormats\nThis ERF was packed with the pack_erf tool from the NWNFileFormats library."; + erf.GetDescriptions().emplace_back(std::move(desc)); + + for (const char* file : files) + { + std::filesystem::path file_path(file); + + std::uintmax_t len = std::filesystem::file_size(file_path); + std::unique_ptr db = std::make_unique(); + db->m_Data.resize(len); + + FILE* f = std::fopen(file, "rb"); + ASSERT(f); + + if (f) + { + std::fread(db->m_Data.data(), len, 1, f); + std::fclose(f); + } + else + { + continue; + } + + Friendly::ErfResource res; + res.m_ResRef = file_path.stem().string(); + res.m_ResType = FileFormats::Resource::ResourceTypeFromString(file_path.extension().string().substr(1).c_str()); + res.m_DataBlock = std::move(db); + erf.GetResources().emplace_back(std::move(res)); + } + + std::string ext = std::filesystem::path(out_path).extension().string().substr(1); + std::transform(std::begin(ext), std::end(ext), std::begin(ext), ::toupper); + std::memcpy(erf.GetFileType(), ext.c_str(), 3); + erf.GetFileType()[3] = ' '; + + return !erf.WriteToFile(out_path); +} + +} + +int main(int argc, char** argv) +{ + if (argc < 3) + { + std::printf("erf_extractor [out_file] [files...]\n"); + return 1; + } + + std::vector paths; + + for (int i = 2; i < argc; ++i) + { + paths.emplace_back(argv[i]); + } + + return pack_erf(argv[1], paths); +} From aafc88daacd5aab2bac557a11b226641a8d2c22c Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Fri, 29 Nov 2019 17:01:13 +0000 Subject: [PATCH 3/6] Fix the Linux build. --- Tools/CMakeLists.txt | 4 ++++ Tools/Tool_ErfPacker.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt index 8b69ab8..5ca2121 100644 --- a/Tools/CMakeLists.txt +++ b/Tools/CMakeLists.txt @@ -10,6 +10,10 @@ add_executable(erf_packer Tool_ErfPacker.cpp) target_link_libraries(erf_packer FileFormats) set_target_properties(erf_packer PROPERTIES FOLDER "Tools") +if (UNIX) + target_link_libraries(erf_packer stdc++fs) +endif() + add_executable(key_bif_extractor Tool_KeyBifExtractor.cpp) target_link_libraries(key_bif_extractor FileFormats) set_target_properties(key_bif_extractor PROPERTIES FOLDER "Tools") diff --git a/Tools/Tool_ErfPacker.cpp b/Tools/Tool_ErfPacker.cpp index 3b9501a..879d7ed 100644 --- a/Tools/Tool_ErfPacker.cpp +++ b/Tools/Tool_ErfPacker.cpp @@ -1,6 +1,8 @@ #include "FileFormats/Erf.hpp" #include "Utility/Assert.hpp" +#include +#include #include #include From d9aa61a40564a9d4d17d5d6a5638ce3eb3183fc7 Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Sun, 1 Dec 2019 12:20:41 +0000 Subject: [PATCH 4/6] Support writing Gff structure to memory. --- FileFormats/Gff/Gff_Friendly.cpp | 5 ++++ FileFormats/Gff/Gff_Friendly.hpp | 3 +++ FileFormats/Gff/Gff_Raw.cpp | 43 +++++++++++++++++++++++++------ FileFormats/Gff/Gff_Raw.hpp | 8 ++++++ Utility/CMakeLists.txt | 1 + Utility/StreamWriter.cpp | 38 +++++++++++++++++++++++++++ Utility/StreamWriter.hpp | 44 ++++++++++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 Utility/StreamWriter.cpp create mode 100644 Utility/StreamWriter.hpp diff --git a/FileFormats/Gff/Gff_Friendly.cpp b/FileFormats/Gff/Gff_Friendly.cpp index 5cffdbe..e0adb0f 100644 --- a/FileFormats/Gff/Gff_Friendly.cpp +++ b/FileFormats/Gff/Gff_Friendly.cpp @@ -277,6 +277,11 @@ bool Gff::WriteToFile(char const* path) const return GffCreator().Create(m_TopLevelStruct, m_FileType)->WriteToFile(path); } +size_t Gff::WriteToBytes(std::byte* bytes, size_t max_len) const +{ + return GffCreator().Create(m_TopLevelStruct, m_FileType)->WriteToBytes(bytes, max_len); +} + std::unique_ptr GffCreator::Create(const GffStruct& topLevelStruct, const char* fileType) { m_RawGff = std::make_unique(); diff --git a/FileFormats/Gff/Gff_Friendly.hpp b/FileFormats/Gff/Gff_Friendly.hpp index 2e4094c..050ca12 100644 --- a/FileFormats/Gff/Gff_Friendly.hpp +++ b/FileFormats/Gff/Gff_Friendly.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include "FileFormats/Gff/Gff_Raw.hpp" @@ -178,6 +180,7 @@ class Gff const char* GetFileType() const; bool WriteToFile(char const* path) const; + size_t WriteToBytes(std::byte* bytes, size_t max_len) const; private: GffStruct m_TopLevelStruct; diff --git a/FileFormats/Gff/Gff_Raw.cpp b/FileFormats/Gff/Gff_Raw.cpp index 8957420..9a3a0d2 100644 --- a/FileFormats/Gff/Gff_Raw.cpp +++ b/FileFormats/Gff/Gff_Raw.cpp @@ -1,6 +1,7 @@ #include "FileFormats/Gff/Gff_Raw.hpp" #include "Utility/Assert.hpp" #include "Utility/MemoryMappedFile.hpp" +#include "Utility/StreamWriter.hpp" #include @@ -41,16 +42,10 @@ bool Gff::WriteToFile(char const* path) const ASSERT(path); FILE* outFile = std::fopen(path, "wb"); - if (outFile) { - std::fwrite(&m_Header, sizeof(m_Header), 1, outFile); - std::fwrite(m_Structs.data(), sizeof(m_Structs[0]), m_Structs.size(), outFile); - std::fwrite(m_Fields.data(), sizeof(m_Fields[0]), m_Fields.size(), outFile); - std::fwrite(m_Labels.data(), sizeof(m_Labels[0]), m_Labels.size(), outFile); - std::fwrite(m_FieldData.data(), sizeof(m_FieldData[0]), m_FieldData.size(), outFile); - std::fwrite(m_FieldIndices.data(), sizeof(m_FieldIndices[0]), m_FieldIndices.size(), outFile); - std::fwrite(m_ListIndices.data(), sizeof(m_ListIndices[0]), m_ListIndices.size(), outFile); + FileStreamWriter writer(outFile); + WriteInternal(&writer); std::fclose(outFile); return true; } @@ -58,6 +53,27 @@ bool Gff::WriteToFile(char const* path) const return false; } +size_t Gff::WriteToBytes(std::byte* bytes, size_t max_len) const +{ + SizeStreamWriter size_writer; + WriteInternal(&size_writer); + size_t len_required = size_writer.GetSize(); + + if (max_len == 0) + { + return len_required; + } + + if (max_len < len_required) + { + return 0; + } + + MemoryStreamWriter memory_writer(bytes); + WriteInternal(&memory_writer); + return len_required; +} + namespace { template @@ -276,6 +292,17 @@ bool Gff::ConstructInternal(std::byte const* bytes) return true; } +void Gff::WriteInternal(IStreamWriter* writer) const +{ + writer->Write(&m_Header, sizeof(m_Header)); + writer->Write(m_Structs.data(), sizeof(m_Structs[0]) * m_Structs.size()); + writer->Write(m_Fields.data(), sizeof(m_Fields[0]) * m_Fields.size()); + writer->Write(m_Labels.data(), sizeof(m_Labels[0]) * m_Labels.size()); + writer->Write(m_FieldData.data(), sizeof(m_FieldData[0]) * m_FieldData.size()); + writer->Write(m_FieldIndices.data(), sizeof(m_FieldIndices[0]) * m_FieldIndices.size()); + writer->Write(m_ListIndices.data(), sizeof(m_ListIndices[0]) * m_ListIndices.size()); +} + namespace { template diff --git a/FileFormats/Gff/Gff_Raw.hpp b/FileFormats/Gff/Gff_Raw.hpp index 7bd9874..070a6fc 100644 --- a/FileFormats/Gff/Gff_Raw.hpp +++ b/FileFormats/Gff/Gff_Raw.hpp @@ -5,6 +5,8 @@ #include #include +class IStreamWriter; + namespace FileFormats::Gff::Raw { // Refer to https://wiki.neverwintervault.org/pages/viewpage.action?pageId=327727 @@ -334,6 +336,11 @@ struct Gff // Writes the raw Gff to disk. bool WriteToFile(char const* path) const; + // Writes the raw Gff to bytes. + // Returns the size written, or 0 if an error occurs. + // If max_len is 0, returns the total required size. + size_t WriteToBytes(std::byte* bytes, size_t max_len) const; + // Below are functions to construct a type from the provided field. GffField::Type_BYTE ConstructBYTE(GffField const& field) const; GffField::Type_CHAR ConstructCHAR(GffField const& field) const; @@ -354,6 +361,7 @@ struct Gff private: bool ConstructInternal(std::byte const* bytes); + void WriteInternal(IStreamWriter* writer) const; void ReadStructs(std::byte const* data); void ReadFields(std::byte const* data); void ReadLabels(std::byte const* data); diff --git a/Utility/CMakeLists.txt b/Utility/CMakeLists.txt index 6ac92e6..56fa179 100644 --- a/Utility/CMakeLists.txt +++ b/Utility/CMakeLists.txt @@ -4,4 +4,5 @@ add_library(Utility STATIC MemoryMappedFile.cpp MemoryMappedFile.hpp MemoryMappedFile_impl.cpp MemoryMappedFile_impl.hpp RAIIWrapper.hpp + StreamWriter.cpp StreamWriter.hpp VirtualObject.cpp VirtualObject.hpp) diff --git a/Utility/StreamWriter.cpp b/Utility/StreamWriter.cpp new file mode 100644 index 0000000..fe1d191 --- /dev/null +++ b/Utility/StreamWriter.cpp @@ -0,0 +1,38 @@ +#include "Utility/StreamWriter.hpp" +#include "Utility/Assert.hpp" +#include + +FileStreamWriter::FileStreamWriter(FILE* file) + : m_File(file) +{ + ASSERT(m_File); +} + +void FileStreamWriter::Write(void const* data, size_t len) +{ + std::fwrite((void*)data, len, 1, m_File); +} + +MemoryStreamWriter::MemoryStreamWriter(std::byte* memory) + : m_Memory(memory), m_Offset(0) +{ } + +void MemoryStreamWriter::Write(void const* data, size_t len) +{ + std::memcpy(m_Memory + m_Offset, data, len); + m_Offset += len; +} + +SizeStreamWriter::SizeStreamWriter() + : m_Size(0) +{ } + +void SizeStreamWriter::Write(void const*, size_t len) +{ + m_Size += len; +} + +size_t SizeStreamWriter::GetSize() const +{ + return m_Size; +} diff --git a/Utility/StreamWriter.hpp b/Utility/StreamWriter.hpp new file mode 100644 index 0000000..782ee13 --- /dev/null +++ b/Utility/StreamWriter.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +// This is a basic interface for stream writing. +// It allows us to write different targets through the same interface. +class IStreamWriter +{ +public: + virtual void Write(void const* data, size_t len) = 0; +}; + +class FileStreamWriter : public IStreamWriter +{ +public: + FileStreamWriter(FILE* file); + virtual void Write(void const* data, size_t len) override; + +private: + FILE* m_File; +}; + +class MemoryStreamWriter : public IStreamWriter +{ +public: + MemoryStreamWriter(std::byte* memory); + virtual void Write(void const* data, size_t len) override; + +private: + std::byte* m_Memory; + size_t m_Offset; +}; + +class SizeStreamWriter : public IStreamWriter +{ +public: + SizeStreamWriter(); + virtual void Write(void const* data, size_t len) override; + size_t GetSize() const; + +private: + size_t m_Size; +}; From 105c1726e7bf95636a93c8642a522ba830fe3eff Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Sun, 1 Dec 2019 16:57:52 +0000 Subject: [PATCH 5/6] Permit creating an empty Friendly::Tlk. --- FileFormats/Tlk/Tlk_Friendly.cpp | 3 +++ FileFormats/Tlk/Tlk_Friendly.hpp | 1 + 2 files changed, 4 insertions(+) diff --git a/FileFormats/Tlk/Tlk_Friendly.cpp b/FileFormats/Tlk/Tlk_Friendly.cpp index cfd92c4..9a4eeac 100644 --- a/FileFormats/Tlk/Tlk_Friendly.cpp +++ b/FileFormats/Tlk/Tlk_Friendly.cpp @@ -6,6 +6,9 @@ namespace FileFormats::Tlk::Friendly { +Tlk::Tlk() +{ } + Tlk::Tlk(Raw::Tlk const& rawTlk) { m_LanguageId = rawTlk.m_Header.m_LanguageID; diff --git a/FileFormats/Tlk/Tlk_Friendly.hpp b/FileFormats/Tlk/Tlk_Friendly.hpp index 298c7af..9e6b452 100644 --- a/FileFormats/Tlk/Tlk_Friendly.hpp +++ b/FileFormats/Tlk/Tlk_Friendly.hpp @@ -21,6 +21,7 @@ struct TlkEntry class Tlk { public: + Tlk(); Tlk(Raw::Tlk const& rawKey); // We use a map rather than unordered_map here because it's more user friendly to iterate from 0 -> max. From dbb51763d29d5633ceb1fbe3f1752d8c5f677a12 Mon Sep 17 00:00:00 2001 From: EctoplasmNWN Date: Sun, 1 Dec 2019 23:15:21 +0100 Subject: [PATCH 6/6] Fix 2da parsing. It was reading one character ahead for each line- which was usually fine on Windows because of CRLF, but would skip empty lines on Linux. --- FileFormats/2da/2da_Raw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FileFormats/2da/2da_Raw.cpp b/FileFormats/2da/2da_Raw.cpp index 3508e54..52aa2e0 100644 --- a/FileFormats/2da/2da_Raw.cpp +++ b/FileFormats/2da/2da_Raw.cpp @@ -150,7 +150,7 @@ bool TwoDA::ConstructInternal(std::byte const* bytes, std::size_t bytesCount) { while (*head++ != '\n' && head < end) {} lines.emplace_back(std::string(tail, head - tail)); - tail = head++; + tail = head; } flattenedLines.clear();