diff --git a/proj/xc12/BoE.xcodeproj/project.pbxproj b/proj/xc12/BoE.xcodeproj/project.pbxproj index 198b1322..6647b211 100755 --- a/proj/xc12/BoE.xcodeproj/project.pbxproj +++ b/proj/xc12/BoE.xcodeproj/project.pbxproj @@ -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 = ""; }; 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 = ""; }; 919DDC091900750D003E7FED /* freetype.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = freetype.framework; path = /Library/Frameworks/freetype.framework; sourceTree = ""; }; + 919F2E71287E4E0500F47750 /* tagfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tagfile.cpp; sourceTree = ""; }; + 919F2E72287E4E0500F47750 /* tagfile.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tagfile.hpp; sourceTree = ""; }; + 919F2E99287E5BB700F47750 /* tagfile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tagfile.cpp; sourceTree = ""; }; + 919F2EAF287F00B800F47750 /* boe_test.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = boe_test.entitlements; sourceTree = ""; }; 91A0B15A1900F73E00EF438F /* mask.frag */ = {isa = PBXFileReference; explicitFileType = sourcecode.glsl; fileEncoding = 4; path = mask.frag; sourceTree = ""; }; 91A32BD10FDB797B00C4E957 /* basicbtns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = basicbtns.cpp; sourceTree = ""; }; 91AC607E0FA26A3B00EEAE67 /* area.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = area.hpp; sourceTree = ""; }; @@ -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\""; diff --git a/src/fileio/tagfile.cpp b/src/fileio/tagfile.cpp new file mode 100644 index 00000000..bd4923d8 --- /dev/null +++ b/src/fileio/tagfile.cpp @@ -0,0 +1,127 @@ +// +// tagfile.cpp +// BoE +// +// Created by Celtic Minstrel on 2022-07-12. +// + +#include "tagfile.hpp" +#include +#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); +} diff --git a/src/fileio/tagfile.hpp b/src/fileio/tagfile.hpp new file mode 100644 index 00000000..aed3286d --- /dev/null +++ b/src/fileio/tagfile.hpp @@ -0,0 +1,276 @@ +// +// tagfile.hpp +// BoE +// +// Created by Celtic Minstrel on 2022-07-12. +// + +#ifndef BoE_tagfile_hpp +#define BoE_tagfile_hpp + +#include +#include +#include +#include // for reference_wrapper +#include +#include +#include + +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 + static void readValueFrom(std::istream& file, T& to) { + file >> to; + } + + template + static void writeValueTo(std::ostream& file, const T& from) { + file << from; + } + + template + void readValueFrom(std::istream& file, std::pair& to) { + readValueFrom(file, to.first); + readValueFrom(file, to.second); + } + + template + void writeValueTo(std::ostream& file, const std::pair& from) { + writeValueTo(file, from.first); file << ' '; + writeValueTo(file, from.second); + } + + template + void readValueFrom(std::istream& file, std::tuple& to) { + readValueFrom(file, std::get<0>(to)); + readValueFrom(file, std::get<1>(to)); + readValueFrom(file, std::get<2>(to)); + } + + template + void writeValueTo(std::ostream& file, const std::tuple& from) { + writeValueTo(file, std::get<0>(from)); file << ' '; + writeValueTo(file, std::get<1>(from)); file << ' '; + writeValueTo(file, std::get<2>(from)); + } + + template + void readValueFrom(std::istream& file, std::tuple& 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 + void writeValueTo(std::ostream& file, const std::tuple& 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> tag_map; + std::vector> 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 +class cTagFile_MultiPage : public cTagFile_Page { + std::shared_ptr 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(); + 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(*this); + if(!next) next = std::make_shared(); + return next->operator[](i - 1); + } + Self& add() { + if(next) return next->add(); + next = std::make_shared(); + return *next; + } + size_t size() { + return 1 + (next ? next->size() : 0); + } +}; + +class cTagFile { + friend class cTagFile_Page; + std::vector> pages; +public: + void readFrom(std::istream& file); + void writeTo(std::ostream& file); +}; + +template class cTagFile_ArrayTag; + +template +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 +class cTagFile_OptionalTag : public cTagFile_Tag { + boost::optional 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 : 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 +class cTagFile_ArrayTag : public cTagFile_Tag { + std::vector 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::const_iterator begin() const { + return values.begin(); + } + typename std::vector::const_iterator end() const { + return values.end(); + } + const T& operator[](size_t i) const { + return values.at(i); + } +}; + +#endif diff --git a/test/tagfile.cpp b/test/tagfile.cpp new file mode 100644 index 00000000..bd53118a --- /dev/null +++ b/test/tagfile.cpp @@ -0,0 +1,188 @@ +// +// tagfile.cpp +// boe_test +// +// Created by Celtic Minstrel on 2022-07-12. +// + +#include "fileio/tagfile.hpp" +#include + +#include "catch.hpp" + +template +std::ostream& operator<<(std::ostream& os, const cTagFile_BasicTag& tag) { + return os << T(tag); +} + +class cSampleTagFilePage1 : public cTagFile_Page { +public: + using cTagFile_Page::cTagFile_Page; + cTagFile_BasicTag a{*this, "A"}, b{*this, "B"}, c{*this, "C"}; +}; + +class cSampleTagFilePage2 : public cTagFile_Page { +public: + using cTagFile_Page::cTagFile_Page; + cTagFile_BasicTag 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 id{*this, "ID"}; + cTagFile_ArrayTag strings{*this, "STRING"}; + cTagFile_ArrayTag> locations{*this, "LOC"}; + cTagFile_OptionalTag filter{*this, "FILTER"}; + cTagFile_OptionalTag count{*this, "COUNT"}; + cTagFile_OptionalTag yes{*this, "YES"}; + cTagFile_OptionalTag no{*this, "NO"}; + cTagFile_BasicTag enable{*this, "ENABLE"}; +}; + +class cTagFileSampleMultiPage : public cTagFile_MultiPage { +public: + using cTagFile_MultiPage::cTagFile_MultiPage; + cTagFile_BasicTag id{*this, "ID"}; + cTagFile_BasicTag value{*this, "VALUE"}; + cTagFile_BasicTag 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!"); + } +}