diff options
author | Eugene Sandulenko | 2008-06-01 11:43:20 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2008-06-01 11:43:20 +0000 |
commit | cc74ec5c39a233c05ad9e8b3eba96649ceb8490b (patch) | |
tree | 56771c0bac722f669e5ae535a5672edff698babc | |
parent | a81e10da49a1418326f324227d6daa06aed346cc (diff) | |
download | scummvm-rg350-cc74ec5c39a233c05ad9e8b3eba96649ceb8490b.tar.gz scummvm-rg350-cc74ec5c39a233c05ad9e8b3eba96649ceb8490b.tar.bz2 scummvm-rg350-cc74ec5c39a233c05ad9e8b3eba96649ceb8490b.zip |
Unarj code based on unarj 2.62. Used by Drascula engine
svn-id: r32460
-rw-r--r-- | common/module.mk | 1 | ||||
-rw-r--r-- | common/unarj.cpp | 666 | ||||
-rw-r--r-- | common/unarj.h | 179 |
3 files changed, 846 insertions, 0 deletions
diff --git a/common/module.mk b/common/module.mk index a7c0f4eb90..c3f2a38c3f 100644 --- a/common/module.mk +++ b/common/module.mk @@ -14,6 +14,7 @@ MODULE_OBJS := \ stream.o \ util.o \ system.o \ + unarj.o \ unzip.o \ zlib.o diff --git a/common/unarj.cpp b/common/unarj.cpp new file mode 100644 index 0000000000..c8e48dce2c --- /dev/null +++ b/common/unarj.cpp @@ -0,0 +1,666 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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$ + * + */ + +// Heavily based on Unarj 2.65 + +/* UNARJ.C, UNARJ, R JUNG, 06/05/02 + * Main Extractor routine + * Copyright (c) 1991-2002 by ARJ Software, Inc. All rights reserved. + * + * This code may be freely used in programs that are NOT ARJ archivers + * (both compress and extract ARJ archives). + * + * If you wish to distribute a modified version of this program, you + * MUST indicate that it is a modified version both in the program and + * source code. + * + * We are holding the copyright on the source code, so please do not + * delete our name from the program files or from the documentation. + * + * We wish to give credit to Haruhiko Okumura for providing the + * basic ideas for ARJ and UNARJ in his program AR. Please note + * that UNARJ is significantly different from AR from an archive + * structural point of view. + * + */ + +#include "common/scummsys.h" +#include "common/util.h" +#include "common/unarj.h" + +namespace Common { + +static uint32 CRCtable[256]; + +static void InitCRC(void) { + const uint32 poly = 0xEDB88320; + int i, j; + uint32 n; + + for (i = 0; i < 256; i++) { + n = i; + for (j = 0; j < 8; j++) + n = (n & 1) ? ((n >> 1) ^ poly) : (n >> 1); + CRCtable[i] = n; + } +} + +static uint32 GetCRC(byte *data, int len) { + uint32 CRC = 0xFFFFFFFF; + int i; + for (i = 0; i < len; i++) + CRC = (CRC >> 8) ^ CRCtable[(CRC ^ data[i]) & 0xFF]; + return CRC ^ 0xFFFFFFFF; +} + +ArjFile::ArjFile() { + InitCRC(); +} + +ArjFile::~ArjFile() { + close(); + + for (uint i = 0; i < _headers.size(); i++) + delete _headers[i]; + + _headers.clear(); +} + +void ArjFile::registerArchive(const String &filename) { + int32 first_hdr_pos; + ArjHeader *header; + + if (!_currArchive.open(filename)) + return; + + first_hdr_pos = findHeader(); + + if (first_hdr_pos < 0) { + warning("ArjFile::registerArchive(): Could not find a valid header"); + return; + } + + _currArchive.seek(first_hdr_pos, SEEK_SET); + if (readHeader() == NULL) + return; + + while ((header = readHeader()) != NULL) { + _headers.push_back(header); + + _currArchive.seek(header->compSize, SEEK_CUR); + + _fileMap[header->filename] = _headers.size() - 1; + _archMap[header->filename] = filename; + } + + _currArchive.close(); + + debug(0, "ArjFile::registerArchive(%s): Located %d files", filename.c_str(), _headers.size()); +} + +int32 ArjFile::findHeader(void) { + long arcpos, lastpos; + int c; + byte header[HEADERSIZE_MAX]; + uint32 crc; + uint16 headersize; + + arcpos = _currArchive.pos(); + _currArchive.seek(0L, SEEK_END); + lastpos = _currArchive.pos() - 2; + if (lastpos > MAXSFX) + lastpos = MAXSFX; + + for ( ; arcpos < lastpos; arcpos++) { + _currArchive.seek(arcpos, SEEK_SET); + c = _currArchive.readByte(); + while (arcpos < lastpos) { + if (c != HEADER_ID_LO) // low order first + c = _currArchive.readByte(); + else if ((c = _currArchive.readByte()) == HEADER_ID_HI) + break; + arcpos++; + } + if (arcpos >= lastpos) + break; + if ((headersize = _currArchive.readUint16LE()) <= HEADERSIZE_MAX) { + _currArchive.read(header, headersize); + crc = GetCRC(header, headersize); + if (crc == _currArchive.readUint32LE()) { + _currArchive.seek(arcpos, SEEK_SET); + return arcpos; + } + } + } + return -1; // could not find a valid header +} + +ArjHeader *ArjFile::readHeader() { + ArjHeader header; + ArjHeader *head; + byte headData[HEADERSIZE_MAX]; + + header.id = _currArchive.readUint16LE(); + if (header.id != HEADER_ID) { + warning("ArjFile::readHeader(): Bad header ID (%x)", header.id); + + return NULL; + } + + header.headerSize = _currArchive.readUint16LE(); + if (header.headerSize == 0) + return NULL; // end of archive + if (header.headerSize > HEADERSIZE_MAX) { + warning("ArjFile::readHeader(): Bad header"); + + return NULL; + } + + int rSize = _currArchive.read(headData, header.headerSize); + + MemoryReadStream readS(headData, rSize); + + header.headerCrc = _currArchive.readUint32LE(); + if (GetCRC(headData, header.headerSize) != header.headerCrc) { + warning("ArjFile::readHeader(): Bad header CRC"); + return NULL; + } + + header.firstHdrSize = readS.readByte(); + header.nbr = readS.readByte(); + header.xNbr = readS.readByte(); + header.hostOs = readS.readByte(); + header.flags = readS.readByte(); + header.method = readS.readByte(); + header.fileType = readS.readByte(); + (void)readS.readByte(); + header.timeStamp = readS.readUint32LE(); + header.compSize = readS.readSint32LE(); + header.origSize = readS.readSint32LE(); + header.fileCRC = readS.readUint32LE(); + header.entryPos = readS.readUint16LE(); + header.fileMode = readS.readUint16LE(); + header.hostData = readS.readUint16LE(); + + if (header.origSize < 0 || header.compSize < 0) { + warning("ArjFile::readHeader(): Wrong file size"); + return NULL; + } + + strncpy(header.filename, (const char *)&headData[header.firstHdrSize], FNAME_MAX); + + strncpy(header.comment, (const char *)&headData[header.firstHdrSize + strlen(header.filename) + 1], COMMENT_MAX); + + /* if extheadersize == 0 then no CRC */ + /* otherwise read extheader data and read 4 bytes for CRC */ + + while ((header.extHeaderSize = _currArchive.readUint16LE()) != 0) + _currArchive.seek((long)(header.extHeaderSize + 4), SEEK_CUR); + + header.pos = _currArchive.pos(); + + head = new ArjHeader(header); + + return head; +} + + +bool ArjFile::open(const Common::String &filename, AccessMode mode) { + _isOpen = false; + + if (!_fileMap.contains(filename)) + return false; + + _isOpen = true; + + ArjHeader *hdr = _headers[_fileMap[filename]]; + + _compsize = hdr->compSize; + _origsize = hdr->origSize; + + _uncompressedData = (byte *)malloc(_origsize); + _outstream = new MemoryWriteStream(_uncompressedData, _origsize); + + _currArchive.open(_archMap[filename]); + _currArchive.seek(hdr->pos, SEEK_SET); + + if (hdr->method == 0) // store + _currArchive.read(_uncompressedData, _origsize); + else if (hdr->method == 1 || hdr->method == 2 || hdr->method == 3) + decode(); + else if (hdr->method == 4) + decode_f(); + + _currArchive.close(); + delete _outstream; + _outstream = NULL; + + _uncompressed = new MemoryReadStream(_uncompressedData, _origsize); + + return true; +} + +void ArjFile::close() { + _isOpen = false; + + delete _uncompressed; + _uncompressed = NULL; + + free(_uncompressedData); + _uncompressedData = NULL; +} + +uint32 ArjFile::read(void *dataPtr, uint32 dataSize) { + return _uncompressed->read(dataPtr, dataSize); +} + +bool ArjFile::eos() { + return _uncompressed->eos(); +} + +uint32 ArjFile::pos() { + return _uncompressed->pos(); +} + +uint32 ArjFile::size() { + return _uncompressed->size(); +} + +void ArjFile::seek(int32 offset, int whence) { + _uncompressed->seek(offset, whence); +} + +void ArjFile::init_getbits() { + _bitbuf = 0; + _subbitbuf = 0; + _bitcount = 0; + fillbuf(2 * CHAR_BIT); +} + +void ArjFile::fillbuf(int n) { // Shift bitbuf n bits left, read n bits + _bitbuf = (_bitbuf << n) & 0xFFFF; /* lose the first n bits */ + while (n > _bitcount) { + _bitbuf |= _subbitbuf << (n -= _bitcount); + if (_compsize != 0) { + _compsize--; + _subbitbuf = _currArchive.readByte(); + } else + _subbitbuf = 0; + _bitcount = CHAR_BIT; + } + _bitbuf |= _subbitbuf >> (_bitcount -= n); +} + +uint16 ArjFile::getbits(int n) { + uint16 x; + + x = _bitbuf >> (2 * CHAR_BIT - n); + fillbuf(n); + return x; +} + + + +/* Huffman decode routines */ + +void ArjFile::make_table(int nchar, byte *bitlen, int tablebits, uint16 *table, int tablesize) { + uint16 count[17], weight[17], start[18], *p; + uint i, k, len, ch, jutbits, avail, nextcode, mask; + + for (i = 1; i <= 16; i++) + count[i] = 0; + for (i = 0; (int)i < nchar; i++) + count[bitlen[i]]++; + + start[1] = 0; + for (i = 1; i <= 16; i++) + start[i + 1] = start[i] + (count[i] << (16 - i)); + if (start[17] != (uint16) (1 << 16)) + error("ArjFile::make_table(): bad file data"); + + jutbits = 16 - tablebits; + for (i = 1; (int)i <= tablebits; i++) { + start[i] >>= jutbits; + weight[i] = 1 << (tablebits - i); + } + while (i <= 16) { + weight[i] = 1 << (16 - i); + i++; + } + + i = start[tablebits + 1] >> jutbits; + if (i != (uint16) (1 << 16)) { + k = 1 << tablebits; + while (i != k) + table[i++] = 0; + } + + avail = nchar; + mask = 1 << (15 - tablebits); + for (ch = 0; (int)ch < nchar; ch++) { + if ((len = bitlen[ch]) == 0) + continue; + k = start[len]; + nextcode = k + weight[len]; + if ((int)len <= tablebits) { + if (nextcode > (uint)tablesize) + error("ArjFile::make_table(): bad file data"); + for (i = start[len]; i < nextcode; i++) + table[i] = ch; + } else { + p = &table[k >> jutbits]; + i = len - tablebits; + while (i != 0) { + if (*p == 0) { + _right[avail] = _left[avail] = 0; + *p = avail++; + } + if (k & mask) + p = &_right[*p]; + else + p = &_left[*p]; + k <<= 1; + i--; + } + *p = ch; + } + start[len] = nextcode; + } +} + +void ArjFile::read_pt_len(int nn, int nbit, int i_special) { + int i, n; + int16 c; + uint16 mask; + + n = getbits(nbit); + if (n == 0) { + c = getbits(nbit); + for (i = 0; i < nn; i++) + _pt_len[i] = 0; + for (i = 0; i < 256; i++) + _pt_table[i] = c; + } else { + i = 0; + while (i < n) { + c = _bitbuf >> (13); + if (c == 7) { + mask = 1 << (12); + while (mask & _bitbuf) { + mask >>= 1; + c++; + } + } + fillbuf((c < 7) ? 3 : (int)(c - 3)); + _pt_len[i++] = (byte)c; + if (i == i_special) { + c = getbits(2); + while (--c >= 0) + _pt_len[i++] = 0; + } + } + while (i < nn) + _pt_len[i++] = 0; + make_table(nn, _pt_len, 8, _pt_table, PTABLESIZE); // replaced sizeof + } +} + +void ArjFile::read_c_len() { + int16 i, c, n; + uint16 mask; + + n = getbits(CBIT); + if (n == 0) { + c = getbits(CBIT); + for (i = 0; i < NC; i++) + _c_len[i] = 0; + for (i = 0; i < CTABLESIZE; i++) + _c_table[i] = c; + } else { + i = 0; + while (i < n) { + c = _pt_table[_bitbuf >> (8)]; + if (c >= NT) { + mask = 1 << (7); + do { + if (_bitbuf & mask) + c = _right[c]; + else + c = _left[c]; + mask >>= 1; + } while (c >= NT); + } + fillbuf((int)(_pt_len[c])); + if (c <= 2) { + if (c == 0) + c = 1; + else if (c == 1) + c = getbits(4) + 3; + else + c = getbits(CBIT) + 20; + while (--c >= 0) + _c_len[i++] = 0; + } + else + _c_len[i++] = (byte)(c - 2); + } + while (i < NC) + _c_len[i++] = 0; + make_table(NC, _c_len, 12, _c_table, CTABLESIZE); // replaced sizeof + } +} + +uint16 ArjFile::decode_c() { + uint16 j, mask; + + if (_blocksize == 0) { + _blocksize = getbits(16); + read_pt_len(NT, TBIT, 3); + read_c_len(); + read_pt_len(NP, PBIT, -1); + } + _blocksize--; + j = _c_table[_bitbuf >> 4]; + if (j >= NC) { + mask = 1 << (3); + do { + if (_bitbuf & mask) + j = _right[j]; + else + j = _left[j]; + mask >>= 1; + } while (j >= NC); + } + fillbuf((int)(_c_len[j])); + return j; +} + +uint16 ArjFile::decode_p() { + uint16 j, mask; + + j = _pt_table[_bitbuf >> (8)]; + if (j >= NP) { + mask = 1 << (7); + do { + if (_bitbuf & mask) + j = _right[j]; + else + j = _left[j]; + mask >>= 1; + } while (j >= NP); + } + fillbuf((int)(_pt_len[j])); + if (j != 0) { + j--; + j = (1 << j) + getbits((int)j); + } + return j; +} + +void ArjFile::decode_start() { + _blocksize = 0; + init_getbits(); +} + +void ArjFile::decode() { + int16 i; + int16 j; + int16 c; + int16 r; + int32 count; + + decode_start(); + count = 0; + r = 0; + + while (count < _origsize) { + if ((c = decode_c()) <= UCHAR_MAX) { + _text[r] = (byte) c; + count++; + if (++r >= DDICSIZ) { + r = 0; + _outstream->write(_text, DDICSIZ); + } + } else { + j = c - (UCHAR_MAX + 1 - THRESHOLD); + count += j; + i = decode_p(); + if ((i = r - i - 1) < 0) + i += DDICSIZ; + if (r > i && r < DDICSIZ - MAXMATCH - 1) { + while (--j >= 0) + _text[r++] = _text[i++]; + } else { + while (--j >= 0) { + _text[r] = _text[i]; + if (++r >= DDICSIZ) { + r = 0; + _outstream->write(_text, DDICSIZ); + } + if (++i >= DDICSIZ) + i = 0; + } + } + } + } + if (r != 0) + _outstream->write(_text, r); +} + +/* Macros */ + +#define BFIL {_getbuf|=_bitbuf>>_getlen;fillbuf(CODE_BIT-_getlen);_getlen=CODE_BIT;} +#define GETBIT(c) {if(_getlen<=0)BFIL c=(_getbuf&0x8000)!=0;_getbuf<<=1;_getlen--;} +#define BPUL(l) {_getbuf<<=l;_getlen-=l;} +#define GETBITS(c,l) {if(_getlen<l)BFIL c=(uint16)_getbuf>>(CODE_BIT-l);BPUL(l)} + +int16 ArjFile::decode_ptr() { + int16 c; + int16 width; + int16 plus; + int16 pwr; + + plus = 0; + pwr = 1 << (STRTP); + for (width = (STRTP); width < (STOPP); width++) { + GETBIT(c); + if (c == 0) + break; + plus += pwr; + pwr <<= 1; + } + if (width != 0) + GETBITS(c, width); + c += plus; + return c; +} + +int16 ArjFile::decode_len() { + int16 c; + int16 width; + int16 plus; + int16 pwr; + + plus = 0; + pwr = 1 << (STRTL); + for (width = (STRTL); width < (STOPL); width++) { + GETBIT(c); + if (c == 0) + break; + plus += pwr; + pwr <<= 1; + } + if (width != 0) + GETBITS(c, width); + c += plus; + return c; +} + +void ArjFile::decode_f() { + int16 i; + int16 j; + int16 c; + int16 r; + int16 pos1; + int32 count; + + init_getbits(); + _getlen = _getbuf = 0; + count = 0; + r = 0; + + while (count < _origsize) { + c = decode_len(); + if (c == 0) { + GETBITS(c, CHAR_BIT); + _text[r] = (byte)c; + count++; + if (++r >= DDICSIZ) { + r = 0; + _outstream->write(_text, DDICSIZ); + } + } else { + j = c - 1 + THRESHOLD; + count += j; + pos1 = decode_ptr(); + if ((i = r - pos1 - 1) < 0) + i += DDICSIZ; + while (j-- > 0) { + _text[r] = _text[i]; + if (++r >= DDICSIZ) { + r = 0; + _outstream->write(_text, DDICSIZ); + } + if (++i >= DDICSIZ) + i = 0; + } + } + } + if (r != 0) + _outstream->write(_text, r); +} + + +} // End of namespace Common diff --git a/common/unarj.h b/common/unarj.h new file mode 100644 index 0000000000..63c6ca582d --- /dev/null +++ b/common/unarj.h @@ -0,0 +1,179 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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$ + * + */ + +#ifndef COMMON_UNARJ_H +#define COMMON_UNARJ_H + +#include "common/file.h" +#include "common/hash-str.h" + +namespace Common { + +#define HEADER_ID 0xEA60 +#define HEADER_ID_HI 0xEA +#define HEADER_ID_LO 0x60 +#define FIRST_HDR_SIZE 30 +#define FIRST_HDR_SIZE_V 34 +#define COMMENT_MAX 2048 +#define FNAME_MAX 512 +#define HEADERSIZE_MAX (FIRST_HDR_SIZE + 10 + FNAME_MAX + COMMENT_MAX) +#define CRC_MASK 0xFFFFFFFFL +#define MAXSFX 25000L + +#define CODE_BIT 16 +#define CHAR_BIT 8 +#define UCHAR_MAX 255 +#define THRESHOLD 3 +#define DDICSIZ 26624 +#define MAXDICBIT 16 +#define MATCHBIT 8 +#define MAXMATCH 256 +#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD) +#define NP (MAXDICBIT + 1) +#define CBIT 9 +#define NT (CODE_BIT + 3) +#define PBIT 5 +#define TBIT 5 + +#if NT > NP +#define NPT NT +#else +#define NPT NP +#endif + +#define CTABLESIZE 4096 +#define PTABLESIZE 256 + +#define STRTP 9 +#define STOPP 13 + +#define STRTL 0 +#define STOPL 7 + +struct ArjHeader { + int32 pos; + uint16 id; + uint16 headerSize; + // + byte firstHdrSize; + byte nbr; + byte xNbr; + byte hostOs; + byte flags; + byte method; + byte fileType; + byte pad; + uint32 timeStamp; + int32 compSize; + int32 origSize; + uint32 fileCRC; + uint16 entryPos; + uint16 fileMode; + uint16 hostData; + char filename[FNAME_MAX]; + char comment[COMMENT_MAX]; + uint16 extHeaderSize; + + uint32 headerCrc; +}; + +typedef HashMap<String, int, IgnoreCase_Hash, IgnoreCase_EqualTo> ArjFilesMap; + +class ArjFile : public File { +public: + ArjFile(); + ~ArjFile(); + + void registerArchive(const String &filename); + + bool open(const Common::String &filename, AccessMode mode = kFileReadMode); + void close(); + + uint32 read(void *dataPtr, uint32 dataSize); + bool eos(); + uint32 pos(); + uint32 size(); + void seek(int32 offset, int whence = SEEK_SET); + bool isOpen() { return _isOpen; } + +private: + File _currArchive; + Array<ArjHeader *> _headers; + ArjFilesMap _fileMap; + StringMap _archMap; + ReadStream *_stream; + byte *_uncompressedData; + MemoryWriteStream *_outstream; + MemoryReadStream *_uncompressed; + + bool _isOpen; + + int32 findHeader(void); + ArjHeader *readHeader(); + + void decode(); + void decode_f(); + + uint16 _bitbuf; + int32 _compsize; + int32 _origsize; + byte _subbitbuf; + int _bitcount; + + void init_getbits(); + void fillbuf(int n); + uint16 getbits(int n); + + + void make_table(int nchar, byte *bitlen, int tablebits, uint16 *table, int tablesize); + void read_pt_len(int nn, int nbit, int i_special); + void read_c_len(void); + uint16 decode_c(void); + uint16 decode_p(void); + void decode_start(void); + int16 decode_ptr(void); + int16 decode_len(void); + +private: + byte _text[DDICSIZ]; + + int16 _getlen; + int16 _getbuf; + + uint16 _left[2 * NC - 1]; + uint16 _right[2 * NC - 1]; + byte _c_len[NC]; + byte _pt_len[NPT]; + + uint16 _c_table[CTABLESIZE]; + uint16 _pt_table[PTABLESIZE]; + uint16 _blocksize; + + +}; + +} // End of namespace Common + +#endif |