Line data Source code
1 : /* Copyright (C) 2020 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 is 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 : Main limitations:
28 : * Can't correctly handle mixed text/elements inside elements -
29 : "<div> <b> Text </b> </div>" and "<div> Te<b/>xt </div>" are
30 : considered identical.
31 : * Tries to avoid using strings - you usually have to load the
32 : numeric IDs and use them instead.
33 :
34 : Theoretical file structure:
35 :
36 : XMB_File {
37 : char Header[4]; // because everyone has one; currently "XMB0"
38 : u32 Version;
39 :
40 : int ElementNameCount;
41 : ZStr8 ElementNames[];
42 :
43 : int AttributeNameCount;
44 : ZStr8 AttributeNames[];
45 :
46 : XMB_Node Root;
47 : }
48 :
49 : XMB_Node {
50 : 0) int Length; // of entire struct, so it can be skipped over
51 :
52 : 4) int ElementName;
53 :
54 : 8) int AttributeCount;
55 : 12) int ChildCount;
56 :
57 : 16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes)
58 : 20) XMB_Text Text;
59 : XMB_Attribute Attributes[];
60 : XMB_Node Children[];
61 :
62 : }
63 :
64 : XMB_Attribute {
65 : int Name;
66 : ZStr8 Value;
67 : }
68 :
69 : ZStr8 {
70 : int Length; // in bytes
71 : char* Text; // null-terminated UTF8
72 : }
73 :
74 : XMB_Text {
75 : 20) int Length; // 0 if there's no text, else 4+sizeof(Text) in bytes including terminator
76 : // If Length != 0:
77 : 24) int LineNumber; // for e.g. debugging scripts
78 : 28) char* Text; // null-terminated UTF8
79 : }
80 :
81 : */
82 :
83 : #ifndef INCLUDED_XEROXMB
84 : #define INCLUDED_XEROXMB
85 :
86 : // Define to use a std::map for name lookups rather than a linear search.
87 : // (The map is usually slower.)
88 : //#define XERO_USEMAP
89 :
90 : #include <string>
91 :
92 : #ifdef XERO_USEMAP
93 : # include <map>
94 : #endif
95 :
96 : #include "ps/CStr.h"
97 :
98 : // File headers, to make sure it doesn't try loading anything other than an XMB
99 : extern const char* HeaderMagicStr;
100 : extern const char* UnfinishedHeaderMagicStr;
101 : extern const u32 XMBVersion;
102 :
103 : class XMBElement;
104 : class XMBElementList;
105 : class XMBAttributeList;
106 :
107 :
108 : class XMBFile
109 : {
110 : public:
111 :
112 1413 : XMBFile() : m_Pointer(NULL) {}
113 :
114 : // Initialise from the contents of an XMB file.
115 : // FileData must remain allocated and unchanged while
116 : // the XMBFile is being used.
117 : // @return indication of success; main cause for failure is attempting to
118 : // load a partially valid XMB file (e.g. if the game was interrupted
119 : // while writing it), which we detect by checking the magic string.
120 : // It also fails when trying to load an XMB file with a different version.
121 : bool Initialise(const char* FileData);
122 :
123 : // Returns the root element
124 : XMBElement GetRoot() const;
125 :
126 :
127 : // Returns internal ID for a given element/attribute string.
128 : int GetElementID(const char* Name) const;
129 : int GetAttributeID(const char* Name) const;
130 :
131 : // For lazy people (e.g. me) when speed isn't vital:
132 :
133 : // Returns element/attribute string for a given internal ID
134 : std::string GetElementString(const int ID) const;
135 : std::string GetAttributeString(const int ID) const;
136 :
137 : private:
138 : const char* m_Pointer;
139 :
140 : #ifdef XERO_USEMAP
141 : std::map<std::string, int> m_ElementNames;
142 : std::map<std::string, int> m_AttributeNames;
143 : #else
144 : int m_ElementNameCount;
145 : int m_AttributeNameCount;
146 : const char* m_ElementPointer;
147 : const char* m_AttributePointer;
148 : #endif
149 :
150 : std::string ReadZStr8();
151 : };
152 :
153 : class XMBElement
154 : {
155 : public:
156 : XMBElement()
157 0 : : m_Pointer(0) {}
158 :
159 : XMBElement(const char* offset)
160 : : m_Pointer(offset) {}
161 :
162 : int GetNodeName() const;
163 : XMBElementList GetChildNodes() const;
164 : XMBAttributeList GetAttributes() const;
165 : CStr8 GetText() const;
166 : // Returns the line number of the text within this element,
167 : // or -1 if there is no text
168 : int GetLineNumber() const;
169 :
170 : private:
171 : // Pointer to the start of the node
172 : const char* m_Pointer;
173 : };
174 :
175 : class XMBElementList
176 : {
177 : public:
178 : XMBElementList(const char* offset, size_t count, const char* endoffset)
179 2356 : : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
180 :
181 : // Get first element in list with the given name.
182 : // Performance is linear in the number of elements in the list.
183 : XMBElement GetFirstNamedItem(const int ElementName) const;
184 :
185 : // Linear in the number of elements in the list
186 : XMBElement operator[](size_t id); // returns Children[id]
187 :
188 : class iterator
189 : {
190 : public:
191 : typedef ptrdiff_t difference_type;
192 : typedef XMBElement value_type;
193 : typedef XMBElement reference; // Because we need to construct the object
194 : typedef XMBElement pointer; // Because we need to construct the object
195 : typedef std::forward_iterator_tag iterator_category;
196 :
197 : iterator(size_t size, const char* ptr, const char* endptr = NULL)
198 2347 : : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
199 3784 : XMBElement operator*() const { return XMBElement(m_CurPointer); }
200 : XMBElement operator->() const { return **this; }
201 : iterator& operator++();
202 :
203 : bool operator==(const iterator& rhs) const
204 : {
205 12182 : return m_Size == rhs.m_Size &&
206 6091 : m_CurItemID == rhs.m_CurItemID &&
207 2307 : m_CurPointer == rhs.m_CurPointer;
208 : }
209 12182 : bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
210 : private:
211 : size_t m_Size;
212 : size_t m_CurItemID;
213 : const char* m_CurPointer;
214 : };
215 2347 : iterator begin() { return iterator(m_Size, m_Pointer); }
216 4694 : iterator end() { return iterator(m_Size, m_Pointer, m_EndPointer); }
217 :
218 4 : size_t size() const { return m_Size; }
219 0 : bool empty() const { return m_Size == 0; }
220 :
221 : private:
222 : size_t m_Size;
223 :
224 : const char* m_Pointer;
225 :
226 : // For optimised sequential access:
227 : size_t m_CurItemID;
228 : const char* m_CurPointer;
229 :
230 : const char* m_EndPointer;
231 : };
232 :
233 :
234 4802 : struct XMBAttribute
235 : {
236 : XMBAttribute() {}
237 : XMBAttribute(int name, const CStr8& value)
238 3376 : : Name(name), Value(value) {};
239 :
240 : int Name;
241 : CStr8 Value; // UTF-8 encoded
242 : };
243 :
244 : class XMBAttributeList
245 : {
246 : public:
247 : XMBAttributeList(const char* offset, size_t count, const char* endoffset)
248 3087 : : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
249 :
250 : // Get the attribute value directly
251 : CStr8 GetNamedItem(const int AttributeName) const;
252 :
253 : // Linear in the number of elements in the list
254 : XMBAttribute operator[](size_t id); // returns Children[id]
255 :
256 : class iterator
257 : {
258 : public:
259 : typedef ptrdiff_t difference_type;
260 : typedef XMBAttribute value_type;
261 : typedef XMBAttribute reference; // Because we need to construct the object
262 : typedef XMBAttribute pointer; // Because we need to construct the object
263 : typedef std::forward_iterator_tag iterator_category;
264 :
265 : iterator(size_t size, const char* ptr, const char* endptr = NULL)
266 2741 : : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
267 : XMBAttribute operator*() const;
268 : XMBAttribute operator->() const { return **this; }
269 : iterator& operator++();
270 :
271 : bool operator==(const iterator& rhs) const
272 : {
273 12164 : return m_Size == rhs.m_Size &&
274 6082 : m_CurItemID == rhs.m_CurItemID &&
275 2709 : m_CurPointer == rhs.m_CurPointer;
276 : }
277 11784 : bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
278 : private:
279 : size_t m_Size;
280 : size_t m_CurItemID;
281 : const char* m_CurPointer;
282 : };
283 2741 : iterator begin() const { return iterator(m_Size, m_Pointer); }
284 5482 : iterator end() const { return iterator(m_Size, m_Pointer, m_EndPointer); }
285 :
286 2 : size_t size() const { return m_Size; }
287 : bool empty() const { return m_Size == 0; }
288 :
289 : private:
290 : size_t m_Size;
291 :
292 : // Pointer to start of attribute list
293 : const char* m_Pointer;
294 :
295 : // For optimised sequential access:
296 : size_t m_CurItemID;
297 : const char* m_CurPointer;
298 :
299 : const char* m_EndPointer;
300 : };
301 :
302 : #endif // INCLUDED_XEROXMB
|