285 lines
7.3 KiB
C++
285 lines
7.3 KiB
C++
/* 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 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
|