xoreos  0.0.5
cbgt.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 #include <cstdio>
26 #include <cstring>
27 
28 #include "src/common/util.h"
29 #include "src/common/error.h"
30 #include "src/common/readstream.h"
31 
32 #include "src/aurora/2dafile.h"
33 #include "src/aurora/smallfile.h"
34 
36 
37 namespace Graphics {
38 
42  cbgt(&c), pal(&p), twoda(&t) {
43 }
44 
45 
48 
49  ReadContext ctx(cbgt, pal, twoda);
50 
51  try {
52  load(ctx);
53  } catch (Common::Exception &e) {
54  e.add("Failed reading CBGT file");
55  throw;
56  }
57 }
58 
60 }
61 
62 void CBGT::load(ReadContext &ctx) {
63  readPalettes(ctx);
64  readPaletteIndices(ctx);
65  readCells(ctx);
66 
67  checkConsistency(ctx);
68 
69  drawImage(ctx);
70 }
71 
73  /* Read the palette data, several plain palettes of 256 BGR555 entries. */
74 
75  try {
76  ctx.pal->seek(0);
77 
78  size_t size = ctx.pal->size();
79  ctx.palettes.reserve((size + 1) / 512);
80 
81  while (size > 0) {
82  const uint32 paletteSize = MIN<size_t>(512, size);
83 
84  ctx.palettes.push_back(new byte[768]);
85  byte *palette = ctx.palettes.back();
86 
87  const uint32 colorCount = (paletteSize / 2) * 3;
88  for (uint32 i = 0; i < colorCount; i += 3) {
89  const uint16 color = ctx.pal->readUint16LE();
90 
91  palette[i + 0] = ((color >> 10) & 0x1F) << 3;
92  palette[i + 1] = ((color >> 5) & 0x1F) << 3;
93  palette[i + 2] = ( color & 0x1F) << 3;
94  }
95 
96  size -= paletteSize;
97  }
98 
99  if (ctx.palettes.empty())
100  throw Common::Exception("No palettes");
101 
102  } catch (Common::Exception &e) {
103  e.add("Failed reading PAL file");
104  throw e;
105  }
106 }
107 
109  /* Read the 2DA file providing the palette index mapping, as well as the width
110  * and height of the final image (by way of number of cells in X and Y direction).
111  *
112  * Basically, the 2DA file contains data in this format:
113  *
114  * 0 1 2 3
115  * 0 palette00.pal palette00.pal palette03.pal palette03.pal
116  * 1 palette00.pal palette00.pal palette03.pal palette03.pal
117  * 2 palette01.pal palette01.pal palette04.pal palette04.pal
118  * 3 palette01.pal palette01.pal palette04.pal palette04.pal
119  * 4 palette02.pal palette02.pal palette05.pal palette05.pal
120  * 5 palette02.pal palette02.pal palette05.pal palette05.pal
121  *
122  * This tells out that
123  * a) There's 4 cells in X direction, so the width of the image is 256 pixels
124  * b) There's 6 cells in Y direction, so the height of the image is 384 pixels
125  * c) Which cell uses which palette
126  */
127 
128  try {
129  Aurora::TwoDAFile twoDA(*ctx.twoda);
130 
131  ctx.width = twoDA.getColumnCount() * 64;
132  ctx.height = twoDA.getRowCount() * 64;
133 
134  if ((ctx.width == 0) || (ctx.width >= 0x8000) || (ctx.height == 0) || (ctx.height >= 0x8000))
135  throw Common::Exception("Dimensions of %ux%u", ctx.width, ctx.height);
136 
137  ctx.maxPaletteIndex = 0;
138 
139  ctx.paletteIndices.reserve(twoDA.getColumnCount() * twoDA.getRowCount());
140  for (uint32 i = 0; i < twoDA.getRowCount(); i++) {
141  const Aurora::TwoDARow &row = twoDA.getRow(i);
142 
143  for (uint32 j = 0; j < twoDA.getColumnCount(); j++) {
144  const Common::UString &palette = row.getString(j);
145 
146  int index = -1;
147 
148  int n = std::sscanf(palette.c_str(), "palette%d.pal", &index);
149  if ((n != 1) || (index < 0))
150  throw Common::Exception("Failed to parse \"%s\" into a palette index", palette.c_str());
151 
152  ctx.paletteIndices.push_back((size_t)index);
153 
154  ctx.maxPaletteIndex = MAX<size_t>(ctx.maxPaletteIndex, index);
155  }
156  }
157 
158  } catch (Common::Exception &e) {
159  e.add("Failed reading 2DA file");
160  throw e;
161  }
162 }
163 
165  /* Read the cell data, each containing 64x64 pixels. Of course, since this
166  * is a *compressed* format, the data is compressed using the LZSS algorithm
167  * also used for .small files. */
168 
169  ctx.cells.reserve(4096);
170 
171  try {
172  // Read the cell offset and sizes
173  for (size_t i = 0; i < 4096; i++) {
174  const uint32 size = ctx.cbgt->readUint16LE();
175  const uint32 offset = ctx.cbgt->readUint16LE() * 512;
176 
177  if (offset < 0x4000)
178  break;
179 
180  ctx.cells.push_back(0);
181  if (size == 0)
182  continue;
183 
184  size_t pos = ctx.cbgt->pos();
185 
186  Common::SeekableSubReadStream cellData(ctx.cbgt, offset, offset + size);
187  ctx.cells.back() = Aurora::Small::decompress(cellData);
188 
189  if (ctx.cells.back()->size() != 4096)
190  throw Common::Exception("Invalid size for cell %u: %u", (uint)i, (uint)ctx.cells.back()->size());
191 
192  ctx.cbgt->seek(pos);
193  }
194 
195  if (ctx.cells.empty())
196  throw Common::Exception("No cells");
197 
198  } catch (Common::Exception &e) {
199  e.add("Failed reading CBGT file");
200  throw e;
201  }
202 }
203 
205  if (ctx.cells.size() != ctx.paletteIndices.size())
206  throw Common::Exception("%u palette indices for %u cells",
207  (uint)ctx.cells.size(), (uint)ctx.paletteIndices.size());
208 
209  if (ctx.maxPaletteIndex >= ctx.palettes.size())
210  throw Common::Exception("Palette index %u out of range (%u)",
211  (uint)ctx.maxPaletteIndex, (uint)ctx.palettes.size());
212 }
213 
214 void CBGT::createImage(uint32 width, uint32 height) {
218 
219  _mipMaps.push_back(new MipMap);
220  _mipMaps.back()->width = width;
221  _mipMaps.back()->height = height;
222  _mipMaps.back()->size = width * height * 4;
223 
224  _mipMaps.back()->data.reset(new byte[_mipMaps.back()->size]);
225  std::memset(_mipMaps.back()->data.get(), 0, _mipMaps.back()->size);
226 }
227 
229  /* Draw the actual image data.
230  *
231  * The image is made up of 64x64 pixel cells, each consisting of 64 8x8 pixel tiles.
232  * This would be 2x1 cells, each with 64 tiles, and each of those would be 8x8 pixel wide:
233  *
234  * C0T00 C0T01 C0T02 C0T03 C0T04 C0T05 C0T06 C0T07 C1T00 C1T01 C1T02 C1T03 C1T04 C1T05 C1T06 C1T07
235  * C0T08 C0T09 C0T10 C0T11 C0T12 C0T13 C0T14 C0T15 C1T08 C1T09 C1T10 C1T11 C1T12 C1T13 C1T14 C1T15
236  * C0T16 C0T17 C0T18 C0T19 C0T20 C0T21 C0T22 C0T23 C1T16 C1T17 C1T18 C1T19 C1T20 C1T21 C1T22 C1T23
237  * C0T24 C0T25 C0T26 C0T27 C0T28 C0T29 C0T30 C0T31 C1T24 C1T25 C1T26 C1T27 C1T28 C1T29 C1T30 C1T31
238  * C0T32 C0T33 C0T34 C0T35 C0T36 C0T37 C0T38 C0T39 C1T32 C1T33 C1T34 C1T35 C1T36 C1T37 C1T38 C1T39
239  * C0T40 C0T41 C0T42 C0T43 C0T44 C0T45 C0T46 C0T47 C1T40 C1T41 C1T42 C1T43 C1T44 C1T45 C1T46 C1T47
240  * C0T48 C0T49 C0T50 C0T51 C0T52 C0T53 C0T54 C0T55 C1T48 C1T49 C1T50 C1T51 C1T52 C1T53 C1T54 C1T55
241  * C0T56 C0T57 C0T58 C0T59 C0T60 C0T61 C0T62 C0T63 C1T56 C1T57 C1T58 C1T59 C1T60 C1T61 C1T62 C1T63
242  *
243  * This unswizzling of the data makes it...a bit complex. */
244 
245  createImage(ctx.width, ctx.height);
246 
247 
248  const uint32 cellWidth = 64;
249  const uint32 cellHeight = 64;
250  const uint32 cellsX = ctx.width / cellWidth;
251 
252  const uint32 tileWidth = 8;
253  const uint32 tileHeight = 8;
254  const uint32 tilesX = cellWidth / tileWidth;
255  const uint32 tilesY = cellHeight / tileHeight;
256 
257  byte *data = _mipMaps.back()->data.get();
258  for (size_t i = 0; i < ctx.cells.size(); i++) {
259  Common::SeekableReadStream *cell = ctx.cells[i];
260  if (!cell)
261  continue;
262 
263  const uint32 xC = i % cellsX;
264  const uint32 yC = i / cellsX;
265 
266  const byte *palette = ctx.palettes[ctx.paletteIndices[i]];
267  const bool is0Transp = (palette[0] == 0xF8) && (palette[1] == 0x00) && (palette[2] == 0xF8);
268 
269  // Pixel position of this cell within the big image
270  const uint32 imagePos = yC * cellHeight * ctx.width + xC * cellWidth;
271 
272  // Go over all tiles
273  for (uint32 yT = 0; yT < tilesY; yT++) {
274  for (uint32 xT = 0; xT < tilesX; xT++) {
275 
276  // Position of the tile within the cell
277  const uint32 tilePos = xT * tileWidth + yT * tileHeight * ctx.width;
278 
279  // Go over all pixels in the tile
280  for (uint32 y = 0; y < tileHeight; y++) {
281  for (uint32 x = 0; x < tileWidth; x++) {
282 
283  // Position of the pixel within the tile
284  const uint32 pos = imagePos + tilePos + x + y * ctx.width;
285  const uint8 pixel = cell->readByte();
286 
287  if (pos > (ctx.width * ctx.height))
288  continue;
289 
290  data[pos * 4 + 0] = palette[pixel * 3 + 0];
291  data[pos * 4 + 1] = palette[pixel * 3 + 1];
292  data[pos * 4 + 2] = palette[pixel * 3 + 2];
293  data[pos * 4 + 3] = ((pixel == 0) && is0Transp) ? 0x00 : 0xFF;
294 
295  }
296  }
297 
298  }
299  }
300 
301  }
302 }
303 
304 } // End of namespace Graphics
Class to hold the two-dimensional array of a 2DA file.
Definition: 2dafile.h:124
uint16 readUint16LE()
Read an unsigned 16-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:122
void checkConsistency(ReadContext &ctx)
Definition: cbgt.cpp:204
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
const Common::UString & getString(size_t column) const
Return the contents of a cell as a string.
Definition: 2dafile.cpp:59
A class holding an UTF-8 string.
Definition: ustring.h:48
Common::SeekableReadStream * twoda
Definition: cbgt.h:71
static void decompress(Common::ReadStream &small, Common::WriteStream &out)
Definition: smallfile.cpp:255
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
void drawImage(ReadContext &ctx)
Definition: cbgt.cpp:228
void readPalettes(ReadContext &ctx)
Definition: cbgt.cpp:72
ReadContext(Common::SeekableReadStream &c, Common::SeekableReadStream &p, Common::SeekableReadStream &t)
Definition: cbgt.cpp:39
void load(ReadContext &ctx)
Definition: cbgt.cpp:62
size_t getRowCount() const
Return the number of rows in the array.
Definition: 2dafile.cpp:400
PixelDataType _dataType
Definition: decoder.h:124
Exception that provides a stack of explanations.
Definition: error.h:36
Basic exceptions to throw.
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
size_t getColumnCount() const
Return the number of columns in the array.
Definition: 2dafile.cpp:404
uint16_t uint16
Definition: types.h:202
PixelFormat _format
Definition: decoder.h:122
Utility templates and functions.
Handling BioWare&#39;s 2DAs (two-dimensional array).
PixelFormatRaw _formatRaw
Definition: decoder.h:123
StackException Exception
Definition: error.h:59
virtual size_t size() const =0
Obtains the total size of the stream, measured in bytes.
Basic reading stream interfaces.
virtual size_t pos() const =0
Obtains the current value of the stream position indicator of the stream.
CBGT(Common::SeekableReadStream &cbgt, Common::SeekableReadStream &pal, Common::SeekableReadStream &twoda)
Definition: cbgt.cpp:46
PaletteIndices paletteIndices
Definition: cbgt.h:74
const TwoDARow & getRow(size_t row) const
Get a row.
Definition: 2dafile.cpp:421
Decompressing "small" files, Nintendo DS LZSS (types 0x00 and 0x10), found in Sonic.
uint32_t uint32
Definition: types.h:204
SeekableSubReadStream provides access to a SeekableReadStream restricted to the range [begin...
Definition: readstream.h:359
A row within a 2DA file.
Definition: 2dafile.h:61
Compressed BackGround Tiles, a BioWare image format found in Sonic.
Common::SeekableReadStream * pal
Definition: cbgt.h:70
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
uint8 byte
Definition: types.h:209
void readCells(ReadContext &ctx)
Definition: cbgt.cpp:164
Common::SeekableReadStream * cbgt
Definition: cbgt.h:69
unsigned int uint
Definition: types.h:211
void createImage(uint32 width, uint32 height)
Definition: cbgt.cpp:214
void readPaletteIndices(ReadContext &ctx)
Definition: cbgt.cpp:108