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 : #include "precompiled.h"
19 :
20 : #include "lib/byte_order.h" // FOURCC_LE
21 : #include "ps/XMB/XMBStorage.h"
22 : #include "ps/XML/Xeromyces.h"
23 :
24 : template<typename T>
25 20543 : static inline T read(const void* ptr)
26 : {
27 : T ret;
28 20543 : memcpy(&ret, ptr, sizeof(T));
29 20543 : return ret;
30 : }
31 :
32 425 : bool XMBData::Initialise(const XMBStorage& doc)
33 : {
34 425 : const char* start = reinterpret_cast<const char*>(doc.m_Buffer.get());
35 425 : m_Pointer = start;
36 425 : char Header[5] = { 0 };
37 425 : strncpy_s(Header, 5, m_Pointer, 4);
38 425 : m_Pointer += 4;
39 :
40 425 : if (strcmp(Header, XMBStorage::UnfinishedHeaderMagicStr) == 0)
41 0 : return false;
42 425 : ENSURE(strcmp(Header, XMBStorage::HeaderMagicStr) == 0 && "Invalid XMB header!");
43 :
44 425 : u32 Version = read<u32>(m_Pointer);
45 425 : m_Pointer += 4;
46 425 : if (Version != XMBStorage::XMBVersion)
47 0 : return false;
48 :
49 : // FIXME Check that m_Pointer doesn't end up past the end of the buffer
50 : // (it shouldn't be all that dangerous since we're only doing read-only
51 : // access, but it might crash on an invalid file, reading a couple of
52 : // billion random element names from RAM)
53 :
54 425 : m_ElementPointer = start + read<u32>(m_Pointer); m_Pointer += 4;
55 425 : m_ElementNameCount = read<int>(m_Pointer); m_Pointer += 4;
56 425 : m_AttributePointer = start + read<u32>(m_Pointer); m_Pointer += 4;
57 425 : m_AttributeNameCount = read<int>(m_Pointer); m_Pointer += 4;
58 : // At this point m_Pointer points to the element start, as expected.
59 425 : return true; // success
60 : }
61 :
62 457 : XMBElement XMBData::GetRoot() const
63 : {
64 457 : return XMBElement(m_Pointer);
65 : }
66 :
67 82 : int XMBData::GetElementID(const char* Name) const
68 : {
69 82 : const char* Pos = m_ElementPointer;
70 :
71 82 : int len = (int)strlen(Name)+1; // count bytes, including null terminator
72 :
73 : // Loop through each string to find a match
74 230 : for (int i = 0; i < m_ElementNameCount; ++i)
75 : {
76 : // See if this could be the right string, checking its
77 : // length and then its contents
78 196 : if (read<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
79 48 : return static_cast<int>(Pos - m_ElementPointer);
80 : // If not, jump to the next string
81 148 : Pos += 4 + read<int>(Pos);
82 : }
83 : // Failed
84 34 : return -1;
85 : }
86 :
87 3251 : int XMBData::GetAttributeID(const char* Name) const
88 : {
89 3251 : const char* Pos = m_AttributePointer;
90 :
91 3251 : int len = (int)strlen(Name)+1; // count bytes, including null terminator
92 :
93 : // Loop through each string to find a match
94 7092 : for (int i = 0; i < m_AttributeNameCount; ++i)
95 : {
96 : // See if this could be the right string, checking its
97 : // length and then its contents
98 4326 : if (read<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
99 485 : return static_cast<int>(Pos - m_AttributePointer);
100 : // If not, jump to the next string
101 3841 : Pos += 4 + read<int>(Pos);
102 : }
103 : // Failed
104 2766 : return -1;
105 : }
106 :
107 512 : const char* XMBData::GetElementString(const int ID) const
108 : {
109 512 : return reinterpret_cast<const char*>(m_ElementPointer + ID + 4);
110 : }
111 :
112 91 : const char* XMBData::GetAttributeString(const int ID) const
113 : {
114 91 : return reinterpret_cast<const char*>(m_AttributePointer + ID + 4);
115 : }
116 :
117 8 : std::string_view XMBData::GetElementStringView(const int ID) const
118 : {
119 8 : return std::string_view(reinterpret_cast<const char*>(m_ElementPointer + ID + 4), read<int>(m_ElementPointer + ID) - 1);
120 : }
121 :
122 5 : std::string_view XMBData::GetAttributeStringView(const int ID) const
123 : {
124 5 : return std::string_view(reinterpret_cast<const char*>(m_AttributePointer + ID + 4), read<int>(m_AttributePointer + ID) - 1);
125 : }
126 :
127 556 : int XMBElement::GetNodeName() const
128 : {
129 556 : if (m_Pointer == NULL)
130 3 : return -1;
131 :
132 553 : return read<int>(m_Pointer + 4); // == ElementName
133 : }
134 :
135 497 : XMBElementList XMBElement::GetChildNodes() const
136 : {
137 497 : if (m_Pointer == NULL)
138 3 : return XMBElementList(NULL, 0, NULL);
139 :
140 : return XMBElementList(
141 494 : m_Pointer + 20 + read<int>(m_Pointer + 16), // == Children[]
142 494 : read<int>(m_Pointer + 12), // == ChildCount
143 494 : m_Pointer + read<int>(m_Pointer) // == &Children[ChildCount]
144 1482 : );
145 : }
146 :
147 1747 : XMBAttributeList XMBElement::GetAttributes() const
148 : {
149 1747 : if (m_Pointer == NULL)
150 3 : return XMBAttributeList(NULL, 0, NULL);
151 :
152 : return XMBAttributeList(
153 1744 : m_Pointer + 24 + read<int>(m_Pointer + 20), // == Attributes[]
154 1744 : read<int>(m_Pointer + 8), // == AttributeCount
155 1744 : m_Pointer + 20 + read<int>(m_Pointer + 16) // == &Attributes[AttributeCount] ( == &Children[])
156 5232 : );
157 : }
158 :
159 517 : CStr8 XMBElement::GetText() const
160 : {
161 : // Return empty string if there's no text
162 517 : if (m_Pointer == NULL || read<int>(m_Pointer + 20) == 0)
163 289 : return CStr8();
164 :
165 228 : return CStr8(m_Pointer + 28);
166 : }
167 :
168 4 : int XMBElement::GetLineNumber() const
169 : {
170 : // Make sure there actually was some text to record the line of
171 4 : if (m_Pointer == NULL || read<int>(m_Pointer + 20) == 0)
172 2 : return -1;
173 : else
174 2 : return read<int>(m_Pointer + 24);
175 : }
176 :
177 9 : XMBElement XMBElementList::GetFirstNamedItem(const int ElementName) const
178 : {
179 9 : const char* Pos = m_Pointer;
180 :
181 : // Maybe not the cleverest algorithm, but it should be
182 : // fast enough with half a dozen attributes:
183 27 : for (size_t i = 0; i < m_Size; ++i)
184 : {
185 24 : int Length = read<int>(Pos);
186 24 : int Name = read<int>(Pos+4);
187 24 : if (Name == ElementName)
188 6 : return XMBElement(Pos);
189 18 : Pos += Length;
190 : }
191 :
192 : // Can't find element
193 3 : return XMBElement();
194 : }
195 :
196 402 : XMBElementList::iterator& XMBElementList::iterator::operator++()
197 : {
198 402 : m_CurPointer += read<int>(m_CurPointer);
199 402 : ++m_CurItemID;
200 402 : return (*this);
201 : }
202 :
203 4 : XMBElement XMBElementList::operator[](size_t id)
204 : {
205 4 : ENSURE(id < m_Size && "Element ID out of range");
206 : const char* Pos;
207 : size_t i;
208 :
209 4 : if (id < m_CurItemID)
210 : {
211 0 : Pos = m_Pointer;
212 0 : i = 0;
213 : }
214 : else
215 : {
216 : // If access is sequential, don't bother scanning
217 : // through all the nodes to find the next one
218 4 : Pos = m_CurPointer;
219 4 : i = m_CurItemID;
220 : }
221 :
222 : // Skip over each preceding node
223 6 : for (; i < id; ++i)
224 1 : Pos += read<int>(Pos);
225 :
226 : // Cache information about this node
227 4 : m_CurItemID = id;
228 4 : m_CurPointer = Pos;
229 :
230 4 : return XMBElement(Pos);
231 : }
232 :
233 351 : CStr8 XMBAttributeList::GetNamedItem(const int AttributeName) const
234 : {
235 351 : const char* Pos = m_Pointer;
236 :
237 : // Maybe not the cleverest algorithm, but it should be
238 : // fast enough with half a dozen attributes:
239 666 : for (size_t i = 0; i < m_Size; ++i)
240 : {
241 631 : if (read<int>(Pos) == AttributeName)
242 316 : return CStr8(Pos+8);
243 315 : Pos += 8 + read<int>(Pos+4); // Skip over the string
244 : }
245 :
246 : // Can't find attribute
247 35 : return CStr8();
248 : }
249 :
250 369 : XMBAttribute XMBAttributeList::iterator::operator*() const
251 : {
252 369 : return XMBAttribute(read<int>(m_CurPointer), CStr8(m_CurPointer+8));
253 : }
254 :
255 337 : XMBAttributeList::iterator& XMBAttributeList::iterator::operator++()
256 : {
257 337 : m_CurPointer += 8 + read<int>(m_CurPointer+4); // skip ID, length, and string data
258 337 : ++m_CurItemID;
259 337 : return (*this);
260 : }
261 :
262 5 : XMBAttribute XMBAttributeList::operator[](size_t id)
263 : {
264 5 : ENSURE(id < m_Size && "Attribute ID out of range");
265 : const char* Pos;
266 : size_t i;
267 :
268 5 : if (id < m_CurItemID)
269 : {
270 0 : Pos = m_Pointer;
271 0 : i = 0;
272 : }
273 : else
274 : {
275 : // If access is sequential, don't bother scanning
276 : // through all the nodes to find the next one
277 5 : Pos = m_CurPointer;
278 5 : i = m_CurItemID;
279 : }
280 :
281 : // Skip over each preceding attribute
282 5 : for (; i < id; ++i)
283 0 : Pos += 8 + read<int>(Pos+4); // skip ID, length, and string data
284 :
285 : // Cache information about this attribute
286 5 : m_CurItemID = id;
287 5 : m_CurPointer = Pos;
288 :
289 5 : return XMBAttribute(read<int>(Pos), CStr8(Pos+8));
290 3 : }
|