* This file is a modify version of dictd-1.9.7's data.c
*
* data.c --
* Created: Tue Jul 16 12:45:41 1996 by faith@dict.org
* Revised: Sat Mar 30 10:46:06 2002 by faith@dict.org
* Copyright 1996, 1997, 1998, 2000, 2002 Rickard E. Faith (faith@dict.org)
* Copyright (C) 2003-2003 Hu Zheng <huzheng_001@163.com>
* Copyright 2011 kubtek <kubtek@mail.com>
*
* This file is part of StarDict.
*
* StarDict 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.
*
* StarDict 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 StarDict. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifdef _WIN32
# include <io.h>
#else
# include <unistd.h>
#endif
#include <limits.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "dictziplib.h"
#define USE_CACHE 1
#define BUFFERSIZE 10240
* Output buffer must be greater than or
* equal to 110% of input buffer size, plus
* 12 bytes.
*/
#define OUT_BUFFER_SIZE 0xffffL
#define IN_BUFFER_SIZE ((unsigned long)((double)(OUT_BUFFER_SIZE - 12) * 0.89))
#define GZ_MAGIC1 0x1f
#define GZ_MAGIC2 0x8b
#define GZ_FTEXT 0x01
#define GZ_FHCRC 0x02
#define GZ_FEXTRA 0x04
#define GZ_FNAME 0x08
#define GZ_COMMENT 0x10
#define GZ_MAX 2
#define GZ_FAST 4
#define GZ_OS_FAT 0
#define GZ_OS_AMIGA 1
#define GZ_OS_VMS 2
#define GZ_OS_UNIX 3
#define GZ_OS_VMCMS 4
#define GZ_OS_ATARI 5
#define GZ_OS_HPFS 6
#define GZ_OS_MAC 7
#define GZ_OS_Z 8
#define GZ_OS_CPM 9
#define GZ_OS_TOPS20 10
#define GZ_OS_NTFS 11
#define GZ_OS_QDOS 12
#define GZ_OS_ACORN 13
#define GZ_OS_UNKNOWN 255
#define GZ_RND_S1 'R'
#define GZ_RND_S2 'A'
#define GZ_ID1 0
#define GZ_ID2 1
#define GZ_CM 2
#define GZ_FLG 3
#define GZ_MTIME 4
#define GZ_XFL 8
#define GZ_OS 9
#define GZ_XLEN 10
#define GZ_FEXTRA_START 12
#define GZ_SI1 12
#define GZ_SI2 13
#define GZ_SUBLEN 14
#define GZ_VERSION 16
#define GZ_CHUNKLEN 18
#define GZ_CHUNKCNT 20
#define GZ_RNDDATA 22
#define DICT_UNKNOWN 0
#define DICT_TEXT 1
#define DICT_GZIP 2
#define DICT_DZIP 3
int dictData::read_header(const std::string &fname, int computeCRC)
{
FILE *str;
int id1, id2, si1, si2;
char buffer[BUFFERSIZE];
int extraLength, subLength;
int i;
char *pt;
int c;
stardict_stat_t stats;
unsigned long crc = crc32( 0L, Z_NULL, 0 );
int count;
unsigned long offset;
if (!(str = fopen(fname.c_str(), "rb"))) {
}
this->headerLength = GZ_XLEN - 1;
this->type = DICT_UNKNOWN;
id1 = getc( str );
id2 = getc( str );
if (id1 != GZ_MAGIC1 || id2 != GZ_MAGIC2) {
this->type = DICT_TEXT;
g_stat(fname.c_str(), &stats);
this->compressedLength = this->length = stats.st_size;
this->origFilename = fname;
this->mtime = stats.st_mtime;
if (computeCRC) {
rewind( str );
while (!feof( str )) {
if ((count = fread( buffer, 1, BUFFERSIZE, str ))) {
crc = crc32(crc, (Bytef *)buffer, count);
}
}
}
this->crc = crc;
fclose( str );
return 0;
}
this->type = DICT_GZIP;
this->method = getc( str );
this->flags = getc( str );
this->mtime = getc( str ) << 0;
this->mtime |= getc( str ) << 8;
this->mtime |= getc( str ) << 16;
this->mtime |= getc( str ) << 24;
this->extraFlags = getc( str );
this->os = getc( str );
if (this->flags & GZ_FEXTRA) {
extraLength = getc( str ) << 0;
extraLength |= getc( str ) << 8;
this->headerLength += extraLength + 2;
si1 = getc( str );
si2 = getc( str );
if (si1 == GZ_RND_S1 || si2 == GZ_RND_S2) {
subLength = getc( str ) << 0;
subLength |= getc( str ) << 8;
this->version = getc( str ) << 0;
this->version |= getc( str ) << 8;
if (this->version != 1) {
}
this->chunkLength = getc( str ) << 0;
this->chunkLength |= getc( str ) << 8;
this->chunkCount = getc( str ) << 0;
this->chunkCount |= getc( str ) << 8;
if (this->chunkCount <= 0) {
fclose( str );
return 5;
}
this->chunks = (int *)malloc(sizeof( this->chunks[0] )
* this->chunkCount );
for (i = 0; i < this->chunkCount; i++) {
this->chunks[i] = getc( str ) << 0;
this->chunks[i] |= getc( str ) << 8;
}
this->type = DICT_DZIP;
} else {
fseek( str, this->headerLength, SEEK_SET );
}
}
if (this->flags & GZ_FNAME) {
pt = buffer;
while ((c = getc( str )) && c != EOF)
*pt++ = c;
*pt = '\0';
this->origFilename = buffer;
this->headerLength += this->origFilename.length() + 1;
} else {
this->origFilename = "";
}
if (this->flags & GZ_COMMENT) {
pt = buffer;
while ((c = getc( str )) && c != EOF)
*pt++ = c;
*pt = '\0';
comment = buffer;
headerLength += comment.length()+1;
} else {
comment = "";
}
if (this->flags & GZ_FHCRC) {
getc( str );
getc( str );
this->headerLength += 2;
}
if (ftell( str ) != this->headerLength + 1) {
}
fseek( str, -8, SEEK_END );
this->crc = getc( str ) << 0;
this->crc |= getc( str ) << 8;
this->crc |= getc( str ) << 16;
this->crc |= getc( str ) << 24;
this->length = getc( str ) << 0;
this->length |= getc( str ) << 8;
this->length |= getc( str ) << 16;
this->length |= getc( str ) << 24;
this->compressedLength = ftell( str );
this->offsets = (unsigned long *)malloc( sizeof( this->offsets[0] )
* this->chunkCount );
for (offset = this->headerLength + 1, i = 0;
i < this->chunkCount;
i++) {
this->offsets[i] = offset;
offset += this->chunks[i];
}
fclose( str );
return 0;
}
bool dictData::open(const std::string& fname, int computeCRC)
{
stardict_stat_t stats;
int j;
this->initialized = 0;
if (!g_file_test(fname.c_str(),
GFileTest(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))) {
return false;
}
if (read_header(fname, computeCRC)) {
return false;
}
if(g_stat(fname.c_str(), &stats))
return false;
this->size = stats.st_size;
if (!mapfile.open(fname.c_str(), size))
return false;
this->start=mapfile.begin();
this->end = this->start + this->size;
for (j = 0; j < DICT_CACHE_SIZE; j++) {
cache[j].chunk = -1;
cache[j].stamp = -1;
cache[j].inBuffer = NULL;
cache[j].count = 0;
}
return true;
}
void dictData::close()
{
int i;
if (this->chunks)
free(this->chunks);
if (this->offsets)
free(this->offsets);
if (this->initialized) {
if (inflateEnd( &this->zStream )) {
}
}
for (i = 0; i < DICT_CACHE_SIZE; ++i){
if (this -> cache [i].inBuffer)
free (this -> cache [i].inBuffer);
}
}
void dictData::read(char *buffer, unsigned long start, unsigned long size)
{
char *pt;
unsigned long end;
int count;
char *inBuffer;
char outBuffer[OUT_BUFFER_SIZE];
int firstChunk, lastChunk;
int firstOffset, lastOffset;
int i, j;
int found, target, lastStamp;
static int stamp = 0;
end = start + size;
switch (this->type) {
case DICT_GZIP:
break;
case DICT_TEXT:
memcpy( buffer, this->start + start, size );
break;
case DICT_DZIP:
if (!this->initialized) {
++this->initialized;
this->zStream.zalloc = NULL;
this->zStream.zfree = NULL;
this->zStream.opaque = NULL;
this->zStream.next_in = 0;
this->zStream.avail_in = 0;
this->zStream.next_out = NULL;
this->zStream.avail_out = 0;
if (inflateInit2( &this->zStream, -15 ) != Z_OK) {
}
}
firstChunk = start / this->chunkLength;
firstOffset = start - firstChunk * this->chunkLength;
lastChunk = end / this->chunkLength;
lastOffset = end - lastChunk * this->chunkLength;
for (pt = buffer, i = firstChunk; i <= lastChunk; i++) {
found = 0;
target = 0;
lastStamp = INT_MAX;
for (j = 0; j < DICT_CACHE_SIZE; j++) {
#if USE_CACHE
if (this->cache[j].chunk == i) {
found = 1;
target = j;
break;
}
#endif
if (this->cache[j].stamp < lastStamp) {
lastStamp = this->cache[j].stamp;
target = j;
}
}
this->cache[target].stamp = ++stamp;
if (found) {
count = this->cache[target].count;
inBuffer = this->cache[target].inBuffer;
} else {
this->cache[target].chunk = i;
if (!this->cache[target].inBuffer)
this->cache[target].inBuffer = (char *)malloc( IN_BUFFER_SIZE );
inBuffer = this->cache[target].inBuffer;
if (this->chunks[i] >= OUT_BUFFER_SIZE ) {
}
memcpy( outBuffer, this->start + this->offsets[i], this->chunks[i] );
this->zStream.next_in = (Bytef *)outBuffer;
this->zStream.avail_in = this->chunks[i];
this->zStream.next_out = (Bytef *)inBuffer;
this->zStream.avail_out = IN_BUFFER_SIZE;
if (inflate( &this->zStream, Z_PARTIAL_FLUSH ) != Z_OK) {
}
if (this->zStream.avail_in) {
}
count = IN_BUFFER_SIZE - this->zStream.avail_out;
this->cache[target].count = count;
}
if (i == firstChunk) {
if (i == lastChunk) {
memcpy( pt, inBuffer + firstOffset, lastOffset-firstOffset);
pt += lastOffset - firstOffset;
} else {
if (count != this->chunkLength ) {
}
memcpy( pt, inBuffer + firstOffset,
this->chunkLength - firstOffset );
pt += this->chunkLength - firstOffset;
}
} else if (i == lastChunk) {
memcpy( pt, inBuffer, lastOffset );
pt += lastOffset;
} else {
assert( count == this->chunkLength );
memcpy( pt, inBuffer, this->chunkLength );
pt += this->chunkLength;
}
}
break;
case DICT_UNKNOWN:
break;
}
}