/* Copyright (C) 2021 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "lib/byte_order.h" // FOURCC_LE #include "ps/XMB/XMBStorage.h" #include "ps/XML/Xeromyces.h" template static inline T read(const void* ptr) { T ret; memcpy(&ret, ptr, sizeof(T)); return ret; } bool XMBData::Initialise(const XMBStorage& doc) { const char* start = reinterpret_cast(doc.m_Buffer.get()); m_Pointer = start; char Header[5] = { 0 }; strncpy_s(Header, 5, m_Pointer, 4); m_Pointer += 4; if (strcmp(Header, XMBStorage::UnfinishedHeaderMagicStr) == 0) return false; ENSURE(strcmp(Header, XMBStorage::HeaderMagicStr) == 0 && "Invalid XMB header!"); u32 Version = read(m_Pointer); m_Pointer += 4; if (Version != XMBStorage::XMBVersion) return false; // FIXME Check that m_Pointer doesn't end up past the end of the buffer // (it shouldn't be all that dangerous since we're only doing read-only // access, but it might crash on an invalid file, reading a couple of // billion random element names from RAM) m_ElementPointer = start + read(m_Pointer); m_Pointer += 4; m_ElementNameCount = read(m_Pointer); m_Pointer += 4; m_AttributePointer = start + read(m_Pointer); m_Pointer += 4; m_AttributeNameCount = read(m_Pointer); m_Pointer += 4; // At this point m_Pointer points to the element start, as expected. return true; // success } XMBElement XMBData::GetRoot() const { return XMBElement(m_Pointer); } int XMBData::GetElementID(const char* Name) const { const char* Pos = m_ElementPointer; int len = (int)strlen(Name)+1; // count bytes, including null terminator // Loop through each string to find a match for (int i = 0; i < m_ElementNameCount; ++i) { // See if this could be the right string, checking its // length and then its contents if (read(Pos) == len && strncasecmp(Pos+4, Name, len) == 0) return static_cast(Pos - m_ElementPointer); // If not, jump to the next string Pos += 4 + read(Pos); } // Failed return -1; } int XMBData::GetAttributeID(const char* Name) const { const char* Pos = m_AttributePointer; int len = (int)strlen(Name)+1; // count bytes, including null terminator // Loop through each string to find a match for (int i = 0; i < m_AttributeNameCount; ++i) { // See if this could be the right string, checking its // length and then its contents if (read(Pos) == len && strncasecmp(Pos+4, Name, len) == 0) return static_cast(Pos - m_AttributePointer); // If not, jump to the next string Pos += 4 + read(Pos); } // Failed return -1; } const char* XMBData::GetElementString(const int ID) const { return reinterpret_cast(m_ElementPointer + ID + 4); } const char* XMBData::GetAttributeString(const int ID) const { return reinterpret_cast(m_AttributePointer + ID + 4); } std::string_view XMBData::GetElementStringView(const int ID) const { return std::string_view(reinterpret_cast(m_ElementPointer + ID + 4), read(m_ElementPointer + ID) - 1); } std::string_view XMBData::GetAttributeStringView(const int ID) const { return std::string_view(reinterpret_cast(m_AttributePointer + ID + 4), read(m_AttributePointer + ID) - 1); } int XMBElement::GetNodeName() const { if (m_Pointer == NULL) return -1; return read(m_Pointer + 4); // == ElementName } XMBElementList XMBElement::GetChildNodes() const { if (m_Pointer == NULL) return XMBElementList(NULL, 0, NULL); return XMBElementList( m_Pointer + 20 + read(m_Pointer + 16), // == Children[] read(m_Pointer + 12), // == ChildCount m_Pointer + read(m_Pointer) // == &Children[ChildCount] ); } XMBAttributeList XMBElement::GetAttributes() const { if (m_Pointer == NULL) return XMBAttributeList(NULL, 0, NULL); return XMBAttributeList( m_Pointer + 24 + read(m_Pointer + 20), // == Attributes[] read(m_Pointer + 8), // == AttributeCount m_Pointer + 20 + read(m_Pointer + 16) // == &Attributes[AttributeCount] ( == &Children[]) ); } CStr8 XMBElement::GetText() const { // Return empty string if there's no text if (m_Pointer == NULL || read(m_Pointer + 20) == 0) return CStr8(); return CStr8(m_Pointer + 28); } int XMBElement::GetLineNumber() const { // Make sure there actually was some text to record the line of if (m_Pointer == NULL || read(m_Pointer + 20) == 0) return -1; else return read(m_Pointer + 24); } XMBElement XMBElementList::GetFirstNamedItem(const int ElementName) const { const char* Pos = m_Pointer; // Maybe not the cleverest algorithm, but it should be // fast enough with half a dozen attributes: for (size_t i = 0; i < m_Size; ++i) { int Length = read(Pos); int Name = read(Pos+4); if (Name == ElementName) return XMBElement(Pos); Pos += Length; } // Can't find element return XMBElement(); } XMBElementList::iterator& XMBElementList::iterator::operator++() { m_CurPointer += read(m_CurPointer); ++m_CurItemID; return (*this); } XMBElement XMBElementList::operator[](size_t id) { ENSURE(id < m_Size && "Element ID out of range"); const char* Pos; size_t i; if (id < m_CurItemID) { Pos = m_Pointer; i = 0; } else { // If access is sequential, don't bother scanning // through all the nodes to find the next one Pos = m_CurPointer; i = m_CurItemID; } // Skip over each preceding node for (; i < id; ++i) Pos += read(Pos); // Cache information about this node m_CurItemID = id; m_CurPointer = Pos; return XMBElement(Pos); } CStr8 XMBAttributeList::GetNamedItem(const int AttributeName) const { const char* Pos = m_Pointer; // Maybe not the cleverest algorithm, but it should be // fast enough with half a dozen attributes: for (size_t i = 0; i < m_Size; ++i) { if (read(Pos) == AttributeName) return CStr8(Pos+8); Pos += 8 + read(Pos+4); // Skip over the string } // Can't find attribute return CStr8(); } XMBAttribute XMBAttributeList::iterator::operator*() const { return XMBAttribute(read(m_CurPointer), CStr8(m_CurPointer+8)); } XMBAttributeList::iterator& XMBAttributeList::iterator::operator++() { m_CurPointer += 8 + read(m_CurPointer+4); // skip ID, length, and string data ++m_CurItemID; return (*this); } XMBAttribute XMBAttributeList::operator[](size_t id) { ENSURE(id < m_Size && "Attribute ID out of range"); const char* Pos; size_t i; if (id < m_CurItemID) { Pos = m_Pointer; i = 0; } else { // If access is sequential, don't bother scanning // through all the nodes to find the next one Pos = m_CurPointer; i = m_CurItemID; } // Skip over each preceding attribute for (; i < id; ++i) Pos += 8 + read(Pos+4); // skip ID, length, and string data // Cache information about this attribute m_CurItemID = id; m_CurPointer = Pos; return XMBAttribute(read(Pos), CStr8(Pos+8)); }