Line data Source code
1 : /* Copyright (C) 2015 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 : #include "precompiled.h"
19 :
20 : #include "Xeromyces.h"
21 :
22 : #include "lib/byte_order.h" // FOURCC_LE
23 :
24 : // external linkage (also used by Xeromyces.cpp)
25 : const char* HeaderMagicStr = "XMB0";
26 : const char* UnfinishedHeaderMagicStr = "XMBu";
27 : // Arbitrary version number - change this if we update the code and
28 : // need to invalidate old users' caches
29 : const u32 XMBVersion = 3;
30 :
31 : template<typename T>
32 : static inline T read(const void* ptr)
33 : {
34 : T ret;
35 198618 : memcpy(&ret, ptr, sizeof(T));
36 : return ret;
37 : }
38 :
39 1402 : bool XMBFile::Initialise(const char* FileData)
40 : {
41 1402 : m_Pointer = FileData;
42 1402 : char Header[5] = { 0 };
43 1402 : strncpy_s(Header, 5, m_Pointer, 4);
44 1402 : m_Pointer += 4;
45 : // (c.f. @return documentation of this function)
46 1402 : if(!strcmp(Header, UnfinishedHeaderMagicStr))
47 : return false;
48 1402 : ENSURE(!strcmp(Header, HeaderMagicStr) && "Invalid XMB header!");
49 :
50 1402 : u32 Version = read<u32>(m_Pointer);
51 1402 : m_Pointer += 4;
52 1402 : if (Version != XMBVersion)
53 : return false;
54 :
55 1402 : int i;
56 :
57 : // FIXME Check that m_Pointer doesn't end up past the end of the buffer
58 : // (it shouldn't be all that dangerous since we're only doing read-only
59 : // access, but it might crash on an invalid file, reading a couple of
60 : // billion random element names from RAM)
61 :
62 : #ifdef XERO_USEMAP
63 : // Build a std::map of all the names->ids
64 : u32 ElementNameCount = read<u32>(m_Pointer); m_Pointer += 4;
65 : for (i = 0; i < ElementNameCount; ++i)
66 : m_ElementNames[ReadZStr8()] = i;
67 :
68 : u32 AttributeNameCount = read<u32>(m_Pointer); m_Pointer += 4;
69 : for (i = 0; i < AttributeNameCount; ++i)
70 : m_AttributeNames[ReadZStr8()] = i;
71 : #else
72 : // Ignore all the names for now, and skip over them
73 : // (remembering the position of the first)
74 1402 : m_ElementNameCount = read<int>(m_Pointer); m_Pointer += 4;
75 1402 : m_ElementPointer = m_Pointer;
76 6085 : for (i = 0; i < m_ElementNameCount; ++i)
77 9366 : m_Pointer += 4 + read<int>(m_Pointer); // skip over the string
78 :
79 1402 : m_AttributeNameCount = read<int>(m_Pointer); m_Pointer += 4;
80 1402 : m_AttributePointer = m_Pointer;
81 4160 : for (i = 0; i < m_AttributeNameCount; ++i)
82 5516 : m_Pointer += 4 + read<int>(m_Pointer); // skip over the string
83 : #endif
84 :
85 : return true; // success
86 : }
87 :
88 0 : std::string XMBFile::ReadZStr8()
89 : {
90 0 : int Length = read<int>(m_Pointer);
91 0 : m_Pointer += 4;
92 0 : std::string String (m_Pointer); // reads up until the first NULL
93 0 : m_Pointer += Length;
94 0 : return String;
95 : }
96 :
97 1434 : XMBElement XMBFile::GetRoot() const
98 : {
99 1434 : return XMBElement(m_Pointer);
100 : }
101 :
102 :
103 : #ifdef XERO_USEMAP
104 :
105 : int XMBFile::GetElementID(const char* Name) const
106 : {
107 : return m_ElementNames[Name];
108 : }
109 :
110 : int XMBFile::GetAttributeID(const char* Name) const
111 : {
112 : return m_AttributeNames[Name];
113 : }
114 :
115 : #else // #ifdef XERO_USEMAP
116 :
117 6771 : int XMBFile::GetElementID(const char* Name) const
118 : {
119 6771 : const char* Pos = m_ElementPointer;
120 :
121 6771 : int len = (int)strlen(Name)+1; // count bytes, including null terminator
122 :
123 : // Loop through each string to find a match
124 24162 : for (int i = 0; i < m_ElementNameCount; ++i)
125 : {
126 : // See if this could be the right string, checking its
127 : // length and then its contents
128 42648 : if (read<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
129 3933 : return i;
130 : // If not, jump to the next string
131 34782 : Pos += 4 + read<int>(Pos);
132 : }
133 : // Failed
134 : return -1;
135 : }
136 :
137 5235 : int XMBFile::GetAttributeID(const char* Name) const
138 : {
139 5235 : const char* Pos = m_AttributePointer;
140 :
141 5235 : int len = (int)strlen(Name)+1; // count bytes, including null terminator
142 :
143 : // Loop through each string to find a match
144 10361 : for (int i = 0; i < m_AttributeNameCount; ++i)
145 : {
146 : // See if this could be the right string, checking its
147 : // length and then its contents
148 15352 : if (read<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
149 2550 : return i;
150 : // If not, jump to the next string
151 10252 : Pos += 4 + read<int>(Pos);
152 : }
153 : // Failed
154 : return -1;
155 : }
156 : #endif // #ifdef XERO_USEMAP / #else
157 :
158 :
159 : // Relatively inefficient, so only use when
160 : // laziness overcomes the need for speed
161 491 : std::string XMBFile::GetElementString(const int ID) const
162 : {
163 491 : const char* Pos = m_ElementPointer;
164 2552 : for (int i = 0; i < ID; ++i)
165 4122 : Pos += 4 + read<int>(Pos);
166 982 : return std::string(Pos+4);
167 : }
168 :
169 74 : std::string XMBFile::GetAttributeString(const int ID) const
170 : {
171 74 : const char* Pos = m_AttributePointer;
172 112 : for (int i = 0; i < ID; ++i)
173 76 : Pos += 4 + read<int>(Pos);
174 148 : return std::string(Pos+4);
175 : }
176 :
177 :
178 :
179 4913 : int XMBElement::GetNodeName() const
180 : {
181 4913 : if (m_Pointer == NULL)
182 : return -1;
183 :
184 9824 : return read<int>(m_Pointer + 4); // == ElementName
185 : }
186 :
187 2356 : XMBElementList XMBElement::GetChildNodes() const
188 : {
189 2356 : if (m_Pointer == NULL)
190 1 : return XMBElementList(NULL, 0, NULL);
191 :
192 2355 : return XMBElementList(
193 4710 : m_Pointer + 20 + read<int>(m_Pointer + 16), // == Children[]
194 4710 : read<int>(m_Pointer + 12), // == ChildCount
195 2355 : m_Pointer + read<int>(m_Pointer) // == &Children[ChildCount]
196 7065 : );
197 : }
198 :
199 3087 : XMBAttributeList XMBElement::GetAttributes() const
200 : {
201 3087 : if (m_Pointer == NULL)
202 1 : return XMBAttributeList(NULL, 0, NULL);
203 :
204 3086 : return XMBAttributeList(
205 6172 : m_Pointer + 24 + read<int>(m_Pointer + 20), // == Attributes[]
206 6172 : read<int>(m_Pointer + 8), // == AttributeCount
207 3086 : m_Pointer + 20 + read<int>(m_Pointer + 16) // == &Attributes[AttributeCount] ( == &Children[])
208 9258 : );
209 : }
210 :
211 1386 : CStr8 XMBElement::GetText() const
212 : {
213 : // Return empty string if there's no text
214 1386 : if (m_Pointer == NULL || read<int>(m_Pointer + 20) == 0)
215 247 : return CStr8();
216 :
217 1139 : return CStr8(m_Pointer + 28);
218 : }
219 :
220 4 : int XMBElement::GetLineNumber() const
221 : {
222 : // Make sure there actually was some text to record the line of
223 4 : if (m_Pointer == NULL || read<int>(m_Pointer + 20) == 0)
224 : return -1;
225 : else
226 4 : return read<int>(m_Pointer + 24);
227 : }
228 :
229 3 : XMBElement XMBElementList::GetFirstNamedItem(const int ElementName) const
230 : {
231 3 : const char* Pos = m_Pointer;
232 :
233 : // Maybe not the cleverest algorithm, but it should be
234 : // fast enough with half a dozen attributes:
235 9 : for (size_t i = 0; i < m_Size; ++i)
236 : {
237 8 : int Length = read<int>(Pos);
238 8 : int Name = read<int>(Pos+4);
239 8 : if (Name == ElementName)
240 2 : return XMBElement(Pos);
241 6 : Pos += Length;
242 : }
243 :
244 : // Can't find element
245 1 : return XMBElement();
246 : }
247 :
248 3744 : XMBElementList::iterator& XMBElementList::iterator::operator++()
249 : {
250 7488 : m_CurPointer += read<int>(m_CurPointer);
251 3744 : ++m_CurItemID;
252 3744 : return (*this);
253 : }
254 :
255 2 : XMBElement XMBElementList::operator[](size_t id)
256 : {
257 2 : ENSURE(id < m_Size && "Element ID out of range");
258 2 : const char* Pos;
259 2 : size_t i;
260 :
261 2 : if (id < m_CurItemID)
262 : {
263 0 : Pos = m_Pointer;
264 0 : i = 0;
265 : }
266 : else
267 : {
268 : // If access is sequential, don't bother scanning
269 : // through all the nodes to find the next one
270 2 : Pos = m_CurPointer;
271 2 : i = m_CurItemID;
272 : }
273 :
274 : // Skip over each preceding node
275 3 : for (; i < id; ++i)
276 2 : Pos += read<int>(Pos);
277 :
278 : // Cache information about this node
279 2 : m_CurItemID = id;
280 2 : m_CurPointer = Pos;
281 :
282 2 : return XMBElement(Pos);
283 : }
284 :
285 349 : CStr8 XMBAttributeList::GetNamedItem(const int AttributeName) const
286 : {
287 349 : const char* Pos = m_Pointer;
288 :
289 : // Maybe not the cleverest algorithm, but it should be
290 : // fast enough with half a dozen attributes:
291 663 : for (size_t i = 0; i < m_Size; ++i)
292 : {
293 1258 : if (read<int>(Pos) == AttributeName)
294 315 : return CStr8(Pos+8);
295 628 : Pos += 8 + read<int>(Pos+4); // Skip over the string
296 : }
297 :
298 : // Can't find attribute
299 34 : return CStr8();
300 : }
301 :
302 3373 : XMBAttribute XMBAttributeList::iterator::operator*() const
303 : {
304 4799 : return XMBAttribute(read<int>(m_CurPointer), CStr8(m_CurPointer+8));
305 : }
306 :
307 3341 : XMBAttributeList::iterator& XMBAttributeList::iterator::operator++()
308 : {
309 6682 : m_CurPointer += 8 + read<int>(m_CurPointer+4); // skip ID, length, and string data
310 3341 : ++m_CurItemID;
311 3341 : return (*this);
312 : }
313 :
314 3 : XMBAttribute XMBAttributeList::operator[](size_t id)
315 : {
316 3 : ENSURE(id < m_Size && "Attribute ID out of range");
317 3 : const char* Pos;
318 3 : size_t i;
319 :
320 3 : if (id < m_CurItemID)
321 : {
322 0 : Pos = m_Pointer;
323 0 : i = 0;
324 : }
325 : else
326 : {
327 : // If access is sequential, don't bother scanning
328 : // through all the nodes to find the next one
329 3 : Pos = m_CurPointer;
330 3 : i = m_CurItemID;
331 : }
332 :
333 : // Skip over each preceding attribute
334 3 : for (; i < id; ++i)
335 0 : Pos += 8 + read<int>(Pos+4); // skip ID, length, and string data
336 :
337 : // Cache information about this attribute
338 3 : m_CurItemID = id;
339 3 : m_CurPointer = Pos;
340 :
341 3 : return XMBAttribute(read<int>(Pos), CStr8(Pos+8));
342 : }
|