xoreos  0.0.5
nftrfont.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 /* Based heavily on the NFTR reader found in the NDS file viewer
26  * and editor Tinke by pleoNeX (<https://github.com/pleonex/tinke>),
27  * which is licensed under the terms of the GPLv3.
28  *
29  * Tinke in turn is based on the NFTR documentation by CUE and
30  * Lyan53 in the Spanish romxhack forums
31  * (<http://romxhack.esforos.com/fuentes-nftr-de-nds-t67>).
32  *
33  * The original copyright note in Tinke reads as follows:
34  *
35  * Copyright (C) 2011 pleoNeX
36  *
37  * This program is free software: you can redistribute it and/or modify
38  * it under the terms of the GNU General Public License as published by
39  * the Free Software Foundation, either version 3 of the License, or
40  * (at your option) any later version.
41  *
42  * This program is distributed in the hope that it will be useful,
43  * but WITHOUT ANY WARRANTY; without even the implied warranty of
44  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45  * GNU General Public License for more details.
46  *
47  * You should have received a copy of the GNU General Public License
48  * along with this program. If not, see <http://www.gnu.org/licenses/>.
49  */
50 
51 #include <cassert>
52 #include <cstring>
53 
54 #include "src/common/scopedptr.h"
55 #include "src/common/util.h"
56 #include "src/common/maths.h"
57 #include "src/common/ustring.h"
58 #include "src/common/strutil.h"
59 #include "src/common/error.h"
60 #include "src/common/readstream.h"
61 #include "src/common/encoding.h"
62 
63 #include "src/aurora/resman.h"
64 
66 
70 
71 static const uint32 kNFTRID = MKTAG('N', 'F', 'T', 'R');
72 static const uint32 kFINFID = MKTAG('F', 'I', 'N', 'F');
73 static const uint32 kCGLPID = MKTAG('C', 'G', 'L', 'P');
74 static const uint32 kCWDHID = MKTAG('C', 'W', 'D', 'H');
75 static const uint32 kCMAPID = MKTAG('C', 'M', 'A', 'P');
76 
77 namespace Graphics {
78 
79 namespace Aurora {
80 
82 }
83 
85  delete data;
86 }
87 
88 
89 NFTRFont::NFTRFont(Common::SeekableReadStream *nftr, bool invertPalette) :
90  _invertPalette(invertPalette), _surface(0) {
91 
92  assert(nftr);
93 
95 
96  load(*nftrEndian);
97 }
98 
99 NFTRFont::NFTRFont(const Common::UString &name, bool invertPalette) :
100  _invertPalette(invertPalette), _surface(0) {
101 
102  Common::SeekableReadStream *nftr = ResMan.getResource(name, ::Aurora::kFileTypeNFTR);
103  if (!nftr)
104  throw Common::Exception("No such font \"%s\"", name.c_str());
105 
107 
108  load(*nftrEndian);
109 }
110 
112 }
113 
115  Header header;
116  readHeader(nftr, header);
117 
118  std::vector<Glyph> glyphs;
119  readGlyphs (nftr, header, glyphs);
120  readWidths (nftr, header, glyphs);
121  readCharMaps(nftr, header, glyphs);
122 
123  drawGlyphs(glyphs);
124 
125  _height = header.height;
126 
127  // Try to find the width of an m. Alternatively, take half of a line's height.
128  std::map<uint32, Char>::const_iterator m = _chars.find('m');
129  _missingWidth = (m != _chars.end()) ? m->second.width : MAX<float>(2.0f, _height / 2);
130 }
131 
133  const uint32 tag = nftr.readUint32();
134  if (tag != kNFTRID)
135  throw Common::Exception("Invalid NFTR file (%s)", Common::debugTag(tag).c_str());
136 
137  const uint16 bom = nftr.readUint16();
138  if (bom != 0xFEFF)
139  throw Common::Exception("Invalid BOM: 0x%04X", (uint) bom);
140 
141  const uint8 versionMinor = nftr.readByte();
142  const uint8 versionMajor = nftr.readByte();
143  if ((versionMajor != 1) || ((versionMinor != 0) && (versionMinor != 1)))
144  throw Common::Exception("Unsupported version %u.%u", versionMajor, versionMinor);
145 
146  const uint32 fileSize = nftr.readUint32();
147  if (fileSize > nftr.size())
148  throw Common::Exception("Size too large (%u > %u)", fileSize, (uint)nftr.size());
149 
150  nftr.skip(2); // Header size
151 
152  header.palMode = nftr.readUint16();
153 
154  readInfo(nftr, header);
155 }
156 
158  /* This section contains global information about the font. */
159 
160  const uint32 tag = nftr.readUint32();
161  if (tag != kFINFID)
162  throw Common::Exception("Invalid info section (%s)", Common::debugTag(tag).c_str());
163 
164  nftr.skip(4); // Section size
165  nftr.skip(1); // Unknown
166 
167  header.height = nftr.readByte();
168 
169  nftr.skip(3); // Unknown
170 
171  header.width = nftr.readByte();
172 
173  nftr.skip(1); // Unknown
174 
175  header.encoding = nftr.readByte();
176 
177  // We only support UTF-32 (0), UTF-16 (1) and CP1252 (3)
178  if ((header.encoding == 2) || (header.encoding > 3))
179  throw Common::Exception("Unsupported encoding (%u)", header.encoding);
180 
181  // Offsets to the different sections
182  header.offsetCGLP = nftr.readUint32() - 8;
183  header.offsetCWDH = nftr.readUint32() - 8;
184  header.offsetCMAP = nftr.readUint32() - 8;
185 }
186 
188  std::vector<Glyph> &glyphs) {
189 
190  /* This section contains information about the glyphs, as well as their graphical data. */
191 
192  nftr.seek(header.offsetCGLP);
193 
194  const uint32 tag = nftr.readUint32();
195  if (tag != kCGLPID)
196  throw Common::Exception("Invalid glyph section (%s)", Common::debugTag(tag).c_str());
197 
198  const uint32 sectionSize = nftr.readUint32();
199 
200  const uint8 width = nftr.readByte(); // Width of the glyph in pixels
201  const uint8 height = nftr.readByte(); // Height of the glyph in pixels
202  const uint16 size = nftr.readUint16(); // Size of the glyph data in bytes
203 
204  nftr.skip(2); // Unknown
205 
206  const uint8 depth = nftr.readByte();
207 
208  if ((depth != 1) && (depth != 2) && (depth != 4) && (depth != 8))
209  throw Common::Exception("Unsupported glyph depth %u", depth);
210 
211  if ((width * height * depth) > (size * 8))
212  throw Common::Exception("Glyph can't fit (%u * %u * %u > %d", width, height, depth, size * 8);
213 
214  const uint8 rotateMode = nftr.readByte();
215  if (rotateMode != 0)
216  throw Common::Exception("Unsupported glyph rotation %u", rotateMode);
217 
218  const uint32 count = (sectionSize - 16) / size;
219  glyphs.resize(count);
220 
221  size_t offset = nftr.pos();
222  for (uint32 i = 0; i < count; i++, offset += size) {
223  glyphs[i].data = new Common::SeekableSubReadStream(&nftr, offset, offset + size);
224 
225  // Copy this information into the glyph to make glyph drawing easier later
226  glyphs[i].palMode = header.palMode;
227  glyphs[i].width = width;
228  glyphs[i].height = height;
229  glyphs[i].depth = depth;
230 
231  // Default advance value, in case the widths section doesn't cover this glyph
232  glyphs[i].advance = header.width + 1;
233 
234  // In case the character map section doesn't cover this glyph
235  glyphs[i].character = 0;
236  }
237 }
238 
240  std::vector<Glyph> &glyphs) {
241 
242  /* This section provides widths for each glyph, which seems to include the number
243  * of pixels in each row, in which column the glyph starts in and the number of
244  * pixels to advance to the next character.
245  *
246  * Or at least that would be logical. Sometimes, the information doesn't quite
247  * fit. Something fishy is going on...
248  */
249 
250  nftr.seek(header.offsetCWDH);
251 
252  const uint32 tag = nftr.readUint32();
253  if (tag != kCWDHID)
254  throw Common::Exception("Invalid widths section (%s)", Common::debugTag(tag).c_str());
255 
256  nftr.skip(4); // Section size
257 
258  const uint16 firstGlyph = nftr.readUint16();
259  const uint16 lastGlyph = nftr.readUint16();
260 
261  nftr.skip(4); // Unknown
262 
263  for (uint32 i = firstGlyph; i <= lastGlyph; i++) {
264  if (i >= glyphs.size())
265  break;
266 
267  nftr.skip(1); // pixel start
268 
269  const uint8 pixelWidth = nftr.readByte() + 1;
270  const uint8 pixelLength = nftr.readByte() + 1;
271 
272  glyphs[i].advance = (pixelWidth == 1) ? pixelLength : pixelWidth;
273  }
274 }
275 
277  std::vector<Glyph> &glyphs) {
278 
279  /* This section provides several character maps, each mapping a range of code points
280  * (in the encoding specified in the header) onto a glyph, in one of three ways:
281  *
282  * - Type 0: Map all code points in the range onto glyphs in order.
283  * For example: map [40, 45] to glyphs [62, 67].
284  * - Type 1: Map all code points in the range onto each one specific glyph
285  * For example: map [40, 45] to { 0, 1, 6, 23, 42, 5 }.
286  * - Type 2: Map each a specific code point onto each a specific glyph
287  * For example. map 40 -> 0, 62 -> 252, 7525 -> 2.
288  *
289  * To make things easier for us, we unroll this information and store the character
290  * it shows (converted to an UTF-32 code point) directly in the Glyph struct.
291  */
292 
293  uint32 nextOffset = header.offsetCMAP;
294  while ((nextOffset != 0) && (nextOffset < nftr.size())) {
295  nftr.seek(nextOffset);
296 
297  const uint32 tag = nftr.readUint32();
298  if (tag != kCMAPID)
299  throw Common::Exception("Invalid character map section (%s)", Common::debugTag(tag).c_str());
300 
301  const uint32 sectionSize = nftr.readUint32();
302 
303  const uint16 firstChar = nftr.readUint16();
304  const uint16 lastChar = nftr.readUint16();
305  if (firstChar > lastChar)
306  throw Common::Exception("Invalid character map range (%u - %u)", firstChar, lastChar);
307 
308  const uint32 type = nftr.readUint32();
309  if (type > 2)
310  throw Common::Exception("Invalid character map type %u", type);
311 
312  nextOffset = nftr.readUint32() - 8;
313 
314  uint32 count, to, from;
315  switch (type) {
316  case 0:
317  count = lastChar - firstChar + 1;
318 
319  to = nftr.readUint16();
320  if ((to + count) > glyphs.size())
321  throw Common::Exception("Invalid character map range (%u + %u > %u)",
322  to, count, (uint)glyphs.size());
323 
324  for (uint32 i = 0; i < count; i++)
325  glyphs[to + i].character = convertToUTF32(firstChar + i, header.encoding);
326  break;
327 
328  case 1:
329  count = (sectionSize - 20 - 2) / 2;
330 
331  for (uint32 i = 0; i < count; i++) {
332  to = nftr.readUint16();
333  if (to == 0xFFFF)
334  continue;
335 
336  if (to >= glyphs.size())
337  throw Common::Exception("Invalid character map range (%u > %u)",
338  to, (uint)glyphs.size());
339 
340  glyphs[to].character = convertToUTF32(firstChar + i, header.encoding);
341  }
342  break;
343 
344  case 2:
345  count = nftr.readUint16();
346 
347  for (uint32 i = 0; i < count; i++) {
348  from = nftr.readUint16();
349  to = nftr.readUint16();
350  if (to == 0xFFFF)
351  continue;
352 
353  if (to >= glyphs.size())
354  throw Common::Exception("Invalid character map range (%u > %u)",
355  to, (uint)glyphs.size());
356 
357  glyphs[to].character = convertToUTF32(from, header.encoding);
358  }
359  break;
360  }
361  }
362 }
363 
364 float NFTRFont::getWidth(uint32 c) const {
365  std::map<uint32, Char>::const_iterator cC = _chars.find(c);
366  if (cC == _chars.end())
367  return _missingWidth;
368 
369  return cC->second.width;
370 }
371 
372 float NFTRFont::getHeight() const {
373  return _height;
374 }
375 
376 void NFTRFont::drawMissing() const {
377  TextureMan.set();
378 
379  const float width = _missingWidth - 1.0f;
380 
381  glBegin(GL_QUADS);
382  glVertex2f(0.0f , 0.0f);
383  glVertex2f(width, 0.0f);
384  glVertex2f(width, _height);
385  glVertex2f(0.0f , _height);
386  glEnd();
387 
388  glTranslatef(width + 1.0f, 0.0f, 0.0f);
389 }
390 
391 void NFTRFont::draw(uint32 c) const {
392  std::map<uint32, Char>::const_iterator cC = _chars.find(c);
393  if (cC == _chars.end()) {
394  drawMissing();
395  return;
396  }
397 
398  TextureMan.set(_texture);
399 
400  glBegin(GL_QUADS);
401  for (int i = 0; i < 4; i++) {
402  glTexCoord2f(cC->second.tX[i], cC->second.tY[i]);
403  glVertex2f (cC->second.vX[i], cC->second.vY[i]);
404  }
405  glEnd();
406 
407  glTranslatef(cC->second.width, 0.0f, 0.0f);
408 }
409 
410 void NFTRFont::drawGlyphs(const std::vector<Glyph> &glyphs) {
411  if (glyphs.empty())
412  return;
413 
414  /* Calculate the optimal-ish size of a texture holding all glyphs.
415  * This assumes that all glyphs are of the same size, which is true
416  * for NFTR fonts. Or at least, they are bound to a maximal size,
417  * which is good enough for us. Due to the fishy-ness in the glyph
418  * widths section (see above), we don't really know the true sizes
419  * of the glyphs anyway.
420  */
421 
422  const uint32 width = glyphs[0].width;
423  const uint32 height = glyphs[0].height;
424  const uint32 pixels = glyphs.size() * width * height;
425 
426  const uint32 textureLength = NEXTPOWER2((uint32) ceil(sqrt(pixels)));
427  if (textureLength > 2048)
428  throw Common::Exception("Too many glyphs (%u @ %ux%u)", (uint)glyphs.size(), width, height);
429 
430  assert(((textureLength / width) * (textureLength / height)) >= glyphs.size());
431 
432  _surface = new Surface(textureLength, textureLength);
434 
435  uint32 x = 0, y = 0;
436  for (std::vector<Glyph>::const_iterator g = glyphs.begin(); g != glyphs.end(); ++g) {
437  drawGlyph(*g, *_surface, x, y);
438 
439  std::pair<std::map<uint32, Char>::iterator, bool> result;
440  result = _chars.insert(std::make_pair(g->character, Char()));
441 
442  Char &ch = result.first->second;
443 
444  ch.width = g->advance;
445 
446  ch.vX[0] = 0.00f; ch.vY[0] = 0.00f;
447  ch.vX[1] = width; ch.vY[1] = 0.00f;
448  ch.vX[2] = width; ch.vY[2] = height;
449  ch.vX[3] = 0.00f; ch.vY[3] = height;
450 
451  const float tX = (float) x / (float) textureLength;
452  const float tY = (float) y / (float) textureLength;
453  const float tW = (float) width / (float) textureLength;
454  const float tH = (float) height / (float) textureLength;
455 
456  ch.tX[0] = tX; ch.tY[0] = tY + tH;
457  ch.tX[1] = tX + tW; ch.tY[1] = tY + tH;
458  ch.tX[2] = tX + tW; ch.tY[2] = tY;
459  ch.tX[3] = tX; ch.tY[3] = tY;
460 
461  x += width;
462  if ((x + width) > textureLength) {
463  x = 0;
464  y += height;
465  }
466  }
467 
469 }
470 
471 void NFTRFont::drawGlyph(const Glyph &glyph, Surface &surface, uint32 x, uint32 y) {
472  glyph.data->seek(0);
473 
474  const uint32 maxColors = 1 << glyph.depth;
475 
476  byte *dest = surface.getData() + (y * surface.getWidth() + x) * 4;
477 
478  uint16 data = 0xFF00;
479  for (uint32 i = 0; i < glyph.height; i++, dest += 4 * surface.getWidth()) {
480  byte *destRow = dest;
481 
482  for (uint32 j = 0; j < glyph.width; j++) {
483  // If there's only our canaries left, read the next byte of data
484  if (data == 0xFF00)
485  data = (glyph.data->readByte() << 8) | 0x00FF;
486 
487  const uint16 pixel = data >> (16 - glyph.depth);
488 
489  /* Depending on the NFTR header, the glyphs' palette can differ:
490  * In the one mode, we have a full white to black gradient. This
491  * is used in fonts that use an outline or a shadow. In the other
492  * mode, the lightest color is a gray, so we have more gray values
493  * to work with. This is used in fonts that anti-alias, especially
494  * in higher-res Asian fonts.
495  *
496  * Note that we also guard against a potential division by 0. */
497  uint16 value = 0;
498  if (glyph.palMode >= 7)
499  value = (maxColors == 2) ? 0xFF : (255 * (pixel ) / (maxColors - 1));
500  else
501  value = (maxColors == 2) ? 0xFF : (255 * (pixel - 1) / (maxColors - 2));
502 
503  /* If we were requested to, invert the value. Note: the uninverted color
504  * of a font is neither definitely black or definitely white. Both exists,
505  * so an NFTR itself doesn't know how it's supposed to be used. */
506  if (_invertPalette)
507  value = 255 - value;
508 
509  *destRow++ = value;
510  *destRow++ = value;
511  *destRow++ = value;
512 
513  // No matter the inversion or the palette mode, 0 is always transparent
514  *destRow++ = pixel == 0 ? 0 : 0xFF;
515 
516  data <<= glyph.depth;
517  }
518  }
519 }
520 
522  /* A rather hacky, ugly and slow way to convert a code point in various
523  * encodings to a UTF-32 code point. Yes, this calls iconv() for every
524  * single code point not already in UTF-32.
525  *
526  * This bit is also the sole reason we don't support NFTR in Shift-JIS
527  * encodings. Sonic doesn't seem to use them... */
528 
529  if (encoding == 0)
530  return codePoint;
531 
532  byte data[4];
533  memset(data, 0, sizeof(data));
534 
536 
537  if (encoding == 1) {
539 
540  WRITE_LE_UINT16(data, codePoint);
541  } else if (encoding == 3) {
543 
544  data[0] = codePoint;
545  }
546 
547  Common::UString str = Common::readString(data, 4, e);
548  if (str.empty())
549  return 0;
550 
551  return *str.begin();
552 }
553 
554 } // End of namespace Aurora
555 
556 } // End of namespace Graphics
void readHeader(Common::SeekableSubReadStreamEndian &nftr, Header &header)
Definition: nftrfont.cpp:132
#define ResMan
Shortcut for accessing the sound manager.
Definition: resman.h:557
void readCharMaps(Common::SeekableSubReadStreamEndian &nftr, Header &header, std::vector< Glyph > &glyphs)
Definition: nftrfont.cpp:276
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
byte * getData()
Definition: surface.cpp:61
static const uint32 kCGLPID
Definition: nftrfont.cpp:73
void drawGlyphs(const std::vector< Glyph > &glyphs)
Definition: nftrfont.cpp:410
static const uint32 kCMAPID
Definition: nftrfont.cpp:75
This is a wrapper around SeekableSubReadStream, but it adds non-endian read methods whose endianness ...
Definition: readstream.h:383
float getWidth(uint32 c) const
Return the width of a character.
Definition: nftrfont.cpp:364
static const uint32 kCWDHID
Definition: nftrfont.cpp:74
A class holding an UTF-8 string.
Definition: ustring.h:48
#define TextureMan
Shortcut for accessing the texture manager.
Definition: textureman.h:127
virtual size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)=0
Sets the stream position indicator for the stream.
uint8_t uint8
Definition: types.h:200
static Texture * create(const Common::UString &name, bool deswizzle=false)
Create a texture from this image resource.
Definition: texture.cpp:323
The Aurora texture manager.
Common::SeekableReadStream * data
Definition: nftrfont.h:70
Mathematical helpers.
NFTRFont(Common::SeekableReadStream *nftr, bool invertPalette=false)
Definition: nftrfont.cpp:89
void readGlyphs(Common::SeekableSubReadStreamEndian &nftr, Header &header, std::vector< Glyph > &glyphs)
Definition: nftrfont.cpp:187
iterator begin() const
Definition: ustring.cpp:253
UTF-16 LE (little endian).
Definition: encoding.h:44
size_t pos() const
Obtains the current value of the stream position indicator of the stream.
Definition: readstream.cpp:140
A font character.
Definition: nftrfont.h:87
static uint32 NEXTPOWER2(uint32 x)
Round up to the next power of 2.
Definition: util.h:84
float getHeight() const
Return the height of a character.
Definition: nftrfont.cpp:372
void draw(uint32 c) const
Draw this character.
Definition: nftrfont.cpp:391
Utility templates and functions for working with strings and streams.
Exception that provides a stack of explanations.
Definition: error.h:36
size_t size() const
Obtains the total size of the stream, measured in bytes.
Definition: readstream.cpp:144
A simple scoped smart pointer template.
Basic exceptions to throw.
std::map< uint32, Char > _chars
Definition: nftrfont.h:100
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
int getWidth() const
Definition: surface.cpp:53
size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)
Sets the stream position indicator for the stream.
Definition: readstream.cpp:148
uint16_t uint16
Definition: types.h:202
Utility templates and functions.
void drawGlyph(const Glyph &glyph, Surface &surface, uint32 x, uint32 y)
Definition: nftrfont.cpp:471
TextureHandle _texture
Definition: nftrfont.h:98
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
void readWidths(Common::SeekableSubReadStreamEndian &nftr, Header &header, std::vector< Glyph > &glyphs)
Definition: nftrfont.cpp:239
Nintendo&#39;s NFTR font, found in Sonic.
Utility functions for working with differing string encodings.
Encoding
Definition: encoding.h:37
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
StackException Exception
Definition: error.h:59
void readInfo(Common::SeekableSubReadStreamEndian &nftr, Header &header)
Definition: nftrfont.cpp:157
Basic reading stream interfaces.
Unicode string handling.
An image surface, in BGRA format.
A texture as used in the Aurora engines.
static const uint32 kNFTRID
Definition: nftrfont.cpp:71
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
void load(Common::SeekableSubReadStreamEndian &nftr)
Definition: nftrfont.cpp:114
static uint32 convertToUTF32(uint16 codePoint, uint8 encoding)
Definition: nftrfont.cpp:521
UString readString(SeekableReadStream &stream, Encoding encoding)
Read a string with the given encoding of a stream.
Definition: encoding.cpp:287
Windows codepage 1252 (Western European, Latin alphabet).
Definition: encoding.h:51
SeekableSubReadStream provides access to a SeekableReadStream restricted to the range [begin...
Definition: readstream.h:359
static const uint32 kFINFID
Definition: nftrfont.cpp:72
static Common::SeekableSubReadStreamEndian * open(Common::SeekableReadStream &stream)
Treat this stream as a Nitro file and return an endian&#39;d stream according to its BOM.
Definition: nitrofile.cpp:52
Interface for a seekable & readable data stream.
Definition: readstream.h:265
byte readByte()
Read an unsigned byte from the stream and return it.
Definition: readstream.h:92
The global resource manager for Aurora resources.
uint8 byte
Definition: types.h:209
unsigned int uint
Definition: types.h:211