xoreos  0.0.5
obbfile.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 <cassert>
26 
27 #include "src/common/util.h"
28 #include "src/common/strutil.h"
30 #include "src/common/error.h"
31 #include "src/common/encoding.h"
32 #include "src/common/deflate.h"
33 
34 #include "src/aurora/obbfile.h"
35 #include "src/aurora/util.h"
36 
37 namespace Aurora {
38 
40  assert(_obb);
41 
42  load(*_obb);
43 }
44 
46 }
47 
49  /* OBB files have no actual header. But they're made up of zlib compressed chunks,
50  * so we just check if we find a zlib header at the start of the file. */
51  if (obb.readUint16BE() != 0x789C)
52  throw Common::Exception("No zlib header, this doesn't look like an Aspyr OBB virtual filesystem");
53 
54  try {
55  /* Extract the resource index and read the resource list out of it. */
56 
58  readResList(*obbIndex);
59 
60  } catch (Common::Exception &e) {
61  e.add("Failed reading OBB file");
62  throw;
63  }
64 }
65 
67  const uint32 resCount = index.readUint32LE();
68  index.skip(4); // Always 0. Possibly space for uint64?
69 
70  _iResources.reserve(resCount);
71 
72  uint32 resIndex = 0;
73  for (uint32 i = 0; i < resCount; i++) {
74  const uint32 nameLength = index.readUint32LE();
75  index.skip(4); // Always 0. Possibly space for uint64?
76 
77  const Common::UString name = Common::readStringFixed(index, Common::kEncodingASCII, nameLength);
78 
79  IResource iRes;
80 
81  iRes.offset = index.readUint32LE();
82  index.skip(4); // Always 0. Possibly space for uint64?
83 
84  iRes.uncompressedSize = index.readUint32LE();
85  index.skip(4); // Always 0. Possibly space for uint64?
86 
87  // Unreliable. See note about the compressed size in getResource()
88  iRes.compressedSize = index.readUint32LE();
89  index.skip(4); // Always 0. Possibly space for uint64?
90 
91  // Entries with a size of 0 are directories. We don't care about directories
92  if (iRes.uncompressedSize == 0)
93  continue;
94 
95  Resource res;
96 
97  res.name = TypeMan.setFileType(name, kFileTypeNone);
98  res.type = TypeMan.getFileType(name);
99  res.index = resIndex++;
100 
101  _resources.push_back(res);
102  _iResources.push_back(iRes);
103  }
104 }
105 
107  /* Find and decompress the resource index.
108  *
109  * It's the last compressed chunk in the OBB file, so we're searching
110  * backwards for 0x78 0x9C (the usual zlib header). That's a bit short,
111  * and can lead to false positives.
112  *
113  * But we also know that each file, after the last chunk, has another
114  * 16 bytes with some sort of meta data, of which the last four bytes
115  * are always 0x00. Immediately afterwards, the next compressed chunk
116  * start. So we can add that to our "marker" to look for.
117  *
118  * That gives us the start of the compressed resource list. To make
119  * sure we're not decompression garbage, we're also looking for the
120  * end of the list. The resource index is always one chunk, and at
121  * the end, there's the usual 16 bytes. For the resource index, the
122  * first four of those is the offset of that start of the chunk, and
123  * the next four bytes are 0x00. We can use that to figure out end.
124  *
125  * With that full range, we can decompress the resource index and
126  * return the decompressed data.
127  *
128  * NOTE: Yes, we're taking quite some shortcuts here. The original
129  * code probably does it differently and more robust. */
130 
131  static const byte kZlibHeader[6] = { 0x00, 0x00, 0x00, 0x00, 0x78, 0x9C };
132  static const size_t kMaxReadBack = 0xFFFFFF; // Should be enough
133 
134  const size_t lastZlib = Common::searchBackwards(obb, kZlibHeader, sizeof(kZlibHeader), kMaxReadBack);
135  if (lastZlib == SIZE_MAX)
136  throw Common::Exception("Couldn't find the last zlib header");
137 
138  byte offsetData[8];
139  WRITE_LE_UINT32(offsetData + 0, lastZlib + 4);
140  WRITE_LE_UINT32(offsetData + 4, 0);
141 
142  Common::SeekableSubReadStream obbZIndexStart(&obb, lastZlib + 4, obb.size());
143 
144  const size_t indexSize = Common::searchBackwards(obbZIndexStart, offsetData, sizeof(offsetData), 0xFFFFFF);
145  if (indexSize == SIZE_MAX)
146  throw Common::Exception("Couldn't find the index end marker");
147 
148  obbZIndexStart.seek(0);
149 
150  return Common::decompressDeflateWithoutOutputSize(obbZIndexStart, indexSize, Common::kWindowBitsMax);
151 }
152 
154  return _resources;
155 }
156 
158  if (index >= _iResources.size())
159  throw Common::Exception("Resource index out of range (%u/%u)", index, (uint)_iResources.size());
160 
161  return _iResources[index];
162 }
163 
165  return getIResource(index).uncompressedSize;
166 }
167 
169  /* Decompress a single file.
170  *
171  * Files in OBB virtual filesystems are split up in zlib compressed chunks.
172  * Each chunk, after decompression, takes up 4096 bytes (except for the last
173  * one, which can be shorter). The compressed size of the chunk is variable.
174  *
175  * Since we know the starting offset of the first chunk of the file, and
176  * the uncompressed data size, we simple decompress one chunk after the
177  * other, starting with the first of the file. Once we have decompressed
178  * as many bytes as the uncompressed size, we know we're done.
179  *
180  * The OBB virtual filesystem also has a chunk list and extra 16 bytes of
181  * meta data at the end of the last compressed chunk, but we don't really
182  * care about any of that (also, we don't really know how either of those
183  * work).
184  *
185  * We also can't use the compressed size, because that includes the extra
186  * 16 bytes. And for the first file in the OBB, it even includes the
187  * compressed size of the chunk list (which is always the second file
188  * in the OBB).
189  *
190  * NOTE: Again, lots of shortcuts. Unlike what the original does. */
191 
192  const IResource &res = getIResource(index);
193 
194  _obb->seek(res.offset);
195 
197 
198  size_t offset = 0;
199  size_t bytesLeft = res.uncompressedSize;
200 
201  while (bytesLeft > 0) {
202  const size_t bytesChunk =
204  data.get() + offset, bytesLeft, 4096);
205 
206  offset += bytesChunk;
207  bytesLeft -= bytesChunk;
208  }
209 
210  return new Common::MemoryReadStream(data.release(), res.uncompressedSize, true);
211 }
212 
213 } // End of namespace Aurora
OBBFile(Common::SeekableReadStream *obb)
Take over this stream and read an OBB file out of it.
Definition: obbfile.cpp:39
uint32 uncompressedSize
The resource&#39;s uncompressed size.
Definition: obbfile.h:75
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
uint32 readUint32LE()
Read an unsigned 32-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:133
#define TypeMan
Shortcut for accessing the file type manager.
Definition: util.h:85
const ResourceList & getResources() const
Return the list of resources.
Definition: obbfile.cpp:153
A class holding an UTF-8 string.
Definition: ustring.h:48
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
Common::ScopedPtr< Common::SeekableReadStream > _obb
Definition: obbfile.h:81
Implementing the reading stream interfaces for plain memory blocks.
Utility functions to handle files used in BioWare&#39;s Aurora engine.
const IResource & getIResource(uint32 index) const
Definition: obbfile.cpp:157
Common::UString name
The resource&#39;s name.
Definition: archive.h:49
Utility templates and functions for working with strings and streams.
size_t decompressDeflateChunk(SeekableReadStream &input, int windowBits, byte *output, size_t outputSize, unsigned int frameSize)
Decompress (inflate) using zlib&#39;s DEFLATE algorithm, until a stream end marker was reached...
Definition: deflate.cpp:154
Exception that provides a stack of explanations.
Definition: error.h:36
ResourceList _resources
External list of resource names and types.
Definition: obbfile.h:84
Basic exceptions to throw.
size_t searchBackwards(SeekableReadStream &haystack, const byte *needle, size_t needleSize, size_t maxReadBack)
Search the stream, backwards, for the last occurrence of a set of bytes.
Definition: strutil.cpp:349
#define UNUSED(x)
Definition: system.h:170
Utility templates and functions.
uint16 readUint16BE()
Read an unsigned 16-bit word stored in big endian (MSB first) order from the stream and return it...
Definition: readstream.h:155
void load(Common::SeekableReadStream &obb)
Definition: obbfile.cpp:48
std::list< Resource > ResourceList
Definition: archive.h:57
uint32 getResourceSize(uint32 index) const
Return the size of a resource.
Definition: obbfile.cpp:164
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
uint32 compressedSize
The resource&#39;s compressed size.
Definition: obbfile.h:76
Simple memory based &#39;stream&#39;, which implements the ReadStream interface for a plain memory block...
Definition: memreadstream.h:66
Utility functions for working with differing string encodings.
Internal resource information.
Definition: obbfile.h:73
uint32 offset
The offset of the resource within the OBB.
Definition: obbfile.h:74
StackException Exception
Definition: error.h:59
A resource within the archive.
Definition: archive.h:48
virtual size_t size() const =0
Obtains the total size of the stream, measured in bytes.
byte * decompressDeflateWithoutOutputSize(const byte *data, size_t inputSize, size_t &outputSize, int windowBits, unsigned int frameSize)
Decompress (inflate) using zlib&#39;s DEFLATE algorithm without knowing the output size.
Definition: deflate.cpp:96
Common::SeekableReadStream * getResource(uint32 index, bool tryNoCopy=false) const
Return a stream of the resource&#39;s contents.
Definition: obbfile.cpp:168
Plain, unextended ASCII (7bit clean).
Definition: encoding.h:40
static const int kWindowBitsMax
Definition: deflate.h:41
PointerType get() const
Returns the plain pointer value.
Definition: scopedptr.h:96
FileType type
The resource&#39;s type.
Definition: archive.h:51
void readResList(Common::SeekableReadStream &index)
Definition: obbfile.cpp:66
uint32_t uint32
Definition: types.h:204
Common::SeekableReadStream * getIndex(Common::SeekableReadStream &obb)
Definition: obbfile.cpp:106
#define SIZE_MAX
Definition: types.h:172
IResourceList _iResources
Internal list of resource offsets and sizes.
Definition: obbfile.h:87
Handling Aspyr&#39;s OBB virtual filesystems.
UString readStringFixed(SeekableReadStream &stream, Encoding encoding, size_t length)
Read length bytes as a string with the given encoding out of a stream.
Definition: encoding.cpp:297
SeekableSubReadStream provides access to a SeekableReadStream restricted to the range [begin...
Definition: readstream.h:359
Compress (deflate) and decompress (inflate) using zlib&#39;s DEFLATE algorithm.
Interface for a seekable & readable data stream.
Definition: readstream.h:265
uint8 byte
Definition: types.h:209
uint32 index
The resource&#39;s local index within the archive.
Definition: archive.h:52
unsigned int uint
Definition: types.h:211