 
/***************************************************************************
                          dictd.cpp  -  description

                             -------------------
    begin                : 28. November 2010
    copyright            : (c) 2007 by Iven Wold - 526f6c616e642053756368616e
    email                : hour.glass2@yahoo.com
    license              : GPL v 2.0
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This file is part of QTrans.                                          *
 *                                                                         *
 *   QTrans 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; version 2 of the License.               *
 *                                                                         *
 *   QTrans 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 QTrans. If not, see <http://www.gnu.org/licenses/> or      *
 *   write to the Free Software Foundation, Inc.,                          *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.         *
 *                                                                         *
 ***************************************************************************/

#include <QDebug>		//ooo
#include <QByteArray>		//ooo
#include <QDebug>		//ooo

#include <zlib.h>

#include <iostream>		//ooo
#include <stdio.h>		//ooo
#include <stdlib.h>		//ooo
#include <assert.h>		//ooo

#define CHUNK 0xffffL

#define GZ_XLEN         10

#define DICT_UNKNOWN    0
#define DICT_TEXT       1
#define DICT_GZIP       2
#define DICT_DZIP       3

#include "dictd.h"

//#define NOPTIMIZE

Dictd::Dictd(const QString &filename)
{

	if( !QFile::exists( filename ) ) {
	      m_isOk = false;
	      return;
	}
	idxFile = new QFile( filename );
	QString defarq = filename;
	defarq.replace( ".index", ".dict" );
	if ( QFile::exists( defarq ) ) {
		dictFile = new QFile( defarq );
		isCompressed = false;
	} else {
		if( !QFile::exists( defarq + ".dz" ) ) {
			  m_isOk = false;
			  return;
		}
		dictFile = new QFile( defarq + ".dz" );
		isCompressed = true;
		dictFile->open( QIODevice::ReadOnly );
		
		dictFile->getChar(&c);
		
		if( c != 0x1f ) { //ID1 = 31 (0x1f, \037)
			 m_isOk = false;
			 return;
		}

		dictFile->getChar(&c);
		
		if( (unsigned char)c != 0x8b ) { //ID2 = 139 (0x8b, \213)
			 m_isOk = false;
			return;
		}
		
		dictFile->getChar(&c); // COMPRESSION
		
		dictFile->getChar(&c);
		
		FLAGS    = (unsigned char)c;
		FTEXT    = FLAGS & 1;
		FHCRC    = FLAGS & 2;
		FEXTRA   = FLAGS & 4;
		FNAME    = FLAGS & 8;
		FCOMMENT = FLAGS & 16;
		
		dictFile->getChar(&c);
		
		MTIME  = (unsigned char)c << 0;
		dictFile->getChar(&c);
		MTIME |= (unsigned char)c << 8;
		dictFile->getChar(&c);
		MTIME |= (unsigned char)c << 16;
		dictFile->getChar(&c);
		MTIME |= (unsigned char)c << 24;

		dictFile->getChar(&c); // Compression type
		
		dictFile->getChar(&c); // Operating System

		if( FEXTRA ) {
		  
			dictFile->getChar(&c);
			XLEN = (unsigned char)c << 0;

			dictFile->getChar(&c);
			XLEN |= (unsigned char)c << 8;
			
			readExtraField();
		}

		if( FNAME ) {
			readFileName();
		}

		if( FCOMMENT ) {
			readFileName();
		}

		if( FHCRC ) {

			dictFile->getChar(&c);
			
			*crc16[0] = (unsigned char)c;

			dictFile->getChar(&c);
			
			*crc16[1] = (unsigned char)c;
			
			this->headerLength += 2;
		}

		//begin of chunks of compressed data
		offset = dictFile->pos();

		/*offsetAdditional = this->headerLength + 1;	//ooo
		
		for ( int i = 0; i < this->CHCNT; i++) {

			offsetsWithHeader.append(offsetAdditional);	//ooo
			offsetAdditional += offsets[i];			//ooo
		}*/
		
		/*offsetAdditional = offset;	//ooo
		
		for ( int i = 0; i < this->CHCNT; i++) {

			offsetsWithHeader.append(offsetAdditional);	//ooo
			offsetAdditional += offsets[i];			//ooo
		}*/
		
		dictFile->close();
	}
}


Dictd::~Dictd()
{
	delete dictFile;
	delete idxFile;
}


