Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : /*
19 : Xeromyces - XMB reading library
20 :
21 : Brief outline:
22 :
23 : XMB originated as a binary representation of XML, with some limitations
24 : but much more efficiency (particularly for loading simple data
25 : classes that don't need much initialisation).
26 :
27 : Theoretical file structure:
28 :
29 : XMB_File {
30 : char Header[4]; // because everyone has one; currently "XMB0"
31 : u32 Version;
32 :
33 : int OffsetFromStartToElementNames;
34 : int ElementNameCount;
35 :
36 : int OffsetFromStartToAttributeNames;
37 : int AttributeNameCount;
38 :
39 : XMB_Node Root;
40 :
41 : ZStr8 ElementNames[];
42 : ZStr8 AttributeNames[];
43 :
44 : }
45 :
46 : XMB_Node {
47 : 0) int Length; // of entire struct, so it can be skipped over
48 :
49 : 4) int ElementName;
50 :
51 : 8) int AttributeCount;
52 : 12) int ChildCount;
53 :
54 : 16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes)
55 : 20) XMB_Text Text;
56 : XMB_Attribute Attributes[];
57 : XMB_Node Children[];
58 :
59 : }
60 :
61 : XMB_Attribute {
62 : int Name;
63 : ZStr8 Value;
64 : }
65 :
66 : ZStr8 {
67 : int Length; // in bytes
68 : char* Text; // null-terminated UTF8
69 : }
70 :
71 : XMB_Text {
72 : 20) int Length; // 0 if there's no text, else 4+sizeof(Text) in bytes including terminator
73 : // If Length != 0:
74 : 24) int LineNumber; // for e.g. debugging scripts
75 : 28) char* Text; // null-terminated UTF8
76 : }
77 :
78 : */
79 :
80 : #ifndef INCLUDED_XEROXMB
81 : #define INCLUDED_XEROXMB
82 :
83 : #include "ps/CStr.h"
84 :
85 : #include <string>
86 : #include <string_view>
87 :
88 : class XMBStorage;
89 :
90 : class XMBElement;
91 : class XMBElementList;
92 : class XMBAttributeList;
93 :
94 :
95 : class XMBData
96 : {
97 : public:
98 :
99 496 : XMBData() : m_Pointer(nullptr) {}
100 :
101 : /*
102 : * Initialise from the contents of an XMBStorage.
103 : * @param doc must remain allocated and unchanged while
104 : * the XMBData is being used.
105 : * @return indication of success; main cause for failure is attempting to
106 : * load a partially valid XMB file (e.g. if the game was interrupted
107 : * while writing it), which we detect by checking the magic string.
108 : * It also fails when trying to load an XMB file with a different version.
109 : */
110 : bool Initialise(const XMBStorage& doc);
111 :
112 : // Returns the root element
113 : XMBElement GetRoot() const;
114 :
115 : // Returns internal ID for a given element/attribute string.
116 : int GetElementID(const char* Name) const;
117 : int GetAttributeID(const char* Name) const;
118 :
119 : // Returns element/attribute string for a given internal ID.
120 : const char* GetElementString(const int ID) const;
121 : const char* GetAttributeString(const int ID) const;
122 :
123 : std::string_view GetElementStringView(const int ID) const;
124 : std::string_view GetAttributeStringView(const int ID) const;
125 :
126 : private:
127 : const char* m_Pointer;
128 :
129 : int m_ElementNameCount;
130 : int m_AttributeNameCount;
131 : const char* m_ElementPointer;
132 : const char* m_AttributePointer;
133 : };
134 :
135 : class XMBElement
136 : {
137 : public:
138 3 : XMBElement()
139 3 : : m_Pointer(0) {}
140 :
141 869 : XMBElement(const char* offset)
142 869 : : m_Pointer(offset) {}
143 :
144 : int GetNodeName() const;
145 : XMBElementList GetChildNodes() const;
146 : XMBAttributeList GetAttributes() const;
147 : CStr8 GetText() const;
148 : // Returns the line number of the text within this element,
149 : // or -1 if there is no text
150 : int GetLineNumber() const;
151 :
152 : private:
153 : // Pointer to the start of the node
154 : const char* m_Pointer;
155 : };
156 :
157 : class XMBElementList
158 : {
159 : public:
160 497 : XMBElementList(const char* offset, size_t count, const char* endoffset)
161 497 : : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
162 :
163 : // Get first element in list with the given name.
164 : // Performance is linear in the number of elements in the list.
165 : XMBElement GetFirstNamedItem(const int ElementName) const;
166 :
167 : // Linear in the number of elements in the list
168 : XMBElement operator[](size_t id); // returns Children[id]
169 :
170 : class iterator
171 : {
172 : public:
173 : typedef ptrdiff_t difference_type;
174 : typedef XMBElement value_type;
175 : typedef XMBElement reference; // Because we need to construct the object
176 : typedef XMBElement pointer; // Because we need to construct the object
177 : typedef std::forward_iterator_tag iterator_category;
178 :
179 944 : iterator(size_t size, const char* ptr, const char* endptr = NULL)
180 944 : : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
181 402 : XMBElement operator*() const { return XMBElement(m_CurPointer); }
182 : XMBElement operator->() const { return **this; }
183 : iterator& operator++();
184 :
185 874 : bool operator==(const iterator& rhs) const
186 : {
187 1748 : return m_Size == rhs.m_Size &&
188 1346 : m_CurItemID == rhs.m_CurItemID &&
189 1346 : m_CurPointer == rhs.m_CurPointer;
190 : }
191 874 : bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
192 : private:
193 : size_t m_Size;
194 : size_t m_CurItemID;
195 : const char* m_CurPointer;
196 : };
197 472 : iterator begin() { return iterator(m_Size, m_Pointer); }
198 472 : iterator end() { return iterator(m_Size, m_Pointer, m_EndPointer); }
199 :
200 12 : size_t size() const { return m_Size; }
201 0 : bool empty() const { return m_Size == 0; }
202 :
203 : private:
204 : size_t m_Size;
205 :
206 : const char* m_Pointer;
207 :
208 : // For optimised sequential access:
209 : size_t m_CurItemID;
210 : const char* m_CurPointer;
211 :
212 : const char* m_EndPointer;
213 : };
214 :
215 :
216 374 : struct XMBAttribute
217 : {
218 : XMBAttribute() {}
219 374 : XMBAttribute(int name, const CStr8& value)
220 374 : : Name(name), Value(value) {};
221 :
222 : int Name;
223 : CStr8 Value; // UTF-8 encoded
224 : };
225 :
226 : class XMBAttributeList
227 : {
228 : public:
229 1747 : XMBAttributeList(const char* offset, size_t count, const char* endoffset)
230 1747 : : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
231 :
232 : // Get the attribute value directly
233 : CStr8 GetNamedItem(const int AttributeName) const;
234 :
235 : // Linear in the number of elements in the list
236 : XMBAttribute operator[](size_t id); // returns Children[id]
237 :
238 : class iterator
239 : {
240 : public:
241 : typedef ptrdiff_t difference_type;
242 : typedef XMBAttribute value_type;
243 : typedef XMBAttribute reference; // Because we need to construct the object
244 : typedef XMBAttribute pointer; // Because we need to construct the object
245 : typedef std::forward_iterator_tag iterator_category;
246 :
247 2786 : iterator(size_t size, const char* ptr, const char* endptr = NULL)
248 2786 : : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
249 : XMBAttribute operator*() const;
250 : XMBAttribute operator->() const { return **this; }
251 : iterator& operator++();
252 :
253 1730 : bool operator==(const iterator& rhs) const
254 : {
255 3460 : return m_Size == rhs.m_Size &&
256 3091 : m_CurItemID == rhs.m_CurItemID &&
257 3091 : m_CurPointer == rhs.m_CurPointer;
258 : }
259 1730 : bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
260 : private:
261 : size_t m_Size;
262 : size_t m_CurItemID;
263 : const char* m_CurPointer;
264 : };
265 1393 : iterator begin() const { return iterator(m_Size, m_Pointer); }
266 1393 : iterator end() const { return iterator(m_Size, m_Pointer, m_EndPointer); }
267 :
268 6 : size_t size() const { return m_Size; }
269 : bool empty() const { return m_Size == 0; }
270 :
271 : private:
272 : size_t m_Size;
273 :
274 : // Pointer to start of attribute list
275 : const char* m_Pointer;
276 :
277 : // For optimised sequential access:
278 : size_t m_CurItemID;
279 : const char* m_CurPointer;
280 :
281 : const char* m_EndPointer;
282 : };
283 :
284 : #endif // INCLUDED_XEROXMB
|