LCOV - code coverage report
Current view: top level - source/ps/XML - XeroXMB.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 22 24 91.7 %
Date: 2021-04-16 20:41:45 Functions: 0 6 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2020 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             : /*
      19             :     Xeromyces - XMB reading library
      20             : 
      21             : Brief outline:
      22             : 
      23             : XMB is a binary representation of XML, with some limitations
      24             : but much more efficiency (particularly for loading simple data
      25             : classes that don't need much initialisation).
      26             : 
      27             : Main limitations:
      28             :  * Can't correctly handle mixed text/elements inside elements -
      29             :    "<div> <b> Text </b> </div>" and "<div> Te<b/>xt </div>" are
      30             :    considered identical.
      31             :  * Tries to avoid using strings - you usually have to load the
      32             :    numeric IDs and use them instead.
      33             : 
      34             : Theoretical file structure:
      35             : 
      36             : XMB_File {
      37             :     char Header[4]; // because everyone has one; currently "XMB0"
      38             :     u32 Version;
      39             : 
      40             :     int ElementNameCount;
      41             :     ZStr8 ElementNames[];
      42             : 
      43             :     int AttributeNameCount;
      44             :     ZStr8 AttributeNames[];
      45             : 
      46             :     XMB_Node Root;
      47             : }
      48             : 
      49             : XMB_Node {
      50             : 0)  int Length; // of entire struct, so it can be skipped over
      51             : 
      52             : 4)  int ElementName;
      53             : 
      54             : 8)  int AttributeCount;
      55             : 12) int ChildCount;
      56             : 
      57             : 16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes)
      58             : 20) XMB_Text Text;
      59             :     XMB_Attribute Attributes[];
      60             :     XMB_Node Children[];
      61             : 
      62             : }
      63             : 
      64             : XMB_Attribute {
      65             :     int Name;
      66             :     ZStr8 Value;
      67             : }
      68             : 
      69             : ZStr8 {
      70             :     int Length; // in bytes
      71             :     char* Text; // null-terminated UTF8
      72             : }
      73             : 
      74             : XMB_Text {
      75             : 20) int Length; // 0 if there's no text, else 4+sizeof(Text) in bytes including terminator
      76             :     // If Length != 0:
      77             : 24) int LineNumber; // for e.g. debugging scripts
      78             : 28) char* Text; // null-terminated UTF8
      79             : }
      80             : 
      81             : */
      82             : 
      83             : #ifndef INCLUDED_XEROXMB
      84             : #define INCLUDED_XEROXMB
      85             : 
      86             : // Define to use a std::map for name lookups rather than a linear search.
      87             : // (The map is usually slower.)
      88             : //#define XERO_USEMAP
      89             : 
      90             : #include <string>
      91             : 
      92             : #ifdef XERO_USEMAP
      93             : # include <map>
      94             : #endif
      95             : 
      96             : #include "ps/CStr.h"
      97             : 
      98             : // File headers, to make sure it doesn't try loading anything other than an XMB
      99             : extern const char* HeaderMagicStr;
     100             : extern const char* UnfinishedHeaderMagicStr;
     101             : extern const u32 XMBVersion;
     102             : 
     103             : class XMBElement;
     104             : class XMBElementList;
     105             : class XMBAttributeList;
     106             : 
     107             : 
     108             : class XMBFile
     109             : {
     110             : public:
     111             : 
     112        1413 :     XMBFile() : m_Pointer(NULL) {}
     113             : 
     114             :     // Initialise from the contents of an XMB file.
     115             :     // FileData must remain allocated and unchanged while
     116             :     // the XMBFile is being used.
     117             :     // @return indication of success; main cause for failure is attempting to
     118             :     // load a partially valid XMB file (e.g. if the game was interrupted
     119             :     // while writing it), which we detect by checking the magic string.
     120             :     // It also fails when trying to load an XMB file with a different version.
     121             :     bool Initialise(const char* FileData);
     122             : 
     123             :     // Returns the root element
     124             :     XMBElement GetRoot() const;
     125             : 
     126             : 
     127             :     // Returns internal ID for a given element/attribute string.
     128             :     int GetElementID(const char* Name) const;
     129             :     int GetAttributeID(const char* Name) const;
     130             : 
     131             :     // For lazy people (e.g. me) when speed isn't vital:
     132             : 
     133             :     // Returns element/attribute string for a given internal ID
     134             :     std::string GetElementString(const int ID) const;
     135             :     std::string GetAttributeString(const int ID) const;
     136             : 
     137             : private:
     138             :     const char* m_Pointer;
     139             : 
     140             : #ifdef XERO_USEMAP
     141             :     std::map<std::string, int> m_ElementNames;
     142             :     std::map<std::string, int> m_AttributeNames;
     143             : #else
     144             :     int m_ElementNameCount;
     145             :     int m_AttributeNameCount;
     146             :     const char* m_ElementPointer;
     147             :     const char* m_AttributePointer;
     148             : #endif
     149             : 
     150             :     std::string ReadZStr8();
     151             : };
     152             : 
     153             : class XMBElement
     154             : {
     155             : public:
     156             :     XMBElement()
     157           0 :         : m_Pointer(0) {}
     158             : 
     159             :     XMBElement(const char* offset)
     160             :         : m_Pointer(offset) {}
     161             : 
     162             :     int GetNodeName() const;
     163             :     XMBElementList GetChildNodes() const;
     164             :     XMBAttributeList GetAttributes() const;
     165             :     CStr8 GetText() const;
     166             :     // Returns the line number of the text within this element,
     167             :     // or -1 if there is no text
     168             :     int GetLineNumber() const;
     169             : 
     170             : private:
     171             :     // Pointer to the start of the node
     172             :     const char* m_Pointer;
     173             : };
     174             : 
     175             : class XMBElementList
     176             : {
     177             : public:
     178             :     XMBElementList(const char* offset, size_t count, const char* endoffset)
     179        2356 :         : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
     180             : 
     181             :     // Get first element in list with the given name.
     182             :     // Performance is linear in the number of elements in the list.
     183             :     XMBElement GetFirstNamedItem(const int ElementName) const;
     184             : 
     185             :     // Linear in the number of elements in the list
     186             :     XMBElement operator[](size_t id); // returns Children[id]
     187             : 
     188             :     class iterator
     189             :     {
     190             :     public:
     191             :         typedef ptrdiff_t difference_type;
     192             :         typedef XMBElement value_type;
     193             :         typedef XMBElement reference; // Because we need to construct the object
     194             :         typedef XMBElement pointer; // Because we need to construct the object
     195             :         typedef std::forward_iterator_tag iterator_category;
     196             : 
     197             :         iterator(size_t size, const char* ptr, const char* endptr = NULL)
     198        2347 :             : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
     199        3784 :         XMBElement operator*() const { return XMBElement(m_CurPointer); }
     200             :         XMBElement operator->() const { return **this; }
     201             :         iterator& operator++();
     202             : 
     203             :         bool operator==(const iterator& rhs) const
     204             :         {
     205       12182 :             return m_Size == rhs.m_Size &&
     206        6091 :                    m_CurItemID == rhs.m_CurItemID &&
     207        2307 :                    m_CurPointer == rhs.m_CurPointer;
     208             :         }
     209       12182 :         bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
     210             :     private:
     211             :         size_t m_Size;
     212             :         size_t m_CurItemID;
     213             :         const char* m_CurPointer;
     214             :     };
     215        2347 :     iterator begin() { return iterator(m_Size, m_Pointer); }
     216        4694 :     iterator end() { return iterator(m_Size, m_Pointer, m_EndPointer); }
     217             : 
     218           4 :     size_t size() const { return m_Size; }
     219           0 :     bool empty() const { return m_Size == 0; }
     220             : 
     221             : private:
     222             :     size_t m_Size;
     223             : 
     224             :     const char* m_Pointer;
     225             : 
     226             :     // For optimised sequential access:
     227             :     size_t m_CurItemID;
     228             :     const char* m_CurPointer;
     229             : 
     230             :     const char* m_EndPointer;
     231             : };
     232             : 
     233             : 
     234        4802 : struct XMBAttribute
     235             : {
     236             :     XMBAttribute() {}
     237             :     XMBAttribute(int name, const CStr8& value)
     238        3376 :         : Name(name), Value(value) {};
     239             : 
     240             :     int Name;
     241             :     CStr8 Value; // UTF-8 encoded
     242             : };
     243             : 
     244             : class XMBAttributeList
     245             : {
     246             : public:
     247             :     XMBAttributeList(const char* offset, size_t count, const char* endoffset)
     248        3087 :         : m_Size(count), m_Pointer(offset), m_CurItemID(0), m_CurPointer(offset), m_EndPointer(endoffset) {}
     249             : 
     250             :     // Get the attribute value directly
     251             :     CStr8 GetNamedItem(const int AttributeName) const;
     252             : 
     253             :     // Linear in the number of elements in the list
     254             :     XMBAttribute operator[](size_t id); // returns Children[id]
     255             : 
     256             :     class iterator
     257             :     {
     258             :     public:
     259             :         typedef ptrdiff_t difference_type;
     260             :         typedef XMBAttribute value_type;
     261             :         typedef XMBAttribute reference; // Because we need to construct the object
     262             :         typedef XMBAttribute pointer; // Because we need to construct the object
     263             :         typedef std::forward_iterator_tag iterator_category;
     264             : 
     265             :         iterator(size_t size, const char* ptr, const char* endptr = NULL)
     266        2741 :             : m_Size(size), m_CurItemID(endptr ? size : 0), m_CurPointer(endptr ? endptr : ptr) {}
     267             :         XMBAttribute operator*() const;
     268             :         XMBAttribute operator->() const { return **this; }
     269             :         iterator& operator++();
     270             : 
     271             :         bool operator==(const iterator& rhs) const
     272             :         {
     273       12164 :             return m_Size == rhs.m_Size &&
     274        6082 :                    m_CurItemID == rhs.m_CurItemID &&
     275        2709 :                    m_CurPointer == rhs.m_CurPointer;
     276             :         }
     277       11784 :         bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
     278             :     private:
     279             :         size_t m_Size;
     280             :         size_t m_CurItemID;
     281             :         const char* m_CurPointer;
     282             :     };
     283        2741 :     iterator begin() const { return iterator(m_Size, m_Pointer); }
     284        5482 :     iterator end() const { return iterator(m_Size, m_Pointer, m_EndPointer); }
     285             : 
     286           2 :     size_t size() const { return m_Size; }
     287             :     bool empty() const { return m_Size == 0; }
     288             : 
     289             : private:
     290             :     size_t m_Size;
     291             : 
     292             :     // Pointer to start of attribute list
     293             :     const char* m_Pointer;
     294             : 
     295             :     // For optimised sequential access:
     296             :     size_t m_CurItemID;
     297             :     const char* m_CurPointer;
     298             : 
     299             :     const char* m_EndPointer;
     300             : };
     301             : 
     302             : #endif // INCLUDED_XEROXMB

Generated by: LCOV version 1.13