1 /* ScummVM Tools
2 *
3 * ScummVM Tools is the legal property of its developers, whose
4 * names are too numerous to list here. Please refer to the
5 * COPYRIGHT 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 /* Extractor for Nippon Safe archives */
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28
29 #include "extract_parallaction.h"
30
Archive(Tool & tool)31 Archive::Archive(Tool &tool) : _tool(tool) {
32 _fileData = NULL;
33 _fileSize = 0;
34 _filePos = 0;
35
36 _numFiles = 0;
37 _numSlots = 0;
38 }
39
~Archive()40 Archive::~Archive() {
41 closeSubfile();
42 }
43
isPackedSubfile(byte * data)44 bool Archive::isPackedSubfile(byte *data) {
45 return (data[0] == 'P' && data[1] == 'P' && data[2] == '2' && data[3] == '0');
46 }
47
getSizeOfPackedSubfile(byte * packedData,uint32 packedSize)48 uint32 Archive::getSizeOfPackedSubfile(byte *packedData, uint32 packedSize) {
49 uint32 size = *(uint32 *)(packedData + packedSize - 4);
50
51 return ((size & 0xFF00)) | ((size & 0xFF0000) >> 16);
52 }
53
findSubfile(const char * filename)54 int32 Archive::findSubfile(const char *filename) {
55 for (uint32 i = 0; i < _numFiles; i++) {
56 if (!scumm_stricmp(filename, _names[i])) return i;
57 }
58
59 return -1;
60 }
61
unpackSubfile(byte * packedData,uint32 packedSize)62 void Archive::unpackSubfile(byte *packedData, uint32 packedSize) {
63 ppdepack(packedData, _fileData, packedSize, _fileSize);
64 }
65
closeSubfile()66 void Archive::closeSubfile() {
67 free(_fileData);
68 _fileData = NULL;
69 _fileSize = 0;
70 _filePos = 0;
71 }
72
getSizeOfSubfile()73 uint32 Archive::getSizeOfSubfile() {
74 return _fileSize;
75 }
76
openSubfile(uint32 index)77 void Archive::openSubfile(uint32 index) {
78 if (index >= _numFiles)
79 throw ToolException("File index out of bounds.");
80
81 closeSubfile();
82
83 uint32 srcOffset = _offsets[index];
84 uint32 srcSize = _sizes[index];
85
86 byte *srcData = (byte *)malloc(srcSize);
87 _file.seek(srcOffset, SEEK_SET);
88 _file.read_throwsOnError(srcData, srcSize);
89
90 if (isPackedSubfile(srcData)) {
91 _fileSize = getSizeOfPackedSubfile(srcData, srcSize);
92 _fileData = (byte *)malloc(_fileSize);
93
94 unpackSubfile(srcData, srcSize);
95
96 free(srcData);
97 } else {
98 _fileSize = srcSize;
99 _fileData = srcData;
100 }
101 }
102
openSubfile(const char * filename)103 void Archive::openSubfile(const char *filename) {
104
105 int32 index = findSubfile(filename);
106
107 if (index == -1)
108 throw ToolException("Could not open subfile.");
109
110 openSubfile(index);
111
112 return;
113 }
114
readSubfile(byte * buf,uint32 size)115 void Archive::readSubfile(byte *buf, uint32 size) {
116 assert(size + _filePos <= _fileSize);
117 memcpy(buf, _fileData + _filePos, size);
118 _filePos += size;
119 return;
120 }
121
open(const char * filename,bool smallArchive)122 void Archive::open(const char *filename, bool smallArchive) {
123 uint16 maxEntries = (smallArchive) ? 180 : 384;
124
125 _file.open(filename, "rb");
126
127 strcpy(_name, filename);
128
129 _file.seek(ARCHIVE_HEADER_SIZE, SEEK_SET);
130 _file.read_throwsOnError(_names, (maxEntries + 1) * ARCHIVE_FILENAME_LEN);
131
132 uint32 i;
133 for (i = 0; i < maxEntries; i++) {
134 if (_names[i][0] == '\0')
135 break;
136 }
137
138 _numFiles = i;
139
140 if (_numFiles < maxEntries) {
141 uint32 *t = (uint32*)_names[i];
142
143 for (; i < (uint32)maxEntries + 1; i++) {
144 if (*t != 0)
145 break;
146 t += 8;
147 }
148 }
149
150 if (i > (uint32)maxEntries)
151 throw ToolException("Could not open archive, index out of range.");
152
153 _numSlots = i;
154
155 _tool.print("%i files found in %i slots (%i empty)", _numFiles, _numSlots, _numSlots - _numFiles);
156
157 _file.seek(_numSlots * ARCHIVE_FILENAME_LEN + ARCHIVE_HEADER_SIZE, SEEK_SET);
158
159 for (i = 0; i < _numSlots; i++) {
160 _sizes[i] = _file.readUint32BE();
161 }
162
163 if (smallArchive) {
164 _offsets[0] = 0x1966;
165 } else {
166 _offsets[0] = 0x4000;
167 }
168
169 _file.pos();
170
171 for (i = 1; i < _numSlots; i++)
172 _offsets[i] = _offsets[i-1] + _sizes[i-1];
173
174 return;
175 }
176
dumpStructs(FILE * dump)177 void Archive::dumpStructs(FILE *dump) {
178 char arcName[32];
179
180 char *s = strrchr(_name, '/');
181 if (s == NULL) {
182 s = strrchr(_name, '\\');
183 if (s == NULL) s = _name;
184 }
185
186 char *d = arcName;
187
188 for (; *s; ) *d++ = toupper(*s++);
189 *d = '\0';
190
191 for (uint32 i = 0; i < _numFiles; i++) {
192 fprintf(dump, "{ \"%s\",%*s%5i, kArchive%s, %7i },\n", _names[i], 32-(int)strlen(_names[i]), " ", _sizes[i], arcName+1, _offsets[i]);
193 }
194 }
195
196
197 #define val(p) ((p)[0]<<16 | (p)[1] << 8 | (p)[2])
198
depackedlen(byte * packed,uint32 plen)199 uint32 depackedlen(byte *packed, uint32 plen) {
200 if (packed[0] != 'P' || packed[1] != 'P' ||
201 packed[2] != '2' || packed[3] != '0')
202 return 0; /* not a powerpacker file */
203
204 return val(packed+plen-4);
205 }
206
207 static uint32 shift_in;
208 static uint32 counter = 0;
209 static byte *source;
210
get_bits(uint32 n)211 static uint32 get_bits(uint32 n) {
212 uint32 result = 0;
213 uint32 i;
214
215 for (i = 0; i < n; i++) {
216 if (counter == 0) {
217 counter = 8;
218 shift_in = *--source;
219 }
220 result = (result<<1) | (shift_in & 1);
221 shift_in >>= 1;
222 counter--;
223 }
224
225 return result;
226 }
227
ppdepack(byte * packed,byte * depacked,uint32 plen,uint32 unplen)228 void ppdepack(byte *packed, byte *depacked, uint32 plen, uint32 unplen) {
229 byte *dest;
230 int n_bits;
231 int idx;
232 uint32 bytes;
233 int to_add;
234 uint32 offset;
235 byte offset_sizes[4];
236 uint32 i;
237
238 shift_in = 0;
239 counter = 0;
240
241 offset_sizes[0] = packed[4]; /* skip signature */
242 offset_sizes[1] = packed[5];
243 offset_sizes[2] = packed[6];
244 offset_sizes[3] = packed[7];
245
246 /* initialize source of bits */
247 source = packed + plen - 4;
248
249 dest = depacked + unplen;
250
251 /* skip bits */
252 get_bits(source[3]);
253
254 /* do it forever, i.e., while the whole file isn't unpacked */
255 while (1) {
256 /* copy some bytes from the source anyway */
257 if (get_bits(1) == 0) {
258 bytes = 0;
259 do {
260 to_add = get_bits(2);
261 bytes += to_add;
262 } while (to_add == 3);
263
264 for (i = 0; i <= bytes; i++)
265 *--dest = get_bits(8);
266
267 if (dest <= depacked)
268 return;
269 }
270
271 /* decode what to copy from the destination file */
272 idx = get_bits(2);
273 n_bits = offset_sizes[idx];
274 /* bytes to copy */
275 bytes = idx + 1;
276 if (bytes == 4) { /* 4 means >=4 */
277 /* and maybe a bigger offset */
278 if (get_bits(1) == 0)
279 offset = get_bits(7);
280 else
281 offset = get_bits(n_bits);
282
283 do {
284 to_add = get_bits(3);
285 bytes += to_add;
286 } while (to_add == 7);
287 } else {
288 offset = get_bits(n_bits);
289 }
290
291 for (i = 0; i <= bytes; i++) {
292 dest[-1] = dest[offset];
293 dest--;
294 }
295
296 if (dest <= depacked)
297 return;
298 }
299
300 }
301
ExtractParallaction(const std::string & name)302 ExtractParallaction::ExtractParallaction(const std::string &name) : Tool(name, TOOLTYPE_EXTRACTION) {
303
304 ToolInput input;
305 input.format = "*.*";
306 _inputPaths.push_back(input);
307
308 _shorthelp = "Extract data files from games built on the Parallaction engine.";
309 _helptext = "\nUsage: " + _name + " [-o <output dir> = out/] [--small] <file>\n" + _shorthelp + "\n";
310 }
311
parseExtraArguments()312 void ExtractParallaction::parseExtraArguments() {
313 if (!_arguments.empty() && _arguments.front() == "--small") {
314 _small = true;
315 _arguments.pop_front();
316 }
317 }
318
execute()319 void ExtractParallaction::execute() {
320
321 Common::Filename inpath(_inputPaths[0].path);
322 Common::Filename &outpath = _outputPath;
323
324 if (outpath.empty())
325 outpath.setFullPath("out/");
326
327 Archive arc(*this);
328 arc.open(inpath.getFullPath().c_str(), _small);
329
330 for (uint32 i = 0; i < arc._numFiles; i++) {
331
332 arc.openSubfile(i);
333
334 char filename[260], * d = filename;
335
336 for (char *s = arc._names[i]; *s; s++, d++)
337 *d = *s == '/' ? '_' : *s;
338 *d = '\0';
339
340 outpath.setFullName(filename);
341
342 Common::File ofile(outpath, "wb");
343 ofile.write(arc._fileData, arc._fileSize);
344 }
345 }
346
347 #ifdef STANDALONE_MAIN
main(int argc,char * argv[])348 int main(int argc, char *argv[]) {
349 ExtractParallaction parallaction(argv[0]);
350 return parallaction.run(argc, argv);
351 }
352 #endif
353
354