summaryrefslogtreecommitdiffhomepage
path: root/serialize/world-impl.hpp
blob: b44ec6b412b0523b49b9ba78b7d449630be1dc7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#ifndef FM_SERIALIZE_WORLD_IMPL
#error "not meant to be included directly"
#endif

#pragma once
#include "src/tile.hpp"
#include "src/pass-mode.hpp"
#include "src/rotation.hpp"
#include "src/entity-type.hpp"
#include <bit>
#include <cstdio>
#include <limits>

/* protocol changelog:
 *  1) Initial version.
 *  2) Tile atlas variant now always a uint8_t. Was uint16_t or uint8_t
 *     depending on value of the tile flag (1 << 6) which is now removed.
 *  3) Serialize scenery. Tile flag (1 << 6) added.
 *  4) Scenery dt now stored as fixed-point uint16_t.
 *  5) Serialize scenery pixel offset.
 *  6) Serialize scenery bboxes.
 *  7) Serialize scenery bbox_size offset.
 *  8) Entity subtypes.
 *  9) Interned strings.
 */

namespace floormat {
struct entity;
struct entity_proto;
} // namespace floormat

namespace floormat::Serialize {

using tilemeta = uint8_t;
using atlasid  = uint16_t;
using chunksiz = uint16_t;
using proto_t  = uint16_t;

namespace {

template<typename T> constexpr inline T int_max = std::numeric_limits<T>::max();

#define file_magic ".floormat.save"

constexpr inline size_t atlas_name_max = 128;
constexpr inline auto null_atlas = (atlasid)-1LL;

constexpr inline size_t character_name_max = 128;
constexpr inline size_t string_max = 512;

constexpr inline proto_t proto_version = 9;
constexpr inline proto_t min_proto_version = 1;
constexpr inline auto chunk_magic = (uint16_t)~0xc0d3;
constexpr inline auto scenery_magic = (uint16_t)~0xb00b;

using pass_mode_i = std::underlying_type_t<pass_mode>;
constexpr inline pass_mode_i pass_mask = pass_mode_COUNT - 1;
constexpr inline auto pass_bits = std::bit_width(pass_mask);
using entity_type_i = std::underlying_type_t<entity_type>;

template<typename T> constexpr inline auto highbit = T(1) << sizeof(T)*8-1;
template<typename T, size_t N, size_t off>
constexpr inline auto highbits = (T(1) << N)-1 << sizeof(T)*8-N-off;

constexpr inline atlasid meta_short_scenery_bit = highbit<atlasid>;
constexpr inline atlasid meta_rotation_bits = highbits<atlasid, rotation_BITS, 1>;
constexpr inline atlasid scenery_id_flag_mask = meta_short_scenery_bit | meta_rotation_bits;
constexpr inline atlasid scenery_id_max = int_max<atlasid> & ~scenery_id_flag_mask;

} // namespace

template<typename T> concept entity_subtype = std::is_base_of_v<entity, T> || std::is_base_of_v<entity_proto, T>;

enum : tilemeta {
    meta_ground         = 1 << 2,
    meta_wall_n         = 1 << 3,
    meta_wall_w         = 1 << 4,
    meta_short_atlasid_ = 1 << 5,
    meta_short_variant_ = 1 << 6,
    meta_scenery_       = 1 << 7,
};

} // 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