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