#pragma once #include #include "asset.h" class BinaryStream { public: enum class ByteOrder : uint8_t { BigEndian, LittleEndian, Host }; static ByteOrder sys_order() { union { uint32_t i; char c[4]; } bint{ 0x01020304 }; return bint.c[0] == 1 ? ByteOrder::BigEndian : ByteOrder::LittleEndian; } template T swap(T x) { #if _MSC_VER >= 1400 if (sizeof(T) == 2) { auto y = _byteswap_ushort(*reinterpret_cast(&x)); return *reinterpret_cast(&y); } else if (sizeof(T) == 4) { auto y = _byteswap_ulong(*reinterpret_cast(&x)); return *reinterpret_cast(&y); } else if (sizeof(T) == 8) { auto y = _byteswap_uint64(*reinterpret_cast(&x)); return *reinterpret_cast(&y); } #else auto p = reinterpret_cast(&x); if (sizeof(T) == 2) { std::swap(p[0], p[1]); } else if (sizeof(T) == 4) { std::swap(p[0], p[3]); std::swap(p[1], p[2]); } else if (sizeof(T) == 8) { std::swap(p[0], p[7]); std::swap(p[1], p[6]); std::swap(p[2], p[5]); std::swap(p[3], p[4]); } else { static_assert(true, "Should not reach here"); } return x; #endif } protected: bool m_swap = false; ByteOrder m_byte_order = ByteOrder::Host; }; class BinaryStreamReader : public BinaryStream { public: BinaryStreamReader(const BinaryStreamReader&) = delete; BinaryStreamReader() = default; ~BinaryStreamReader(); void init(uint8_t* data_ptr, size_t size, ByteOrder byte_order = ByteOrder::Host); bool load(const std::string& path, ByteOrder byte_order = ByteOrder::Host); size_t pos(); void skip(size_t bytes); // snap to the next 4-alignment void snap(); bool eof(); bool has_data(size_t sz); uint8_t ru8(); uint16_t ru16(); uint32_t ru32(); uint64_t ru64(); int8_t ri8(); int16_t ri16(); int32_t ri32(); int64_t ri64(); float rflt(); double rdbl(); std::string pick(size_t chars); std::string rstring(); std::string rstring(size_t len); std::wstring rwstring(); std::wstring rwstring(size_t len); std::string rpascal(); std::vector rraw(); std::vector rraw(size_t bytes); std::vector rrle(size_t encoded_bytes); std::string rkey_or_string(); protected: template inline T align4(T x) { return ((x - T{ 1 }) & (~(T{ 3 }))) + T{ 4 }; } template T read() { T ret{}; if (m_ptr) { if (has_data(sizeof(T))) ret = *reinterpret_cast(m_cur); m_cur += sizeof(T); } return ret; } template T* advance(size_t bytes) { T* ret = nullptr; if (m_ptr) { if (has_data(bytes)) ret = reinterpret_cast(m_cur); m_cur += bytes; } return ret; } private: uint8_t *m_ptr = nullptr; uint8_t *m_cur = nullptr; size_t m_size = 0; Asset m_asset; }; class BinaryStreamWriter : public BinaryStream { public: std::vector m_data; BinaryStreamWriter(const BinaryStreamWriter&) = delete; BinaryStreamWriter() = default; ~BinaryStreamWriter() = default; void init(ByteOrder byte_order = ByteOrder::Host); bool save(const std::string& path) const; void skip(size_t bytes, uint8_t fill = 0); // snap to the next 4-alignment void snap(); void wu8 (uint8_t v); void wu16(uint16_t v); void wu32(uint32_t v); void wu64(uint64_t v); void wi8 (int8_t v); void wi16(int16_t v); void wi32(int32_t v); void wi64(int64_t v); void wflt(float v); void wdbl(double v); void wstring(std::string s); void wstring_raw(std::string s); void wwstring(std::wstring s); void wwstring_raw(std::wstring s); void wpascal(std::string s); void wraw(std::vector raw); void wrle(std::vector data); void wkey_or_string(std::string s); protected: template void write(T x) { uint8_t* bytes = reinterpret_cast(&x); m_data.insert(m_data.end(), bytes, bytes + sizeof(T)); } template void write(const T* ptr, size_t size) { const uint8_t* bytes = reinterpret_cast(ptr); m_data.insert(m_data.end(), bytes, bytes + size * sizeof(T)); } };