File size: 3,953 Bytes
3ce1a40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#pragma once
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <stdexcept>
#include "../core/math.h"

namespace unify {

/// Little-endian binary writer. Floats are written as their IEEE-754 bit patterns,
/// so a round trip is bit-exact (required for deterministic save states).
class BinaryWriter {
public:
    void u8(uint8_t v) { buf_.push_back(v); }
    void u16(uint16_t v) { for (int i = 0; i < 2; i++) buf_.push_back(uint8_t(v >> (8 * i))); }
    void u32(uint32_t v) { for (int i = 0; i < 4; i++) buf_.push_back(uint8_t(v >> (8 * i))); }
    void u64(uint64_t v) { for (int i = 0; i < 8; i++) buf_.push_back(uint8_t(v >> (8 * i))); }
    void i32(int32_t v) { u32(uint32_t(v)); }
    void f32(float v) { uint32_t b; std::memcpy(&b, &v, 4); u32(b); }
    void f64(double v) { uint64_t b; std::memcpy(&b, &v, 8); u64(b); }
    void boolean(bool v) { u8(v ? 1 : 0); }
    void vec2(Vec2 v) { f32(v.x); f32(v.y); }
    void str(const std::string& s) { u32(uint32_t(s.size())); bytes(s.data(), s.size()); }
    void bytes(const void* p, size_t n) { auto b = static_cast<const uint8_t*>(p); buf_.insert(buf_.end(), b, b + n); }
    void blob(const std::vector<uint8_t>& v) { u32(uint32_t(v.size())); bytes(v.data(), v.size()); }

    /// Tagged, length-prefixed section. Readers can skip sections they do not understand,
    /// which is what lets the save format evolve without breaking old files.
    size_t begin_section(const char tag[4]) { bytes(tag, 4); size_t at = buf_.size(); u32(0); return at; }
    void end_section(size_t at) {
        uint32_t len = uint32_t(buf_.size() - at - 4);
        for (int i = 0; i < 4; i++) buf_[at + i] = uint8_t(len >> (8 * i));
    }

    const std::vector<uint8_t>& data() const { return buf_; }
    std::vector<uint8_t> take() { return std::move(buf_); }
    size_t size() const { return buf_.size(); }
private:
    std::vector<uint8_t> buf_;
};

struct SerializationError : std::runtime_error { using std::runtime_error::runtime_error; };

class BinaryReader {
public:
    BinaryReader(const uint8_t* data, size_t size) : p_(data), end_(data + size) {}
    explicit BinaryReader(const std::vector<uint8_t>& v) : BinaryReader(v.data(), v.size()) {}
    uint8_t u8() { need(1); return *p_++; }
    uint16_t u16() { need(2); uint16_t v = uint16_t(p_[0] | (p_[1] << 8)); p_ += 2; return v; }
    uint32_t u32() { need(4); uint32_t v = 0; for (int i = 0; i < 4; i++) v |= uint32_t(p_[i]) << (8 * i); p_ += 4; return v; }
    uint64_t u64() { need(8); uint64_t v = 0; for (int i = 0; i < 8; i++) v |= uint64_t(p_[i]) << (8 * i); p_ += 8; return v; }
    int32_t i32() { return int32_t(u32()); }
    float f32() { uint32_t b = u32(); float v; std::memcpy(&v, &b, 4); return v; }
    double f64() { uint64_t b = u64(); double v; std::memcpy(&v, &b, 8); return v; }
    bool boolean() { return u8() != 0; }
    Vec2 vec2() { float x = f32(); return {x, f32()}; }
    std::string str() { uint32_t n = u32(); need(n); std::string s(reinterpret_cast<const char*>(p_), n); p_ += n; return s; }
    void bytes(void* out, size_t n) { need(n); std::memcpy(out, p_, n); p_ += n; }
    std::vector<uint8_t> blob() { uint32_t n = u32(); need(n); std::vector<uint8_t> v(p_, p_ + n); p_ += n; return v; }

    /// Reads the next section header. Returns a reader limited to the section body.
    bool next_section(char tag[5], BinaryReader& body) {
        if (p_ == end_) return false;
        need(8);
        std::memcpy(tag, p_, 4); tag[4] = 0; p_ += 4;
        uint32_t len = u32();
        need(len);
        body = BinaryReader(p_, len);
        p_ += len;
        return true;
    }
    bool at_end() const { return p_ == end_; }
    size_t remaining() const { return size_t(end_ - p_); }
private:
    void need(size_t n) const { if (size_t(end_ - p_) < n) throw SerializationError("unexpected end of data"); }
    const uint8_t* p_;
    const uint8_t* end_;
};

}  // namespace unify