void Dictd::readExtraField()
{
	//kdDebug() << "readExtraField()" << endl;
	offsets.clear();

	dictFile->getChar(&c);

	SI1 = /*(unsigned char)*/c;

	dictFile->getChar(&c);

	SI2 = /*(unsigned char)*/c;

	// length of the subfield data
	// length of the subfield data, excluding the 4 initial bytes		//ooo

	if ( SI1 == 'R' || SI2 == 'A' ) {
	  
		dictFile->getChar(&c);
		LEN = (unsigned char)c << 0;

		dictFile->getChar(&c);
		LEN += (unsigned char)c << 8;
	}

	int size = (int)LEN - 6;
	//int size = (int)LEN + 4;	//ooo

	// Version
	dictFile->getChar(&c);

	VER = (unsigned char)c << 0;
	dictFile->getChar(&c);

	VER += (unsigned char)c << 8;

	// length of a "chunk" of data
	dictFile->getChar(&c);

	CHLEN = (unsigned char)c << 0;
	dictFile->getChar(&c);

	CHLEN += (unsigned char)c << 8;

	// how many chunks are preset
	dictFile->getChar(&c);

	CHCNT = (unsigned char)c << 0;
	dictFile->getChar(&c);

	CHCNT += (unsigned char)c << 8;

	unsigned long data;
	unsigned char data_stack;	//ooo
	//uint data;	//ooo
	int b = 0;

	for(int a = 0; a < size; a++) {
	//for(int a = 0; a < CHCNT; a++) {		//ooo

		// how long is each chunk after compression
		dictFile->getChar(&c);
		//dictFile->read(&c, 8);
		
		//data_stack = (unsigned char)c << 0;	//ooo
		data  = (unsigned char)c << 0;
		
		dictFile->getChar(&c);
		//dictFile->read(&c, 8);
		
		//data_stack += (unsigned char)c << 8;	//ooo
		data += (unsigned char)c << 8;
		
		//data = data_stack;			//ooo
		
		b++;					//ooo
		a++;					//ooo
		offsets.append( data );
	}

	this->type = DICT_DZIP;		//ooo
	
}


void Dictd::readFileName()
{

	QString filename_str;
	//char byte;
	//char *byte;	//ooo
	
	//byte = dictFile->getChar(c);

	/*if ( dictFile->getChar(&c) ) {
		//byte = c;
		*byte = c;
	}*/
	
	byte = buffer;		//ooo
	//dictFile->getChar(&c);
	//*byte = c;

	//while( byte != '\0' ) {
	//while( dictFile->getChar(&c) && c != EOF ) {		//ooo
	while( dictFile->getChar(&c) && c != '\0' ) {		//ooo
		   //filename_str += byte;
		*byte++ = c;			//ooo
		   //byte = dictFile->getChar(c);

		   /*if ( dictFile->getChar(&c) ) {
		 	byte = c;
		   }*/

	}
	
	/*while (*byte != '\0') {
		*byte++ = c;
	}*/
	
	*byte = '\0';	//ooo

	//m_filename = filename_str;
	m_filename = buffer;	//ooo
	//m_filename = QString("%1").arg(byte);
	//m_filename = byte;

}

