2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-06-02 22:47:42 +00:00

Extract Gdt reading and writing classes to GdtStream file

This commit is contained in:
Jan 2021-03-06 10:47:25 +01:00
parent defc388210
commit aa972614e5
5 changed files with 380 additions and 330 deletions

View File

@ -1,323 +1,9 @@
#include "Gdt.h"
#include <sstream>
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<char>(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<GdtEntry>(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);
}

View File

@ -2,7 +2,6 @@
#include <memory>
#include <vector>
#include <iostream>
#include "GdtEntry.h"
#include "GdtVersion.h"
@ -13,6 +12,6 @@ public:
GdtVersion m_version;
std::vector<std::unique_ptr<GdtEntry>> m_entries;
bool ReadFromStream(std::istream& stream);
void WriteToStream(std::ostream& stream) const;
Gdt();
explicit Gdt(GdtVersion version);
};

View File

@ -0,0 +1,314 @@
#include "GdtStream.h"
#include <iostream>
#include <sstream>
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<char>(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<GdtEntry>(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();
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <iostream>
#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);
};

View File

@ -3,6 +3,7 @@
#include <sstream>
#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");
}
}
}
}