123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code 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.
- Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- /*
- =================================================================================
- idCompressor_None
- =================================================================================
- */
- class idCompressor_None : public idCompressor {
- public:
- idCompressor_None( void );
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- float GetCompressionRatio( void ) const;
- const char * GetName( void );
- const char * GetFullPath( void );
- int Read( void *outData, int outLength );
- int Write( const void *inData, int inLength );
- int Length( void );
- ID_TIME_T Timestamp( void );
- int Tell( void );
- void ForceFlush( void );
- void Flush( void );
- int Seek( long offset, fsOrigin_t origin );
- protected:
- idFile * file;
- bool compress;
- };
- /*
- ================
- idCompressor_None::idCompressor_None
- ================
- */
- idCompressor_None::idCompressor_None( void ) {
- file = NULL;
- compress = true;
- }
- /*
- ================
- idCompressor_None::Init
- ================
- */
- void idCompressor_None::Init( idFile *f, bool compress, int wordLength ) {
- this->file = f;
- this->compress = compress;
- }
- /*
- ================
- idCompressor_None::FinishCompress
- ================
- */
- void idCompressor_None::FinishCompress( void ) {
- }
- /*
- ================
- idCompressor_None::GetCompressionRatio
- ================
- */
- float idCompressor_None::GetCompressionRatio( void ) const {
- return 0.0f;
- }
- /*
- ================
- idCompressor_None::GetName
- ================
- */
- const char *idCompressor_None::GetName( void ) {
- if ( file ) {
- return file->GetName();
- } else {
- return "";
- }
- }
- /*
- ================
- idCompressor_None::GetFullPath
- ================
- */
- const char *idCompressor_None::GetFullPath( void ) {
- if ( file ) {
- return file->GetFullPath();
- } else {
- return "";
- }
- }
- /*
- ================
- idCompressor_None::Write
- ================
- */
- int idCompressor_None::Write( const void *inData, int inLength ) {
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- return file->Write( inData, inLength );
- }
- /*
- ================
- idCompressor_None::Read
- ================
- */
- int idCompressor_None::Read( void *outData, int outLength ) {
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- return file->Read( outData, outLength );
- }
- /*
- ================
- idCompressor_None::Length
- ================
- */
- int idCompressor_None::Length( void ) {
- if ( file ) {
- return file->Length();
- } else {
- return 0;
- }
- }
- /*
- ================
- idCompressor_None::Timestamp
- ================
- */
- ID_TIME_T idCompressor_None::Timestamp( void ) {
- if ( file ) {
- return file->Timestamp();
- } else {
- return 0;
- }
- }
- /*
- ================
- idCompressor_None::Tell
- ================
- */
- int idCompressor_None::Tell( void ) {
- if ( file ) {
- return file->Tell();
- } else {
- return 0;
- }
- }
- /*
- ================
- idCompressor_None::ForceFlush
- ================
- */
- void idCompressor_None::ForceFlush( void ) {
- if ( file ) {
- file->ForceFlush();
- }
- }
- /*
- ================
- idCompressor_None::Flush
- ================
- */
- void idCompressor_None::Flush( void ) {
- if ( file ) {
- file->ForceFlush();
- }
- }
- /*
- ================
- idCompressor_None::Seek
- ================
- */
- int idCompressor_None::Seek( long offset, fsOrigin_t origin ) {
- common->Error( "cannot seek on idCompressor" );
- return -1;
- }
- /*
- =================================================================================
- idCompressor_BitStream
- Base class for bit stream compression.
- =================================================================================
- */
- class idCompressor_BitStream : public idCompressor_None {
- public:
- idCompressor_BitStream( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- float GetCompressionRatio( void ) const;
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- protected:
- byte buffer[65536];
- int wordLength;
- int readTotalBytes;
- int readLength;
- int readByte;
- int readBit;
- const byte * readData;
- int writeTotalBytes;
- int writeLength;
- int writeByte;
- int writeBit;
- byte * writeData;
- protected:
- void InitCompress( const void *inData, const int inLength );
- void InitDecompress( void *outData, int outLength );
- void WriteBits( int value, int numBits );
- int ReadBits( int numBits );
- void UnreadBits( int numBits );
- int Compare( const byte *src1, int bitPtr1, const byte *src2, int bitPtr2, int maxBits ) const;
- };
- /*
- ================
- idCompressor_BitStream::Init
- ================
- */
- void idCompressor_BitStream::Init( idFile *f, bool compress, int wordLength ) {
- assert( wordLength >= 1 && wordLength <= 32 );
- this->file = f;
- this->compress = compress;
- this->wordLength = wordLength;
- readTotalBytes = 0;
- readLength = 0;
- readByte = 0;
- readBit = 0;
- readData = NULL;
- writeTotalBytes = 0;
- writeLength = 0;
- writeByte = 0;
- writeBit = 0;
- writeData = NULL;
- }
- /*
- ================
- idCompressor_BitStream::InitCompress
- ================
- */
- ID_INLINE void idCompressor_BitStream::InitCompress( const void *inData, const int inLength ) {
- readLength = inLength;
- readByte = 0;
- readBit = 0;
- readData = (const byte *) inData;
- if ( !writeLength ) {
- writeLength = sizeof( buffer );
- writeByte = 0;
- writeBit = 0;
- writeData = buffer;
- }
- }
- /*
- ================
- idCompressor_BitStream::InitDecompress
- ================
- */
- ID_INLINE void idCompressor_BitStream::InitDecompress( void *outData, int outLength ) {
- if ( !readLength ) {
- readLength = file->Read( buffer, sizeof( buffer ) );
- readByte = 0;
- readBit = 0;
- readData = buffer;
- }
- writeLength = outLength;
- writeByte = 0;
- writeBit = 0;
- writeData = (byte *) outData;
- }
- /*
- ================
- idCompressor_BitStream::WriteBits
- ================
- */
- void idCompressor_BitStream::WriteBits( int value, int numBits ) {
- int put, fraction;
- // Short circuit for writing single bytes at a time
- if ( writeBit == 0 && numBits == 8 && writeByte < writeLength ) {
- writeByte++;
- writeTotalBytes++;
- writeData[writeByte - 1] = value;
- return;
- }
- while( numBits ) {
- if ( writeBit == 0 ) {
- if ( writeByte >= writeLength ) {
- if ( writeData == buffer ) {
- file->Write( buffer, writeByte );
- writeByte = 0;
- } else {
- put = numBits;
- writeBit = put & 7;
- writeByte += ( put >> 3 ) + ( writeBit != 0 );
- writeTotalBytes += ( put >> 3 ) + ( writeBit != 0 );
- return;
- }
- }
- writeData[writeByte] = 0;
- writeByte++;
- writeTotalBytes++;
- }
- put = 8 - writeBit;
- if ( put > numBits ) {
- put = numBits;
- }
- fraction = value & ( ( 1 << put ) - 1 );
- writeData[writeByte - 1] |= fraction << writeBit;
- numBits -= put;
- value >>= put;
- writeBit = ( writeBit + put ) & 7;
- }
- }
- /*
- ================
- idCompressor_BitStream::ReadBits
- ================
- */
- int idCompressor_BitStream::ReadBits( int numBits ) {
- int value, valueBits, get, fraction;
- value = 0;
- valueBits = 0;
- // Short circuit for reading single bytes at a time
- if ( readBit == 0 && numBits == 8 && readByte < readLength ) {
- readByte++;
- readTotalBytes++;
- return readData[readByte - 1];
- }
- while ( valueBits < numBits ) {
- if ( readBit == 0 ) {
- if ( readByte >= readLength ) {
- if ( readData == buffer ) {
- readLength = file->Read( buffer, sizeof( buffer ) );
- readByte = 0;
- } else {
- get = numBits - valueBits;
- readBit = get & 7;
- readByte += ( get >> 3 ) + ( readBit != 0 );
- readTotalBytes += ( get >> 3 ) + ( readBit != 0 );
- return value;
- }
- }
- readByte++;
- readTotalBytes++;
- }
- get = 8 - readBit;
- if ( get > (numBits - valueBits) ) {
- get = (numBits - valueBits);
- }
- fraction = readData[readByte - 1];
- fraction >>= readBit;
- fraction &= ( 1 << get ) - 1;
- value |= fraction << valueBits;
- valueBits += get;
- readBit = ( readBit + get ) & 7;
- }
- return value;
- }
- /*
- ================
- idCompressor_BitStream::UnreadBits
- ================
- */
- void idCompressor_BitStream::UnreadBits( int numBits ) {
- readByte -= ( numBits >> 3 );
- readTotalBytes -= ( numBits >> 3 );
- if ( readBit == 0 ) {
- readBit = 8 - ( numBits & 7 );
- } else {
- readBit -= numBits & 7;
- if ( readBit <= 0 ) {
- readByte--;
- readTotalBytes--;
- readBit = ( readBit + 8 ) & 7;
- }
- }
- if ( readByte < 0 ) {
- readByte = 0;
- readBit = 0;
- }
- }
- /*
- ================
- idCompressor_BitStream::Compare
- ================
- */
- int idCompressor_BitStream::Compare( const byte *src1, int bitPtr1, const byte *src2, int bitPtr2, int maxBits ) const {
- int i;
- // If the two bit pointers are aligned then we can use a faster comparison
- if ( ( bitPtr1 & 7 ) == (bitPtr2 & 7 ) && maxBits > 16 ) {
- const byte *p1 = &src1[bitPtr1 >> 3];
- const byte *p2 = &src2[bitPtr2 >> 3];
- int bits = 0;
- int bitsRemain = maxBits;
- // Compare the first couple bits (if any)
- if ( bitPtr1 & 7 ) {
- for ( i = (bitPtr1 & 7); i < 8; i++, bits++ ) {
- if ( ( ( *p1 >> i ) ^ ( *p2 >> i ) ) & 1 ) {
- return bits;
- }
- bitsRemain--;
- }
- p1++;
- p2++;
- }
- int remain = bitsRemain >> 3;
- // Compare the middle bytes as ints
- while ( remain >= 4 && (*(const int *)p1 == *(const int *)p2) ) {
- p1 += 4;
- p2 += 4;
- remain -= 4;
- bits += 32;
- }
- // Compare the remaining bytes
- while ( remain > 0 && (*p1 == *p2) ) {
- p1++;
- p2++;
- remain--;
- bits += 8;
- }
- // Compare the last couple of bits (if any)
- int finalBits = 8;
- if ( remain == 0 ) {
- finalBits = ( bitsRemain & 7 );
- }
- for ( i = 0; i < finalBits; i++, bits++ ) {
- if ( ( ( *p1 >> i ) ^ ( *p2 >> i ) ) & 1 ) {
- return bits;
- }
- }
- assert(bits == maxBits);
- return bits;
- } else {
- for ( i = 0; i < maxBits; i++ ) {
- if ( ( ( src1[bitPtr1 >> 3] >> ( bitPtr1 & 7 ) ) ^ ( src2[bitPtr2 >> 3] >> ( bitPtr2 & 7 ) ) ) & 1 ) {
- break;
- }
- bitPtr1++;
- bitPtr2++;
- }
- return i;
- }
- }
- /*
- ================
- idCompressor_BitStream::Write
- ================
- */
- int idCompressor_BitStream::Write( const void *inData, int inLength ) {
- int i;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- InitCompress( inData, inLength );
- for ( i = 0; i < inLength; i++ ) {
- WriteBits( ReadBits( 8 ), 8 );
- }
- return i;
- }
- /*
- ================
- idCompressor_BitStream::FinishCompress
- ================
- */
- void idCompressor_BitStream::FinishCompress( void ) {
- if ( compress == false ) {
- return;
- }
- if ( writeByte ) {
- file->Write( buffer, writeByte );
- }
- writeLength = 0;
- writeByte = 0;
- writeBit = 0;
- }
- /*
- ================
- idCompressor_BitStream::Read
- ================
- */
- int idCompressor_BitStream::Read( void *outData, int outLength ) {
- int i;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- InitDecompress( outData, outLength );
- for ( i = 0; i < outLength && readLength >= 0; i++ ) {
- WriteBits( ReadBits( 8 ), 8 );
- }
- return i;
- }
- /*
- ================
- idCompressor_BitStream::GetCompressionRatio
- ================
- */
- float idCompressor_BitStream::GetCompressionRatio( void ) const {
- if ( compress ) {
- return ( readTotalBytes - writeTotalBytes ) * 100.0f / readTotalBytes;
- } else {
- return ( writeTotalBytes - readTotalBytes ) * 100.0f / writeTotalBytes;
- }
- }
- /*
- =================================================================================
- idCompressor_RunLength
- The following algorithm implements run length compression with an arbitrary
- word size.
- =================================================================================
- */
- class idCompressor_RunLength : public idCompressor_BitStream {
- public:
- idCompressor_RunLength( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- private:
- int runLengthCode;
- };
- /*
- ================
- idCompressor_RunLength::Init
- ================
- */
- void idCompressor_RunLength::Init( idFile *f, bool compress, int wordLength ) {
- idCompressor_BitStream::Init( f, compress, wordLength );
- runLengthCode = ( 1 << wordLength ) - 1;
- }
- /*
- ================
- idCompressor_RunLength::Write
- ================
- */
- int idCompressor_RunLength::Write( const void *inData, int inLength ) {
- int bits, nextBits, count;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- InitCompress( inData, inLength );
- while( readByte <= readLength ) {
- count = 1;
- bits = ReadBits( wordLength );
- for ( nextBits = ReadBits( wordLength ); nextBits == bits; nextBits = ReadBits( wordLength ) ) {
- count++;
- if ( count >= ( 1 << wordLength ) ) {
- if ( count >= ( 1 << wordLength ) + 3 || bits == runLengthCode ) {
- break;
- }
- }
- }
- if ( nextBits != bits ) {
- UnreadBits( wordLength );
- }
- if ( count > 3 || bits == runLengthCode ) {
- WriteBits( runLengthCode, wordLength );
- WriteBits( bits, wordLength );
- if ( bits != runLengthCode ) {
- count -= 3;
- }
- WriteBits( count - 1, wordLength );
- } else {
- while( count-- ) {
- WriteBits( bits, wordLength );
- }
- }
- }
- return inLength;
- }
- /*
- ================
- idCompressor_RunLength::Read
- ================
- */
- int idCompressor_RunLength::Read( void *outData, int outLength ) {
- int bits, count;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- InitDecompress( outData, outLength );
- while( writeByte <= writeLength && readLength >= 0 ) {
- bits = ReadBits( wordLength );
- if ( bits == runLengthCode ) {
- bits = ReadBits( wordLength );
- count = ReadBits( wordLength ) + 1;
- if ( bits != runLengthCode ) {
- count += 3;
- }
- while( count-- ) {
- WriteBits( bits, wordLength );
- }
- } else {
- WriteBits( bits, wordLength );
- }
- }
- return writeByte;
- }
- /*
- =================================================================================
- idCompressor_RunLength_ZeroBased
- The following algorithm implements run length compression with an arbitrary
- word size for data with a lot of zero bits.
- =================================================================================
- */
- class idCompressor_RunLength_ZeroBased : public idCompressor_BitStream {
- public:
- idCompressor_RunLength_ZeroBased( void ) {}
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- private:
- };
- /*
- ================
- idCompressor_RunLength_ZeroBased::Write
- ================
- */
- int idCompressor_RunLength_ZeroBased::Write( const void *inData, int inLength ) {
- int bits, count;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- InitCompress( inData, inLength );
- while( readByte <= readLength ) {
- count = 0;
- for ( bits = ReadBits( wordLength ); bits == 0 && count < ( 1 << wordLength ); bits = ReadBits( wordLength ) ) {
- count++;
- }
- if ( count ) {
- WriteBits( 0, wordLength );
- WriteBits( count - 1, wordLength );
- UnreadBits( wordLength );
- } else {
- WriteBits( bits, wordLength );
- }
- }
- return inLength;
- }
- /*
- ================
- idCompressor_RunLength_ZeroBased::Read
- ================
- */
- int idCompressor_RunLength_ZeroBased::Read( void *outData, int outLength ) {
- int bits, count;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- InitDecompress( outData, outLength );
- while( writeByte <= writeLength && readLength >= 0 ) {
- bits = ReadBits( wordLength );
- if ( bits == 0 ) {
- count = ReadBits( wordLength ) + 1;
- while( count-- > 0 ) {
- WriteBits( 0, wordLength );
- }
- } else {
- WriteBits( bits, wordLength );
- }
- }
- return writeByte;
- }
- /*
- =================================================================================
- idCompressor_Huffman
- The following algorithm is based on the adaptive Huffman algorithm described
- in Sayood's Data Compression book. The ranks are not actually stored, but
- implicitly defined by the location of a node within a doubly-linked list
- =================================================================================
- */
- const int HMAX = 256; // Maximum symbol
- const int NYT = HMAX; // NYT = Not Yet Transmitted
- const int INTERNAL_NODE = HMAX + 1; // internal node
- typedef struct nodetype {
- struct nodetype *left, *right, *parent; // tree structure
- struct nodetype *next, *prev; // doubly-linked list
- struct nodetype **head; // highest ranked node in block
- int weight;
- int symbol;
- } huffmanNode_t;
- class idCompressor_Huffman : public idCompressor_None {
- public:
- idCompressor_Huffman( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- float GetCompressionRatio( void ) const;
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- private:
- byte seq[65536];
- int bloc;
- int blocMax;
- int blocIn;
- int blocNode;
- int blocPtrs;
- int compressedSize;
- int unCompressedSize;
- huffmanNode_t * tree;
- huffmanNode_t * lhead;
- huffmanNode_t * ltail;
- huffmanNode_t * loc[HMAX+1];
- huffmanNode_t **freelist;
- huffmanNode_t nodeList[768];
- huffmanNode_t * nodePtrs[768];
- private:
- void AddRef( byte ch );
- int Receive( huffmanNode_t *node, int *ch );
- void Transmit( int ch, byte *fout );
- void PutBit( int bit, byte *fout, int *offset );
- int GetBit( byte *fout, int *offset );
- void Add_bit( char bit, byte *fout );
- int Get_bit();
- huffmanNode_t **Get_ppnode();
- void Free_ppnode( huffmanNode_t **ppnode );
- void Swap( huffmanNode_t *node1, huffmanNode_t *node2 );
- void Swaplist( huffmanNode_t *node1, huffmanNode_t *node2 );
- void Increment( huffmanNode_t *node );
- void Send( huffmanNode_t *node, huffmanNode_t *child, byte *fout );
- };
- /*
- ================
- idCompressor_Huffman::Init
- ================
- */
- void idCompressor_Huffman::Init( idFile *f, bool compress, int wordLength ) {
- int i;
- this->file = f;
- this->compress = compress;
- bloc = 0;
- blocMax = 0;
- blocIn = 0;
- blocNode = 0;
- blocPtrs = 0;
- compressedSize = 0;
- unCompressedSize = 0;
- tree = NULL;
- lhead = NULL;
- ltail = NULL;
- for( i = 0; i < (HMAX+1); i++ ) {
- loc[i] = NULL;
- }
- freelist = NULL;
- for( i = 0; i < 768; i++ ) {
- memset( &nodeList[i], 0, sizeof(huffmanNode_t) );
- nodePtrs[i] = NULL;
- }
- if ( compress ) {
- // Add the NYT (not yet transmitted) node into the tree/list
- tree = lhead = loc[NYT] = &nodeList[blocNode++];
- tree->symbol = NYT;
- tree->weight = 0;
- lhead->next = lhead->prev = NULL;
- tree->parent = tree->left = tree->right = NULL;
- loc[NYT] = tree;
- } else {
- // Initialize the tree & list with the NYT node
- tree = lhead = ltail = loc[NYT] = &nodeList[blocNode++];
- tree->symbol = NYT;
- tree->weight = 0;
- lhead->next = lhead->prev = NULL;
- tree->parent = tree->left = tree->right = NULL;
- }
- }
- /*
- ================
- idCompressor_Huffman::PutBit
- ================
- */
- void idCompressor_Huffman::PutBit( int bit, byte *fout, int *offset) {
- bloc = *offset;
- if ( (bloc&7) == 0 ) {
- fout[(bloc>>3)] = 0;
- }
- fout[(bloc>>3)] |= bit << (bloc&7);
- bloc++;
- *offset = bloc;
- }
- /*
- ================
- idCompressor_Huffman::GetBit
- ================
- */
- int idCompressor_Huffman::GetBit( byte *fin, int *offset) {
- int t;
- bloc = *offset;
- t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
- bloc++;
- *offset = bloc;
- return t;
- }
- /*
- ================
- idCompressor_Huffman::Add_bit
- Add a bit to the output file (buffered)
- ================
- */
- void idCompressor_Huffman::Add_bit( char bit, byte *fout ) {
- if ( (bloc&7) == 0 ) {
- fout[(bloc>>3)] = 0;
- }
- fout[(bloc>>3)] |= bit << (bloc&7);
- bloc++;
- }
- /*
- ================
- idCompressor_Huffman::Get_bit
- Get one bit from the input file (buffered)
- ================
- */
- int idCompressor_Huffman::Get_bit() {
- int t;
- int wh = bloc >> 3;
- int whb = wh>> 16;
- if ( whb != blocIn ) {
- blocMax += file->Read( seq, sizeof( seq ) );
- blocIn++;
- }
- wh &= 0xffff;
- t = ( seq[wh] >> ( bloc & 7 ) ) & 0x1;
- bloc++;
- return t;
- }
- /*
- ================
- idCompressor_Huffman::Get_ppnode
- ================
- */
- huffmanNode_t **idCompressor_Huffman::Get_ppnode() {
- huffmanNode_t **tppnode;
- if ( !freelist ) {
- return &nodePtrs[blocPtrs++];
- } else {
- tppnode = freelist;
- freelist = (huffmanNode_t **)*tppnode;
- return tppnode;
- }
- }
- /*
- ================
- idCompressor_Huffman::Free_ppnode
- ================
- */
- void idCompressor_Huffman::Free_ppnode( huffmanNode_t **ppnode ) {
- *ppnode = (huffmanNode_t *)freelist;
- freelist = ppnode;
- }
- /*
- ================
- idCompressor_Huffman::Swap
- Swap the location of the given two nodes in the tree.
- ================
- */
- void idCompressor_Huffman::Swap( huffmanNode_t *node1, huffmanNode_t *node2 ) {
- huffmanNode_t *par1, *par2;
- par1 = node1->parent;
- par2 = node2->parent;
- if ( par1 ) {
- if ( par1->left == node1 ) {
- par1->left = node2;
- } else {
- par1->right = node2;
- }
- } else {
- tree = node2;
- }
- if ( par2 ) {
- if ( par2->left == node2 ) {
- par2->left = node1;
- } else {
- par2->right = node1;
- }
- } else {
- tree = node1;
- }
-
- node1->parent = par2;
- node2->parent = par1;
- }
- /*
- ================
- idCompressor_Huffman::Swaplist
- Swap the given two nodes in the linked list (update ranks)
- ================
- */
- void idCompressor_Huffman::Swaplist( huffmanNode_t *node1, huffmanNode_t *node2 ) {
- huffmanNode_t *par1;
- par1 = node1->next;
- node1->next = node2->next;
- node2->next = par1;
- par1 = node1->prev;
- node1->prev = node2->prev;
- node2->prev = par1;
- if ( node1->next == node1 ) {
- node1->next = node2;
- }
- if ( node2->next == node2 ) {
- node2->next = node1;
- }
- if ( node1->next ) {
- node1->next->prev = node1;
- }
- if ( node2->next ) {
- node2->next->prev = node2;
- }
- if ( node1->prev ) {
- node1->prev->next = node1;
- }
- if ( node2->prev ) {
- node2->prev->next = node2;
- }
- }
- /*
- ================
- idCompressor_Huffman::Increment
- ================
- */
- void idCompressor_Huffman::Increment( huffmanNode_t *node ) {
- huffmanNode_t *lnode;
- if ( !node ) {
- return;
- }
- if ( node->next != NULL && node->next->weight == node->weight ) {
- lnode = *node->head;
- if ( lnode != node->parent ) {
- Swap( lnode, node );
- }
- Swaplist( lnode, node );
- }
- if ( node->prev && node->prev->weight == node->weight ) {
- *node->head = node->prev;
- } else {
- *node->head = NULL;
- Free_ppnode( node->head );
- }
- node->weight++;
- if ( node->next && node->next->weight == node->weight ) {
- node->head = node->next->head;
- } else {
- node->head = Get_ppnode();
- *node->head = node;
- }
- if ( node->parent ) {
- Increment( node->parent );
- if ( node->prev == node->parent ) {
- Swaplist( node, node->parent );
- if ( *node->head == node ) {
- *node->head = node->parent;
- }
- }
- }
- }
- /*
- ================
- idCompressor_Huffman::AddRef
- ================
- */
- void idCompressor_Huffman::AddRef( byte ch ) {
- huffmanNode_t *tnode, *tnode2;
- if ( loc[ch] == NULL ) { /* if this is the first transmission of this node */
- tnode = &nodeList[blocNode++];
- tnode2 = &nodeList[blocNode++];
- tnode2->symbol = INTERNAL_NODE;
- tnode2->weight = 1;
- tnode2->next = lhead->next;
- if ( lhead->next ) {
- lhead->next->prev = tnode2;
- if ( lhead->next->weight == 1 ) {
- tnode2->head = lhead->next->head;
- } else {
- tnode2->head = Get_ppnode();
- *tnode2->head = tnode2;
- }
- } else {
- tnode2->head = Get_ppnode();
- *tnode2->head = tnode2;
- }
- lhead->next = tnode2;
- tnode2->prev = lhead;
-
- tnode->symbol = ch;
- tnode->weight = 1;
- tnode->next = lhead->next;
- if ( lhead->next ) {
- lhead->next->prev = tnode;
- if ( lhead->next->weight == 1 ) {
- tnode->head = lhead->next->head;
- } else {
- /* this should never happen */
- tnode->head = Get_ppnode();
- *tnode->head = tnode2;
- }
- } else {
- /* this should never happen */
- tnode->head = Get_ppnode();
- *tnode->head = tnode;
- }
- lhead->next = tnode;
- tnode->prev = lhead;
- tnode->left = tnode->right = NULL;
-
- if ( lhead->parent ) {
- if ( lhead->parent->left == lhead ) { /* lhead is guaranteed to by the NYT */
- lhead->parent->left = tnode2;
- } else {
- lhead->parent->right = tnode2;
- }
- } else {
- tree = tnode2;
- }
-
- tnode2->right = tnode;
- tnode2->left = lhead;
-
- tnode2->parent = lhead->parent;
- lhead->parent = tnode->parent = tnode2;
-
- loc[ch] = tnode;
-
- Increment( tnode2->parent );
- } else {
- Increment( loc[ch] );
- }
- }
- /*
- ================
- idCompressor_Huffman::Receive
- Get a symbol.
- ================
- */
- int idCompressor_Huffman::Receive( huffmanNode_t *node, int *ch ) {
- while ( node && node->symbol == INTERNAL_NODE ) {
- if ( Get_bit() ) {
- node = node->right;
- } else {
- node = node->left;
- }
- }
- if ( !node ) {
- return 0;
- }
- return (*ch = node->symbol);
- }
- /*
- ================
- idCompressor_Huffman::Send
- Send the prefix code for this node.
- ================
- */
- void idCompressor_Huffman::Send( huffmanNode_t *node, huffmanNode_t *child, byte *fout ) {
- if ( node->parent ) {
- Send( node->parent, node, fout );
- }
- if ( child ) {
- if ( node->right == child ) {
- Add_bit( 1, fout );
- } else {
- Add_bit( 0, fout );
- }
- }
- }
- /*
- ================
- idCompressor_Huffman::Transmit
- Send a symbol.
- ================
- */
- void idCompressor_Huffman::Transmit( int ch, byte *fout ) {
- int i;
- if ( loc[ch] == NULL ) {
- /* huffmanNode_t hasn't been transmitted, send a NYT, then the symbol */
- Transmit( NYT, fout );
- for ( i = 7; i >= 0; i-- ) {
- Add_bit( (char)((ch >> i) & 0x1), fout );
- }
- } else {
- Send( loc[ch], NULL, fout );
- }
- }
- /*
- ================
- idCompressor_Huffman::Write
- ================
- */
- int idCompressor_Huffman::Write( const void *inData, int inLength ) {
- int i, ch;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- for ( i = 0; i < inLength; i++ ) {
- ch = ((const byte *)inData)[i];
- Transmit( ch, seq ); /* Transmit symbol */
- AddRef( (byte)ch ); /* Do update */
- int b = (bloc>>3);
- if ( b > 32768 ) {
- file->Write( seq, b );
- seq[0] = seq[b];
- bloc &= 7;
- compressedSize += b;
- }
- }
- unCompressedSize += i;
- return i;
- }
- /*
- ================
- idCompressor_Huffman::FinishCompress
- ================
- */
- void idCompressor_Huffman::FinishCompress( void ) {
- if ( compress == false ) {
- return;
- }
-
- bloc += 7;
- int str = (bloc>>3);
- if ( str ) {
- file->Write( seq, str );
- compressedSize += str;
- }
- }
- /*
- ================
- idCompressor_Huffman::Read
- ================
- */
- int idCompressor_Huffman::Read( void *outData, int outLength ) {
- int i, j, ch;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- if ( bloc == 0 ) {
- blocMax = file->Read( seq, sizeof( seq ) );
- blocIn = 0;
- }
- for ( i = 0; i < outLength; i++ ) {
- ch = 0;
- // don't overflow reading from the file
- if ( ( bloc >> 3 ) > blocMax ) {
- break;
- }
- Receive( tree, &ch ); /* Get a character */
- if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */
- ch = 0;
- for ( j = 0; j < 8; j++ ) {
- ch = ( ch << 1 ) + Get_bit();
- }
- }
-
- ((byte *)outData)[i] = ch; /* Write symbol */
- AddRef( (byte) ch ); /* Increment node */
- }
- compressedSize = bloc >> 3;
- unCompressedSize += i;
- return i;
- }
- /*
- ================
- idCompressor_Huffman::GetCompressionRatio
- ================
- */
- float idCompressor_Huffman::GetCompressionRatio( void ) const {
- return ( unCompressedSize - compressedSize ) * 100.0f / unCompressedSize;
- }
- /*
- =================================================================================
- idCompressor_Arithmetic
- The following algorithm is based on the Arithmetic Coding methods described
- by Mark Nelson. The probability table is implicitly stored.
- =================================================================================
- */
- const int AC_WORD_LENGTH = 8;
- const int AC_NUM_BITS = 16;
- const int AC_MSB_SHIFT = 15;
- const int AC_MSB2_SHIFT = 14;
- const int AC_MSB_MASK = 0x8000;
- const int AC_MSB2_MASK = 0x4000;
- const int AC_HIGH_INIT = 0xffff;
- const int AC_LOW_INIT = 0x0000;
- class idCompressor_Arithmetic : public idCompressor_BitStream {
- public:
- idCompressor_Arithmetic( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
-
- private:
- typedef struct acProbs_s {
- unsigned int low;
- unsigned int high;
- } acProbs_t;
-
- typedef struct acSymbol_s {
- unsigned int low;
- unsigned int high;
- int position;
- } acSymbol_t;
- acProbs_t probabilities[1<<AC_WORD_LENGTH];
- int symbolBuffer;
- int symbolBit;
- unsigned short low;
- unsigned short high;
- unsigned short code;
- unsigned int underflowBits;
- unsigned int scale;
- private:
- void InitProbabilities( void );
- void UpdateProbabilities( acSymbol_t* symbol );
- int ProbabilityForCount( unsigned int count );
- void CharToSymbol( byte c, acSymbol_t* symbol );
- void EncodeSymbol( acSymbol_t* symbol );
- int SymbolFromCount( unsigned int count, acSymbol_t* symbol );
- int GetCurrentCount( void );
- void RemoveSymbolFromStream( acSymbol_t* symbol );
- void PutBit( int bit );
- int GetBit( void );
- void WriteOverflowBits( void );
- };
- /*
- ================
- idCompressor_Arithmetic::Init
- ================
- */
- void idCompressor_Arithmetic::Init( idFile *f, bool compress, int wordLength ) {
- idCompressor_BitStream::Init( f, compress, wordLength );
- symbolBuffer = 0;
- symbolBit = 0;
- }
- /*
- ================
- idCompressor_Arithmetic::InitProbabilities
- ================
- */
- void idCompressor_Arithmetic::InitProbabilities( void ) {
- high = AC_HIGH_INIT;
- low = AC_LOW_INIT;
- underflowBits = 0;
- code = 0;
- for( int i = 0; i < (1<<AC_WORD_LENGTH); i++ ) {
- probabilities[ i ].low = i;
- probabilities[ i ].high = i + 1;
- }
- scale = (1<<AC_WORD_LENGTH);
- }
- /*
- ================
- idCompressor_Arithmetic::UpdateProbabilities
- ================
- */
- void idCompressor_Arithmetic::UpdateProbabilities( acSymbol_t* symbol ) {
- int i, x;
- x = symbol->position;
-
- probabilities[ x ].high++;
- for( i = x + 1; i < (1<<AC_WORD_LENGTH); i++ ) {
- probabilities[ i ].low++;
- probabilities[ i ].high++;
- }
- scale++;
- }
- /*
- ================
- idCompressor_Arithmetic::GetCurrentCount
- ================
- */
- int idCompressor_Arithmetic::GetCurrentCount( void ) {
- return (unsigned int) ( ( ( ( (long) code - low ) + 1 ) * scale - 1 ) / ( ( (long) high - low ) + 1 ) );
- }
- /*
- ================
- idCompressor_Arithmetic::ProbabilityForCount
- ================
- */
- int idCompressor_Arithmetic::ProbabilityForCount( unsigned int count ) {
- #if 1
- int len, mid, offset, res;
- len = (1<<AC_WORD_LENGTH);
- mid = len;
- offset = 0;
- res = 0;
- while( mid > 0 ) {
- mid = len >> 1;
- if ( count >= probabilities[offset+mid].high ) {
- offset += mid;
- len -= mid;
- res = 1;
- }
- else if ( count < probabilities[offset+mid].low ) {
- len -= mid;
- res = 0;
- } else {
- return offset+mid;
- }
- }
- return offset+res;
- #else
- int j;
- for( j = 0; j < (1<<AC_WORD_LENGTH); j++ ) {
- if ( count >= probabilities[ j ].low && count < probabilities[ j ].high ) {
- return j;
- }
- }
- assert( false );
- return 0;
- #endif
- }
- /*
- ================
- idCompressor_Arithmetic::SymbolFromCount
- ================
- */
- int idCompressor_Arithmetic::SymbolFromCount( unsigned int count, acSymbol_t* symbol ) {
- int p = ProbabilityForCount( count );
- symbol->low = probabilities[ p ].low;
- symbol->high = probabilities[ p ].high;
- symbol->position = p;
- return p;
- }
- /*
- ================
- idCompressor_Arithmetic::RemoveSymbolFromStream
- ================
- */
- void idCompressor_Arithmetic::RemoveSymbolFromStream( acSymbol_t* symbol ) {
- long range;
- range = ( long )( high - low ) + 1;
- high = low + ( unsigned short )( ( range * symbol->high ) / scale - 1 );
- low = low + ( unsigned short )( ( range * symbol->low ) / scale );
- while( true ) {
- if ( ( high & AC_MSB_MASK ) == ( low & AC_MSB_MASK ) ) {
- } else if( ( low & AC_MSB2_MASK ) == AC_MSB2_MASK && ( high & AC_MSB2_MASK ) == 0 ) {
- code ^= AC_MSB2_MASK;
- low &= AC_MSB2_MASK - 1;
- high |= AC_MSB2_MASK;
- } else {
- UpdateProbabilities( symbol );
- return;
- }
- low <<= 1;
- high <<= 1;
- high |= 1;
- code <<= 1;
- code |= ReadBits( 1 );
- }
- }
- /*
- ================
- idCompressor_Arithmetic::GetBit
- ================
- */
- int idCompressor_Arithmetic::GetBit( void ) {
- int getbit;
- if( symbolBit <= 0 ) {
- // read a new symbol out
- acSymbol_t symbol;
- symbolBuffer = SymbolFromCount( GetCurrentCount(), &symbol );
- RemoveSymbolFromStream( &symbol );
- symbolBit = AC_WORD_LENGTH;
- }
- getbit = ( symbolBuffer >> ( AC_WORD_LENGTH - symbolBit ) ) & 1;
- symbolBit--;
- return getbit;
- }
- /*
- ================
- idCompressor_Arithmetic::EncodeSymbol
- ================
- */
- void idCompressor_Arithmetic::EncodeSymbol( acSymbol_t* symbol ) {
- unsigned int range;
-
- // rescale high and low for the new symbol.
- range = ( high - low ) + 1;
- high = low + ( unsigned short )(( range * symbol->high ) / scale - 1 );
- low = low + ( unsigned short )(( range * symbol->low ) / scale );
- while( true ) {
- if ( ( high & AC_MSB_MASK ) == ( low & AC_MSB_MASK ) ) {
- // the high digits of low and high have converged, and can be written to the stream
- WriteBits( high >> AC_MSB_SHIFT, 1 );
- while( underflowBits > 0 ) {
- WriteBits( ~high >> AC_MSB_SHIFT, 1 );
- underflowBits--;
- }
- } else if ( ( low & AC_MSB2_MASK ) && !( high & AC_MSB2_MASK ) ) {
- // underflow is in danger of happening, 2nd digits are converging but 1st digits don't match
- underflowBits += 1;
- low &= AC_MSB2_MASK - 1;
- high |= AC_MSB2_MASK;
- } else {
- UpdateProbabilities( symbol );
- return;
- }
- low <<= 1;
- high <<= 1;
- high |= 1;
- }
- }
- /*
- ================
- idCompressor_Arithmetic::CharToSymbol
- ================
- */
- void idCompressor_Arithmetic::CharToSymbol( byte c, acSymbol_t* symbol ) {
- symbol->low = probabilities[ c ].low;
- symbol->high = probabilities[ c ].high;
- symbol->position = c;
- }
- /*
- ================
- idCompressor_Arithmetic::PutBit
- ================
- */
- void idCompressor_Arithmetic::PutBit( int putbit ) {
- symbolBuffer |= ( putbit & 1 ) << symbolBit;
- symbolBit++;
- if( symbolBit >= AC_WORD_LENGTH ) {
- acSymbol_t symbol;
- CharToSymbol( symbolBuffer, &symbol );
- EncodeSymbol( &symbol );
- symbolBit = 0;
- symbolBuffer = 0;
- }
- }
- /*
- ================
- idCompressor_Arithmetic::WriteOverflowBits
- ================
- */
- void idCompressor_Arithmetic::WriteOverflowBits( void ) {
- WriteBits( low >> AC_MSB2_SHIFT, 1 );
- underflowBits++;
- while( underflowBits-- > 0 ) {
- WriteBits( ~low >> AC_MSB2_SHIFT, 1 );
- }
- }
- /*
- ================
- idCompressor_Arithmetic::Write
- ================
- */
- int idCompressor_Arithmetic::Write( const void *inData, int inLength ) {
- int i, j;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- InitCompress( inData, inLength );
- for( i = 0; i < inLength; i++ ) {
- if ( ( readTotalBytes & ( ( 1 << 14 ) - 1 ) ) == 0 ) {
- if ( readTotalBytes ) {
- WriteOverflowBits();
- WriteBits( 0, 15 );
- while( writeBit ) {
- WriteBits( 0, 1 );
- }
- WriteBits( 255, 8 );
- }
- InitProbabilities();
- }
- for ( j = 0; j < 8; j++ ) {
- PutBit( ReadBits( 1 ) );
- }
- }
- return inLength;
- }
- /*
- ================
- idCompressor_Arithmetic::FinishCompress
- ================
- */
- void idCompressor_Arithmetic::FinishCompress( void ) {
- if ( compress == false ) {
- return;
- }
- WriteOverflowBits();
- idCompressor_BitStream::FinishCompress();
- }
- /*
- ================
- idCompressor_Arithmetic::Read
- ================
- */
- int idCompressor_Arithmetic::Read( void *outData, int outLength ) {
- int i, j;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- InitDecompress( outData, outLength );
- for( i = 0; i < outLength && readLength >= 0; i++ ) {
- if ( ( writeTotalBytes & ( ( 1 << 14 ) - 1 ) ) == 0 ) {
- if ( writeTotalBytes ) {
- while( readBit ) {
- ReadBits( 1 );
- }
- while( ReadBits( 8 ) == 0 && readLength > 0 ) {
- }
- }
- InitProbabilities();
- for ( j = 0; j < AC_NUM_BITS; j++ ) {
- code <<= 1;
- code |= ReadBits( 1 );
- }
- }
- for ( j = 0; j < 8; j++ ) {
- WriteBits( GetBit(), 1 );
- }
- }
- return i;
- }
- /*
- =================================================================================
- idCompressor_LZSS
- In 1977 Abraham Lempel and Jacob Ziv presented a dictionary based scheme for
- text compression called LZ77. For any new text LZ77 outputs an offset/length
- pair to previously seen text and the next new byte after the previously seen
- text.
- In 1982 James Storer and Thomas Szymanski presented a modification on the work
- of Lempel and Ziv called LZSS. LZ77 always outputs an offset/length pair, even
- if a match is only one byte long. An offset/length pair usually takes more than
- a single byte to store and the compression is not optimal for small match sizes.
- LZSS uses a bit flag which tells whether the following data is a literal (byte)
- or an offset/length pair.
- The following algorithm is an implementation of LZSS with arbitrary word size.
- =================================================================================
- */
- const int LZSS_BLOCK_SIZE = 65535;
- const int LZSS_HASH_BITS = 10;
- const int LZSS_HASH_SIZE = ( 1 << LZSS_HASH_BITS );
- const int LZSS_HASH_MASK = ( 1 << LZSS_HASH_BITS ) - 1;
- const int LZSS_OFFSET_BITS = 11;
- const int LZSS_LENGTH_BITS = 5;
- class idCompressor_LZSS : public idCompressor_BitStream {
- public:
- idCompressor_LZSS( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- protected:
- int offsetBits;
- int lengthBits;
- int minMatchWords;
- byte block[LZSS_BLOCK_SIZE];
- int blockSize;
- int blockIndex;
- int hashTable[LZSS_HASH_SIZE];
- int hashNext[LZSS_BLOCK_SIZE * 8];
- protected:
- bool FindMatch( int startWord, int startValue, int &wordOffset, int &numWords );
- void AddToHash( int index, int hash );
- int GetWordFromBlock( int wordOffset ) const;
- virtual void CompressBlock( void );
- virtual void DecompressBlock( void );
- };
- /*
- ================
- idCompressor_LZSS::Init
- ================
- */
- void idCompressor_LZSS::Init( idFile *f, bool compress, int wordLength ) {
- idCompressor_BitStream::Init( f, compress, wordLength );
- offsetBits = LZSS_OFFSET_BITS;
- lengthBits = LZSS_LENGTH_BITS;
- minMatchWords = ( offsetBits + lengthBits + wordLength ) / wordLength;
- blockSize = 0;
- blockIndex = 0;
- }
- /*
- ================
- idCompressor_LZSS::FindMatch
- ================
- */
- bool idCompressor_LZSS::FindMatch( int startWord, int startValue, int &wordOffset, int &numWords ) {
- int i, n, hash, bottom, maxBits;
- wordOffset = startWord;
- numWords = minMatchWords - 1;
- bottom = Max( 0, startWord - ( ( 1 << offsetBits ) - 1 ) );
- maxBits = ( blockSize << 3 ) - startWord * wordLength;
- hash = startValue & LZSS_HASH_MASK;
- for ( i = hashTable[hash]; i >= bottom; i = hashNext[i] ) {
- n = Compare( block, i * wordLength, block, startWord * wordLength, Min( maxBits, ( startWord - i ) * wordLength ) );
- if ( n > numWords * wordLength ) {
- numWords = n / wordLength;
- wordOffset = i;
- if ( numWords > ( ( 1 << lengthBits ) - 1 + minMatchWords ) - 1 ) {
- numWords = ( ( 1 << lengthBits ) - 1 + minMatchWords ) - 1;
- break;
- }
- }
- }
- return ( numWords >= minMatchWords );
- }
- /*
- ================
- idCompressor_LZSS::AddToHash
- ================
- */
- void idCompressor_LZSS::AddToHash( int index, int hash ) {
- hashNext[index] = hashTable[hash];
- hashTable[hash] = index;
- }
- /*
- ================
- idCompressor_LZSS::GetWordFromBlock
- ================
- */
- int idCompressor_LZSS::GetWordFromBlock( int wordOffset ) const {
- int blockBit, blockByte, value, valueBits, get, fraction;
- blockBit = ( wordOffset * wordLength ) & 7;
- blockByte = ( wordOffset * wordLength ) >> 3;
- if ( blockBit != 0 ) {
- blockByte++;
- }
- value = 0;
- valueBits = 0;
- while ( valueBits < wordLength ) {
- if ( blockBit == 0 ) {
- if ( blockByte >= LZSS_BLOCK_SIZE ) {
- return value;
- }
- blockByte++;
- }
- get = 8 - blockBit;
- if ( get > (wordLength - valueBits) ) {
- get = (wordLength - valueBits);
- }
- fraction = block[blockByte - 1];
- fraction >>= blockBit;
- fraction &= ( 1 << get ) - 1;
- value |= fraction << valueBits;
- valueBits += get;
- blockBit = ( blockBit + get ) & 7;
- }
- return value;
- }
- /*
- ================
- idCompressor_LZSS::CompressBlock
- ================
- */
- void idCompressor_LZSS::CompressBlock( void ) {
- int i, startWord, startValue, wordOffset, numWords;
- InitCompress( block, blockSize );
- memset( hashTable, -1, sizeof( hashTable ) );
- memset( hashNext, -1, sizeof( hashNext ) );
- startWord = 0;
- while( readByte < readLength ) {
- startValue = ReadBits( wordLength );
- if ( FindMatch( startWord, startValue, wordOffset, numWords ) ) {
- WriteBits( 1, 1 );
- WriteBits( startWord - wordOffset, offsetBits );
- WriteBits( numWords - minMatchWords, lengthBits );
- UnreadBits( wordLength );
- for ( i = 0; i < numWords; i++ ) {
- startValue = ReadBits( wordLength );
- AddToHash( startWord, startValue & LZSS_HASH_MASK );
- startWord++;
- }
- } else {
- WriteBits( 0, 1 );
- WriteBits( startValue, wordLength );
- AddToHash( startWord, startValue & LZSS_HASH_MASK );
- startWord++;
- }
- }
- blockSize = 0;
- }
- /*
- ================
- idCompressor_LZSS::DecompressBlock
- ================
- */
- void idCompressor_LZSS::DecompressBlock( void ) {
- int i, offset, startWord, numWords;
- InitDecompress( block, LZSS_BLOCK_SIZE );
- startWord = 0;
- while( writeByte < writeLength && readLength >= 0 ) {
- if ( ReadBits( 1 ) ) {
- offset = startWord - ReadBits( offsetBits );
- numWords = ReadBits( lengthBits ) + minMatchWords;
- for ( i = 0; i < numWords; i++ ) {
- WriteBits( GetWordFromBlock( offset + i ), wordLength );
- startWord++;
- }
- } else {
- WriteBits( ReadBits( wordLength ), wordLength );
- startWord++;
- }
- }
- blockSize = Min( writeByte, LZSS_BLOCK_SIZE );
- }
- /*
- ================
- idCompressor_LZSS::Write
- ================
- */
- int idCompressor_LZSS::Write( const void *inData, int inLength ) {
- int i, n;
- if ( compress == false || inLength <= 0 ) {
- return 0;
- }
- for ( n = i = 0; i < inLength; i += n ) {
- n = LZSS_BLOCK_SIZE - blockSize;
- if ( inLength - i >= n ) {
- memcpy( block + blockSize, ((const byte *)inData) + i, n );
- blockSize = LZSS_BLOCK_SIZE;
- CompressBlock();
- blockSize = 0;
- } else {
- memcpy( block + blockSize, ((const byte *)inData) + i, inLength - i );
- n = inLength - i;
- blockSize += n;
- }
- }
- return inLength;
- }
- /*
- ================
- idCompressor_LZSS::FinishCompress
- ================
- */
- void idCompressor_LZSS::FinishCompress( void ) {
- if ( compress == false ) {
- return;
- }
- if ( blockSize ) {
- CompressBlock();
- }
- idCompressor_BitStream::FinishCompress();
- }
- /*
- ================
- idCompressor_LZSS::Read
- ================
- */
- int idCompressor_LZSS::Read( void *outData, int outLength ) {
- int i, n;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- if ( !blockSize ) {
- DecompressBlock();
- }
- for ( n = i = 0; i < outLength; i += n ) {
- if ( !blockSize ) {
- return i;
- }
- n = blockSize - blockIndex;
- if ( outLength - i >= n ) {
- memcpy( ((byte *)outData) + i, block + blockIndex, n );
- DecompressBlock();
- blockIndex = 0;
- } else {
- memcpy( ((byte *)outData) + i, block + blockIndex, outLength - i );
- n = outLength - i;
- blockIndex += n;
- }
- }
- return outLength;
- }
- /*
- =================================================================================
- idCompressor_LZSS_WordAligned
- Outputs word aligned compressed data.
- =================================================================================
- */
- class idCompressor_LZSS_WordAligned : public idCompressor_LZSS {
- public:
- idCompressor_LZSS_WordAligned( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- private:
- virtual void CompressBlock( void );
- virtual void DecompressBlock( void );
- };
- /*
- ================
- idCompressor_LZSS_WordAligned::Init
- ================
- */
- void idCompressor_LZSS_WordAligned::Init( idFile *f, bool compress, int wordLength ) {
- idCompressor_LZSS::Init( f, compress, wordLength );
- offsetBits = 2 * wordLength;
- lengthBits = wordLength;
- minMatchWords = ( offsetBits + lengthBits + wordLength ) / wordLength;
- blockSize = 0;
- blockIndex = 0;
- }
- /*
- ================
- idCompressor_LZSS_WordAligned::CompressBlock
- ================
- */
- void idCompressor_LZSS_WordAligned::CompressBlock( void ) {
- int i, startWord, startValue, wordOffset, numWords;
- InitCompress( block, blockSize );
- memset( hashTable, -1, sizeof( hashTable ) );
- memset( hashNext, -1, sizeof( hashNext ) );
- startWord = 0;
- while( readByte < readLength ) {
- startValue = ReadBits( wordLength );
- if ( FindMatch( startWord, startValue, wordOffset, numWords ) ) {
- WriteBits( numWords - ( minMatchWords - 1 ), lengthBits );
- WriteBits( startWord - wordOffset, offsetBits );
- UnreadBits( wordLength );
- for ( i = 0; i < numWords; i++ ) {
- startValue = ReadBits( wordLength );
- AddToHash( startWord, startValue & LZSS_HASH_MASK );
- startWord++;
- }
- } else {
- WriteBits( 0, lengthBits );
- WriteBits( startValue, wordLength );
- AddToHash( startWord, startValue & LZSS_HASH_MASK );
- startWord++;
- }
- }
- blockSize = 0;
- }
- /*
- ================
- idCompressor_LZSS_WordAligned::DecompressBlock
- ================
- */
- void idCompressor_LZSS_WordAligned::DecompressBlock( void ) {
- int i, offset, startWord, numWords;
- InitDecompress( block, LZSS_BLOCK_SIZE );
- startWord = 0;
- while( writeByte < writeLength && readLength >= 0 ) {
- numWords = ReadBits( lengthBits );
- if ( numWords ) {
- numWords += ( minMatchWords - 1 );
- offset = startWord - ReadBits( offsetBits );
- for ( i = 0; i < numWords; i++ ) {
- WriteBits( GetWordFromBlock( offset + i ), wordLength );
- startWord++;
- }
- } else {
- WriteBits( ReadBits( wordLength ), wordLength );
- startWord++;
- }
- }
- blockSize = Min( writeByte, LZSS_BLOCK_SIZE );
- }
- /*
- =================================================================================
- idCompressor_LZW
- http://www.unisys.com/about__unisys/lzw
- http://www.dogma.net/markn/articles/lzw/lzw.htm
- http://www.cs.cf.ac.uk/Dave/Multimedia/node214.html
- http://www.cs.duke.edu/csed/curious/compression/lzw.html
- http://oldwww.rasip.fer.hr/research/compress/algorithms/fund/lz/lzw.html
- This is the same compression scheme used by GIF with the exception that
- the EOI and clear codes are not explicitly stored. Instead EOI happens
- when the input stream runs dry and CC happens when the table gets to big.
- This is a derivation of LZ78, but the dictionary starts with all single
- character values so only code words are output. It is similar in theory
- to LZ77, but instead of using the previous X bytes as a lookup table, a table
- is built as the stream is read. The compressor and decompressor use the
- same formula, so the tables should be exactly alike. The only catch is the
- decompressor is always one step behind the compressor and may get a code not
- yet in the table. In this case, it is easy to determine what the next code
- is going to be (it will be the previous string plus the first byte of the
- previous string).
- The dictionary can be any size, but 12 bits seems to produce best results for
- most sample data. The code size is variable. It starts with the minimum
- number of bits required to store the dictionary and automatically increases
- as the dictionary gets bigger (it starts at 9 bits and grows to 10 bits when
- item 512 is added, 11 bits when 1024 is added, etc...) once the the dictionary
- is filled (4096 items for a 12 bit dictionary), the whole thing is cleared and
- the process starts over again.
- The compressor increases the bit size after it adds the item, while the
- decompressor does before it adds the item. The difference is subtle, but
- it's because decompressor being one step behind. Otherwise, the decompressor
- would read 512 with only 9 bits.
- If "Hello" is in the dictionary, then "Hell", "Hel", "He" and "H" will be too.
- We use this to our advantage by storing the index of the previous code, and
- the value of the last character. This means when we traverse through the
- dictionary, we get the characters in reverse.
- Dictionary entries 0-255 are always going to have the values 0-255
- =================================================================================
- */
- class idCompressor_LZW : public idCompressor_BitStream {
- public:
- idCompressor_LZW( void ) {}
- void Init( idFile *f, bool compress, int wordLength );
- void FinishCompress( void );
- int Write( const void *inData, int inLength );
- int Read( void *outData, int outLength );
- protected:
- int AddToDict( int w, int k );
- int Lookup( int w, int k );
- bool BumpBits();
- int WriteChain( int code );
- void DecompressBlock();
- static const int LZW_BLOCK_SIZE = 32767;
- static const int LZW_START_BITS = 9;
- static const int LZW_FIRST_CODE = (1 << (LZW_START_BITS-1));
- static const int LZW_DICT_BITS = 12;
- static const int LZW_DICT_SIZE = 1 << LZW_DICT_BITS;
- // Dictionary data
- struct {
- int k;
- int w;
- } dictionary[LZW_DICT_SIZE];
- idHashIndex index;
- int nextCode;
- int codeBits;
- // Block data
- byte block[LZW_BLOCK_SIZE];
- int blockSize;
- int blockIndex;
- // Used by the compressor
- int w;
- // Used by the decompressor
- int oldCode;
- };
- /*
- ================
- idCompressor_LZW::Init
- ================
- */
- void idCompressor_LZW::Init( idFile *f, bool compress, int wordLength ) {
- idCompressor_BitStream::Init( f, compress, wordLength );
- for ( int i=0; i<LZW_FIRST_CODE; i++ ) {
- dictionary[i].k = i;
- dictionary[i].w = -1;
- }
- index.Clear();
- nextCode = LZW_FIRST_CODE;
- codeBits = LZW_START_BITS;
- blockSize = 0;
- blockIndex = 0;
- w = -1;
- oldCode = -1;
- }
- /*
- ================
- idCompressor_LZW::Read
- ================
- */
- int idCompressor_LZW::Read( void *outData, int outLength ) {
- int i, n;
- if ( compress == true || outLength <= 0 ) {
- return 0;
- }
- if ( !blockSize ) {
- DecompressBlock();
- }
- for ( n = i = 0; i < outLength; i += n ) {
- if ( !blockSize ) {
- return i;
- }
- n = blockSize - blockIndex;
- if ( outLength - i >= n ) {
- memcpy( ((byte *)outData) + i, block + blockIndex, n );
- DecompressBlock();
- blockIndex = 0;
- } else {
- memcpy( ((byte *)outData) + i, block + blockIndex, outLength - i );
- n = outLength - i;
- blockIndex += n;
- }
- }
- return outLength;
- }
- /*
- ================
- idCompressor_LZW::Lookup
- ================
- */
- int idCompressor_LZW::Lookup( int w, int k ) {
- int j;
- if ( w == -1 ) {
- return k;
- } else {
- for ( j = index.First( w ^ k ); j >= 0 ; j = index.Next( j ) ) {
- if ( dictionary[ j ].k == k && dictionary[ j ].w == w ) {
- return j;
- }
- }
- }
- return -1;
- }
- /*
- ================
- idCompressor_LZW::AddToDict
- ================
- */
- int idCompressor_LZW::AddToDict( int w, int k ) {
- dictionary[ nextCode ].k = k;
- dictionary[ nextCode ].w = w;
- index.Add( w ^ k, nextCode );
- return nextCode++;
- }
- /*
- ================
- idCompressor_LZW::BumpBits
- Possibly increments codeBits
- Returns true if the dictionary was cleared
- ================
- */
- bool idCompressor_LZW::BumpBits() {
- if ( nextCode == ( 1 << codeBits ) ) {
- codeBits ++;
- if ( codeBits > LZW_DICT_BITS ) {
- nextCode = LZW_FIRST_CODE;
- codeBits = LZW_START_BITS;
- index.Clear();
- return true;
- }
- }
- return false;
- }
- /*
- ================
- idCompressor_LZW::FinishCompress
- ================
- */
- void idCompressor_LZW::FinishCompress( void ) {
- WriteBits( w, codeBits );
- idCompressor_BitStream::FinishCompress();
- }
- /*
- ================
- idCompressor_LZW::Write
- ================
- */
- int idCompressor_LZW::Write( const void *inData, int inLength ) {
- int i;
- InitCompress( inData, inLength );
- for ( i = 0; i < inLength; i++ ) {
- int k = ReadBits( 8 );
- int code = Lookup(w, k);
- if ( code >= 0 ) {
- w = code;
- } else {
- WriteBits( w, codeBits );
- if ( !BumpBits() ) {
- AddToDict( w, k );
- }
- w = k;
- }
- }
- return inLength;
- }
- /*
- ================
- idCompressor_LZW::WriteChain
- The chain is stored backwards, so we have to write it to a buffer then output the buffer in reverse
- ================
- */
- int idCompressor_LZW::WriteChain( int code ) {
- byte chain[LZW_DICT_SIZE];
- int firstChar = 0;
- int i = 0;
- do {
- assert( i < LZW_DICT_SIZE-1 && code >= 0 );
- chain[i++] = dictionary[code].k;
- code = dictionary[code].w;
- } while ( code >= 0 );
- firstChar = chain[--i];
- for ( ; i >= 0; i-- ) {
- WriteBits( chain[i], 8 );
- }
- return firstChar;
- }
- /*
- ================
- idCompressor_LZW::DecompressBlock
- ================
- */
- void idCompressor_LZW::DecompressBlock() {
- int code, firstChar;
- InitDecompress( block, LZW_BLOCK_SIZE );
- while( writeByte < writeLength - LZW_DICT_SIZE && readLength > 0 ) {
- assert( codeBits <= LZW_DICT_BITS );
- code = ReadBits( codeBits );
- if ( readLength == 0 ) {
- break;
- }
- if ( oldCode == -1 ) {
- assert( code < 256 );
- WriteBits( code, 8 );
- oldCode = code;
- firstChar = code;
- continue;
- }
- if ( code >= nextCode ) {
- assert( code == nextCode );
- firstChar = WriteChain( oldCode );
- WriteBits( firstChar, 8 );
- } else {
- firstChar = WriteChain( code );
- }
- AddToDict( oldCode, firstChar );
- if ( BumpBits() ) {
- oldCode = -1;
- } else {
- oldCode = code;
- }
- }
- blockSize = Min( writeByte, LZW_BLOCK_SIZE );
- }
- /*
- =================================================================================
- idCompressor
- =================================================================================
- */
- /*
- ================
- idCompressor::AllocNoCompression
- ================
- */
- idCompressor * idCompressor::AllocNoCompression( void ) {
- return new idCompressor_None();
- }
- /*
- ================
- idCompressor::AllocBitStream
- ================
- */
- idCompressor * idCompressor::AllocBitStream( void ) {
- return new idCompressor_BitStream();
- }
- /*
- ================
- idCompressor::AllocRunLength
- ================
- */
- idCompressor * idCompressor::AllocRunLength( void ) {
- return new idCompressor_RunLength();
- }
- /*
- ================
- idCompressor::AllocRunLength_ZeroBased
- ================
- */
- idCompressor * idCompressor::AllocRunLength_ZeroBased( void ) {
- return new idCompressor_RunLength_ZeroBased();
- }
- /*
- ================
- idCompressor::AllocHuffman
- ================
- */
- idCompressor * idCompressor::AllocHuffman( void ) {
- return new idCompressor_Huffman();
- }
- /*
- ================
- idCompressor::AllocArithmetic
- ================
- */
- idCompressor * idCompressor::AllocArithmetic( void ) {
- return new idCompressor_Arithmetic();
- }
- /*
- ================
- idCompressor::AllocLZSS
- ================
- */
- idCompressor * idCompressor::AllocLZSS( void ) {
- return new idCompressor_LZSS();
- }
- /*
- ================
- idCompressor::AllocLZSS_WordAligned
- ================
- */
- idCompressor * idCompressor::AllocLZSS_WordAligned( void ) {
- return new idCompressor_LZSS_WordAligned();
- }
- /*
- ================
- idCompressor::AllocLZW
- ================
- */
- idCompressor * idCompressor::AllocLZW( void ) {
- return new idCompressor_LZW();
- }
|