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:
2023-01-04 19:57:00 -05:00
parent 43ad1be6ca
commit b469b3aeea
4 changed files with 605 additions and 0 deletions

View File

@@ -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
View 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
View 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
View 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!");
}
}