1#pragma once
2
3#include <ore/StringView.h>
4#include <ore/Types.h>
5#include <type_traits>
6#include <utility>
7
8namespace ore {
9
10template <typename T>
11constexpr T AlignUpToPowerOf2(T val, int base) {
12 return val + base - 1 & static_cast<unsigned int>(-base);
13}
14
15struct RelocationTable;
16
17struct BinaryBlockHeader {
18 BinaryBlockHeader* FindNextBlock(int type);
19 const BinaryBlockHeader* FindNextBlock(int type) const;
20 BinaryBlockHeader* GetNextBlock();
21 const BinaryBlockHeader* GetNextBlock() const;
22 void SetNextBlock(BinaryBlockHeader* block);
23
24 u32 magic;
25 int next_block_offset;
26};
27
28struct BinaryFileHeader {
29 bool IsValid(s64 magic_, int ver_major_, int ver_minor_, int ver_patch_, int ver_sub_) const;
30 bool IsSignatureValid(s64 magic_) const;
31 bool IsVersionValid(int major, int minor, int patch, int sub) const;
32 bool IsEndianReverse() const;
33 bool IsEndianValid() const;
34
35 bool IsAlignmentValid() const;
36 int GetAlignment() const;
37 void SetAlignment(int alignment_);
38
39 bool IsRelocated() const;
40 void SetRelocated(bool relocated);
41
42 void SetByteOrderMark();
43
44 int GetFileSize() const;
45 void SetFileSize(int size);
46
47 StringView GetFileName() const;
48 void SetFileName(const StringView& name);
49
50 RelocationTable* GetRelocationTable();
51 void SetRelocationTable(RelocationTable* table);
52
53 BinaryBlockHeader* GetFirstBlock();
54 const BinaryBlockHeader* GetFirstBlock() const;
55 void SetFirstBlock(BinaryBlockHeader* block);
56
57 BinaryBlockHeader* FindFirstBlock(int type);
58 const BinaryBlockHeader* FindFirstBlock(int type) const;
59
60 u64 magic;
61 u8 ver_major;
62 u8 ver_minor;
63 u8 ver_patch;
64 u8 ver_sub;
65 s16 bom;
66 u8 alignment;
67 u8 _f;
68 int file_name_offset;
69 u16 relocation_flags;
70 u16 first_block_offset;
71 int relocation_table_offset;
72 int file_size;
73};
74
75template <typename T>
76struct BinTString {
77 // Make it impossible to accidentally construct a (partial, broken) copy.
78 BinTString(const BinTString&) = delete;
79 auto operator=(const BinTString&) = delete;
80
81 T* data() { return chars; }
82 const T* data() const { return chars; }
83
84 T& operator[](size_t idx) { return data()[idx]; }
85 const T& operator[](size_t idx) const { return data()[idx]; }
86
87 auto begin() { return data(); }
88 auto begin() const { return data(); }
89
90 auto end() { return data() + length; }
91 auto end() const { return data() + length; }
92
93 bool empty() const { return length == 0; }
94
95 // NOLINTNEXTLINE(google-explicit-constructor)
96 operator TStringView<T>() const { return {data(), length}; }
97
98 BinTString* NextString() { return const_cast<BinTString*>(std::as_const(*this).NextString()); }
99
100 const BinTString* NextString() const {
101 // XXX: this shouldn't have to be a separate case.
102 if constexpr (std::is_same_v<T, wchar_t>) {
103 const auto offset = ((2 + (4 * (length + 1) - 1)) & -4) + 2;
104 return reinterpret_cast<const BinTString*>(reinterpret_cast<const char*>(this) +
105 offset);
106
107 } else {
108 // + 1 for the null terminator
109 const auto offset = offsetof(BinTString, chars) + sizeof(T) * (length + 1);
110 return reinterpret_cast<const BinTString*>(
111 reinterpret_cast<const char*>(this) +
112 AlignUpToPowerOf2(offset, alignof(BinTString)));
113 }
114 }
115
116 u16 length;
117 T chars[1];
118};
119
120using BinString = BinTString<char>;
121using BinWString = BinTString<wchar_t>;
122
123template <typename T>
124struct BinTPtr {
125 void Clear() { offset_or_ptr = 0; }
126 void Set(T* ptr) { offset_or_ptr = reinterpret_cast<u64>(ptr); }
127
128 // Only use this after relocation.
129 T* Get() { return reinterpret_cast<T*>(offset_or_ptr); }
130 const T* Get() const { return reinterpret_cast<const T*>(offset_or_ptr); }
131
132 void SetOffset(void* base, void* ptr) {
133 offset_or_ptr = static_cast<int>(ptr ? uintptr_t(ptr) - uintptr_t(base) : 0);
134 }
135
136 u64 GetOffset() const { return offset_or_ptr; }
137
138 T* ToPtr(void* base) const {
139 const auto offset = static_cast<int>(offset_or_ptr);
140 if (offset == 0)
141 return nullptr;
142 return reinterpret_cast<T*>(reinterpret_cast<char*>(base) + offset);
143 }
144
145 void Relocate(void* base) { Set(ToPtr(base)); }
146 void Unrelocate(void* base) { SetOffset(base, Get()); }
147
148 u64 offset_or_ptr;
149};
150
151static_assert(sizeof(u64) >= sizeof(void*));
152
153} // namespace ore
154