Add a new class to encapsulate the file format used by save files
The class is not yet used in the wild, but does have a unit test
This commit is contained in:
@@ -201,6 +201,8 @@
|
||||
919DDC0D19007517003E7FED /* freetype.framework in Copy Libraries and Frameworks */ = {isa = PBXBuildFile; fileRef = 919DDC091900750D003E7FED /* freetype.framework */; };
|
||||
919DDC0E1900751C003E7FED /* freetype.framework in Copy Libraries and Frameworks */ = {isa = PBXBuildFile; fileRef = 919DDC091900750D003E7FED /* freetype.framework */; };
|
||||
919DDC0F1900751F003E7FED /* freetype.framework in Copy Libraries and Frameworks */ = {isa = PBXBuildFile; fileRef = 919DDC091900750D003E7FED /* freetype.framework */; };
|
||||
919F2E7E287E529600F47750 /* tagfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919F2E71287E4E0500F47750 /* tagfile.cpp */; };
|
||||
919F2E9A287E5BB700F47750 /* tagfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919F2E99287E5BB700F47750 /* tagfile.cpp */; };
|
||||
919F2ECA287F023100F47750 /* SFML.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 91F6F8E218F87F3700E3EA15 /* SFML.framework */; };
|
||||
919F2ECB287F023100F47750 /* sfml-audio.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 91F6F8DD18F87F3700E3EA15 /* sfml-audio.framework */; };
|
||||
919F2ECC287F023100F47750 /* sfml-graphics.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 91F6F8DE18F87F3700E3EA15 /* sfml-graphics.framework */; };
|
||||
@@ -729,6 +731,10 @@
|
||||
919DDBFA19006CC9003E7FED /* libboost_filesystem-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_filesystem-mt.dylib"; path = "/opt/local/libexec/boost/1.76/lib/libboost_filesystem-mt.dylib"; sourceTree = "<absolute>"; };
|
||||
919DDBFB19006CC9003E7FED /* libboost_system-mt.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libboost_system-mt.dylib"; path = "/opt/local/libexec/boost/1.76/lib/libboost_system-mt.dylib"; sourceTree = "<absolute>"; };
|
||||
919DDC091900750D003E7FED /* freetype.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = freetype.framework; path = /Library/Frameworks/freetype.framework; sourceTree = "<absolute>"; };
|
||||
919F2E71287E4E0500F47750 /* tagfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tagfile.cpp; sourceTree = "<group>"; };
|
||||
919F2E72287E4E0500F47750 /* tagfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tagfile.hpp; sourceTree = "<group>"; };
|
||||
919F2E99287E5BB700F47750 /* tagfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tagfile.cpp; sourceTree = "<group>"; };
|
||||
919F2EAF287F00B800F47750 /* boe_test.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = boe_test.entitlements; sourceTree = "<group>"; };
|
||||
91A0B15A1900F73E00EF438F /* mask.frag */ = {isa = PBXFileReference; explicitFileType = sourcecode.glsl; fileEncoding = 4; path = mask.frag; sourceTree = "<group>"; };
|
||||
91A32BD10FDB797B00C4E957 /* basicbtns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = basicbtns.cpp; sourceTree = "<group>"; };
|
||||
91AC607E0FA26A3B00EEAE67 /* area.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = area.hpp; sourceTree = "<group>"; };
|
||||
@@ -993,6 +999,7 @@
|
||||
2BF04A9F0BF51845006C0831 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
919F2EAF287F00B800F47750 /* boe_test.entitlements */,
|
||||
91279BAB0F9CFB18007B0D52 /* common */,
|
||||
91B3EF380F969E4A00BF5B67 /* Game */,
|
||||
91B3EEF90F969BBD00BF5B67 /* CharEd */,
|
||||
@@ -1282,10 +1289,12 @@
|
||||
91E5C7A60F9F615400C21460 /* fileio_scen.cpp */,
|
||||
915E09081A316D89008BDF00 /* map_parse.cpp */,
|
||||
915325181A2E37EE000A9A1C /* special_parse.cpp */,
|
||||
919F2E71287E4E0500F47750 /* tagfile.cpp */,
|
||||
91BFA3D81902AD78001686E4 /* tarball.cpp */,
|
||||
91E5C7A50F9F615400C21460 /* fileio.hpp */,
|
||||
915E09071A316D6A008BDF00 /* map_parse.hpp */,
|
||||
91F06E8F1A2EBEE70038E902 /* special_parse.hpp */,
|
||||
919F2E72287E4E0500F47750 /* tagfile.hpp */,
|
||||
91BFA3D91902ADD5001686E4 /* tarball.hpp */,
|
||||
91BFA3DC19033E00001686E4 /* gzstream */,
|
||||
912DFE8718E24B0B00B00D75 /* resmgr */,
|
||||
@@ -1507,6 +1516,7 @@
|
||||
91CC173A1B421CA0003D9A69 /* scen_read.cpp */,
|
||||
91CC173B1B421CA0003D9A69 /* scen_write.cpp */,
|
||||
919B13A51BBDE985009905A4 /* spec_legacy.cpp */,
|
||||
919F2E99287E5BB700F47750 /* tagfile.cpp */,
|
||||
9176FEC41D550EFD006EF694 /* talk_legacy.cpp */,
|
||||
91C2A6EE1B8FAA8E00346948 /* talk_read.cpp */,
|
||||
91E381471B97675900F69B81 /* talk_write.cpp */,
|
||||
@@ -1984,6 +1994,7 @@
|
||||
919CC25E1B37736900273FDA /* universe.cpp in Sources */,
|
||||
919CC2731B3773FD00273FDA /* fileio_party.cpp in Sources */,
|
||||
919CC2801B37744800273FDA /* pc.editors.cpp in Sources */,
|
||||
919F2E7E287E529600F47750 /* tagfile.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -2110,6 +2121,7 @@
|
||||
911A14031B8FAFC600900FD9 /* town_read.cpp in Sources */,
|
||||
911A14041B8FB00300900FD9 /* talk_read.cpp in Sources */,
|
||||
911A14051B8FB00600900FD9 /* out_read.cpp in Sources */,
|
||||
919F2E9A287E5BB700F47750 /* tagfile.cpp in Sources */,
|
||||
91E381461B97673700F69B81 /* town_write.cpp in Sources */,
|
||||
91E381481B97677900F69B81 /* talk_write.cpp in Sources */,
|
||||
91E3814A1B97679800F69B81 /* out_write.cpp in Sources */,
|
||||
@@ -2639,6 +2651,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = boe_test.entitlements;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
@@ -2661,6 +2674,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = boe_test.entitlements;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = "@loader_path/TestFrameworks\"";
|
||||
|
127
src/fileio/tagfile.cpp
Normal file
127
src/fileio/tagfile.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// tagfile.cpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2022-07-12.
|
||||
//
|
||||
|
||||
#include "tagfile.hpp"
|
||||
#include <sstream>
|
||||
#include "fileio.hpp"
|
||||
|
||||
void cTagFile::readFrom(std::istream& file) {
|
||||
for(auto& p : pages) p.get().reset();
|
||||
std::istringstream page_in;
|
||||
std::string cur_page;
|
||||
size_t i = 0;
|
||||
while(getline(file, cur_page, '\f')) {
|
||||
page_in.clear();
|
||||
page_in.str(cur_page);
|
||||
while(!pages[i].get().canReadFrom(page_in)) {
|
||||
i++;
|
||||
}
|
||||
if(i >= pages.size()) break;
|
||||
pages[i].get().readFrom(page_in);
|
||||
}
|
||||
}
|
||||
|
||||
void cTagFile::writeTo(std::ostream& file) {
|
||||
bool first = true;
|
||||
for(const auto& page : pages) {
|
||||
if(!first) file << '\f';
|
||||
first = false;
|
||||
page.get().writeTo(file);
|
||||
}
|
||||
}
|
||||
|
||||
cTagFile_Page::cTagFile_Page(cTagFile& owner) {
|
||||
owner.pages.push_back(*this);
|
||||
}
|
||||
|
||||
void cTagFile_Page::readFrom(std::istream& file) {
|
||||
hasBeenRead = true;
|
||||
std::istringstream tag_in;
|
||||
for(std::string key; file >> key; ) {
|
||||
if(tag_map.count(key) == 0) continue;
|
||||
std::string line;
|
||||
getline(file, line);
|
||||
tag_in.clear();
|
||||
tag_in.str(line);
|
||||
tag_map.at(key).get().readFrom(key, tag_in);
|
||||
tag_in.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void cTagFile_Page::writeTo(std::ostream& file) {
|
||||
for(const auto& tag : tag_list) {
|
||||
tag.get().writeTo(tag.get().key, file);
|
||||
}
|
||||
}
|
||||
|
||||
bool cTagFile_Page::canReadFrom(std::istream&) const {
|
||||
return !hasBeenRead;
|
||||
}
|
||||
|
||||
void cTagFile_Page::reset() {
|
||||
hasBeenRead = false;
|
||||
}
|
||||
|
||||
std::string cTagFile_Page::getFirstKey() const {
|
||||
if(tag_list.empty()) return std::string();
|
||||
return tag_list.front().get().key;
|
||||
}
|
||||
|
||||
cTagFile_Tag::cTagFile_Tag(cTagFile_Page& owner, const std::string& key) : key(key) {
|
||||
owner.tag_map.emplace(key, *this);
|
||||
owner.tag_list.push_back(*this);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::readValueFrom(std::istream& file, std::string& to) {
|
||||
to = read_maybe_quoted_string(file);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::writeValueTo(std::ostream& file, const std::string& from) {
|
||||
file << maybe_quote_string(from);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::readValueFrom(std::istream& file, bool& to) {
|
||||
auto f = file.flags();
|
||||
file >> std::boolalpha >> to;
|
||||
file.flags(f);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::writeValueTo(std::ostream& file, const bool& from) {
|
||||
auto f = file.flags();
|
||||
file << std::boolalpha << from;
|
||||
file.flags(f);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::readValueFrom(std::istream& file, char& to) {
|
||||
int temp;
|
||||
file >> temp;
|
||||
to = char(temp);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::writeValueTo(std::ostream& file, const char& from) {
|
||||
file << int(from);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::readValueFrom(std::istream& file, signed char& to) {
|
||||
int temp;
|
||||
file >> temp;
|
||||
to = char(temp);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::writeValueTo(std::ostream& file, const signed char& from) {
|
||||
file << int(from);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::readValueFrom(std::istream& file, unsigned char& to) {
|
||||
int temp;
|
||||
file >> temp;
|
||||
to = char(temp);
|
||||
}
|
||||
|
||||
void cTagFile_Tag::writeValueTo(std::ostream& file, const unsigned char& from) {
|
||||
file << int(from);
|
||||
}
|
276
src/fileio/tagfile.hpp
Normal file
276
src/fileio/tagfile.hpp
Normal file
@@ -0,0 +1,276 @@
|
||||
//
|
||||
// tagfile.hpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2022-07-12.
|
||||
//
|
||||
|
||||
#ifndef BoE_tagfile_hpp
|
||||
#define BoE_tagfile_hpp
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <functional> // for reference_wrapper
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
class cTagFile;
|
||||
class cTagFile_Page;
|
||||
|
||||
class cTagFile_Tag {
|
||||
protected:
|
||||
void readValueFrom(std::istream& file, std::string& to);
|
||||
void writeValueTo(std::ostream& file, const std::string& from);
|
||||
void readValueFrom(std::istream& file, bool& to);
|
||||
void writeValueTo(std::ostream& file, const bool& from);
|
||||
void readValueFrom(std::istream& file, char& to);
|
||||
void writeValueTo(std::ostream& file, const char& from);
|
||||
void readValueFrom(std::istream& file, signed char& to);
|
||||
void writeValueTo(std::ostream& file, const signed char& from);
|
||||
void readValueFrom(std::istream& file, unsigned char& to);
|
||||
void writeValueTo(std::ostream& file, const unsigned char& from);
|
||||
|
||||
template<typename T>
|
||||
static void readValueFrom(std::istream& file, T& to) {
|
||||
file >> to;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void writeValueTo(std::ostream& file, const T& from) {
|
||||
file << from;
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
void readValueFrom(std::istream& file, std::pair<A, B>& to) {
|
||||
readValueFrom(file, to.first);
|
||||
readValueFrom(file, to.second);
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
void writeValueTo(std::ostream& file, const std::pair<A, B>& from) {
|
||||
writeValueTo(file, from.first); file << ' ';
|
||||
writeValueTo(file, from.second);
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
void readValueFrom(std::istream& file, std::tuple<A, B, C>& to) {
|
||||
readValueFrom(file, std::get<0>(to));
|
||||
readValueFrom(file, std::get<1>(to));
|
||||
readValueFrom(file, std::get<2>(to));
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C>
|
||||
void writeValueTo(std::ostream& file, const std::tuple<A, B, C>& from) {
|
||||
writeValueTo(file, std::get<0>(from)); file << ' ';
|
||||
writeValueTo(file, std::get<1>(from)); file << ' ';
|
||||
writeValueTo(file, std::get<2>(from));
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C, typename D>
|
||||
void readValueFrom(std::istream& file, std::tuple<A, B, C, D>& to) {
|
||||
readValueFrom(file, std::get<0>(to));
|
||||
readValueFrom(file, std::get<1>(to));
|
||||
readValueFrom(file, std::get<2>(to));
|
||||
readValueFrom(file, std::get<3>(to));
|
||||
}
|
||||
|
||||
template<typename A, typename B, typename C, typename D>
|
||||
void writeValueTo(std::ostream& file, const std::tuple<A, B, C, D>& from) {
|
||||
writeValueTo(file, std::get<0>(from)); file << ' ';
|
||||
writeValueTo(file, std::get<1>(from)); file << ' ';
|
||||
writeValueTo(file, std::get<2>(from)); file << ' ';
|
||||
writeValueTo(file, std::get<3>(from));
|
||||
}
|
||||
|
||||
public:
|
||||
const std::string key;
|
||||
cTagFile_Tag(cTagFile_Page& owner, const std::string& key);
|
||||
virtual void readFrom(const std::string& key, std::istream& file) = 0;
|
||||
virtual void writeTo(const std::string& key, std::ostream& file) = 0;
|
||||
virtual ~cTagFile_Tag() = default;
|
||||
};
|
||||
|
||||
class cTagFile_Page {
|
||||
friend class cTagFile_Tag;
|
||||
std::map<std::string, std::reference_wrapper<cTagFile_Tag>> tag_map;
|
||||
std::vector<std::reference_wrapper<cTagFile_Tag>> tag_list;
|
||||
protected:
|
||||
mutable bool hasBeenRead = false;
|
||||
std::string getFirstKey() const;
|
||||
cTagFile_Page() = default;
|
||||
public:
|
||||
cTagFile_Page(cTagFile& owner);
|
||||
virtual void readFrom(std::istream& file);
|
||||
virtual void writeTo(std::ostream& file);
|
||||
virtual bool canReadFrom(std::istream& file) const;
|
||||
virtual void reset();
|
||||
virtual ~cTagFile_Page() = default;
|
||||
};
|
||||
|
||||
template<typename Self>
|
||||
class cTagFile_MultiPage : public cTagFile_Page {
|
||||
std::shared_ptr<Self> next;
|
||||
public:
|
||||
using cTagFile_Page::cTagFile_Page;
|
||||
void readFrom(std::istream& file) override {
|
||||
if(!hasBeenRead) {
|
||||
cTagFile_Page::readFrom(file);
|
||||
} else {
|
||||
if(!next) next = std::make_shared<Self>();
|
||||
next->readFrom(file);
|
||||
}
|
||||
}
|
||||
void writeTo(std::ostream& file) override {
|
||||
cTagFile_Page::writeTo(file);
|
||||
if(next) {
|
||||
file << '\f';
|
||||
next->writeTo(file);
|
||||
}
|
||||
}
|
||||
bool canReadFrom(std::istream& file) const override {
|
||||
auto at = file.tellg();
|
||||
std::string key;
|
||||
file >> key;
|
||||
file.seekg(at);
|
||||
return key == getFirstKey();
|
||||
}
|
||||
void reset() override {
|
||||
cTagFile_Page::reset();
|
||||
next.reset();
|
||||
}
|
||||
Self& operator[](size_t i) {
|
||||
if(i == 0) return static_cast<Self&>(*this);
|
||||
if(!next) next = std::make_shared<Self>();
|
||||
return next->operator[](i - 1);
|
||||
}
|
||||
Self& add() {
|
||||
if(next) return next->add();
|
||||
next = std::make_shared<Self>();
|
||||
return *next;
|
||||
}
|
||||
size_t size() {
|
||||
return 1 + (next ? next->size() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
class cTagFile {
|
||||
friend class cTagFile_Page;
|
||||
std::vector<std::reference_wrapper<cTagFile_Page>> pages;
|
||||
public:
|
||||
void readFrom(std::istream& file);
|
||||
void writeTo(std::ostream& file);
|
||||
};
|
||||
|
||||
template<typename T> class cTagFile_ArrayTag;
|
||||
|
||||
template<typename T>
|
||||
class cTagFile_BasicTag : public cTagFile_Tag {
|
||||
T value;
|
||||
public:
|
||||
using cTagFile_Tag::cTagFile_Tag;
|
||||
void readFrom(const std::string&, std::istream& file) override {
|
||||
readValueFrom(file, value);
|
||||
}
|
||||
void writeTo(const std::string& key, std::ostream& file) override {
|
||||
file << key << ' ';
|
||||
writeValueTo(file, value);
|
||||
file << '\n';
|
||||
}
|
||||
operator T() const { return value; }
|
||||
cTagFile_BasicTag operator=(const T& new_value) { value = new_value; return *this; }
|
||||
T& operator*() { return value; }
|
||||
const T& operator*() const { return value; }
|
||||
T* operator->() { return &value; }
|
||||
const T* operator->() const { return &value; }
|
||||
bool operator==(const T& other) { return value == other; }
|
||||
bool operator!=(const T& other) { return value != other; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class cTagFile_OptionalTag : public cTagFile_Tag {
|
||||
boost::optional<T> value;
|
||||
public:
|
||||
using cTagFile_Tag::cTagFile_Tag;
|
||||
void readFrom(const std::string&, std::istream& file) override {
|
||||
value.emplace();
|
||||
readValueFrom(file, *value);
|
||||
}
|
||||
void writeTo(const std::string& key, std::ostream& file) override {
|
||||
if(value.has_value()) {
|
||||
file << key << ' ';
|
||||
writeValueTo(file, *value);
|
||||
file << '\n';
|
||||
}
|
||||
}
|
||||
const T& get() const { return *value; }
|
||||
cTagFile_OptionalTag operator=(const T& new_value) { value = new_value; return *this; }
|
||||
cTagFile_OptionalTag operator=(boost::none_t) { value.reset(); return *this; }
|
||||
T& operator*() { return value; }
|
||||
const T& operator*() const { return value; }
|
||||
T* operator->() { return &value; }
|
||||
const T* operator->() const { return &value; }
|
||||
bool operator==(const T& other) { return value == other; }
|
||||
bool operator!=(const T& other) { return value != other; }
|
||||
bool operator==(boost::none_t) { return value == boost::none; }
|
||||
bool operator!=(boost::none_t) { return value != boost::none; }
|
||||
};
|
||||
|
||||
template<>
|
||||
class cTagFile_OptionalTag<bool> : public cTagFile_Tag {
|
||||
bool value = false;
|
||||
public:
|
||||
using cTagFile_Tag::cTagFile_Tag;
|
||||
void readFrom(const std::string&, std::istream&) override {
|
||||
value = true;
|
||||
}
|
||||
void writeTo(const std::string& key, std::ostream& file) override {
|
||||
if(value) {
|
||||
file << key << '\n';
|
||||
}
|
||||
}
|
||||
const bool& get() const { return value; }
|
||||
cTagFile_OptionalTag operator=(const bool& new_value) { value = new_value; return *this; }
|
||||
bool& operator*() { return value; }
|
||||
const bool& operator*() const { return value; }
|
||||
bool operator==(const bool& other) { return value == other; }
|
||||
bool operator!=(const bool& other) { return value != other; }
|
||||
bool operator==(boost::none_t) { return false; }
|
||||
bool operator!=(boost::none_t) { return false; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class cTagFile_ArrayTag : public cTagFile_Tag {
|
||||
std::vector<T> values;
|
||||
public:
|
||||
using cTagFile_Tag::cTagFile_Tag;
|
||||
void readFrom(const std::string&, std::istream& file) override {
|
||||
values.emplace_back();
|
||||
readValueFrom(file, values.back());
|
||||
}
|
||||
void writeTo(const std::string& key, std::ostream& file) override {
|
||||
for(const auto& v : values) {
|
||||
file << key << ' ';
|
||||
writeValueTo(file, v);
|
||||
file << '\n';
|
||||
}
|
||||
}
|
||||
size_t size() const {
|
||||
return values.size();
|
||||
}
|
||||
void add(const T& v) {
|
||||
values.push_back(v);
|
||||
}
|
||||
typename std::vector<T>::const_iterator begin() const {
|
||||
return values.begin();
|
||||
}
|
||||
typename std::vector<T>::const_iterator end() const {
|
||||
return values.end();
|
||||
}
|
||||
const T& operator[](size_t i) const {
|
||||
return values.at(i);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
188
test/tagfile.cpp
Normal file
188
test/tagfile.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// tagfile.cpp
|
||||
// boe_test
|
||||
//
|
||||
// Created by Celtic Minstrel on 2022-07-12.
|
||||
//
|
||||
|
||||
#include "fileio/tagfile.hpp"
|
||||
#include <sstream>
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
template<typename T>
|
||||
std::ostream& operator<<(std::ostream& os, const cTagFile_BasicTag<T>& tag) {
|
||||
return os << T(tag);
|
||||
}
|
||||
|
||||
class cSampleTagFilePage1 : public cTagFile_Page {
|
||||
public:
|
||||
using cTagFile_Page::cTagFile_Page;
|
||||
cTagFile_BasicTag<int> a{*this, "A"}, b{*this, "B"}, c{*this, "C"};
|
||||
};
|
||||
|
||||
class cSampleTagFilePage2 : public cTagFile_Page {
|
||||
public:
|
||||
using cTagFile_Page::cTagFile_Page;
|
||||
cTagFile_BasicTag<std::string> x{*this, "X"}, y{*this, "Y"}, z{*this, "Z"};
|
||||
};
|
||||
|
||||
class cSampleTagFile : public cTagFile {
|
||||
public:
|
||||
cSampleTagFilePage1 p1{*this};
|
||||
cSampleTagFilePage2 p2{*this};
|
||||
};
|
||||
|
||||
class cComplexTagFilePage : public cTagFile_Page {
|
||||
public:
|
||||
using cTagFile_Page::cTagFile_Page;
|
||||
cTagFile_BasicTag<int> id{*this, "ID"};
|
||||
cTagFile_ArrayTag<std::string> strings{*this, "STRING"};
|
||||
cTagFile_ArrayTag<std::pair<int, int>> locations{*this, "LOC"};
|
||||
cTagFile_OptionalTag<int> filter{*this, "FILTER"};
|
||||
cTagFile_OptionalTag<int> count{*this, "COUNT"};
|
||||
cTagFile_OptionalTag<bool> yes{*this, "YES"};
|
||||
cTagFile_OptionalTag<bool> no{*this, "NO"};
|
||||
cTagFile_BasicTag<bool> enable{*this, "ENABLE"};
|
||||
};
|
||||
|
||||
class cTagFileSampleMultiPage : public cTagFile_MultiPage<cTagFileSampleMultiPage> {
|
||||
public:
|
||||
using cTagFile_MultiPage::cTagFile_MultiPage;
|
||||
cTagFile_BasicTag<char> id{*this, "ID"};
|
||||
cTagFile_BasicTag<int> value{*this, "VALUE"};
|
||||
cTagFile_BasicTag<std::string> comment{*this, "COMMENT"};
|
||||
};
|
||||
|
||||
class cComplexTagFile : public cTagFile {
|
||||
public:
|
||||
cSampleTagFilePage1 p1{*this};
|
||||
cComplexTagFilePage p2{*this};
|
||||
cTagFileSampleMultiPage p3{*this};
|
||||
};
|
||||
|
||||
TEST_CASE("Simple tag file") {
|
||||
static const std::string file_contents =
|
||||
"A 12\n"
|
||||
"B 22\n"
|
||||
"C 45\n"
|
||||
"\f"
|
||||
"X 'Hello World'\n"
|
||||
"Y foo\n"
|
||||
"Z Blah!\n"
|
||||
;
|
||||
cSampleTagFile content;
|
||||
SECTION("output") {
|
||||
std::ostringstream file;
|
||||
content.p1.a = 12;
|
||||
content.p1.b = 22;
|
||||
content.p1.c = 45;
|
||||
content.p2.x = "Hello World";
|
||||
content.p2.y = "foo";
|
||||
content.p2.z = "Blah!";
|
||||
content.writeTo(file);
|
||||
CHECK(file.str() == file_contents);
|
||||
}
|
||||
SECTION("input") {
|
||||
std::istringstream file(file_contents);
|
||||
content.readFrom(file);
|
||||
CHECK(content.p1.a == 12);
|
||||
CHECK(content.p1.b == 22);
|
||||
CHECK(content.p1.c == 45);
|
||||
CHECK(content.p2.x == std::string("Hello World"));
|
||||
CHECK(content.p2.y == std::string("foo"));
|
||||
CHECK(content.p2.z == std::string("Blah!"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Complex tag file") {
|
||||
static const std::string file_contents =
|
||||
"A 12\n"
|
||||
"B 22\n"
|
||||
"C 45\n"
|
||||
"\f"
|
||||
"ID 123\n"
|
||||
"STRING foo\n"
|
||||
"STRING bar\n"
|
||||
"LOC 1 5\n"
|
||||
"LOC 12 22\n"
|
||||
"LOC 143 9\n"
|
||||
"COUNT 12\n"
|
||||
"YES\n"
|
||||
"ENABLE false\n"
|
||||
"\f"
|
||||
"ID 12\n"
|
||||
"VALUE 400\n"
|
||||
"COMMENT 'This is a comment!!!'\n"
|
||||
"\f"
|
||||
"ID 13\n"
|
||||
"VALUE 128\n"
|
||||
"COMMENT 'Nope nope nope'\n"
|
||||
"\f"
|
||||
"ID 18\n"
|
||||
"VALUE 90\n"
|
||||
"COMMENT \"It's great!\"\n"
|
||||
;
|
||||
cComplexTagFile content;
|
||||
SECTION("output") {
|
||||
std::ostringstream file;
|
||||
content.p1.a = 12;
|
||||
content.p1.b = 22;
|
||||
content.p1.c = 45;
|
||||
content.p2.id = 123;
|
||||
content.p2.strings.add("foo");
|
||||
content.p2.strings.add("bar");
|
||||
content.p2.locations.add({1,5});
|
||||
content.p2.locations.add({12,22});
|
||||
content.p2.locations.add({143,9});
|
||||
content.p2.count = 12;
|
||||
content.p2.yes = true;
|
||||
content.p2.no = false;
|
||||
content.p2.enable = false;
|
||||
content.p3[0].id = 0x0c;
|
||||
content.p3[0].value = 400;
|
||||
content.p3[0].comment = "This is a comment!!!";
|
||||
content.p3[1].id = 0x0d;
|
||||
content.p3[1].value = 128;
|
||||
content.p3[1].comment = "Nope nope nope";
|
||||
auto& p3c = content.p3.add();
|
||||
p3c.id = 0x12;
|
||||
p3c.value = 90;
|
||||
p3c.comment = "It's great!";
|
||||
content.writeTo(file);
|
||||
CHECK(file.str() == file_contents);
|
||||
}
|
||||
SECTION("input") {
|
||||
std::istringstream file(file_contents);
|
||||
content.readFrom(file);
|
||||
CHECK(content.p1.a == 12);
|
||||
CHECK(content.p1.b == 22);
|
||||
CHECK(content.p1.c == 45);
|
||||
CHECK(content.p2.id == 123);
|
||||
REQUIRE(content.p2.strings.size() == 2);
|
||||
CHECK(content.p2.strings[0] == "foo");
|
||||
CHECK(content.p2.strings[1] == "bar");
|
||||
REQUIRE(content.p2.locations.size() == 3);
|
||||
CHECK(content.p2.locations[0].first == 1);
|
||||
CHECK(content.p2.locations[0].second == 5);
|
||||
CHECK(content.p2.locations[1].first == 12);
|
||||
CHECK(content.p2.locations[1].second == 22);
|
||||
CHECK(content.p2.locations[2].first == 143);
|
||||
CHECK(content.p2.locations[2].second == 9);
|
||||
CHECK(content.p2.filter == boost::none);
|
||||
CHECK(content.p2.count == 12);
|
||||
CHECK(content.p2.yes == true);
|
||||
CHECK(content.p2.no == false);
|
||||
CHECK(content.p2.enable == false);
|
||||
REQUIRE(content.p3.size() == 3);
|
||||
CHECK(content.p3[0].id == '\x0c');
|
||||
CHECK(content.p3[0].value == 400);
|
||||
CHECK(content.p3[0].comment == "This is a comment!!!");
|
||||
CHECK(content.p3[1].id == '\x0d');
|
||||
CHECK(content.p3[1].value == 128);
|
||||
CHECK(content.p3[1].comment == "Nope nope nope");
|
||||
CHECK(content.p3[2].id == '\x12');
|
||||
CHECK(content.p3[2].value == 90);
|
||||
CHECK(content.p3[2].comment == "It's great!");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user