From aa972614e5062c1ff1ce940ba642b2230aea40a3 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 6 Mar 2021 10:47:25 +0100 Subject: [PATCH] Extract Gdt reading and writing classes to GdtStream file --- src/ObjCommon/Obj/Gdt/Gdt.cpp | 322 +---------------------- src/ObjCommon/Obj/Gdt/Gdt.h | 5 +- src/ObjCommon/Obj/Gdt/GdtStream.cpp | 314 ++++++++++++++++++++++ src/ObjCommon/Obj/Gdt/GdtStream.h | 43 +++ test/ObjCommonTests/Obj/Gdt/GdtTests.cpp | 26 +- 5 files changed, 380 insertions(+), 330 deletions(-) create mode 100644 src/ObjCommon/Obj/Gdt/GdtStream.cpp create mode 100644 src/ObjCommon/Obj/Gdt/GdtStream.h diff --git a/src/ObjCommon/Obj/Gdt/Gdt.cpp b/src/ObjCommon/Obj/Gdt/Gdt.cpp index bdcbffe2..081ea81e 100644 --- a/src/ObjCommon/Obj/Gdt/Gdt.cpp +++ b/src/ObjCommon/Obj/Gdt/Gdt.cpp @@ -1,323 +1,9 @@ #include "Gdt.h" -#include +Gdt::Gdt() += default; -class GdtConst +Gdt::Gdt(GdtVersion version) + : m_version(std::move(version)) { -public: - static constexpr const char* VERSION_ENTRY_NAME = "version"; - static constexpr const char* VERSION_ENTRY_GDF = "version.gdf"; - static constexpr const char* VERSION_KEY_GAME = "game"; - static constexpr const char* VERSION_KEY_VERSION = "version"; -}; - -class GdtReader -{ - std::istream& m_stream; - char m_char; - bool m_peeked; - int m_line; - - void PrintError(const std::string& message) const - { - std::cout << "GDT Error at line " << m_line << ": " << message << "\n"; - } - - int PeekChar() - { - if (m_peeked) - return m_char; - - int c; - do - { - c = m_stream.get(); - } - while (isspace(c)); - - m_peeked = true; - m_char = c; - return c; - } - - int NextChar() - { - if (m_peeked) - { - m_peeked = false; - return m_char; - } - - int c; - do - { - c = m_stream.get(); - } - while (isspace(c)); - - return c; - } - - bool ReadStringContent(std::string& str) - { - std::ostringstream ss; - - if (NextChar() != '"') - { - PrintError("Expected string opening tag"); - return false; - } - - auto c = m_stream.get(); - while (c != '"' && c != '\n' && c != EOF) - { - ss << static_cast(c); - c = m_stream.get(); - } - - if (c == '"') - { - str = ss.str(); - return true; - } - - return false; - } - - static GdtEntry* GetEntryByName(const Gdt& gdt, const std::string& name) - { - for (const auto& entry : gdt.m_entries) - { - if (entry->m_name == name) - return entry.get(); - } - - return nullptr; - } - - bool ReadProperties(GdtEntry& entry) - { - while (PeekChar() == '"') - { - std::string propertyKey; - std::string propertyValue; - - if (!ReadStringContent(propertyKey)) - return false; - - if (PeekChar() != '"') - { - PrintError("Expected value string"); - return false; - } - - if (!ReadStringContent(propertyValue)) - return false; - - entry.m_properties.emplace(std::move(propertyKey), std::move(propertyValue)); - } - - - if (NextChar() != '}') - { - PrintError("Expected closing tags"); - return false; - } - - return true; - } - - bool AddEntry(Gdt& gdt, GdtEntry& entry) const - { - if(entry.m_name == GdtConst::VERSION_ENTRY_NAME - && entry.m_gdf_name == GdtConst::VERSION_ENTRY_GDF) - { - auto foundEntry = entry.m_properties.find(GdtConst::VERSION_KEY_GAME); - if(foundEntry == entry.m_properties.end()) - { - PrintError("Version does not feature game property"); - return false; - } - gdt.m_version.m_game = foundEntry->second; - - foundEntry = entry.m_properties.find(GdtConst::VERSION_KEY_VERSION); - if (foundEntry == entry.m_properties.end()) - { - PrintError("Version does not feature version property"); - return false; - } - gdt.m_version.m_version = strtol(foundEntry->second.c_str(), nullptr, 0); - } - else - { - gdt.m_entries.emplace_back(std::make_unique(std::move(entry))); - } - - return true; - } - -public: - explicit GdtReader(std::istream& stream) - : m_stream(stream), - m_char(0), - m_peeked(false), - m_line(0) - { - } - - bool Read(Gdt& gdt) - { - if (NextChar() != '{') - { - PrintError("Expected opening tag"); - return false; - } - - while (PeekChar() == '"') - { - GdtEntry entry; - - if (!ReadStringContent(entry.m_name)) - { - PrintError("Failed to read string"); - return false; - } - - if (PeekChar() == '(') - { - NextChar(); - if (!ReadStringContent(entry.m_gdf_name)) - return false; - if (NextChar() != ')') - { - PrintError("Expected closing parenthesis"); - return false; - } - } - else if (PeekChar() == '[') - { - NextChar(); - std::string parentName; - if (!ReadStringContent(parentName)) - return false; - if (NextChar() != ']') - { - PrintError("Expected closing square brackets"); - return false; - } - entry.m_parent = GetEntryByName(gdt, parentName); - if (entry.m_parent == nullptr) - { - PrintError("Could not find parent with name"); - return false; - } - } - else - { - return false; - } - - if (NextChar() != '{') - { - PrintError("Expected opening tag for entries"); - return false; - } - - if (!ReadProperties(entry)) - return false; - - if (!AddEntry(gdt, entry)) - return false; - } - - - if (NextChar() != '}') - { - PrintError("Expected closing tags"); - return false; - } - - return true; - } -}; - -class GdtWriter -{ - std::ostream& m_stream; - unsigned m_intendation_level; - - void DoIntendation() const - { - for (auto i = 0u; i < m_intendation_level; i++) - m_stream << "\t"; - } - - void WriteVersion(const GdtVersion& gdtVersion) - { - GdtEntry versionEntry; - versionEntry.m_name = GdtConst::VERSION_ENTRY_NAME; - versionEntry.m_gdf_name = GdtConst::VERSION_ENTRY_GDF; - versionEntry.m_properties[GdtConst::VERSION_KEY_GAME] = gdtVersion.m_game; - versionEntry.m_properties[GdtConst::VERSION_KEY_VERSION] = std::to_string(gdtVersion.m_version); - - WriteEntry(versionEntry); - } - - void WriteEntry(const GdtEntry& entry) - { - DoIntendation(); - m_stream << "\"" << entry.m_name << "\" "; - if (entry.m_parent) - m_stream << "[ \"" << entry.m_parent->m_name << "\" ]\n"; - else - m_stream << "( \"" << entry.m_gdf_name << "\" )\n"; - DoIntendation(); - m_stream << "{\n"; - - m_intendation_level++; - - for (const auto& [propertyKey, propertyValue] : entry.m_properties) - { - DoIntendation(); - m_stream << "\"" << propertyKey << "\" \"" << propertyValue << "\"\n"; - } - - m_intendation_level--; - DoIntendation(); - m_stream << "}\n"; - } - -public: - explicit GdtWriter(std::ostream& stream) - : m_stream(stream), - m_intendation_level(0) - { - } - - void Write(const Gdt& gdt) - { - m_stream << "{\n"; - m_intendation_level++; - - WriteVersion(gdt.m_version); - for (const auto& entry : gdt.m_entries) - { - WriteEntry(*entry); - } - - m_intendation_level--; - m_stream << "}"; - } -}; - -bool Gdt::ReadFromStream(std::istream& stream) -{ - GdtReader reader(stream); - return reader.Read(*this); -} - -void Gdt::WriteToStream(std::ostream& stream) const -{ - GdtWriter writer(stream); - writer.Write(*this); } diff --git a/src/ObjCommon/Obj/Gdt/Gdt.h b/src/ObjCommon/Obj/Gdt/Gdt.h index 8c0b509f..da880089 100644 --- a/src/ObjCommon/Obj/Gdt/Gdt.h +++ b/src/ObjCommon/Obj/Gdt/Gdt.h @@ -2,7 +2,6 @@ #include #include -#include #include "GdtEntry.h" #include "GdtVersion.h" @@ -13,6 +12,6 @@ public: GdtVersion m_version; std::vector> m_entries; - bool ReadFromStream(std::istream& stream); - void WriteToStream(std::ostream& stream) const; + Gdt(); + explicit Gdt(GdtVersion version); }; diff --git a/src/ObjCommon/Obj/Gdt/GdtStream.cpp b/src/ObjCommon/Obj/Gdt/GdtStream.cpp new file mode 100644 index 00000000..25cea25c --- /dev/null +++ b/src/ObjCommon/Obj/Gdt/GdtStream.cpp @@ -0,0 +1,314 @@ +#include "GdtStream.h" + +#include +#include + +class GdtConst +{ +public: + static constexpr const char* VERSION_ENTRY_NAME = "version"; + static constexpr const char* VERSION_ENTRY_GDF = "version.gdf"; + static constexpr const char* VERSION_KEY_GAME = "game"; + static constexpr const char* VERSION_KEY_VERSION = "version"; +}; + +void GdtReader::PrintError(const std::string& message) const +{ + std::cout << "GDT Error at line " << m_line << ": " << message << "\n"; +} + +int GdtReader::PeekChar() +{ + if (m_peeked) + return m_char; + + int c; + do + { + c = m_stream.get(); + } + while (isspace(c)); + + m_peeked = true; + m_char = c; + return c; +} + +int GdtReader::NextChar() +{ + if (m_peeked) + { + m_peeked = false; + return m_char; + } + + int c; + do + { + c = m_stream.get(); + } + while (isspace(c)); + + return c; +} + +bool GdtReader::ReadStringContent(std::string& str) +{ + std::ostringstream ss; + + if (NextChar() != '"') + { + PrintError("Expected string opening tag"); + return false; + } + + auto c = m_stream.get(); + while (c != '"' && c != '\n' && c != EOF) + { + ss << static_cast(c); + c = m_stream.get(); + } + + if (c == '"') + { + str = ss.str(); + return true; + } + + return false; +} + +GdtEntry* GdtReader::GetEntryByName(const Gdt& gdt, const std::string& name) +{ + for (const auto& entry : gdt.m_entries) + { + if (entry->m_name == name) + return entry.get(); + } + + return nullptr; +} + +bool GdtReader::ReadProperties(GdtEntry& entry) +{ + while (PeekChar() == '"') + { + std::string propertyKey; + std::string propertyValue; + + if (!ReadStringContent(propertyKey)) + return false; + + if (PeekChar() != '"') + { + PrintError("Expected value string"); + return false; + } + + if (!ReadStringContent(propertyValue)) + return false; + + entry.m_properties.emplace(std::move(propertyKey), std::move(propertyValue)); + } + + + if (NextChar() != '}') + { + PrintError("Expected closing tags"); + return false; + } + + return true; +} + +bool GdtReader::AddEntry(Gdt& gdt, GdtEntry& entry) const +{ + if (entry.m_name == GdtConst::VERSION_ENTRY_NAME + && entry.m_gdf_name == GdtConst::VERSION_ENTRY_GDF) + { + auto foundEntry = entry.m_properties.find(GdtConst::VERSION_KEY_GAME); + if (foundEntry == entry.m_properties.end()) + { + PrintError("Version does not feature game property"); + return false; + } + gdt.m_version.m_game = foundEntry->second; + + foundEntry = entry.m_properties.find(GdtConst::VERSION_KEY_VERSION); + if (foundEntry == entry.m_properties.end()) + { + PrintError("Version does not feature version property"); + return false; + } + gdt.m_version.m_version = strtol(foundEntry->second.c_str(), nullptr, 0); + } + else + { + gdt.m_entries.emplace_back(std::make_unique(std::move(entry))); + } + + return true; +} + +GdtReader::GdtReader(std::istream& stream) + : m_stream(stream), + m_char(0), + m_peeked(false), + m_line(0) +{ +} + +bool GdtReader::Read(Gdt& gdt) +{ + if (NextChar() != '{') + { + PrintError("Expected opening tag"); + return false; + } + + while (PeekChar() == '"') + { + GdtEntry entry; + + if (!ReadStringContent(entry.m_name)) + { + PrintError("Failed to read string"); + return false; + } + + if (PeekChar() == '(') + { + NextChar(); + if (!ReadStringContent(entry.m_gdf_name)) + return false; + if (NextChar() != ')') + { + PrintError("Expected closing parenthesis"); + return false; + } + } + else if (PeekChar() == '[') + { + NextChar(); + std::string parentName; + if (!ReadStringContent(parentName)) + return false; + if (NextChar() != ']') + { + PrintError("Expected closing square brackets"); + return false; + } + entry.m_parent = GetEntryByName(gdt, parentName); + if (entry.m_parent == nullptr) + { + PrintError("Could not find parent with name"); + return false; + } + } + else + { + return false; + } + + if (NextChar() != '{') + { + PrintError("Expected opening tag for entries"); + return false; + } + + if (!ReadProperties(entry)) + return false; + + if (!AddEntry(gdt, entry)) + return false; + } + + + if (NextChar() != '}') + { + PrintError("Expected closing tags"); + return false; + } + + return true; +} + +GdtOutputStream::GdtOutputStream(std::ostream& stream) + : m_stream(stream), + m_open(false), + m_intendation_level(0) +{ +} + +void GdtOutputStream::BeginStream() +{ + if (!m_open) + { + m_stream << "{\n"; + m_intendation_level++; + m_open = true; + } +} + +void GdtOutputStream::DoIntendation() const +{ + for (auto i = 0u; i < m_intendation_level; i++) + m_stream << "\t"; +} + +void GdtOutputStream::WriteVersion(const GdtVersion& gdtVersion) +{ + GdtEntry versionEntry; + versionEntry.m_name = GdtConst::VERSION_ENTRY_NAME; + versionEntry.m_gdf_name = GdtConst::VERSION_ENTRY_GDF; + versionEntry.m_properties[GdtConst::VERSION_KEY_GAME] = gdtVersion.m_game; + versionEntry.m_properties[GdtConst::VERSION_KEY_VERSION] = std::to_string(gdtVersion.m_version); + + WriteEntry(versionEntry); +} + +void GdtOutputStream::WriteEntry(const GdtEntry& entry) +{ + DoIntendation(); + m_stream << "\"" << entry.m_name << "\" "; + if (entry.m_parent) + m_stream << "[ \"" << entry.m_parent->m_name << "\" ]\n"; + else + m_stream << "( \"" << entry.m_gdf_name << "\" )\n"; + DoIntendation(); + m_stream << "{\n"; + + m_intendation_level++; + + for (const auto& [propertyKey, propertyValue] : entry.m_properties) + { + DoIntendation(); + m_stream << "\"" << propertyKey << "\" \"" << propertyValue << "\"\n"; + } + + m_intendation_level--; + DoIntendation(); + m_stream << "}\n"; +} + +void GdtOutputStream::EndStream() +{ + if (m_open) + { + m_intendation_level--; + m_stream << "}"; + m_open = false; + } +} + +void GdtOutputStream::WriteGdt(const Gdt& gdt, std::ostream& stream) +{ + GdtOutputStream out(stream); + out.BeginStream(); + out.WriteVersion(gdt.m_version); + + for (const auto& entry : gdt.m_entries) + out.WriteEntry(*entry); + + out.EndStream(); +} diff --git a/src/ObjCommon/Obj/Gdt/GdtStream.h b/src/ObjCommon/Obj/Gdt/GdtStream.h new file mode 100644 index 00000000..2cf6b251 --- /dev/null +++ b/src/ObjCommon/Obj/Gdt/GdtStream.h @@ -0,0 +1,43 @@ +#pragma once +#include + +#include "Gdt.h" + +class GdtReader +{ + std::istream& m_stream; + char m_char; + bool m_peeked; + int m_line; + + static GdtEntry* GetEntryByName(const Gdt& gdt, const std::string& name); + void PrintError(const std::string& message) const; + int PeekChar(); + int NextChar(); + bool ReadStringContent(std::string& str); + bool ReadProperties(GdtEntry& entry); + bool AddEntry(Gdt& gdt, GdtEntry& entry) const; + +public: + explicit GdtReader(std::istream& stream); + bool Read(Gdt& gdt); +}; + +class GdtOutputStream +{ + std::ostream& m_stream; + bool m_open; + unsigned m_intendation_level; + + void DoIntendation() const; + +public: + explicit GdtOutputStream(std::ostream& stream); + + void BeginStream(); + void WriteVersion(const GdtVersion& gdtVersion); + void WriteEntry(const GdtEntry& entry); + void EndStream(); + + static void WriteGdt(const Gdt& gdt, std::ostream& stream); +}; diff --git a/test/ObjCommonTests/Obj/Gdt/GdtTests.cpp b/test/ObjCommonTests/Obj/Gdt/GdtTests.cpp index 020ee19d..0043345a 100644 --- a/test/ObjCommonTests/Obj/Gdt/GdtTests.cpp +++ b/test/ObjCommonTests/Obj/Gdt/GdtTests.cpp @@ -3,6 +3,7 @@ #include #include "Obj/Gdt/Gdt.h" +#include "Obj/Gdt/GdtStream.h" namespace obj::gdt { @@ -18,7 +19,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_entries.size() == 1); @@ -46,7 +48,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_entries.size() == 1); @@ -74,7 +77,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_entries.empty()); REQUIRE(gdt.m_version.m_game == "t6"); @@ -98,7 +102,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_version.m_game == "t6"); REQUIRE(gdt.m_version.m_version == 1337); @@ -144,7 +149,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_version.m_game == "t6"); REQUIRE(gdt.m_version.m_version == 1337); @@ -208,7 +214,8 @@ namespace obj::gdt std::istringstream ss(gdtString); Gdt gdt; - REQUIRE(gdt.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt)); REQUIRE(gdt.m_version.m_game == "t6"); REQUIRE(gdt.m_version.m_version == 1337); @@ -259,12 +266,13 @@ namespace obj::gdt } std::stringstream ss; - gdt.WriteToStream(ss); + GdtOutputStream::WriteGdt(gdt, ss); std::cout << ss.str(); Gdt gdt2; - REQUIRE(gdt2.ReadFromStream(ss)); + GdtReader reader(ss); + REQUIRE(reader.Read(gdt2)); REQUIRE(gdt2.m_version.m_game == "whatagame"); REQUIRE(gdt2.m_version.m_version == 6969); @@ -292,4 +300,4 @@ namespace obj::gdt REQUIRE(entry.m_properties.at("idk") == "whattotypeanymore"); } } -} \ No newline at end of file +}