Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/ps/XMB/XMBData.h
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
/* 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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
/* | |||||
Xeromyces - XMB reading library | |||||
Brief outline: | |||||
XMB originated as a binary representation of XML, with some limitations | |||||
but much more efficiency (particularly for loading simple data | |||||
classes that don't need much initialisation). | |||||
Theoretical file structure: | |||||
XMB_File { | |||||
char Header[4]; // because everyone has one; currently "XMB0" | |||||
u32 Version; | |||||
int OffsetFromStartToElementNames; | |||||
int ElementNameCount; | |||||
int OffsetFromStartToAttributeNames; | |||||
int AttributeNameCount; | |||||
XMB_Node Root; | |||||
ZStr8 ElementNames[]; | |||||
ZStr8 AttributeNames[]; | |||||
} | |||||
XMB_Node { | |||||
0) int Length; // of entire struct, so it can be skipped over | |||||
4) int ElementName; | |||||
8) int AttributeCount; | |||||
12) int ChildCount; | |||||
16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes) | |||||
20) XMB_Text Text; | |||||
XMB_Attribute Attributes[]; | |||||
XMB_Node Children[]; | |||||
} | |||||
XMB_Attribute { | |||||
int Name; | |||||
ZStr8 Value; | |||||
} | |||||
ZStr8 { | |||||
int Length; // in bytes | |||||
char* Text; // null-terminated UTF8 | |||||
} | |||||
XMB_Text { | |||||
20) int Length; // 0 if there's no text, else 4+sizeof(Text) in bytes including terminator | |||||
// If Length != 0: | |||||
24) int LineNumber; // for e.g. debugging scripts | |||||
28) char* Text; // null-terminated UTF8 | |||||
} | |||||
*/ | |||||
#ifndef INCLUDED_XEROXMB | |||||
#define INCLUDED_XEROXMB | |||||
#include "ps/CStr.h" | |||||
#include <string> | |||||
#include <string_view> | |||||
class CXeromyces; | |||||
class XMBStorage; | |||||
class XMBElement; | |||||
class XMBElementList; | |||||
class XMBAttributeList; | |||||
class XMBData | |||||
{ | |||||
public: | |||||
XMBData() : m_Pointer(nullptr) {} | |||||
/* | |||||
* Initialise from the contents of an XMBStorage. | |||||
* @param doc must remain allocated and unchanged while | |||||
* the XMBData is being used. | |||||
* @return indication of success; main cause for failure is attempting to | |||||
* load a partially valid XMB file (e.g. if the game was interrupted | |||||
* while writing it), which we detect by checking the magic string. | |||||
* It also fails when trying to load an XMB file with a different version. | |||||
*/ | |||||
bool Initialise(const XMBStorage& doc); | |||||
// Returns the root element | |||||
XMBElement GetRoot() const; | |||||
// Returns internal ID for a given element/attribute string. | |||||
int GetElementID(const char* Name) const; | |||||
int GetAttributeID(const char* Name) const; | |||||
// Returns element/attribute string for a given internal ID. | |||||
const char* GetElementString(const int ID) const; | |||||
const char* GetAttributeString(const int ID) const; | |||||
std::string_view GetElementStringView(const int ID) const; | |||||
std::string_view GetAttributeStringView(const int ID) const; | |||||
private: | |||||
const char* m_Pointer; | |||||
int m_ElementNameCount; | |||||
int m_AttributeNameCount; | |||||
const char* m_ElementPointer; | |||||
const char* m_AttributePointer; | |||||
}; | |||||
class XMBElement | |||||
{ | |||||
public: | |||||
XMBElement() | |||||
: m_Pointer(0) {} | |||||
XMBElement(const char* offset) | |||||
: m_Pointer(offset) {} | |||||
int GetNodeName() const; | |||||
XMBElementList GetChildNodes() const; | |||||
XMBAttributeList GetAttributes() const; | |||||
CStr8 GetText() const; | |||||
// Returns the line number of the text within this element, | |||||
// or -1 if there is no text | |||||
int GetLineNumber() const; | |||||
private: | |||||
// Pointer to the start of the node | |||||
const char* m_Pointer; | |||||
}; | |||||
class XMBElementList | |||||
{ | |||||
public: | |||||
XMBElementList(const char* offset, size_t count, const char* endoffset) | |||||
: m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {} | |||||
// Get first element in list with the given name. | |||||
// Performance is linear in the number of elements in the list. | |||||
XMBElement GetFirstNamedItem(const int ElementName) const; | |||||
// Linear in the number of elements in the list | |||||
XMBElement operator[](size_t id); // returns Children[id] | |||||
class iterator | |||||
{ | |||||
public: | |||||
typedef ptrdiff_t difference_type; | |||||
typedef XMBElement value_type; | |||||
typedef XMBElement reference; // Because we need to construct the object | |||||
typedef XMBElement pointer; // Because we need to construct the object | |||||
typedef std::forward_iterator_tag iterator_category; | |||||
iterator(size_t size, const char* ptr, const char* endptr = NULL) | |||||
: m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {} | |||||
XMBElement operator*() const { return XMBElement(m_CurPointer); } | |||||
XMBElement operator->() const { return **this; } | |||||
iterator& operator++(); | |||||
bool operator==(const iterator& rhs) const | |||||
{ | |||||
return m_Size == rhs.m_Size && | |||||
m_CurItemID == rhs.m_CurItemID && | |||||
m_CurPointer == rhs.m_CurPointer; | |||||
} | |||||
bool operator!=(const iterator& rhs) const { return !(*this == rhs); } | |||||
private: | |||||
size_t m_Size; | |||||
size_t m_CurItemID; | |||||
const char* m_CurPointer; | |||||
}; | |||||
iterator begin() { return iterator(m_Size, m_Pointer); } | |||||
iterator end() { return iterator(m_Size, m_Pointer, m_EndPointer); } | |||||
size_t size() const { return m_Size; } | |||||
bool empty() const { return m_Size == 0; } | |||||
private: | |||||
size_t m_Size; | |||||
const char* m_Pointer; | |||||
// For optimised sequential access: | |||||
size_t m_CurItemID; | |||||
const char* m_CurPointer; | |||||
const char* m_EndPointer; | |||||
}; | |||||
struct XMBAttribute | |||||
{ | |||||
XMBAttribute() {} | |||||
XMBAttribute(int name, const CStr8& value) | |||||
: Name(name), Value(value) {}; | |||||
int Name; | |||||
CStr8 Value; // UTF-8 encoded | |||||
}; | |||||
class XMBAttributeList | |||||
{ | |||||
public: | |||||
XMBAttributeList(const char* offset, size_t count, const char* endoffset) | |||||
: m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {} | |||||
// Get the attribute value directly | |||||
CStr8 GetNamedItem(const int AttributeName) const; | |||||
// Linear in the number of elements in the list | |||||
XMBAttribute operator[](size_t id); // returns Children[id] | |||||
class iterator | |||||
{ | |||||
public: | |||||
typedef ptrdiff_t difference_type; | |||||
typedef XMBAttribute value_type; | |||||
typedef XMBAttribute reference; // Because we need to construct the object | |||||
typedef XMBAttribute pointer; // Because we need to construct the object | |||||
typedef std::forward_iterator_tag iterator_category; | |||||
iterator(size_t size, const char* ptr, const char* endptr = NULL) | |||||
: m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {} | |||||
XMBAttribute operator*() const; | |||||
XMBAttribute operator->() const { return **this; } | |||||
iterator& operator++(); | |||||
bool operator==(const iterator& rhs) const | |||||
{ | |||||
return m_Size == rhs.m_Size && | |||||
m_CurItemID == rhs.m_CurItemID && | |||||
m_CurPointer == rhs.m_CurPointer; | |||||
} | |||||
bool operator!=(const iterator& rhs) const { return !(*this == rhs); } | |||||
private: | |||||
size_t m_Size; | |||||
size_t m_CurItemID; | |||||
const char* m_CurPointer; | |||||
}; | |||||
iterator begin() const { return iterator(m_Size, m_Pointer); } | |||||
iterator end() const { return iterator(m_Size, m_Pointer, m_EndPointer); } | |||||
size_t size() const { return m_Size; } | |||||
bool empty() const { return m_Size == 0; } | |||||
private: | |||||
size_t m_Size; | |||||
// Pointer to start of attribute list | |||||
const char* m_Pointer; | |||||
// For optimised sequential access: | |||||
size_t m_CurItemID; | |||||
const char* m_CurPointer; | |||||
const char* m_EndPointer; | |||||
}; | |||||
#endif // INCLUDED_XEROXMB |
Wildfire Games · Phabricator