QString Dictd::search( const QString &word )
{
 
	struct entry entry;
	
	QByteArray result;		//ooo
	QByteArray resultFinal;		//ooo
	
	QString line, headword;
	char buf[1024];
	bool found = false;
	int pos, pos1;
	idxFile->open( QIODevice::ReadOnly );

	while( !idxFile->atEnd() ) {
		//idxFile->readLine( line, 1024 );
		idxFile->readLine(buf, sizeof(buf));						//ooo
		line = buf;									//ooo
		//pos = line.find( '\t' );
		pos = line.indexOf( '\t' );							//ooo
		//headword = QString::fromUtf8( line.left( pos ).lower().local8Bit() );
		headword = QString::fromUtf8( line.left( pos ).toLower().toLocal8Bit() );	//ooo

		if( QString::localeAwareCompare( headword, word ) != 0 )
			if( found ) break;
		else continue;
			found = true;

		pos++;
		//pos1 = line.find( '\t', pos );
		pos1 = line.indexOf( '\t', pos );						//ooo
		headword = line.mid( pos, pos1 - pos );
		//entry.position = b64_decode( headword.local8Bit() );
		entry.position = b64_decode( headword.toLocal8Bit() );				//ooo

		pos1++;
		headword = line.right( line.length() - pos1 ).remove('\n');
		//entry.size = b64_decode( headword.local8Bit() );
		entry.size = b64_decode( headword.toLocal8Bit() );				//ooo

	//}		//ooo
	
		if( isCompressed ) {
	  
			char pt[CHUNK];				//ooo
			uint a;					//ooo
			//QByteArray result;			//ooo
			result.clear();				//ooo
			//result.resize( entry.size + 1 );	//ooo
	  
			uint chunk = entry.position / CHLEN ;
			uint pos = entry.position % CHLEN;
			uint lastChunk = (entry.position + entry.size) / CHLEN;		//ooo
			uint lastPos = (entry.position + entry.size) % CHLEN;		//ooo

			dictFile->open( QIODevice::ReadOnly );
		
			for (uint i = chunk; i <= lastChunk; i++) {	//ooo

				// How many chunks (bytes) maust be added up	//ooo
				unsigned long chunksAddedUp = 0;
				for (uint a=0;a<chunk;a++) chunksAddedUp += offsets[a];
		 
				// Jump to chunk we are looking for
				//dictFile->seek(offsetsWithHeader[i]);
				dictFile->seek(offset + chunksAddedUp);		//ooo
				unsigned int chunkLen = offsets[i];

				//QByteArray data;
				//data.resize(chunkLen + 1);
				unsigned char data[chunkLen];		//ooo
				QByteArray ba;				//ooo
				ba.resize(1);				//ooo
				bool ok;
				int hex;
				//data[chunkLen] = '\0';

				// Get the compressed data
				//for (unsigned long a = 0; a < offsets[i]; a++) {
				for (unsigned long a = 0; a < chunkLen; a++) {
					dictFile->getChar(&c);
					//dictFile->read(&c,1);
					data[a] = (unsigned char)c;
					if (a==0) {
						ba.append(c);
					}
					//data.append((unsigned char)c);
				}
			
				//data[a] = '\0';			//ooo
				//dictFile->close();		//ooo

				if (i == chunk && i == lastChunk) {

					memcpy(pt, Inflate(data, offsets[i]) + pos, lastPos - pos);
					//memcpy(pt, Inflate(data, chunkLen) + pos, lastPos - pos);
					//memcpy(pt, Inflate(data) + pos, lastPos - pos);
					pt[lastPos-pos] = '\0';						//ooo
			  
				} else {

					if( i == chunk ) {
			
						memcpy(pt, Inflate(data, offsets[i]) + pos, CHLEN - pos);
						//memcpy(pt, Inflate(data, chunkLen) + pos, CHLEN - pos);
						//memcpy(pt, Inflate(data) + pos, CHLEN - pos);
						pt[CHLEN - pos] = '\0';						//ooo
				
					} else if( i == lastChunk) {
			
						memcpy(pt, Inflate(data, offsets[i]), lastPos);
						//memcpy(pt, Inflate(data, chunkLen), lastPos);
						//memcpy(pt, Inflate(data), lastPos);
						pt[lastPos] = '\0';						//ooo
			 
					} else {
			 
						memcpy(pt, Inflate(data, offsets[i]), CHLEN);
						//memcpy(pt, Inflate(data, chunkLen), CHLEN);
						//memcpy(pt, Inflate(data), CHLEN);
						pt[CHLEN] = '\0';						//ooo
			 
					}
				}

				//result.append( Inflate( data4, offsets[i] ));
				//result.append( parseData(pt).toAscii().data() );			//ooo
				result.append(pt);
				resultFinal.append(pt);

			}
		
			dictFile->close();
			resultFinal.append('\n');

		} else {
	  
			dictFile->open( QIODevice::ReadOnly );
			dictFile->seek(entry.position);
		
			result.resize( entry.size + 1 );
			result.clear();						//ooo
		
			for( uint a = 0; a < entry.size; a++ ) {
		  
				dictFile->getChar(&c);
				result[a] = c;

			}

			dictFile->close();
			result.insert(entry.size, QByteArray("\n"));		//ooo
			result.insert(entry.size+1, QByteArray("\0"));		//ooo
			resultFinal.append(result);

		}
	
	}	//while
	
	idxFile->close();
	
	if( !found ) {
		return QString::null;
	}/* else {
		resultFinal.append('\n');
	}*/
	
	return QString::fromUtf8(resultFinal);				//ooo
	
}


