1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program 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 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program 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 this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "mads/compression.h"
24
25 namespace MADS {
26
27 const char *const madsPackString = "MADSPACK";
28 const char *const FabInputExceededError = "FabDecompressor - Passed end of input buffer during decompression";
29 const char *const FabOutputExceededError = "FabDecompressor - Decompressed data exceeded specified size";
30
isCompressed(Common::SeekableReadStream * stream)31 bool MadsPack::isCompressed(Common::SeekableReadStream *stream) {
32 // Check whether the passed stream is packed
33
34 char tempBuffer[8];
35 stream->seek(0);
36 if (stream->read(tempBuffer, 8) == 8) {
37 if (!strncmp(tempBuffer, madsPackString, 8))
38 return true;
39 }
40
41 return false;
42 }
43
MadsPack(Common::SeekableReadStream * stream)44 MadsPack::MadsPack(Common::SeekableReadStream *stream) {
45 initialize(stream);
46 }
47
MadsPack(const Common::String & resourceName,MADSEngine * vm)48 MadsPack::MadsPack(const Common::String &resourceName, MADSEngine *vm) {
49 File file(resourceName);
50 initialize(&file);
51 file.close();
52 }
53
initialize(Common::SeekableReadStream * stream)54 void MadsPack::initialize(Common::SeekableReadStream *stream) {
55 if (!MadsPack::isCompressed(stream))
56 error("Attempted to decompress a resource that was not MadsPacked");
57
58 stream->seek(14);
59 _count = stream->readUint16LE();
60 _items = new MadsPackEntry[_count];
61
62 byte *headerData = new byte[0xA0];
63 byte *header = headerData;
64 stream->read(headerData, 0xA0);
65
66 for (int i = 0; i < _count; ++i, header += 10) {
67 // Get header data
68 _items[i]._type = (CompressionType)*header;
69 _items[i]._priority = *(header + 1);
70 _items[i]._size = READ_LE_UINT32(header + 2);
71 _items[i]._compressedSize = READ_LE_UINT32(header + 6);
72
73 byte *sourceData = new byte[_items[i]._compressedSize];
74 stream->read(sourceData, _items[i]._compressedSize);
75
76 switch (_items[i]._type) {
77 case COMPRESS_NONE:
78 // Entry isn't compressed
79 _items[i]._data = sourceData;
80 break;
81
82 case COMPRESS_FAB: {
83 // Decompress the entry
84 _items[i]._data = new byte[_items[i]._size];
85
86 FabDecompressor fab;
87 fab.decompress(sourceData, _items[i]._compressedSize, _items[i]._data, _items[i]._size);
88 delete[] sourceData;
89 break;
90 }
91
92 default:
93 error("Unknown compression type encountered");
94 }
95 }
96
97 delete[] headerData;
98 _dataOffset = stream->pos();
99 }
100
~MadsPack()101 MadsPack::~MadsPack() {
102 for (int i = 0; i < _count; ++i)
103 delete[] _items[i]._data;
104 delete[] _items;
105 }
106
107 //--------------------------------------------------------------------------
108
decompress(const byte * srcData,int srcSize,byte * destData,int destSize)109 void FabDecompressor::decompress(const byte *srcData, int srcSize, byte *destData, int destSize) {
110 byte copyLen, copyOfsShift, copyOfsMask, copyLenMask;
111 unsigned long copyOfs;
112 byte *destP;
113
114 // Validate that the data starts with the FAB header
115 if (strncmp((const char *)srcData, "FAB", 3) != 0)
116 error("FabDecompressor - Invalid compressed data");
117
118 int shiftVal = srcData[3];
119 if ((shiftVal < 10) || (shiftVal > 13))
120 error("FabDecompressor - Invalid shift start");
121
122 copyOfsShift = 16 - shiftVal;
123 copyOfsMask = 0xFF << (shiftVal - 8);
124 copyLenMask = (1 << copyOfsShift) - 1;
125 copyOfs = 0xFFFF0000;
126 destP = destData;
127
128 // Initialize data fields
129 _srcData = srcData;
130 _srcP = _srcData + 6;
131 _srcSize = srcSize;
132 _bitsLeft = 16;
133 _bitBuffer = READ_LE_UINT16(srcData + 4);
134
135 for (;;) {
136 if (getBit() == 0) {
137 if (getBit() == 0) {
138 copyLen = ((getBit() << 1) | getBit()) + 2;
139 copyOfs = *_srcP++ | 0xFFFFFF00;
140 } else {
141 copyOfs = (((_srcP[1] >> copyOfsShift) | copyOfsMask) << 8) | _srcP[0];
142 copyLen = _srcP[1] & copyLenMask;
143 _srcP += 2;
144 if (copyLen == 0) {
145 copyLen = *_srcP++;
146 if (copyLen == 0)
147 break;
148 else if (copyLen == 1)
149 continue;
150 else
151 copyLen++;
152 } else {
153 copyLen += 2;
154 }
155 copyOfs |= 0xFFFF0000;
156 }
157 while (copyLen-- > 0) {
158 if (destP - destData == destSize)
159 error(FabOutputExceededError);
160
161 *destP = destP[(signed int)copyOfs];
162 destP++;
163 }
164 } else {
165 if (_srcP - srcData == srcSize)
166 error(FabInputExceededError);
167 if (destP - destData == destSize)
168 error(FabOutputExceededError);
169
170 *destP++ = *_srcP++;
171 }
172 }
173
174 if (destP - destData != destSize)
175 error("FabDecompressor - Decompressed data does not match header decompressed size");
176 }
177
getBit()178 int FabDecompressor::getBit() {
179 _bitsLeft--;
180 if (_bitsLeft == 0) {
181 if (_srcP - _srcData == _srcSize)
182 error(FabInputExceededError);
183
184 _bitBuffer = (READ_LE_UINT16(_srcP) << 1) | (_bitBuffer & 1);
185 _srcP += 2;
186 _bitsLeft = 16;
187 }
188
189 int bit = _bitBuffer & 1;
190 _bitBuffer >>= 1;
191 return bit;
192 }
193
194 } // End of namespace MADS
195