Files
scummvm-tools/extract_parallaction.cpp
T
Hampus Nilsson 168e91e076 *Started on CLI interface, already works but there are many edges to sort out.
*Added a common 'Tools' class to be used by both the CLI and the GUI, right now it's NOT in use by the GUI, so there is some code clutter.
*Rename gui/tools.h to gui/tools.cpp
*Tools now know their own type.
*Some other small changes.

svn-id: r42670
2009-07-23 01:41:14 +00:00

346 lines
7.1 KiB
C++

/* extract_parallaction - Extractor for Nippon Safe archives
* Copyright (C) 2007 The ScummVM project
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "extract_parallaction.h"
Archive::Archive(Tool &tool) : _tool(tool) {
_fileData = NULL;
_fileSize = 0;
_filePos = 0;
_numFiles = 0;
_numSlots = 0;
}
Archive::~Archive() {
closeSubfile();
}
bool Archive::isPackedSubfile(byte *data) {
return (data[0] == 'P' && data[1] == 'P' && data[2] == '2' && data[3] == '0');
}
uint32 Archive::getSizeOfPackedSubfile(byte *packedData, uint32 packedSize) {
uint32 size = *(uint32 *)(packedData + packedSize - 4);
return ((size & 0xFF00)) | ((size & 0xFF0000) >> 16);
}
int32 Archive::findSubfile(const char *filename) {
for (uint32 i = 0; i < _numFiles; i++) {
if (!scumm_stricmp(filename, _names[i])) return i;
}
return -1;
}
void Archive::unpackSubfile(byte *packedData, uint32 packedSize) {
ppdepack(packedData, _fileData, packedSize, _fileSize);
}
void Archive::closeSubfile() {
if (_fileData)
free(_fileData);
_fileData = NULL;
_fileSize = 0;
_filePos = 0;
}
uint32 Archive::getSizeOfSubfile() {
return _fileSize;
}
void Archive::openSubfile(uint32 index) {
if (index >= _numFiles) exit(-3);
closeSubfile();
uint32 srcOffset = _offsets[index];
uint32 srcSize = _sizes[index];
byte *srcData = (byte *)malloc(srcSize);
_file.seek(srcOffset, SEEK_SET);
_file.read(srcData, 1, srcSize);
if (isPackedSubfile(srcData)) {
_fileSize = getSizeOfPackedSubfile(srcData, srcSize);
_fileData = (byte *)malloc(_fileSize);
unpackSubfile(srcData, srcSize);
free(srcData);
} else {
_fileSize = srcSize;
_fileData = srcData;
}
}
void Archive::openSubfile(const char *filename) {
int32 index = findSubfile(filename);
if (index == -1) exit(-2);
openSubfile(index);
return;
}
void Archive::readSubfile(byte *buf, uint32 size) {
assert(size + _filePos <= _fileSize);
memcpy(buf, _fileData + _filePos, size);
_filePos += size;
return;
}
void Archive::open(const char *filename, bool smallArchive) {
uint16 maxEntries = (smallArchive) ? 180 : 384;
_file.open(filename, "rb");
strcpy(_name, filename);
_file.seek(ARCHIVE_HEADER_SIZE, SEEK_SET);
_file.read(_names, maxEntries + 1, ARCHIVE_FILENAME_LEN);
uint32 i;
for (i = 0; i < maxEntries; i++) {
if (_names[i][0] == '\0')
break;
}
_numFiles = i;
if (_numFiles < maxEntries) {
uint32 *t = (uint32*)_names[i];
for (; i < (uint32)maxEntries + 1; i++) {
if (*t != 0)
break;
t += 8;
}
}
if (i > (uint32)maxEntries)
exit(9);
_numSlots = i;
_tool.print("%i files found in %i slots (%i empty)\n", _numFiles, _numSlots, _numSlots - _numFiles);
_file.seek(_numSlots * ARCHIVE_FILENAME_LEN + ARCHIVE_HEADER_SIZE, SEEK_SET);
for (i = 0; i < _numSlots; i++) {
_sizes[i] = _file.readUint32BE();
}
if (smallArchive) {
_offsets[0] = 0x1966;
} else {
_offsets[0] = 0x4000;
}
_file.pos();
for (i = 1; i < _numSlots; i++)
_offsets[i] = _offsets[i-1] + _sizes[i-1];
return;
}
void Archive::dumpStructs(FILE *dump) {
char arcName[32];
char *s = strrchr(_name, '/');
if (s == NULL) {
s = strrchr(_name, '\\');
if (s == NULL) s = _name;
}
char *d = arcName;
for (; *s; ) *d++ = toupper(*s++);
*d = '\0';
for (uint32 i = 0; i < _numFiles; i++) {
fprintf(dump, "{ \"%s\",%*s%5i, kArchive%s, %7i },\n", _names[i], 32-(int)strlen(_names[i]), " ", _sizes[i], arcName+1, _offsets[i]);
}
}
#define val(p) ((p)[0]<<16 | (p)[1] << 8 | (p)[2])
uint32 depackedlen(byte *packed, uint32 plen) {
if (packed[0] != 'P' || packed[1] != 'P' ||
packed[2] != '2' || packed[3] != '0')
return 0; /* not a powerpacker file */
return val(packed+plen-4);
}
static uint32 shift_in;
static uint32 counter = 0;
static byte *source;
static uint32 get_bits(uint32 n) {
uint32 result = 0;
uint32 i;
for (i = 0; i < n; i++) {
if (counter == 0) {
counter = 8;
shift_in = *--source;
}
result = (result<<1) | (shift_in & 1);
shift_in >>= 1;
counter--;
}
return result;
}
void ppdepack(byte *packed, byte *depacked, uint32 plen, uint32 unplen) {
byte *dest;
int n_bits;
int idx;
uint32 bytes;
int to_add;
uint32 offset;
byte offset_sizes[4];
uint32 i;
shift_in = 0;
counter = 0;
offset_sizes[0] = packed[4]; /* skip signature */
offset_sizes[1] = packed[5];
offset_sizes[2] = packed[6];
offset_sizes[3] = packed[7];
/* initialize source of bits */
source = packed + plen - 4;
dest = depacked + unplen;
/* skip bits */
get_bits(source[3]);
/* do it forever, i.e., while the whole file isn't unpacked */
while (1) {
/* copy some bytes from the source anyway */
if (get_bits(1) == 0) {
bytes = 0;
do {
to_add = get_bits(2);
bytes += to_add;
} while (to_add == 3);
for (i = 0; i <= bytes; i++)
*--dest = get_bits(8);
if (dest <= depacked)
return;
}
/* decode what to copy from the destination file */
idx = get_bits(2);
n_bits = offset_sizes[idx];
/* bytes to copy */
bytes = idx + 1;
if (bytes == 4) { /* 4 means >=4 */
/* and maybe a bigger offset */
if (get_bits(1) == 0)
offset = get_bits(7);
else
offset = get_bits(n_bits);
do {
to_add = get_bits(3);
bytes += to_add;
} while (to_add == 7);
} else {
offset = get_bits(n_bits);
}
for (i = 0; i <= bytes; i++) {
dest[-1] = dest[offset];
dest--;
}
if (dest <= depacked)
return;
}
}
ExtractParallaction::ExtractParallaction(const std::string &name) : Tool(name, TOOLTYPE_EXTRACTION) {
ToolInput input;
input.format = "*.*";
_inputPaths.push_back(input);
_helptext = "\nUsage: " + _name + " [--small] [-o <output dir> = out/] <file>\n";
}
void ExtractParallaction::parseExtraArguments() {
if (_arguments[_arguments_parsed] == "--small") {
_small = true;
++_arguments_parsed;
}
}
void ExtractParallaction::execute() {
Filename inpath(_inputPaths[0].path);
Filename &outpath = _outputPath;
if (outpath.empty())
outpath.setFullPath("out/");
Archive arc(*this);
arc.open(inpath.getFullPath().c_str(), _small);
for (uint32 i = 0; i < arc._numFiles; i++) {
arc.openSubfile(i);
char filename[260], * d = filename;
for (char *s = arc._names[i]; *s; s++, d++)
*d = *s == '/' ? '_' : *s;
*d = '\0';
outpath.setFullName(d);
File ofile(outpath, "wb");
ofile.write(arc._fileData, 1, arc._fileSize);
}
}
#ifdef STANDALONE_MAIN
int main(int argc, char *argv[]) {
ExtractParallaction parallaction(argv[0]);
return parallaction.run(argc, argv);
}
#endif