summaryrefslogtreecommitdiffhomepage
path: root/serialize
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-10-28 22:10:27 +0200
committerStanislaw Halik <sthalik@misaki.pl>2022-10-28 22:10:27 +0200
commit280b4c235fe11dd629f882f0fb5054384fcee1d7 (patch)
treee89735003b52b04e3c17a3ad5532b36e055cdda7 /serialize
parent1b84fc144f77c4ebef6fdc0a476410420e0a95b3 (diff)
more work
Diffstat (limited to 'serialize')
-rw-r--r--serialize/binary-reader.hpp6
-rw-r--r--serialize/binary-reader.inl15
-rw-r--r--serialize/binary-writer.hpp1
-rw-r--r--serialize/world-impl.hpp5
-rw-r--r--serialize/world-reader.cpp89
-rw-r--r--serialize/world-writer.cpp21
-rw-r--r--serialize/world.cpp12
7 files changed, 116 insertions, 33 deletions
diff --git a/serialize/binary-reader.hpp b/serialize/binary-reader.hpp
index 3b6eae9a..7eed2145 100644
--- a/serialize/binary-reader.hpp
+++ b/serialize/binary-reader.hpp
@@ -1,11 +1,12 @@
#pragma once
#include "binary-serializer.hpp"
#include <iterator>
+#include <Corrade/Containers/StringView.h>
namespace floormat::Serialize {
-union value_u {
- alignas(alignof(double)) char bytes[8];
+union alignas(alignof(double)) value_u {
+ char bytes[8];
unsigned char uc;
std::uint8_t u8;
std::uint16_t u16;
@@ -44,6 +45,7 @@ struct binary_reader final {
template<typename T> 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; }
+ constexpr StringView read_asciiz_string() noexcept;
private:
std::size_t num_bytes_read = 0;
diff --git a/serialize/binary-reader.inl b/serialize/binary-reader.inl
index cf935a76..ad2d59bc 100644
--- a/serialize/binary-reader.inl
+++ b/serialize/binary-reader.inl
@@ -33,7 +33,7 @@ template<typename T>
T binary_reader<It>::read() noexcept
{
value_u buf = read_u<T>();
- return *reinterpret_cast<T>(buf.bytes);
+ return *reinterpret_cast<T*>(buf.bytes);
}
template<string_input_iterator It>
@@ -79,9 +79,18 @@ constexpr value_u binary_reader<It>::read_u() noexcept
template<string_input_iterator It, serializable T>
binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept
{
- value_u u = reader.template read<T>();
- x = *reinterpret_cast<T*>(&u.bytes[0]);
+ x = reader.template read<T>();
return reader;
}
+template<string_input_iterator It>
+constexpr StringView binary_reader<It>::read_asciiz_string() noexcept
+{
+ const It pos = it;
+ while (it != end)
+ if (char c = *it++; c == '\0')
+ return StringView{pos, (std::size_t)std::distance(pos, end), StringViewFlag::NullTerminated};
+ fm_abort("unexpected EOF while reading a string");
+}
+
} // namespace floormat::Serialize
diff --git a/serialize/binary-writer.hpp b/serialize/binary-writer.hpp
index ead0824d..02fe427d 100644
--- a/serialize/binary-writer.hpp
+++ b/serialize/binary-writer.hpp
@@ -11,7 +11,6 @@ struct binary_writer final {
template<integer T> constexpr void write(T x) noexcept;
template<std::floating_point T> 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:
diff --git a/serialize/world-impl.hpp b/serialize/world-impl.hpp
index b2d74db3..26073edd 100644
--- a/serialize/world-impl.hpp
+++ b/serialize/world-impl.hpp
@@ -12,8 +12,9 @@ namespace floormat::Serialize {
namespace {
using tilemeta = std::uint8_t;
-using imgvar = std::uint8_t;
+using imgvar = 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();
@@ -45,7 +46,7 @@ namespace {
struct FILE_raii final {
FILE_raii(FILE* s) noexcept : s{s} {}
- ~FILE_raii() noexcept { if (s) ::fclose(s); }
+ ~FILE_raii() noexcept { close(); }
operator FILE*() noexcept { return s; }
void close() noexcept { if (s) ::fclose(s); s = nullptr; }
private:
diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp
index 86b07834..299386de 100644
--- a/serialize/world-reader.cpp
+++ b/serialize/world-reader.cpp
@@ -2,6 +2,8 @@
#include "world-impl.hpp"
#include "binary-reader.inl"
#include "src/world.hpp"
+#include "src/loader.hpp"
+#include "src/tile-atlas.hpp"
namespace floormat::Serialize {
@@ -12,9 +14,11 @@ struct reader_state final {
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();
- void read_chunks();
+ 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;
@@ -22,11 +26,84 @@ private:
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];
+ atlases[i] = loader.tile_atlas(s.read_asciiz_string(), 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>();
+ const auto make_atlas = [&] -> tile_image {
+ auto atlas = lookup_atlas(s.read<atlasid>());
+ auto id = s.read<imgvar>();
+ return { atlas, id };
+ };
+ tile t;
+ if (flags & meta_ground)
+ t.ground_image = 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);
+ }
+ chunk[i] = std::move(t);
+ }
+ }
+}
+
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
@@ -47,17 +124,17 @@ world world::deserialize(StringView filename)
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(\"%s\", 0, SEEK_END): %s", filename.data(), strerror(errbuf));
+ 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\"): %s", filename.data(), strerror(errbuf));
+ fm_abort("ftell: %s", strerror(errbuf));
if (int ret = ::fseek(f, 0, SEEK_SET); ret != 0)
- fm_abort("fseek(\"%s\", 0, SEEK_SET): %s", filename.data(), strerror(errbuf));
+ fm_abort("fseek(SEEK_SET): %s", strerror(errbuf));
auto buf_ = std::make_unique<char[]>(len);
if (auto ret = ::fread(&buf_[0], len, 1, f); ret != 1)
- fm_abort("fread(\"%s\", %zu): %s", filename.data(), len, strerror(errbuf));
+ fm_abort("fread %zu: %s", len, strerror(errbuf));
world w;
Serialize::reader_state s{w};
diff --git a/serialize/world-writer.cpp b/serialize/world-writer.cpp
index 80c8de4a..c0f59e9b 100644
--- a/serialize/world-writer.cpp
+++ b/serialize/world-writer.cpp
@@ -106,8 +106,6 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord)
s << flags;
- static_assert(std::is_same_v<imgvar, imgvar>);
-
if (img_g != null_atlas)
s << img_g << x.ground_image.variant;
if (img_n != null_atlas)
@@ -126,8 +124,8 @@ void writer_state::serialize_chunk(const chunk& c, chunk_coords coord)
void writer_state::serialize_atlases()
{
- const std::size_t sz = tile_images.size();
- fm_assert(sz < int_max<atlasid>);
+ 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()};
@@ -143,6 +141,8 @@ void writer_state::serialize_atlases()
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);
}
fm_assert(s.bytes_written() <= atlasbuf_size);
@@ -165,9 +165,15 @@ ArrayView<const char> writer_state::serialize_world()
}
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(proto_version);
+ len += sizeof(p.x);
+ len += sizeof(c.x);
for (const auto& buf : chunk_bufs)
len += buf.size();
len += atlas_buf.size();
@@ -176,14 +182,15 @@ ArrayView<const char> writer_state::serialize_world()
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());
+ 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(std::initializer_list<char>{ char(proto_version & 0xff), char((proto_version >> 8) & 0xff) });
+ copy(p.bytes);
copy(atlas_buf);
+ copy(c.bytes);
for (const auto& buf : chunk_bufs)
copy(buf);
return {file_buf.data(), file_buf.size()};
diff --git a/serialize/world.cpp b/serialize/world.cpp
deleted file mode 100644
index 70d4d0c9..00000000
--- a/serialize/world.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#define FM_SERIALIZE_WORLD_IMPL
-#include "world-impl.hpp"
-#include "binary-reader.inl"
-#include <Corrade/Utility/Path.h>
-
-namespace floormat {
-
-namespace Path = Corrade::Utility::Path;
-
-
-
-} // namespace floormat