unsigned long Dictd::b64_decode( const char *val )
{
   /*int b64_index[256] = {
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,62, 99,99,99,63,
    52,53,54,55, 56,57,58,59, 60,61,99,99, 99,99,99,99,
    99, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
    15,16,17,18, 19,20,21,22, 23,24,25,99, 99,99,99,99,
    99,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
    41,42,43,44, 45,46,47,48, 49,50,51,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99,
    99,99,99,99, 99,99,99,99, 99,99,99,99, 99,99,99,99, };*/
   
   //#define XX 100
   #define XX 99

   static int b64_index[256] = {
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
    52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
    XX, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
    15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
    XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
    41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
    XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
   };
   
   unsigned long v = 0;
   int           i;
   int           offset = 0;
   int           len = strlen( val );

   for (i = len - 1; i >= 0; i--) {
      int tmp = b64_index[ (unsigned char)val[i] ];
      v |= tmp << offset;
      offset += 6;
   }

   return v;
}


char* Dictd::Inflate( unsigned char *data, unsigned long size )		//ooo
//char* Dictd::Inflate( const QByteArray &data )		//ooo
{
	
	int ret;
	unsigned have;			//ooo
	//int have;			//ooo
	z_stream strm;
	char in[CHUNK];			//ooo
	char out[CHUNK];
        char *result;	//xxx
        result = (char *)malloc( CHUNK + 1 );	//xxx

	// Inicialization of zlib
	/* allocate inflate state */	//ooo
	strm.zalloc = Z_NULL;
	strm.zfree = Z_NULL;
	strm.opaque = Z_NULL;
	strm.avail_in = 0;
	strm.next_in = Z_NULL;
	   //ret = inflateInit2( &strm, -MAX_WBITS );
	ret = inflateInit2( &strm, -15 );		//ooo
	   //ret = inflateInit2( &strm, 8 );		//ooo
	   //ret = inflateInit(&strm);			//ooo
	   
	if (ret != Z_OK) {
	  
		return "/////";
		printf("Cannot initialize inflation engine: %s\n", strm.msg );
		
	}
	
	/*if (inflateInit2( &strm, -15 ) != Z_OK) {
		printf("Cannot initialize inflation engine: %s\n", strm.msg );
	}*/
	
	/* decompress until deflate stream ends or end of file */
	//do {								//ooo
		//strm.avail_in = fread(in, 1, CHUNK, source);		//ooo
		
		// Compressed data
		strm.avail_in = size;			//ooo
		strm.next_in = (Bytef*)data;		//ooo
		
		//strm.avail_in = data.size();			//ooo
		//strm.next_in = (Bytef*)data.data();		//ooo

		/* run inflate() on input until output buffer not full */
		do {
			strm.avail_out = CHUNK;
			strm.next_out = (Bytef*)out;
			   //ret = inflate(&strm, Z_SYNC_FLUSH);
			   //ret = inflate(&strm, Z_NO_FLUSH);	//ooo
			ret = inflate(&strm, Z_PARTIAL_FLUSH);	//ooo
			   //assert(ret != Z_STREAM_ERROR);  /* state not clobbered */	//ooo

			if ( ret != Z_OK ) {

				printf("zlib inflate problem: %s\n", strm.msg );

			}
			
			/*if (inflate( strm,  Z_PARTIAL_FLUSH ) != Z_OK) {
				printf("zlib inflate problem: %s\n", strm.msg );
			}*/

			switch (ret) {
				case Z_NEED_DICT:
					ret = Z_DATA_ERROR;     /* and fall through */

				case Z_STREAM_ERROR:
					break;

				case Z_DATA_ERROR:
					break;

				case Z_MEM_ERROR:
					(void)inflateEnd(&strm);
					break;
			}
			
			have = CHUNK - strm.avail_out;		//ooo
			
			/*if (fwrite(out, 1, have, dest) != have || ferror(dest)) {	//ooo
				(void)inflateEnd(&strm);
				return Z_ERRNO;
			}*/

		} while (strm.avail_out == 0);
		
	/* done when inflate() says it's done */
	//} while (ret != Z_STREAM_END);
	
	/* clean up and return */
	//ret = inflateEnd(&strm);
	(void)inflateEnd(&strm);	//ooo
        strcpy(result, out);	//xxx

	return result;
	
	//std::cout << "StarDict::parseData = " << parseData(out).toAscii().data() << "\n";	//ooo

	//return &out[0];			//ooo
}


//#include "dictd.moc"
