mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 13:23:56 -07:00
Declare "lib/posix/posix.h" as source for <strings.h>
Make include-what-you-use happy with files in source/ps/{XMB,XML} and
fix what needs to be fixed after including missing compile flags.
Ref: #8086
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
297 lines
7.5 KiB
C++
297 lines
7.5 KiB
C++
/* Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include "XMBData.h"
|
|
|
|
#include "lib/debug.h"
|
|
#include "lib/posix/posix.h"
|
|
#include "lib/secure_crt.h"
|
|
#include "lib/types.h"
|
|
#include "ps/XMB/XMBStorage.h"
|
|
|
|
#include <cstring>
|
|
#include <memory>
|
|
|
|
template<typename T>
|
|
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<const char*>(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<u32>(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<u32>(m_Pointer); m_Pointer += 4;
|
|
m_ElementNameCount = read<int>(m_Pointer); m_Pointer += 4;
|
|
m_AttributePointer = start + read<u32>(m_Pointer); m_Pointer += 4;
|
|
m_AttributeNameCount = read<int>(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<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
|
|
return static_cast<int>(Pos - m_ElementPointer);
|
|
// If not, jump to the next string
|
|
Pos += 4 + read<int>(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<int>(Pos) == len && strncasecmp(Pos+4, Name, len) == 0)
|
|
return static_cast<int>(Pos - m_AttributePointer);
|
|
// If not, jump to the next string
|
|
Pos += 4 + read<int>(Pos);
|
|
}
|
|
// Failed
|
|
return -1;
|
|
}
|
|
|
|
const char* XMBData::GetElementString(const int ID) const
|
|
{
|
|
return reinterpret_cast<const char*>(m_ElementPointer + ID + 4);
|
|
}
|
|
|
|
const char* XMBData::GetAttributeString(const int ID) const
|
|
{
|
|
return reinterpret_cast<const char*>(m_AttributePointer + ID + 4);
|
|
}
|
|
|
|
std::string_view XMBData::GetElementStringView(const int ID) const
|
|
{
|
|
return std::string_view(reinterpret_cast<const char*>(m_ElementPointer + ID + 4), read<int>(m_ElementPointer + ID) - 1);
|
|
}
|
|
|
|
std::string_view XMBData::GetAttributeStringView(const int ID) const
|
|
{
|
|
return std::string_view(reinterpret_cast<const char*>(m_AttributePointer + ID + 4), read<int>(m_AttributePointer + ID) - 1);
|
|
}
|
|
|
|
int XMBElement::GetNodeName() const
|
|
{
|
|
if (m_Pointer == NULL)
|
|
return -1;
|
|
|
|
return read<int>(m_Pointer + 4); // == ElementName
|
|
}
|
|
|
|
XMBElementList XMBElement::GetChildNodes() const
|
|
{
|
|
if (m_Pointer == NULL)
|
|
return XMBElementList(NULL, 0, NULL);
|
|
|
|
return XMBElementList(
|
|
m_Pointer + 20 + read<int>(m_Pointer + 16), // == Children[]
|
|
read<int>(m_Pointer + 12), // == ChildCount
|
|
m_Pointer + read<int>(m_Pointer) // == &Children[ChildCount]
|
|
);
|
|
}
|
|
|
|
XMBAttributeList XMBElement::GetAttributes() const
|
|
{
|
|
if (m_Pointer == NULL)
|
|
return XMBAttributeList(NULL, 0, NULL);
|
|
|
|
return XMBAttributeList(
|
|
m_Pointer + 24 + read<int>(m_Pointer + 20), // == Attributes[]
|
|
read<int>(m_Pointer + 8), // == AttributeCount
|
|
m_Pointer + 20 + read<int>(m_Pointer + 16) // == &Attributes[AttributeCount] ( == &Children[])
|
|
);
|
|
}
|
|
|
|
CStr8 XMBElement::GetText() const
|
|
{
|
|
// Return empty string if there's no text
|
|
if (m_Pointer == NULL || read<int>(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<int>(m_Pointer + 20) == 0)
|
|
return -1;
|
|
else
|
|
return read<int>(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<int>(Pos);
|
|
int Name = read<int>(Pos+4);
|
|
if (Name == ElementName)
|
|
return XMBElement(Pos);
|
|
Pos += Length;
|
|
}
|
|
|
|
// Can't find element
|
|
return XMBElement();
|
|
}
|
|
|
|
XMBElementList::iterator& XMBElementList::iterator::operator++()
|
|
{
|
|
m_CurPointer += read<int>(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<int>(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<int>(Pos) == AttributeName)
|
|
return CStr8(Pos+8);
|
|
Pos += 8 + read<int>(Pos+4); // Skip over the string
|
|
}
|
|
|
|
// Can't find attribute
|
|
return CStr8();
|
|
}
|
|
|
|
XMBAttribute XMBAttributeList::iterator::operator*() const
|
|
{
|
|
return XMBAttribute(read<int>(m_CurPointer), CStr8(m_CurPointer+8));
|
|
}
|
|
|
|
XMBAttributeList::iterator& XMBAttributeList::iterator::operator++()
|
|
{
|
|
m_CurPointer += 8 + read<int>(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<int>(Pos+4); // skip ID, length, and string data
|
|
|
|
// Cache information about this attribute
|
|
m_CurItemID = id;
|
|
m_CurPointer = Pos;
|
|
|
|
return XMBAttribute(read<int>(Pos), CStr8(Pos+8));
|
|
}
|