| 1 | Consider a hypothetical XML-based image format: |
| 2 | |
| 3 | {{{ |
| 4 | #!xml |
| 5 | <?xml version="1.0" encoding="utf-8"?> |
| 6 | <image> |
| 7 | <width>2</width> |
| 8 | <height>2</height> |
| 9 | <row> |
| 10 | <pixel red="255" green="0" blue="255"/> |
| 11 | <pixel red="255" green="0" blue="128"/> |
| 12 | </row> |
| 13 | <row> |
| 14 | <pixel red="255" green="0" blue="128"/> |
| 15 | <pixel red="255" green="0" blue="0"/> |
| 16 | </row> |
| 17 | </image> |
| 18 | }}} |
| 19 | |
| 20 | The code to read it would look something like: |
| 21 | {{{ |
| 22 | #!c |
| 23 | void ReadImage(const char* Filename) |
| 24 | { |
| 25 | |
| 26 | // First you need to load an XML file from disk. It uses the VFS system, |
| 27 | // so put all the XML files inside data/mods/official/<Filename>. |
| 28 | // The XML is automatically converted to an XMB when necessary. |
| 29 | // Load() returns either PSRETURN_OK or a PSRETURN_Xeromyces_* |
| 30 | |
| 31 | CXeromyces XeroFile; |
| 32 | if (XeroFile.Load(Filename) != PSRETURN_OK) |
| 33 | // Loading failed - respond in whatever way is appropriate. |
| 34 | return; |
| 35 | |
| 36 | // Rather than doing a large number of string comparisons, Xeromyces |
| 37 | // gives a unique numeric ID to each element and attribute name in a |
| 38 | // file; the numbers must therefore be read before you can understand |
| 39 | // the file's content. |
| 40 | // |
| 41 | // The element/attribute names *must* be specified here in lowercase. |
| 42 | // (The system is case-sensitive; the XML files are automatically |
| 43 | // converted to lowercase, but strings in the code aren't.) |
| 44 | // |
| 45 | // If an element/attribute doesn't exist in that particular file, |
| 46 | // it will return an ID of -1. |
| 47 | |
| 48 | int el_image = XeroFile.getElementID("image"); |
| 49 | int el_width = XeroFile.getElementID("width"); |
| 50 | int el_height = XeroFile.getElementID("height"); |
| 51 | int el_row = XeroFile.getElementID("row"); |
| 52 | |
| 53 | // All XML files have a single root element (in this example it's |
| 54 | // the <image>). The "XMBElement" class provides access to the |
| 55 | // element. |
| 56 | |
| 57 | XMBElement Root = XeroFile.getRoot(); |
| 58 | |
| 59 | // If you're paranoid, check that you're actually reading the |
| 60 | // correct type of XML file. |
| 61 | assert(Root.getNodeName() == el_image); |
| 62 | |
| 63 | // Some uninteresting things to help with the example: |
| 64 | int width, height; |
| 65 | pixel pixels[MAX_IMAGE_SIZE][MAX_IMAGE_SIZE]; |
| 66 | int y = 0; |
| 67 | |
| 68 | // You now iterate through each child element of the root element - |
| 69 | // in this example, the children are <width>, <height> and two <row>s. |
| 70 | |
| 71 | XMBElementList RootChildren = Root.getChildNodes(); |
| 72 | |
| 73 | for (int i = 0; i < RootChildren.Count; ++i) |
| 74 | { |
| 75 | XMBElement Child = RootChildren.item(i); |
| 76 | |
| 77 | // Now you check what type of element it is, and respond appropriately. |
| 78 | |
| 79 | int ChildName = Child.getNodeName(); |
| 80 | |
| 81 | if (ChildName == el_width) |
| 82 | { |
| 83 | CStr val (Child.getText()); |
| 84 | width = val.ToInt(); |
| 85 | } |
| 86 | // It's possible to use strings rather than the IDs, although |
| 87 | // it isn't particularly efficient. |
| 88 | // getElementString translates an ID back into a string. |
| 89 | else if (XeroFile.getElementString(ChildName) == "height") |
| 90 | { |
| 91 | CStr val (Child.getText()); |
| 92 | height = val.ToInt(); |
| 93 | } |
| 94 | else if (ChildName == el_row) |
| 95 | { |
| 96 | // Since XML files tend to be recursive, it's generally nicer |
| 97 | // to split things into several functions. |
| 98 | ReadPixelRow(Child, XeroFile, pixels[y++]); |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | |
| 104 | // XMBElements are only 4 bytes, so there's no need to pass them |
| 105 | // around in a more efficient way. |
| 106 | void ReadPixelRow(XMBElement Row, CXeromyces& XeroFile, pixel* Pixels) |
| 107 | { |
| 108 | |
| 109 | // Load the IDs for the elements/attributes that are |
| 110 | // going to be used in this pixel. |
| 111 | int el_pixel = XeroFile.getElementID("pixel"); |
| 112 | int at_red = XeroFile.getAttributeID("red"); |
| 113 | int at_green = XeroFile.getAttributeID("green"); |
| 114 | int at_blue = XeroFile.getAttributeID("blue"); |
| 115 | |
| 116 | // Iterate through the children again |
| 117 | XMBElementList Children = Row.getChildNodes(); |
| 118 | for (int x = 0; x < Children.Count; ++x) |
| 119 | { |
| 120 | XMBElement Pixel = Children.item(x); |
| 121 | |
| 122 | assert(Pixel.getNodeName() == el_pixel); |
| 123 | |
| 124 | // You can iterate through the attributes in the same way: |
| 125 | XMBAttributeList Attrs = Pixel.getAttributes(); |
| 126 | for (int i = 0; i < Attrs.Count; ++i) |
| 127 | { |
| 128 | XMBAttribute Attr = Attrs.item(i); |
| 129 | |
| 130 | if (Attr.Name == at_red) |
| 131 | { |
| 132 | CStr val (Attr.Value); |
| 133 | Pixels[x].r = val.ToInt(); |
| 134 | } |
| 135 | else if (Attr.Name == at_green) |
| 136 | { |
| 137 | CStr val (Attr.Value); |
| 138 | Pixels[x].g = val.ToInt(); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // You can also get attributes by asking for a particular |
| 143 | // one, which is sometimes more convenient. |
| 144 | // getNamedItem() returns an empty string if the attribute |
| 145 | // has not been specified in the file. |
| 146 | |
| 147 | CStr val (Attrs.getNamedItem(at_blue)); |
| 148 | Pixels[x].b = val.ToInt(); |
| 149 | } |
| 150 | } |
| 151 | }}} |