2008-06-01 11:43:20 +00:00
/* 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 .
*
2021-12-26 18:47:58 +01:00
* 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 3 of the License , or
* ( at your option ) any later version .
2008-06-01 11:43:20 +00:00
*
* 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
2021-12-26 18:47:58 +01:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2008-06-01 11:43:20 +00:00
*
*/
2008-09-13 17:41:42 +00:00
//
// This file is heavily based on the arj code available under the GPL
// from http://arj.sourceforge.net/ , version 3.10.22 .
2008-06-01 11:43:20 +00:00
# include "common/scummsys.h"
2008-11-07 09:57:54 +00:00
# include "common/archive.h"
2009-01-30 05:25:17 +00:00
# include "common/debug.h"
2022-12-01 11:53:02 +01:00
# include "common/compression/unarj.h"
2010-02-08 21:51:36 +00:00
# include "common/file.h"
# include "common/hash-str.h"
2010-11-19 17:03:07 +00:00
# include "common/memstream.h"
# include "common/bufferedstream.h"
2011-04-24 11:34:27 +03:00
# include "common/textconsole.h"
2008-06-01 11:43:20 +00:00
namespace Common {
2008-11-07 09:46:12 +00:00
# define ARJ_UCHAR_MAX 255
# define ARJ_CHAR_BIT 8
# define ARJ_COMMENT_MAX 2048
# define ARJ_FILENAME_MAX 512
# define ARJ_CODE_BIT 16
# define ARJ_THRESHOLD 3
# define ARJ_DICSIZ 26624
# define ARJ_FDICSIZ ARJ_DICSIZ
# define ARJ_MAXDICBIT 16
# define ARJ_MAXMATCH 256
# define ARJ_NC (ARJ_UCHAR_MAX + ARJ_MAXMATCH + 2 - ARJ_THRESHOLD)
# define ARJ_NP (ARJ_MAXDICBIT + 1)
# define ARJ_NT (ARJ_CODE_BIT + 3)
# if ARJ_NT > ARJ_NP
# define ARJ_NPT ARJ_NT
# else
# define ARJ_NPT ARJ_NP
# endif
# define ARJ_CTABLESIZE 4096
# define ARJ_PTABLESIZE 256
2010-02-08 16:56:04 +00:00
// these struct represents a file inside an Arj archive
2008-11-07 09:46:12 +00:00
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 [ ARJ_FILENAME_MAX ] ;
char comment [ ARJ_COMMENT_MAX ] ;
uint32 headerCrc ;
} ;
2008-11-07 10:14:57 +00:00
static int32 findHeader ( SeekableReadStream & stream ) ;
static ArjHeader * readHeader ( SeekableReadStream & stream ) ;
2008-11-07 09:46:12 +00:00
class ArjDecoder {
public :
2008-11-07 10:14:57 +00:00
ArjDecoder ( const ArjHeader * hdr ) {
_compsize = hdr - > compSize ;
2018-04-05 20:25:28 +02:00
_compressed = nullptr ;
_outstream = nullptr ;
2013-10-05 07:02:18 +01:00
_bitbuf = 0 ;
_bytebuf = 0 ;
_bitcount = 0 ;
_blocksize = 0 ;
2008-11-07 10:14:57 +00:00
}
2008-12-22 11:22:15 +00:00
2008-11-07 10:14:57 +00:00
~ ArjDecoder ( ) {
delete _compressed ;
delete _outstream ;
}
2008-11-07 09:46:12 +00:00
2008-11-07 10:14:57 +00:00
void decode ( int32 origsize ) ;
void decode_f ( int32 origsize ) ;
2008-11-07 09:46:12 +00:00
2010-11-18 17:02:51 +00:00
ReadStream * _compressed ;
2008-11-07 09:46:12 +00:00
MemoryWriteStream * _outstream ;
//protected:
uint16 _bitbuf ;
uint16 _bytebuf ;
int32 _compsize ;
int _bitcount ;
void init_getbits ( ) ;
void fillbuf ( int n ) ;
uint16 getbits ( int n ) ;
2008-12-22 11:22:15 +00:00
2008-11-07 09:46:12 +00:00
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 _ntext [ ARJ_FDICSIZ ] ;
uint16 _left [ 2 * ARJ_NC - 1 ] ;
uint16 _right [ 2 * ARJ_NC - 1 ] ;
byte _c_len [ ARJ_NC ] ;
byte _pt_len [ ARJ_NPT ] ;
uint16 _c_table [ ARJ_CTABLESIZE ] ;
uint16 _pt_table [ ARJ_PTABLESIZE ] ;
uint16 _blocksize ;
} ;
2008-09-13 17:41:42 +00:00
# define HEADER_ID 0xEA60
# define HEADER_ID_HI 0xEA
# define HEADER_ID_LO 0x60
# define FIRST_HDR_SIZE 30
# define HEADERSIZE_MAX (FIRST_HDR_SIZE + 10 + ARJ_FILENAME_MAX + ARJ_COMMENT_MAX)
# define CRC_MASK 0xFFFFFFFFL
# define HSLIMIT_ARJ 524288L
# define CBIT 9
# define PBIT 5
# define TBIT 5
2010-02-08 16:14:42 +00:00
// Source for CRC32::init, CRC32::checksum : crc32.c
class CRC32 {
static uint32 _table [ 256 ] ;
static bool _initialized ;
2008-09-13 17:41:42 +00:00
2010-02-08 16:14:42 +00:00
private :
static void init ( ) {
const uint32 poly = 0xEDB88320 ;
int i , j ;
uint32 r ;
for ( i = 0 ; i < 256 ; i + + ) {
r = i ;
for ( j = 0 ; j < 8 ; j + + )
if ( r & 1 )
r = ( r > > 1 ) ^ poly ;
else
r > > = 1 ;
_table [ i ] = r ;
}
2008-06-01 11:43:20 +00:00
2010-02-08 16:14:42 +00:00
_initialized = true ;
2008-06-01 11:43:20 +00:00
}
2010-02-08 16:14:42 +00:00
public :
static uint32 checksum ( byte * data , int len ) {
if ( ! _initialized ) {
init ( ) ;
}
uint32 CRC = 0xFFFFFFFF ;
int i ;
for ( i = 0 ; i < len ; i + + )
CRC = ( CRC > > 8 ) ^ _table [ ( CRC ^ data [ i ] ) & 0xFF ] ;
return CRC ^ 0xFFFFFFFF ;
}
} ;
2008-06-01 11:43:20 +00:00
2010-02-08 16:14:42 +00:00
bool CRC32 : : _initialized = false ;
uint32 CRC32 : : _table [ 256 ] ;
2008-06-01 11:43:20 +00:00
2008-09-13 17:41:42 +00:00
// Source for findHeader and readHeader: arj_arcv.c
2008-11-07 10:14:57 +00:00
int32 findHeader ( SeekableReadStream & stream ) {
2008-09-13 17:41:42 +00:00
long end_pos , tmp_pos ;
int id ;
2008-06-01 11:43:20 +00:00
byte header [ HEADERSIZE_MAX ] ;
uint32 crc ;
2008-09-13 17:41:42 +00:00
uint16 basic_hdr_size ;
2008-06-01 11:43:20 +00:00
2008-11-07 09:46:12 +00:00
tmp_pos = stream . pos ( ) ;
stream . seek ( 0L , SEEK_END ) ;
end_pos = stream . pos ( ) - 2 ;
2008-09-13 17:41:42 +00:00
if ( end_pos > = tmp_pos + HSLIMIT_ARJ )
end_pos = tmp_pos + HSLIMIT_ARJ ;
while ( tmp_pos < end_pos ) {
2008-11-07 09:46:12 +00:00
stream . seek ( tmp_pos , SEEK_SET ) ;
id = stream . readByte ( ) ;
2008-09-13 17:41:42 +00:00
while ( tmp_pos < end_pos ) {
2008-10-05 12:18:16 +00:00
if ( id = = HEADER_ID_LO ) {
2008-11-07 09:46:12 +00:00
if ( ( id = stream . readByte ( ) ) = = HEADER_ID_HI )
2008-09-13 17:41:42 +00:00
break ;
2008-10-05 12:18:16 +00:00
} else
2008-11-07 09:46:12 +00:00
id = stream . readByte ( ) ;
2008-09-13 17:41:42 +00:00
tmp_pos + + ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
if ( tmp_pos > = end_pos )
return - 1 ;
2008-11-07 09:46:12 +00:00
if ( ( basic_hdr_size = stream . readUint16LE ( ) ) < = HEADERSIZE_MAX ) {
stream . read ( header , basic_hdr_size ) ;
2010-02-08 16:14:42 +00:00
crc = CRC32 : : checksum ( header , basic_hdr_size ) ;
2008-11-07 09:46:12 +00:00
if ( crc = = stream . readUint32LE ( ) ) {
stream . seek ( tmp_pos , SEEK_SET ) ;
2008-09-13 17:41:42 +00:00
return tmp_pos ;
2008-06-01 11:43:20 +00:00
}
}
2008-09-13 17:41:42 +00:00
tmp_pos + + ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
return - 1 ;
2008-06-01 11:43:20 +00:00
}
2008-11-07 10:14:57 +00:00
ArjHeader * readHeader ( SeekableReadStream & stream ) {
2008-06-01 11:43:20 +00:00
ArjHeader header ;
ArjHeader * head ;
byte headData [ HEADERSIZE_MAX ] ;
2008-09-13 17:41:42 +00:00
// Strictly check the header ID
2008-11-07 09:46:12 +00:00
header . id = stream . readUint16LE ( ) ;
2008-06-01 11:43:20 +00:00
if ( header . id ! = HEADER_ID ) {
warning ( " ArjFile::readHeader(): Bad header ID (%x) " , header . id ) ;
2018-04-05 20:25:28 +02:00
return nullptr ;
2008-06-01 11:43:20 +00:00
}
2008-11-07 09:46:12 +00:00
header . headerSize = stream . readUint16LE ( ) ;
2008-06-01 11:43:20 +00:00
if ( header . headerSize = = 0 )
2018-04-05 20:25:28 +02:00
return nullptr ; // end of archive
2008-06-01 11:43:20 +00:00
if ( header . headerSize > HEADERSIZE_MAX ) {
warning ( " ArjFile::readHeader(): Bad header " ) ;
2018-04-05 20:25:28 +02:00
return nullptr ;
2008-06-01 11:43:20 +00:00
}
2008-11-07 09:46:12 +00:00
int rSize = stream . read ( headData , header . headerSize ) ;
2008-06-01 11:43:20 +00:00
MemoryReadStream readS ( headData , rSize ) ;
2008-11-07 09:46:12 +00:00
header . headerCrc = stream . readUint32LE ( ) ;
2010-02-08 16:14:42 +00:00
if ( CRC32 : : checksum ( headData , header . headerSize ) ! = header . headerCrc ) {
2008-06-01 11:43:20 +00:00
warning ( " ArjFile::readHeader(): Bad header CRC " ) ;
2018-04-05 20:25:28 +02:00
return nullptr ;
2008-06-01 11:43:20 +00:00
}
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 ( ) ;
2008-09-13 17:41:42 +00:00
( void ) readS . readByte ( ) ; // password_modifier
2008-06-01 11:43:20 +00:00
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 ( ) ;
2008-09-13 17:41:42 +00:00
// static int check_file_size()
2008-06-01 11:43:20 +00:00
if ( header . origSize < 0 | | header . compSize < 0 ) {
warning ( " ArjFile::readHeader(): Wrong file size " ) ;
2018-04-05 20:25:28 +02:00
return nullptr ;
2008-06-01 11:43:20 +00:00
}
2011-08-06 09:47:19 +02:00
strlcpy ( header . filename , ( const char * ) & headData [ header . firstHdrSize ] , ARJ_FILENAME_MAX ) ;
strlcpy ( header . comment , ( const char * ) & headData [ header . firstHdrSize + strlen ( header . filename ) + 1 ] , ARJ_COMMENT_MAX ) ;
2008-06-01 11:43:20 +00:00
2008-12-22 11:22:15 +00:00
// Process extended headers, if any
uint16 extHeaderSize ;
2008-11-07 09:46:12 +00:00
while ( ( extHeaderSize = stream . readUint16LE ( ) ) ! = 0 )
stream . seek ( ( long ) ( extHeaderSize + 4 ) , SEEK_CUR ) ;
2008-06-01 11:43:20 +00:00
2008-11-07 09:46:12 +00:00
header . pos = stream . pos ( ) ;
2008-06-01 11:43:20 +00:00
head = new ArjHeader ( header ) ;
return head ;
}
2008-09-13 17:41:42 +00:00
// Source for init_getbits: arj_file.c (decode_start_stub)
2008-11-07 09:46:12 +00:00
void ArjDecoder : : init_getbits ( ) {
2008-06-01 11:43:20 +00:00
_bitbuf = 0 ;
2008-09-13 17:41:42 +00:00
_bytebuf = 0 ;
2008-06-01 11:43:20 +00:00
_bitcount = 0 ;
2008-09-13 17:41:42 +00:00
fillbuf ( ARJ_CHAR_BIT * 2 ) ;
2008-06-01 11:43:20 +00:00
}
2008-12-22 11:22:15 +00:00
// Source for fillbuf, getbits: decode.c
2008-11-07 09:46:12 +00:00
void ArjDecoder : : fillbuf ( int n ) {
2008-09-13 17:41:42 +00:00
while ( _bitcount < n ) {
_bitbuf = ( _bitbuf < < _bitcount ) | ( _bytebuf > > ( 8 - _bitcount ) ) ;
n - = _bitcount ;
if ( _compsize > 0 ) {
2008-06-01 11:43:20 +00:00
_compsize - - ;
2008-09-13 17:41:42 +00:00
_bytebuf = _compressed - > readByte ( ) ;
} else {
_bytebuf = 0 ;
}
_bitcount = 8 ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
_bitcount - = n ;
_bitbuf = ( _bitbuf < < n ) | ( _bytebuf > > ( 8 - n ) ) ;
_bytebuf < < = n ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
// Reads a series of bits into the input buffer */
2008-11-07 09:46:12 +00:00
uint16 ArjDecoder : : getbits ( int n ) {
2008-09-13 17:41:42 +00:00
uint16 rc ;
2008-06-01 11:43:20 +00:00
2008-09-13 17:41:42 +00:00
rc = _bitbuf > > ( ARJ_CODE_BIT - n ) ;
2008-06-01 11:43:20 +00:00
fillbuf ( n ) ;
2008-09-13 17:41:42 +00:00
return rc ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
// Huffman decode routines
// Source: decode.c
2008-06-01 11:43:20 +00:00
2008-09-13 17:41:42 +00:00
// Creates a table for decoding
2008-11-07 09:46:12 +00:00
void ArjDecoder : : make_table ( int nchar , byte * bitlen , int tablebits , uint16 * table , int tablesize ) {
2008-09-13 17:41:42 +00:00
uint16 count [ 17 ] , weight [ 17 ] , start [ 18 ] ;
uint16 * p ;
2008-06-01 11:43:20 +00:00
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 ) )
2008-11-07 09:46:12 +00:00
error ( " ArjDecoder::make_table(): bad file data " ) ;
2008-06-01 11:43:20 +00:00
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 )
2008-11-07 09:46:12 +00:00
error ( " ArjDecoder::make_table(): bad file data " ) ;
2008-06-01 11:43:20 +00:00
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 ;
2008-09-13 17:41:42 +00:00
* p = avail ;
avail + + ;
2008-06-01 11:43:20 +00:00
}
if ( k & mask )
p = & _right [ * p ] ;
else
p = & _left [ * p ] ;
k < < = 1 ;
i - - ;
}
* p = ch ;
}
start [ len ] = nextcode ;
}
}
2008-09-13 17:41:42 +00:00
// Reads length of data pending
2008-11-07 09:46:12 +00:00
void ArjDecoder : : read_pt_len ( int nn , int nbit , int i_special ) {
2008-06-01 11:43:20 +00:00
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 ) {
2008-09-13 17:41:42 +00:00
c = _bitbuf > > 13 ;
2008-06-01 11:43:20 +00:00
if ( c = = 7 ) {
2008-09-13 17:41:42 +00:00
mask = 1 < < 12 ;
2008-06-01 11:43:20 +00:00
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 ;
2008-09-13 17:41:42 +00:00
make_table ( nn , _pt_len , 8 , _pt_table , ARJ_PTABLESIZE ) ;
2008-06-01 11:43:20 +00:00
}
}
2008-09-13 17:41:42 +00:00
// Reads a character table
2008-11-07 09:46:12 +00:00
void ArjDecoder : : read_c_len ( ) {
2008-06-01 11:43:20 +00:00
int16 i , c , n ;
uint16 mask ;
n = getbits ( CBIT ) ;
if ( n = = 0 ) {
c = getbits ( CBIT ) ;
2008-09-13 17:41:42 +00:00
for ( i = 0 ; i < ARJ_NC ; i + + )
2008-06-01 11:43:20 +00:00
_c_len [ i ] = 0 ;
2008-09-13 17:41:42 +00:00
for ( i = 0 ; i < ARJ_CTABLESIZE ; i + + )
2008-06-01 11:43:20 +00:00
_c_table [ i ] = c ;
} else {
i = 0 ;
while ( i < n ) {
c = _pt_table [ _bitbuf > > ( 8 ) ] ;
2008-09-13 17:41:42 +00:00
if ( c > = ARJ_NT ) {
mask = 1 < < 7 ;
2008-06-01 11:43:20 +00:00
do {
if ( _bitbuf & mask )
c = _right [ c ] ;
else
c = _left [ c ] ;
mask > > = 1 ;
2008-09-13 17:41:42 +00:00
} while ( c > = ARJ_NT ) ;
2008-06-01 11:43:20 +00:00
}
fillbuf ( ( int ) ( _pt_len [ c ] ) ) ;
if ( c < = 2 ) {
if ( c = = 0 )
c = 1 ;
2008-09-13 17:41:42 +00:00
else if ( c = = 1 ) {
c = getbits ( 4 ) ;
c + = 3 ;
} else {
c = getbits ( CBIT ) ;
c + = 20 ;
}
2008-06-01 11:43:20 +00:00
while ( - - c > = 0 )
_c_len [ i + + ] = 0 ;
}
else
_c_len [ i + + ] = ( byte ) ( c - 2 ) ;
}
2008-09-13 17:41:42 +00:00
while ( i < ARJ_NC )
2008-06-01 11:43:20 +00:00
_c_len [ i + + ] = 0 ;
2008-09-13 17:41:42 +00:00
make_table ( ARJ_NC , _c_len , 12 , _c_table , ARJ_CTABLESIZE ) ;
2008-06-01 11:43:20 +00:00
}
}
2008-09-13 17:41:42 +00:00
// Decodes a single character
2008-11-07 09:46:12 +00:00
uint16 ArjDecoder : : decode_c ( ) {
2008-06-01 11:43:20 +00:00
uint16 j , mask ;
if ( _blocksize = = 0 ) {
2008-09-13 17:41:42 +00:00
_blocksize = getbits ( ARJ_CODE_BIT ) ;
read_pt_len ( ARJ_NT , TBIT , 3 ) ;
2008-06-01 11:43:20 +00:00
read_c_len ( ) ;
2008-09-13 17:41:42 +00:00
read_pt_len ( ARJ_NP , PBIT , - 1 ) ;
2008-06-01 11:43:20 +00:00
}
_blocksize - - ;
j = _c_table [ _bitbuf > > 4 ] ;
2008-09-13 17:41:42 +00:00
if ( j > = ARJ_NC ) {
mask = 1 < < 3 ;
2008-06-01 11:43:20 +00:00
do {
if ( _bitbuf & mask )
j = _right [ j ] ;
else
j = _left [ j ] ;
mask > > = 1 ;
2008-09-13 17:41:42 +00:00
} while ( j > = ARJ_NC ) ;
2008-06-01 11:43:20 +00:00
}
fillbuf ( ( int ) ( _c_len [ j ] ) ) ;
return j ;
}
2008-09-13 17:41:42 +00:00
// Decodes a control character
2008-11-07 09:46:12 +00:00
uint16 ArjDecoder : : decode_p ( ) {
2008-06-01 11:43:20 +00:00
uint16 j , mask ;
2008-09-13 17:41:42 +00:00
j = _pt_table [ _bitbuf > > 8 ] ;
if ( j > = ARJ_NP ) {
mask = 1 < < 7 ;
2008-06-01 11:43:20 +00:00
do {
if ( _bitbuf & mask )
j = _right [ j ] ;
else
j = _left [ j ] ;
mask > > = 1 ;
2008-09-13 17:41:42 +00:00
} while ( j > = ARJ_NP ) ;
2008-06-01 11:43:20 +00:00
}
fillbuf ( ( int ) ( _pt_len [ j ] ) ) ;
if ( j ! = 0 ) {
j - - ;
j = ( 1 < < j ) + getbits ( ( int ) j ) ;
}
return j ;
}
2008-09-13 17:41:42 +00:00
// Initializes memory for decoding
2008-11-07 09:46:12 +00:00
void ArjDecoder : : decode_start ( ) {
2008-06-01 11:43:20 +00:00
_blocksize = 0 ;
init_getbits ( ) ;
}
2008-09-13 17:41:42 +00:00
// Decodes the entire file
2008-11-07 10:14:57 +00:00
void ArjDecoder : : decode ( int32 origsize ) {
2008-06-01 11:43:20 +00:00
int16 i ;
int16 r ;
2008-09-13 17:41:42 +00:00
int16 c ;
int16 j ;
2008-06-01 11:43:20 +00:00
int32 count ;
decode_start ( ) ;
2008-11-07 10:14:57 +00:00
count = origsize ;
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-09-13 17:41:42 +00:00
while ( count > 0 ) {
2008-06-01 20:05:21 +00:00
if ( ( c = decode_c ( ) ) < = ARJ_UCHAR_MAX ) {
2008-09-13 17:41:42 +00:00
_ntext [ r ] = ( byte ) c ;
count - - ;
if ( + + r > = ARJ_DICSIZ ) {
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-09-13 17:41:42 +00:00
_outstream - > write ( _ntext , ARJ_DICSIZ ) ;
2008-06-01 11:43:20 +00:00
}
} else {
2008-09-13 17:41:42 +00:00
j = c - ( ARJ_UCHAR_MAX + 1 - ARJ_THRESHOLD ) ;
count - = j ;
i = r - decode_p ( ) - 1 ;
if ( i < 0 )
i + = ARJ_DICSIZ ;
if ( r > i & & r < ARJ_DICSIZ - ARJ_MAXMATCH - 1 ) {
2008-06-01 11:43:20 +00:00
while ( - - j > = 0 )
2008-09-13 17:41:42 +00:00
_ntext [ r + + ] = _ntext [ i + + ] ;
2008-06-01 11:43:20 +00:00
} else {
while ( - - j > = 0 ) {
2008-09-13 17:41:42 +00:00
_ntext [ r ] = _ntext [ i ] ;
if ( + + r > = ARJ_DICSIZ ) {
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-09-13 17:41:42 +00:00
_outstream - > write ( _ntext , ARJ_DICSIZ ) ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
if ( + + i > = ARJ_DICSIZ )
2008-06-01 11:43:20 +00:00
i = 0 ;
}
}
}
}
2008-09-13 17:41:42 +00:00
if ( r > 0 )
_outstream - > write ( _ntext , r ) ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
// Backward pointer decoding
2008-11-07 09:46:12 +00:00
int16 ArjDecoder : : decode_ptr ( ) {
2008-06-01 20:05:21 +00:00
int16 c = 0 ;
2008-06-01 11:43:20 +00:00
int16 width ;
int16 plus ;
int16 pwr ;
plus = 0 ;
2008-12-22 11:22:15 +00:00
pwr = 1 < < 9 ;
2008-09-13 17:41:42 +00:00
for ( width = 9 ; width < 13 ; width + + ) {
c = getbits ( 1 ) ;
2008-06-01 11:43:20 +00:00
if ( c = = 0 )
break ;
plus + = pwr ;
pwr < < = 1 ;
}
if ( width ! = 0 )
2008-09-13 17:41:42 +00:00
c = getbits ( width ) ;
2008-06-01 11:43:20 +00:00
c + = plus ;
return c ;
}
2008-09-13 17:41:42 +00:00
// Reference length decoding
2008-11-07 09:46:12 +00:00
int16 ArjDecoder : : decode_len ( ) {
2008-06-01 20:05:21 +00:00
int16 c = 0 ;
2008-06-01 11:43:20 +00:00
int16 width ;
int16 plus ;
int16 pwr ;
plus = 0 ;
2008-09-13 17:41:42 +00:00
pwr = 1 ;
for ( width = 0 ; width < 7 ; width + + ) {
c = getbits ( 1 ) ;
2008-06-01 11:43:20 +00:00
if ( c = = 0 )
break ;
plus + = pwr ;
pwr < < = 1 ;
}
if ( width ! = 0 )
2008-09-13 17:41:42 +00:00
c = getbits ( width ) ;
2008-06-01 11:43:20 +00:00
c + = plus ;
return c ;
}
2008-09-13 17:41:42 +00:00
// Decodes the entire file, using method 4
2008-11-07 10:14:57 +00:00
void ArjDecoder : : decode_f ( int32 origsize ) {
2008-06-01 11:43:20 +00:00
int16 i ;
int16 j ;
int16 c ;
int16 r ;
2008-09-13 17:41:42 +00:00
uint32 ncount ;
2008-06-01 11:43:20 +00:00
init_getbits ( ) ;
2008-09-13 17:41:42 +00:00
ncount = 0 ;
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-11-07 10:14:57 +00:00
while ( ncount < ( uint32 ) origsize ) {
2008-06-01 11:43:20 +00:00
c = decode_len ( ) ;
if ( c = = 0 ) {
2008-09-13 17:41:42 +00:00
ncount + + ;
_ntext [ r ] = ( byte ) getbits ( 8 ) ;
if ( + + r > = ARJ_FDICSIZ ) {
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-09-13 17:41:42 +00:00
_outstream - > write ( _ntext , ARJ_FDICSIZ ) ;
2008-06-01 11:43:20 +00:00
}
} else {
2008-09-13 17:41:42 +00:00
j = c - 1 + ARJ_THRESHOLD ;
ncount + = j ;
if ( ( i = r - decode_ptr ( ) - 1 ) < 0 )
i + = ARJ_FDICSIZ ;
2008-06-01 11:43:20 +00:00
while ( j - - > 0 ) {
2008-09-13 17:41:42 +00:00
_ntext [ r ] = _ntext [ i ] ;
if ( + + r > = ARJ_FDICSIZ ) {
2008-06-01 11:43:20 +00:00
r = 0 ;
2008-09-13 17:41:42 +00:00
_outstream - > write ( _ntext , ARJ_FDICSIZ ) ;
2008-06-01 11:43:20 +00:00
}
2008-09-13 17:41:42 +00:00
if ( + + i > = ARJ_FDICSIZ )
2008-06-01 11:43:20 +00:00
i = 0 ;
}
}
}
if ( r ! = 0 )
2008-09-13 17:41:42 +00:00
_outstream - > write ( _ntext , r ) ;
2008-06-01 11:43:20 +00:00
}
2010-02-08 16:15:05 +00:00
# pragma mark ArjArchive implementation
2008-06-01 11:43:20 +00:00
2022-11-27 20:50:43 +01:00
struct ArjFileChunk {
ArjHeader * _header ;
uint _volume ;
ArjFileChunk ( ArjHeader * header , uint volume ) : _header ( header ) , _volume ( volume ) { }
} ;
typedef HashMap < String , Array < ArjFileChunk > , IgnoreCase_Hash , IgnoreCase_EqualTo > ArjHeadersMap ;
2010-02-08 21:46:50 +00:00
2022-11-30 01:49:41 +01:00
class ArjArchive : public MemcachingCaseInsensitiveArchive {
2010-02-09 01:07:39 +00:00
ArjHeadersMap _headers ;
2022-11-27 20:50:43 +01:00
Array < String > _arjFilenames ;
2010-02-08 21:46:50 +00:00
public :
2022-11-27 20:50:43 +01:00
ArjArchive ( const Array < String > & names ) ;
2010-02-08 21:46:50 +00:00
virtual ~ ArjArchive ( ) ;
2011-08-06 09:47:19 +02:00
// Archive implementation
2021-11-13 22:30:12 +02:00
bool hasFile ( const Path & path ) const override ;
int listMembers ( ArchiveMemberList & list ) const override ;
const ArchiveMemberPtr getMember ( const Path & path ) const override ;
2022-11-30 01:49:41 +01:00
Common : : SharedArchiveContents readContentsForPath ( const Common : : String & translated ) const override ;
Common : : String translatePath ( const Common : : Path & path ) const override {
return path . toString ( ) ;
}
2010-02-08 21:46:50 +00:00
} ;
2022-11-27 20:50:43 +01:00
ArjArchive : : ~ ArjArchive ( ) {
debug ( 0 , " ArjArchive Destructor Called " ) ;
ArjHeadersMap : : iterator it = _headers . begin ( ) ;
for ( ; it ! = _headers . end ( ) ; + + it ) {
for ( uint i = 0 ; i < it - > _value . size ( ) ; i + + )
delete it - > _value [ i ] . _header ;
}
}
2010-07-21 18:17:51 +00:00
2022-11-27 20:50:43 +01:00
ArjArchive : : ArjArchive ( const Array < String > & filenames ) : _arjFilenames ( filenames ) {
for ( uint i = 0 ; i < _arjFilenames . size ( ) ; i + + ) {
File arjFile ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
if ( ! arjFile . open ( _arjFilenames [ i ] ) ) {
warning ( " ArjArchive::ArjArchive(): Could not find the archive file " ) ;
return ;
}
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
int32 firstHeaderOffset = findHeader ( arjFile ) ;
2011-01-28 02:56:07 +00:00
2022-11-27 20:50:43 +01:00
if ( firstHeaderOffset < 0 ) {
warning ( " ArjArchive::ArjArchive(): Could not find a valid header " ) ;
return ;
}
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
ArjHeader * header = nullptr ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
arjFile . seek ( firstHeaderOffset , SEEK_SET ) ;
if ( ( header = readHeader ( arjFile ) ) = = nullptr )
return ;
delete header ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
while ( ( header = readHeader ( arjFile ) ) ! = nullptr ) {
_headers [ header - > filename ] . push_back ( ArjFileChunk ( header , i ) ) ;
arjFile . seek ( header - > compSize , SEEK_CUR ) ;
}
2010-02-09 01:07:39 +00:00
}
2022-12-01 11:53:02 +01:00
2022-11-27 20:50:43 +01:00
debug ( 0 , " ArjArchive::ArjArchive(%d volume(s) starting with %s): Located %d files " , filenames . size ( ) , filenames . empty ( ) ? " " : filenames [ 0 ] . c_str ( ) , _headers . size ( ) ) ;
2010-02-08 16:15:05 +00:00
}
2010-02-08 16:14:23 +00:00
2021-08-01 19:33:22 -04:00
bool ArjArchive : : hasFile ( const Path & path ) const {
String name = path . toString ( ) ;
2010-02-09 01:07:39 +00:00
return _headers . contains ( name ) ;
2010-02-08 16:15:05 +00:00
}
2011-12-13 17:20:25 +01:00
int ArjArchive : : listMembers ( ArchiveMemberList & list ) const {
2010-02-08 16:15:05 +00:00
int matches = 0 ;
2011-12-13 17:20:25 +01:00
ArjHeadersMap : : const_iterator it = _headers . begin ( ) ;
2010-02-08 16:15:05 +00:00
for ( ; it ! = _headers . end ( ) ; + + it ) {
2022-11-27 20:50:43 +01:00
list . push_back ( ArchiveMemberList : : value_type ( new GenericArchiveMember ( it - > _value [ 0 ] . _header - > filename , this ) ) ) ;
2010-02-08 16:15:05 +00:00
matches + + ;
2010-02-08 16:14:23 +00:00
}
2010-02-08 16:15:05 +00:00
return matches ;
}
2021-08-01 19:33:22 -04:00
const ArchiveMemberPtr ArjArchive : : getMember ( const Path & path ) const {
String name = path . toString ( ) ;
2010-02-08 16:15:05 +00:00
if ( ! hasFile ( name ) )
return ArchiveMemberPtr ( ) ;
return ArchiveMemberPtr ( new GenericArchiveMember ( name , this ) ) ;
}
2022-11-30 01:49:41 +01:00
Common : : SharedArchiveContents ArjArchive : : readContentsForPath ( const Common : : String & name ) const {
2010-02-09 01:07:39 +00:00
if ( ! _headers . contains ( name ) ) {
2022-11-30 01:49:41 +01:00
return Common : : SharedArchiveContents ( ) ;
2010-02-08 16:15:05 +00:00
}
2022-11-27 20:50:43 +01:00
const Array < ArjFileChunk > & hdrs = _headers [ name ] ;
2010-02-08 16:15:05 +00:00
2022-11-27 20:50:43 +01:00
uint64 uncompressedSize = 0 ;
uint totalChunks ;
for ( totalChunks = 0 ; totalChunks < hdrs . size ( ) ; totalChunks + + ) {
uncompressedSize + = hdrs [ totalChunks ] . _header - > origSize ;
if ( ! ( hdrs [ totalChunks ] . _header - > flags & 0x4 ) ) {
totalChunks + + ;
break ;
}
}
// Prevent overflows
if ( uncompressedSize > 0x70000000 )
2022-11-30 01:49:41 +01:00
return Common : : SharedArchiveContents ( ) ;
2010-02-08 16:14:23 +00:00
// TODO: It would be good if ArjFile could decompress files in a streaming
// mode, so it would not need to pre-allocate the entire output.
2022-11-30 01:49:41 +01:00
byte * uncompressedData = new byte [ uncompressedSize ] ;
2022-11-27 20:50:43 +01:00
uint32 uncompressedPtr = 0 ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
for ( uint chunk = 0 ; chunk < totalChunks ; chunk + + ) {
File archiveFile ;
ArjHeader * hdr = hdrs [ chunk ] . _header ;
archiveFile . open ( _arjFilenames [ hdrs [ chunk ] . _volume ] ) ;
archiveFile . seek ( hdr - > pos , SEEK_SET ) ;
if ( hdr - > method = = 0 ) { // store
int32 len = archiveFile . read ( uncompressedData + uncompressedPtr , hdr - > origSize ) ;
assert ( len = = hdr - > origSize ) ;
} else {
ArjDecoder * decoder = new ArjDecoder ( hdr ) ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
// TODO: It might not be appropriate to use this wrapper inside ArjFile.
// If reading from archiveFile directly is too slow to be usable,
// maybe the filesystem code should instead wrap its files
// in a BufferedReadStream.
decoder - > _compressed = wrapBufferedReadStream ( & archiveFile , 4096 , DisposeAfterUse : : NO ) ;
decoder - > _outstream = new MemoryWriteStream ( uncompressedData + uncompressedPtr , hdr - > origSize ) ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
if ( hdr - > method = = 1 | | hdr - > method = = 2 | | hdr - > method = = 3 )
decoder - > decode ( hdr - > origSize ) ;
else if ( hdr - > method = = 4 )
decoder - > decode_f ( hdr - > origSize ) ;
2010-02-08 16:14:23 +00:00
2022-11-27 20:50:43 +01:00
delete decoder ;
}
uncompressedPtr + = hdr - > origSize ;
2010-02-08 16:14:23 +00:00
}
2022-11-30 01:49:41 +01:00
return Common : : SharedArchiveContents ( uncompressedData , uncompressedSize ) ;
2010-02-08 16:15:05 +00:00
}
2010-02-08 21:46:50 +00:00
Archive * makeArjArchive ( const String & name ) {
2022-11-27 20:50:43 +01:00
return new ArjArchive ( { name } ) ;
}
Archive * makeArjArchive ( const Array < String > & names ) {
return new ArjArchive ( names ) ;
2010-02-08 21:46:50 +00:00
}
2008-06-01 11:43:20 +00:00
} // End of namespace Common