diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2022-10-29 16:51:36 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2022-10-29 16:51:36 +0200 |
commit | 9813d9263e0b45f46b90579ea217117da823dad7 (patch) | |
tree | 88199e91e5ec2f9469bb8197e89b7cd5c9ce45ab /serialize | |
parent | 4c982b2ffe99f5dee70657424161de9541e00a21 (diff) |
convert line endings
Diffstat (limited to 'serialize')
-rw-r--r-- | serialize/binary-reader.hpp | 98 | ||||
-rw-r--r-- | serialize/binary-reader.inl | 166 | ||||
-rw-r--r-- | serialize/binary-writer.hpp | 46 | ||||
-rw-r--r-- | serialize/world-impl.hpp | 120 | ||||
-rw-r--r-- | serialize/world-reader.cpp | 302 | ||||
-rw-r--r-- | serialize/world-writer.cpp | 556 |
6 files changed, 644 insertions, 644 deletions
diff --git a/serialize/binary-reader.hpp b/serialize/binary-reader.hpp index 2d09d51b..881ed88f 100644 --- a/serialize/binary-reader.hpp +++ b/serialize/binary-reader.hpp @@ -1,49 +1,49 @@ -#pragma once
-#include "binary-serializer.hpp"
-#include <iterator>
-#include <Corrade/Containers/StringView.h>
-
-namespace floormat::Serialize {
-
-template<typename T>
-concept char_sequence = requires(T& x, const T& cx) {
- requires std::same_as<decltype(std::begin(x)), decltype(std::end(x))>;
- requires std::same_as<decltype(std::cbegin(cx)), decltype(std::cend(cx))>;
- requires std::forward_iterator<decltype(std::begin(x))>;
- requires std::forward_iterator<decltype(std::cbegin(cx))>;
- requires std::same_as<char, std::decay_t<decltype(*std::begin(x))>>;
- requires std::same_as<char, std::decay_t<decltype(*std::cbegin(x))>>;
-};
-
-template<typename It>
-concept string_input_iterator = requires(It it) {
- requires std::forward_iterator<It>;
- requires std::is_same_v<char, std::decay_t<decltype(*it)>>;
-};
-
-template<string_input_iterator It>
-struct binary_reader final {
- template<char_sequence Seq> explicit constexpr binary_reader(const Seq& seq) noexcept;
- constexpr binary_reader(It begin, It end) noexcept;
- constexpr void assert_end() noexcept;
-
- template<serializable T> constexpr T read() noexcept;
- template<std::size_t N> constexpr std::array<char, N> read() noexcept;
- constexpr std::size_t bytes_read() const noexcept { return num_bytes_read; }
- template<std::size_t Max> constexpr auto read_asciiz_string() noexcept;
-
-private:
- std::size_t num_bytes_read = 0;
- It it, end;
-};
-
-template<string_input_iterator It, serializable T>
-binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept;
-
-template<string_input_iterator It> binary_reader(It&& begin, It&& end) -> binary_reader<std::decay_t<It>>;
-
-template<typename Array>
-binary_reader(Array&& array) -> binary_reader<std::decay_t<decltype(std::begin(array))>>;
-
-} // namespace floormat::Serialize
-
+#pragma once +#include "binary-serializer.hpp" +#include <iterator> +#include <Corrade/Containers/StringView.h> + +namespace floormat::Serialize { + +template<typename T> +concept char_sequence = requires(T& x, const T& cx) { + requires std::same_as<decltype(std::begin(x)), decltype(std::end(x))>; + requires std::same_as<decltype(std::cbegin(cx)), decltype(std::cend(cx))>; + requires std::forward_iterator<decltype(std::begin(x))>; + requires std::forward_iterator<decltype(std::cbegin(cx))>; + requires std::same_as<char, std::decay_t<decltype(*std::begin(x))>>; + requires std::same_as<char, std::decay_t<decltype(*std::cbegin(x))>>; +}; + +template<typename It> +concept string_input_iterator = requires(It it) { + requires std::forward_iterator<It>; + requires std::is_same_v<char, std::decay_t<decltype(*it)>>; +}; + +template<string_input_iterator It> +struct binary_reader final { + template<char_sequence Seq> explicit constexpr binary_reader(const Seq& seq) noexcept; + constexpr binary_reader(It begin, It end) noexcept; + constexpr void assert_end() noexcept; + + template<serializable T> constexpr T read() noexcept; + template<std::size_t N> constexpr std::array<char, N> read() noexcept; + constexpr std::size_t bytes_read() const noexcept { return num_bytes_read; } + template<std::size_t Max> constexpr auto read_asciiz_string() noexcept; + +private: + std::size_t num_bytes_read = 0; + It it, end; +}; + +template<string_input_iterator It, serializable T> +binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept; + +template<string_input_iterator It> binary_reader(It&& begin, It&& end) -> binary_reader<std::decay_t<It>>; + +template<typename Array> +binary_reader(Array&& array) -> binary_reader<std::decay_t<decltype(std::begin(array))>>; + +} // namespace floormat::Serialize + diff --git a/serialize/binary-reader.inl b/serialize/binary-reader.inl index b190f463..2230dff6 100644 --- a/serialize/binary-reader.inl +++ b/serialize/binary-reader.inl @@ -1,83 +1,83 @@ -#pragma once
-#include "binary-reader.hpp"
-#include "compat/assert.hpp"
-
-namespace floormat::Serialize {
-
-template<string_input_iterator It>
-template<char_sequence Seq>
-constexpr binary_reader<It>::binary_reader(const Seq& seq) noexcept
- : it{std::begin(seq)}, end{std::end(seq)}
-{}
-
-template<string_input_iterator It>
-constexpr binary_reader<It>::binary_reader(It begin, It end) noexcept :
- it{begin}, end{end}
-{}
-
-template<string_input_iterator It>
-template<serializable T>
-constexpr T binary_reader<It>::read() noexcept
-{
- constexpr std::size_t N = sizeof(T);
- fm_assert((std::ptrdiff_t)N <= std::distance(it, end));
- num_bytes_read += N;
- char buf[N];
- for (std::size_t i = 0; i < N; i++)
- buf[i] = *it++;
- return maybe_byteswap(std::bit_cast<T, decltype(buf)>(buf));
-}
-
-template<string_input_iterator It>
-template<std::size_t N>
-constexpr std::array<char, N> binary_reader<It>::read() noexcept
-{
- std::array<char, N> array;
- if (std::is_constant_evaluated())
- array = {};
- fm_assert(N <= (std::size_t)std::distance(it, end));
- num_bytes_read += N;
- for (std::size_t i = 0; i < N; i++)
- array[i] = *it++;
- return array;
-}
-
-template<string_input_iterator It>
-constexpr void binary_reader<It>::assert_end() noexcept
-{
- fm_assert(it == end);
-}
-
-template<string_input_iterator It, serializable T>
-binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept
-{
- x = reader.template read<T>();
- return reader;
-}
-
-template<string_input_iterator It>
-template<std::size_t MAX>
-constexpr auto binary_reader<It>::read_asciiz_string() noexcept
-{
- static_assert(MAX > 0);
-
- struct fixed_string final {
- char buf[MAX];
- std::size_t len;
- };
-
- fixed_string ret;
- for (std::size_t i = 0; i < MAX && it != end; i++)
- {
- const char c = *it++;
- ret.buf[i] = c;
- if (c == '\0')
- {
- ret.len = i;
- return ret;
- }
- }
- fm_abort("can't find string terminator");
-}
-
-} // namespace floormat::Serialize
+#pragma once +#include "binary-reader.hpp" +#include "compat/assert.hpp" + +namespace floormat::Serialize { + +template<string_input_iterator It> +template<char_sequence Seq> +constexpr binary_reader<It>::binary_reader(const Seq& seq) noexcept + : it{std::begin(seq)}, end{std::end(seq)} +{} + +template<string_input_iterator It> +constexpr binary_reader<It>::binary_reader(It begin, It end) noexcept : + it{begin}, end{end} +{} + +template<string_input_iterator It> +template<serializable T> +constexpr T binary_reader<It>::read() noexcept +{ + constexpr std::size_t N = sizeof(T); + fm_assert((std::ptrdiff_t)N <= std::distance(it, end)); + num_bytes_read += N; + char buf[N]; + for (std::size_t i = 0; i < N; i++) + buf[i] = *it++; + return maybe_byteswap(std::bit_cast<T, decltype(buf)>(buf)); +} + +template<string_input_iterator It> +template<std::size_t N> +constexpr std::array<char, N> binary_reader<It>::read() noexcept +{ + std::array<char, N> array; + if (std::is_constant_evaluated()) + array = {}; + fm_assert(N <= (std::size_t)std::distance(it, end)); + num_bytes_read += N; + for (std::size_t i = 0; i < N; i++) + array[i] = *it++; + return array; +} + +template<string_input_iterator It> +constexpr void binary_reader<It>::assert_end() noexcept +{ + fm_assert(it == end); +} + +template<string_input_iterator It, serializable T> +binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept +{ + x = reader.template read<T>(); + return reader; +} + +template<string_input_iterator It> +template<std::size_t MAX> +constexpr auto binary_reader<It>::read_asciiz_string() noexcept +{ + static_assert(MAX > 0); + + struct fixed_string final { + char buf[MAX]; + std::size_t len; + }; + + fixed_string ret; + for (std::size_t i = 0; i < MAX && it != end; i++) + { + const char c = *it++; + ret.buf[i] = c; + if (c == '\0') + { + ret.len = i; + return ret; + } + } + fm_abort("can't find string terminator"); +} + +} // namespace floormat::Serialize diff --git a/serialize/binary-writer.hpp b/serialize/binary-writer.hpp index 13580eb0..098f9aa2 100644 --- a/serialize/binary-writer.hpp +++ b/serialize/binary-writer.hpp @@ -1,23 +1,23 @@ -#pragma once
-#include "binary-serializer.hpp"
-#include <iterator>
-#include <Corrade/Containers/StringView.h>
-
-namespace floormat::Serialize {
-
-template<std::output_iterator<char> It>
-struct binary_writer final {
- explicit constexpr binary_writer(It it) noexcept;
- template<serializable T> constexpr void write(T x) noexcept;
- constexpr void write_asciiz_string(StringView str) noexcept;
- constexpr std::size_t bytes_written() const noexcept { return _bytes_written; }
-
-private:
- It it;
- std::size_t _bytes_written;
-};
-
-template<std::output_iterator<char> It, serializable T>
-constexpr binary_writer<It>& operator<<(binary_writer<It>& writer, T x) noexcept;
-
-} // namespace floormat::Serialize
+#pragma once +#include "binary-serializer.hpp" +#include <iterator> +#include <Corrade/Containers/StringView.h> + +namespace floormat::Serialize { + +template<std::output_iterator<char> It> +struct binary_writer final { + explicit constexpr binary_writer(It it) noexcept; + template<serializable T> constexpr void write(T x) noexcept; + constexpr void write_asciiz_string(StringView str) noexcept; + constexpr std::size_t bytes_written() const noexcept { return _bytes_written; } + +private: + It it; + std::size_t _bytes_written; +}; + +template<std::output_iterator<char> It, serializable T> +constexpr binary_writer<It>& operator<<(binary_writer<It>& writer, T x) noexcept; + +} // namespace floormat::Serialize diff --git a/serialize/world-impl.hpp b/serialize/world-impl.hpp index 54e4622c..a0414606 100644 --- a/serialize/world-impl.hpp +++ b/serialize/world-impl.hpp @@ -1,60 +1,60 @@ -#ifndef FM_SERIALIZE_WORLD_IMPL
-#error "not meant to be included directly"
-#endif
-
-#pragma once
-#include "src/tile.hpp"
-#include <bit>
-#include <cstdio>
-
-namespace floormat::Serialize {
-
-namespace {
-
-using tilemeta = std::uint8_t;
-using varid = decltype(tile_image::variant);
-using atlasid = std::uint16_t;
-using chunksiz = std::uint16_t;
-using enum tile::pass_mode;
-
-template<typename T> constexpr inline T int_max = std::numeric_limits<T>::max();
-
-#define file_magic ".floormat.save"
-
-constexpr inline std::size_t atlas_name_max = 128;
-constexpr inline auto null_atlas = (atlasid)-1LL;
-
-constexpr inline std::uint16_t proto_version = 1;
-constexpr inline auto chunk_magic = (std::uint16_t)~0xc0d3;
-
-constexpr inline std::underlying_type_t<tile::pass_mode> pass_mask = pass_blocked | pass_shoot_through | pass_ok;
-constexpr inline auto pass_bits = std::bit_width(pass_mask);
-
-enum : tilemeta {
- meta_ground = 1 << (pass_bits + 0),
- meta_wall_n = 1 << (pass_bits + 1),
- meta_wall_w = 1 << (pass_bits + 2),
- meta_short_atlasid = 1 << (pass_bits + 3),
- meta_short_variant = 1 << (pass_bits + 4),
-};
-
-} // namespace
-
-} // namespace floormat::Serialize
-
-namespace floormat {
-
-namespace {
-
-struct FILE_raii final {
- FILE_raii(FILE* s) noexcept : s{s} {}
- ~FILE_raii() noexcept { close(); }
- operator FILE*() noexcept { return s; }
- void close() noexcept { if (s) ::fclose(s); s = nullptr; }
-private:
- FILE* s;
-};
-
-} // namespace
-
-} // namespace floormat
+#ifndef FM_SERIALIZE_WORLD_IMPL +#error "not meant to be included directly" +#endif + +#pragma once +#include "src/tile.hpp" +#include <bit> +#include <cstdio> + +namespace floormat::Serialize { + +namespace { + +using tilemeta = std::uint8_t; +using varid = decltype(tile_image::variant); +using atlasid = std::uint16_t; +using chunksiz = std::uint16_t; +using enum tile::pass_mode; + +template<typename T> constexpr inline T int_max = std::numeric_limits<T>::max(); + +#define file_magic ".floormat.save" + +constexpr inline std::size_t atlas_name_max = 128; +constexpr inline auto null_atlas = (atlasid)-1LL; + +constexpr inline std::uint16_t proto_version = 1; +constexpr inline auto chunk_magic = (std::uint16_t)~0xc0d3; + +constexpr inline std::underlying_type_t<tile::pass_mode> pass_mask = pass_blocked | pass_shoot_through | pass_ok; +constexpr inline auto pass_bits = std::bit_width(pass_mask); + +enum : tilemeta { + meta_ground = 1 << (pass_bits + 0), + meta_wall_n = 1 << (pass_bits + 1), + meta_wall_w = 1 << (pass_bits + 2), + meta_short_atlasid = 1 << (pass_bits + 3), + meta_short_variant = 1 << (pass_bits + 4), +}; + +} // namespace + +} // namespace floormat::Serialize + +namespace floormat { + +namespace { + +struct FILE_raii final { + FILE_raii(FILE* s) noexcept : s{s} {} + ~FILE_raii() noexcept { close(); } + operator FILE*() noexcept { return s; } + void close() noexcept { if (s) ::fclose(s); s = nullptr; } +private: + FILE* s; +}; + +} // namespace + +} // namespace floormat diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp index 6172703b..385d6e39 100644 --- a/serialize/world-reader.cpp +++ b/serialize/world-reader.cpp @@ -1,151 +1,151 @@ -#define FM_SERIALIZE_WORLD_IMPL
-#include "world-impl.hpp"
-#include "binary-reader.inl"
-#include "src/world.hpp"
-#include "src/loader.hpp"
-#include "src/tile-atlas.hpp"
-
-namespace floormat::Serialize {
-
-namespace {
-
-struct reader_state final {
- explicit reader_state(world& world) noexcept;
- void deserialize_world(ArrayView<const char> buf);
-
-private:
- using reader_t = binary_reader<decltype(ArrayView<const char>{}.cbegin())>;
-
- std::shared_ptr<tile_atlas> lookup_atlas(atlasid id);
- void read_atlases(reader_t& reader);
- void read_chunks(reader_t& reader);
-
- std::unordered_map<atlasid, std::shared_ptr<tile_atlas>> atlases;
- struct world* world;
-};
-
-reader_state::reader_state(struct world& world) noexcept : world{&world} {}
-
-void reader_state::read_atlases(reader_t& s)
-{
- const auto N = s.read<atlasid>();
- atlases.reserve(N * 2);
- for (atlasid i = 0; i < N; i++)
- {
- Vector2ub size;
- s >> size[0];
- s >> size[1];
- const auto& [buf, len] = s.read_asciiz_string<atlas_name_max>();
- atlases[i] = loader.tile_atlas({buf, len}, size);
- }
-}
-
-std::shared_ptr<tile_atlas> reader_state::lookup_atlas(atlasid id)
-{
- if (auto it = atlases.find(id); it != atlases.end())
- return it->second;
- else
- fm_abort("not such atlas: '%zu'", (std::size_t)id);
-}
-
-void reader_state::read_chunks(reader_t& s)
-{
- const auto N = s.read<chunksiz>();
-
- for (std::size_t i = 0; i < N; i++)
- {
- std::decay_t<decltype(chunk_magic)> magic;
- s >> magic;
- if (magic != chunk_magic)
- fm_abort("bad chunk magic");
- chunk_coords coord;
- s >> coord.x;
- s >> coord.y;
- auto& chunk = (*world)[coord];
- for (std::size_t i = 0; i < TILE_COUNT; i++)
- {
- const tilemeta flags = s.read<tilemeta>();
- tile& t = chunk[i];
- using uchar = std::uint8_t;
- const auto make_atlas = [&]() -> tile_image {
- auto id = flags & meta_short_atlasid ? (atlasid)(s.read<uchar>()) : s.read<atlasid>();
- auto v = flags & meta_short_variant ? (varid) (s.read<uchar>()) : s.read<varid>();
- auto atlas = lookup_atlas(id);
- fm_assert(v < atlas->num_tiles());
- return { atlas, v };
- };
-
- if (flags & meta_ground)
- t.ground = make_atlas();
- if (flags & meta_wall_n)
- t.wall_north = make_atlas();
- if (flags & meta_wall_w)
- t.wall_west = make_atlas();
-
- switch (auto x = flags & pass_mask)
- {
- case tile::pass_shoot_through:
- case tile::pass_blocked:
- case tile::pass_ok:
- t.passability = (tile::pass_mode)x;
- break;
- default:
- fm_abort("bad pass mode '%zu' for tile %zu", i, (std::size_t)x);
- }
- }
- }
-}
-
-void reader_state::deserialize_world(ArrayView<const char> buf)
-{
- auto s = binary_reader{buf};
- if (!!::memcmp(s.read<std::size(file_magic)-1>().data(), file_magic, std::size(file_magic)-1))
- fm_abort("bad magic");
- std::decay_t<decltype(proto_version)> proto;
- s >> proto;
- if (proto != proto_version)
- fm_abort("bad proto version '%zu' (should be '%zu')",
- (std::size_t)proto, (std::size_t)proto_version);
- read_atlases(s);
- read_chunks(s);
- s.assert_end();
-}
-
-} // namespace
-
-} // namespace floormat::Serialize
-
-namespace floormat {
-
-world world::deserialize(StringView filename)
-{
- char errbuf[128];
- constexpr auto strerror = []<std::size_t N> (char (&buf)[N]) -> const char* {
- ::strerror_s(buf, std::size(buf), errno);
- return buf;
- };
- fm_assert(filename.flags() & StringViewFlag::NullTerminated);
- FILE_raii f = ::fopen(filename.data(), "rb");
- if (!f)
- fm_abort("fopen(\"%s\", \"r\"): %s", filename.data(), strerror(errbuf));
- if (int ret = ::fseek(f, 0, SEEK_END); ret != 0)
- fm_abort("fseek(SEEK_END): %s", strerror(errbuf));
- std::size_t len;
- if (auto len_ = ::ftell(f); len_ >= 0)
- len = (std::size_t)len_;
- else
- fm_abort("ftell: %s", strerror(errbuf));
- if (int ret = ::fseek(f, 0, SEEK_SET); ret != 0)
- fm_abort("fseek(SEEK_SET): %s", strerror(errbuf));
- auto buf_ = std::make_unique<char[]>(len);
-
- if (auto ret = ::fread(&buf_[0], 1, len, f); ret != len)
- fm_abort("fread short read: %s", strerror(errbuf));
-
- world w;
- Serialize::reader_state s{w};
- s.deserialize_world({buf_.get(), len});
- return w;
-}
-
-} // namespace floormat
+#define FM_SERIALIZE_WORLD_IMPL +#include "world-impl.hpp" +#include "binary-reader.inl" +#include "src/world.hpp" +#include "src/loader.hpp" +#include "src/tile-atlas.hpp" + +namespace floormat::Serialize { + +namespace { + +struct reader_state final { + explicit reader_state(world& world) noexcept; + void deserialize_world(ArrayView<const char> buf); + +private: + using reader_t = binary_reader<decltype(ArrayView<const char>{}.cbegin())>; + + std::shared_ptr<tile_atlas> lookup_atlas(atlasid id); + void read_atlases(reader_t& reader); + void read_chunks(reader_t& reader); + + std::unordered_map<atlasid, std::shared_ptr<tile_atlas>> atlases; + struct world* world; +}; + +reader_state::reader_state(struct world& world) noexcept : world{&world} {} + +void reader_state::read_atlases(reader_t& s) +{ + const auto N = s.read<atlasid>(); + atlases.reserve(N * 2); + for (atlasid i = 0; i < N; i++) + { + Vector2ub size; + s >> size[0]; + s >> size[1]; + const auto& [buf, len] = s.read_asciiz_string<atlas_name_max>(); + atlases[i] = loader.tile_atlas({buf, len}, size); + } +} + +std::shared_ptr<tile_atlas> reader_state::lookup_atlas(atlasid id) +{ + if (auto it = atlases.find(id); it != atlases.end()) + return it->second; + else + fm_abort("not such atlas: '%zu'", (std::size_t)id); +} + +void reader_state::read_chunks(reader_t& s) +{ + const auto N = s.read<chunksiz>(); + + for (std::size_t i = 0; i < N; i++) + { + std::decay_t<decltype(chunk_magic)> magic; + s >> magic; + if (magic != chunk_magic) + fm_abort("bad chunk magic"); + chunk_coords coord; + s >> coord.x; + s >> coord.y; + auto& chunk = (*world)[coord]; + for (std::size_t i = 0; i < TILE_COUNT; i++) + { + const tilemeta flags = s.read<tilemeta>(); + tile& t = chunk[i]; + using uchar = std::uint8_t; + const auto make_atlas = [&]() -> tile_image { + auto id = flags & meta_short_atlasid ? (atlasid)(s.read<uchar>()) : s.read<atlasid>(); + auto v = flags & meta_short_variant ? (varid) (s.read<uchar>()) : s.read<varid>(); + auto atlas = lookup_atlas(id); + fm_assert(v < atlas->num_tiles()); + return { atlas, v }; + }; + + if (flags & meta_ground) + t.ground = make_atlas(); + if (flags & meta_wall_n) + t.wall_north = make_atlas(); + if (flags & meta_wall_w) + t.wall_west = make_atlas(); + + switch (auto x = flags & pass_mask) + { + case tile::pass_shoot_through: + case tile::pass_blocked: + case tile::pass_ok: + t.passability = (tile::pass_mode)x; + break; + default: + fm_abort("bad pass mode '%zu' for tile %zu", i, (std::size_t)x); + } + } + } +} + +void reader_state::deserialize_world(ArrayView<const char> buf) +{ + auto s = binary_reader{buf}; + if (!!::memcmp(s.read<std::size(file_magic)-1>().data(), file_magic, std::size(file_magic)-1)) + fm_abort("bad magic"); + std::decay_t<decltype(proto_version)> proto; + s >> proto; + if (proto != proto_version) + fm_abort("bad proto version '%zu' (should be '%zu')", + (std::size_t)proto, (std::size_t)proto_version); + read_atlases(s); + read_chunks(s); + s.assert_end(); +} + +} // namespace + +} // namespace floormat::Serialize + +namespace floormat { + +world world::deserialize(StringView filename) +{ + char errbuf[128]; + constexpr auto strerror = []<std::size_t N> (char (&buf)[N]) -> const char* { + ::strerror_s(buf, std::size(buf), errno); + return buf; + }; + fm_assert(filename.flags() & StringViewFlag::NullTerminated); + FILE_raii f = ::fopen(filename.data(), "rb"); + if (!f) + fm_abort("fopen(\"%s\", \"r\"): %s", filename.data(), strerror(errbuf)); + if (int ret = ::fseek(f, 0, SEEK_END); ret != 0) + fm_abort("fseek(SEEK_END): %s", strerror(errbuf)); + std::size_t len; + if (auto len_ = ::ftell(f); len_ >= 0) + len = (std::size_t)len_; + else + fm_abort("ftell: %s", strerror(errbuf)); + if (int ret = ::fseek(f, 0, SEEK_SET); ret != 0) + fm_abort("fseek(SEEK_SET): %s", strerror(errbuf)); + auto buf_ = std::make_unique<char[]>(len); + + if (auto ret = ::fread(&buf_[0], 1, len, f); ret != len) + fm_abort("fread short read: %s", strerror(errbuf)); + + world w; + Serialize::reader_state s{w}; + s.deserialize_world({buf_.get(), len}); + return w; +} + +} // namespace floormat diff --git a/serialize/world-writer.cpp b/serialize/world-writer.cpp index 84d894bd..a55e6eb1 100644 --- a/serialize/world-writer.cpp +++ b/serialize/world-writer.cpp @@ -1,278 +1,278 @@ -#define FM_SERIALIZE_WORLD_IMPL
-#include "world-impl.hpp"
-
-#include "src/tile-atlas.hpp"
-#include "binary-writer.inl"
-#include "src/global-coords.hpp"
-#include "src/chunk.hpp"
-#include "src/world.hpp"
-#include <vector>
-#include <algorithm>
-#include <Corrade/Containers/StringView.h>
-#include <Corrade/Utility/Path.h>
-
-namespace Path = Corrade::Utility::Path;
-
-namespace floormat::Serialize {
-
-namespace {
-
-struct interned_atlas final {
- const tile_atlas* img;
- atlasid index;
-};
-
-struct writer_state final {
- writer_state(const world& world);
- ArrayView<const char> serialize_world();
- fm_DECLARE_DEFAULT_MOVE_ASSIGNMENT_(writer_state);
- fm_DECLARE_DEPRECATED_COPY_ASSIGNMENT(writer_state);
-
-private:
- atlasid intern_atlas(const tile_image& img);
- atlasid maybe_intern_atlas(const tile_image& img);
- void serialize_chunk(const chunk& c, chunk_coords coord);
- void serialize_atlases();
-
- const struct world* world;
- std::vector<char> atlas_buf, chunk_buf, file_buf;
- std::vector<std::vector<char>> chunk_bufs;
- std::unordered_map<const void*, interned_atlas> tile_images;
-};
-
-constexpr auto tile_size = sizeof(tilemeta) + (sizeof(atlasid) + sizeof(varid)) * 3;
-
-constexpr auto chunkbuf_size =
- sizeof(chunk_magic) + sizeof(chunk_coords) + tile_size * TILE_COUNT;
-
-#ifdef __GNUG__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4996)
-#endif
-
-writer_state::writer_state(const struct world& world) : world{&world}
-{
- chunk_buf.reserve(chunkbuf_size);
- chunk_bufs.reserve(world.chunks().size());
- atlas_buf.reserve(atlas_name_max * 64);
-}
-
-#ifdef __GNUG__
-#pragma GCC diagnostic pop
-#elif defined _MSC_VER
-#pragma warning(pop)
-#endif
-
-atlasid writer_state::intern_atlas(const tile_image& img)
-{
- const void* const ptr = img.atlas.get();
- fm_debug_assert(ptr != nullptr);
- if (auto it = tile_images.find(ptr); it != tile_images.end())
- return it->second.index;
- else
- return (tile_images[ptr] = { &*img.atlas, (atlasid)tile_images.size() }).index;
-}
-
-atlasid writer_state::maybe_intern_atlas(const tile_image& img)
-{
- return img ? intern_atlas(img) : null_atlas;
-}
-
-void writer_state::serialize_chunk(const chunk& c, chunk_coords coord)
-{
- fm_assert(chunk_buf.empty());
- chunk_buf.resize(chunkbuf_size);
-
- auto s = binary_writer{chunk_buf.begin()};
-
- s << chunk_magic << coord.x << coord.y;
-
- for (std::size_t i = 0; i < TILE_COUNT; i++)
- {
- const tile& x = c[i];
-
- fm_debug_assert(s.bytes_written() + tile_size <= chunkbuf_size);
-
- auto img_g = maybe_intern_atlas(x.ground);
- auto img_n = maybe_intern_atlas(x.wall_north);
- auto img_w = maybe_intern_atlas(x.wall_west);
-
- tilemeta flags = {};
- flags |= meta_ground * (img_g != null_atlas);
- flags |= meta_wall_n * (img_n != null_atlas);
- flags |= meta_wall_w * (img_w != null_atlas);
-
- using uchar = std::uint8_t;
-
- constexpr auto ashortp = [](atlasid id) {
- return id == null_atlas || id == (uchar)id;
- };
- constexpr auto vshortp = [](const tile_image& img) {
- return !img.atlas || img.variant == (uchar)img.variant;
- };
-
- if (flags != 0 && ashortp(img_g) && ashortp(img_n) && ashortp(img_w))
- flags |= meta_short_atlasid;
- if (flags != 0 && vshortp(x.ground) && vshortp(x.wall_north) && vshortp(x.wall_west))
- flags |= meta_short_variant;
-
- fm_debug_assert((x.passability & pass_mask) == x.passability);
- flags |= x.passability;
-
- s << flags;
-
-#ifndef FM_NO_DEBUG
- constexpr auto check_atlas = [](const tile_image& x) {
- if (x.atlas)
- fm_assert(x.variant < x.atlas->num_tiles());
- };
- check_atlas(x.ground);
- check_atlas(x.wall_north);
- check_atlas(x.wall_west);
-#endif
-
- const auto write = [&](atlasid x, varid v) {
- flags & meta_short_atlasid ? s << (uchar) x : s << x;
- flags & meta_short_variant ? s << (uchar) v : s << v;
- };
-
- if (img_g != null_atlas)
- write(img_g, x.ground.variant);
- if (img_n != null_atlas)
- write(img_n, x.wall_north.variant);
- if (img_w != null_atlas)
- write(img_w, x.wall_west.variant);
- }
-
- const auto nbytes = s.bytes_written();
- fm_assert(nbytes <= chunkbuf_size);
-
- chunk_buf.resize(nbytes);
- chunk_bufs.push_back(chunk_buf);
- chunk_buf.clear();
-}
-
-void writer_state::serialize_atlases()
-{
- fm_assert(tile_images.size() < int_max<atlasid>);
- const auto sz = (atlasid)tile_images.size();
- const auto atlasbuf_size = sizeof(sz) + atlas_name_max*sz;
- atlas_buf.resize(atlasbuf_size);
- auto s = binary_writer{atlas_buf.begin()};
- fm_assert(sz <= int_max<atlasid>);
-
- s << sz;
-
- std::vector<interned_atlas> atlases;
- atlases.reserve(tile_images.size());
-
- for (const auto& [_, t] : tile_images)
- atlases.push_back(t);
- std::sort(atlases.begin(), atlases.end(), [](const auto& a, const auto& b) {
- return a.index < b.index;
- });
-
- for (const auto& [atlas, _] : atlases)
- {
- const auto name = atlas->name();
- const auto namesiz = name.size();
- fm_debug_assert(s.bytes_written() + namesiz + 1 <= atlasbuf_size);
- fm_assert(namesiz <= atlas_name_max - 1); // null terminated
- fm_debug_assert(name.find('\0') == name.cend());
- const auto sz2 = atlas->num_tiles2();
- s << sz2[0]; s << sz2[1];
- s.write_asciiz_string(name);
- }
- atlas_buf.resize(s.bytes_written());
- fm_assert(s.bytes_written() <= atlasbuf_size);
-}
-
-#ifdef __GNUG__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#elif defined _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4996)
-#endif
-
-ArrayView<const char> writer_state::serialize_world()
-{
- for (const auto& [pos, c] : world->chunks())
- {
-#ifndef FM_NO_DEBUG
- if (c.empty(true))
- fm_warn("chunk %hd:%hd is empty", pos.x, pos.y);
-#endif
- serialize_chunk(c, pos);
- }
- serialize_atlases();
-
- using proto_t = std::decay_t<decltype(proto_version)>;
- union { chunksiz x; char bytes[sizeof x]; } c = {.x = maybe_byteswap((chunksiz)world->size())};
- union { proto_t x; char bytes[sizeof x]; } p = {.x = maybe_byteswap(proto_version)};
- fm_assert(world->size() <= int_max<chunksiz>);
-
- std::size_t len = 0;
- len += std::size(file_magic)-1;
- len += sizeof(p.x);
- len += sizeof(c.x);
- for (const auto& buf : chunk_bufs)
- len += buf.size();
- len += atlas_buf.size();
- file_buf.resize(len);
- auto it = file_buf.begin();
- const auto copy = [&](const auto& in) {
-#ifndef FM_NO_DEBUG
- auto len1 = std::distance(std::cbegin(in), std::cend(in)),
- len2 = std::distance(it, file_buf.end());
- fm_assert(len1 <= len2);
-#endif
- it = std::copy(std::cbegin(in), std::cend(in), it);
- };
- copy(Containers::StringView{file_magic, std::size(file_magic)-1});
- copy(p.bytes);
- copy(atlas_buf);
- copy(c.bytes);
- for (const auto& buf : chunk_bufs)
- copy(buf);
- return {file_buf.data(), file_buf.size()};
-}
-
-#ifdef __GNUG__
-#pragma GCC diagnostic pop
-#elif defined _MSC_VER
-#pragma warning(pop)
-#endif
-
-} // namespace
-
-} // namespace floormat::Serialize
-
-namespace floormat {
-
-void world::serialize(StringView filename)
-{
- collect(true);
- char errbuf[128];
- constexpr auto strerror = []<std::size_t N> (char (&buf)[N]) -> const char* {
- ::strerror_s(buf, std::size(buf), errno);
- return buf;
- };
- fm_assert(filename.flags() & StringViewFlag::NullTerminated);
- if (Path::exists(filename))
- Path::remove(filename);
- FILE_raii file = ::fopen(filename.data(), "wb");
- if (!file)
- fm_abort("fopen(\"%s\", \"w\"): %s", filename.data(), strerror(errbuf));
- Serialize::writer_state s{*this};
- const auto array = s.serialize_world();
- if (auto len = ::fwrite(array.data(), array.size(), 1, file); len != 1)
- fm_abort("fwrite: %s", strerror(errbuf));
- if (int ret = ::fflush(file); ret != 0)
- fm_abort("fflush: %s", strerror(errbuf));
-}
-
-} // namespace floormat
+#define FM_SERIALIZE_WORLD_IMPL +#include "world-impl.hpp" + +#include "src/tile-atlas.hpp" +#include "binary-writer.inl" +#include "src/global-coords.hpp" +#include "src/chunk.hpp" +#include "src/world.hpp" +#include <vector> +#include <algorithm> +#include <Corrade/Containers/StringView.h> +#include <Corrade/Utility/Path.h> + +namespace Path = Corrade::Utility::Path; + +namespace floormat::Serialize { + +namespace { + +struct interned_atlas final { + const tile_atlas* img; + atlasid index; +}; + +struct writer_state final { + writer_state(const world& world); + ArrayView<const char> serialize_world(); + fm_DECLARE_DEFAULT_MOVE_ASSIGNMENT_(writer_state); + fm_DECLARE_DEPRECATED_COPY_ASSIGNMENT(writer_state); + +private: + atlasid intern_atlas(const tile_image& img); + atlasid maybe_intern_atlas(const tile_image& img); + void serialize_chunk(const chunk& c, chunk_coords coord); + void serialize_atlases(); + + const struct world* world; + std::vector<char> atlas_buf, chunk_buf, file_buf; + std::vector<std::vector<char>> chunk_bufs; + std::unordered_map<const void*, interned_atlas> tile_images; +}; + +constexpr auto tile_size = sizeof(tilemeta) + (sizeof(atlasid) + sizeof(varid)) * 3; + +constexpr auto chunkbuf_size = + sizeof(chunk_magic) + sizeof(chunk_coords) + tile_size * TILE_COUNT; + +#ifdef __GNUG__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +writer_state::writer_state(const struct world& world) : world{&world} +{ + chunk_buf.reserve(chunkbuf_size); + chunk_bufs.reserve(world.chunks().size()); + atlas_buf.reserve(atlas_name_max * 64); +} + +#ifdef __GNUG__ +#pragma GCC diagnostic pop +#elif defined _MSC_VER +#pragma warning(pop) +#endif + +atlasid writer_state::intern_atlas(const tile_image& img) +{ + const void* const ptr = img.atlas.get(); + fm_debug_assert(ptr != nullptr); + if (auto it = tile_images.find(ptr); it != tile_images.end()) + return it->second.index; + else + return (tile_images[ptr] = { &*img.atlas, (atlasid)tile_images.size() }).index; +} + +atlasid writer_state::maybe_intern_atlas(const tile_image& img) +{ + return img ? intern_atlas(img) : null_atlas; +} + +void writer_state::serialize_chunk(const chunk& c, chunk_coords coord) +{ + fm_assert(chunk_buf.empty()); + chunk_buf.resize(chunkbuf_size); + + auto s = binary_writer{chunk_buf.begin()}; + + s << chunk_magic << coord.x << coord.y; + + for (std::size_t i = 0; i < TILE_COUNT; i++) + { + const tile& x = c[i]; + + fm_debug_assert(s.bytes_written() + tile_size <= chunkbuf_size); + + auto img_g = maybe_intern_atlas(x.ground); + auto img_n = maybe_intern_atlas(x.wall_north); + auto img_w = maybe_intern_atlas(x.wall_west); + + tilemeta flags = {}; + flags |= meta_ground * (img_g != null_atlas); + flags |= meta_wall_n * (img_n != null_atlas); + flags |= meta_wall_w * (img_w != null_atlas); + + using uchar = std::uint8_t; + + constexpr auto ashortp = [](atlasid id) { + return id == null_atlas || id == (uchar)id; + }; + constexpr auto vshortp = [](const tile_image& img) { + return !img.atlas || img.variant == (uchar)img.variant; + }; + + if (flags != 0 && ashortp(img_g) && ashortp(img_n) && ashortp(img_w)) + flags |= meta_short_atlasid; + if (flags != 0 && vshortp(x.ground) && vshortp(x.wall_north) && vshortp(x.wall_west)) + flags |= meta_short_variant; + + fm_debug_assert((x.passability & pass_mask) == x.passability); + flags |= x.passability; + + s << flags; + +#ifndef FM_NO_DEBUG + constexpr auto check_atlas = [](const tile_image& x) { + if (x.atlas) + fm_assert(x.variant < x.atlas->num_tiles()); + }; + check_atlas(x.ground); + check_atlas(x.wall_north); + check_atlas(x.wall_west); +#endif + + const auto write = [&](atlasid x, varid v) { + flags & meta_short_atlasid ? s << (uchar) x : s << x; + flags & meta_short_variant ? s << (uchar) v : s << v; + }; + + if (img_g != null_atlas) + write(img_g, x.ground.variant); + if (img_n != null_atlas) + write(img_n, x.wall_north.variant); + if (img_w != null_atlas) + write(img_w, x.wall_west.variant); + } + + const auto nbytes = s.bytes_written(); + fm_assert(nbytes <= chunkbuf_size); + + chunk_buf.resize(nbytes); + chunk_bufs.push_back(chunk_buf); + chunk_buf.clear(); +} + +void writer_state::serialize_atlases() +{ + fm_assert(tile_images.size() < int_max<atlasid>); + const auto sz = (atlasid)tile_images.size(); + const auto atlasbuf_size = sizeof(sz) + atlas_name_max*sz; + atlas_buf.resize(atlasbuf_size); + auto s = binary_writer{atlas_buf.begin()}; + fm_assert(sz <= int_max<atlasid>); + + s << sz; + + std::vector<interned_atlas> atlases; + atlases.reserve(tile_images.size()); + + for (const auto& [_, t] : tile_images) + atlases.push_back(t); + std::sort(atlases.begin(), atlases.end(), [](const auto& a, const auto& b) { + return a.index < b.index; + }); + + for (const auto& [atlas, _] : atlases) + { + const auto name = atlas->name(); + const auto namesiz = name.size(); + fm_debug_assert(s.bytes_written() + namesiz + 1 <= atlasbuf_size); + fm_assert(namesiz <= atlas_name_max - 1); // null terminated + fm_debug_assert(name.find('\0') == name.cend()); + const auto sz2 = atlas->num_tiles2(); + s << sz2[0]; s << sz2[1]; + s.write_asciiz_string(name); + } + atlas_buf.resize(s.bytes_written()); + fm_assert(s.bytes_written() <= atlasbuf_size); +} + +#ifdef __GNUG__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +ArrayView<const char> writer_state::serialize_world() +{ + for (const auto& [pos, c] : world->chunks()) + { +#ifndef FM_NO_DEBUG + if (c.empty(true)) + fm_warn("chunk %hd:%hd is empty", pos.x, pos.y); +#endif + serialize_chunk(c, pos); + } + serialize_atlases(); + + using proto_t = std::decay_t<decltype(proto_version)>; + union { chunksiz x; char bytes[sizeof x]; } c = {.x = maybe_byteswap((chunksiz)world->size())}; + union { proto_t x; char bytes[sizeof x]; } p = {.x = maybe_byteswap(proto_version)}; + fm_assert(world->size() <= int_max<chunksiz>); + + std::size_t len = 0; + len += std::size(file_magic)-1; + len += sizeof(p.x); + len += sizeof(c.x); + for (const auto& buf : chunk_bufs) + len += buf.size(); + len += atlas_buf.size(); + file_buf.resize(len); + auto it = file_buf.begin(); + const auto copy = [&](const auto& in) { +#ifndef FM_NO_DEBUG + auto len1 = std::distance(std::cbegin(in), std::cend(in)), + len2 = std::distance(it, file_buf.end()); + fm_assert(len1 <= len2); +#endif + it = std::copy(std::cbegin(in), std::cend(in), it); + }; + copy(Containers::StringView{file_magic, std::size(file_magic)-1}); + copy(p.bytes); + copy(atlas_buf); + copy(c.bytes); + for (const auto& buf : chunk_bufs) + copy(buf); + return {file_buf.data(), file_buf.size()}; +} + +#ifdef __GNUG__ +#pragma GCC diagnostic pop +#elif defined _MSC_VER +#pragma warning(pop) +#endif + +} // namespace + +} // namespace floormat::Serialize + +namespace floormat { + +void world::serialize(StringView filename) +{ + collect(true); + char errbuf[128]; + constexpr auto strerror = []<std::size_t N> (char (&buf)[N]) -> const char* { + ::strerror_s(buf, std::size(buf), errno); + return buf; + }; + fm_assert(filename.flags() & StringViewFlag::NullTerminated); + if (Path::exists(filename)) + Path::remove(filename); + FILE_raii file = ::fopen(filename.data(), "wb"); + if (!file) + fm_abort("fopen(\"%s\", \"w\"): %s", filename.data(), strerror(errbuf)); + Serialize::writer_state s{*this}; + const auto array = s.serialize_world(); + if (auto len = ::fwrite(array.data(), array.size(), 1, file); len != 1) + fm_abort("fwrite: %s", strerror(errbuf)); + if (int ret = ::fflush(file); ret != 0) + fm_abort("fflush: %s", strerror(errbuf)); +} + +} // namespace floormat |