diff --git a/PseuWoW.sln b/PseuWoW.sln index 312fa00..546e2d2 100644 --- a/PseuWoW.sln +++ b/PseuWoW.sln @@ -26,6 +26,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Irrlicht", "src\dep\src\irr {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StuffExtract", "src\tools\stuffextract.vcproj", "{EFFE60F4-DA39-41E8-9E53-E462000A2D91}" + ProjectSection(ProjectDependencies) = postProject + {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} + {F548FC51-24A4-45FF-A381-BEBC39F18270} = {F548FC51-24A4-45FF-A381-BEBC39F18270} + EndProjectSection +EndProject Global GlobalSection(SolutionConfiguration) = preSolution Debug = Debug @@ -74,6 +80,14 @@ Global {E08E042A-6C45-411B-92BE-3CC31331019F}.Release.Build.0 = Release|Win32 {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU DebugInfo.ActiveCfg = Release - Fast FPU DebugInfo|Win32 {E08E042A-6C45-411B-92BE-3CC31331019F}.Release - Fast FPU DebugInfo.Build.0 = Release - Fast FPU DebugInfo|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Debug.ActiveCfg = Debug|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Debug.Build.0 = Debug|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Relase - Fast FPU.ActiveCfg = Release|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Relase - Fast FPU.Build.0 = Release|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Release.ActiveCfg = Release|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Release.Build.0 = Release|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Release - Fast FPU DebugInfo.ActiveCfg = Release|Win32 + {EFFE60F4-DA39-41E8-9E53-E462000A2D91}.Release - Fast FPU DebugInfo.Build.0 = Release|Win32 EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EndGlobalSection diff --git a/bin/conf/PseuWoW.conf.default b/bin/conf/PseuWoW.conf.default index 17158d7..9858f04 100644 --- a/bin/conf/PseuWoW.conf.default +++ b/bin/conf/PseuWoW.conf.default @@ -99,7 +99,7 @@ cmdchar=. // set this to 1 if you want to have a 3D-window showing stuff // NOTE: this is only to activate the window for now, 3D-stuff will come later!! // until some frame-limiter is implemented, it will always use 100% CPU usage when -// the GUI is enabled and activated. (if its in background - 0% CPU load.) +// the GUI is enabled and activated. (if its in background - way less CPU load.) enablegui=0 diff --git a/bin/conf/gui.conf.default b/bin/conf/gui.conf.default new file mode 100644 index 0000000..e906d55 --- /dev/null +++ b/bin/conf/gui.conf.default @@ -0,0 +1,24 @@ +[#uppercase] +[GUI] + +// drivers: +// 0: Nulldevice (no graphics) +// 1: fast but crappy software driver +// 2: good software driver, but slower +// 3: OpenGL +// 4: DirectX 8.1 +// 5: DirectX 9.0c +driver=5 + +// resolution and more +resx=1024 +resy=768 +windowed=1 +vsync=0 + +// use realtime shadows? +// works only with DirectX or OpenGL +shadows=1 + +// color depth; 16 or 32 bit +depth=32 \ No newline at end of file diff --git a/bin/data/race.scp b/bin/data/race.scp deleted file mode 100644 index d7008aa..0000000 --- a/bin/data/race.scp +++ /dev/null @@ -1,31 +0,0 @@ -[1] -defaultlang=7 -name=Human - -[2] -defaultlang=1 -name=Orc - -[3] -defaultlang=7 -name=Dwarf - -[4] -defaultlang=7 -name=Night Elf - -[5] -defaultlang=1 -name=Undead - -[6] -defaultlang=1 -name=Tauren - -[7] -defaultlang=7 -name=Gnome - -[8] -defaultlang=1 -name=Troll diff --git a/bin/data/class.scp b/bin/data/scp/class.scp similarity index 100% rename from bin/data/class.scp rename to bin/data/scp/class.scp diff --git a/bin/data/gender.scp b/bin/data/scp/gender.scp similarity index 100% rename from bin/data/gender.scp rename to bin/data/scp/gender.scp diff --git a/bin/data/language.scp b/bin/data/scp/language.scp similarity index 100% rename from bin/data/language.scp rename to bin/data/scp/language.scp diff --git a/bin/data/map.scp b/bin/data/scp/map.scp similarity index 100% rename from bin/data/map.scp rename to bin/data/scp/map.scp diff --git a/bin/data/scp/race.scp b/bin/data/scp/race.scp new file mode 100644 index 0000000..c05fed9 --- /dev/null +++ b/bin/data/scp/race.scp @@ -0,0 +1,35 @@ +// note that the faction is equal to the default language id: +// 1: Horde, Orcish +// 7: Alliance, Common + +[1] +faction=7 +name=Human + +[2] +faction=1 +name=Orc + +[3] +faction=7 +name=Dwarf + +[4] +faction=7 +name=Night Elf + +[5] +faction=1 +name=Undead + +[6] +faction=1 +name=Tauren + +[7] +faction=7 +name=Gnome + +[8] +faction=1 +name=Troll diff --git a/bin/data/zone.scp b/bin/data/scp/zone.scp similarity index 100% rename from bin/data/zone.scp rename to bin/data/scp/zone.scp diff --git a/bin/scripts/_onwhisper.def b/bin/scripts/_onwhisper.def index 3d37aaa..7f6d512 100644 --- a/bin/scripts/_onwhisper.def +++ b/bin/scripts/_onwhisper.def @@ -3,4 +3,4 @@ // EXECUTED EVERYTIME A WHISPER IS BEEING RECIEVED // comment out the following line if you dont need it -//REPLY,0 No need to whisper me, i am not yet programmed for it! \ No newline at end of file +//REPLY,0 [BOT] No need to whisper, nobody is here who could read what you wrote. \ No newline at end of file diff --git a/bin/scripts/config.def b/bin/scripts/config.def index 8f5df8c..8c4d21b 100644 --- a/bin/scripts/config.def +++ b/bin/scripts/config.def @@ -3,12 +3,16 @@ LOADCONF PseuWoW.conf LOADCONF users.conf -// Apply the configureation +// Apply the configuration APPLYCONF // Apply user permissions APPLYPERMISSIONS +IF ${#ENABLEGUI} + LOADCONF gui.conf +ENDIF + // remove dangerous variables CLEANUPVARS diff --git a/bin/scripts/exloadscp.def b/bin/scripts/exloadscp.def new file mode 100644 index 0000000..04dfbad --- /dev/null +++ b/bin/scripts/exloadscp.def @@ -0,0 +1,6 @@ +#permission=255 +if ?{fileexists ${@def}} + loadscp,{${@0}} ${@def} +else + logdebug skipped loading of non-existent file '${@def}' +endif \ No newline at end of file diff --git a/bin/scripts/loadallscp.def b/bin/scripts/loadallscp.def index a2972ca..e05eac1 100644 --- a/bin/scripts/loadallscp.def +++ b/bin/scripts/loadallscp.def @@ -5,11 +5,16 @@ LOG * Loading SCP data storages... // example: // LOADSCP,test data/test.scp -LOADSCP,class data/class.scp -LOADSCP,gender data/gender.scp -LOADSCP,language data/language.scp -LOADSCP,map data/map.scp -LOADSCP,race data/race.scp +// load default databases +LOADSCP,class data/scp/class.scp +LOADSCP,gender data/scp/gender.scp +LOADSCP,language data/scp/language.scp +LOADSCP,map data/scp/map.scp +LOADSCP,race data/scp/race.scp + +// load extended databases if present. +EXLOADSCP,sound data/scp/sound.scp +EXLOADSCP,emote data/scp/emote.scp LOG * SCP loaded. diff --git a/bin/scripts/say.def b/bin/scripts/say.def index e5d0126..4bf5db5 100644 --- a/bin/scripts/say.def +++ b/bin/scripts/say.def @@ -3,7 +3,7 @@ SET,lang ${@0} SET,msg ${@def} -DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} defaultlang} +DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} faction} DEFAULT,lang ${#DEFAULTLANG} LOGDEBUG * Saying '${msg}' in lang ${lang} diff --git a/bin/scripts/whisper.def b/bin/scripts/whisper.def index 7dab89e..d9f6dde 100644 --- a/bin/scripts/whisper.def +++ b/bin/scripts/whisper.def @@ -5,7 +5,7 @@ SET,msg ${@def} SET,player ${@0} SET,lang ${@1} -DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} defaultlang} +DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} faction} DEFAULT,lang ${#DEFAULTLANG} LOGDEBUG * Whisp to '{${player}}' '{${msg}}' in lang '${lang}' diff --git a/bin/scripts/yell.def b/bin/scripts/yell.def index 4dd3162..f230e0f 100644 --- a/bin/scripts/yell.def +++ b/bin/scripts/yell.def @@ -3,7 +3,7 @@ SET,lang ${@0} SET,msg ${@def} -DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} defaultlang} +DEFAULT,#DEFAULTLANG ?{GETSCPVALUE,race,{${@myrace}} faction} DEFAULT,lang ${#DEFAULTLANG} LOGDEBUG * Yelling '${msg}' in lang ${lang} diff --git a/src/Client/DefScript/DefScript.cpp b/src/Client/DefScript/DefScript.cpp index c8cd1b3..8aa4987 100644 --- a/src/Client/DefScript/DefScript.cpp +++ b/src/Client/DefScript/DefScript.cpp @@ -87,6 +87,7 @@ void DefScriptPackage::_InitFunctions(void) AddFunc("uppercase",&DefScriptPackage::func_uppercase); AddFunc("lowercase",&DefScriptPackage::func_lowercase); AddFunc("random",&DefScriptPackage::func_random); + AddFunc("fileexists",&DefScriptPackage::func_fileexists); } void DefScriptPackage::AddFunc(std::string n,DefReturnResult (DefScriptPackage::*f)(CmdSet& Set)) diff --git a/src/Client/DefScript/DefScript.h b/src/Client/DefScript/DefScript.h index cb70ebc..f5d7890 100644 --- a/src/Client/DefScript/DefScript.h +++ b/src/Client/DefScript/DefScript.h @@ -177,6 +177,7 @@ private: DefReturnResult func_uppercase(CmdSet&); DefReturnResult func_lowercase(CmdSet&); DefReturnResult func_random(CmdSet&); + DefReturnResult func_fileexists(CmdSet&); // setup own function declarations here # include "DefScriptInterfaceInclude.h" diff --git a/src/Client/DefScript/DefScriptFunctions.cpp b/src/Client/DefScript/DefScriptFunctions.cpp index 8f81f54..2e620a1 100644 --- a/src/Client/DefScript/DefScriptFunctions.cpp +++ b/src/Client/DefScript/DefScriptFunctions.cpp @@ -453,5 +453,17 @@ DefReturnResult DefScriptPackage::func_random(CmdSet& Set) return r; } +DefReturnResult DefScriptPackage::func_fileexists(CmdSet& Set) +{ + std::fstream f; + f.open(Set.defaultarg.c_str(),std::ios_base::in); + if (f.is_open()) + { + f.close(); + return true; + } + return false; +} + diff --git a/src/Client/DefScript/DynamicEvent.cpp b/src/Client/DefScript/DynamicEvent.cpp index 3129ccb..9017be3 100644 --- a/src/Client/DefScript/DynamicEvent.cpp +++ b/src/Client/DefScript/DynamicEvent.cpp @@ -44,9 +44,12 @@ void DefScript_DynamicEventMgr::Remove(std::string name) return; for(DefDynamicEventList::iterator i = _storage.begin(); i != _storage.end(); i++) { - delete *i; - _storage.erase(i); - break; + if((*i)->name == name) + { + delete *i; + _storage.erase(i); + break; + } } return; } diff --git a/src/Client/GUI/PseuGUI.cpp b/src/Client/GUI/PseuGUI.cpp index 88278e6..353b7c3 100644 --- a/src/Client/GUI/PseuGUI.cpp +++ b/src/Client/GUI/PseuGUI.cpp @@ -102,7 +102,7 @@ void PseuGUI::UseShadows(bool b) _shadows = b; } -// if this fuction is called fom another thread the device will not work correctly. o_O +// if this fuction is called from another thread the device will not work correctly. o_O void PseuGUI::_Init(void) { _device = createDevice(_driverType,dimension2d(_xres,_yres),_colordepth,!_windowed,_shadows,_vsync); @@ -116,6 +116,7 @@ void PseuGUI::_Init(void) void PseuGUI::Shutdown(void) { + DEBUG(logdebug("PseuGUI::Shutdown()")); _mustdie = true; if(_device) { @@ -135,36 +136,31 @@ void PseuGUI::Run(void) while(_device && _device->run() && !_mustdie) { - if (_device->isWindowActive()) + if (!_device->isWindowActive()) { - _driver->beginScene(true, true, 0); - - _smgr->drawAll(); - - _driver->endScene(); - - fps = _driver->getFPS(); - - if (lastFPS != fps) - { - core::stringw str = L"PseuWoW ["; - str += _driver->getName(); - str += "] FPS:"; - str += fps; - - // this call does actually lock up the whole window, and i just dont know why; but definitely a threading issue. - _device->setWindowCaption(str.c_str()); - - lastFPS = fps; - DEBUG(logdebug("PseuGUI: Current FPS: %u",fps)); - } - + _device->sleep(10); // save cpu & gpu power if not focused } - else + _driver->beginScene(true, true, 0); + + _smgr->drawAll(); + + _driver->endScene(); + + fps = _driver->getFPS(); + + if (lastFPS != fps) { - _device->setWindowCaption(L"PseuWoW - Halted"); - _device->yield(); + core::stringw str = L"PseuWoW ["; + str += _driver->getName(); + str += "] FPS:"; + str += fps; + + _device->setWindowCaption(str.c_str()); + + lastFPS = fps; + DEBUG(logdebug("PseuGUI: Current FPS: %u",fps)); } + } DEBUG(logdebug("PseuGUI::Run() finished")); Shutdown(); diff --git a/src/Client/PseuWoW.cpp b/src/Client/PseuWoW.cpp index f52d874..0d321d3 100644 --- a/src/Client/PseuWoW.cpp +++ b/src/Client/PseuWoW.cpp @@ -3,11 +3,10 @@ #include #include -#include "Auth/ByteBuffer.h" +#include "ByteBuffer.h" #include "DefScript/DefScript.h" #include "DefScriptInterface.h" #include "Auth/BigNumber.h" -#include "Auth/ByteBuffer.h" #include "DefScript/DefScript.h" #include "Realm/RealmSocket.h" #include "World/WorldSession.h" @@ -132,6 +131,36 @@ bool PseuInstance::Init(void) { } } + // TODO: find a better loaction where to place this block! + if(GetConf()->enablegui) + { + uint16 x,y,depth; + uint8 driver; + bool shadows,vsync,win; + + driver=(uint8)atoi(GetScripts()->variables.Get("GUI::DRIVER").c_str()); + vsync=(bool)atoi(GetScripts()->variables.Get("GUI::VSYNC").c_str()); + depth=(uint8)atoi(GetScripts()->variables.Get("GUI::DEPTH").c_str()); + x=(uint16)atoi(GetScripts()->variables.Get("GUI::RESX").c_str()); + y=(uint16)atoi(GetScripts()->variables.Get("GUI::RESY").c_str()); + win=(bool)atoi(GetScripts()->variables.Get("GUI::WINDOWED").c_str()); + shadows=(bool)atoi(GetScripts()->variables.Get("GUI::SHADOWS").c_str()); + log("GUI settings: driver=%u, depth=%u, res=%ux%u, windowed=%u, shadows=%u",driver,depth,x,y,win,shadows); + if(x>0 && y>0 && (depth==16 || depth==32) && driver>0 && driver<=5) + { + PseuGUIRunnable *rgui = new PseuGUIRunnable(); + PseuGUI *gui = rgui->GetGUI(); + gui->SetInstance(this); + gui->SetDriver(driver); + gui->SetResolution(x,y,depth); + gui->SetVSync(vsync); + gui->UseShadows(shadows); + ZThread::Thread *t = new ZThread::Thread(rgui); + } + else + logerror("GUI: incorrect settings!"); + } + if(GetConf()->enablecli) { log("Starting CLI..."); @@ -139,16 +168,6 @@ bool PseuInstance::Init(void) { ZThread::Thread t(_cli); } - // TODO: find a better loaction where to place this block! - if(GetConf()->enablegui) - { - PseuGUIRunnable *rgui = new PseuGUIRunnable(); - PseuGUI *gui = rgui->GetGUI(); - gui->SetInstance(this); - // TODO: set resolution, shadows(on/off) and more here... - ZThread::Thread *t = new ZThread::Thread(rgui); - } - if(_error) { logcritical("Errors while initializing!"); @@ -220,7 +239,14 @@ void PseuInstance::Run(void) void PseuInstance::Update() { if(_sh.GetCount()) + { _sh.Select(0,0); // update the realmsocket + if(deleterealm) + { + deleterealm=false; + _rsession = NULL; // was deleted by SocketHandler already! + } + } if(createWorldSession && (!_wsession)) { @@ -301,10 +327,7 @@ void PseuInstanceConf::ApplyFromVarSet(VarSet &v) notifyping=(bool)atoi(v.Get("NOTIFYPING").c_str()); showmyopcodes=(bool)atoi(v.Get("SHOWMYOPCODES").c_str()); disablespellcheck=(bool)atoi(v.Get("DISABLESPELLCHECK").c_str()); - - // gui related enablegui=(bool)atoi(v.Get("ENABLEGUI").c_str()); - // TODO: add configs for resolution, fullscreen, etc. see PseuGUI::... for a list of functions. // clientversion is a bit more complicated to add { diff --git a/src/Client/PseuWoW.h b/src/Client/PseuWoW.h index 518dc58..4a32ffc 100644 --- a/src/Client/PseuWoW.h +++ b/src/Client/PseuWoW.h @@ -3,6 +3,7 @@ #define _PSEUWOW_H #include "common.h" +#include "HelperDefs.h" #include "log.h" #include "Auth/BigNumber.h" #include "DefScript/DefScript.h" @@ -88,6 +89,7 @@ class PseuInstance void Sleep(uint32 msecs); bool createWorldSession; + bool deleterealm; private: diff --git a/src/Client/Realm/RealmSocket.cpp b/src/Client/Realm/RealmSocket.cpp index 86148e1..55f0b2f 100644 --- a/src/Client/Realm/RealmSocket.cpp +++ b/src/Client/Realm/RealmSocket.cpp @@ -1,6 +1,6 @@ #include "common.h" #include "PseuWoW.h" -#include "Auth/ByteBuffer.h" +#include "ByteBuffer.h" #include "Auth/Sha1.h" #include "Auth/BigNumber.h" #include "RealmSocket.h" diff --git a/src/Client/World/WorldPacket.h b/src/Client/World/WorldPacket.h index 51610e3..3ecbaff 100644 --- a/src/Client/World/WorldPacket.h +++ b/src/Client/World/WorldPacket.h @@ -2,7 +2,7 @@ #define _WORLDPACKET_H #include "SysDefs.h" -#include "Auth/ByteBuffer.h" +#include "ByteBuffer.h" class WorldPacket : public ByteBuffer { diff --git a/src/Client/World/WorldSession.cpp b/src/Client/World/WorldSession.cpp index de6c1d3..d99f813 100644 --- a/src/Client/World/WorldSession.cpp +++ b/src/Client/World/WorldSession.cpp @@ -48,7 +48,11 @@ void WorldSession::Start(void) { log("Connecting to '%s' on port %u",GetInstance()->GetConf()->worldhost.c_str(),GetInstance()->GetConf()->worldport); _socket->Open(GetInstance()->GetConf()->worldhost,GetInstance()->GetConf()->worldport); - GetInstance()->GetRSession()->SetCloseAndDelete(); // realm socket is no longer needed + if(GetInstance()->GetRSession()) + { + GetInstance()->GetRSession()->SetCloseAndDelete(); // realm socket is no longer needed + GetInstance()->deleterealm=true; + } _valid=true; _sh.Add(_socket); _socket->SetDeleteByHandler(); @@ -85,7 +89,7 @@ void WorldSession::Update(void) if( _socket && _sh.GetCount() ) _sh.Select(0,0); - if(!_socket) + /*if(!_socket) { if(_valid) { @@ -93,8 +97,17 @@ void WorldSession::Update(void) } _logged=_authed=_valid=false; return; - } + }*/ + if(!_socket) + { + if(_valid) + { + this->Start(); + } + _logged=_authed=_valid=false; + return; + } OpcodeHandler *table = _GetOpcodeHandlerTable(); bool known=false; diff --git a/src/Client/World/WorldSocket.cpp b/src/Client/World/WorldSocket.cpp index 94a425f..9170574 100644 --- a/src/Client/World/WorldSocket.cpp +++ b/src/Client/World/WorldSocket.cpp @@ -18,6 +18,8 @@ void WorldSocket::OnConnect() void WorldSocket::OnConnectFailed() { logerror("WorldSocket::OnConnectFailed()\n"); + if(_session) + _session->SetSocket(NULL); } void WorldSocket::OnDelete() @@ -29,6 +31,7 @@ void WorldSocket::OnDelete() void WorldSocket::OnException() { + DEBUG(logdebug("WorldSocket::OnException()")); this->SetCloseAndDelete(); } diff --git a/src/PseuWoW.vcproj b/src/PseuWoW.vcproj index 9f41235..b5ed4fd 100644 --- a/src/PseuWoW.vcproj +++ b/src/PseuWoW.vcproj @@ -216,9 +216,6 @@ - - diff --git a/src/Client/Auth/ByteBuffer.h b/src/shared/ByteBuffer.h similarity index 100% rename from src/Client/Auth/ByteBuffer.h rename to src/shared/ByteBuffer.h diff --git a/src/shared/ZCompressor.h b/src/shared/ZCompressor.h index a7cb08e..a6117fa 100644 --- a/src/shared/ZCompressor.h +++ b/src/shared/ZCompressor.h @@ -2,7 +2,7 @@ #ifndef _ZCOMPRESSOR_H #define _ZCOMPRESSOR_H -#include "Auth/ByteBuffer.h" +#include "ByteBuffer.h" class ZCompressor : public ByteBuffer diff --git a/src/shared/common.h b/src/shared/common.h index dff6625..ff0efbb 100644 --- a/src/shared/common.h +++ b/src/shared/common.h @@ -16,18 +16,19 @@ #include #include -#define SDL_THREADS_DISABLED true - -//#include "SDL/SDL.h" // disabled for now until needed -#include "zthread/FastMutex.h" -#include "zthread/LockedQueue.h" -#include "zthread/Runnable.h" -#include "zthread/Thread.h" +#ifndef _COMMON_SKIP_THREADS +# include "zthread/FastMutex.h" +# include "zthread/LockedQueue.h" +# include "zthread/Runnable.h" +# include "zthread/Thread.h" +#endif #include "SysDefs.h" #include "DebugStuff.h" -#include "HelperDefs.h" #include "tools.h" +#include "ByteBuffer.h" + + #endif diff --git a/src/shared/tools.cpp b/src/shared/tools.cpp index 6afeca7..2dc2835 100644 --- a/src/shared/tools.cpp +++ b/src/shared/tools.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "tools.h" #ifndef _WIN32 @@ -128,9 +129,22 @@ std::deque GetFileList(std::string path) # endif - while(files.front()=="." || files.front()=="..") + while(files.size() && (files.front()=="." || files.front()=="..")) files.pop_front(); return files; } +bool FileExists(char *fn) +{ + std::fstream f; + f.open(fn,std::ios_base::in); + if (f.is_open()) + { + f.close(); + return true; + } + return false; +} + + diff --git a/src/shared/tools.h b/src/shared/tools.h index 23327d4..c485c15 100644 --- a/src/shared/tools.h +++ b/src/shared/tools.h @@ -16,5 +16,6 @@ std::string getDateString(void); uint64 toInt(std::string); std::string toHexDump(uint8* array,uint32 size,bool spaces=true); std::deque GetFileList(std::string); +bool FileExists(char*); #endif \ No newline at end of file diff --git a/src/tools/stuffextract.vcproj b/src/tools/stuffextract.vcproj new file mode 100644 index 0000000..09eebda --- /dev/null +++ b/src/tools/stuffextract.vcproj @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tools/stuffextract/DBCFieldData.h b/src/tools/stuffextract/DBCFieldData.h new file mode 100644 index 0000000..a790409 --- /dev/null +++ b/src/tools/stuffextract/DBCFieldData.h @@ -0,0 +1,191 @@ +#ifndef DBCFIELDDATA_H +#define DBCFIELDDATA_H + +// NOTE: fields checked for 1.12.2 deDE + +// defines fields numbers in EmotesText.dbc +// unk fields are always 0, EMOTESTEXT_EMOTE_STRING is string and the rest is uint32. +enum EmotesTextEnum +{ + EMOTESTEXT_EMOTE_ID = 0, + EMOTESTEXT_EMOTE_STRING, + EMOTESTEXT_ANIM, + EMOTESTEXT_SOMEONE_TO_SOMEONE, + EMOTESTEXT_SOMEONE_TO_ME, + EMOTESTEXT_ME_TO_SOMEONE, + EMOTESTEXT_UNK_1, + EMOTESTEXT_SOMEONE_TO_NONE, + EMOTESTEXT_UNK_2, + EMOTESTEXT_ME_TO_NONE, + EMOTESTEXT_UNK_3, + EMOTESTEXT_SOMEONE_TO_SOMEONE_FEMALE, + EMOTESTEXT_SOMEONE_TO_ME_FEMALE, + EMOTESTEXT_UNK_4, // me to someone (female)? dbc is empty. + EMOTESTEXT_UNK_5, + EMOTESTEXT_SOMEONE_TO_NONE_FEMALE, + EMOTESTEXT_UNK_6, + EMOTESTEXT_UNK_7, // me to none (female)? dbc is empty. + EMOTESTEXT_UNK_8, + EMOTESTEXT_END +}; + +static const char *EmotesTextFieldNames[] = +{ + "", // EMOTESTEXT_EMOTE_ID + "", // EMOTESTEXT_EMOTE_STRING + "", // EMOTESTEXT_ANIM + "OneToOne", // EMOTESTEXT_SOMEONE_TO_SOMEONE + "OneToMe", // EMOTESTEXT_SOMEONE_TO_ME + "MeToOne", // EMOTESTEXT_ME_TO_SOMEONE + "", // EMOTESTEXT_UNK_1 + "OneGeneral", // EMOTESTEXT_SOMEONE_TO_NONE + "", // EMOTESTEXT_UNK_2 + "MeGeneral", // EMOTESTEXT_ME_TO_NONE + "", // EMOTESTEXT_UNK_3 + "OneToOneFemale", // EMOTESTEXT_SOMEONE_TO_SOMEONE_FEMALE + "OneToMeFemale", // EMOTESTEXT_SOMEONE_TO_ME_FEMALE + "", // EMOTESTEXT_UNK_4 + "", // EMOTESTEXT_UNK_5 + "OneGeneralFemale", // EMOTESTEXT_SOMEONE_TO_NONE_FEMALE + "", // EMOTESTEXT_UNK_6 + "", // EMOTESTEXT_UNK_7 + "", // EMOTESTEXT_UNK_8 + "" // EMOTESTEXT_END +}; + +enum EmotesTextDataEnum +{ + EMOTESTEXTDATA_TEXTID = 0, + EMOTESTEXTDATA_STRING = 4 + // rest of the fields is 0 always +}; + +enum EmotesTextSoundEnum +{ + EMOTESTEXTSOUND_TEXTID = 0, + EMOTESTEXTSOUND_EMOTEID, + EMOTESTEXTSOUND_RACE, + EMOTESTEXTSOUND_ISFEMALE, + EMOTESTEXTSOUND_SOUNDID, + EMOTESTEXTSOUND_END +}; + +enum SoundEntriesEnum +{ + SOUNDENTRY_SOUNDID = 0, + SOUNDENTRY_UNK_1, // seems to be a flag or something + SOUNDENTRY_NAME_STRING, + SOUNDENTRY_FILE_1, + SOUNDENTRY_FILE_2, + SOUNDENTRY_FILE_3, + SOUNDENTRY_FILE_4, + SOUNDENTRY_FILE_5, + SOUNDENTRY_FILE_6, + SOUNDENTRY_FILE_7, + SOUNDENTRY_FILE_8, + SOUNDENTRY_FILE_9, + SOUNDENTRY_FILE_10, + SOUNDENTRY_BOOL_1, // these fields store either 0 or 1 + SOUNDENTRY_BOOL_2, // no idea what they are good for.. + SOUNDENTRY_BOOL_3, + SOUNDENTRY_BOOL_4, + SOUNDENTRY_BOOL_5, + SOUNDENTRY_BOOL_6, + SOUNDENTRY_BOOL_7, + SOUNDENTRY_BOOL_8, + SOUNDENTRY_BOOL_9, + SOUNDENTRY_BOOL_10, + SOUNDENTRY_PATH, // path where SOUNDENTRY_FILE_x is stored. has no trailing '\'. + SOUNDENTRY_UNK_2, // seems to be a float between 0...1 (sound volume?) + SOUNDENTRY_UNK_3, // another field storing flags? + SOUNDENTRY_UNK_4, // probably float data + SOUNDENTRY_UNK_5, // probably float data + SOUNDENTRY_UNK_6, // int value, mostly 0 or 2. only 2 cases where this field is 1 + SOUNDENTRY_END +}; + +static const char *SoundEntriesFormat = "ixsssssssssssiiiiiiiiiisxxxxx"; + +static const char *SoundEntriesFieldNames[] = +{ + "", + "", + "name", + "file1", "file2", "file3", "file4", "file5", "file6", "file7", "file8", "file9", "file10", // 10 file names + "","","","","","","","","","", // 10 unk bools + "path", + "volume", // ok lets just assume its volume (unk2) + "", "", "", "", "" // unk3 - unk6 +}; + +enum ChrRacesEnum +{ + CHRRACES_RACEID = 0, + CHRRACES_UNK_1, + CHRRACES_UNK_2, + CHRRACES_UNK_3, + CHRRACES_MODELM, // model for male player + CHRRACES_MODELF, // model for female player + CHRRACES_NAME_SHORT, // 2 chars name abbrev (Hu,Or,Tr, etc) + CHRRACES_UNK_4, + CHRRACES_FACTION, // 1=Alliance, 7=Horde + CHRRACES_UNK_5, + CHRRACES_UNK_6, + CHRRACES_UNK_7, + CHRRACES_UNK_8, + CHRRACES_UNK_9, + CHRRACES_UNK_10, + CHRRACES_NAME_GENERAL, // always the english(?) name (without spaces). used in texture names etc. + CHRRACES_UNK_11, + CHRRACES_UNK_12, + CHRRACES_UNK_13, + CHRRACES_UNK_14, + CHRRACES_NAME_LOCAL, // localized(?) name (spanish,german or whatever) + CHRRACES_UNK_15, + CHRRACES_UNK_16, + CHRRACES_UNK_17, + CHRRACES_UNK_18, + CHRRACES_UNK_19, + CHRRACES_UNK_20, + CHRRACES_TRAITS, // string that defines the face decorations on char create + CHRRACES_UNK_21, + CHARRACES_END +}; + +static const char *ChrRacesFormat = "ixxxiisxixxxxxxsxxxxsxxxxxxsx"; + +static const char *ChrRacesFieldNames[] = +{ + "", + "", + "", + "", + "model_m", + "model_f", + "name_short", + "", + "faction", + "", + "", + "", + "", + "", + "", + "name_general", + "", + "", + "", + "", + "name", + "", + "", + "", + "", + "", + "", + "traits", + "", + "" +}; + +#endif diff --git a/src/tools/stuffextract/MPQFile.cpp b/src/tools/stuffextract/MPQFile.cpp new file mode 100644 index 0000000..fd51661 --- /dev/null +++ b/src/tools/stuffextract/MPQFile.cpp @@ -0,0 +1,36 @@ +#include "MPQFile.h" + +// MPQ file to be opened +MPQFile::MPQFile(const char *fn) +{ + _mpq = NULL; + _isopen = SFileOpenArchive(fn,0,0,&_mpq); +} + +bool MPQFile::HasFile(char *fn) +{ + return SFileHasFile(_mpq,fn); +} + +// get size of a file within an mpq archive +ByteBuffer MPQFile::ReadFile(char *fn) +{ + ByteBuffer bb; + HANDLE fh; + if(!SFileOpenFileEx(_mpq, fn, 0, &fh)) + return bb; + uint32 size = SFileGetFileSize(fh); + bb.resize(size); + SFileReadFile(fh, (void*)bb.contents(), size, NULL, NULL); + return bb; +} + +uint32 MPQFile::GetFileSize(char *fn) +{ + HANDLE fh; + if(!SFileOpenFileEx(_mpq, fn, 0, &fh)) + return 0; + uint32 size = SFileGetFileSize(fh); + SFileCloseFile(fh); + return size; +} \ No newline at end of file diff --git a/src/tools/stuffextract/MPQFile.h b/src/tools/stuffextract/MPQFile.h new file mode 100644 index 0000000..ed418e0 --- /dev/null +++ b/src/tools/stuffextract/MPQFile.h @@ -0,0 +1,24 @@ +#ifndef MPQFILE_H +#define MPQFILE_H + +#define _COMMON_NO_THREADS +#include "common.h" +#include "StormLib/StormLib.h" +#include "StormLib/SCommon.h" + +class MPQFile +{ +public: + MPQFile(const char*); + inline bool IsOpen(void) { return _isopen; } + ByteBuffer ReadFile(char*); + uint32 GetFileSize(char*); + bool HasFile(char*); + +private: + HANDLE _mpq; + bool _isopen; + +}; + +#endif diff --git a/src/tools/stuffextract/MPQHelper.cpp b/src/tools/stuffextract/MPQHelper.cpp new file mode 100644 index 0000000..960bd4d --- /dev/null +++ b/src/tools/stuffextract/MPQHelper.cpp @@ -0,0 +1,52 @@ +#include "common.h" +#include "MPQHelper.h" +#include "MPQFile.h" + + +MPQHelper::MPQHelper() +{ +} + +bool MPQHelper::AssignArchive(char *fn) +{ + // first, check which patch files are avalible. + // store patches in reversed order, that patch-9.mpq is checked first, and patch.mpq checked last + if(FileExists("Data/patch.MPQ")) + _patches.push_front("Data/patch.MPQ"); + for(uint32 i=1; i::iterator i = _patches.begin(); i != _patches.end(); i++) + { + MPQFile mpq((*i).c_str()); + if(mpq.IsOpen() && mpq.HasFile(fn)) + { + printf("MPQE: Using %s from %s\n",fn,i->c_str()); + bb = mpq.ReadFile(fn); + return bb; + } + } + MPQFile arch(_archive.c_str()); + if(arch.IsOpen() && arch.HasFile(fn)) + { + printf("MPQE: Using %s from %s\n",fn,_archive.c_str()); + bb = arch.ReadFile(fn); + } + return bb; +} + + + + diff --git a/src/tools/stuffextract/MPQHelper.h b/src/tools/stuffextract/MPQHelper.h new file mode 100644 index 0000000..bb86221 --- /dev/null +++ b/src/tools/stuffextract/MPQHelper.h @@ -0,0 +1,17 @@ +#ifndef MPQHELPER_H +#define MPQHELPER_H + +#define MAX_PATCH_NUMBER 9 + +class MPQHelper +{ +public: + MPQHelper(); + bool AssignArchive(char*); + ByteBuffer ExtractFile(char*); +private: + std::list _patches; // patch.mpq - patch-9.mpq + std::string _archive; +}; + +#endif \ No newline at end of file diff --git a/src/tools/stuffextract/StormLib/SCommon.cpp b/src/tools/stuffextract/StormLib/SCommon.cpp new file mode 100644 index 0000000..cfe34fd --- /dev/null +++ b/src/tools/stuffextract/StormLib/SCommon.cpp @@ -0,0 +1,1074 @@ +/*****************************************************************************/ +/* SCommon.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for StormLib, used by all SFile*** modules */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 24.03.03 1.00 Lad The first version of SFileCommon.cpp */ +/* 19.11.03 1.01 Dan Big endian handling */ +/* 12.06.04 1.01 Lad Renamed to SCommon.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +char StormLibCopyright[] = "StormLib v 4.50 Copyright Ladislav Zezula 1998-2003"; + +//----------------------------------------------------------------------------- +// The buffer for decryption engine. + +TMPQArchive * pFirstOpen = NULL; // The first member of MPQ archives chain +LCID lcLocale = LANG_NEUTRAL; // File locale +USHORT wPlatform = 0; // File platform + +//----------------------------------------------------------------------------- +// Compression types + +// +// Data compressions +// +// Can be combination of MPQ_COMPRESSION_PKWARE, MPQ_COMPRESSION_BZIP2 +// and MPQ_COMPRESSION_ZLIB. Some newer compressions are not supported +// by older games. The table of supported compressions is here: +// +// MPQ_COMPRESSION_PKWARE - All games since Diablo I +// MPQ_COMPRESSION_ZLIB - Games since Starcraft +// MPQ_COMPRESSION_BZIP2 - Games since World of Warcraft +// + +static int nDataCmp = MPQ_COMPRESSION_ZLIB; + +// +// WAVE compressions by quality level +// + +static int uWaveCmpLevel[] = {-1, 4, 2}; +static int uWaveCmpType[] = {MPQ_COMPRESSION_PKWARE, 0x81, 0x81}; + +//----------------------------------------------------------------------------- +// Storm buffer functions + +// Buffer for the decryption engine +#define STORM_BUFFER_SIZE 0x500 +static DWORD StormBuffer[STORM_BUFFER_SIZE]; +static BOOL bStormBufferCreated = FALSE; + +int PrepareStormBuffer() +{ + DWORD dwSeed = 0x00100001; + DWORD index1 = 0; + DWORD index2 = 0; + int i; + + // Initialize the decryption buffer. + // Do nothing if already done. + if(bStormBufferCreated == FALSE) + { + for(index1 = 0; index1 < 0x100; index1++) + { + for(index2 = index1, i = 0; i < 5; i++, index2 += 0x100) + { + DWORD temp1, temp2; + + dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB; + temp1 = (dwSeed & 0xFFFF) << 0x10; + + dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB; + temp2 = (dwSeed & 0xFFFF); + + StormBuffer[index2] = (temp1 | temp2); + } + } + bStormBufferCreated = TRUE; + } + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Encrypting and decrypting hash table + +void EncryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength) +{ + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; // One key character + + // Prepare seeds + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + // Encrypt it + dwSeed2 = 0xEEEEEEEE; + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *pdwTable; + *pdwTable++ = ch ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + } +} + +void DecryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength) +{ + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; // One key character + + // Prepare seeds + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + // Decrypt it + dwSeed2 = 0xEEEEEEEE; + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *pdwTable ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + *pdwTable++ = ch; + } +} + +//----------------------------------------------------------------------------- +// Encrypting and decrypting block table + +void EncryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength) +{ + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; // One key character + + // Prepare seeds + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + // Decrypt it + dwSeed2 = 0xEEEEEEEE; + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *pdwTable; + *pdwTable++ = ch ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + } +} + +void DecryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength) +{ + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; // One key character + + // Prepare seeds + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + // Encrypt it + dwSeed2 = 0xEEEEEEEE; + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *pdwTable ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + *pdwTable++ = ch; + } +} + +//----------------------------------------------------------------------------- +// Functions tries to get file decryption key. The trick comes from block +// positions which are stored at the begin of each compressed file. We know the +// file size, that means we know number of blocks that means we know the first +// DWORD value in block position. And if we know encrypted and decrypted value, +// we can find the decryption key !!! +// +// hf - MPQ file handle +// block - DWORD array of block positions +// ch - Decrypted value of the first block pos + +DWORD DetectFileSeed(DWORD * block, DWORD decrypted) +{ + DWORD saveSeed1; + DWORD temp = *block ^ decrypted; // temp = seed1 + seed2 + temp -= 0xEEEEEEEE; // temp = seed1 + StormBuffer[0x400 + (seed1 & 0xFF)] + + for(int i = 0; i < 0x100; i++) // Try all 255 possibilities + { + DWORD seed1; + DWORD seed2 = 0xEEEEEEEE; + DWORD ch; + + // Try the first DWORD (We exactly know the value) + seed1 = temp - StormBuffer[0x400 + i]; + seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; + ch = block[0] ^ (seed1 + seed2); + + if(ch != decrypted) + continue; + + // Add 1 because we are decrypting block positions + saveSeed1 = seed1 + 1; + + // If OK, continue and test the second value. We don't know exactly the value, + // but we know that the second one has lower 16 bits set to zero + // (no compressed block is larger than 0xFFFF bytes) + seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); + seed2 = ch + seed2 + (seed2 << 5) + 3; + + seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; + ch = block[1] ^ (seed1 + seed2); + + if((ch & 0xFFFF0000) == 0) + return saveSeed1; + } + return 0; +} + +// Function tries to detect file seed. It expectes at least two uncompressed bytes +DWORD DetectFileSeed2(DWORD * pdwBlock, UINT nDwords, ...) +{ + va_list argList; + DWORD dwDecrypted[0x10]; + DWORD saveSeed1; + DWORD dwTemp; + DWORD i, j; + + // We need at least two DWORDS to detect the seed + if(nDwords < 0x02 || nDwords > 0x10) + return 0; + + va_start(argList, nDwords); + for(i = 0; i < nDwords; i++) + dwDecrypted[i] = va_arg(argList, DWORD); + va_end(argList); + + dwTemp = (*pdwBlock ^ dwDecrypted[0]) - 0xEEEEEEEE; + for(i = 0; i < 0x100; i++) // Try all 255 possibilities + { + DWORD seed1; + DWORD seed2 = 0xEEEEEEEE; + DWORD ch; + + // Try the first DWORD + seed1 = dwTemp - StormBuffer[0x400 + i]; + seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; + ch = pdwBlock[0] ^ (seed1 + seed2); + + if(ch != dwDecrypted[0]) + continue; + + saveSeed1 = seed1; + + // If OK, continue and test all bytes. + for(j = 1; j < nDwords; j++) + { + seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); + seed2 = ch + seed2 + (seed2 << 5) + 3; + + seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; + ch = pdwBlock[j] ^ (seed1 + seed2); + + if(ch == dwDecrypted[j] && j == nDwords - 1) + return saveSeed1; + } + } + return 0; +} + + +//----------------------------------------------------------------------------- +// Encrypting and decrypting MPQ blocks + +void EncryptMPQBlock(DWORD * block, DWORD dwLength, DWORD dwSeed1) +{ + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + // Round to DWORDs + dwLength >>= 2; + + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *block; + *block++ = ch ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + } +} + +void DecryptMPQBlock(DWORD * block, DWORD dwLength, DWORD dwSeed1) +{ + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + // Round to DWORDs + dwLength >>= 2; + + while(dwLength-- > 0) + { + dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; + ch = *block ^ (dwSeed1 + dwSeed2); + + dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); + dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; + *block++ = ch; + } +} + + +DWORD DecryptHashIndex(TMPQArchive * ha, const char * szFileName) +{ + BYTE * pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x000 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + return (dwSeed1 & (ha->pHeader->dwHashTableSize - 1)); +} + +DWORD DecryptName1(const char * szFileName) +{ + BYTE * pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x100 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + return dwSeed1; +} + +DWORD DecryptName2(const char * szFileName) +{ + BYTE * pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + int ch; + + while(*pbKey != 0) + { + ch = toupper(*pbKey++); + + dwSeed1 = StormBuffer[0x200 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + return dwSeed1; +} + +DWORD DecryptFileSeed(const char * szFileName) +{ + BYTE * pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; // EBX + DWORD dwSeed2 = 0xEEEEEEEE; // ESI + DWORD ch; + + while(*pbKey != 0) + { + ch = toupper(*pbKey++); // ECX + + dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + return dwSeed1; +} + +TMPQHash * GetHashEntry(TMPQArchive * ha, const char * szFileName) +{ + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash0; // File hash entry (start) + TMPQHash * pHash; // File hash entry (current) + DWORD dwIndex = (DWORD)(DWORD_PTR)szFileName; + DWORD dwName1; + DWORD dwName2; + + // If filename is given by index, we have to search all hash entries for the right index. + if(dwIndex <= ha->pHeader->dwBlockTableSize) + { + // Pass all the hash entries and find the + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex == dwIndex) + return pHash; + } + return NULL; + } + + // Decrypt name and block index + dwIndex = DecryptHashIndex(ha, szFileName); + dwName1 = DecryptName1(szFileName); + dwName2 = DecryptName2(szFileName); + pHash = pHash0 = ha->pHashTable + dwIndex; + + // Look for hash index + while(pHash->dwBlockIndex != HASH_ENTRY_FREE) + { + if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex != HASH_ENTRY_DELETED) + return pHash; + + // Move to the next hash entry + if(++pHash >= pHashEnd) + pHash = ha->pHashTable; + if(pHash == pHash0) + break; + } + + // File was not found + return NULL; +} + +// Retrieves the locale-specific hash entry +TMPQHash * GetHashEntryEx(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash0 = NULL; // Language-neutral hash entry + TMPQHash * pHashX = NULL; // Language-speficic + TMPQHash * pHash = GetHashEntry(ha, szFileName); + + if(pHash != NULL) + { + TMPQHash * pHashStart = pHash; + DWORD dwName1 = pHash->dwName1; + DWORD dwName2 = pHash->dwName2; + + while(pHash->dwBlockIndex != HASH_ENTRY_FREE) + { + if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2) + { + if(pHash->lcLocale == LANG_NEUTRAL) + pHash0 = pHash; + if(pHash->lcLocale == lcLocale) + pHashX = pHash; + + // If both found, break the loop + if(pHash0 != NULL && pHashX != NULL) + break; + } + + if(++pHash >= pHashEnd) + pHash = ha->pHashTable; + if(pHash == pHashStart) + return NULL; + } + + if(lcLocale != LANG_NEUTRAL && pHashX != NULL) + return pHashX; + if(pHash0 != NULL) + return pHash0; + return NULL; + } + + return pHash; +} + +// Encrypts file name and gets the hash entry +// Returns the hash pointer, which is always within the allocated array +TMPQHash * FindFreeHashEntry(TMPQArchive * ha, const char * szFileName) +{ + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash0; // File hash entry (search start) + TMPQHash * pHash; // File hash entry + DWORD dwIndex = DecryptHashIndex(ha, szFileName); + DWORD dwName1 = DecryptName1(szFileName); + DWORD dwName2 = DecryptName2(szFileName); + DWORD dwBlockIndex = 0xFFFFFFFF; + + // Save the starting hash position + pHash = pHash0 = ha->pHashTable + dwIndex; + + // Look for the first free hash entry. Can be also a deleted entry + while(pHash->dwBlockIndex < HASH_ENTRY_DELETED) + { + if(++pHash >= pHashEnd) + pHash = ha->pHashTable; + if(pHash == pHash0) + return NULL; + } + + // Fill the hash entry with the informations about the file name + pHash->dwName1 = dwName1; + pHash->dwName2 = dwName2; + pHash->lcLocale = (USHORT)lcLocale; + pHash->wPlatform = wPlatform; + + // Now we have to find a free block entry + for(dwIndex = 0; dwIndex < ha->pHeader->dwBlockTableSize; dwIndex++) + { + TMPQBlock * pBlock = ha->pBlockTable + dwIndex; + + if((pBlock->dwFlags & MPQ_FILE_EXISTS) == 0) + { + dwBlockIndex = dwIndex; + break; + } + } + + // If no free block entry found, we have to use the index + // at the end of the current block table + if(dwBlockIndex == 0xFFFFFFFF) + dwBlockIndex = ha->pHeader->dwBlockTableSize; + pHash->dwBlockIndex = dwBlockIndex; + return pHash; +} + +//----------------------------------------------------------------------------- +// Checking for valid archive handle and valid file handle + +BOOL IsValidMpqHandle(TMPQArchive * ha) +{ + if(ha == NULL || IsBadReadPtr(ha, sizeof(TMPQArchive))) + return FALSE; + if(ha->pHeader == NULL || IsBadReadPtr(ha->pHeader, sizeof(TMPQHeader))) + return FALSE; + + return (ha->pHeader->dwID == ID_MPQ); +} + +BOOL IsValidFileHandle(TMPQFile * hf) +{ + if(hf == NULL || IsBadReadPtr(hf, sizeof(TMPQFile))) + return FALSE; + + if(hf->hFile != INVALID_HANDLE_VALUE) + return TRUE; + + return IsValidMpqHandle(hf->ha); +} + +// This function writes a local file into the MPQ archive. +// Returns 0 if OK, otherwise error code. +// TODO: Test for archives > 4GB +int AddFileToArchive( + TMPQArchive * ha, + HANDLE hFile, + const char * szArchivedName, + DWORD dwFlags, + DWORD dwQuality, + int nFileType, + BOOL * pbReplaced) +{ + LARGE_INTEGER RelativePos = {0}; + LARGE_INTEGER FilePos = {0}; + LARGE_INTEGER TempPos; + TMPQBlockEx * pBlockEx = NULL; // Entry in the extended block table + TMPQBlock * pBlock = NULL; // Entry in the block table + TMPQHash * pHash = NULL; // Entry in the hash table + DWORD * pdwBlockPos = NULL; // Block position table (compressed files only) + BYTE * pbFileData = NULL; // Uncompressed (source) data + BYTE * pbCompressed = NULL; // Compressed (target) data + BYTE * pbToWrite = NULL; // Data to write to the file + DWORD dwBlockPosLen = 0; // Length of the block table positions + DWORD dwTransferred = 0; // Number of bytes written into archive file + DWORD dwFileSize = 0; // Size of the file to add + DWORD dwSeed1 = 0; // Encryption seed + DWORD nBlocks = 0; // Number of file blocks + DWORD nBlock = 0; // Index of the currently written block + BOOL bReplaced = FALSE; // TRUE if replaced, FALSE if added + int nCmpFirst = nDataCmp; // Compression for the first data block + int nCmpNext = nDataCmp; // Compression for the next data blocks + int nCmp = nDataCmp; // Current compression + int nCmpLevel = -1; // Compression level + int nError = ERROR_SUCCESS; + + // Set the correct compression types + if(dwFlags & MPQ_FILE_COMPRESS_PKWARE) + nCmpFirst = nCmpNext = MPQ_COMPRESSION_PKWARE; + + if(dwFlags & MPQ_FILE_COMPRESS_MULTI) + { + if(nFileType == SFILE_TYPE_DATA) + nCmpFirst = nCmpNext = nDataCmp; + + if(nFileType == SFILE_TYPE_WAVE) + { + nCmpNext = uWaveCmpType[dwQuality]; + nCmpLevel = uWaveCmpLevel[dwQuality]; + } + } + + // Check if the file already exists in the archive + if(nError == ERROR_SUCCESS) + { + if((pHash = GetHashEntryEx(ha, szArchivedName, lcLocale)) != NULL) + { + if(pHash->lcLocale == lcLocale) + { + if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0) + { + nError = ERROR_ALREADY_EXISTS; + pHash = NULL; + } + else + bReplaced = TRUE; + } + else + pHash = NULL; + } + + if(nError == ERROR_SUCCESS && pHash == NULL) + { + pHash = FindFreeHashEntry(ha, szArchivedName); + if(pHash == NULL) + nError = ERROR_HANDLE_DISK_FULL; + } + } + + // Get the block table entry for the file + if(nError == ERROR_SUCCESS) + { + DWORD dwFileSizeHigh = 0; + + // Get the size of the added file + dwFileSize = GetFileSize(hFile, &dwFileSizeHigh); + if(dwFileSizeHigh != 0) + nError = ERROR_PARAMETER_QUOTA_EXCEEDED; + + // Fix the flags, if the file is too small + if(dwFileSize < 0x04) + dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIXSEED); + if(dwFileSize < 0x20) + dwFlags &= ~MPQ_FILE_COMPRESSED; + + if(pHash->dwBlockIndex == HASH_ENTRY_FREE) + pHash->dwBlockIndex = ha->pHeader->dwBlockTableSize; + + // The block table index cannot be larger than hash table size + if(pHash->dwBlockIndex >= ha->pHeader->dwHashTableSize) + nError = ERROR_HANDLE_DISK_FULL; + } + + // The file will be stored after the end of the last archived file + // (i.e. at old position of archived file + if(nError == ERROR_SUCCESS) + { + TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; + const char * szTemp = strrchr(szArchivedName, '\\'); + + // Get the position of the first file + RelativePos.QuadPart = ha->pHeader->dwHeaderSize; + + // Find the position of the last file. It has to be after the last archived file + // (Do not use the dwArchiveSize here, because it may or may not + // include the hash table at the end of the file + pBlockEx = ha->pExtBlockTable; + for(pBlock = ha->pBlockTable; pBlock < pBlockEnd; pBlock++, pBlockEx++) + { + if(pBlock->dwFlags & MPQ_FILE_EXISTS) + { + TempPos.HighPart = pBlockEx->wFilePosHigh; + TempPos.LowPart = pBlock->dwFilePos; + TempPos.QuadPart += pBlock->dwCSize; + + if(TempPos.QuadPart > RelativePos.QuadPart) + RelativePos = TempPos; + } + } + + // When format V1, we cannot exceed 4 GB + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) + { + TempPos.QuadPart = ha->MpqPos.QuadPart + RelativePos.QuadPart; + TempPos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + TempPos.QuadPart += dwFileSize; + + if(TempPos.HighPart != 0) + nError = ERROR_DISK_FULL; + } + + // Get pointers to both block entries of the file + pBlockEx = ha->pExtBlockTable + pHash->dwBlockIndex; + pBlock = ha->pBlockTable + pHash->dwBlockIndex; + + // Save the file size info + pBlockEx->wFilePosHigh = (USHORT)RelativePos.HighPart; + pBlock->dwFilePos = RelativePos.LowPart; + pBlock->dwFSize = GetFileSize(hFile, NULL); + pBlock->dwFlags = dwFlags | MPQ_FILE_EXISTS; + + // Create seed1 for file encryption + if(szTemp != NULL) + szArchivedName = szTemp + 1; + + if(dwFlags & MPQ_FILE_ENCRYPTED) + { + dwSeed1 = DecryptFileSeed(szArchivedName); + + if(dwFlags & MPQ_FILE_FIXSEED) + dwSeed1 = (dwSeed1 + pBlock->dwFilePos) ^ pBlock->dwFSize; + } + } + + // Allocate buffer for the input data + if(nError == ERROR_SUCCESS) + { + nBlocks = (pBlock->dwFSize / ha->dwBlockSize) + 1; + if(pBlock->dwFSize % ha->dwBlockSize) + nBlocks++; + + pBlock->dwCSize = 0; + if((pbFileData = ALLOCMEM(BYTE, ha->dwBlockSize)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + pbToWrite = pbFileData; + } + + // Allocate buffers for the compressed data + if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_COMPRESSED)) + { + pdwBlockPos = ALLOCMEM(DWORD, nBlocks + 1); + pbCompressed = ALLOCMEM(BYTE, ha->dwBlockSize * 2); + if(pdwBlockPos == NULL || pbCompressed == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + pbToWrite = pbCompressed; + } + + // Set the file position to the point where the file will be stored + if(nError == ERROR_SUCCESS) + { + // Set the file pointer to file data position + FilePos.QuadPart = ha->MpqPos.QuadPart + RelativePos.QuadPart; + if(FilePos.QuadPart != ha->FilePointer.QuadPart) + { + SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN); + ha->FilePointer = FilePos; + } + } + + // Write block positions (if the file will be compressed) + if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_COMPRESSED)) + { + dwBlockPosLen = nBlocks * sizeof(DWORD); + if(dwFlags & MPQ_FILE_HAS_EXTRA) + dwBlockPosLen += sizeof(DWORD); + + memset(pdwBlockPos, 0, dwBlockPosLen); + pdwBlockPos[0] = dwBlockPosLen; + + // Write the block positions + BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks); + WriteFile(ha->hFile, pdwBlockPos, dwBlockPosLen, &dwTransferred, NULL); + BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks); + + if(dwTransferred == dwBlockPosLen) + pBlock->dwCSize += dwBlockPosLen; + else + nError = GetLastError(); + + // Update the current position in the file + ha->HashTablePos.QuadPart = FilePos.QuadPart + dwTransferred; + } + + // Write all file blocks + if(nError == ERROR_SUCCESS) + { + nCmp = nCmpFirst; + + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + for(nBlock = 0; nBlock < nBlocks-1; nBlock++) + { + DWORD dwInLength = ha->dwBlockSize; + DWORD dwOutLength = ha->dwBlockSize; + + // Load the block from the file + ReadFile(hFile, pbFileData, ha->dwBlockSize, &dwInLength, NULL); + if(dwInLength == 0) + break; + + // Compress the block, if necessary + dwOutLength = dwInLength; + if(pBlock->dwFlags & MPQ_FILE_COMPRESSED) + { + // Should be enough for compression + int nOutLength = ha->dwBlockSize * 2; + int nCmpType = 0; + + if(pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE) + Compress_pklib((char *)pbCompressed, &nOutLength, (char *)pbFileData, dwInLength, &nCmpType, 0); + + if(pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI) + SCompCompress((char *)pbCompressed, &nOutLength, (char *)pbFileData, dwInLength, nCmp, 0, nCmpLevel); + + // The compressed block size must NOT be the same or greater like + // the original block size. If yes, do not compress the block + // and store the data as-is. + if(nOutLength >= (int)dwInLength) + { + memcpy(pbCompressed, pbFileData, dwInLength); + nOutLength = dwInLength; + } + + // Update block positions + dwOutLength = nOutLength; + pdwBlockPos[nBlock+1] = pdwBlockPos[nBlock] + dwOutLength; + nCmp = nCmpNext; + } + + // Encrypt the block, if necessary + if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbToWrite, dwOutLength / sizeof(DWORD)); + EncryptMPQBlock((DWORD *)pbToWrite, dwOutLength, dwSeed1 + nBlock); + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbToWrite, dwOutLength / sizeof(DWORD)); + } + + // Write the block + WriteFile(ha->hFile, pbToWrite, dwOutLength, &dwTransferred, NULL); + if(dwTransferred != dwOutLength) + { + nError = ERROR_DISK_FULL; + break; + } + + // Update the hash table position and the compressed file size + ha->HashTablePos.QuadPart += dwTransferred; + pBlock->dwCSize += dwOutLength; + } + } + + // Now save the block positions + if(nError == ERROR_SUCCESS && (pBlock->dwFlags & MPQ_FILE_COMPRESSED)) + { + if(dwFlags & MPQ_FILE_HAS_EXTRA) + pdwBlockPos[nBlocks] = pdwBlockPos[nBlocks-1]; + + // If file is encrypted, block positions are also encrypted + if(dwFlags & MPQ_FILE_ENCRYPTED) + EncryptMPQBlock(pdwBlockPos, dwBlockPosLen, dwSeed1 - 1); + + // Set the position back to the block table + SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN); + + // Write block positions to the archive + BSWAP_ARRAY32_UNSIGNED((DWORD *)pdwBlockPos, nBlocks); + WriteFile(ha->hFile, pdwBlockPos, dwBlockPosLen, &dwTransferred, NULL); + if(dwTransferred != dwBlockPosLen) + nError = ERROR_DISK_FULL; + + ha->FilePointer.QuadPart = ha->FilePointer.QuadPart + dwTransferred; + } + + // If success, we have to change the settings + // in MPQ header. If failed, we have to clean hash entry + if(nError == ERROR_SUCCESS) + { + ha->pLastFile = NULL; + ha->dwBlockPos = 0; + ha->dwBuffPos = 0; + ha->dwFlags |= MPQ_FLAG_CHANGED; + + // Add new entry to the block table (if needed) + if(pHash->dwBlockIndex >= ha->pHeader->dwBlockTableSize) + ha->pHeader->dwBlockTableSize++; + + // Hash table size in the TMPQArchive is already set at this point + RelativePos.QuadPart = ha->HashTablePos.QuadPart - ha->MpqPos.QuadPart; + ha->pHeader->dwHashTablePos = RelativePos.LowPart; + ha->pHeader->wHashTablePosHigh = (USHORT)RelativePos.HighPart; + + // Update block table pos + RelativePos.QuadPart += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); + ha->pHeader->wBlockTablePosHigh = (USHORT)RelativePos.HighPart; + ha->pHeader->dwBlockTablePos = RelativePos.LowPart; + ha->BlockTablePos.QuadPart = RelativePos.QuadPart + ha->MpqPos.QuadPart; + + // If the archive size exceeded 4GB, we have to use extended block table pos + RelativePos.QuadPart += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + if(RelativePos.HighPart != 0) + { + ha->pHeader->ExtBlockTablePos = RelativePos; + ha->ExtBlockTablePos.QuadPart = RelativePos.QuadPart + ha->MpqPos.QuadPart; + + RelativePos.QuadPart += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx)); + } + + // Update archive size (only valid for version V1) + ha->MpqSize = RelativePos; + ha->pHeader->dwArchiveSize = ha->MpqSize.LowPart; + } + else + { + // Clear the hash table entry + if(pHash != NULL) + memset(pHash, 0xFF, sizeof(TMPQHash)); + } + + // Cleanup + if(pbCompressed != NULL) + FREEMEM(pbCompressed); + if(pdwBlockPos != NULL) + FREEMEM(pdwBlockPos); + if(pbFileData != NULL) + FREEMEM(pbFileData); + if(pbReplaced != NULL) + *pbReplaced = bReplaced; + return nError; +} + +int SetDataCompression(int nDataCompression) +{ + nDataCmp = nDataCompression; + return 0; +} + +// This method saves MPQ header, hash table and block table. +// TODO: Test for archives > 4GB +int SaveMPQTables(TMPQArchive * ha) +{ + BYTE * pbBuffer = NULL; + DWORD dwBytes; + DWORD dwWritten; + DWORD dwBuffSize = max(ha->pHeader->dwHashTableSize, ha->pHeader->dwBlockTableSize); + int nError = ERROR_SUCCESS; + + // Allocate buffer for encrypted tables + if(nError == ERROR_SUCCESS) + { + // Allocate temporary buffer for tables encryption + pbBuffer = ALLOCMEM(BYTE, sizeof(TMPQHash) * dwBuffSize); + if(pbBuffer == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Write the MPQ Header + if(nError == ERROR_SUCCESS) + { + DWORD dwHeaderSize = ha->pHeader->dwHeaderSize; + + // Write the MPQ header + SetFilePointer(ha->hFile, ha->MpqPos.LowPart, &ha->MpqPos.HighPart, FILE_BEGIN); + + // Convert to little endian for file save + BSWAP_TMPQHEADER(ha->pHeader); + WriteFile(ha->hFile, ha->pHeader, dwHeaderSize, &dwWritten, NULL); + BSWAP_TMPQHEADER(ha->pHeader); + + if(dwWritten != ha->pHeader->dwHeaderSize) + nError = ERROR_DISK_FULL; + } + + // Write the hash table + if(nError == ERROR_SUCCESS) + { + // Copy the hash table to temporary buffer + dwBytes = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + memcpy(pbBuffer, ha->pHashTable, dwBytes); + + // Convert to little endian for file save + EncryptHashTable((DWORD *)pbBuffer, (BYTE *)"(hash table)", dwBytes >> 2); + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBuffer, dwBytes / sizeof(DWORD)); + + // Set the file pointer to the offset of the hash table and write it + SetFilePointer(ha->hFile, ha->HashTablePos.LowPart, (PLONG)&ha->HashTablePos.HighPart, FILE_BEGIN); + WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL); + if(dwWritten != dwBytes) + nError = ERROR_DISK_FULL; + } + + // Write the block table + if(nError == ERROR_SUCCESS) + { + // Copy the block table to temporary buffer + dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + memcpy(pbBuffer, ha->pBlockTable, dwBytes); + + // Encrypt the block table and write it to the file + EncryptBlockTable((DWORD *)pbBuffer, (BYTE *)"(block table)", dwBytes >> 2); + + // Convert to little endian for file save + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBuffer, dwBytes / sizeof(DWORD)); + WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL); + if(dwWritten != dwBytes) + nError = ERROR_DISK_FULL; + } + + // Write the extended block table + if(nError == ERROR_SUCCESS && ha->pHeader->ExtBlockTablePos.QuadPart != 0) + { + // We expect format V2 or newer in this case + assert(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2); + + // Copy the block table to temporary buffer + dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); + memcpy(pbBuffer, ha->pExtBlockTable, dwBytes); + + // Convert to little endian for file save + BSWAP_ARRAY16_UNSIGNED((USHORT *)pbBuffer, dwBytes / sizeof(USHORT)); + WriteFile(ha->hFile, pbBuffer, dwBytes, &dwWritten, NULL); + if(dwWritten != dwBytes) + nError = ERROR_DISK_FULL; + } + + + // Set end of file here + if(nError == ERROR_SUCCESS) + { + SetEndOfFile(ha->hFile); + } + + // Cleanup and exit + if(pbBuffer != NULL) + FREEMEM(pbBuffer); + return nError; +} + +// Frees the MPQ archive +// TODO: Test for archives > 4GB +void FreeMPQArchive(TMPQArchive *& ha) +{ + if(ha != NULL) + { + FREEMEM(ha->pbBlockBuffer); + FREEMEM(ha->pBlockTable); + FREEMEM(ha->pExtBlockTable); + FREEMEM(ha->pHashTable); + if(ha->pListFile != NULL) + SListFileFreeListFile(ha); + + if(ha->hFile != INVALID_HANDLE_VALUE) + CloseHandle(ha->hFile); + FREEMEM(ha); + ha = NULL; + } +} diff --git a/src/tools/stuffextract/StormLib/SCommon.h b/src/tools/stuffextract/StormLib/SCommon.h new file mode 100644 index 0000000..904c2de --- /dev/null +++ b/src/tools/stuffextract/StormLib/SCommon.h @@ -0,0 +1,88 @@ +/*****************************************************************************/ +/* SCommon.h Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for encryption/decryption from Storm.dll. Included by */ +/* SFile*** functions, do not include and do not use this file directly */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 24.03.03 1.00 Lad The first version of SFileCommon.h */ +/* 12.06.04 1.00 Lad Renamed to SCommon.h */ +/*****************************************************************************/ + +#ifndef __SCOMMON_H__ +#define __SCOMMON_H__ + +//----------------------------------------------------------------------------- +// StormLib private defines + +#define SFILE_TYPE_DATA 0 // Process the file as data file +#define SFILE_TYPE_WAVE 1 // Process the file as WAVe file + +//----------------------------------------------------------------------------- +// External variables + +extern TMPQArchive * pFirstOpen; +extern LCID lcLocale; + +//----------------------------------------------------------------------------- +// Encryption and decryption functions + +int PrepareStormBuffer(); + +void EncryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength); +void DecryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength); +TMPQHash * FindFreeHashEntry(TMPQArchive * ha, const char * szFileName); + +void EncryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength); +void DecryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength); + +DWORD DetectFileSeed(DWORD * block, DWORD decrypted); +DWORD DetectFileSeed2(DWORD * block, UINT nDwords, ...); +void EncryptMPQBlock(DWORD * pdwBlock, DWORD dwLength, DWORD dwSeed1); +void DecryptMPQBlock(DWORD * pdwBlock, DWORD dwLength, DWORD dwSeed1); + +DWORD DecryptHashIndex(TMPQArchive * ha, const char * szFileName); +DWORD DecryptName1 (const char * szFileName); +DWORD DecryptName2 (const char * szFileName); +DWORD DecryptFileSeed (const char * szFileName); + +TMPQHash * GetHashEntry (TMPQArchive * ha, const char * szFileName); +TMPQHash * GetHashEntryEx(TMPQArchive * ha, const char * szFileName, LCID lcLocale); + +//----------------------------------------------------------------------------- +// Compression and decompression functions + +int Compress_pklib (char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel); +int Decompress_pklib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength); + +//----------------------------------------------------------------------------- +// Checking functions + +BOOL IsValidMpqHandle(TMPQArchive * ha); +BOOL IsValidFileHandle(TMPQFile * hf); + +//----------------------------------------------------------------------------- +// Other functions + +BOOL SFileOpenArchiveEx(const char * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMPQ, DWORD dwAccessMode = GENERIC_READ); +int AddFileToArchive(TMPQArchive * ha, HANDLE hFile, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality, int nFileType, BOOL * pbReplaced); +int SetDataCompression(int nDataCompression); +int SaveMPQTables(TMPQArchive * ha); +void FreeMPQArchive(TMPQArchive *& ha); + +BOOL CheckWildCard(const char * szString, const char * szWildCard); + +//----------------------------------------------------------------------------- +// Listfile functions + +int SListFileCreateListFile(TMPQArchive * ha); +int SListFileAddNode(TMPQArchive * ha, const char * szAddedFile); +int SListFileRemoveNode(TMPQArchive * ha, const char * szFileName); +int SListFileRenameNode(TMPQArchive * ha, const char * szOldFileName, const char * szNewFileName); +int SListFileFreeListFile(TMPQArchive * ha); + +int SListFileSaveToMpq(TMPQArchive * ha); + +#endif // __SCOMMON_H__ + diff --git a/src/tools/stuffextract/StormLib/SCompression.cpp b/src/tools/stuffextract/StormLib/SCompression.cpp new file mode 100644 index 0000000..7d2c50d --- /dev/null +++ b/src/tools/stuffextract/StormLib/SCompression.cpp @@ -0,0 +1,715 @@ +/*****************************************************************************/ +/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module serves as a bridge between StormLib code and (de)compression */ +/* functions. All (de)compression calls go (and should only go) through this */ +/* module. No system headers should be included in this module to prevent */ +/* compile-time problems. */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 01.04.03 1.00 Lad The first version of SCompression.cpp */ +/* 19.11.03 1.01 Dan Big endian handling */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +#include + +// Include functions from Pkware Data Compression Library +#include "pklib/pklib.h" + +// Include functions from zlib +#ifndef __SYS_ZLIB +#include "zlib/zlib.h" // Include functions from zlib +#else +#include // If zlib is available on system, use this instead +#endif + +// Include functions from Huffmann compression +#include "huffman/huff.h" + +// Include functions from WAVe compression +#include "wave/wave.h" + +// Include functions from BZip2 compression library +#ifndef __SYS_BZLIB +#include "bzip2/bzlib.h" // Include functions from bzlib +#else +#include // If bzlib is available on system, use this instead +#endif + +//----------------------------------------------------------------------------- +// Local structures + +// Information about the input and output buffers for pklib +typedef struct +{ + char * pInBuff; // Pointer to input data buffer + int nInPos; // Current offset in input data buffer + int nInBytes; // Number of bytes in the input buffer + char * pOutBuff; // Pointer to output data buffer + int nOutPos; // Position in the output buffer + int nMaxOut; // Maximum number of bytes in the output buffer +} TDataInfo; + +// Table of compression functions +typedef int (*COMPRESS)(char *, int *, char *, int, int *, int); +typedef struct +{ + unsigned long dwMask; // Compression mask + COMPRESS Compress; // Compression function +} TCompressTable; + +// Table of decompression functions +typedef int (*DECOMPRESS)(char *, int *, char *, int); +typedef struct +{ + unsigned long dwMask; // Decompression bit + DECOMPRESS Decompress; // Decompression function +} TDecompressTable; + + +/*****************************************************************************/ +/* */ +/* Support functions for Pkware Data Compression Library */ +/* */ +/*****************************************************************************/ + +// Function loads data from the input buffer. Used by Pklib's "implode" +// and "explode" function as user-defined callback +// Returns number of bytes loaded +// +// char * buf - Pointer to a buffer where to store loaded data +// unsigned int * size - Max. number of bytes to read +// void * param - Custom pointer, parameter of implode/explode + +static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) +{ + TDataInfo * pInfo = (TDataInfo *)param; + unsigned int nMaxAvail = (pInfo->nInBytes - pInfo->nInPos); + unsigned int nToRead = *size; + + // Check the case when not enough data available + if(nToRead > nMaxAvail) + nToRead = nMaxAvail; + + // Load data and increment offsets + memcpy(buf, pInfo->pInBuff + pInfo->nInPos, nToRead); + pInfo->nInPos += nToRead; + + return nToRead; +} + +// Function for store output data. Used by Pklib's "implode" and "explode" +// as user-defined callback +// +// char * buf - Pointer to data to be written +// unsigned int * size - Number of bytes to write +// void * param - Custom pointer, parameter of implode/explode + +static void WriteOutputData(char * buf, unsigned int * size, void * param) +{ + TDataInfo * pInfo = (TDataInfo *)param; + unsigned int nMaxWrite = (pInfo->nMaxOut - pInfo->nOutPos); + unsigned int nToWrite = *size; + + // Check the case when not enough space in the output buffer + if(nToWrite > nMaxWrite) + nToWrite = nMaxWrite; + + // Write output data and increments offsets + memcpy(pInfo->pOutBuff + pInfo->nOutPos, buf, nToWrite); + pInfo->nOutPos += nToWrite; +} + +/*****************************************************************************/ +/* */ +/* "80" is IMA ADPCM stereo (de)compression */ +/* "40" is IMA ADPCM mono (de)compression */ +/* */ +/*****************************************************************************/ + +int Compress_wave_mono(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel) +{ + // Prepare the compression level for the next compression + // (After us, the Huffmann compression will be called) + if(0 < nCmpLevel && nCmpLevel <= 2) + { + nCmpLevel = 4; + *pCmpType = 6; + } + else if(nCmpLevel == 3) + { + nCmpLevel = 6; + *pCmpType = 8; + } + else + { + nCmpLevel = 5; + *pCmpType = 7; + } + *pdwOutLength = CompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (short *)pbInBuffer, dwInLength, 1, nCmpLevel); + return 0; +} + +int Decompress_wave_mono(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + *pdwOutLength = DecompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (unsigned char *)pbInBuffer, dwInLength, 1); + return 1; +} + +int Compress_wave_stereo(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel) +{ + // Prepare the compression type for the next compression + // (After us, the Huffmann compression will be called) + if(0 < nCmpLevel && nCmpLevel <= 2) + { + nCmpLevel = 4; + *pCmpType = 6; + } + else if(nCmpLevel == 3) + { + nCmpLevel = 6; + *pCmpType = 8; + } + else + { + nCmpLevel = 5; + *pCmpType = 7; + } + *pdwOutLength = CompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (short *)pbInBuffer, dwInLength, 2, nCmpLevel); + return 0; +} + +int Decompress_wave_stereo(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + *pdwOutLength = DecompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (unsigned char *)pbInBuffer, dwInLength, 2); + return 1; +} + +/*****************************************************************************/ +/* */ +/* The "01" (de)compression is the Huffman (de)compression */ +/* */ +/*****************************************************************************/ + +// 1500F4C0 +int Compress_huff(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int /* nCmpLevel */) +{ + THuffmannTree ht; // Huffmann tree for compression + TOutputStream os; // Output stream + + // Initialize output stream + os.pbOutBuffer = (unsigned char *)pbOutBuffer; + os.dwOutSize = *pdwOutLength; + os.pbOutPos = (unsigned char *)pbOutBuffer; + os.dwBitBuff = 0; + os.nBits = 0; + + // Initialize the Huffmann tree for compression + ht.InitTree(true); + + *pdwOutLength = ht.DoCompression(&os, (unsigned char *)pbInBuffer, dwInLength, *pCmpType); + + // The following code is not necessary to run, because it has no + // effect on the output data. It only clears the huffmann tree, but when + // the tree is on the stack, who cares ? +// ht.UninitTree(); + return 0; +} + +// 1500F5F0 +int Decompress_huff(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int /* dwInLength */) +{ + THuffmannTree ht; + TInputStream is; + + // Initialize input stream +// is.pbInBuffer = (unsigned char *)pbInBuffer; + is.dwBitBuff = BSWAP_INT32_UNSIGNED(*(unsigned long *)pbInBuffer); + pbInBuffer += sizeof(unsigned long); + is.pbInBuffer = (unsigned char *)pbInBuffer; + is.nBits = 32; + + // Initialize the Huffmann tree for compression + ht.InitTree(false); + *pdwOutLength = ht.DoDecompression((unsigned char *)pbOutBuffer, *pdwOutLength, &is); + + // The following code is not necessary to run, because it has no + // effect on the output data. It only clears the huffmann tree, but when + // the tree is on the stack, who cares ? +// ht.UninitTree(); + return 0; +} + +/*****************************************************************************/ +/* */ +/* The "02" (de)compression is the ZLIB (de)compression */ +/* */ +/*****************************************************************************/ + +int Compress_zlib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * /* pCmpType */, int /* nCmpLevel */) +{ + z_stream z; // Stream information for zlib + int nResult; + + // Fill the stream structure for zlib + z.next_in = (Bytef *)pbInBuffer; + z.avail_in = (uInt)dwInLength; + z.total_in = dwInLength; + z.next_out = (Bytef *)pbOutBuffer; + z.avail_out = *pdwOutLength; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + // Initialize the compression structure. Storm.dll uses zlib version 1.1.3 + *pdwOutLength = 0; + if((nResult = deflateInit(&z, Z_DEFAULT_COMPRESSION)) == 0) + { + // Call zlib to compress the data + nResult = deflate(&z, Z_FINISH); + + if(nResult == Z_OK || nResult == Z_STREAM_END) + *pdwOutLength = z.total_out; + + deflateEnd(&z); + } + return nResult; +} + +int Decompress_zlib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + z_stream z; // Stream information for zlib + int nResult; + + // Fill the stream structure for zlib + z.next_in = (Bytef *)pbInBuffer; + z.avail_in = (uInt)dwInLength; + z.total_in = dwInLength; + z.next_out = (Bytef *)pbOutBuffer; + z.avail_out = *pdwOutLength; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 + if((nResult = inflateInit(&z)) == 0) + { + // Call zlib to decompress the data + nResult = inflate(&z, Z_FINISH); + *pdwOutLength = z.total_out; + inflateEnd(&z); + } + return nResult; +} + +/*****************************************************************************/ +/* */ +/* The "08" (de)compression is the Pkware DCL (de)compression */ +/* */ +/*****************************************************************************/ + +int Compress_pklib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int /* nCmpLevel */) +{ + TDataInfo Info; // Data information + char * work_buf = ALLOCMEM(char, CMP_BUFFER_SIZE);// Pklib's work buffer + unsigned int dict_size; // Dictionary size + unsigned int ctype; // Compression type + + // Fill data information structure + Info.pInBuff = pbInBuffer; + Info.nInPos = 0; + Info.nInBytes = dwInLength; + Info.pOutBuff = pbOutBuffer; + Info.nOutPos = 0; + Info.nMaxOut = *pdwOutLength; + + // Set the compression type and dictionary size + ctype = (*pCmpType == 2) ? CMP_ASCII : CMP_BINARY; + if (dwInLength < 0x600) + dict_size = 0x400; + else if(0x600 <= dwInLength && dwInLength < 0xC00) + dict_size = 0x800; + else + dict_size = 0x1000; + + // Do the compression + implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size); + *pdwOutLength = Info.nOutPos; + FREEMEM(work_buf); + return 0; +} + +int Decompress_pklib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + TDataInfo Info; // Data information + char * work_buf = ALLOCMEM(char, EXP_BUFFER_SIZE);// Pklib's work buffer + + // Fill data information structure + Info.pInBuff = pbInBuffer; + Info.nInPos = 0; + Info.nInBytes = dwInLength; + Info.pOutBuff = pbOutBuffer; + Info.nOutPos = 0; + Info.nMaxOut = *pdwOutLength; + + // Do the decompression + explode(ReadInputData, WriteOutputData, work_buf, &Info); + + // Fix: If PKLIB is unable to decompress the data, they are uncompressed + if(Info.nOutPos == 0) + { + Info.nOutPos = min(*pdwOutLength, dwInLength); + memcpy(pbOutBuffer, pbInBuffer, Info.nOutPos); + } + + *pdwOutLength = Info.nOutPos; + FREEMEM(work_buf); + return 0; +} + +/*****************************************************************************/ +/* */ +/* The "10" (de)compression is the Bzip2 (de)compression */ +/* */ +/*****************************************************************************/ + +int Compress_bzip2(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel) +{ + bz_stream strm; + int blockSize100k; + int workFactor = 30; + + // Keep compiler happy + nCmpLevel = nCmpLevel; + + // Initialize the BZLIB compression + strm.bzalloc = NULL; + strm.bzfree = NULL; + + // Adjust the block size + blockSize100k = *pCmpType; + if(blockSize100k < 1 || blockSize100k > 9) + blockSize100k = 9; + + // Blizzard uses 9 as blockSize100k, (0 as workFactor) + if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == 0) + { + strm.next_in = pbInBuffer; + strm.avail_in = dwInLength; + strm.next_out = pbOutBuffer; + strm.avail_out = *pdwOutLength; + + // Perform the compression + while(BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH) != BZ_STREAM_END); + + // Put the stream into idle state + BZ2_bzCompressEnd(&strm); + *pdwOutLength = strm.total_out_lo32; + } + else + { + *pdwOutLength = 0; + } + + return 0; +} + +int Decompress_bzip2(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + bz_stream strm; + + // Initialize the BZLIB decompression + strm.bzalloc = NULL; + strm.bzfree = NULL; + if(BZ2_bzDecompressInit(&strm, 0, 0) == 0) + { + strm.next_in = pbInBuffer; + strm.avail_in = dwInLength; + strm.next_out = pbOutBuffer; + strm.avail_out = *pdwOutLength; + + // Perform the decompression + while(BZ2_bzDecompress(&strm) != BZ_STREAM_END); + + // Put the stream into idle state + BZ2_bzDecompressEnd(&strm); + *pdwOutLength = strm.total_out_lo32; + } + else + { + // Set zero output length + *pdwOutLength = 0; + } + + return 0; +} + +/*****************************************************************************/ +/* */ +/* SCompCompress */ +/* */ +/*****************************************************************************/ + +// This table contains compress functions which can be applied to +// uncompressed blocks. Each bit set means the corresponding +// compression method/function must be applied. +// +// WAVes compression Data compression +// ------------------ ------------------- +// 1st block - 0x08 0x08 (D, HF, W2, SC, D2) +// Rest blocks - 0x81 0x02 (W3) + +static TCompressTable cmp_table[] = +{ + {MPQ_COMPRESSION_WAVE_MONO, Compress_wave_mono}, // IMA ADPCM mono compression + {MPQ_COMPRESSION_WAVE_STEREO, Compress_wave_stereo}, // IMA ADPCM stereo compression + {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression + {MPQ_COMPRESSION_ZLIB, Compress_zlib}, // Compression with the "zlib" library + {MPQ_COMPRESSION_PKWARE, Compress_pklib}, // Compression with Pkware DCL + {MPQ_COMPRESSION_BZIP2, Compress_bzip2} // Compression Bzip2 library +}; + +int WINAPI SCompCompress(char * pbCompressed, int * pdwOutLength, char * pbUncompressed, int dwInLength, + int uCompressions, int nCmpType, int nCmpLevel) +{ + char * pbTempBuff = NULL; // Temporary storage for decompressed data + char * pbOutput = pbCompressed; // Current output buffer + char * pbInput; // Current input buffer + int uCompressions2; + int dwCompressCount = 0; + int dwDoneCount = 0; + int dwOutSize = 0; + int dwInSize = dwInLength; + int dwEntries = (sizeof(cmp_table) / sizeof(TCompressTable)); + int nResult = 1; + int i; + + // Check for valid parameters + if(!pdwOutLength || *pdwOutLength < dwInLength || !pbCompressed || !pbUncompressed) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Count the compressions + for(i = 0, uCompressions2 = uCompressions; i < dwEntries; i++) + { + if(uCompressions & cmp_table[i].dwMask) + dwCompressCount++; + + uCompressions2 &= ~cmp_table[i].dwMask; + } + + // If a compression remains, do nothing + if(uCompressions2 != 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // If more that one compression, allocate intermediate buffer + if(dwCompressCount >= 2) + pbTempBuff = ALLOCMEM(char, *pdwOutLength + 1); + + // Perform the compressions + pbInput = pbUncompressed; + dwInSize = dwInLength; + for(i = 0, uCompressions2 = uCompressions; i < dwEntries; i++) + { + if(uCompressions2 & cmp_table[i].dwMask) + { + // Set the right output buffer + dwCompressCount--; + pbOutput = (dwCompressCount & 1) ? pbTempBuff : pbCompressed; + + // Perform the partial compression + dwOutSize = *pdwOutLength - 1; + + cmp_table[i].Compress(pbOutput + 1, &dwOutSize, pbInput, dwInSize, &nCmpType, nCmpLevel); + if(dwOutSize == 0) + { + SetLastError(ERROR_GEN_FAILURE); + *pdwOutLength = 0; + nResult = 0; + break; + } + + // If the compression failed, copy the block instead + if(dwOutSize >= dwInSize - 1) + { + if(dwDoneCount > 0) + pbOutput++; + + memcpy(pbOutput, pbInput, dwInSize); + pbInput = pbOutput; + uCompressions &= ~cmp_table[i].dwMask; + dwOutSize = dwInSize; + } + else + { + pbInput = pbOutput + 1; + dwInSize = dwOutSize; + dwDoneCount++; + } + } + } + + // Copy the compressed data to the correct output buffer + if(nResult != 0) + { + if(uCompressions && (dwInSize + 1) < *pdwOutLength) + { + if(pbOutput != pbCompressed && pbOutput != pbCompressed + 1) + memcpy(pbCompressed, pbOutput, dwInSize); + *pbCompressed = (char)uCompressions; + *pdwOutLength = dwInSize + 1; + } + else + { + memmove(pbCompressed, pbUncompressed, dwInSize); + *pdwOutLength = dwInSize; + } + } + + // Cleanup and return + if(pbTempBuff != NULL) + FREEMEM(pbTempBuff); + return nResult; +} + +/*****************************************************************************/ +/* */ +/* SCompDecompress */ +/* */ +/*****************************************************************************/ + +// This table contains decompress functions which can be applied to +// uncompressed blocks. The compression mask is stored in the first byte +// of compressed block +static TDecompressTable dcmp_table[] = +{ + {MPQ_COMPRESSION_BZIP2, Decompress_bzip2}, // Decompression with Bzip2 library + {MPQ_COMPRESSION_PKWARE, Decompress_pklib}, // Decompression with Pkware Data Compression Library + {MPQ_COMPRESSION_ZLIB, Decompress_zlib}, // Decompression with the "zlib" library + {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression + {MPQ_COMPRESSION_WAVE_STEREO, Decompress_wave_stereo}, // IMA ADPCM stereo decompression + {MPQ_COMPRESSION_WAVE_MONO, Decompress_wave_mono} // IMA ADPCM mono decompression +}; + +int WINAPI SCompDecompress(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength) +{ + char * pbTempBuff = NULL; // Temporary storage for decompressed data + char * pbWorkBuff = NULL; // Where to store decompressed data + int dwOutLength = *pdwOutLength; // For storage number of output bytes + unsigned fDecompressions1; // Decompressions applied to the block + unsigned fDecompressions2; // Just another copy of decompressions applied to the block + int dwCount = 0; // Counter for every use + int dwEntries = (sizeof(dcmp_table) / sizeof(TDecompressTable)); + int nResult = 1; + int i; + + // If the input length is the same as output, do nothing. + if(dwInLength == dwOutLength) + { + if(pbInBuffer == pbOutBuffer) + return 1; + + memcpy(pbOutBuffer, pbInBuffer, dwInLength); + *pdwOutLength = dwInLength; + return 1; + } + + // Get applied compression types and decrement data length + fDecompressions1 = fDecompressions2 = (unsigned char)*pbInBuffer++; + dwInLength--; + + // Search decompression table type and get all types of compression + for(i = 0; i < dwEntries; i++) + { + // We have to apply this decompression ? + if(fDecompressions1 & dcmp_table[i].dwMask) + dwCount++; + + // Clear this flag from temporary variable. + fDecompressions2 &= ~dcmp_table[i].dwMask; + } + + // Check if there is some method unhandled + // (E.g. compressed by future versions) + if(fDecompressions2 != 0) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // If there is more than only one compression, we have to allocate extra buffer + if(dwCount >= 2) + pbTempBuff = ALLOCMEM(char, dwOutLength); + + // Apply all decompressions + for(i = 0, dwCount = 0; i < dwEntries; i++) + { + // If not used this kind of compression, skip the loop + if(fDecompressions1 & dcmp_table[i].dwMask) + { + // If odd case, use target buffer for output, otherwise use allocated tempbuffer + pbWorkBuff = (dwCount++ & 1) ? pbTempBuff : pbOutBuffer; + dwOutLength = *pdwOutLength; + + // Decompress buffer using corresponding function + dcmp_table[i].Decompress(pbWorkBuff, &dwOutLength, pbInBuffer, dwInLength); + if(dwOutLength == 0) + { + SetLastError(ERROR_GEN_FAILURE); + nResult = 0; + break; + } + + // Move output length to src length for next compression + dwInLength = dwOutLength; + pbInBuffer = pbWorkBuff; + } + } + + // If output buffer is not the same like target buffer, we have to copy data + if(nResult != 0) + { + if(pbWorkBuff != pbOutBuffer) + memcpy(pbOutBuffer, pbInBuffer, dwOutLength); + + } + + // Delete temporary buffer, if necessary + if(pbTempBuff != NULL) + FREEMEM(pbTempBuff); + + *pdwOutLength = dwOutLength; + return nResult; +} + +/*****************************************************************************/ +/* */ +/* SCompSetDataCompression */ +/* */ +/*****************************************************************************/ + +int WINAPI SCompSetDataCompression(int nDataCompression) +{ + int nValidMask = (MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_PKWARE | MPQ_COMPRESSION_BZIP2); + + if((nDataCompression & nValidMask) != nDataCompression) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + SetDataCompression(nDataCompression); + return TRUE; +} + + diff --git a/src/tools/stuffextract/StormLib/SFileCompactArchive.cpp b/src/tools/stuffextract/StormLib/SFileCompactArchive.cpp new file mode 100644 index 0000000..30540d1 --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileCompactArchive.cpp @@ -0,0 +1,691 @@ +/*****************************************************************************/ +/* SFileCompactArchive.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Archive compacting function */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */ +/* 19.11.03 1.01 Dan Big endian handling */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +/*****************************************************************************/ +/* Local structures */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Local variables */ +/*****************************************************************************/ + +static COMPACTCB CompactCB = NULL; +static void * lpUserData = NULL; + +/*****************************************************************************/ +/* Local functions */ +/*****************************************************************************/ + +// Creates a copy of hash table +static TMPQHash * CopyHashTable(TMPQArchive * ha) +{ + TMPQHash * pHashTableCopy = ALLOCMEM(TMPQHash, ha->pHeader->dwHashTableSize); + + if(pHashTableCopy != NULL) + memcpy(pHashTableCopy, ha->pHashTable, sizeof(TMPQHash) * ha->pHeader->dwHashTableSize); + + return pHashTableCopy; +} + +// TODO: Test for archives > 4GB +static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, DWORD * pFileSeeds) +{ + TMPQHash * pHashTableCopy = NULL; // Copy of the hash table + TMPQHash * pHash; + TMPQHash * pHashEnd = NULL; // End of the hash table + DWORD dwFileCount = 0; + int nError = ERROR_SUCCESS; + + // First of all, create a copy of hash table + if(nError == ERROR_SUCCESS) + { + if((pHashTableCopy = CopyHashTable(ha)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + pHashEnd = pHashTableCopy + ha->pHeader->dwHashTableSize; + + // Notify the user + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_CHECKING_FILES, 0, ha->pHeader->dwBlockTableSize); + } + + // Now check all the files from the filelist + if(nError == ERROR_SUCCESS) + { + SFILE_FIND_DATA wf; + HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile); + BOOL bResult = TRUE; + + // Do while some files have been found + while(hFind != NULL && bResult) + { + TMPQHash * pHash = GetHashEntry(ha, wf.cFileName); + + // If the hash table entry has been found, find it's position + // in the hash table copy + if(pHash != NULL) + { + pHash = pHashTableCopy + (pHash - ha->pHashTable); + if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) + { + TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; + DWORD dwSeed = 0; + + // Resolve the file seed. Use plain file name for it + if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + char * szFileName = strrchr(wf.cFileName, '\\'); + + if(szFileName == NULL) + szFileName = wf.cFileName; + else + szFileName++; + + dwSeed = DecryptFileSeed(szFileName); + if(pBlock->dwFlags & MPQ_FILE_FIXSEED) + dwSeed = (dwSeed + pBlock->dwFilePos) ^ pBlock->dwFSize; + } + pFileSeeds[pHash->dwBlockIndex] = dwSeed; + + pHash->dwName1 = 0xFFFFFFFF; + pHash->dwName2 = 0xFFFFFFFF; + pHash->lcLocale = 0xFFFF; + pHash->wPlatform = 0xFFFF; + pHash->dwBlockIndex = 0xFFFFFFFF; + } + } + // Notify the user + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_CHECKING_FILES, ++dwFileCount, ha->pHeader->dwBlockTableSize); + + // Find the next file in the archive + bResult = SFileFindNextFile(hFind, &wf); + } + + if(hFind != NULL) + SFileFindClose(hFind); + } + + // When the filelist checking is complete, parse the hash table copy and find the + if(nError == ERROR_SUCCESS) + { + // Notify the user about checking hash table + dwFileCount = 0; + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_CHECKING_HASH_TABLE, dwFileCount, ha->pHeader->dwBlockTableSize); + + for(pHash = pHashTableCopy; pHash < pHashEnd; pHash++) + { + // If there is an unresolved entry, try to detect its seed. If it fails, + // we cannot complete the work + if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) + { + HANDLE hFile = NULL; + DWORD dwFlags = 0; + DWORD dwSeed = 0; + + if(SFileOpenFileEx((HANDLE)ha, (char *)(DWORD_PTR)pHash->dwBlockIndex, 0, &hFile)) + { + TMPQFile * hf = (TMPQFile *)hFile; + dwFlags = hf->pBlock->dwFlags; + dwSeed = hf->dwSeed1; + SFileCloseFile(hFile); + } + + // If the file is encrypted, we have to check + // If we can apply the file decryption seed + if(dwFlags & MPQ_FILE_ENCRYPTED && dwSeed == 0) + { + nError = ERROR_CAN_NOT_COMPLETE; + break; + } + + // Remember the seed + pFileSeeds[pHash->dwBlockIndex] = dwSeed; + + // Notify the user + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_CHECKING_HASH_TABLE, ++dwFileCount, ha->pHeader->dwBlockTableSize); + } + } + } + + // Delete the copy of hash table + if(pHashTableCopy != NULL) + FREEMEM(pHashTableCopy); + return nError; +} + +// Copies all file blocks into another archive. +// TODO: Test for archives > 4GB +static int CopyMpqFileBlocks( + HANDLE hFile, + TMPQArchive * ha, + TMPQBlockEx * pBlockEx, + TMPQBlock * pBlock, + DWORD dwSeed) +{ + LARGE_INTEGER FilePos = {0}; + DWORD * pdwBlockPos2 = NULL; // File block positions to be written to target file + DWORD * pdwBlockPos = NULL; // File block positions (unencrypted) + BYTE * pbBlock = NULL; // Buffer for the file block + DWORD dwTransferred; // Number of bytes transferred + DWORD dwCSize = 0; // Compressed file size + DWORD dwBytes = 0; // Number of bytes + DWORD dwSeed1 = 0; // File seed used for decryption + DWORD dwSeed2 = 0; // File seed used for encryption + DWORD nBlocks = 0; // Number of file blocks + DWORD nBlock = 0; // Currently processed file block + int nError = ERROR_SUCCESS; + + // When file length is zero, do nothing + if(pBlock->dwFSize == 0) + return ERROR_SUCCESS; + + // Calculate number of blocks in the file + if(nError == ERROR_SUCCESS) + { + nBlocks = pBlock->dwFSize / ha->dwBlockSize; + if(pBlock->dwFSize % ha->dwBlockSize) + nBlocks++; + pbBlock = ALLOCMEM(BYTE, ha->dwBlockSize); + if(pbBlock == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Set the position to the begin of the file within archive + if(nError == ERROR_SUCCESS) + { + FilePos.HighPart = pBlockEx->wFilePosHigh; + FilePos.LowPart = pBlock->dwFilePos; + FilePos.QuadPart += ha->MpqPos.QuadPart; + if(SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN) != FilePos.LowPart) + nError = GetLastError(); + } + + // Remember the position in the destination file + if(nError == ERROR_SUCCESS) + { + FilePos.HighPart = 0; + FilePos.LowPart = SetFilePointer(hFile, 0, &FilePos.HighPart, FILE_CURRENT); + } + + // Resolve decryption seeds. The 'dwSeed' parameter is the decryption + // seed for the file. + if(nError == ERROR_SUCCESS && (pBlock->dwFlags & MPQ_FILE_ENCRYPTED)) + { + dwSeed1 = dwSeed; + if(pBlock->dwFlags & MPQ_FILE_FIXSEED) + dwSeed = (dwSeed1 ^ pBlock->dwFSize) - pBlock->dwFilePos; + + dwSeed2 = dwSeed; + if(pBlock->dwFlags & MPQ_FILE_FIXSEED) + dwSeed2 = (dwSeed + (DWORD)(FilePos.QuadPart - ha->MpqPos.QuadPart)) ^ pBlock->dwFSize; + } + + // Load the file positions from the archive and save it to the target file + // (only if the file is compressed) + if(pBlock->dwFlags & MPQ_FILE_COMPRESSED) + { + // Allocate buffers + if(nError == ERROR_SUCCESS) + { + pdwBlockPos = ALLOCMEM(DWORD, nBlocks + 2); + pdwBlockPos2 = ALLOCMEM(DWORD, nBlocks + 2); + + if(pdwBlockPos == NULL || pdwBlockPos2 == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Load the block positions + if(nError == ERROR_SUCCESS) + { + dwBytes = (nBlocks + 1) * sizeof(DWORD); + if(pBlock->dwFlags & MPQ_FILE_HAS_EXTRA) + dwBytes += sizeof(DWORD); + + ReadFile(ha->hFile, pdwBlockPos, dwBytes, &dwTransferred, NULL); + if(dwTransferred != dwBytes) + nError = ERROR_FILE_CORRUPT; + } + + // Re-encrypt the block table positions + if(nError == ERROR_SUCCESS) + { + BSWAP_ARRAY32_UNSIGNED(pdwBlockPos, dwBytes / sizeof(DWORD)); + if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + DecryptMPQBlock(pdwBlockPos, dwBytes, dwSeed1 - 1); + if(pdwBlockPos[0] != dwBytes) + nError = ERROR_FILE_CORRUPT; + + memcpy(pdwBlockPos2, pdwBlockPos, dwBytes); + EncryptMPQBlock(pdwBlockPos2, dwBytes, dwSeed2 - 1); + } + else + { + memcpy(pdwBlockPos2, pdwBlockPos, dwBytes); + } + BSWAP_ARRAY32_UNSIGNED(pdwBlockPos2, dwBytes / sizeof(DWORD)); + } + + // Write to the target file + if(nError == ERROR_SUCCESS) + { + WriteFile(hFile, pdwBlockPos2, dwBytes, &dwTransferred, NULL); + dwCSize += dwTransferred; + if(dwTransferred != dwBytes) + nError = ERROR_DISK_FULL; + } + } + + // Now we have to copy all file block. We will do it without + // recompression, because re-compression is not necessary in this case + if(nError == ERROR_SUCCESS) + { + for(nBlock = 0; nBlock < nBlocks; nBlock++) + { + // Fix: The last block must not be exactly the size of one block. + dwBytes = ha->dwBlockSize; + if(nBlock == nBlocks - 1) + { + dwBytes = pBlock->dwFSize - (ha->dwBlockSize * (nBlocks - 1)); + } + + if(pBlock->dwFlags & MPQ_FILE_COMPRESSED) + dwBytes = pdwBlockPos[nBlock+1] - pdwBlockPos[nBlock]; + + // Read the file block + ReadFile(ha->hFile, pbBlock, dwBytes, &dwTransferred, NULL); + if(dwTransferred != dwBytes) + { + nError = ERROR_FILE_CORRUPT; + break; + } + + // If necessary, re-encrypt the block + // Note: Recompression is not necessary here. Unlike encryption, + // the compression does not depend on the position of the file in MPQ. + if((pBlock->dwFlags & MPQ_FILE_ENCRYPTED) && dwSeed1 != dwSeed2) + { + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBlock, dwBytes/sizeof(DWORD)); + DecryptMPQBlock((DWORD *)pbBlock, dwBytes, dwSeed1 + nBlock); + EncryptMPQBlock((DWORD *)pbBlock, dwBytes, dwSeed2 + nBlock); + BSWAP_ARRAY32_UNSIGNED((DWORD *)pbBlock, dwBytes/sizeof(DWORD)); + } + + // Now write the block back to the file + WriteFile(hFile, pbBlock, dwBytes, &dwTransferred, NULL); + dwCSize += dwTransferred; + if(dwTransferred != dwBytes) + { + nError = ERROR_DISK_FULL; + break; + } + } + } + + // Copy the file extras, if any + // These extras does not seem to be encrypted, and their purpose is unknown + if(nError == ERROR_SUCCESS && (pBlock->dwFlags & MPQ_FILE_HAS_EXTRA)) + { + dwBytes = pdwBlockPos[nBlocks + 1] - pdwBlockPos[nBlocks]; + if(dwBytes != 0) + { + ReadFile(ha->hFile, pbBlock, dwBytes, &dwTransferred, NULL); + if(dwTransferred == dwBytes) + { + WriteFile(hFile, pbBlock, dwBytes, &dwTransferred, NULL); + dwCSize += dwTransferred; + if(dwTransferred != dwBytes) + nError = ERROR_DISK_FULL; + } + else + { + nError = ERROR_FILE_CORRUPT; + } + } + } + + // Update file position in the block table + if(nError == ERROR_SUCCESS) + { + // At this point, number of bytes written should be exactly + // the same like the compressed file size. If it isn't, there's something wrong + // (maybe new archive version ?) + assert(dwCSize == pBlock->dwCSize); + + // Update file pos in the block table + FilePos.QuadPart -= ha->MpqPos.QuadPart; + pBlockEx->wFilePosHigh = (USHORT)FilePos.HighPart; + pBlock->dwFilePos = FilePos.LowPart; + } + + // Cleanup and return + if(pdwBlockPos2 != NULL) + FREEMEM(pdwBlockPos2); + if(pdwBlockPos != NULL) + FREEMEM(pdwBlockPos); + if(pbBlock != NULL) + FREEMEM(pbBlock); + return nError; +} + + +static int CopyNonMpqData( + HANDLE hSrcFile, + HANDLE hTrgFile, + LARGE_INTEGER & DataSizeToCopy) +{ + LARGE_INTEGER DataSize = DataSizeToCopy; + DWORD dwTransferred; + DWORD dwToRead; + char DataBuffer[0x1000]; + int nError = ERROR_SUCCESS; + + while(DataSize.QuadPart > 0) + { + // Get the proper size of data + dwToRead = sizeof(DataBuffer); + if(DataSize.HighPart == 0 && DataSize.LowPart < dwToRead) + dwToRead = DataSize.LowPart; + + // Read the source file + ReadFile(hSrcFile, DataBuffer, dwToRead, &dwTransferred, NULL); + if(dwTransferred != dwToRead) + { + nError = ERROR_CAN_NOT_COMPLETE; + break; + } + + // Write to the target file + WriteFile(hTrgFile, DataBuffer, dwToRead, &dwTransferred, NULL); + if(dwTransferred != dwToRead) + { + nError = ERROR_DISK_FULL; + break; + } + + // Decrement the number of data to be copied + DataSize.QuadPart -= dwTransferred; + } + + return ERROR_SUCCESS; +} + +// TODO: Test for archives > 4GB +static int CopyMpqFiles(HANDLE hFile, TMPQArchive * ha, DWORD * pFileSeeds) +{ + TMPQBlockEx * pBlockEx; + TMPQBlock * pBlock; + DWORD dwSeed1; + DWORD dwIndex; + int nError = ERROR_SUCCESS; + + // Walk through all files and write them to the destination MPQ archive + for(dwIndex = 0; dwIndex < ha->pHeader->dwBlockTableSize; dwIndex++) + { + pBlockEx = ha->pExtBlockTable + dwIndex; + pBlock = ha->pBlockTable + dwIndex; + dwSeed1 = pFileSeeds[dwIndex]; + + // Notify the caller about work + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_COMPACTING_FILES, dwIndex, ha->pHeader->dwBlockTableSize); + +// if(dwIndex == 0x1B9) +// DebugBreak(); + + // Copy all the file blocks + // Debug: Break at (dwIndex == 5973) + if(pBlock->dwFlags & MPQ_FILE_EXISTS) + { + nError = CopyMpqFileBlocks(hFile, ha, pBlockEx, pBlock, dwSeed1); + if(nError != ERROR_SUCCESS) + break; + } + } + + // Cleanup and exit + return nError; +} + + +/*****************************************************************************/ +/* Public functions */ +/*****************************************************************************/ + +BOOL WINAPI SFileSetCompactCallback(HANDLE /* hMPQ */, COMPACTCB aCompactCB, void * lpData) +{ + CompactCB = aCompactCB; + lpUserData = lpData; + return TRUE; +} + +//----------------------------------------------------------------------------- +// Archive compacting (incomplete) + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileCompactArchive(HANDLE hMPQ, const char * szListFile, BOOL /* bReserved */) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD * pFileSeeds = NULL; + char szTempFile[MAX_PATH] = ""; + char * szTemp = NULL; + DWORD dwTransferred; + int nError = ERROR_SUCCESS; + + // Test the valid parameters + if(!IsValidMpqHandle(ha)) + nError = ERROR_INVALID_PARAMETER; + + // Create the table with file seeds + if(nError == ERROR_SUCCESS) + { + if((pFileSeeds = ALLOCMEM(DWORD, ha->pHeader->dwBlockTableSize)) != NULL) + memset(pFileSeeds, 0, sizeof(DWORD) * ha->pHeader->dwBlockTableSize); + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // First of all, we have to check of we are able to decrypt all files. + // If not, sorry, but the archive cannot be compacted. + if(nError == ERROR_SUCCESS) + nError = CheckIfAllFilesKnown(ha, szListFile, pFileSeeds); + + // Get the temporary file name and create it + if(nError == ERROR_SUCCESS) + { + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_COPYING_NON_MPQ_DATA, 0, 0); + + strcpy(szTempFile, ha->szFileName); + if((szTemp = strrchr(szTempFile, '.')) != NULL) + strcpy(szTemp + 1, "mp_"); + else + strcat(szTempFile, "_"); + + hFile = CreateFile(szTempFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if(hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Write the data before MPQ header (if any) + if(nError == ERROR_SUCCESS && ha->MpqPos.QuadPart > 0) + { + SetFilePointer(ha->hFile, 0, NULL, FILE_BEGIN); + if(ha->pShunt != NULL) + nError = CopyNonMpqData(ha->hFile, hFile, ha->ShuntPos); + else + nError = CopyNonMpqData(ha->hFile, hFile, ha->MpqPos); + } + + // Write the MPQ shunt (if any) + if(nError == ERROR_SUCCESS && ha->pShunt != NULL) + { + BSWAP_TMPQSHUNT(ha->pShunt); + WriteFile(hFile, ha->pShunt, sizeof(TMPQShunt), &dwTransferred, NULL); + BSWAP_TMPQSHUNT(ha->pShunt); + + if(dwTransferred != sizeof(TMPQShunt)) + nError = ERROR_DISK_FULL; + } + + // Write the data between MPQ shunt and the MPQ header (if any) + if(nError == ERROR_SUCCESS && ha->pShunt != NULL) + { + LARGE_INTEGER BytesToCopy; + + BytesToCopy.QuadPart = ha->MpqPos.QuadPart - (ha->ShuntPos.QuadPart + sizeof(TMPQShunt)); + nError = CopyNonMpqData(ha->hFile, hFile, BytesToCopy); + } + + // Write the MPQ header + if(nError == ERROR_SUCCESS) + { + BSWAP_TMPQHEADER(ha->pHeader); + WriteFile(hFile, ha->pHeader, ha->pHeader->dwHeaderSize, &dwTransferred, NULL); + BSWAP_TMPQHEADER(ha->pHeader); + if(dwTransferred != ha->pHeader->dwHeaderSize) + nError = ERROR_DISK_FULL; + } + + // Write the data between the header and between the first file + // For this, we have to determine where the first file begins + if(nError == ERROR_SUCCESS) + { + LARGE_INTEGER FirstFilePos; + LARGE_INTEGER TempPos; + TMPQBlockEx * pBlockEx = ha->pExtBlockTable; + TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; + TMPQBlock * pBlock = ha->pBlockTable; + + // Maximum file position + FirstFilePos.HighPart = 0x7FFFFFFF; + FirstFilePos.LowPart = 0xFFFFFFFF; + + // Find the block with the least position in the MPQ + while(pBlock < pBlockEnd) + { + TempPos.HighPart = pBlockEx->wFilePosHigh; + TempPos.LowPart = pBlock->dwFilePos; + if(TempPos.QuadPart < FirstFilePos.QuadPart) + FirstFilePos = TempPos; + + pBlockEx++; + pBlock++; + } + + // Set the position in the source file right after the file header + TempPos.QuadPart = ha->MpqPos.QuadPart + ha->pHeader->dwHeaderSize; + SetFilePointer(ha->hFile, TempPos.LowPart, &TempPos.HighPart, FILE_BEGIN); + + // Get the number of bytes to copy + FirstFilePos.QuadPart -= ha->pHeader->dwHeaderSize; + nError = CopyNonMpqData(ha->hFile, hFile, FirstFilePos); + } + + // Now write all file blocks. + if(nError == ERROR_SUCCESS) + nError = CopyMpqFiles(hFile, ha, pFileSeeds); + + // Now we need to update the tables positions + // (but only if the tables are at the end of the file) + if(nError == ERROR_SUCCESS) + { + LARGE_INTEGER RelativePos; + LARGE_INTEGER FilePos = {0}; + + // Set the hash table position + FilePos.LowPart = SetFilePointer(hFile, 0, &FilePos.HighPart, FILE_CURRENT); + RelativePos.QuadPart = FilePos.QuadPart - ha->MpqPos.QuadPart; + ha->pHeader->wHashTablePosHigh = (USHORT)RelativePos.HighPart; + ha->pHeader->dwHashTablePos = RelativePos.LowPart; + ha->HashTablePos = FilePos; + + // Set the block table position + RelativePos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + FilePos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + ha->pHeader->wBlockTablePosHigh = (USHORT)RelativePos.HighPart; + ha->pHeader->dwBlockTablePos = RelativePos.LowPart; + ha->BlockTablePos = FilePos; + + // Set the extended block table position + RelativePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + FilePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + if(ha->ExtBlockTablePos.QuadPart != 0) + { + ha->pHeader->ExtBlockTablePos = RelativePos; + ha->ExtBlockTablePos = FilePos; + + RelativePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); + FilePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); + } + + // Set the archive size + ha->pHeader->dwArchiveSize = RelativePos.LowPart; + ha->MpqSize = RelativePos; + } + + // If succeeded, update the tables in the file + if(nError == ERROR_SUCCESS) + { + CloseHandle(ha->hFile); + ha->FilePointer.QuadPart = 0; + ha->hFile = hFile; + hFile = INVALID_HANDLE_VALUE; + nError = SaveMPQTables(ha); + } + + // If all succeeded, switch the archives + if(nError == ERROR_SUCCESS) + { + if(CompactCB != NULL) + CompactCB(lpUserData, CCB_CLOSING_ARCHIVE, 0, 0); + + if(!DeleteFile(ha->szFileName) || // Delete the old archive + !CloseHandle(ha->hFile) || // Close the new archive + !MoveFile(szTempFile, ha->szFileName)) // Rename the temporary archive + nError = GetLastError(); + } + + // Now open the freshly renamed archive file + if(nError == ERROR_SUCCESS) + { + ha->hFile = CreateFile(ha->szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(ha->hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Invalidate the positions of the archive + if(nError == ERROR_SUCCESS) + { + ha->FilePointer.QuadPart = 0; + ha->pLastFile = NULL; + ha->dwBlockPos = 0; + ha->dwBuffPos = 0; + } + + // Cleanup and return + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + if(pFileSeeds != NULL) + FREEMEM(pFileSeeds); + if(nError != ERROR_SUCCESS) + SetLastError(nError); + DeleteFile(szTempFile); + CompactCB = NULL; + return (nError == ERROR_SUCCESS); +} diff --git a/src/tools/stuffextract/StormLib/SFileCreateArchiveEx.cpp b/src/tools/stuffextract/StormLib/SFileCreateArchiveEx.cpp new file mode 100644 index 0000000..f99888d --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileCreateArchiveEx.cpp @@ -0,0 +1,530 @@ +/*****************************************************************************/ +/* SFileCreateArchiveEx.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* MPQ Editing functions */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 24.03.03 1.00 Lad Splitted from SFileOpenArchive.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +//----------------------------------------------------------------------------- +// Defines + +#define DEFAULT_BLOCK_SIZE 3 // Default size of the block + +//----------------------------------------------------------------------------- +// Local tables + +static DWORD PowersOfTwo[] = +{ + 0x0000002, 0x0000004, 0x0000008, + 0x0000010, 0x0000020, 0x0000040, 0x0000080, + 0x0000100, 0x0000200, 0x0000400, 0x0000800, + 0x0001000, 0x0002000, 0x0004000, 0x0008000, + 0x0010000, 0x0020000, 0x0040000, 0x0080000, + 0x0000000 +}; + +/*****************************************************************************/ +/* Public functions */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// Opens or creates a (new) MPQ archive. +// +// szMpqName - Name of the archive to be created. +// +// dwCreationDisposition: +// +// Value Archive exists Archive doesn't exist +// ---------- --------------------- --------------------- +// CREATE_NEW Fails Creates new archive +// CREATE_ALWAYS Overwrites existing Creates new archive +// OPEN_EXISTING Opens the archive Fails +// OPEN_ALWAYS Opens the archive Creates new archive +// +// The above mentioned values can be combined with the following flags: +// +// MPQ_CREATE_ARCHIVE_V1 - Creates MPQ archive version 1 +// MPQ_CREATE_ARCHIVE_V2 - Creates MPQ archive version 2 +// +// dwHashTableSize - Size of the hash table (only if creating a new archive). +// Must be between 2^4 (= 16) and 2^18 (= 262 144) +// +// phMpq - Receives handle to the archive +// + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileCreateArchiveEx(const char * szMpqName, DWORD dwCreationDisposition, DWORD dwHashTableSize, HANDLE * phMPQ) +{ + LARGE_INTEGER MpqPos = {0}; // Position of MPQ header in the file + TMPQArchive * ha = NULL; // MPQ archive handle + HANDLE hFile = INVALID_HANDLE_VALUE; // File handle + DWORD dwTransferred = 0; // Number of bytes written into the archive + USHORT wFormatVersion; + BOOL bFileExists = FALSE; + int nIndex = 0; + int nError = ERROR_SUCCESS; + + // Pre-initialize the result value + if(phMPQ != NULL) + *phMPQ = NULL; + + // Check the parameters, if they are valid + if(szMpqName == NULL || *szMpqName == 0 || phMPQ == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + // Check the value of dwCreationDisposition against file existence + bFileExists = (GetFileAttributes(szMpqName) != 0xFFFFFFFF); + + // Extract format version from the "dwCreationDisposition" + wFormatVersion = (USHORT)(dwCreationDisposition >> 0x10); + dwCreationDisposition &= 0x0000FFFF; + + // If the file exists and open required, do it. + if(bFileExists && (dwCreationDisposition == OPEN_EXISTING || dwCreationDisposition == OPEN_ALWAYS)) + { + // Try to open the archive normal way. If it fails, it means that + // the file exist, but it is not a MPQ archive. + if(SFileOpenArchiveEx(szMpqName, 0, 0, phMPQ, GENERIC_READ | GENERIC_WRITE)) + return TRUE; + } + + // Two error cases + if(dwCreationDisposition == CREATE_NEW && bFileExists) + { + SetLastError(ERROR_ALREADY_EXISTS); + return FALSE; + } + if(dwCreationDisposition == OPEN_EXISTING && bFileExists == FALSE) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + // At this point, we have to create the archive. If the file exists, + // we will convert it to MPQ archive. + // Check the value of hash table size. It has to be a power of two + // and must be between HASH_TABLE_SIZE_MIN and HASH_TABLE_SIZE_MAX + if(dwHashTableSize < HASH_TABLE_SIZE_MIN) + dwHashTableSize = HASH_TABLE_SIZE_MIN; + if(dwHashTableSize > HASH_TABLE_SIZE_MAX) + dwHashTableSize = HASH_TABLE_SIZE_MAX; + + // Round the hash table size up to the nearest power of two + for(nIndex = 0; PowersOfTwo[nIndex] != 0; nIndex++) + { + if(dwHashTableSize <= PowersOfTwo[nIndex]) + { + dwHashTableSize = PowersOfTwo[nIndex]; + break; + } + } + + // Prepare the buffer for decryption engine + if(nError == ERROR_SUCCESS) + nError = PrepareStormBuffer(); + + // Get the position where the MPQ header will begin. + if(nError == ERROR_SUCCESS) + { + hFile = CreateFile(szMpqName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + dwCreationDisposition, + 0, + NULL); + if(hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Retrieve the file size and round it up to 0x200 bytes + if(nError == ERROR_SUCCESS) + { + MpqPos.LowPart = GetFileSize(hFile, (LPDWORD)&MpqPos.HighPart); + MpqPos.QuadPart += 0x1FF; + MpqPos.LowPart &= 0xFFFFFE00; + + if(wFormatVersion == MPQ_FORMAT_VERSION_1 && MpqPos.HighPart != 0) + nError = ERROR_DISK_FULL; + if(wFormatVersion == MPQ_FORMAT_VERSION_2 && MpqPos.HighPart > 0x0000FFFF) + nError = ERROR_DISK_FULL; + } + + // Move to the end of the file (i.e. begin of the MPQ) + if(nError == ERROR_SUCCESS) + { + if(SetFilePointer(hFile, MpqPos.LowPart, &MpqPos.HighPart, FILE_BEGIN) == 0xFFFFFFFF) + nError = GetLastError(); + } + + // Set the new end of the file to the MPQ header position + if(nError == ERROR_SUCCESS) + { + if(!SetEndOfFile(hFile)) + nError = GetLastError(); + } + + // Create the archive handle + if(nError == ERROR_SUCCESS) + { + if((ha = ALLOCMEM(TMPQArchive, 1)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Fill the MPQ archive handle structure and create the header, + // block buffer, hash table and block table + if(nError == ERROR_SUCCESS) + { + memset(ha, 0, sizeof(TMPQArchive)); + strcpy(ha->szFileName, szMpqName); + ha->hFile = hFile; + ha->dwBlockSize = 0x200 << DEFAULT_BLOCK_SIZE; + ha->MpqPos = MpqPos; + ha->FilePointer = MpqPos; + ha->pHeader = &ha->Header; + ha->pHashTable = ALLOCMEM(TMPQHash, dwHashTableSize); + ha->pBlockTable = ALLOCMEM(TMPQBlock, dwHashTableSize); + ha->pExtBlockTable = ALLOCMEM(TMPQBlockEx, dwHashTableSize); + ha->pbBlockBuffer = ALLOCMEM(BYTE, ha->dwBlockSize); + ha->pListFile = NULL; + ha->dwFlags |= MPQ_FLAG_CHANGED; + + if(!ha->pHashTable || !ha->pBlockTable || !ha->pExtBlockTable || !ha->pbBlockBuffer) + nError = GetLastError(); + hFile = INVALID_HANDLE_VALUE; + } + + // Fill the MPQ header and all buffers + if(nError == ERROR_SUCCESS) + { + LARGE_INTEGER TempPos; + TMPQHeader2 * pHeader = ha->pHeader; + DWORD dwHeaderSize = (wFormatVersion == MPQ_FORMAT_VERSION_2) ? sizeof(TMPQHeader2) : sizeof(TMPQHeader); + + memset(pHeader, 0, sizeof(TMPQHeader2)); + pHeader->dwID = ID_MPQ; + pHeader->dwHeaderSize = dwHeaderSize; + pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash); + pHeader->wFormatVersion = wFormatVersion; + pHeader->wBlockSize = 3; // 0x1000 bytes per block + pHeader->dwHashTableSize = dwHashTableSize; + + // Set proper hash table positions + ha->HashTablePos.QuadPart = ha->MpqPos.QuadPart + pHeader->dwHeaderSize; + ha->pHeader->dwHashTablePos = pHeader->dwHeaderSize; + ha->pHeader->wHashTablePosHigh = 0; + + // Set proper block table positions + ha->BlockTablePos.QuadPart = ha->HashTablePos.QuadPart + + (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); + TempPos.QuadPart = ha->BlockTablePos.QuadPart - ha->MpqPos.QuadPart; + ha->pHeader->dwBlockTablePos = TempPos.LowPart; + ha->pHeader->wBlockTablePosHigh = (USHORT)TempPos.HighPart; + + // For now, we set extended block table positioon top zero unless we add enough + // files to cause the archive size exceed 4 GB + ha->ExtBlockTablePos.QuadPart = 0; + + // Clear all tables + memset(ha->pBlockTable, 0, sizeof(TMPQBlock) * dwHashTableSize); + memset(ha->pExtBlockTable, 0, sizeof(TMPQBlockEx) * dwHashTableSize); + memset(ha->pHashTable, 0xFF, sizeof(TMPQHash) * dwHashTableSize); + } + + // Write the MPQ header to the file + if(nError == ERROR_SUCCESS) + { + DWORD dwHeaderSize = ha->pHeader->dwHeaderSize; + + BSWAP_TMPQHEADER(ha->pHeader); + WriteFile(ha->hFile, ha->pHeader, dwHeaderSize, &dwTransferred, NULL); + BSWAP_TMPQHEADER(ha->pHeader); + + if(dwTransferred != ha->pHeader->dwHeaderSize) + nError = ERROR_DISK_FULL; + + ha->FilePointer.QuadPart = ha->MpqPos.QuadPart + dwTransferred; + ha->MpqSize.QuadPart += dwTransferred; + } + + // Create the internal listfile + if(nError == ERROR_SUCCESS) + nError = SListFileCreateListFile(ha); + + // Try to add the internal listfile + if(nError == ERROR_SUCCESS) + SFileAddListFile((HANDLE)ha, NULL); + + // Cleanup : If an error, delete all buffers and return + if(nError != ERROR_SUCCESS) + { + FreeMPQArchive(ha); + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + SetLastError(nError); + } + + // Return the values + *phMPQ = (HANDLE)ha; + return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Changes locale ID of a file + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) +{ + TMPQFile * hf = (TMPQFile *)hFile; + + // Invalid handle => do nothing + if(IsValidFileHandle(hf) == FALSE || IsValidMpqHandle(hf->ha) == FALSE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + // If the file has not been open for writing, do nothing. + if(hf->ha->pListFile == NULL) + return ERROR_ACCESS_DENIED; + + hf->pHash->lcLocale = (USHORT)lcNewLocale; + hf->ha->dwFlags |= MPQ_FLAG_CHANGED; + return TRUE; +} + +//----------------------------------------------------------------------------- +// Adds a file into the archive + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileAddFileEx(HANDLE hMPQ, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality, int nFileType) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + HANDLE hFile = INVALID_HANDLE_VALUE; + BOOL bReplaced = FALSE; // TRUE if replacing file in the archive + int nError = ERROR_SUCCESS; + + if(nError == ERROR_SUCCESS) + { + // Check valid parameters + if(IsValidMpqHandle(ha) == FALSE || szFileName == NULL || *szFileName == 0 || szArchivedName == NULL || *szArchivedName == 0) + nError = ERROR_INVALID_PARAMETER; + + // Check the values of dwFlags + if((dwFlags & MPQ_FILE_COMPRESS_PKWARE) && (dwFlags & MPQ_FILE_COMPRESS_MULTI)) + nError = ERROR_INVALID_PARAMETER; + } + + // If anyone is trying to add listfile, and the archive already has a listfile, + // deny the operation, but return success. + if(nError == ERROR_SUCCESS) + { + if(ha->pListFile != NULL && !_stricmp(szFileName, LISTFILE_NAME)) + return ERROR_SUCCESS; + } + + // Open added file + if(nError == ERROR_SUCCESS) + { + hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, NULL); + if(hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + if(nError == ERROR_SUCCESS) + nError = AddFileToArchive(ha, hFile, szArchivedName, dwFlags, dwQuality, nFileType, &bReplaced); + + // Add the file into listfile also + if(nError == ERROR_SUCCESS && bReplaced == FALSE) + nError = SListFileAddNode(ha, szArchivedName); + + // Cleanup and exit + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +// Adds a data file into the archive +// TODO: Test for archives > 4GB +BOOL WINAPI SFileAddFile(HANDLE hMPQ, const char * szFileName, const char * szArchivedName, DWORD dwFlags) +{ + return SFileAddFileEx(hMPQ, szFileName, szArchivedName, dwFlags, 0, SFILE_TYPE_DATA); +} + +// Adds a WAVE file into the archive +// TODO: Test for archives > 4GB +BOOL WINAPI SFileAddWave(HANDLE hMPQ, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality) +{ + return SFileAddFileEx(hMPQ, szFileName, szArchivedName, dwFlags, dwQuality, SFILE_TYPE_WAVE); +} + +//----------------------------------------------------------------------------- +// BOOL SFileRemoveFile(HANDLE hMPQ, char * szFileName) +// +// This function removes a file from the archive. The file content +// remains there, only the entries in the hash table and in the block +// table are updated. + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileRemoveFile(HANDLE hMPQ, const char * szFileName, DWORD dwSearchScope) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + TMPQBlockEx * pBlockEx = NULL; // Block entry of deleted file + TMPQBlock * pBlock = NULL; // Block entry of deleted file + TMPQHash * pHash = NULL; // Hash entry of deleted file + DWORD dwBlockIndex = 0; + int nError = ERROR_SUCCESS; + + // Check the parameters + if(nError == ERROR_SUCCESS) + { + if(IsValidMpqHandle(ha) == FALSE) + nError = ERROR_INVALID_PARAMETER; + if(dwSearchScope != SFILE_OPEN_BY_INDEX && *szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + } + + // Do not allow to remove listfile + if(nError == ERROR_SUCCESS) + { + if(dwSearchScope != SFILE_OPEN_BY_INDEX && !_stricmp(szFileName, LISTFILE_NAME)) + nError = ERROR_ACCESS_DENIED; + } + + // Get hash entry belonging to this file + if(nError == ERROR_SUCCESS) + { + if((pHash = GetHashEntryEx(ha, (char *)szFileName, lcLocale)) == NULL) + nError = ERROR_FILE_NOT_FOUND; + } + + // If index was not found, or is greater than number of files, exit. + if(nError == ERROR_SUCCESS) + { + if((dwBlockIndex = pHash->dwBlockIndex) > ha->pHeader->dwBlockTableSize) + nError = ERROR_FILE_NOT_FOUND; + } + + // Get block and test if the file is not already deleted + if(nError == ERROR_SUCCESS) + { + pBlockEx = ha->pExtBlockTable + dwBlockIndex; + pBlock = ha->pBlockTable + dwBlockIndex; + if((pBlock->dwFlags & MPQ_FILE_EXISTS) == 0) + nError = ERROR_FILE_NOT_FOUND; + } + + // Now invalidate the block entry and the hash entry. Do not make any + // relocations and file copying, use SFileCompactArchive for it. + if(nError == ERROR_SUCCESS) + { + pBlockEx->wFilePosHigh = 0; + pBlock->dwFilePos = 0; + pBlock->dwFSize = 0; + pBlock->dwCSize = 0; + pBlock->dwFlags = 0; + pHash->dwName1 = 0xFFFFFFFF; + pHash->dwName2 = 0xFFFFFFFF; + pHash->lcLocale = 0xFFFF; + pHash->wPlatform = 0xFFFF; + pHash->dwBlockIndex = HASH_ENTRY_DELETED; + + // Update MPQ archive + ha->dwFlags |= MPQ_FLAG_CHANGED; + } + + // Remove the file from the list file + if(nError == ERROR_SUCCESS && lcLocale == LANG_NEUTRAL) + nError = SListFileRemoveNode(ha, szFileName); + + // Resolve error and exit + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +// Renames the file within the archive. +// TODO: Test for archives > 4GB +BOOL WINAPI SFileRenameFile(HANDLE hMPQ, const char * szFileName, const char * szNewFileName) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + TMPQHash * pOldHash = NULL; // Hash entry for the original file + TMPQHash * pNewHash = NULL; // Hash entry for the renamed file + DWORD dwBlockIndex = 0; + int nError = ERROR_SUCCESS; + + // Test the valid parameters + if(nError == ERROR_SUCCESS) + { + if(hMPQ == NULL || szNewFileName == NULL || *szNewFileName == 0) + nError = ERROR_INVALID_PARAMETER; + if(szFileName == NULL || *szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + } + + // Do not allow to rename listfile + if(nError == ERROR_SUCCESS) + { + if(!_stricmp(szFileName, LISTFILE_NAME)) + nError = ERROR_ACCESS_DENIED; + } + + // Test if the file already exists in the archive + if(nError == ERROR_SUCCESS) + { + if((pNewHash = GetHashEntryEx(ha, szNewFileName, lcLocale)) != NULL) + nError = ERROR_ALREADY_EXISTS; + } + + // Get the hash table entry for the original file + if(nError == ERROR_SUCCESS) + { + if((pOldHash = GetHashEntryEx(ha, szFileName, lcLocale)) == NULL) + nError = ERROR_FILE_NOT_FOUND; + } + + // Get the hash table entry for the renamed file + if(nError == ERROR_SUCCESS) + { + // Save block table index and remove the hash table entry + dwBlockIndex = pOldHash->dwBlockIndex; + pOldHash->dwName1 = 0xFFFFFFFF; + pOldHash->dwName2 = 0xFFFFFFFF; + pOldHash->lcLocale = 0xFFFF; + pOldHash->wPlatform = 0xFFFF; + pOldHash->dwBlockIndex = HASH_ENTRY_DELETED; + + if((pNewHash = FindFreeHashEntry(ha, szNewFileName)) == NULL) + nError = ERROR_CAN_NOT_COMPLETE; + } + + // Save the block index and clear the hash entry + if(nError == ERROR_SUCCESS) + { + // Copy the block table index + pNewHash->dwBlockIndex = dwBlockIndex; + ha->dwFlags |= MPQ_FLAG_CHANGED; + } + + // Rename the file in the list file + if(nError == ERROR_SUCCESS) + nError = SListFileRenameNode(ha, szFileName, szNewFileName); + + // Resolve error and return + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + diff --git a/src/tools/stuffextract/StormLib/SFileExtractFile.cpp b/src/tools/stuffextract/StormLib/SFileExtractFile.cpp new file mode 100644 index 0000000..c1803ce --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileExtractFile.cpp @@ -0,0 +1,63 @@ +/*****************************************************************************/ +/* SFileExtractFile.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Simple extracting utility */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 20.06.03 1.00 Lad The first version of SFileExtractFile.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted) +{ + HANDLE hLocalFile = INVALID_HANDLE_VALUE; + HANDLE hMpqFile = NULL; + int nError = ERROR_SUCCESS; + + // Create the local file + if(nError == ERROR_SUCCESS) + { + hLocalFile = CreateFile(szExtracted, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if(hLocalFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Open the MPQ file + if(nError == ERROR_SUCCESS) + { + if(!SFileOpenFileEx(hMpq, szToExtract, 0, &hMpqFile)) + nError = GetLastError(); + } + + // Copy the file's content + if(nError == ERROR_SUCCESS) + { + char szBuffer[0x1000]; + DWORD dwTransferred = 1; + + while(dwTransferred > 0) + { + SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL); + if(dwTransferred == 0) + break; + + WriteFile(hLocalFile, szBuffer, dwTransferred, &dwTransferred, NULL); + if(dwTransferred == 0) + break; + } + } + + // Close the files + if(hMpqFile != NULL) + SFileCloseFile(hMpqFile); + if(hLocalFile != INVALID_HANDLE_VALUE) + CloseHandle(hLocalFile); + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (BOOL)(nError == ERROR_SUCCESS); +} diff --git a/src/tools/stuffextract/StormLib/SFileFindFile.cpp b/src/tools/stuffextract/StormLib/SFileFindFile.cpp new file mode 100644 index 0000000..08da7a4 --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileFindFile.cpp @@ -0,0 +1,291 @@ +/*****************************************************************************/ +/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* A module for file searching within MPQs */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +//----------------------------------------------------------------------------- +// Defines + +#define LISTFILE_CACHE_SIZE 0x1000 + +//----------------------------------------------------------------------------- +// Local functions + +static BOOL IsValidSearchHandle(TMPQSearch * hs) +{ + if(hs == NULL || IsBadReadPtr(hs, sizeof(TMPQSearch))) + return FALSE; + + if(!IsValidMpqHandle(hs->ha)) + return FALSE; + + return TRUE; +} + +// This function compares a string with a wildcard search string. +// returns TRUE, when the string matches with the wildcard. +BOOL CheckWildCard(const char * szString, const char * szWildCard) +{ + char * szTemp; // Temporary helper pointer + int nResult = 0; // For memcmp return values + int nMustNotMatch = 0; // Number of following chars int szString, + // which must not match with szWildCard + int nMustMatch = 0; // Number of the following characters, + // which must match + + // When the string is empty, it does not match with every wildcard + if(*szString == 0) + return FALSE; + + // When the mask is empty, it matches to every wildcard + if(szWildCard == NULL || *szWildCard == 0) + return FALSE; + + // Do normal test + for(;;) + { + switch(*szWildCard) + { + case '*': // Means "every number of characters" + // Skip all asterisks + while(*szWildCard == '*') + szWildCard++; + + // When no more characters in wildcard, it means that the strings match + if(*szWildCard == 0) + return TRUE; + + // The next N characters must not agree + nMustNotMatch |= 0x70000000; + break; + + case '?': // Means "One or no character" + while(*szWildCard == '?') + { + nMustNotMatch++; + szWildCard++; + } + break; + + default: + // If the two characters match + if(toupper(*szString) == toupper(*szWildCard)) + { + // When end of string, they agree + if(*szString == 0) + return TRUE; + + nMustNotMatch = 0; + szWildCard++; + szString++; + break; + } + + // If the next character must match, the string does not match + if(nMustNotMatch == 0) + return FALSE; + + // Count the characters which must match after characters + // that must not match + szTemp = (char *)szWildCard; + nMustMatch = 0; + while(*szTemp != 0 && *szTemp != '*' && *szTemp != '?') + { + nMustMatch++; + szTemp++; + } + + // Now skip characters from szString up to number of chars + // that must not match + nResult = -1; + while(nMustNotMatch > 0 && *szString != 0) + { + if((nResult = _strnicmp(szString, szWildCard, nMustMatch)) == 0) + break; + + szString++; + nMustNotMatch--; + } + + // Make one more comparison + if(nMustNotMatch == 0) + nResult = _strnicmp(szString, szWildCard, nMustMatch); + + // If a match has been found, continue the search + if(nResult == 0) + { + nMustNotMatch = 0; + szWildCard += nMustMatch; + szString += nMustMatch; + break; + } + return FALSE; + } + } +} + +// Performs one MPQ search +// TODO: Test for archives > 4GB +static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) +{ + TMPQArchive * ha = hs->ha; + TFileNode * pNode; + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash = ha->pHashTable + hs->dwNextIndex; + + // Do until some file found or no more files + while(pHash < pHashEnd) + { + pNode = ha->pListFile[hs->dwNextIndex++]; + + // If this entry is free, do nothing + if(pHash->dwBlockIndex < HASH_ENTRY_FREE && (DWORD_PTR)pNode < HASH_ENTRY_FREE) + { + // Check the file name. + if(CheckWildCard(pNode->szFileName, hs->szSearchMask)) + { + TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; + + lpFindFileData->lcLocale = pHash->lcLocale; + lpFindFileData->dwFileSize = pBlock->dwFSize; + lpFindFileData->dwFileFlags = pBlock->dwFlags; + lpFindFileData->dwBlockIndex = pHash->dwBlockIndex; + lpFindFileData->dwCompSize = pBlock->dwCSize; + + // Fill the file name and plain file name + strcpy(lpFindFileData->cFileName, pNode->szFileName); + lpFindFileData->szPlainName = strrchr(lpFindFileData->cFileName, '\\'); + if(lpFindFileData->szPlainName == NULL) + lpFindFileData->szPlainName = lpFindFileData->cFileName; + else + lpFindFileData->szPlainName++; + + // Fill the next entry + return ERROR_SUCCESS; + } + } + + pHash++; + } + + // No more files found, return error + return ERROR_NO_MORE_FILES; +} + +// TODO: Test for archives > 4GB +static void FreeMPQSearch(TMPQSearch *& hs) +{ + if(hs != NULL) + { + FREEMEM(hs); + hs = NULL; + } +} + +//----------------------------------------------------------------------------- +// Public functions + +// TODO: Test for archives > 4GB +HANDLE WINAPI SFileFindFirstFile(HANDLE hMPQ, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + TMPQSearch * hs = NULL; // Search object handle + size_t nSize = 0; + int nError = ERROR_SUCCESS; + + // Check for the valid parameters + if(nError == ERROR_SUCCESS) + { + if(!IsValidMpqHandle(ha)) + nError = ERROR_INVALID_PARAMETER; + + if(szMask == NULL || lpFindFileData == NULL) + nError = ERROR_INVALID_PARAMETER; + + if(szListFile == NULL && !IsValidMpqHandle(ha)) + nError = ERROR_INVALID_PARAMETER; + } + + // Include the listfile into the MPQ's internal listfile + // Note that if the listfile name is NULL, do nothing because the + // internal listfile is always included. + if(nError == ERROR_SUCCESS && szListFile != NULL) + nError = SFileAddListFile((HANDLE)ha, szListFile); + + // Allocate the structure for MPQ search + if(nError == ERROR_SUCCESS) + { + nSize = sizeof(TMPQSearch) + strlen(szMask) + 1; + if((hs = (TMPQSearch *)ALLOCMEM(char, nSize)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Perform the first search + if(nError == ERROR_SUCCESS) + { + memset(hs, 0, sizeof(TMPQSearch)); + hs->ha = ha; + hs->dwNextIndex = 0; + strcpy(hs->szSearchMask, szMask); + nError = DoMPQSearch(hs, lpFindFileData); + } + + // Cleanup + if(nError != ERROR_SUCCESS) + { + FreeMPQSearch(hs); + SetLastError(nError); + } + + // Return the result value + return (HANDLE)hs; +} + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) +{ + TMPQSearch * hs = (TMPQSearch *)hFind; + int nError = ERROR_SUCCESS; + + // Check the parameters + if(nError == ERROR_SUCCESS) + { + if(!IsValidSearchHandle(hs) || lpFindFileData == NULL) + nError = ERROR_INVALID_PARAMETER; + } + + if(nError == ERROR_SUCCESS) + nError = DoMPQSearch(hs, lpFindFileData); + + if(nError != ERROR_SUCCESS) + { + SetLastError(nError); + return FALSE; + } + return TRUE; +} + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileFindClose(HANDLE hFind) +{ + TMPQSearch * hs = (TMPQSearch *)hFind; + + // Check the parameters + if(!IsValidSearchHandle(hs)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + FreeMPQSearch(hs); + return TRUE; +} diff --git a/src/tools/stuffextract/StormLib/SFileOpenArchive.cpp b/src/tools/stuffextract/StormLib/SFileOpenArchive.cpp new file mode 100644 index 0000000..842c71a --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileOpenArchive.cpp @@ -0,0 +1,497 @@ +/*****************************************************************************/ +/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */ +/* */ +/* Author : Ladislav Zezula */ +/* E-mail : ladik@zezula.net */ +/* WWW : www.zezula.net */ +/*---------------------------------------------------------------------------*/ +/* Archive functions of Storm.dll */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */ +/* 19.11.03 1.01 Dan Big endian handling */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +/*****************************************************************************/ +/* Local functions */ +/*****************************************************************************/ + +static BOOL IsAviFile(TMPQHeader * pHeader) +{ + DWORD * AviHdr = (DWORD *)pHeader; + + // Test for 'RIFF', 'AVI ' or 'LIST' + return (AviHdr[0] == 'FFIR' && AviHdr[2] == ' IVA' && AviHdr[3] == 'TSIL'); +} + +// This function gets the right positions of the hash table and the block table. +// TODO: Test for archives > 4GB +static int RelocateMpqTablePositions(TMPQArchive * ha) +{ + TMPQHeader2 * pHeader = ha->pHeader; + LARGE_INTEGER FileSize; + LARGE_INTEGER TempSize; + + // Get the size of the file + FileSize.LowPart = GetFileSize(ha->hFile, (LPDWORD)&FileSize.HighPart); + + // Set the proper hash table position + ha->HashTablePos.HighPart = pHeader->wHashTablePosHigh; + ha->HashTablePos.LowPart = pHeader->dwHashTablePos; + ha->HashTablePos.QuadPart += ha->MpqPos.QuadPart; + if(ha->HashTablePos.QuadPart > FileSize.QuadPart) + return ERROR_BAD_FORMAT; + + // Set the proper block table position + ha->BlockTablePos.HighPart = pHeader->wBlockTablePosHigh; + ha->BlockTablePos.LowPart = pHeader->dwBlockTablePos; + ha->BlockTablePos.QuadPart += ha->MpqPos.QuadPart; + if(ha->BlockTablePos.QuadPart > FileSize.QuadPart) + return ERROR_BAD_FORMAT; + + // Set the proper position of the extended block table + if(pHeader->ExtBlockTablePos.QuadPart != 0) + { + ha->ExtBlockTablePos = pHeader->ExtBlockTablePos; + ha->ExtBlockTablePos.QuadPart += ha->MpqPos.QuadPart; + if(ha->ExtBlockTablePos.QuadPart > FileSize.QuadPart) + return ERROR_BAD_FORMAT; + } + + // Size of MPQ archive is computed as the biggest of + // (EndOfBlockTable, EndOfHashTable, EndOfExtBlockTable) + TempSize.QuadPart = ha->HashTablePos.QuadPart + (pHeader->dwHashTableSize * sizeof(TMPQHash)); + if(TempSize.QuadPart > ha->MpqSize.QuadPart) + ha->MpqSize = TempSize; + TempSize.QuadPart = ha->BlockTablePos.QuadPart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + if(TempSize.QuadPart > ha->MpqSize.QuadPart) + ha->MpqSize = TempSize; + TempSize.QuadPart = ha->ExtBlockTablePos.QuadPart + (pHeader->dwBlockTableSize * sizeof(TMPQBlockEx)); + if(TempSize.QuadPart > ha->MpqSize.QuadPart) + ha->MpqSize = TempSize; + + // MPQ size does not include the bytes before MPQ header + ha->MpqSize.QuadPart -= ha->MpqPos.QuadPart; + return ERROR_SUCCESS; +} + + +/*****************************************************************************/ +/* Public functions */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// SFileGetLocale and SFileSetLocale +// Set the locale for all neewly opened archives and files + +LCID WINAPI SFileGetLocale() +{ + return lcLocale; +} + +LCID WINAPI SFileSetLocale(LCID lcNewLocale) +{ + lcLocale = lcNewLocale; + return lcLocale; +} + +//----------------------------------------------------------------------------- +// SFileOpenArchiveEx (not a public function !!!) +// +// szFileName - MPQ archive file name to open +// dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives +// dwFlags - If contains MPQ_OPEN_NO_LISTFILE, then the internal list file will not be used. +// phMPQ - Pointer to store open archive handle + +BOOL SFileOpenArchiveEx( + const char * szMpqName, + DWORD dwPriority, + DWORD dwFlags, + HANDLE * phMPQ, + DWORD dwAccessMode) +{ + LARGE_INTEGER TempPos; + TMPQArchive * ha = NULL; // Archive handle + HANDLE hFile = INVALID_HANDLE_VALUE;// Opened archive file handle + DWORD dwMaxBlockIndex = 0; // Maximum value of block entry + DWORD dwBlockTableSize = 0; // Block table size. + DWORD dwTransferred; // Number of bytes read + DWORD dwBytes = 0; // Number of bytes to read + int nError = ERROR_SUCCESS; + + // Check the right parameters + if(nError == ERROR_SUCCESS) + { + if(szMpqName == NULL || *szMpqName == 0 || phMPQ == NULL) + nError = ERROR_INVALID_PARAMETER; + } + + // Ensure that StormBuffer is allocated + if(nError == ERROR_SUCCESS) + nError = PrepareStormBuffer(); + + // Open the MPQ archive file + if(nError == ERROR_SUCCESS) + { + hFile = CreateFile(szMpqName, dwAccessMode, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Allocate the MPQhandle + if(nError == ERROR_SUCCESS) + { + if((ha = ALLOCMEM(TMPQArchive, 1)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Initialize handle structure and allocate structure for MPQ header + if(nError == ERROR_SUCCESS) + { + memset(ha, 0, sizeof(TMPQArchive)); + strncpy(ha->szFileName, szMpqName, strlen(szMpqName)); + ha->hFile = hFile; + ha->dwPriority = dwPriority; + ha->pHeader = &ha->Header; + ha->pListFile = NULL; + hFile = INVALID_HANDLE_VALUE; + } + + // Find the offset of MPQ header within the file + if(nError == ERROR_SUCCESS) + { + LARGE_INTEGER SearchPos = {0}; + LARGE_INTEGER MpqPos = {0}; + DWORD dwHeaderID; + + for(;;) + { + // Invalidate the MPQ ID and read the eventual header + SetFilePointer(ha->hFile, MpqPos.LowPart, &MpqPos.HighPart, FILE_BEGIN); + ReadFile(ha->hFile, ha->pHeader, sizeof(TMPQHeader2), &dwTransferred, NULL); + dwHeaderID = BSWAP_INT32_UNSIGNED(ha->pHeader->dwID); + + // Special check : Some MPQs are actually AVI files, only with + // changed extension. + if(MpqPos.QuadPart == 0 && IsAviFile(ha->pHeader)) + { + nError = ERROR_AVI_FILE; + break; + } + + // If different number of bytes read, break the loop + if(dwTransferred != sizeof(TMPQHeader2)) + { + nError = ERROR_BAD_FORMAT; + break; + } + + // If there is the MPQ shunt signature, process it + if(dwHeaderID == ID_MPQ_SHUNT && ha->pShunt == NULL) + { + // Fill the shunt header + ha->ShuntPos = MpqPos; + ha->pShunt = &ha->Shunt; + memcpy(ha->pShunt, ha->pHeader, sizeof(TMPQShunt)); + BSWAP_TMPQSHUNT(ha->pShunt); + + // Set the MPQ pos and repeat the search + MpqPos.QuadPart = SearchPos.QuadPart + ha->pShunt->dwHeaderPos; + continue; + } + + // There must be MPQ header signature + if(dwHeaderID == ID_MPQ) + { + BSWAP_TMPQHEADER(ha->pHeader); + + // Save the position where the MPQ header has been found + ha->MpqPos = MpqPos; + + // If valid signature has been found, break the loop + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) + { + // W3M Map Protectors set some garbage value into the "dwHeaderSize" + // field of MPQ header. This value is apparently ignored by Storm.dll + if(ha->pHeader->dwHeaderSize != sizeof(TMPQHeader) && + ha->pHeader->dwHeaderSize != sizeof(TMPQHeader2)) + { + ha->dwFlags |= MPQ_FLAG_PROTECTED; + ha->pHeader->dwHeaderSize = sizeof(TMPQHeader); + } + + if(ha->pHeader->dwHashTablePos < ha->pHeader->dwArchiveSize && + ha->pHeader->dwBlockTablePos < ha->pHeader->dwArchiveSize) + { + break; + } + } + + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_2) + { + break; + } + + nError = ERROR_NOT_SUPPORTED; + break; + } + + // If a MPQ shunt already has been found, + // and no MPQ header was at potision pointed by the shunt, + // then the archive is corrupt + if(ha->pShunt != NULL) + { + nError = ERROR_BAD_FORMAT; + break; + } + + // Move to the next possible offset + SearchPos.QuadPart += 0x200; + MpqPos = SearchPos; + } + } + + // Relocate tables position + if(nError == ERROR_SUCCESS) + { + // Clear the fields not supported in older formats + if(ha->pHeader->wFormatVersion < MPQ_FORMAT_VERSION_2) + { + ha->pHeader->ExtBlockTablePos.QuadPart = 0; + ha->pHeader->wBlockTablePosHigh = 0; + ha->pHeader->wHashTablePosHigh = 0; + } + + ha->dwBlockSize = (0x200 << ha->pHeader->wBlockSize); + nError = RelocateMpqTablePositions(ha); + } + + // Allocate buffers + if(nError == ERROR_SUCCESS) + { + // + // Note that the block table should be as large as the hash table + // (For later file additions). + // + // I have found a MPQ which has the block table larger than + // the hash table. We should avoid buffer overruns caused by that. + // + dwBlockTableSize = max(ha->pHeader->dwHashTableSize, ha->pHeader->dwBlockTableSize); + + ha->pHashTable = ALLOCMEM(TMPQHash, ha->pHeader->dwHashTableSize); + ha->pBlockTable = ALLOCMEM(TMPQBlock, dwBlockTableSize); + ha->pExtBlockTable = ALLOCMEM(TMPQBlockEx, dwBlockTableSize); + ha->pbBlockBuffer = ALLOCMEM(BYTE, ha->dwBlockSize); + + if(!ha->pHashTable || !ha->pBlockTable || !ha->pExtBlockTable || !ha->pbBlockBuffer) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Read the hash table into memory + if(nError == ERROR_SUCCESS) + { + dwBytes = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + SetFilePointer(ha->hFile, ha->HashTablePos.LowPart, &ha->HashTablePos.HighPart, FILE_BEGIN); + ReadFile(ha->hFile, ha->pHashTable, dwBytes, &dwTransferred, NULL); + + if(dwTransferred != dwBytes) + nError = ERROR_FILE_CORRUPT; + } + + // Decrypt hash table and check if it is correctly decrypted + if(nError == ERROR_SUCCESS) + { + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash; + + // We have to convert the hash table from LittleEndian + BSWAP_ARRAY32_UNSIGNED((DWORD *)ha->pHashTable, (dwBytes / sizeof(DWORD))); + DecryptHashTable((DWORD *)ha->pHashTable, (BYTE *)"(hash table)", (ha->pHeader->dwHashTableSize * 4)); + + // Check hash table if is correctly decrypted + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + // Note: Some MPQs from World of Warcraft have wPlatform set to 0x0100. + + // If not free or deleted hash entry, check for valid values + if(pHash->dwBlockIndex < HASH_ENTRY_DELETED) + { + // The block index should not be larger than size of the block table + if(pHash->dwBlockIndex > ha->pHeader->dwBlockTableSize) + { + nError = ERROR_BAD_FORMAT; + break; + } + + // Remember the highest block table entry + if(pHash->dwBlockIndex > dwMaxBlockIndex) + dwMaxBlockIndex = pHash->dwBlockIndex; + } + } + } + + // Now, read the block table + if(nError == ERROR_SUCCESS) + { + memset(ha->pBlockTable, 0, dwBlockTableSize * sizeof(TMPQBlock)); + + dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + SetFilePointer(ha->hFile, ha->BlockTablePos.LowPart, &ha->BlockTablePos.HighPart, FILE_BEGIN); + ReadFile(ha->hFile, ha->pBlockTable, dwBytes, &dwTransferred, NULL); + + // We have to convert every DWORD in ha->block from LittleEndian + BSWAP_ARRAY32_UNSIGNED((DWORD *)ha->pBlockTable, dwBytes / sizeof(DWORD)); + + if(dwTransferred != dwBytes) + nError = ERROR_FILE_CORRUPT; + } + + // Decrypt block table. + // Some MPQs don't have Decrypted block table, e.g. cracked Diablo version + // We have to check if block table is really encrypted + if(nError == ERROR_SUCCESS) + { + TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; + TMPQBlock * pBlock = ha->pBlockTable; + BOOL bBlockTableEncrypted = FALSE; + + // Verify all blocks entries in the table + // The loop usually stops at the first entry + while(pBlock < pBlockEnd) + { + // The lower 8 bits of the MPQ flags are always zero. + // Note that this may change in next MPQ versions + if(pBlock->dwFlags & 0x000000FF) + { + bBlockTableEncrypted = TRUE; + break; + } + + // Move to the next block table entry + pBlock++; + } + + if(bBlockTableEncrypted) + { + DecryptBlockTable((DWORD *)ha->pBlockTable, + (BYTE *)"(block table)", + (ha->pHeader->dwBlockTableSize * 4)); + } + } + + // Now, read the extended block table. + // For V1 archives, we still will maintain the extended block table + // (it will be filled with zeros) + // TODO: Test with >4GB + if(nError == ERROR_SUCCESS) + { + memset(ha->pExtBlockTable, 0, dwBlockTableSize * sizeof(TMPQBlockEx)); + + if(ha->pHeader->ExtBlockTablePos.QuadPart != 0) + { + dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); + SetFilePointer(ha->hFile, + ha->ExtBlockTablePos.LowPart, + &ha->ExtBlockTablePos.HighPart, + FILE_BEGIN); + ReadFile(ha->hFile, ha->pExtBlockTable, dwBytes, &dwTransferred, NULL); + + // We have to convert every DWORD in ha->block from LittleEndian + BSWAP_ARRAY16_UNSIGNED((USHORT *)ha->pExtBlockTable, dwBytes / sizeof(USHORT)); + + // The extended block table is not encrypted (so far) + if(dwTransferred != dwBytes) + nError = ERROR_FILE_CORRUPT; + } + } + + // Verify the both block tables (If the MPQ file is not protected) + if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0) + { + TMPQBlockEx * pBlockEx = ha->pExtBlockTable; + TMPQBlock * pBlockEnd = ha->pBlockTable + dwMaxBlockIndex + 1; + TMPQBlock * pBlock = ha->pBlockTable; + + // If the MPQ file is not protected, + // we will check if all sizes in the block table is correct. + // Note that we will not relocate the block table (change from previous versions) + for(; pBlock < pBlockEnd; pBlock++, pBlockEx++) + { + if(pBlock->dwFlags & MPQ_FILE_EXISTS) + { + // Get the 64-bit file position + TempPos.HighPart = pBlockEx->wFilePosHigh; + TempPos.LowPart = pBlock->dwFilePos; + + if(TempPos.QuadPart > ha->MpqSize.QuadPart || pBlock->dwCSize > ha->MpqSize.QuadPart) + { + nError = ERROR_BAD_FORMAT; + break; + } + } + } + } + + // If the user didn't specified otherwise, + // include the internal listfile to the TMPQArchive structure + if((dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) + { + if(nError == ERROR_SUCCESS) + SListFileCreateListFile(ha); + + // Add the internal listfile + if(nError == ERROR_SUCCESS) + SFileAddListFile((HANDLE)ha, NULL); + } + + // Cleanup and exit + if(nError != ERROR_SUCCESS) + { + FreeMPQArchive(ha); + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + SetLastError(nError); + } + else + { + if(pFirstOpen == NULL) + pFirstOpen = ha; + } + *phMPQ = ha; + return (nError == ERROR_SUCCESS); +} + +BOOL WINAPI SFileOpenArchive(const char * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMPQ) +{ + return SFileOpenArchiveEx(szMpqName, dwPriority, dwFlags, phMPQ, GENERIC_READ); +} + +//----------------------------------------------------------------------------- +// BOOL SFileCloseArchive(HANDLE hMPQ); +// + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileCloseArchive(HANDLE hMPQ) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + + if(!IsValidMpqHandle(ha)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if(ha->dwFlags & MPQ_FLAG_CHANGED) + { + SListFileSaveToMpq(ha); + SaveMPQTables(ha); + } + FreeMPQArchive(ha); + return TRUE; +} + diff --git a/src/tools/stuffextract/StormLib/SFileOpenFileEx.cpp b/src/tools/stuffextract/StormLib/SFileOpenFileEx.cpp new file mode 100644 index 0000000..cc9a7d7 --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileOpenFileEx.cpp @@ -0,0 +1,403 @@ +/*****************************************************************************/ +/* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description : */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +/*****************************************************************************/ +/* Local functions */ +/*****************************************************************************/ + +// TODO: Test for archives > 4GB +static BOOL OpenLocalFile(const char * szFileName, HANDLE * phFile) +{ + TMPQFile * hf = NULL; + HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if(hFile != INVALID_HANDLE_VALUE) + { + // Allocate and initialize file handle + size_t nHandleSize = sizeof(TMPQFile) + strlen(szFileName); + if((hf = (TMPQFile *)ALLOCMEM(char, nHandleSize)) != NULL) + { + memset(hf, 0, nHandleSize); + strcpy(hf->szFileName, szFileName); + hf->hFile = hFile; + *phFile = hf; + return TRUE; + } + else + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } + *phFile = NULL; + return FALSE; +} + +// TODO: Test for archives > 4GB +static void FreeMPQFile(TMPQFile *& hf) +{ + if(hf != NULL) + { + if(hf->hFile != INVALID_HANDLE_VALUE) + CloseHandle(hf->hFile); + if(hf->pdwBlockPos != NULL) + FREEMEM(hf->pdwBlockPos); + if(hf->pbFileBuffer != NULL) + FREEMEM(hf->pbFileBuffer); + FREEMEM(hf); + hf = NULL; + } +} + +/*****************************************************************************/ +/* Public functions */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// SFileEnumLocales enums all locale versions within MPQ. +// Functions fills all available language identifiers on a file into the buffer +// pointed by plcLocales. There must be enough entries to copy the localed, +// otherwise the function returns ERROR_INSUFFICIENT_BUFFER. + +// TODO: Test for archives > 4GB +int WINAPI SFileEnumLocales( + HANDLE hMPQ, + const char * szFileName, + LCID * plcLocales, + DWORD * pdwMaxLocales, + DWORD dwSearchScope) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + TMPQHash * pHash = NULL; + TMPQHash * pHashEnd = NULL; + DWORD dwLocales = 0; + int nError = ERROR_SUCCESS; + + // Test the parameters + if(nError == ERROR_SUCCESS) + { + if(!IsValidMpqHandle(ha) || pdwMaxLocales == NULL) + nError = ERROR_INVALID_PARAMETER; + if(dwSearchScope == SFILE_OPEN_BY_INDEX && (DWORD_PTR)szFileName > ha->pHeader->dwBlockTableSize) + nError = ERROR_INVALID_PARAMETER; + if(dwSearchScope != SFILE_OPEN_BY_INDEX && *szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + } + + // Retrieve the hash entry for the required file + if(nError == ERROR_SUCCESS) + { + pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + + if(dwSearchScope == SFILE_OPEN_BY_INDEX) + { + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex == (DWORD_PTR)szFileName) + break; + } + if(pHash == pHashEnd) + pHash = NULL; + } + else + pHash = GetHashEntry(ha, szFileName); + } + + // If the file was not found, sorry + if(nError == ERROR_SUCCESS) + { + if(pHash == NULL) + nError = ERROR_FILE_NOT_FOUND; + } + + // Count the entries which correspond to the same file name + if(nError == ERROR_SUCCESS) + { + TMPQHash * pSaveHash = pHash; + DWORD dwName1 = pHash->dwName1; + DWORD dwName2 = pHash->dwName2; + + // For nameless access, return 1 locale always + if(dwSearchScope == SFILE_OPEN_BY_INDEX) + dwLocales++; + else + { + while(pHash < pHashEnd && pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2) + { + dwLocales++; + pHash++; + } + } + + pHash = pSaveHash; + } + + // Test if there is enough space to copy the locales + if(nError == ERROR_SUCCESS) + { + DWORD dwMaxLocales = *pdwMaxLocales; + + *pdwMaxLocales = dwLocales; + if(dwMaxLocales < dwLocales) + nError = ERROR_INSUFFICIENT_BUFFER; + } + + // Fill all the locales + if(nError == ERROR_SUCCESS) + { + for(DWORD i = 0; i < dwLocales; i++, pHash++) + *plcLocales++ = (LCID)pHash->lcLocale; + } + return nError; +} + +//----------------------------------------------------------------------------- +// SFileHasFile +// +// hMPQ - Handle of opened MPQ archive +// szFileName - Name of file to look for + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileHasFile(HANDLE hMPQ, char * szFileName) +{ + TMPQArchive * ha = (TMPQArchive *)hMPQ; + int nError = ERROR_SUCCESS; + + if(nError == ERROR_SUCCESS) + { + if(ha == NULL) + nError = ERROR_INVALID_PARAMETER; + if(*szFileName == 0) + nError = ERROR_INVALID_PARAMETER; + } + + // Prepare the file opening + if(nError == ERROR_SUCCESS) + { + if(GetHashEntryEx(ha, szFileName, lcLocale) == NULL) + { + nError = ERROR_FILE_NOT_FOUND; + } + } + + // Cleanup + if(nError != ERROR_SUCCESS) + { + SetLastError(nError); + } + + return (nError == ERROR_SUCCESS); +} + + +//----------------------------------------------------------------------------- +// SFileOpenFileEx +// +// hMPQ - Handle of opened MPQ archive +// szFileName - Name of file to open +// dwSearchScope - Where to search +// phFile - Pointer to store opened file handle + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileOpenFileEx(HANDLE hMPQ, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) +{ + LARGE_INTEGER FilePos; + TMPQArchive * ha = (TMPQArchive *)hMPQ; + TMPQFile * hf = NULL; + TMPQHash * pHash = NULL; // Hash table index + TMPQBlock * pBlock = NULL; // File block + TMPQBlockEx * pBlockEx = NULL; + DWORD dwHashIndex = 0; // Hash table index + DWORD dwBlockIndex = (DWORD)-1; // Found table index + size_t nHandleSize = 0; // Memory space necessary to allocate TMPQHandle + int nError = ERROR_SUCCESS; + +#ifdef _DEBUG + // Due to increasing numbers of files in MPQs, I had to change the behavior + // of opening by file index. Now, the SFILE_OPEN_BY_INDEX value of dwSearchScope + // must be entered. This check will allow to find code places that are incompatible + // with the new behavior. + if(dwSearchScope != SFILE_OPEN_BY_INDEX && szFileName != NULL) + { + assert((DWORD_PTR)szFileName > 0x10000); + } +#endif + + if(nError == ERROR_SUCCESS) + { + if(ha == NULL && dwSearchScope == SFILE_OPEN_FROM_MPQ) + nError = ERROR_INVALID_PARAMETER; + if(phFile == NULL) + nError = ERROR_INVALID_PARAMETER; + if(dwSearchScope == SFILE_OPEN_BY_INDEX && (DWORD_PTR)szFileName > ha->pHeader->dwBlockTableSize) + nError = ERROR_INVALID_PARAMETER; + if(dwSearchScope != SFILE_OPEN_BY_INDEX && (szFileName == NULL || *szFileName == 0)) + nError = ERROR_INVALID_PARAMETER; + } + + // Prepare the file opening + if(nError == ERROR_SUCCESS) + { + // When the file is given by number, ... + if(dwSearchScope == SFILE_OPEN_BY_INDEX) + { + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + + // Set handle size to be sizeof(TMPQFile) + length of FileXXXXXXXX.xxx + nHandleSize = sizeof(TMPQFile) + 20; + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if((DWORD_PTR)szFileName == pHash->dwBlockIndex) + { + dwHashIndex = (DWORD)(pHash - ha->pHashTable); + dwBlockIndex = pHash->dwBlockIndex; + break; + } + } + } + else + { + // If we have to open a disk file + if(dwSearchScope == SFILE_OPEN_LOCAL_FILE) + return OpenLocalFile(szFileName, phFile); + + nHandleSize = sizeof(TMPQFile) + strlen(szFileName); + if((pHash = GetHashEntryEx(ha, szFileName, lcLocale)) != NULL) + { + dwHashIndex = (DWORD)(pHash - ha->pHashTable); + dwBlockIndex = pHash->dwBlockIndex; + } + } + } + + // Get block index from file name and test it + if(nError == ERROR_SUCCESS) + { + // If index was not found, or is greater than number of files, exit. + if(dwBlockIndex == (DWORD)-1 || dwBlockIndex > ha->pHeader->dwBlockTableSize) + nError = ERROR_FILE_NOT_FOUND; + } + + // Get block and test if the file was not already deleted. + if(nError == ERROR_SUCCESS) + { + // Get both block tables and file position + pBlockEx = ha->pExtBlockTable + dwBlockIndex; + pBlock = ha->pBlockTable + dwBlockIndex; + FilePos.HighPart = pBlockEx->wFilePosHigh; + FilePos.LowPart = pBlock->dwFilePos; + + if(FilePos.QuadPart > ha->MpqSize.QuadPart || + pBlock->dwCSize > ha->MpqSize.QuadPart) + nError = ERROR_FILE_CORRUPT; + if((pBlock->dwFlags & MPQ_FILE_EXISTS) == 0) + nError = ERROR_FILE_NOT_FOUND; + if(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) + nError = ERROR_NOT_SUPPORTED; + } + + // Allocate file handle + if(nError == ERROR_SUCCESS) + { + if((hf = (TMPQFile *)ALLOCMEM(char, nHandleSize)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Initialize file handle + if(nError == ERROR_SUCCESS) + { + memset(hf, 0, nHandleSize); + hf->hFile = INVALID_HANDLE_VALUE; + hf->ha = ha; + hf->pBlockEx = pBlockEx; + hf->pBlock = pBlock; + hf->nBlocks = (hf->pBlock->dwFSize + ha->dwBlockSize - 1) / ha->dwBlockSize; + hf->pHash = pHash; + + hf->MpqFilePos.HighPart = pBlockEx->wFilePosHigh; + hf->MpqFilePos.LowPart = pBlock->dwFilePos; + hf->MpqFilePos.QuadPart += ha->MpqPos.QuadPart; + + hf->dwHashIndex = dwHashIndex; + hf->dwFileIndex = dwBlockIndex; + + // Allocate buffers for decompression. + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) + { + // Allocate buffer for block positions. At the begin of file are stored + // DWORDs holding positions of each block relative from begin of file in the archive + // As for newer MPQs, there may be one additional entry in the block table + // (if the MPQ_FILE_HAS_EXTRA flag is set). + // Allocate the buffer to include this DWORD as well + + if((hf->pdwBlockPos = ALLOCMEM(DWORD, hf->nBlocks + 2)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Decrypt file seed. Cannot be used if the file is given by index + if(dwSearchScope != SFILE_OPEN_BY_INDEX) + { + if(hf->pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + const char * szTemp = strrchr(szFileName, '\\'); + + strcpy(hf->szFileName, szFileName); + if(szTemp != NULL) + szFileName = szTemp + 1; + hf->dwSeed1 = DecryptFileSeed((char *)szFileName); + + if(hf->pBlock->dwFlags & MPQ_FILE_FIXSEED) + { + hf->dwSeed1 = (hf->dwSeed1 + hf->pBlock->dwFilePos) ^ hf->pBlock->dwFSize; + } + } + } + else + { + // If the file is encrypted and not compressed, we cannot detect the file seed + if(SFileGetFileName(hf, hf->szFileName) == FALSE) + nError = GetLastError(); + } + } + + // Cleanup + if(nError != ERROR_SUCCESS) + { + FreeMPQFile(hf); + SetLastError(nError); + } + + *phFile = hf; + return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// BOOL SFileCloseFile(HANDLE hFile); + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileCloseFile(HANDLE hFile) +{ + TMPQFile * hf = (TMPQFile *)hFile; + + if(!IsValidFileHandle(hf)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + // Set the last accessed file in the archive + if(hf->ha != NULL) + hf->ha->pLastFile = NULL; + + // Free the structure + FreeMPQFile(hf); + return TRUE; +} diff --git a/src/tools/stuffextract/StormLib/SFileReadFile.cpp b/src/tools/stuffextract/StormLib/SFileReadFile.cpp new file mode 100644 index 0000000..396da83 --- /dev/null +++ b/src/tools/stuffextract/StormLib/SFileReadFile.cpp @@ -0,0 +1,826 @@ +/*****************************************************************************/ +/* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description : */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.99 1.00 Lad The first version of SFileReadFile.cpp */ +/* 24.03.99 1.00 Lad Added the SFileGetFileInfo function */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" + +//----------------------------------------------------------------------------- +// Defines + +#define ID_WAVE 0x46464952 // Signature of WAVes for name breaking +#define ID_EXE 0x00005A4D // Signature of executable files + +//----------------------------------------------------------------------------- +// Local structures + +struct TID2Ext +{ + DWORD dwID; + char * szExt; +}; + +//----------------------------------------------------------------------------- +// ReadMPQBlock +// +// hf - MPQ File handle. +// dwBlockPos - Position of block in the file (relative to file begin) +// buffer - Pointer to target buffer to store blocks. +// dwBlockSize - Number of bytes to read. Must be multiplier of block size. +// +// Returns number of bytes read. + +// TODO: Test for archives > 4GB +static DWORD WINAPI ReadMPQBlocks(TMPQFile * hf, DWORD dwBlockPos, BYTE * buffer, DWORD blockBytes) +{ + LARGE_INTEGER FilePos; + TMPQArchive * ha = hf->ha; // Archive handle + BYTE * tempBuffer = NULL; // Buffer for reading compressed data from the file + DWORD dwFilePos = dwBlockPos; // Reading position from the file + DWORD dwToRead; // Number of bytes to read + DWORD blockNum; // Block number (needed for decrypt) + DWORD dwBytesRead = 0; // Total number of bytes read + DWORD bytesRemain = 0; // Number of data bytes remaining up to the end of the file + DWORD nBlocks; // Number of blocks to load + DWORD i; + + // Test parameters. Block position and block size must be block-aligned, block size nonzero + if((dwBlockPos & (ha->dwBlockSize - 1)) || blockBytes == 0) + return 0; + + // Check the end of file + if((dwBlockPos + blockBytes) > hf->pBlock->dwFSize) + blockBytes = hf->pBlock->dwFSize - dwBlockPos; + + bytesRemain = hf->pBlock->dwFSize - dwBlockPos; + blockNum = dwBlockPos / ha->dwBlockSize; + nBlocks = blockBytes / ha->dwBlockSize; + if(blockBytes % ha->dwBlockSize) + nBlocks++; + + // If file has variable block positions, we have to load them + if((hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) && hf->bBlockPosLoaded == FALSE) + { + // Move file pointer to the begin of the file in the MPQ + if(hf->MpqFilePos.QuadPart != ha->FilePointer.QuadPart) + { + SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN); + } + + // Read block positions from begin of file. + dwToRead = (hf->nBlocks+1) * sizeof(DWORD); + if(hf->pBlock->dwFlags & MPQ_FILE_HAS_EXTRA) + dwToRead += sizeof(DWORD); + + // Read the block pos table and convert the buffer to little endian + ReadFile(ha->hFile, hf->pdwBlockPos, dwToRead, &dwBytesRead, NULL); + BSWAP_ARRAY32_UNSIGNED(hf->pdwBlockPos, (hf->nBlocks+1)); + + // + // If the archive if protected some way, perform additional check + // Sometimes, the file appears not to be encrypted, but it is. + // + // Note: In WoW 1.10+, there's a new flag. With this flag present, + // there's one additional entry in the block table. + // + + if(hf->pdwBlockPos[0] != dwBytesRead) + hf->pBlock->dwFlags |= MPQ_FILE_ENCRYPTED; + + // Decrypt loaded block positions if necessary + if(hf->pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + // If we don't know the file seed, try to find it. + if(hf->dwSeed1 == 0) + hf->dwSeed1 = DetectFileSeed(hf->pdwBlockPos, dwBytesRead); + + // If we don't know the file seed, sorry but we cannot extract the file. + if(hf->dwSeed1 == 0) + return 0; + + // Decrypt block positions + DecryptMPQBlock(hf->pdwBlockPos, dwBytesRead, hf->dwSeed1 - 1); + + // Check if the block positions are correctly decrypted + // I don't know why, but sometimes it will result invalid block positions on some files + if(hf->pdwBlockPos[0] != dwBytesRead) + { + // Try once again to detect file seed and decrypt the blocks + // TODO: Test with >4GB + SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN); + ReadFile(ha->hFile, hf->pdwBlockPos, dwToRead, &dwBytesRead, NULL); + + BSWAP_ARRAY32_UNSIGNED(hf->pdwBlockPos, (hf->nBlocks+1)); + hf->dwSeed1 = DetectFileSeed(hf->pdwBlockPos, dwBytesRead); + DecryptMPQBlock(hf->pdwBlockPos, dwBytesRead, hf->dwSeed1 - 1); + + // Check if the block positions are correctly decrypted + if(hf->pdwBlockPos[0] != dwBytesRead) + return 0; + } + } + + // Update hf's variables + ha->FilePointer.QuadPart = hf->MpqFilePos.QuadPart + dwBytesRead; + hf->bBlockPosLoaded = TRUE; + } + + // Get file position and number of bytes to read + dwFilePos = dwBlockPos; + dwToRead = blockBytes; + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) + { + dwFilePos = hf->pdwBlockPos[blockNum]; + dwToRead = hf->pdwBlockPos[blockNum + nBlocks] - dwFilePos; + } + FilePos.QuadPart = hf->MpqFilePos.QuadPart + dwFilePos; + + // Get work buffer for store read data + tempBuffer = buffer; + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) + { + if((tempBuffer = ALLOCMEM(BYTE, dwToRead)) == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + + // Set file pointer, if necessary + if(ha->FilePointer.QuadPart != FilePos.QuadPart) + { + SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN); + } + + // 15018F87 : Read all requested blocks + ReadFile(ha->hFile, tempBuffer, dwToRead, &dwBytesRead, NULL); + ha->FilePointer.QuadPart = FilePos.QuadPart + dwBytesRead; + + // Block processing part. + DWORD blockStart = 0; // Index of block start in work buffer + DWORD blockSize = min(blockBytes, ha->dwBlockSize); + DWORD index = blockNum; // Current block index + + dwBytesRead = 0; // Clear read byte counter + + // Walk through all blocks + for(i = 0; i < nBlocks; i++, index++) + { + BYTE * inputBuffer = tempBuffer + blockStart; + int outLength = ha->dwBlockSize; + + if(bytesRemain < (DWORD)outLength) + outLength = bytesRemain; + + // Get current block length + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) + blockSize = hf->pdwBlockPos[index+1] - hf->pdwBlockPos[index]; + + // If block is encrypted, we have to decrypt it. + if(hf->pBlock->dwFlags & MPQ_FILE_ENCRYPTED) + { + BSWAP_ARRAY32_UNSIGNED((DWORD *)inputBuffer, blockSize / sizeof(DWORD)); + + // If we don't know the seed, try to decode it as WAVE file + if(hf->dwSeed1 == 0) + hf->dwSeed1 = DetectFileSeed2((DWORD *)inputBuffer, 3, ID_WAVE, hf->pBlock->dwFSize - 8, 0x45564157); + + // Let's try MSVC's standard EXE or header + if(hf->dwSeed1 == 0) + hf->dwSeed1 = DetectFileSeed2((DWORD *)inputBuffer, 2, 0x00905A4D, 0x00000003); + + if(hf->dwSeed1 == 0) + return 0; + + DecryptMPQBlock((DWORD *)inputBuffer, blockSize, hf->dwSeed1 + index); + BSWAP_ARRAY32_UNSIGNED((DWORD *)inputBuffer, blockSize / sizeof(DWORD)); + } + + // If the block is really compressed, decompress it. + // WARNING : Some block may not be compressed, it can be determined only + // by comparing uncompressed and compressed size !!! + if(blockSize < (DWORD)outLength) + { + // Is the file compressed with PKWARE Data Compression Library ? + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE) + Decompress_pklib((char *)buffer, &outLength, (char *)inputBuffer, (int)blockSize); + + // Is it a file compressed by Blizzard's multiple compression ? + // Note that Storm.dll v 1.0.9 distributed with Warcraft III + // passes the full path name of the opened archive as the new last parameter + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI) + SCompDecompress((char *)buffer, &outLength, (char *)inputBuffer, (int)blockSize); + dwBytesRead += outLength; + buffer += outLength; + } + else + { + if(buffer != inputBuffer) + memcpy(buffer, inputBuffer, blockSize); + + dwBytesRead += blockSize; + buffer += blockSize; + } + blockStart += blockSize; + bytesRemain -= outLength; + } + + // Delete input buffer, if necessary + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) + FREEMEM(tempBuffer); + + return dwBytesRead; +} + +// When this function is called, it is already ensured that the parameters are valid +// (e.g. the "dwToRead + dwFilePos" is not greater than the file size) +// TODO: Test for archives > 4GB +static DWORD WINAPI ReadMPQFileSingleUnit(TMPQFile * hf, DWORD dwFilePos, BYTE * pbBuffer, DWORD dwToRead) +{ + TMPQArchive * ha = hf->ha; + DWORD dwBytesRead = 0; + + if(ha->FilePointer.QuadPart != hf->MpqFilePos.QuadPart) + { + SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN); + ha->FilePointer = hf->MpqFilePos; + } + + // If the file is really compressed, decompress it. + // Otherwise, read the data as-is to the caller. + if(hf->pBlock->dwCSize < hf->pBlock->dwFSize) + { + if(hf->pbFileBuffer == NULL) + { + BYTE * inputBuffer = NULL; + int outputBufferSize = (int)hf->pBlock->dwFSize; + int inputBufferSize = (int)hf->pBlock->dwCSize; + + hf->pbFileBuffer = ALLOCMEM(BYTE, outputBufferSize); + inputBuffer = ALLOCMEM(BYTE, inputBufferSize); + if(inputBuffer != NULL && hf->pbFileBuffer != NULL) + { + // Read the compressed file data + ReadFile(ha->hFile, inputBuffer, inputBufferSize, &dwBytesRead, NULL); + + // Is the file compressed with PKWARE Data Compression Library ? + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE) + Decompress_pklib((char *)hf->pbFileBuffer, &outputBufferSize, (char *)inputBuffer, (int)inputBufferSize); + + // Is it a file compressed by Blizzard's multiple compression ? + // Note that Storm.dll v 1.0.9 distributed with Warcraft III + // passes the full path name of the opened archive as the new last parameter + if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI) + SCompDecompress((char *)hf->pbFileBuffer, &outputBufferSize, (char *)inputBuffer, (int)inputBufferSize); + } + + // Free the temporary buffer + if(inputBuffer != NULL) + FREEMEM(inputBuffer); + } + + // Copy the file data, if any there + if(hf->pbFileBuffer != NULL) + { + memcpy(pbBuffer, hf->pbFileBuffer + dwFilePos, dwToRead); + dwBytesRead += dwToRead; + } + } + else + { + // Read the uncompressed file data + ReadFile(ha->hFile, pbBuffer, dwToRead, &dwBytesRead, NULL); + dwBytesRead = (int)dwBytesRead; + } + + return (DWORD)dwBytesRead; +} + + +//----------------------------------------------------------------------------- +// ReadMPQFile + +// TODO: Test for archives > 4GB +static DWORD WINAPI ReadMPQFile(TMPQFile * hf, DWORD dwFilePos, BYTE * pbBuffer, DWORD dwToRead) +{ + TMPQArchive * ha = hf->ha; + TMPQBlock * pBlock = hf->pBlock; // Pointer to file block + DWORD dwBytesRead = 0; // Number of bytes read from the file + DWORD dwBlockPos; // Position in the file aligned to the whole blocks + DWORD dwLoaded; + + // File position is greater or equal to file size ? + if(dwFilePos >= pBlock->dwFSize) + return dwBytesRead; + + // If too few bytes in the file remaining, cut them + if((pBlock->dwFSize - dwFilePos) < dwToRead) + dwToRead = (pBlock->dwFSize - dwFilePos); + + // If the file is stored as single unit, handle it separately + if(pBlock->dwFlags & MPQ_FILE_SINGLE_UNIT) + return ReadMPQFileSingleUnit(hf, dwFilePos, pbBuffer, dwToRead); + + // Block position in the file + dwBlockPos = dwFilePos & ~(ha->dwBlockSize - 1); // Position in the block + + // Load the first block, if incomplete. It may be loaded in the cache buffer. + // We have to check if this block is loaded. If not, load it. + if((dwFilePos % ha->dwBlockSize) != 0) + { + // Number of bytes remaining in the buffer + DWORD dwToCopy; + DWORD dwLoaded = ha->dwBlockSize; + + // Check if data are loaded in the cache + if(hf != ha->pLastFile || dwBlockPos != ha->dwBlockPos) + { + // Load one MPQ block into archive buffer + dwLoaded = ReadMPQBlocks(hf, dwBlockPos, ha->pbBlockBuffer, ha->dwBlockSize); + if(dwLoaded == 0) + return (DWORD)-1; + + // Save lastly accessed file and block position for later use + ha->pLastFile = hf; + ha->dwBlockPos = dwBlockPos; + ha->dwBuffPos = dwFilePos % ha->dwBlockSize; + } + dwToCopy = dwLoaded - ha->dwBuffPos; + if(dwToCopy > dwToRead) + dwToCopy = dwToRead; + + // Copy data from block buffer into target buffer + memcpy(pbBuffer, ha->pbBlockBuffer + ha->dwBuffPos, dwToCopy); + + // Update pointers + dwToRead -= dwToCopy; + dwBytesRead += dwToCopy; + pbBuffer += dwToCopy; + dwBlockPos += ha->dwBlockSize; + ha->dwBuffPos += dwToCopy; + + // If all, return. + if(dwToRead == 0) + return dwBytesRead; + } + + // Load the whole ("middle") blocks only if there are more or equal one block + if(dwToRead > ha->dwBlockSize) + { + DWORD dwBlockBytes = dwToRead & ~(ha->dwBlockSize - 1); + + dwLoaded = ReadMPQBlocks(hf, dwBlockPos, pbBuffer, dwBlockBytes); + if(dwLoaded == 0) + return (DWORD)-1; + + // Update pointers + dwToRead -= dwLoaded; + dwBytesRead += dwLoaded; + pbBuffer += dwLoaded; + dwBlockPos += dwLoaded; + + // If all, return. + if(dwToRead == 0) + return dwBytesRead; + } + + // Load the terminating block + if(dwToRead > 0) + { + DWORD dwToCopy = ha->dwBlockSize; + + // Check if data are loaded in the cache + if(hf != ha->pLastFile || dwBlockPos != ha->dwBlockPos) + { + // Load one MPQ block into archive buffer + dwToCopy = ReadMPQBlocks(hf, dwBlockPos, ha->pbBlockBuffer, ha->dwBlockSize); + if(dwToCopy == 0) + return (DWORD)-1; + + // Save lastly accessed file and block position for later use + ha->pLastFile = hf; + ha->dwBlockPos = dwBlockPos; + } + ha->dwBuffPos = 0; + + // Check number of bytes read + if(dwToCopy > dwToRead) + dwToCopy = dwToRead; + + memcpy(pbBuffer, ha->pbBlockBuffer, dwToCopy); + dwBytesRead += dwToCopy; + ha->dwBuffPos = dwToCopy; + } + + // Return what we've read + return dwBytesRead; +} + +//----------------------------------------------------------------------------- +// SFileReadFile + +// TODO: Test for archives > 4GB +BOOL WINAPI SFileReadFile(HANDLE hFile, VOID * lpBuffer, DWORD dwToRead, DWORD * pdwRead, LPOVERLAPPED lpOverlapped) +{ + TMPQFile * hf = (TMPQFile *)hFile; + DWORD dwBytes = 0; // Number of bytes (for everything) + int nError = ERROR_SUCCESS; + + // Zero the number of bytes read + if(pdwRead != NULL) + *pdwRead = 0; + + // Check valid parameters + if(nError == ERROR_SUCCESS) + { + if(hf == NULL || lpBuffer == NULL) + nError = ERROR_INVALID_PARAMETER; + } + + // If direct access to the file, use Win32 for reading + if(nError == ERROR_SUCCESS && hf->hFile != INVALID_HANDLE_VALUE) + { + DWORD dwTransferred; + + ReadFile(hf->hFile, lpBuffer, dwToRead, &dwTransferred, lpOverlapped); + if(dwTransferred < dwToRead) + { + SetLastError(ERROR_HANDLE_EOF); + return FALSE; + } + + if(pdwRead != NULL) + *pdwRead = dwTransferred; + return TRUE; + } + + // Read all the bytes available in the buffer (If any) + if(nError == ERROR_SUCCESS) + { + if(dwToRead > 0) + { + dwBytes = ReadMPQFile(hf, hf->dwFilePos, (BYTE *)lpBuffer, dwToRead); + if(dwBytes == (DWORD)-1) + { + SetLastError(ERROR_CAN_NOT_COMPLETE); + return FALSE; + } + hf->ha->pLastFile = hf; + hf->dwFilePos += dwBytes; + } + if(pdwRead != NULL) + *pdwRead = dwBytes; + } + + // Check number of bytes read. If not OK, return FALSE. + if(dwBytes < dwToRead) + { + SetLastError(ERROR_HANDLE_EOF); + return FALSE; + } + return TRUE; +} + +//----------------------------------------------------------------------------- +// SFileGetFilePos +// +// Returns position of archive file in the archive (relative to begin of file) + +// TODO: Test for archives > 4GB +DWORD WINAPI SFileGetFilePos(HANDLE hFile, DWORD * pdwFilePosHigh) +{ + TMPQFile * hf = (TMPQFile *)hFile; + + if(pdwFilePosHigh != NULL) + *pdwFilePosHigh = 0; + + if(hf == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)-1; + } + + // If opened as plain file, ... + if(hf->hFile != INVALID_HANDLE_VALUE) + return 0; + + // If opened from archive, return file size + if(pdwFilePosHigh != NULL) + *pdwFilePosHigh = hf->MpqFilePos.HighPart; + return hf->MpqFilePos.LowPart; +} + +//----------------------------------------------------------------------------- +// SFileGetFileSize + +// TODO: Test for archives > 4GB +DWORD WINAPI SFileGetFileSize(HANDLE hFile, DWORD * pdwFileSizeHigh) +{ + TMPQFile * hf = (TMPQFile *)hFile; + + if(pdwFileSizeHigh != NULL) + *pdwFileSizeHigh = 0; + + if(hf == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)-1; + } + + // If opened as plain file, ... + if(hf->hFile != INVALID_HANDLE_VALUE) + return GetFileSize(hf->hFile, pdwFileSizeHigh); + + // If opened from archive, return file size + return hf->pBlock->dwFSize; +} + +// TODO: Test for archives > 4GB +DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * pdwFilePosHigh, DWORD dwMethod) +{ + TMPQArchive * ha; + TMPQFile * hf = (TMPQFile *)hFile; + + if(hf == NULL || (pdwFilePosHigh != NULL && *pdwFilePosHigh != 0)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return (DWORD)-1; + } + + // If opened as plain file, call Win32 API + if(hf->hFile != INVALID_HANDLE_VALUE) + return SetFilePointer(hf->hFile, lFilePos, pdwFilePosHigh, dwMethod); + ha = hf->ha; + + switch(dwMethod) + { + case FILE_BEGIN: + // Cannot set pointer before begin of file + if(-lFilePos > (LONG)hf->dwFilePos) + hf->dwFilePos = 0; + else + hf->dwFilePos = lFilePos; + break; + + case FILE_CURRENT: + // Cannot set pointer before begin of file + if(-lFilePos > (LONG)hf->dwFilePos) + hf->dwFilePos = 0; + else + hf->dwFilePos += lFilePos; + break; + + case FILE_END: + // Cannot set file position before begin of file + if(-lFilePos >= (LONG)hf->pBlock->dwFSize) + hf->dwFilePos = 0; + else + hf->dwFilePos = hf->pBlock->dwFSize + lFilePos; + break; + + default: + return ERROR_INVALID_PARAMETER; + } + + if(hf == ha->pLastFile && (hf->dwFilePos & ~(ha->dwBlockSize - 1)) == ha->dwBlockPos) + ha->dwBuffPos = hf->dwFilePos & (ha->dwBlockSize - 1); + else + { + ha->pLastFile = NULL; + ha->dwBuffPos = 0; + } + + return hf->dwFilePos; +} + +//----------------------------------------------------------------------------- +// Tries to retrieve the file name + +static TID2Ext id2ext[] = +{ + {0x1A51504D, "mpq"}, // MPQ archive header ID ('MPQ\x1A') + {0x46464952, "wav"}, // WAVE header 'RIFF' + {0x324B4D53, "smk"}, // Old "Smacker Video" files 'SMK2' + {0x694B4942, "bik"}, // Bink video files (new) + {0x0801050A, "pcx"}, // PCX images used in Diablo I + {0x544E4F46, "fnt"}, // Font files used in Diablo II + {0x6D74683C, "html"}, // HTML ' 4GB +BOOL WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) +{ + TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle + char * szExt = "xxx"; // Default extension + DWORD dwFirstBytes[2]; // The first 4 bytes of the file + DWORD dwFilePos; // Saved file position + int nError = ERROR_SUCCESS; + int i; + + // Pre-zero the output buffer + if(szFileName != NULL) + *szFileName = 0; + + // Check valid parameters + if(nError == ERROR_SUCCESS) + { + if(hf == NULL || szFileName == NULL) + nError = ERROR_INVALID_PARAMETER; + } + + // If the file name is already filled, return it. + if(nError == ERROR_SUCCESS && *hf->szFileName != 0) + { + if(szFileName != hf->szFileName) + strcpy(szFileName, hf->szFileName); + return TRUE; + } + + if(nError == ERROR_SUCCESS) + { + if(hf->dwFileIndex == (DWORD)-1) + nError = ERROR_CAN_NOT_COMPLETE; + } + + // Read the first 8 bytes from the file + if(nError == ERROR_SUCCESS) + { + dwFirstBytes[0] = dwFirstBytes[1] = 0; + dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT); + if(!SFileReadFile(hFile, &dwFirstBytes, sizeof(dwFirstBytes), NULL)) + nError = GetLastError(); + BSWAP_ARRAY32_UNSIGNED(dwFirstBytes, sizeof(dwFirstBytes) / sizeof(DWORD)); + SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN); + } + + if(nError == ERROR_SUCCESS) + { + if((dwFirstBytes[0] & 0x0000FFFF) == ID_EXE) + szExt = "exe"; + else if(dwFirstBytes[0] == 0x00000006 && dwFirstBytes[1] == 0x00000001) + szExt = "dc6"; + else + { + for(i = 0; id2ext[i].szExt != NULL; i++) + { + if(id2ext[i].dwID == dwFirstBytes[0]) + { + szExt = id2ext[i].szExt; + break; + } + } + } + + // Create the file name + sprintf(hf->szFileName, "File%08lu.%s", hf->dwFileIndex, szExt); + if(szFileName != hf->szFileName) + strcpy(szFileName, hf->szFileName); + } + return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Retrieves an information about an archive or about a file within the archive +// +// hMpqOrFile - Handle to an MPQ archive or to a file +// dwInfoType - Information to obtain + +// TODO: Test for archives > 4GB +DWORD_PTR WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType) +{ + TMPQArchive * ha = (TMPQArchive *)hMpqOrFile; + TMPQFile * hf = (TMPQFile *)hMpqOrFile; + TMPQBlock * pBlockEnd; + TMPQBlock * pBlock; + DWORD dwFileCount = 0; + DWORD dwSeed; + + switch(dwInfoType) + { + case SFILE_INFO_ARCHIVE_SIZE: + if(IsValidMpqHandle(ha)) + return ha->pHeader->dwArchiveSize; + break; + + case SFILE_INFO_HASH_TABLE_SIZE: // Size of the hash table + if(IsValidMpqHandle(ha)) + return ha->pHeader->dwHashTableSize; + break; + + case SFILE_INFO_BLOCK_TABLE_SIZE: // Size of the hash table + if(IsValidMpqHandle(ha)) + return ha->pHeader->dwBlockTableSize; + break; + + case SFILE_INFO_BLOCK_SIZE: + if(IsValidMpqHandle(ha)) + return ha->dwBlockSize; + break; + + case SFILE_INFO_HASH_TABLE: + if(IsValidMpqHandle(ha)) + return (DWORD_PTR)ha->pHashTable; + break; + + case SFILE_INFO_BLOCK_TABLE: + if(IsValidMpqHandle(ha)) + return (DWORD_PTR)ha->pBlockTable; + break; + + case SFILE_INFO_NUM_FILES: + if(IsValidMpqHandle(ha)) + { + pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; + for(pBlock = ha->pBlockTable; pBlock < pBlockEnd; pBlock++) + { + if(pBlock->dwFlags & MPQ_FILE_EXISTS) + dwFileCount++; + } + return dwFileCount; + } + break; + + case SFILE_INFO_HASH_INDEX: + if(IsValidFileHandle(hf)) + return hf->dwHashIndex; + break; + + case SFILE_INFO_CODENAME1: + if(IsValidFileHandle(hf)) + return hf->pHash->dwName1; + break; + + case SFILE_INFO_CODENAME2: + if(IsValidFileHandle(hf)) + return hf->pHash->dwName2; + break; + + case SFILE_INFO_LOCALEID: + if(IsValidFileHandle(hf)) + return hf->pHash->lcLocale; + break; + + case SFILE_INFO_BLOCKINDEX: + if(IsValidFileHandle(hf)) + return hf->dwFileIndex; + break; + + case SFILE_INFO_FILE_SIZE: + if(IsValidFileHandle(hf)) + return hf->pBlock->dwFSize; + break; + + case SFILE_INFO_COMPRESSED_SIZE: + if(IsValidFileHandle(hf)) + return hf->pBlock->dwCSize; + break; + + case SFILE_INFO_FLAGS: + if(IsValidFileHandle(hf)) + return hf->pBlock->dwFlags; + break; + + case SFILE_INFO_POSITION: + if(IsValidFileHandle(hf)) + return hf->pBlock->dwFilePos; + break; + + case SFILE_INFO_SEED: + if(IsValidFileHandle(hf)) + return hf->dwSeed1; + break; + + case SFILE_INFO_SEED_UNFIXED: + if(IsValidFileHandle(hf)) + { + dwSeed = hf->dwSeed1; + if(hf->pBlock->dwFlags & MPQ_FILE_FIXSEED) + dwSeed = (dwSeed ^ hf->pBlock->dwFSize) - (DWORD)(hf->MpqFilePos.QuadPart - hf->ha->MpqPos.QuadPart); + return dwSeed; + } + break; + } + + // Unknown parameter or invalid handle + SetLastError(ERROR_INVALID_PARAMETER); + return 0xFFFFFFFF; +} diff --git a/src/tools/stuffextract/StormLib/SListFile.cpp b/src/tools/stuffextract/StormLib/SListFile.cpp new file mode 100644 index 0000000..7d887cf --- /dev/null +++ b/src/tools/stuffextract/StormLib/SListFile.cpp @@ -0,0 +1,561 @@ +/*****************************************************************************/ +/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */ +/*---------------------------------------------------------------------------*/ +/* Description: */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 12.06.04 1.00 Lad The first version of SListFile.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "SCommon.h" +#include + +//----------------------------------------------------------------------------- +// Listfile entry structure + +#define LISTFILE_CACHE_SIZE 0x1000 // Size of one cache element +#define NO_MORE_CHARACTERS 256 +#define HASH_TABLE_SIZE 31 // Initial hash table size (should be a prime number) + +// TODO: Check on x64 !!! +#define LISTFILE_ENTRY_DELETED (DWORD_PTR)(-2) +#define LISTFILE_ENTRY_FREE (DWORD_PTR)(-1) + +struct TListFileCache +{ + HANDLE hFile; // Stormlib file handle + char * szMask; // File mask + DWORD dwFileSize; // Total size of the cached file + DWORD dwBuffSize; // File of the cache + DWORD dwFilePos; // Position of the cache in the file + BYTE * pBegin; // The begin of the listfile cache + BYTE * pPos; + BYTE * pEnd; // The last character in the file cache + + BYTE Buffer[1]; // Listfile cache itself +}; + +//----------------------------------------------------------------------------- +// Local functions (cache) + +// Reloads the cache. Returns number of characters +// that has been loaded into the cache. +static int ReloadCache(TListFileCache * pCache) +{ + // Check if there is enough characters in the cache + // If not, we have to reload the next block + if(pCache->pPos >= pCache->pEnd) + { + // If the cache is already at the end, do nothing more + if((pCache->dwFilePos + pCache->dwBuffSize) >= pCache->dwFileSize) + return 0; + + pCache->dwFilePos += pCache->dwBuffSize; + SFileReadFile(pCache->hFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL); + if(pCache->dwBuffSize == 0) + return 0; + + // Set the buffer pointers + pCache->pBegin = + pCache->pPos = &pCache->Buffer[0]; + pCache->pEnd = pCache->pBegin + pCache->dwBuffSize; + } + + return pCache->dwBuffSize; +} + +static size_t ReadLine(TListFileCache * pCache, char * szLine, int nMaxChars) +{ + char * szLineBegin = szLine; + char * szLineEnd = szLine + nMaxChars - 1; + +__BeginLoading: + + // Skip newlines, spaces, tabs and another non-printable stuff + while(pCache->pPos < pCache->pEnd && *pCache->pPos <= 0x20) + pCache->pPos++; + + // Copy the remaining characters + while(pCache->pPos < pCache->pEnd && szLine < szLineEnd) + { + // If we have found a newline, stop loading + if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A) + break; + + *szLine++ = *pCache->pPos++; + } + + // If we now need to reload the cache, do it + if(pCache->pPos == pCache->pEnd) + { + if(ReloadCache(pCache) > 0) + goto __BeginLoading; + } + + *szLine = 0; + return (szLine - szLineBegin); +} + +//----------------------------------------------------------------------------- +// Local functions (listfile nodes) + +// This function creates the name for the listfile. +// the file will be created under unique name in the temporary directory +static void GetListFileName(TMPQArchive * /* ha */, char * szListFile) +{ + char szTemp[MAX_PATH]; + + // Create temporary file name int TEMP directory + GetTempPath(sizeof(szTemp)-1, szTemp); + GetTempFileName(szTemp, LISTFILE_NAME, 0, szListFile); +} + +// Creates new listfile. The listfile is an array of TListFileNode +// structures. The size of the array is the same like the hash table size, +// the ordering is the same too (listfile item index is the same like +// the index in the MPQ hash table) + +int SListFileCreateListFile(TMPQArchive * ha) +{ + DWORD dwItems = ha->pHeader->dwHashTableSize; + + // The listfile should be NULL now + assert(ha->pListFile == NULL); + + ha->pListFile = ALLOCMEM(TFileNode *, dwItems); + if(ha->pListFile == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + memset(ha->pListFile, 0xFF, dwItems * sizeof(TFileNode *)); + return ERROR_SUCCESS; +} + +// Adds a filename into the listfile. If the file name is already there, +// does nothing. +int SListFileAddNode(TMPQArchive * ha, const char * szFileName) +{ + TFileNode * pNode = NULL; + TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash0 = GetHashEntry(ha, szFileName); + TMPQHash * pHash = pHash0; + DWORD dwHashIndex = 0; + size_t nLength; // File name lentgth + DWORD dwName1; + DWORD dwName2; + + // If the file does not exist within the MPQ, do nothing + if(pHash == NULL) + return ERROR_SUCCESS; + + // If the listfile entry already exists, do nothing + dwHashIndex = (DWORD)(pHash - ha->pHashTable); + dwName1 = pHash->dwName1; + dwName2 = pHash->dwName2; + if((DWORD_PTR)ha->pListFile[dwHashIndex] <= LISTFILE_ENTRY_DELETED) + return ERROR_SUCCESS; + + // Create the listfile node and insert it into the listfile table + nLength = strlen(szFileName); + pNode = (TFileNode *)ALLOCMEM(char, sizeof(TFileNode) + nLength); + pNode->dwRefCount = 0; + pNode->nLength = nLength; + strcpy(pNode->szFileName, szFileName); + + // Fill the nodes for all language versions + while(pHash->dwBlockIndex < LISTFILE_ENTRY_DELETED) + { + if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2) + { + pNode->dwRefCount++; + ha->pListFile[pHash - ha->pHashTable] = pNode; + } + + if(++pHash >= pHashEnd) + pHash = ha->pHashTable; + if(pHash == pHash0) + break; + } + + return ERROR_SUCCESS; +} + +// Removes a filename from the listfile. +// If the name is not there, does nothing +int SListFileRemoveNode(TMPQArchive * ha, const char * szFileName) +{ + TFileNode * pNode = NULL; + TMPQHash * pHash = GetHashEntry(ha, szFileName); + size_t nHashIndex = 0; + + if(pHash != NULL) + { + nHashIndex = pHash - ha->pHashTable; + pNode = ha->pListFile[nHashIndex]; + ha->pListFile[nHashIndex] = (TFileNode *)LISTFILE_ENTRY_DELETED; + + // If the reference count has reached zero, do nothing + if(--pNode->dwRefCount == 0) + FREEMEM(pNode); + } + return ERROR_SUCCESS; +} + + +// Renames a node. We will not deal with the renaming, we'll simply +// remove the old node and insert the new one. +// TODO: Test for archives > 4GB +int SListFileRenameNode(TMPQArchive * ha, const char * szOldFileName, const char * szNewFileName) +{ + SListFileRemoveNode(ha, szOldFileName); + return SListFileAddNode(ha, szNewFileName); +} + +// TODO: Test for archives > 4GB +int SListFileFreeListFile(TMPQArchive * ha) +{ + if(ha->pListFile != NULL) + { + for(DWORD i = 0; i < ha->pHeader->dwHashTableSize; i++) + { + TFileNode * pNode = ha->pListFile[i]; + + if((DWORD_PTR)pNode < LISTFILE_ENTRY_FREE) + { + if(--pNode->dwRefCount == 0) + { + FREEMEM(pNode); + ha->pListFile[i] = (TFileNode *)LISTFILE_ENTRY_FREE; + } + } + } + + FREEMEM(ha->pListFile); + ha->pListFile = NULL; + } + + return ERROR_SUCCESS; +} + +// Saves the whole listfile into the MPQ. +// TODO: Test for archives > 4GB +int SListFileSaveToMpq(TMPQArchive * ha) +{ + TFileNode * pNode = NULL; + TMPQHash * pHashEnd = NULL; + TMPQHash * pHash0 = NULL; + TMPQHash * pHash = NULL; + HANDLE hFile = INVALID_HANDLE_VALUE; + char szListFile[MAX_PATH]; + char szBuffer[MAX_PATH+4]; + DWORD dwTransferred; + size_t nLength = 0; + DWORD dwName1 = 0; + DWORD dwName2 = 0; + LCID lcSave = lcLocale; + int nError = ERROR_SUCCESS; + + // If no listfile, do nothing + if(ha->pListFile == NULL) + return ERROR_SUCCESS; + + // Create the local listfile + if(nError == ERROR_SUCCESS) + { + GetListFileName(ha, szListFile); + hFile = CreateFile(szListFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); + if(hFile == INVALID_HANDLE_VALUE) + nError = GetLastError(); + } + + // Find the hash entry corresponding to listfile + pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + pHash0 = pHash = GetHashEntry(ha, 0); + if(pHash == NULL) + pHash0 = pHash = ha->pHashTable; + + // Save the file + if(nError == ERROR_SUCCESS) + { + for(;;) + { + if(pHash->dwName1 != dwName1 && pHash->dwName2 != dwName2 && pHash->dwBlockIndex < LISTFILE_ENTRY_DELETED) + { + dwName1 = pHash->dwName1; + dwName2 = pHash->dwName2; + pNode = ha->pListFile[pHash - ha->pHashTable]; + + if((DWORD_PTR)pNode < LISTFILE_ENTRY_DELETED) + { + memcpy(szBuffer, pNode->szFileName, pNode->nLength); + szBuffer[pNode->nLength + 0] = 0x0D; + szBuffer[pNode->nLength + 1] = 0x0A; + WriteFile(hFile, szBuffer, (DWORD)(pNode->nLength + 2), &dwTransferred, NULL); + } + } + + if(++pHash >= pHashEnd) + pHash = ha->pHashTable; + if(pHash == pHash0) + break; + } + + // Write the listfile name (if not already there) + if(GetHashEntry(ha, LISTFILE_NAME) == NULL) + { + nLength = strlen(LISTFILE_NAME); + memcpy(szBuffer, LISTFILE_NAME, nLength); + szBuffer[nLength + 0] = 0x0D; + szBuffer[nLength + 1] = 0x0A; + WriteFile(hFile, szBuffer, (DWORD)(nLength + 2), &dwTransferred, NULL); + } + + // Add the listfile into the archive. + SFileSetLocale(LANG_NEUTRAL); + nError = AddFileToArchive(ha, hFile, LISTFILE_NAME, MPQ_FILE_COMPRESS_PKWARE | MPQ_FILE_ENCRYPTED | MPQ_FILE_REPLACEEXISTING, 0, SFILE_TYPE_DATA, NULL); + } + + // Close the temporary file. This will delete it too. + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + + lcLocale = lcSave; + return nError; +} + +//----------------------------------------------------------------------------- +// File functions + +// Adds a listfile into the MPQ archive. +// Note that the function does not remove the +// TODO: Test for archives > 4GB +int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile) +{ + TListFileCache * pCache = NULL; + TMPQArchive * ha = (TMPQArchive *)hMpq; + HANDLE hListFile = NULL; + char szFileName[MAX_PATH + 1]; + DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; + DWORD dwCacheSize = 0; + DWORD dwFileSize = 0; + size_t nLength = 0; + int nError = ERROR_SUCCESS; + + // If the szListFile is NULL, it means we have to open internal listfile + if(szListFile == NULL) + { + szListFile = LISTFILE_NAME; + dwSearchScope = SFILE_OPEN_FROM_MPQ; + } + + // Open the local/internal listfile + if(nError == ERROR_SUCCESS) + { + if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile)) + nError = GetLastError(); + } + + if(nError == ERROR_SUCCESS) + { + dwCacheSize = + dwFileSize = SFileGetFileSize(hListFile, NULL); + + // Try to allocate memory for the complete file. If it fails, + // load the part of the file + pCache = (TListFileCache *)ALLOCMEM(char, (sizeof(TListFileCache) + dwCacheSize)); + if(pCache == NULL) + { + dwCacheSize = LISTFILE_CACHE_SIZE; + pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize); + } + + if(pCache == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + if(nError == ERROR_SUCCESS) + { + // Initialize the file cache + memset(pCache, 0, sizeof(TListFileCache)); + pCache->hFile = hListFile; + pCache->dwFileSize = dwFileSize; + pCache->dwBuffSize = dwCacheSize; + pCache->dwFilePos = 0; + + // Fill the cache + SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL); + + // Initialize the pointers + pCache->pBegin = + pCache->pPos = &pCache->Buffer[0]; + pCache->pEnd = pCache->pBegin + pCache->dwBuffSize; + + // Load the node tree + while((nLength = ReadLine(pCache, szFileName, sizeof(szFileName) - 1)) > 0) + SListFileAddNode(ha, szFileName); + + // Add well-known names + // Sometimes, they are not in listfile, but they exist in the archive + SListFileAddNode(ha, LISTFILE_NAME); + SListFileAddNode(ha, SIGNATURE_NAME); + SListFileAddNode(ha, ATTRIBUTES_NAME); + } + + // Cleanup & exit + if(pCache != NULL) + SListFileFindClose((HANDLE)pCache); + return nError; +} + +//----------------------------------------------------------------------------- +// Passing through the listfile + +// TODO: Test for archives > 4GB +HANDLE SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) +{ + TListFileCache * pCache = NULL; + TMPQArchive * ha = (TMPQArchive *)hMpq; + HANDLE hListFile = NULL; + DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; + DWORD dwCacheSize = 0; + DWORD dwFileSize = 0; + size_t nLength = 0; + int nError = ERROR_SUCCESS; + + // Initialize the structure with zeros + memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); + + // If the szListFile is NULL, it means we have to open internal listfile + if(szListFile == NULL) + { + szListFile = LISTFILE_NAME; + dwSearchScope = SFILE_OPEN_FROM_MPQ; + } + + // Open the local/internal listfile + if(nError == ERROR_SUCCESS) + { + if(!SFileOpenFileEx((HANDLE)ha, szListFile, dwSearchScope, &hListFile)) + nError = GetLastError(); + } + + if(nError == ERROR_SUCCESS) + { + dwCacheSize = + dwFileSize = SFileGetFileSize(hListFile, NULL); + + // Try to allocate memory for the complete file. If it fails, + // load the part of the file + pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize); + if(pCache == NULL) + { + dwCacheSize = LISTFILE_CACHE_SIZE; + pCache = (TListFileCache *)ALLOCMEM(char, sizeof(TListFileCache) + dwCacheSize); + } + + if(pCache == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + if(nError == ERROR_SUCCESS) + { + // Initialize the file cache + memset(pCache, 0, sizeof(TListFileCache)); + pCache->hFile = hListFile; + pCache->dwFileSize = dwFileSize; + pCache->dwBuffSize = dwCacheSize; + pCache->dwFilePos = 0; + if(szMask != NULL) + { + pCache->szMask = ALLOCMEM(char, strlen(szMask) + 1); + strcpy(pCache->szMask, szMask); + } + + // Fill the cache + SFileReadFile(hListFile, pCache->Buffer, pCache->dwBuffSize, &pCache->dwBuffSize, NULL); + + // Initialize the pointers + pCache->pBegin = + pCache->pPos = &pCache->Buffer[0]; + pCache->pEnd = pCache->pBegin + pCache->dwBuffSize; + + for(;;) + { + // Read the (next) line + nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); + if(nLength == 0) + { + nError = ERROR_NO_MORE_FILES; + break; + } + + // If some mask entered, check it + if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) + break; + } + } + + // Cleanup & exit + if(nError != ERROR_SUCCESS) + { + memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); + SListFileFindClose((HANDLE)pCache); + pCache = NULL; + + SetLastError(nError); + } + return (HANDLE)pCache; +} + +// TODO: Test for archives > 4GB +BOOL SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) +{ + TListFileCache * pCache = (TListFileCache *)hFind; + size_t nLength; + BOOL bResult = FALSE; + int nError = ERROR_SUCCESS; + + for(;;) + { + // Read the (next) line + nLength = ReadLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); + if(nLength == 0) + { + nError = ERROR_NO_MORE_FILES; + break; + } + + // If some mask entered, check it + if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) + { + bResult = TRUE; + break; + } + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return bResult; +} + +// TODO: Test for archives > 4GB +BOOL SListFileFindClose(HANDLE hFind) +{ + TListFileCache * pCache = (TListFileCache *)hFind; + + if(pCache != NULL) + { + if(pCache->hFile != NULL) + SFileCloseFile(pCache->hFile); + if(pCache->szMask != NULL) + FREEMEM(pCache->szMask); + + FREEMEM(pCache); + return TRUE; + } + + return FALSE; +} + diff --git a/src/tools/stuffextract/StormLib/StormDll.h b/src/tools/stuffextract/StormLib/StormDll.h new file mode 100644 index 0000000..721230c --- /dev/null +++ b/src/tools/stuffextract/StormLib/StormDll.h @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* Storm.h Copyright Justin Olbrantz(Quantam) 2000 */ +/*---------------------------------------------------------------------------*/ +/* Storm Interface Library v1.0 for Windows */ +/* */ +/* Author : Justin Olbrantz(Quantam) */ +/* E-mail : omega@dragonfire.net */ +/* WWW : www.campaigncreations.com/starcraft/mpq2k/inside_mopaq/ */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.00 1.00 Qua The first version of Storm.h */ +/* 11.04.03 1.00 Lad Added some functions */ +/*****************************************************************************/ + +// We need the Windows data types for the Storm prototypes +#include + +#ifndef __STORM_H__ +#define __STORM_H__ + +// Somethimes is necessary to change the function names so they +// will not conflict with other MPQ tools. +#ifdef STORM_ALTERNATE_NAMES + #define SFILE(Name) Storm##Name + #define SCOMP(Name) Storm##Name +#else + #define SFILE(Name) SFile##Name + #define SCOMP(Name) SComp##Name +#endif + + +// Just in case anyone is still using C out there +#ifdef __cplusplus +extern "C" { +#endif + +// Storm file function prototypes +BOOL WINAPI SFILE(OpenArchive)(LPCSTR lpFileName, DWORD dwPriority, DWORD dwFlags, HANDLE *hMPQ); +BOOL WINAPI SFILE(CloseArchive)(HANDLE hMPQ); +BOOL WINAPI SFILE(GetArchiveName)(HANDLE hMPQ, LPCSTR lpBuffer, DWORD dwBufferLength); +BOOL WINAPI SFILE(OpenFile)(LPCSTR lpFileName, HANDLE *hFile); +BOOL WINAPI SFILE(OpenFileEx)(HANDLE hMPQ, LPCSTR lpFileName, DWORD dwSearchScope, HANDLE *hFile); +BOOL WINAPI SFILE(CloseFile)(HANDLE hFile); +DWORD WINAPI SFILE(GetFileSize)(HANDLE hFile, LPDWORD lpFileSizeHigh); +BOOL WINAPI SFILE(GetFileArchive)(HANDLE hFile, HANDLE *hMPQ); +BOOL WINAPI SFILE(GetFileName)(HANDLE hFile, LPCSTR lpBuffer, DWORD dwBufferLength); +DWORD WINAPI SFILE(SetFilePointer)(HANDLE hFile, long lDistanceToMove, PLONG lplDistanceToMoveHigh, DWORD dwMoveMethod); +BOOL WINAPI SFILE(ReadFile)(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,LPOVERLAPPED lpOverlapped); +LCID WINAPI SFILE(SetLocale)(LCID nNewLocale); +BOOL WINAPI SFILE(GetBasePath)(LPCSTR lpBuffer, DWORD dwBufferLength); +BOOL WINAPI SFILE(SetBasePath)(LPCSTR lpNewBasePath); + +// Storm (de)compression functions +BOOL WINAPI SCOMP(Compress) (char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int uCmp, int uCmpType, int nCmpLevel); +BOOL WINAPI SCOMP(Decompress)(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength); + + +#if defined(_MSC_VER) && !defined(BUILDING_STORM_CPP) +#pragma comment(lib, "Storm.lib") // Force linking Storm.lib and thus Storm.dll +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __STORM_H__ diff --git a/src/tools/stuffextract/StormLib/StormLib.h b/src/tools/stuffextract/StormLib/StormLib.h new file mode 100644 index 0000000..68d01ad --- /dev/null +++ b/src/tools/stuffextract/StormLib/StormLib.h @@ -0,0 +1,581 @@ +/*****************************************************************************/ +/* StormLib.h Copyright (c) Ladislav Zezula 1999-2005 */ +/*---------------------------------------------------------------------------*/ +/* StormLib library v 5.00 */ +/* */ +/* Author : Ladislav Zezula */ +/* E-mail : ladik@zezula.net */ +/* WWW : http://www.zezula.net */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.99 1.00 Lad Created */ +/* 24.03.03 2.50 Lad Version 2.50 */ +/* 02.04.03 3.00 Lad Version 3.00 with compression */ +/* 11.04.03 3.01 Lad Renamed to StormLib.h for compatibility with */ +/* original headers for Storm.dll */ +/* 10.05.03 3.02 Lad Added Pkware DCL compression */ +/* 26.05.03 4.00 Lad Completed all compressions */ +/* 18.06.03 4.01 Lad Added SFileSetFileLocale */ +/* Added SFileExtractFile */ +/* 26.07.03 4.02 Lad Implemented nameless rename and delete */ +/* 26.07.03 4.03 Lad Added support for protected MPQs */ +/* 28.08.03 4.10 Lad Fixed bugs that caused StormLib incorrectly work */ +/* with Diablo I savegames and with files having full */ +/* hash table */ +/* 08.12.03 4.11 DCH Fixed bug in reading file block larger than 0x1000 */ +/* on certain files. */ +/* Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING */ +/* (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/ +/* 21.12.03 4.50 Lad Completed port for Mac */ +/* Fixed bug in compacting (if fsize is mul of 0x1000) */ +/* Fixed bug in SCompCompress */ +/* 27.05.04 4.51 Lad Changed memory management from new/delete to our */ +/* own macros */ +/* 22.06.04 4.60 Lad Optimized search. Support for multiple listfiles. */ +/* 30.09.04 4.61 Lad Fixed some bugs (Aaargh !!!) */ +/* Correctly works if HashTableSize > BlockTableSize */ +/* 29.12.04 4.70 Lad Fixed compatibility problem with MPQs from WoW */ +/* 14.07.05 5.00 Lad Added the BZLIB compression support */ +/* Added suport of files stored as single unit */ +/* 17.04.06 5.01 Lad Converted to MS Visual Studio 8.0 */ +/* Fixed issue with protected Warcraft 3 protected maps */ +/* 15.05.06 5.02 Lad Fixed issue with WoW 1.10+ */ +/* 07.09.06 5.10 Lad Fixed processing files longer than 2GB */ +/* 22.11.06 6.00 Lad Support for MPQ archives V2 */ +/*****************************************************************************/ + +#ifndef __STORMLIB_H_ +#define __STORMLIB_H_ + +#include "StormPort.h" + +//----------------------------------------------------------------------------- +// Use the apropriate library +// +// The library type is encoded in the library name as the following +// StormLibXYZ.lib +// +// X - D for Debug version, R for Release version +// Y - A for ANSI version, U for Unicode version (Unicode version does not exist yet) +// Z - S for static C library, D for multithreaded DLL C-library +// + +#define __STORMLIB_SELF__ + +#if defined(_MSC_VER) && !defined (__STORMLIB_SELF__) + #ifdef _DEBUG // DEBUG VERSIONS + #ifdef _DLL + #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi Dynamic version + #else + #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi Static version + #endif + #else // RELEASE VERSIONS + #ifdef _DLL + #pragma comment(lib, "StormLibRAD.lib") // Release Ansi Dynamic version + #else + #pragma comment(lib, "StormLibRAS.lib") // Release Ansi Static version + #endif + #endif +#endif + +//----------------------------------------------------------------------------- +// Defines + +#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') +#define ID_MPQ_SHUNT 0x1B51504D // MPQ shunt entry ('MPQ\x1B') + +#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file. + +// Values for SFileCreateArchiveEx +#define HASH_TABLE_SIZE_MIN 0x00002 +#define HASH_TABLE_SIZE_MAX 0x40000 + +#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted hash entry +#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free hash entry + +// Values for SFileOpenArchive +#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD +#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM + +// Values for SFileOpenFile +#define SFILE_OPEN_FROM_MPQ 0 // Open the file from the MPQ archive +#define SFILE_OPEN_BY_INDEX 1 // The 'szFileName' parameter is actually the file index +#define SFILE_OPEN_LOCAL_FILE (DWORD)-1 // Open the file from the MPQ archive + +// Flags for TMPQArchive::dwFlags +#define MPQ_FLAG_CHANGED 0x00000001 // If set, the MPQ has been changed +#define MPQ_FLAG_PROTECTED 0x00000002 // Set on protected MPQs (like W3M maps) + +// Flags for SFileAddFile +#define MPQ_FILE_COMPRESS_PKWARE 0x00000100 // Compression made by PKWARE Data Compression Library +#define MPQ_FILE_COMPRESS_MULTI 0x00000200 // Multiple compressions +#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed +#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted +#define MPQ_FILE_FIXSEED 0x00020000 // File decrypt seed has to be fixed +#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam) +#define MPQ_FILE_DUMMY_FILE 0x02000000 // The file is only 1 byte long and its name is a hash +#define MPQ_FILE_HAS_EXTRA 0x04000000 // The file has extra data appended after regular data. + // Must be with compressed files only +#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted +#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile) + +#define MPQ_FILE_VALID_FLAGS (MPQ_FILE_COMPRESS_PKWARE | \ + MPQ_FILE_COMPRESS_MULTI | \ + MPQ_FILE_ENCRYPTED | \ + MPQ_FILE_FIXSEED | \ + MPQ_FILE_SINGLE_UNIT | \ + MPQ_FILE_DUMMY_FILE | \ + MPQ_FILE_HAS_EXTRA | \ + MPQ_FILE_EXISTS) + +// Compression types for multilpe compressions +#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) +#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression +#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression +#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression +#define MPQ_COMPRESSION_WAVE_MONO 0x40 // +#define MPQ_COMPRESSION_WAVE_STEREO 0x80 // + + +// Constants for SFileAddWave +#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression +#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression +#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression + +// Constants for SFileGetFileInfo +#define SFILE_INFO_ARCHIVE_SIZE 1 // MPQ size (value from header) +#define SFILE_INFO_HASH_TABLE_SIZE 2 // Size of hash table, in entries +#define SFILE_INFO_BLOCK_TABLE_SIZE 3 // Number of entries in the block table +#define SFILE_INFO_BLOCK_SIZE 4 // Size of file block (in bytes) +#define SFILE_INFO_HASH_TABLE 5 // Pointer to Hash table (TMPQHash *) +#define SFILE_INFO_BLOCK_TABLE 6 // Pointer to Block Table (TMPQBlock *) +#define SFILE_INFO_NUM_FILES 7 // Real number of files within archive +//------ +#define SFILE_INFO_HASH_INDEX 8 // Hash index of file in MPQ +#define SFILE_INFO_CODENAME1 9 // The first codename of the file +#define SFILE_INFO_CODENAME2 10 // The second codename of the file +#define SFILE_INFO_LOCALEID 11 // Locale ID of file in MPQ +#define SFILE_INFO_BLOCKINDEX 12 // Index to Block Table +#define SFILE_INFO_FILE_SIZE 13 // Original file size +#define SFILE_INFO_COMPRESSED_SIZE 14 // Compressed file size +#define SFILE_INFO_FLAGS 15 // File flags +#define SFILE_INFO_POSITION 16 // File position within archive +#define SFILE_INFO_SEED 17 // File decryption seed +#define SFILE_INFO_SEED_UNFIXED 18 // Decryption seed not fixed to file pos and size + +// Values for compact callback +#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total) +#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total) +#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used +#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total) +#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used + +#define LISTFILE_NAME "(listfile)" // Name of internal listfile +#define SIGNATURE_NAME "(signature)" // Name of internal signature +#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file + +#define STORMLIB_VERSION (0x0600) // Current version of StormLib + +#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade +#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer, + +// Flags for SFileOpenArchiveEx +#define MPQ_OPEN_NO_LISTFILE 0x00000001 // Don't add the internal listfile + +// supports archives with size > 4 GB +// Additional flags for SFileCreateArchiveEx +#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive with size up to 4GB +#define MPQ_CREATE_ARCHIVE_V2 0x00010000 // Creates archive larger than 4 GB + +//----------------------------------------------------------------------------- +// Structures + +#if (defined(WIN32) || defined(WIN64)) +#include +#else +#pragma pack(1) +#endif + +struct TMPQFile; + +struct TMPQShunt +{ + // The ID_MPQ_SHUNT ('MPQ\x1B') signature + DWORD dwID; + + DWORD dwUnknown; + + // Position of the MPQ header, relative to the begin of the shunt + DWORD dwHeaderPos; +}; + + +// MPQ file header +struct TMPQHeader +{ + // The ID_MPQ ('MPQ\x1A') signature + DWORD dwID; + + // Size of the archive header + DWORD dwHeaderSize; + + // Size of MPQ archive + // This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive + // is calculated as the size from the beginning of the archive to the end of the hash table, + // block table, or extended block table (whichever is largest). + DWORD dwArchiveSize; + + // 0 = Original format + // 1 = Extended format (The Burning Crusade and newer) + USHORT wFormatVersion; + + // Power of two exponent specifying the number of 512-byte disk sectors in each logical sector + // in the archive. The size of each logical sector in the archive is 512 * 2^SectorSizeShift. + // Bugs in the Storm library dictate that this should always be 3 (4096 byte sectors). + USHORT wBlockSize; + + // Offset to the beginning of the hash table, relative to the beginning of the archive. + DWORD dwHashTablePos; + + // Offset to the beginning of the block table, relative to the beginning of the archive. + DWORD dwBlockTablePos; + + // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for + // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. + DWORD dwHashTableSize; + + // Number of entries in the block table + DWORD dwBlockTableSize; +}; + + +// Extended MPQ file header. Valid only if wFormatVersion is 1 or higher +struct TMPQHeader2 : public TMPQHeader +{ + // Offset to the beginning of the extended block table, relative to the beginning of the archive. + LARGE_INTEGER ExtBlockTablePos; + + // High 16 bits of the hash table offset for large archives. + USHORT wHashTablePosHigh; + + // High 16 bits of the block table offset for large archives. + USHORT wBlockTablePosHigh; +}; + + +// Hash entry. All files in the archive are searched by their hashes. +struct TMPQHash +{ + // The hash of the file path, using method A. + DWORD dwName1; + + // The hash of the file path, using method B. + DWORD dwName2; + +#ifdef PLATFORM_LITTLE_ENDIAN + + // The language of the file. This is a Windows LANGID data type, and uses the same values. + // 0 indicates the default language (American English), or that the file is language-neutral. + USHORT lcLocale; + + // The platform the file is used for. 0 indicates the default platform. + // No other values have been observed. + USHORT wPlatform; + +#else + + USHORT wPlatform; + USHORT lcLocale; + +#endif + + // If the hash table entry is valid, this is the index into the block table of the file. + // Otherwise, one of the following two values: + // - FFFFFFFFh: Hash table entry is empty, and has always been empty. + // Terminates searches for a given file. + // - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file). + // Does not terminate searches for a given file. + DWORD dwBlockIndex; +}; + + +// File description block contains informations about the file +struct TMPQBlock +{ + // Offset of the beginning of the block, relative to the beginning of the archive. + DWORD dwFilePos; + + // Compressed file size + DWORD dwCSize; + + // Only valid if the block is a file; otherwise meaningless, and should be 0. + // If the file is compressed, this is the size of the uncompressed file data. + DWORD dwFSize; + + // Flags for the file. See MPQ_FILE_XXXX constants + DWORD dwFlags; +}; + + +// The extended block table was added to support archives larger than 4 gigabytes (2^32 bytes). +// The table contains the upper bits of the archive offsets for each block in the block table. +// It is simply an array of int16s, which become bits 32-47 of the archive offsets for each block, +// with bits 48-63 being zero. Individual blocks in the archive are still limited to 4 gigabytes +// in size. This table is only present in Burning Crusade format archives that exceed 4 gigabytes size. +struct TMPQBlockEx +{ + USHORT wFilePosHigh; +}; + + +struct TFileNode +{ + DWORD dwRefCount; // Number of references + // There can be more files that have the same name. + // (e.g. multiple language files). We don't want to + // have an entry for each of them, so the entries will be referenced. + // When a number of node references reaches zero, + // the node will be deleted + + size_t nLength; // File name length + char szFileName[1]; // File name, variable length +}; + +#if (defined(WIN32) || defined(WIN64)) +#include +#else +#pragma options align=reset +#endif + +// Archive handle structure. Note that it does not agree with Storm.dll's structure. +struct TMPQArchive +{ +// TMPQArchive * pNext; // Next archive (used by Storm.dll only) +// TMPQArchive * pPrev; // Previous archive (used by Storm.dll only) + char szFileName[MAX_PATH]; // Opened archive file name + HANDLE hFile; // File handle + DWORD dwPriority; // Priority of the archive + LARGE_INTEGER ShuntPos; // Position of MPQShunt (only valid if a shunt is present) + LARGE_INTEGER MpqPos; // MPQ position in the file, relative to the begin of the file + LARGE_INTEGER MpqSize; // Size of MPQ archive + LARGE_INTEGER HashTablePos; // Offset of the hast table in the MPQ, relative to the begin of the file + LARGE_INTEGER BlockTablePos; // Offset of the hast table in the MPQ, relative to the begin + LARGE_INTEGER ExtBlockTablePos; // Offset of the extended block table, relative to the begin + LARGE_INTEGER FilePointer; // Current position in the file (relative to begin of the file) + + TMPQFile * pLastFile; // Recently read file + DWORD dwBlockPos; // Position of loaded block in the file + DWORD dwBlockSize; // Size of file block + BYTE * pbBlockBuffer; // Buffer (cache) for file block + DWORD dwBuffPos; // Position in block buffer + TMPQShunt * pShunt; // MPQ shunt (NULL if not present in the file) + TMPQHeader2 * pHeader; // MPQ file header + TMPQHash * pHashTable; // Hash table + TMPQBlock * pBlockTable; // Block table + TMPQBlockEx * pExtBlockTable; // Extended block table + + TMPQShunt Shunt; // MPQ shunt. Valid only when ID_MPQ_SHUNT has been found + TMPQHeader2 Header; // MPQ header + + // Non-Storm.dll members + TFileNode ** pListFile; // File name array +// HANDLE hListFile; // Handle to temporary listfile (when open with write access) + DWORD dwFlags; // See MPQ_FLAG_XXXXX +// BOOL bChanged; // TRUE if the archive was changed since open. +// BOOL bProtected; // TRUE if the archive is protected by somehow +}; + + +// File handle structure. Note that it does not agree with Storm.dll structures +struct TMPQFile +{ + HANDLE hFile; // File handle + TMPQArchive * ha; // Archive handle + TMPQHash * pHash; // Hash table entry + TMPQBlockEx * pBlockEx; // Pointer to extended file block entry + TMPQBlock * pBlock; // File block pointer + DWORD dwSeed1; // Seed used for file decrypt + DWORD dwFilePos; // Current file position + LARGE_INTEGER MpqFilePos; // Position of the file data in MPQ archive + // (relative to file begin) + + DWORD * pdwBlockPos; // Position of each file block (only for compressed files) + DWORD nBlocks; // Number of blocks in the file (incl. the last noncomplete one) + BOOL bBlockPosLoaded; // TRUE if block positions loaded + BYTE * pbFileBuffer; // Decompressed file (for single unit files, size is the uncompressed file size) + + DWORD dwHashIndex; // Index to Hash table + DWORD dwFileIndex; // Index to Block table + char szFileName[1]; // File name (variable length) +}; + + +// Used by searching in MPQ archives +struct TMPQSearch +{ + TMPQArchive * ha; // Handle to MPQ, where the search runs + DWORD dwNextIndex; // The next searched hash index + DWORD dwName1; // Lastly found Name1 + DWORD dwName2; // Lastly found Name2 + char szSearchMask[1]; // Search mask (variable length) +}; + + +struct SFILE_FIND_DATA +{ + char cFileName[MAX_PATH]; // Full name of the found file + char * szPlainName; // Pointer to file part + LCID lcLocale; // Locale version + DWORD dwFileSize; // File size in bytes + DWORD dwFileFlags; // File flags (compressed or encrypted) + DWORD dwBlockIndex; // Block index for the file + DWORD dwCompSize; // Compressed file size +}; + +//----------------------------------------------------------------------------- +// Memory management +// +// We use our own macros for allocating/freeing memory. If you want +// to redefine them, please keep the following rules +// +// - The memory allocation must return NULL if not enough memory +// (i.e not to throw exception) +// - It is not necessary to fill the allocated block with zeros +// - Memory freeing function must not test the pointer to NULL. +// + + +__inline void * DebugMalloc(char * szFile, int nLine, int nSize) +{ + void * ptr = malloc(nSize + 100); + char * plain; + + plain = strrchr(szFile, '\\'); + if(plain == NULL) + plain = strrchr(szFile, '/'); + if(plain == NULL) + plain = szFile; + +#if _MSC_VER > 0x1300 + sprintf_s((char *)ptr, nSize+100, "%s(%u)", plain, nLine); +#else + sprintf((char *)ptr, "%s(%u)", plain, nLine); +#endif + + return (char *)ptr + 100; +} + + +__inline void DebugFree(void * ptr) +{ + free((char *)ptr - 100); +} + + +#ifndef ALLOCMEM + #define ALLOCMEM(type, nitems) (type *)malloc((nitems) * sizeof(type)) + #define FREEMEM(ptr) free(ptr) +#endif + +//#define ALLOCMEM(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type)) +//#define FREEMEM(ptr) DebugFree(ptr) + +//----------------------------------------------------------------------------- +// Functions in StormLib - compatible with Storm.dll + +// Typedefs for functions exported by Storm.dll +typedef LCID (WINAPI * SFILESETLOCALE)(LCID); +typedef BOOL (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *); +typedef BOOL (WINAPI * SFILECLOSEARCHIVE)(HANDLE); +typedef BOOL (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *); +typedef BOOL (WINAPI * SFILECLOSEFILE)(HANDLE); +typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, DWORD *); +typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD); +typedef BOOL (WINAPI * SFILEREADFILE)(HANDLE, VOID *, DWORD, DWORD *, LPOVERLAPPED); + +// Archive opening/closing +LCID WINAPI SFileSetLocale(LCID lcNewLocale); +LCID WINAPI SFileGetLocale(); +BOOL WINAPI SFileOpenArchive(const char * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMPQ); +BOOL WINAPI SFileCloseArchive(HANDLE hMPQ); + +// File opening/closing +BOOL WINAPI SFileOpenFileEx(HANDLE hMPQ, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); +BOOL WINAPI SFileCloseFile(HANDLE hFile); + +// File I/O +DWORD WINAPI SFileGetFilePos(HANDLE hFile, DWORD * pdwFilePosHigh = NULL); +DWORD WINAPI SFileGetFileSize(HANDLE hFile, DWORD * pdwFileSizeHigh = NULL); +DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * pdwFilePosHigh, DWORD dwMethod); +BOOL WINAPI SFileReadFile(HANDLE hFile, VOID * lpBuffer, DWORD dwToRead, DWORD * pdwRead = NULL, LPOVERLAPPED lpOverlapped = NULL); + +BOOL WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted); + +// Adds another listfile into MPQ. The currently added listfile(s) remain, +// so you can use this API to combining more listfiles. +// Note that this function is internally called by SFileFindFirstFile +int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile); + +//----------------------------------------------------------------------------- +// Functions in StormLib - not implemented in Storm.dll + +// Archive creating and editing +BOOL WINAPI SFileCreateArchiveEx(const char * szMpqName, DWORD dwCreationDisposition, DWORD dwHashTableSize, HANDLE * phMPQ); +BOOL WINAPI SFileAddFile(HANDLE hMPQ, const char * szFileName, const char * szArchivedName, DWORD dwFlags); +BOOL WINAPI SFileAddWave(HANDLE hMPQ, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); +BOOL WINAPI SFileRemoveFile(HANDLE hMPQ, const char * szFileName, DWORD dwSearchScope = SFILE_OPEN_BY_INDEX); +BOOL WINAPI SFileRenameFile(HANDLE hMPQ, const char * szOldFileName, const char * szNewFileName); +BOOL WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); + +// Retrieving info about the file +BOOL WINAPI SFileHasFile(HANDLE hMPQ, char * szFileName); +BOOL WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); +DWORD_PTR WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType); + +// File search +// Note that the SFileFindFirstFileEx has been removed. Use SListFileFindFirst/Next +HANDLE WINAPI SFileFindFirstFile(HANDLE hMPQ, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile); +BOOL WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +BOOL WINAPI SFileFindClose(HANDLE hFind); + +// Listfile search +HANDLE SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); +BOOL SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +BOOL SListFileFindClose(HANDLE hFind); + +// Archive compacting +typedef void (WINAPI * COMPACTCB)(void * lpUserData, DWORD dwWorkType, DWORD dwParam1, DWORD dwParam2); +BOOL WINAPI SFileSetCompactCallback(HANDLE hMPQ, COMPACTCB CompactCB, void * lpData); +BOOL WINAPI SFileCompactArchive(HANDLE hMPQ, const char * szListFile = NULL, BOOL bReserved = 0); + +// Locale support +int WINAPI SFileEnumLocales(HANDLE hMPQ, const char * szFileName, LCID * plcLocales, DWORD * pdwMaxLocales, DWORD dwSearchScope = SFILE_OPEN_BY_INDEX); + +// (De)compression +int WINAPI SCompCompress (char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int uCompressions, int nCmpType, int nCmpLevel); +int WINAPI SCompDecompress (char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength); + +// Sets the default data compression for files added to MPQ, +// if MPQ_FILE_COMPRESS_MULTI has been specified in call to SFileAddFile +int WINAPI SCompSetDataCompression(int nDataCompression); + +//----------------------------------------------------------------------------- +// Functions from Storm.dll. They use slightly different names for keeping +// possibility to use them together with StormLib (StormXXX instead of SFileXXX) + +#ifdef __LINK_STORM_DLL__ + #define STORM_ALTERNATE_NAMES // Force Storm.h to use alternate fnc names + #include "StormDll.h" +#endif // __LINK_STORM_DLL__ + +//----------------------------------------------------------------------------- +// GFX decode functions. See GfxDecode.cpp for details and description + +USHORT WINAPI celGetFrameCount(BYTE * fileBuf); +BYTE * WINAPI celGetFrameData(BYTE *fileBuf, BYTE *palette, USHORT xsize, USHORT frame, USHORT *ysize, USHORT *maxX=NULL); +USHORT WINAPI cl2GetFrameCount(BYTE *fileBuf); +BYTE ** WINAPI cl2GetDirData(BYTE *fileBuf, BYTE *palette, USHORT xsize, USHORT dir, USHORT *ysize); +BYTE * WINAPI pcxGetData(BYTE *filebuf, DWORD filesize, BYTE transcol, USHORT *xsize, USHORT *ysize); + +#endif // __STORMLIB_H_ diff --git a/src/tools/stuffextract/StormLib/StormPort.h b/src/tools/stuffextract/StormLib/StormPort.h new file mode 100644 index 0000000..35f12f6 --- /dev/null +++ b/src/tools/stuffextract/StormLib/StormPort.h @@ -0,0 +1,278 @@ +/*****************************************************************************/ +/* StormPort.h Copyright (c) Marko Friedemann 2001 */ +/*---------------------------------------------------------------------------*/ +/* Portability module for the StormLib library. Contains a wrapper symbols */ +/* to make the compilation under Linux work */ +/* */ +/* Author: Marko Friedemann */ +/* Created at: Mon Jan 29 18:26:01 CEST 2001 */ +/* Computer: whiplash.flachland-chemnitz.de */ +/* System: Linux 2.4.0 on i686 */ +/* */ +/* Author: Sam Wilkins */ +/* System: Mac OS X and port to big endian processor */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.01.01 1.00 Mar Created */ +/* 24.03.03 1.01 Lad Some cosmetic changes */ +/* 12.11.03 1.02 Dan Macintosh compatibility */ +/* 24.07.04 1.03 Sam Mac OS X compatibility */ +/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */ +/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */ +/*****************************************************************************/ + +#ifndef __STORMPORT_H__ +#define __STORMPORT_H__ + +// Defines for Windows +#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64)) + + // In MSVC 8.0, there are some functions declared as deprecated. + #if _MSC_VER >= 1400 + #define _CRT_SECURE_NO_DEPRECATE + #define _CRT_NON_CONFORMING_SWPRINTFS + #endif + + #include + #include + #include + #define PLATFORM_LITTLE_ENDIAN 1 + + #ifdef WIN64 + #define PLATFORM_64BIT + #else + #define PLATFORM_32BIT + #endif + + #define PLATFORM_DEFINED // The platform is known now + +#endif + +// Defines for Mac Carbon +#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac Carbon API + + // Macintosh using Carbon + #include // Mac OS X + #define _stricmp strcasecmp // Case insensitive strcmp has a different name on this platform. + #define _strnicmp strncasecmp + + typedef void * LPCSTR; + typedef unsigned long * LPDWORD; + typedef long * PLONG; + typedef void * LPVOID; + typedef unsigned int UINT; + + #define PKEXPORT + #define __SYS_ZLIB + #define __SYS_BZLIB + #define LANG_NEUTRAL 0 + + #if defined(__BIG_ENDIAN__) + #define PLATFORM_LITTLE_ENDIAN 0 + #else + #define PLATFORM_LITTLE_ENDIAN 1 // Apple is now making Macs with Intel CPUs + #endif + #define PLATFORM_DEFINED // The platform is known now + +#endif + +// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* +// Ladik : Why the hell Linux does not use some OS-dependent #define ? +#if !defined(PLATFORM_DEFINED) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define PLATFORM_LITTLE_ENDIAN 1 + #define PLATFORM_DEFINED + #define LANG_NEUTRAL 0 + +#endif /* not __powerc */ + + +#if !defined(WIN32) && !defined(WIN64) + + // Typedefs for ANSI C + typedef unsigned char BYTE; + typedef short SHORT; + typedef unsigned short WORD; + typedef unsigned short USHORT; + typedef long LONG; + typedef unsigned long DWORD; + typedef unsigned long DWORD_PTR; + typedef long LONG_PTR; + typedef long long LONGLONG; +#ifndef __OBJC__ + #define BOOL bool +#endif + typedef void * HANDLE; + typedef void * LPOVERLAPPED; // Unsupported on Linux + typedef char TCHAR; + typedef unsigned long LCID; + + typedef void * LPCSTR; + typedef unsigned long * LPDWORD; + typedef long * PLONG; + typedef void * LPVOID; + typedef unsigned int UINT; + + typedef struct _FILETIME + { + DWORD dwLowDateTime; + DWORD dwHighDateTime; + } + FILETIME, *PFILETIME; + + typedef union _LARGE_INTEGER + { + #if PLATFORM_LITTLE_ENDIAN + struct + { + DWORD LowPart; + LONG HighPart; + }; + #else + struct + { + LONG HighPart; + DWORD LowPart; + }; + #endif + LONGLONG QuadPart; + } + LARGE_INTEGER, *PLARGE_INTEGER; + + // Some Windows-specific defines + #ifndef MAX_PATH + #define MAX_PATH 1024 + #endif + + #ifndef TRUE + #define TRUE true + #endif + + #ifndef FALSE + #define FALSE false + #endif + + #define VOID void + #define WINAPI + + #define FILE_BEGIN SEEK_SET + #define FILE_CURRENT SEEK_CUR + #define FILE_END SEEK_END + + #define CREATE_NEW 1 + #define CREATE_ALWAYS 2 + #define OPEN_EXISTING 3 + #define OPEN_ALWAYS 4 + + #define FILE_SHARE_READ 0x00000001L + #define GENERIC_WRITE 0x40000000 + #define GENERIC_READ 0x80000000 + + #define FILE_FLAG_DELETE_ON_CLOSE 1 // Sam: Added these two defines so it would compile. + #define FILE_FLAG_SEQUENTIAL_SCAN 2 + + #define ERROR_SUCCESS 0 + #define ERROR_INVALID_FUNCTION 1 + #define ERROR_FILE_NOT_FOUND 2 + #define ERROR_ACCESS_DENIED 5 + #define ERROR_NOT_ENOUGH_MEMORY 8 + #define ERROR_BAD_FORMAT 11 + #define ERROR_NO_MORE_FILES 18 + #define ERROR_GEN_FAILURE 31 + #define ERROR_HANDLE_EOF 38 + #define ERROR_HANDLE_DISK_FULL 39 + #define ERROR_NOT_SUPPORTED 50 + #define ERROR_INVALID_PARAMETER 87 + #define ERROR_DISK_FULL 112 + #define ERROR_CALL_NOT_IMPLEMENTED 120 + #define ERROR_ALREADY_EXISTS 183 + #define ERROR_CAN_NOT_COMPLETE 1003 + #define ERROR_PARAMETER_QUOTA_EXCEEDED 1283 + #define ERROR_FILE_CORRUPT 1392 + #define ERROR_INSUFFICIENT_BUFFER 4999 + + #define INVALID_HANDLE_VALUE ((HANDLE) -1) + + #ifndef min + #define min(a, b) ((a < b) ? a : b) + #endif + + #ifndef max + #define max(a, b) ((a > b) ? a : b) + #endif + + #define _stricmp strcasecmp + #define _strnicmp strncasecmp + + extern int globalerr; + + void SetLastError(int err); + int GetLastError(); + char *ErrString(int err); + + // Emulation of functions for file I/O available in Win32 + HANDLE CreateFile(const char * lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, void * lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + BOOL CloseHandle(HANDLE hObject); + + DWORD GetFileSize(HANDLE hFile, DWORD * lpFileSizeHigh); + DWORD SetFilePointer(HANDLE, LONG lDistanceToMove, LONG * lpDistanceToMoveHigh, DWORD dwMoveMethod); + BOOL SetEndOfFile(HANDLE hFile); + + BOOL ReadFile(HANDLE hFile, void * lpBuffer, DWORD nNumberOfBytesToRead, DWORD * lpNumberOfBytesRead, void * lpOverLapped); + BOOL WriteFile(HANDLE hFile, const void * lpBuffer, DWORD nNumberOfBytesToWrite, DWORD * lpNumberOfBytesWritten, void * lpOverLapped); + + BOOL IsBadReadPtr(const void * ptr, int size); + DWORD GetFileAttributes(const char * szileName); + + BOOL DeleteFile(const char * lpFileName); + BOOL MoveFile(const char * lpFromFileName, const char * lpToFileName); + void GetTempPath(DWORD szTempLength, char * szTemp); + void GetTempFileName(const char * lpTempFolderPath, const char * lpFileName, DWORD something, char * szLFName); + + #define strnicmp strncasecmp + +#endif // !WIN32 + +#if PLATFORM_LITTLE_ENDIAN + #define BSWAP_INT16_UNSIGNED(a) (a) + #define BSWAP_INT16_SIGNED(a) (a) + #define BSWAP_INT32_UNSIGNED(a) (a) + #define BSWAP_INT32_SIGNED(a) (a) + #define BSWAP_ARRAY16_UNSIGNED(a,b) {} + #define BSWAP_ARRAY32_UNSIGNED(a,b) {} + #define BSWAP_TMPQSHUNT(a) {} + #define BSWAP_TMPQHEADER(a) {} +#else + extern unsigned short SwapUShort(unsigned short); + extern unsigned long SwapULong(unsigned long); + extern short SwapShort(unsigned short); + extern long SwapLong(unsigned long); + extern void ConvertUnsignedLongBuffer(unsigned long *buffer, unsigned long nbLongs); + extern void ConvertUnsignedShortBuffer(unsigned short *buffer, unsigned long nbShorts); + extern void ConvertTMPQShunt(void *shunt); + extern void ConvertTMPQHeader(void *header); + #define BSWAP_INT16_UNSIGNED(a) SwapUShort((a)) + #define BSWAP_INT32_UNSIGNED(a) SwapULong((a)) + #define BSWAP_INT16_SIGNED(a) SwapShort((a)) + #define BSWAP_INT32_SIGNED(a) SwapLong((a)) + #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUnsignedShortBuffer((a),(b)) + #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUnsignedLongBuffer((a),(b)) + #define BSWAP_TMPQSHUNT(a) ConvertTMPQShunt((a)) + #define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a)) +#endif + +#endif // __STORMPORT_H__ diff --git a/src/tools/stuffextract/StormLib/StormPortLinux.cpp b/src/tools/stuffextract/StormLib/StormPortLinux.cpp new file mode 100644 index 0000000..9fd31f3 --- /dev/null +++ b/src/tools/stuffextract/StormLib/StormPortLinux.cpp @@ -0,0 +1,168 @@ +/******************************************************************** +* +* Description: implementation for StormLib - linux port +* intended to be used in GLdiablo +* +* ----> StormLib was originally developed for Windows by +* Ladislav Zezula (www.zezula.net), and he did +* a _great_ job! Thanks Ladislav! +* +* this is currently a quick and dirty hack to get it working +* don't expect beauty and/or miracles :) +* +* these are function wraps to execute Windows API calls +* as native Macintosh file calls (open/close/read/write/...) +* +* continue you work: added some wrapping functions for GNU/Linux by XPinguin +* +* Author: Marko Friedemann +* Created at: Mon Jan 29 19:01:37 CEST 2001 +* Computer: whiplash.flachland-chemnitz.de +* System: Linux 2.4.0 on i686 +* +* Copyright (c) 2001 BMX-Chemnitz.DE All rights reserved. +* +********************************************************************/ + +#ifndef _WIN32 +#include "StormPort.h" + +int globalerr; + +void SetLastError(int err) +{ + globalerr = err; +} + +int GetLastError() +{ + return(globalerr); +} + +char *ErrString(int err) +{ + switch (err) { + case ERROR_INVALID_FUNCTION: + return "function not implemented"; + case ERROR_FILE_NOT_FOUND: + return "file not found"; + case ERROR_ACCESS_DENIED: + return "access denied"; + case ERROR_NOT_ENOUGH_MEMORY: + return "not enough memory"; + case ERROR_BAD_FORMAT: + return "bad format"; + case ERROR_NO_MORE_FILES: + return "no more files"; + case ERROR_HANDLE_EOF: + return "access beyound EOF"; + case ERROR_HANDLE_DISK_FULL: + return "no space left on device"; + case ERROR_INVALID_PARAMETER: + return "invalid parameter"; + case ERROR_DISK_FULL: + return "no space left on device"; + case ERROR_ALREADY_EXISTS: + return "file exists"; + case ERROR_CAN_NOT_COMPLETE: + return "operation cannot be completed"; + default: + return "unknown error"; + } +} + +HANDLE CreateFile(const char *sFileName, DWORD ulMode, DWORD ulSharing, void *pSecAttrib, DWORD ulCreation, DWORD ulFlags, HANDLE hFile) +{ + switch (ulCreation) { + case OPEN_EXISTING: + return (HANDLE)open(sFileName, O_RDONLY | O_LARGEFILE); + case OPEN_ALWAYS: + return (HANDLE)open(sFileName, O_RDWR | O_CREAT); + case CREATE_NEW: + return (HANDLE)open(sFileName, O_RDWR | O_CREAT | O_TRUNC); + default: + return INVALID_HANDLE_VALUE; + } +} + +BOOL CloseHandle(HANDLE hFile) +{ + return (close((int)hFile) == 0); +} + +DWORD GetFileSize(HANDLE hFile, DWORD *ulOffSetHigh) +{ + if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE)) + return 0xffffffff; + + struct stat fileinfo; + fstat((int)hFile, &fileinfo); + + return fileinfo.st_size; +} + +DWORD SetFilePointer(HANDLE hFile, LONG lOffSetLow, LONG *pOffSetHigh, DWORD ulMethod) +{ + return lseek64((int)hFile, (off64_t)(*pOffSetHigh) << 32 | (DWORD)lOffSetLow, ulMethod); +} + +BOOL SetEndOfFile(HANDLE hFile) +{ + return (ftruncate((int)hFile, lseek((int)hFile, 0, SEEK_CUR)) == 0); +} + +BOOL ReadFile(HANDLE hFile, void *pBuffer, DWORD ulLen, DWORD *ulRead, void *pOverLapped) +{ + ssize_t count; + if ((count = read((int)hFile, pBuffer, ulLen)) == -1) { + *ulRead = 0; + return false; + } + *ulRead = count; + return true; +} + +BOOL WriteFile(HANDLE hFile, const void *pBuffer, DWORD ulLen, DWORD *ulWritten, void *pOverLapped) +{ + ssize_t count; + if ((count = write((int)hFile, pBuffer, ulLen)) == -1) { + *ulWritten = 0; + return false; + } + *ulWritten = count; + return true; +} + +// Check if a memory block is accessible for reading +BOOL IsBadReadPtr(const void * ptr, int size) +{ + return FALSE; +} + +// Returns attributes of a file +DWORD GetFileAttributes(const char * szFileName) +{ + return 0; +} + +void GetTempPath(DWORD szTempLength, char * szTemp) +{ + strncpy(szTemp, P_tmpdir, szTempLength); +} + +void GetTempFileName(const char * lpTempFolderPath, const char * lpFileName, DWORD something, char * szLFName) +{ + strcpy(szLFName, tempnam(lpTempFolderPath, lpFileName)); +} + +BOOL DeleteFile(const char *lpFileName) +{ + return (BOOL)remove(lpFileName); +} + +BOOL MoveFile(const char *lpExistingFileName, const char *lpNewFileName) +{ + return rename(lpExistingFileName, lpNewFileName); +} + +#endif diff --git a/src/tools/stuffextract/StormLib/StormPortMac.cpp b/src/tools/stuffextract/StormLib/StormPortMac.cpp new file mode 100644 index 0000000..92cc3d7 --- /dev/null +++ b/src/tools/stuffextract/StormLib/StormPortMac.cpp @@ -0,0 +1,762 @@ +/******************************************************************** +* +* Description: implementation for StormLib - Macintosh port +* +* these are function wraps to execute Windows API calls +* as native Macintosh file calls (open/close/read/write/...) +* requires Mac OS X +* +* Derived from Marko Friedemann +* StormPort.cpp for Linux +* +* Author: Daniel Chiaramello +* +* Carbonized by: Sam Wilkins +* +********************************************************************/ + +#ifndef _WIN32 || _WIN64 +#include "StormPort.h" +#include "StormLib.h" + +// FUNCTIONS EXTRACTED FROM MOREFILE PACKAGE!!! +// FEEL FREE TO REMOVE THEM AND TO ADD THE ORIGINAL ONES! + +/***************************************************************************** +* BEGIN OF MOREFILES COPY-PASTE +*****************************************************************************/ + +#ifdef __USEPRAGMAINTERNAL + #ifdef __MWERKS__ + #pragma internal on + #endif +#endif + +union TLongAnd4Bytes +{ + unsigned char bytes[4]; + unsigned long uvalue; + signed long svalue; +}; + + +static OSErr FSGetFullPath(const FSRef *ref, UInt8 *fullPath, UInt32 fullPathLength) +{ + OSErr result; + + result = FSRefMakePath(ref, fullPath, fullPathLength); + + return result; +} + +static OSErr FSLocationFromFullPath(const void *fullPath, FSRef *ref) +{ + OSErr result; + + result = FSPathMakeRef((UInt8 *)fullPath, ref, NULL); // Create an FSRef from the path + return result; +} + +/*****************************************************************************/ + +/*****************************************************************************/ + +static OSErr FSCreateCompat(const FSRef *parentRef, OSType creator, OSType fileType, const UniChar *fileName, + UniCharCount nameLength, FSRef *ref) +{ + FSCatalogInfo theCatInfo; + OSErr theErr; + ((FileInfo *)&theCatInfo.finderInfo)->fileCreator = creator; + ((FileInfo *)&theCatInfo.finderInfo)->fileType = fileType; + ((FileInfo *)&theCatInfo.finderInfo)->finderFlags = 0; + SetPt(&((FileInfo *)&theCatInfo.finderInfo)->location, 0, 0); + ((FileInfo *)&theCatInfo.finderInfo)->reservedField = 0; + + theErr = FSCreateFileUnicode(parentRef, nameLength, fileName, kFSCatInfoFinderInfo, &theCatInfo, ref, NULL); + return theErr; +} + + +/*****************************************************************************/ + +static OSErr FSOpenDFCompat(FSRef *ref, char permission, short *refNum) +{ + HFSUniStr255 forkName; + OSErr theErr; + Boolean isFolder, wasChanged; + + theErr = FSResolveAliasFile(ref, TRUE, &isFolder, &wasChanged); + if (theErr != noErr) + return theErr; + + FSGetDataForkName(&forkName); + theErr = FSOpenFork(ref, forkName.length, forkName.unicode, permission, refNum); + return theErr; +} + +/***************************************************************************** +* END OF MOREFILES COPY-PASTE +*****************************************************************************/ + +#pragma mark - + +int globalerr; + +/******************************************************************** +* SwapLong +********************************************************************/ + +unsigned long SwapULong(unsigned long data) +{ + // Apple provided function + uint32_t result; + + __asm__("lwbrx %0,0,%1" : "=r" (result) : "r" (&data), "m" (data)); + return result; + +/* + TLongAnd4Bytes Work; + unsigned char * value_as_4bytes = (unsigned char *)&value; + + Work.bytes[0] = value_as_4bytes[3]; + Work.bytes[1] = value_as_4bytes[2]; + Work.bytes[2] = value_as_4bytes[1]; + Work.bytes[3] = value_as_4bytes[0]; + + return Work.uvalue; +*/ +} + +long SwapLong(unsigned long data) +{ + // Apple provided function + uint32_t result; + + __asm__("lwbrx %0,0,%1" : "=r" (result) : "r" (&data), "m" (data)); + return (long)result; + +/* + TLongAnd4Bytes Work; + unsigned char * value_as_4bytes = (unsigned char *)&value; + + Work.bytes[0] = value_as_4bytes[3]; + Work.bytes[1] = value_as_4bytes[2]; + Work.bytes[2] = value_as_4bytes[1]; + Work.bytes[3] = value_as_4bytes[0]; + + return Work.svalue; +*/ +} + +/******************************************************************** +* SwapShort +********************************************************************/ +unsigned short SwapUShort(unsigned short data) +{ + // Apple provided function + uint16_t result; + __asm__("lhbrx %0,0,%1" : "=r" (result) : "r" (&data), "m" (data)); + return result; +} + +short SwapShort(unsigned short data) +{ + // Apple provided function + uint16_t result; + __asm__("lhbrx %0,0,%1" : "=r" (result) : "r" (&data), "m" (data)); + return (short)result; +} + +/******************************************************************** +* ConvertUnsignedLongBuffer +********************************************************************/ +void ConvertUnsignedLongBuffer(unsigned long *buffer, unsigned long nbLongs) +{ + while (nbLongs-- > 0) + { + *buffer = SwapLong(*buffer); + buffer++; + } +} + +/******************************************************************** +* ConvertUnsignedShortBuffer +********************************************************************/ +void ConvertUnsignedShortBuffer(unsigned short *buffer, unsigned long nbShorts) +{ + while (nbShorts-- > 0) + { + *buffer = SwapShort(*buffer); + buffer++; + } +} + +/******************************************************************** +* ConvertTMPQShunt +********************************************************************/ +void ConvertTMPQShunt(void *shunt) +{ + TMPQShunt * theShunt = (TMPQShunt *)shunt; + + theShunt->dwID = SwapULong(theShunt->dwID); + theShunt->dwUnknown = SwapULong(theShunt->dwUnknown); + theShunt->dwHeaderPos = SwapULong(theShunt->dwHeaderPos); +} + +/******************************************************************** +* ConvertTMPQHeader +********************************************************************/ +void ConvertTMPQHeader(void *header) +{ + TMPQHeader2 * theHeader = (TMPQHeader2 *)header; + + theHeader->dwID = SwapULong(theHeader->dwID); + theHeader->dwHeaderSize = SwapULong(theHeader->dwHeaderSize); + theHeader->dwArchiveSize = SwapULong(theHeader->dwArchiveSize); + theHeader->wFormatVersion = SwapUShort(theHeader->wFormatVersion); + theHeader->wBlockSize = SwapUShort(theHeader->wBlockSize); + theHeader->dwHashTablePos = SwapULong(theHeader->dwHashTablePos); + theHeader->dwBlockTablePos = SwapULong(theHeader->dwBlockTablePos); + theHeader->dwHashTableSize = SwapULong(theHeader->dwHashTableSize); + theHeader->dwBlockTableSize = SwapULong(theHeader->dwBlockTableSize); + + if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + { + DWORD dwTemp = theHeader->ExtBlockTablePos.LowPart; + theHeader->ExtBlockTablePos.LowPart = theHeader->ExtBlockTablePos.HighPart; + theHeader->ExtBlockTablePos.HighPart = dwTemp; + theHeader->ExtBlockTablePos.LowPart = SwapULong(theHeader->ExtBlockTablePos.LowPart); + theHeader->ExtBlockTablePos.HighPart = SwapULong(theHeader->ExtBlockTablePos.HighPart); + theHeader->wHashTablePosHigh = SwapUShort(theHeader->wHashTablePosHigh); + theHeader->wBlockTablePosHigh = SwapUShort(theHeader->wBlockTablePosHigh); + } +} + +/******************************************************************** +* ConvertTMPQHash +********************************************************************/ +void ConvertHashTable(void *hashtable, DWORD nHashEntries) +{ + TMPQHash * theHash = (TMPQHash *)hashtable; + USHORT lcLocale; + + for(DWORD i = 0; i < nHashEntries; i++, theHash++) + { + lcLocale = theHash->lcLocale; + theHash->lcLocale = theHash->wPlatform; + theHash->wPlatform = lcLocale; + } +} + +#pragma mark - + +/******************************************************************** +* SetLastError +********************************************************************/ +void SetLastError(int err) +{ + globalerr = err; +} + +/******************************************************************** +* GetLastError +********************************************************************/ +int GetLastError() +{ + return globalerr; +} + +/******************************************************************** +* ErrString +********************************************************************/ +char *ErrString(int err) +{ + switch (err) + { + case ERROR_INVALID_FUNCTION: + return "function not implemented"; + case ERROR_FILE_NOT_FOUND: + return "file not found"; + case ERROR_ACCESS_DENIED: + return "access denied"; + case ERROR_NOT_ENOUGH_MEMORY: + return "not enough memory"; + case ERROR_BAD_FORMAT: + return "bad format"; + case ERROR_NO_MORE_FILES: + return "no more files"; + case ERROR_HANDLE_EOF: + return "access beyound EOF"; + case ERROR_HANDLE_DISK_FULL: + return "no space left on device"; + case ERROR_INVALID_PARAMETER: + return "invalid parameter"; + case ERROR_DISK_FULL: + return "no space left on device"; + case ERROR_ALREADY_EXISTS: + return "file exists"; + case ERROR_CAN_NOT_COMPLETE: + return "operation cannot be completed"; + case ERROR_INSUFFICIENT_BUFFER: + return "insufficient buffer"; + default: + return "unknown error"; + } +} + +#pragma mark - + +/******************************************************************** +* GetTempPath - returns a '/' or ':'-terminated path +* szTempLength: length for path +* szTemp: file path +********************************************************************/ +void GetTempPath(DWORD szTempLength, char * szTemp) // I think I'll change this to use FSRefs. +{ + FSRef theFSRef; + OSErr theErr = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, kCreateFolder, &theFSRef); + if (theErr == noErr) + { + theErr = FSGetFullPath(&theFSRef, (UInt8 *)szTemp, MAX_PATH); + if (theErr != noErr) + szTemp[0] = '\0'; + } + else + szTemp[0] = '\0'; + strcat(szTemp, "/"); + + SetLastError(theErr); +} + +/******************************************************************** +* GetTempFileName +* lpTempFolderPath: the temporary folder path, terminated by "/" +* lpFileName: a file name base +* something: unknown +* szLFName: the final path, built from the path, the file name and a random pattern +********************************************************************/ +void GetTempFileName(const char * lpTempFolderPath, const char * lpFileName, DWORD something, char * szLFName) +{ +#pragma unused (something) + char tmp[2] = "A"; + + while (true) + { + HANDLE fHandle; + + strcpy(szLFName, lpTempFolderPath); + strcat(szLFName, lpFileName); + strcat(szLFName, tmp); + + if ((fHandle = CreateFile(szLFName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) + // OK we found it! + break; + CloseHandle(fHandle); + tmp[0]++; + } +} + +/******************************************************************** +* DeleteFile +* lpFileName: file path +********************************************************************/ +BOOL DeleteFile(const char * lpFileName) +{ + OSErr theErr; + FSRef theFileRef; + + theErr = FSLocationFromFullPath(lpFileName, &theFileRef); + if (theErr != noErr) + { + SetLastError(theErr); + return FALSE; + } + + theErr = FSDeleteObject(&theFileRef); + + SetLastError(theErr); + + return theErr == noErr; +} + +/******************************************************************** +* MoveFile +* lpFromFileName: old file path +* lpToFileName: new file path +********************************************************************/ +BOOL MoveFile(const char * lpFromFileName, const char * lpToFileName) +{ + OSErr theErr; + FSRef fromFileRef; + FSRef toFileRef; + FSRef parentFolderRef; + + // Get the path to the old file + theErr = FSLocationFromFullPath(lpFromFileName, &fromFileRef); + if (theErr != noErr) + { + SetLastError(theErr); + return false; + } + + // Get the path to the new folder for the file + char folderName[strlen(lpToFileName)]; + CFStringRef folderPathCFString = CFStringCreateWithCString(NULL, lpToFileName, kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, folderPathCFString, kCFURLPOSIXPathStyle, FALSE); + CFURLRef folderURL = CFURLCreateCopyDeletingLastPathComponent(NULL, fileURL); + CFURLGetFileSystemRepresentation(folderURL, TRUE, (UInt8 *)folderName, strlen(lpToFileName)); + theErr = FSLocationFromFullPath(folderName, &parentFolderRef); + CFRelease(fileURL); + CFRelease(folderURL); + CFRelease(folderPathCFString); + + // Move the old file + theErr = FSMoveObject(&fromFileRef, &parentFolderRef, &toFileRef); + if (theErr != noErr) + { + SetLastError(theErr); + return false; + } + + // Get a CFString for the new file name + CFStringRef newFileNameCFString = CFStringCreateWithCString(NULL, lpToFileName, kCFStringEncodingUTF8); + fileURL = CFURLCreateWithFileSystemPath(NULL, newFileNameCFString, kCFURLPOSIXPathStyle, FALSE); + CFRelease(newFileNameCFString); + newFileNameCFString = CFURLCopyLastPathComponent(fileURL); + CFRelease(fileURL); + + // Convert CFString to Unicode and rename the file + UniChar unicodeFileName[256]; + CFStringGetCharacters(newFileNameCFString, CFRangeMake(0, CFStringGetLength(newFileNameCFString)), + unicodeFileName); + theErr = FSRenameUnicode(&toFileRef, CFStringGetLength(newFileNameCFString), unicodeFileName, + kTextEncodingUnknown, NULL); + if (theErr != noErr) + { + SetLastError(theErr); + CFRelease(newFileNameCFString); + return false; + } + + CFRelease(newFileNameCFString); + + SetLastError(theErr); + return true; +} + +/******************************************************************** +* CreateFile +* ulMode: GENERIC_READ | GENERIC_WRITE +* ulSharing: FILE_SHARE_READ +* pSecAttrib: NULL +* ulCreation: OPEN_EXISTING, OPEN_ALWAYS, CREATE_NEW +* ulFlags: 0 +* hFile: NULL +********************************************************************/ +HANDLE CreateFile( const char *sFileName, /* file name */ + DWORD ulMode, /* access mode */ + DWORD ulSharing, /* share mode */ + void *pSecAttrib, /* SD */ + DWORD ulCreation, /* how to create */ + DWORD ulFlags, /* file attributes */ + HANDLE hFile ) /* handle to template file */ +{ +#pragma unused (ulSharing, pSecAttrib, ulFlags, hFile) + + OSErr theErr; + FSRef theFileRef; + FSRef theParentRef; + short fileRef; + char permission; + static OSType gCreator; + static OSType gType; + + theErr = FSLocationFromFullPath(sFileName, &theFileRef); + if (theErr == fnfErr) + { // Create the FSRef for the parent directory. + memset(&theFileRef, 0, sizeof(FSRef)); + UInt8 folderName[MAX_PATH]; + CFStringRef folderPathCFString = CFStringCreateWithCString(NULL, sFileName, kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, folderPathCFString, kCFURLPOSIXPathStyle, FALSE); + CFURLRef folderURL = CFURLCreateCopyDeletingLastPathComponent(NULL, fileURL); + CFURLGetFileSystemRepresentation(folderURL, TRUE, folderName, MAX_PATH); + theErr = FSLocationFromFullPath(folderName, &theParentRef); + CFRelease(fileURL); + CFRelease(folderURL); + CFRelease(folderPathCFString); + } + if (theErr != noErr) + { + SetLastError(theErr); + if (ulCreation == OPEN_EXISTING || theErr != fnfErr) + return INVALID_HANDLE_VALUE; + } + + if (ulCreation != OPEN_EXISTING) + { /* We create the file */ + UniChar unicodeFileName[256]; + CFStringRef filePathCFString = CFStringCreateWithCString(NULL, sFileName, kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, filePathCFString, kCFURLPOSIXPathStyle, FALSE); + CFStringRef fileNameCFString = CFURLCopyLastPathComponent(fileURL); + CFStringGetCharacters(fileNameCFString, CFRangeMake(0, CFStringGetLength(fileNameCFString)), + unicodeFileName); + theErr = FSCreateCompat(&theParentRef, gCreator, gType, unicodeFileName, + CFStringGetLength(fileNameCFString), &theFileRef); + CFRelease(fileNameCFString); + CFRelease(filePathCFString); + CFRelease(fileURL); + if (theErr != noErr) + { + SetLastError(theErr); + return INVALID_HANDLE_VALUE; + } + } + + if (ulMode == GENERIC_READ) + permission = fsRdPerm; + else + { + if (ulMode == GENERIC_WRITE) + permission = fsWrPerm; + else + permission = fsRdWrPerm; + } + theErr = FSOpenDFCompat(&theFileRef, permission, &fileRef); + + SetLastError(theErr); + + if (theErr == noErr) + return (HANDLE)(int)fileRef; + else + return INVALID_HANDLE_VALUE; +} + +/******************************************************************** +* CloseHandle +********************************************************************/ +BOOL CloseHandle( HANDLE hFile ) /* handle to object */ +{ + OSErr theErr; + + if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE)) + return FALSE; + + theErr = FSCloseFork((short)(int)hFile); + + SetLastError(theErr); + + return theErr != noErr; +} + +/******************************************************************** +* GetFileSize +********************************************************************/ +DWORD GetFileSize( HANDLE hFile, /* handle to file */ + DWORD *ulOffSetHigh ) /* high-order word of file size */ +{ + SInt64 fileLength; + OSErr theErr; + + if ((hFile == NULL) || (hFile == INVALID_HANDLE_VALUE)) + { + SetLastError(theErr); + return -1u; + } + + theErr = FSGetForkSize((short)(int)hFile, &fileLength); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + if (ulOffSetHigh != NULL) + *ulOffSetHigh = fileLength >> 32; + + SetLastError(theErr); + + return fileLength; +} + +/******************************************************************** +* SetFilePointer +* pOffSetHigh: NULL +* ulMethod: FILE_BEGIN, FILE_CURRENT +********************************************************************/ +DWORD SetFilePointer( HANDLE hFile, /* handle to file */ + LONG lOffSetLow, /* bytes to move pointer */ + LONG *pOffSetHigh, /* bytes to move pointer */ + DWORD ulMethod ) /* starting point */ +{ + OSErr theErr; + + if (ulMethod == FILE_CURRENT) + { + SInt64 bytesToMove; + + if (pOffSetHigh != NULL) + bytesToMove = ((SInt64)*pOffSetHigh << 32) + lOffSetLow; + else + bytesToMove = lOffSetLow; + + SInt64 newPos; + + theErr = FSSetForkPosition((short)(int)hFile, fsFromMark, bytesToMove); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + theErr = FSGetForkPosition((short)(int)hFile, &newPos); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + if (pOffSetHigh != NULL) + *pOffSetHigh = newPos >> 32; + + SetLastError(theErr); + return newPos; + } + else if (ulMethod == FILE_BEGIN) + { + SInt64 bytesToMove; + + if (pOffSetHigh != NULL) + bytesToMove = ((SInt64)*pOffSetHigh << 32) + lOffSetLow; + else + bytesToMove = lOffSetLow; + + theErr = FSSetForkPosition((short)(int)hFile, fsFromStart, bytesToMove); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + SetLastError(theErr); + return lOffSetLow; + } + else + { + SInt64 bytesToMove; + + if (pOffSetHigh != NULL) + bytesToMove = ((SInt64)*pOffSetHigh << 32) + lOffSetLow; + else + bytesToMove = lOffSetLow; + + SInt64 newPos; + + theErr = FSSetForkPosition((short)(int)hFile, fsFromLEOF, bytesToMove); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + theErr = FSGetForkPosition((short)(int)hFile, &newPos); + if (theErr != noErr) + { + SetLastError(theErr); + return -1u; + } + + if (pOffSetHigh != NULL) + *pOffSetHigh = newPos >> 32; + + SetLastError(theErr); + return newPos; + } +} + +/******************************************************************** +* SetEndOfFile +********************************************************************/ +BOOL SetEndOfFile( HANDLE hFile ) /* handle to file */ +{ + OSErr theErr; + + theErr = FSSetForkSize((short)(int)hFile, fsAtMark, 0); + + SetLastError(theErr); + + return theErr == noErr; +} + +/******************************************************************** +* ReadFile +* pOverLapped: NULL +********************************************************************/ +BOOL ReadFile( HANDLE hFile, /* handle to file */ + void *pBuffer, /* data buffer */ + DWORD ulLen, /* number of bytes to read */ + DWORD *ulRead, /* number of bytes read */ + void *pOverLapped ) /* overlapped buffer */ +{ +#pragma unused (pOverLapped) + + ByteCount nbCharsRead; + OSErr theErr; + + nbCharsRead = ulLen; + theErr = FSReadFork((short)(int)hFile, fsAtMark, 0, nbCharsRead, pBuffer, &nbCharsRead); + *ulRead = nbCharsRead; + + SetLastError(theErr); + + return theErr == noErr; +} + +/******************************************************************** +* WriteFile +* pOverLapped: NULL +********************************************************************/ +BOOL WriteFile( HANDLE hFile, /* handle to file */ + const void *pBuffer, /* data buffer */ + DWORD ulLen, /* number of bytes to write */ + DWORD *ulWritten, /* number of bytes written */ + void *pOverLapped ) /* overlapped buffer */ +{ +#pragma unused (pOverLapped) + + ByteCount nbCharsToWrite; + OSErr theErr; + + nbCharsToWrite = ulLen; + theErr = FSWriteFork((short)(int)hFile, fsAtMark, 0, nbCharsToWrite, pBuffer, &nbCharsToWrite); + *ulWritten = nbCharsToWrite; + + SetLastError(theErr); + + return theErr == noErr; +} + +// Check if a memory block is accessible for reading. It's probably too +// hard to check on Mac, so sorry, we'll just have to crash +BOOL IsBadReadPtr(const void * ptr, int size) +{ +#pragma unused (ptr, size) + + return FALSE; +} + +// Returns attributes of a file. Actually, it doesn't, it just checks if +// the file exists, since that's all StormLib uses it for +DWORD GetFileAttributes(const char * szFileName) +{ + FSRef theRef; + OSErr theErr; + + theErr = FSLocationFromFullPath(szFileName, &theRef); + + if (theErr != noErr) + return -1u; + else + return 0; +} + +#endif diff --git a/src/tools/stuffextract/StormLib/bzip2/blocksort.c b/src/tools/stuffextract/StormLib/bzip2/blocksort.c new file mode 100644 index 0000000..33ec9f5 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/blocksort.c @@ -0,0 +1,1141 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. + + To get some idea how the block sorting algorithms in this file + work, read my paper + On the Performance of BWT Sorting Algorithms + in Proceedings of the IEEE Data Compression Conference 2000, + Snowbird, Utah, USA, 27-30 March 2000. The main sort in this + file implements the algorithm called cache in the paper. +--*/ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/bzip2.c b/src/tools/stuffextract/StormLib/bzip2/bzip2.c new file mode 100644 index 0000000..79f87a5 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/bzip2.c @@ -0,0 +1,2107 @@ + +/*-----------------------------------------------------------*/ +/*--- A block-sorting, lossless compressor bzip2.c ---*/ +/*-----------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +/*----------------------------------------------------*/ +/*--- IMPORTANT ---*/ +/*----------------------------------------------------*/ + +/*-- + WARNING: + This program and library (attempts to) compress data by + performing several non-trivial transformations on it. + Unless you are 100% familiar with *all* the algorithms + contained herein, and with the consequences of modifying them, + you should NOT meddle with the compression or decompression + machinery. Incorrect changes can and very likely *will* + lead to disasterous loss of data. + + DISCLAIMER: + I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE + USE OF THIS PROGRAM, HOWSOEVER CAUSED. + + Every compression of a file implies an assumption that the + compressed file can be decompressed to reproduce the original. + Great efforts in design, coding and testing have been made to + ensure that this program works correctly. However, the + complexity of the algorithms, and, in particular, the presence + of various special cases in the code which occur with very low + but non-zero probability make it impossible to rule out the + possibility of bugs remaining in the program. DO NOT COMPRESS + ANY DATA WITH THIS PROGRAM AND/OR LIBRARY UNLESS YOU ARE PREPARED + TO ACCEPT THE POSSIBILITY, HOWEVER SMALL, THAT THE DATA WILL + NOT BE RECOVERABLE. + + That is not to say this program is inherently unreliable. + Indeed, I very much hope the opposite is true. bzip2/libbzip2 + has been carefully constructed and extensively tested. + + PATENTS: + To the best of my knowledge, bzip2/libbzip2 does not use any + patented algorithms. However, I do not have the resources + available to carry out a full patent search. Therefore I cannot + give any guarantee of the above statement. +--*/ + + + +/*----------------------------------------------------*/ +/*--- and now for something much more pleasant :-) ---*/ +/*----------------------------------------------------*/ + +/*---------------------------------------------*/ +/*-- + Place a 1 beside your platform, and 0 elsewhere. +--*/ + +/*-- + Generic 32-bit Unix. + Also works on 64-bit Unix boxes. + This is the default. +--*/ +#define BZ_UNIX 1 + +/*-- + Win32, as seen by Jacob Navia's excellent + port of (Chris Fraser & David Hanson)'s excellent + lcc compiler. Or with MS Visual C. + This is selected automatically if compiled by a compiler which + defines _WIN32, not including the Cygwin GCC. +--*/ +#define BZ_LCCWIN32 0 + +#if defined(_WIN32) && !defined(__CYGWIN__) +#undef BZ_LCCWIN32 +#define BZ_LCCWIN32 1 +#undef BZ_UNIX +#define BZ_UNIX 0 +#endif + + +/*---------------------------------------------*/ +/*-- + Some stuff for all platforms. +--*/ + +#include +#include +#include +#include +#include +#include +#include +#include "bzlib.h" + +#define ERROR_IF_EOF(i) { if ((i) == EOF) ioError(); } +#define ERROR_IF_NOT_ZERO(i) { if ((i) != 0) ioError(); } +#define ERROR_IF_MINUS_ONE(i) { if ((i) == (-1)) ioError(); } + + +/*---------------------------------------------*/ +/*-- + Platform-specific stuff. +--*/ + +#if BZ_UNIX +# include +# include +# include +# include +# include +# include + +# define PATH_SEP '/' +# define MY_LSTAT lstat +# define MY_STAT stat +# define MY_S_ISREG S_ISREG +# define MY_S_ISDIR S_ISDIR + +# define APPEND_FILESPEC(root, name) \ + root=snocString((root), (name)) + +# define APPEND_FLAG(root, name) \ + root=snocString((root), (name)) + +# define SET_BINARY_MODE(fd) /**/ + +# ifdef __GNUC__ +# define NORETURN __attribute__ ((noreturn)) +# else +# define NORETURN /**/ +# endif + +# ifdef __DJGPP__ +# include +# include +# undef MY_LSTAT +# undef MY_STAT +# define MY_LSTAT stat +# define MY_STAT stat +# undef SET_BINARY_MODE +# define SET_BINARY_MODE(fd) \ + do { \ + int retVal = setmode ( fileno ( fd ), \ + O_BINARY ); \ + ERROR_IF_MINUS_ONE ( retVal ); \ + } while ( 0 ) +# endif + +# ifdef __CYGWIN__ +# include +# include +# undef SET_BINARY_MODE +# define SET_BINARY_MODE(fd) \ + do { \ + int retVal = setmode ( fileno ( fd ), \ + O_BINARY ); \ + ERROR_IF_MINUS_ONE ( retVal ); \ + } while ( 0 ) +# endif +#endif /* BZ_UNIX */ + + + +#if BZ_LCCWIN32 +# include +# include +# include + +# define NORETURN /**/ +# define PATH_SEP '\\' +# define MY_LSTAT _stat +# define MY_STAT _stat +# define MY_S_ISREG(x) ((x) & _S_IFREG) +# define MY_S_ISDIR(x) ((x) & _S_IFDIR) + +# define APPEND_FLAG(root, name) \ + root=snocString((root), (name)) + +# define APPEND_FILESPEC(root, name) \ + root = snocString ((root), (name)) + +# define SET_BINARY_MODE(fd) \ + do { \ + int retVal = setmode ( fileno ( fd ), \ + O_BINARY ); \ + ERROR_IF_MINUS_ONE ( retVal ); \ + } while ( 0 ) + +#endif /* BZ_LCCWIN32 */ + + +/*---------------------------------------------*/ +/*-- + Some more stuff for all platforms :-) +--*/ + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +/*-- + IntNative is your platform's `native' int size. + Only here to avoid probs with 64-bit platforms. +--*/ +typedef int IntNative; + + +/*---------------------------------------------------*/ +/*--- Misc (file handling) data decls ---*/ +/*---------------------------------------------------*/ + +Int32 verbosity; +Bool keepInputFiles, smallMode, deleteOutputOnInterrupt; +Bool forceOverwrite, testFailsExist, unzFailsExist, noisy; +Int32 numFileNames, numFilesProcessed, blockSize100k; +Int32 exitValue; + +/*-- source modes; F==file, I==stdin, O==stdout --*/ +#define SM_I2O 1 +#define SM_F2O 2 +#define SM_F2F 3 + +/*-- operation modes --*/ +#define OM_Z 1 +#define OM_UNZ 2 +#define OM_TEST 3 + +Int32 opMode; +Int32 srcMode; + +#define FILE_NAME_LEN 1034 + +Int32 longestFileName; +Char inName [FILE_NAME_LEN]; +Char outName[FILE_NAME_LEN]; +Char tmpName[FILE_NAME_LEN]; +Char *progName; +Char progNameReally[FILE_NAME_LEN]; +FILE *outputHandleJustInCase; +Int32 workFactor; + +static void panic ( Char* ) NORETURN; +static void ioError ( void ) NORETURN; +static void outOfMemory ( void ) NORETURN; +static void configError ( void ) NORETURN; +static void crcError ( void ) NORETURN; +static void cleanUpAndFail ( Int32 ) NORETURN; +static void compressedStreamEOF ( void ) NORETURN; + +static void copyFileName ( Char*, Char* ); +static void* myMalloc ( Int32 ); + + + +/*---------------------------------------------------*/ +/*--- An implementation of 64-bit ints. Sigh. ---*/ +/*--- Roll on widespread deployment of ANSI C9X ! ---*/ +/*---------------------------------------------------*/ + +typedef + struct { UChar b[8]; } + UInt64; + + +static +void uInt64_from_UInt32s ( UInt64* n, UInt32 lo32, UInt32 hi32 ) +{ + n->b[7] = (UChar)((hi32 >> 24) & 0xFF); + n->b[6] = (UChar)((hi32 >> 16) & 0xFF); + n->b[5] = (UChar)((hi32 >> 8) & 0xFF); + n->b[4] = (UChar) (hi32 & 0xFF); + n->b[3] = (UChar)((lo32 >> 24) & 0xFF); + n->b[2] = (UChar)((lo32 >> 16) & 0xFF); + n->b[1] = (UChar)((lo32 >> 8) & 0xFF); + n->b[0] = (UChar) (lo32 & 0xFF); +} + + +static +double uInt64_to_double ( UInt64* n ) +{ + Int32 i; + double base = 1.0; + double sum = 0.0; + for (i = 0; i < 8; i++) { + sum += base * (double)(n->b[i]); + base *= 256.0; + } + return sum; +} + + +static +Bool uInt64_isZero ( UInt64* n ) +{ + Int32 i; + for (i = 0; i < 8; i++) + if (n->b[i] != 0) return 0; + return 1; +} + + +/* Divide *n by 10, and return the remainder. */ +static +Int32 uInt64_qrm10 ( UInt64* n ) +{ + UInt32 rem, tmp; + Int32 i; + rem = 0; + for (i = 7; i >= 0; i--) { + tmp = rem * 256 + n->b[i]; + n->b[i] = tmp / 10; + rem = tmp % 10; + } + return rem; +} + + +/* ... and the Whole Entire Point of all this UInt64 stuff is + so that we can supply the following function. +*/ +static +void uInt64_toAscii ( char* outbuf, UInt64* n ) +{ + Int32 i, q; + UChar buf[32]; + Int32 nBuf = 0; + UInt64 n_copy = *n; + do { + q = uInt64_qrm10 ( &n_copy ); + buf[nBuf] = q + '0'; + nBuf++; + } while (!uInt64_isZero(&n_copy)); + outbuf[nBuf] = 0; + for (i = 0; i < nBuf; i++) + outbuf[i] = buf[nBuf-i-1]; +} + + +/*---------------------------------------------------*/ +/*--- Processing of complete files and streams ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------*/ +static +Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------*/ +static +void compressStream ( FILE *stream, FILE *zStream ) +{ + BZFILE* bzf = NULL; + UChar ibuf[5000]; + Int32 nIbuf; + UInt32 nbytes_in_lo32, nbytes_in_hi32; + UInt32 nbytes_out_lo32, nbytes_out_hi32; + Int32 bzerr, bzerr_dummy, ret; + + SET_BINARY_MODE(stream); + SET_BINARY_MODE(zStream); + + if (ferror(stream)) goto errhandler_io; + if (ferror(zStream)) goto errhandler_io; + + bzf = BZ2_bzWriteOpen ( &bzerr, zStream, + blockSize100k, verbosity, workFactor ); + if (bzerr != BZ_OK) goto errhandler; + + if (verbosity >= 2) fprintf ( stderr, "\n" ); + + while (True) { + + if (myfeof(stream)) break; + nIbuf = fread ( ibuf, sizeof(UChar), 5000, stream ); + if (ferror(stream)) goto errhandler_io; + if (nIbuf > 0) BZ2_bzWrite ( &bzerr, bzf, (void*)ibuf, nIbuf ); + if (bzerr != BZ_OK) goto errhandler; + + } + + BZ2_bzWriteClose64 ( &bzerr, bzf, 0, + &nbytes_in_lo32, &nbytes_in_hi32, + &nbytes_out_lo32, &nbytes_out_hi32 ); + if (bzerr != BZ_OK) goto errhandler; + + if (ferror(zStream)) goto errhandler_io; + ret = fflush ( zStream ); + if (ret == EOF) goto errhandler_io; + if (zStream != stdout) { + ret = fclose ( zStream ); + outputHandleJustInCase = NULL; + if (ret == EOF) goto errhandler_io; + } + outputHandleJustInCase = NULL; + if (ferror(stream)) goto errhandler_io; + ret = fclose ( stream ); + if (ret == EOF) goto errhandler_io; + + if (verbosity >= 1) { + if (nbytes_in_lo32 == 0 && nbytes_in_hi32 == 0) { + fprintf ( stderr, " no data compressed.\n"); + } else { + Char buf_nin[32], buf_nout[32]; + UInt64 nbytes_in, nbytes_out; + double nbytes_in_d, nbytes_out_d; + uInt64_from_UInt32s ( &nbytes_in, + nbytes_in_lo32, nbytes_in_hi32 ); + uInt64_from_UInt32s ( &nbytes_out, + nbytes_out_lo32, nbytes_out_hi32 ); + nbytes_in_d = uInt64_to_double ( &nbytes_in ); + nbytes_out_d = uInt64_to_double ( &nbytes_out ); + uInt64_toAscii ( buf_nin, &nbytes_in ); + uInt64_toAscii ( buf_nout, &nbytes_out ); + fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, " + "%5.2f%% saved, %s in, %s out.\n", + nbytes_in_d / nbytes_out_d, + (8.0 * nbytes_out_d) / nbytes_in_d, + 100.0 * (1.0 - nbytes_out_d / nbytes_in_d), + buf_nin, + buf_nout + ); + } + } + + return; + + errhandler: + BZ2_bzWriteClose64 ( &bzerr_dummy, bzf, 1, + &nbytes_in_lo32, &nbytes_in_hi32, + &nbytes_out_lo32, &nbytes_out_hi32 ); + switch (bzerr) { + case BZ_CONFIG_ERROR: + configError(); break; + case BZ_MEM_ERROR: + outOfMemory (); break; + case BZ_IO_ERROR: + errhandler_io: + ioError(); break; + default: + panic ( "compress:unexpected error" ); + } + + panic ( "compress:end" ); + /*notreached*/ +} + + + +/*---------------------------------------------*/ +static +Bool uncompressStream ( FILE *zStream, FILE *stream ) +{ + BZFILE* bzf = NULL; + Int32 bzerr, bzerr_dummy, ret, nread, streamNo, i; + UChar obuf[5000]; + UChar unused[BZ_MAX_UNUSED]; + Int32 nUnused; + void* unusedTmpV; + UChar* unusedTmp; + + nUnused = 0; + streamNo = 0; + + SET_BINARY_MODE(stream); + SET_BINARY_MODE(zStream); + + if (ferror(stream)) goto errhandler_io; + if (ferror(zStream)) goto errhandler_io; + + while (True) { + + bzf = BZ2_bzReadOpen ( + &bzerr, zStream, verbosity, + (int)smallMode, unused, nUnused + ); + if (bzf == NULL || bzerr != BZ_OK) goto errhandler; + streamNo++; + + while (bzerr == BZ_OK) { + nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 ); + if (bzerr == BZ_DATA_ERROR_MAGIC) goto trycat; + if ((bzerr == BZ_OK || bzerr == BZ_STREAM_END) && nread > 0) + fwrite ( obuf, sizeof(UChar), nread, stream ); + if (ferror(stream)) goto errhandler_io; + } + if (bzerr != BZ_STREAM_END) goto errhandler; + + BZ2_bzReadGetUnused ( &bzerr, bzf, &unusedTmpV, &nUnused ); + if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" ); + + unusedTmp = (UChar*)unusedTmpV; + for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i]; + + BZ2_bzReadClose ( &bzerr, bzf ); + if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" ); + + if (nUnused == 0 && myfeof(zStream)) break; + } + + closeok: + if (ferror(zStream)) goto errhandler_io; + ret = fclose ( zStream ); + if (ret == EOF) goto errhandler_io; + + if (ferror(stream)) goto errhandler_io; + ret = fflush ( stream ); + if (ret != 0) goto errhandler_io; + if (stream != stdout) { + ret = fclose ( stream ); + outputHandleJustInCase = NULL; + if (ret == EOF) goto errhandler_io; + } + outputHandleJustInCase = NULL; + if (verbosity >= 2) fprintf ( stderr, "\n " ); + return True; + + trycat: + if (forceOverwrite) { + rewind(zStream); + while (True) { + if (myfeof(zStream)) break; + nread = fread ( obuf, sizeof(UChar), 5000, zStream ); + if (ferror(zStream)) goto errhandler_io; + if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream ); + if (ferror(stream)) goto errhandler_io; + } + goto closeok; + } + + errhandler: + BZ2_bzReadClose ( &bzerr_dummy, bzf ); + switch (bzerr) { + case BZ_CONFIG_ERROR: + configError(); break; + case BZ_IO_ERROR: + errhandler_io: + ioError(); break; + case BZ_DATA_ERROR: + crcError(); + case BZ_MEM_ERROR: + outOfMemory(); + case BZ_UNEXPECTED_EOF: + compressedStreamEOF(); + case BZ_DATA_ERROR_MAGIC: + if (zStream != stdin) fclose(zStream); + if (stream != stdout) fclose(stream); + if (streamNo == 1) { + return False; + } else { + if (noisy) + fprintf ( stderr, + "\n%s: %s: trailing garbage after EOF ignored\n", + progName, inName ); + return True; + } + default: + panic ( "decompress:unexpected error" ); + } + + panic ( "decompress:end" ); + return True; /*notreached*/ +} + + +/*---------------------------------------------*/ +static +Bool testStream ( FILE *zStream ) +{ + BZFILE* bzf = NULL; + Int32 bzerr, bzerr_dummy, ret, nread, streamNo, i; + UChar obuf[5000]; + UChar unused[BZ_MAX_UNUSED]; + Int32 nUnused; + void* unusedTmpV; + UChar* unusedTmp; + + nUnused = 0; + streamNo = 0; + + SET_BINARY_MODE(zStream); + if (ferror(zStream)) goto errhandler_io; + + while (True) { + + bzf = BZ2_bzReadOpen ( + &bzerr, zStream, verbosity, + (int)smallMode, unused, nUnused + ); + if (bzf == NULL || bzerr != BZ_OK) goto errhandler; + streamNo++; + + while (bzerr == BZ_OK) { + nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 ); + if (bzerr == BZ_DATA_ERROR_MAGIC) goto errhandler; + } + if (bzerr != BZ_STREAM_END) goto errhandler; + + BZ2_bzReadGetUnused ( &bzerr, bzf, &unusedTmpV, &nUnused ); + if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" ); + + unusedTmp = (UChar*)unusedTmpV; + for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i]; + + BZ2_bzReadClose ( &bzerr, bzf ); + if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" ); + if (nUnused == 0 && myfeof(zStream)) break; + + } + + if (ferror(zStream)) goto errhandler_io; + ret = fclose ( zStream ); + if (ret == EOF) goto errhandler_io; + + if (verbosity >= 2) fprintf ( stderr, "\n " ); + return True; + + errhandler: + BZ2_bzReadClose ( &bzerr_dummy, bzf ); + if (verbosity == 0) + fprintf ( stderr, "%s: %s: ", progName, inName ); + switch (bzerr) { + case BZ_CONFIG_ERROR: + configError(); break; + case BZ_IO_ERROR: + errhandler_io: + ioError(); break; + case BZ_DATA_ERROR: + fprintf ( stderr, + "data integrity (CRC) error in data\n" ); + return False; + case BZ_MEM_ERROR: + outOfMemory(); + case BZ_UNEXPECTED_EOF: + fprintf ( stderr, + "file ends unexpectedly\n" ); + return False; + case BZ_DATA_ERROR_MAGIC: + if (zStream != stdin) fclose(zStream); + if (streamNo == 1) { + fprintf ( stderr, + "bad magic number (file not created by bzip2)\n" ); + return False; + } else { + if (noisy) + fprintf ( stderr, + "trailing garbage after EOF ignored\n" ); + return True; + } + default: + panic ( "test:unexpected error" ); + } + + panic ( "test:end" ); + return True; /*notreached*/ +} + + +/*---------------------------------------------------*/ +/*--- Error [non-] handling grunge ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------*/ +static +void setExit ( Int32 v ) +{ + if (v > exitValue) exitValue = v; +} + + +/*---------------------------------------------*/ +static +void cadvise ( void ) +{ + if (noisy) + fprintf ( + stderr, + "\nIt is possible that the compressed file(s) have become corrupted.\n" + "You can use the -tvv option to test integrity of such files.\n\n" + "You can use the `bzip2recover' program to attempt to recover\n" + "data from undamaged sections of corrupted files.\n\n" + ); +} + + +/*---------------------------------------------*/ +static +void showFileNames ( void ) +{ + if (noisy) + fprintf ( + stderr, + "\tInput file = %s, output file = %s\n", + inName, outName + ); +} + + +/*---------------------------------------------*/ +static +void cleanUpAndFail ( Int32 ec ) +{ + IntNative retVal; + struct MY_STAT statBuf; + + if ( srcMode == SM_F2F + && opMode != OM_TEST + && deleteOutputOnInterrupt ) { + + /* Check whether input file still exists. Delete output file + only if input exists to avoid loss of data. Joerg Prante, 5 + January 2002. (JRS 06-Jan-2002: other changes in 1.0.2 mean + this is less likely to happen. But to be ultra-paranoid, we + do the check anyway.) */ + retVal = MY_STAT ( inName, &statBuf ); + if (retVal == 0) { + if (noisy) + fprintf ( stderr, + "%s: Deleting output file %s, if it exists.\n", + progName, outName ); + if (outputHandleJustInCase != NULL) + fclose ( outputHandleJustInCase ); + retVal = remove ( outName ); + if (retVal != 0) + fprintf ( stderr, + "%s: WARNING: deletion of output file " + "(apparently) failed.\n", + progName ); + } else { + fprintf ( stderr, + "%s: WARNING: deletion of output file suppressed\n", + progName ); + fprintf ( stderr, + "%s: since input file no longer exists. Output file\n", + progName ); + fprintf ( stderr, + "%s: `%s' may be incomplete.\n", + progName, outName ); + fprintf ( stderr, + "%s: I suggest doing an integrity test (bzip2 -tv)" + " of it.\n", + progName ); + } + } + + if (noisy && numFileNames > 0 && numFilesProcessed < numFileNames) { + fprintf ( stderr, + "%s: WARNING: some files have not been processed:\n" + "%s: %d specified on command line, %d not processed yet.\n\n", + progName, progName, + numFileNames, numFileNames - numFilesProcessed ); + } + setExit(ec); + exit(exitValue); +} + + +/*---------------------------------------------*/ +static +void panic ( Char* s ) +{ + fprintf ( stderr, + "\n%s: PANIC -- internal consistency error:\n" + "\t%s\n" + "\tThis is a BUG. Please report it to me at:\n" + "\tjseward@bzip.org\n", + progName, s ); + showFileNames(); + cleanUpAndFail( 3 ); +} + + +/*---------------------------------------------*/ +static +void crcError ( void ) +{ + fprintf ( stderr, + "\n%s: Data integrity error when decompressing.\n", + progName ); + showFileNames(); + cadvise(); + cleanUpAndFail( 2 ); +} + + +/*---------------------------------------------*/ +static +void compressedStreamEOF ( void ) +{ + if (noisy) { + fprintf ( stderr, + "\n%s: Compressed file ends unexpectedly;\n\t" + "perhaps it is corrupted? *Possible* reason follows.\n", + progName ); + perror ( progName ); + showFileNames(); + cadvise(); + } + cleanUpAndFail( 2 ); +} + + +/*---------------------------------------------*/ +static +void ioError ( void ) +{ + fprintf ( stderr, + "\n%s: I/O or other error, bailing out. " + "Possible reason follows.\n", + progName ); + perror ( progName ); + showFileNames(); + cleanUpAndFail( 1 ); +} + + +/*---------------------------------------------*/ +static +void mySignalCatcher ( IntNative n ) +{ + fprintf ( stderr, + "\n%s: Control-C or similar caught, quitting.\n", + progName ); + cleanUpAndFail(1); +} + + +/*---------------------------------------------*/ +static +void mySIGSEGVorSIGBUScatcher ( IntNative n ) +{ + if (opMode == OM_Z) + fprintf ( + stderr, + "\n%s: Caught a SIGSEGV or SIGBUS whilst compressing.\n" + "\n" + " Possible causes are (most likely first):\n" + " (1) This computer has unreliable memory or cache hardware\n" + " (a surprisingly common problem; try a different machine.)\n" + " (2) A bug in the compiler used to create this executable\n" + " (unlikely, if you didn't compile bzip2 yourself.)\n" + " (3) A real bug in bzip2 -- I hope this should never be the case.\n" + " The user's manual, Section 4.3, has more info on (1) and (2).\n" + " \n" + " If you suspect this is a bug in bzip2, or are unsure about (1)\n" + " or (2), feel free to report it to me at: jseward@bzip.org.\n" + " Section 4.3 of the user's manual describes the info a useful\n" + " bug report should have. If the manual is available on your\n" + " system, please try and read it before mailing me. If you don't\n" + " have the manual or can't be bothered to read it, mail me anyway.\n" + "\n", + progName ); + else + fprintf ( + stderr, + "\n%s: Caught a SIGSEGV or SIGBUS whilst decompressing.\n" + "\n" + " Possible causes are (most likely first):\n" + " (1) The compressed data is corrupted, and bzip2's usual checks\n" + " failed to detect this. Try bzip2 -tvv my_file.bz2.\n" + " (2) This computer has unreliable memory or cache hardware\n" + " (a surprisingly common problem; try a different machine.)\n" + " (3) A bug in the compiler used to create this executable\n" + " (unlikely, if you didn't compile bzip2 yourself.)\n" + " (4) A real bug in bzip2 -- I hope this should never be the case.\n" + " The user's manual, Section 4.3, has more info on (2) and (3).\n" + " \n" + " If you suspect this is a bug in bzip2, or are unsure about (2)\n" + " or (3), feel free to report it to me at: jseward@bzip.org.\n" + " Section 4.3 of the user's manual describes the info a useful\n" + " bug report should have. If the manual is available on your\n" + " system, please try and read it before mailing me. If you don't\n" + " have the manual or can't be bothered to read it, mail me anyway.\n" + "\n", + progName ); + + showFileNames(); + if (opMode == OM_Z) + cleanUpAndFail( 3 ); else + { cadvise(); cleanUpAndFail( 2 ); } +} + + +/*---------------------------------------------*/ +static +void outOfMemory ( void ) +{ + fprintf ( stderr, + "\n%s: couldn't allocate enough memory\n", + progName ); + showFileNames(); + cleanUpAndFail(1); +} + + +/*---------------------------------------------*/ +static +void configError ( void ) +{ + fprintf ( stderr, + "bzip2: I'm not configured correctly for this platform!\n" + "\tI require Int32, Int16 and Char to have sizes\n" + "\tof 4, 2 and 1 bytes to run properly, and they don't.\n" + "\tProbably you can fix this by defining them correctly,\n" + "\tand recompiling. Bye!\n" ); + setExit(3); + exit(exitValue); +} + + +/*---------------------------------------------------*/ +/*--- The main driver machinery ---*/ +/*---------------------------------------------------*/ + +/* All rather crufty. The main problem is that input files + are stat()d multiple times before use. This should be + cleaned up. +*/ + +/*---------------------------------------------*/ +static +void pad ( Char *s ) +{ + Int32 i; + if ( (Int32)strlen(s) >= longestFileName ) return; + for (i = 1; i <= longestFileName - (Int32)strlen(s); i++) + fprintf ( stderr, " " ); +} + + +/*---------------------------------------------*/ +static +void copyFileName ( Char* to, Char* from ) +{ + if ( strlen(from) > FILE_NAME_LEN-10 ) { + fprintf ( + stderr, + "bzip2: file name\n`%s'\n" + "is suspiciously (more than %d chars) long.\n" + "Try using a reasonable file name instead. Sorry! :-)\n", + from, FILE_NAME_LEN-10 + ); + setExit(1); + exit(exitValue); + } + + strncpy(to,from,FILE_NAME_LEN-10); + to[FILE_NAME_LEN-10]='\0'; +} + + +/*---------------------------------------------*/ +static +Bool fileExists ( Char* name ) +{ + FILE *tmp = fopen ( name, "rb" ); + Bool exists = (tmp != NULL); + if (tmp != NULL) fclose ( tmp ); + return exists; +} + + +/*---------------------------------------------*/ +/* Open an output file safely with O_EXCL and good permissions. + This avoids a race condition in versions < 1.0.2, in which + the file was first opened and then had its interim permissions + set safely. We instead use open() to create the file with + the interim permissions required. (--- --- rw-). + + For non-Unix platforms, if we are not worrying about + security issues, simple this simply behaves like fopen. +*/ +FILE* fopen_output_safely ( Char* name, const char* mode ) +{ +# if BZ_UNIX + FILE* fp; + IntNative fh; + fh = open(name, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR); + if (fh == -1) return NULL; + fp = fdopen(fh, mode); + if (fp == NULL) close(fh); + return fp; +# else + return fopen(name, mode); +# endif +} + + +/*---------------------------------------------*/ +/*-- + if in doubt, return True +--*/ +static +Bool notAStandardFile ( Char* name ) +{ + IntNative i; + struct MY_STAT statBuf; + + i = MY_LSTAT ( name, &statBuf ); + if (i != 0) return True; + if (MY_S_ISREG(statBuf.st_mode)) return False; + return True; +} + + +/*---------------------------------------------*/ +/*-- + rac 11/21/98 see if file has hard links to it +--*/ +static +Int32 countHardLinks ( Char* name ) +{ + IntNative i; + struct MY_STAT statBuf; + + i = MY_LSTAT ( name, &statBuf ); + if (i != 0) return 0; + return (statBuf.st_nlink - 1); +} + + +/*---------------------------------------------*/ +/* Copy modification date, access date, permissions and owner from the + source to destination file. We have to copy this meta-info off + into fileMetaInfo before starting to compress / decompress it, + because doing it afterwards means we get the wrong access time. + + To complicate matters, in compress() and decompress() below, the + sequence of tests preceding the call to saveInputFileMetaInfo() + involves calling fileExists(), which in turn establishes its result + by attempting to fopen() the file, and if successful, immediately + fclose()ing it again. So we have to assume that the fopen() call + does not cause the access time field to be updated. + + Reading of the man page for stat() (man 2 stat) on RedHat 7.2 seems + to imply that merely doing open() will not affect the access time. + Therefore we merely need to hope that the C library only does + open() as a result of fopen(), and not any kind of read()-ahead + cleverness. + + It sounds pretty fragile to me. Whether this carries across + robustly to arbitrary Unix-like platforms (or even works robustly + on this one, RedHat 7.2) is unknown to me. Nevertheless ... +*/ +#if BZ_UNIX +static +struct MY_STAT fileMetaInfo; +#endif + +static +void saveInputFileMetaInfo ( Char *srcName ) +{ +# if BZ_UNIX + IntNative retVal; + /* Note use of stat here, not lstat. */ + retVal = MY_STAT( srcName, &fileMetaInfo ); + ERROR_IF_NOT_ZERO ( retVal ); +# endif +} + + +static +void applySavedMetaInfoToOutputFile ( Char *dstName ) +{ +# if BZ_UNIX + IntNative retVal; + struct utimbuf uTimBuf; + + uTimBuf.actime = fileMetaInfo.st_atime; + uTimBuf.modtime = fileMetaInfo.st_mtime; + + retVal = chmod ( dstName, fileMetaInfo.st_mode ); + ERROR_IF_NOT_ZERO ( retVal ); + + retVal = utime ( dstName, &uTimBuf ); + ERROR_IF_NOT_ZERO ( retVal ); + + retVal = chown ( dstName, fileMetaInfo.st_uid, fileMetaInfo.st_gid ); + /* chown() will in many cases return with EPERM, which can + be safely ignored. + */ +# endif +} + + +/*---------------------------------------------*/ +static +Bool containsDubiousChars ( Char* name ) +{ +# if BZ_UNIX + /* On unix, files can contain any characters and the file expansion + * is performed by the shell. + */ + return False; +# else /* ! BZ_UNIX */ + /* On non-unix (Win* platforms), wildcard characters are not allowed in + * filenames. + */ + for (; *name != '\0'; name++) + if (*name == '?' || *name == '*') return True; + return False; +# endif /* BZ_UNIX */ +} + + +/*---------------------------------------------*/ +#define BZ_N_SUFFIX_PAIRS 4 + +Char* zSuffix[BZ_N_SUFFIX_PAIRS] + = { ".bz2", ".bz", ".tbz2", ".tbz" }; +Char* unzSuffix[BZ_N_SUFFIX_PAIRS] + = { "", "", ".tar", ".tar" }; + +static +Bool hasSuffix ( Char* s, Char* suffix ) +{ + Int32 ns = strlen(s); + Int32 nx = strlen(suffix); + if (ns < nx) return False; + if (strcmp(s + ns - nx, suffix) == 0) return True; + return False; +} + +static +Bool mapSuffix ( Char* name, + Char* oldSuffix, Char* newSuffix ) +{ + if (!hasSuffix(name,oldSuffix)) return False; + name[strlen(name)-strlen(oldSuffix)] = 0; + strcat ( name, newSuffix ); + return True; +} + + +/*---------------------------------------------*/ +static +void compress ( Char *name ) +{ + FILE *inStr; + FILE *outStr; + Int32 n, i; + struct MY_STAT statBuf; + + deleteOutputOnInterrupt = False; + + if (name == NULL && srcMode != SM_I2O) + panic ( "compress: bad modes\n" ); + + switch (srcMode) { + case SM_I2O: + copyFileName ( inName, "(stdin)" ); + copyFileName ( outName, "(stdout)" ); + break; + case SM_F2F: + copyFileName ( inName, name ); + copyFileName ( outName, name ); + strcat ( outName, ".bz2" ); + break; + case SM_F2O: + copyFileName ( inName, name ); + copyFileName ( outName, "(stdout)" ); + break; + } + + if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) { + if (noisy) + fprintf ( stderr, "%s: There are no files matching `%s'.\n", + progName, inName ); + setExit(1); + return; + } + if ( srcMode != SM_I2O && !fileExists ( inName ) ) { + fprintf ( stderr, "%s: Can't open input file %s: %s.\n", + progName, inName, strerror(errno) ); + setExit(1); + return; + } + for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++) { + if (hasSuffix(inName, zSuffix[i])) { + if (noisy) + fprintf ( stderr, + "%s: Input file %s already has %s suffix.\n", + progName, inName, zSuffix[i] ); + setExit(1); + return; + } + } + if ( srcMode == SM_F2F || srcMode == SM_F2O ) { + MY_STAT(inName, &statBuf); + if ( MY_S_ISDIR(statBuf.st_mode) ) { + fprintf( stderr, + "%s: Input file %s is a directory.\n", + progName,inName); + setExit(1); + return; + } + } + if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) { + if (noisy) + fprintf ( stderr, "%s: Input file %s is not a normal file.\n", + progName, inName ); + setExit(1); + return; + } + if ( srcMode == SM_F2F && fileExists ( outName ) ) { + if (forceOverwrite) { + remove(outName); + } else { + fprintf ( stderr, "%s: Output file %s already exists.\n", + progName, outName ); + setExit(1); + return; + } + } + if ( srcMode == SM_F2F && !forceOverwrite && + (n=countHardLinks ( inName )) > 0) { + fprintf ( stderr, "%s: Input file %s has %d other link%s.\n", + progName, inName, n, n > 1 ? "s" : "" ); + setExit(1); + return; + } + + if ( srcMode == SM_F2F ) { + /* Save the file's meta-info before we open it. Doing it later + means we mess up the access times. */ + saveInputFileMetaInfo ( inName ); + } + + switch ( srcMode ) { + + case SM_I2O: + inStr = stdin; + outStr = stdout; + if ( isatty ( fileno ( stdout ) ) ) { + fprintf ( stderr, + "%s: I won't write compressed data to a terminal.\n", + progName ); + fprintf ( stderr, "%s: For help, type: `%s --help'.\n", + progName, progName ); + setExit(1); + return; + }; + break; + + case SM_F2O: + inStr = fopen ( inName, "rb" ); + outStr = stdout; + if ( isatty ( fileno ( stdout ) ) ) { + fprintf ( stderr, + "%s: I won't write compressed data to a terminal.\n", + progName ); + fprintf ( stderr, "%s: For help, type: `%s --help'.\n", + progName, progName ); + if ( inStr != NULL ) fclose ( inStr ); + setExit(1); + return; + }; + if ( inStr == NULL ) { + fprintf ( stderr, "%s: Can't open input file %s: %s.\n", + progName, inName, strerror(errno) ); + setExit(1); + return; + }; + break; + + case SM_F2F: + inStr = fopen ( inName, "rb" ); + outStr = fopen_output_safely ( outName, "wb" ); + if ( outStr == NULL) { + fprintf ( stderr, "%s: Can't create output file %s: %s.\n", + progName, outName, strerror(errno) ); + if ( inStr != NULL ) fclose ( inStr ); + setExit(1); + return; + } + if ( inStr == NULL ) { + fprintf ( stderr, "%s: Can't open input file %s: %s.\n", + progName, inName, strerror(errno) ); + if ( outStr != NULL ) fclose ( outStr ); + setExit(1); + return; + }; + break; + + default: + panic ( "compress: bad srcMode" ); + break; + } + + if (verbosity >= 1) { + fprintf ( stderr, " %s: ", inName ); + pad ( inName ); + fflush ( stderr ); + } + + /*--- Now the input and output handles are sane. Do the Biz. ---*/ + outputHandleJustInCase = outStr; + deleteOutputOnInterrupt = True; + compressStream ( inStr, outStr ); + outputHandleJustInCase = NULL; + + /*--- If there was an I/O error, we won't get here. ---*/ + if ( srcMode == SM_F2F ) { + applySavedMetaInfoToOutputFile ( outName ); + deleteOutputOnInterrupt = False; + if ( !keepInputFiles ) { + IntNative retVal = remove ( inName ); + ERROR_IF_NOT_ZERO ( retVal ); + } + } + + deleteOutputOnInterrupt = False; +} + + +/*---------------------------------------------*/ +static +void uncompress ( Char *name ) +{ + FILE *inStr; + FILE *outStr; + Int32 n, i; + Bool magicNumberOK; + Bool cantGuess; + struct MY_STAT statBuf; + + deleteOutputOnInterrupt = False; + + if (name == NULL && srcMode != SM_I2O) + panic ( "uncompress: bad modes\n" ); + + cantGuess = False; + switch (srcMode) { + case SM_I2O: + copyFileName ( inName, "(stdin)" ); + copyFileName ( outName, "(stdout)" ); + break; + case SM_F2F: + copyFileName ( inName, name ); + copyFileName ( outName, name ); + for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++) + if (mapSuffix(outName,zSuffix[i],unzSuffix[i])) + goto zzz; + cantGuess = True; + strcat ( outName, ".out" ); + break; + case SM_F2O: + copyFileName ( inName, name ); + copyFileName ( outName, "(stdout)" ); + break; + } + + zzz: + if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) { + if (noisy) + fprintf ( stderr, "%s: There are no files matching `%s'.\n", + progName, inName ); + setExit(1); + return; + } + if ( srcMode != SM_I2O && !fileExists ( inName ) ) { + fprintf ( stderr, "%s: Can't open input file %s: %s.\n", + progName, inName, strerror(errno) ); + setExit(1); + return; + } + if ( srcMode == SM_F2F || srcMode == SM_F2O ) { + MY_STAT(inName, &statBuf); + if ( MY_S_ISDIR(statBuf.st_mode) ) { + fprintf( stderr, + "%s: Input file %s is a directory.\n", + progName,inName); + setExit(1); + return; + } + } + if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) { + if (noisy) + fprintf ( stderr, "%s: Input file %s is not a normal file.\n", + progName, inName ); + setExit(1); + return; + } + if ( /* srcMode == SM_F2F implied && */ cantGuess ) { + if (noisy) + fprintf ( stderr, + "%s: Can't guess original name for %s -- using %s\n", + progName, inName, outName ); + /* just a warning, no return */ + } + if ( srcMode == SM_F2F && fileExists ( outName ) ) { + if (forceOverwrite) { + remove(outName); + } else { + fprintf ( stderr, "%s: Output file %s already exists.\n", + progName, outName ); + setExit(1); + return; + } + } + if ( srcMode == SM_F2F && !forceOverwrite && + (n=countHardLinks ( inName ) ) > 0) { + fprintf ( stderr, "%s: Input file %s has %d other link%s.\n", + progName, inName, n, n > 1 ? "s" : "" ); + setExit(1); + return; + } + + if ( srcMode == SM_F2F ) { + /* Save the file's meta-info before we open it. Doing it later + means we mess up the access times. */ + saveInputFileMetaInfo ( inName ); + } + + switch ( srcMode ) { + + case SM_I2O: + inStr = stdin; + outStr = stdout; + if ( isatty ( fileno ( stdin ) ) ) { + fprintf ( stderr, + "%s: I won't read compressed data from a terminal.\n", + progName ); + fprintf ( stderr, "%s: For help, type: `%s --help'.\n", + progName, progName ); + setExit(1); + return; + }; + break; + + case SM_F2O: + inStr = fopen ( inName, "rb" ); + outStr = stdout; + if ( inStr == NULL ) { + fprintf ( stderr, "%s: Can't open input file %s:%s.\n", + progName, inName, strerror(errno) ); + if ( inStr != NULL ) fclose ( inStr ); + setExit(1); + return; + }; + break; + + case SM_F2F: + inStr = fopen ( inName, "rb" ); + outStr = fopen_output_safely ( outName, "wb" ); + if ( outStr == NULL) { + fprintf ( stderr, "%s: Can't create output file %s: %s.\n", + progName, outName, strerror(errno) ); + if ( inStr != NULL ) fclose ( inStr ); + setExit(1); + return; + } + if ( inStr == NULL ) { + fprintf ( stderr, "%s: Can't open input file %s: %s.\n", + progName, inName, strerror(errno) ); + if ( outStr != NULL ) fclose ( outStr ); + setExit(1); + return; + }; + break; + + default: + panic ( "uncompress: bad srcMode" ); + break; + } + + if (verbosity >= 1) { + fprintf ( stderr, " %s: ", inName ); + pad ( inName ); + fflush ( stderr ); + } + + /*--- Now the input and output handles are sane. Do the Biz. ---*/ + outputHandleJustInCase = outStr; + deleteOutputOnInterrupt = True; + magicNumberOK = uncompressStream ( inStr, outStr ); + outputHandleJustInCase = NULL; + + /*--- If there was an I/O error, we won't get here. ---*/ + if ( magicNumberOK ) { + if ( srcMode == SM_F2F ) { + applySavedMetaInfoToOutputFile ( outName ); + deleteOutputOnInterrupt = False; + if ( !keepInputFiles ) { + IntNative retVal = remove ( inName ); + ERROR_IF_NOT_ZERO ( retVal ); + } + } + } else { + unzFailsExist = True; + deleteOutputOnInterrupt = False; + if ( srcMode == SM_F2F ) { + IntNative retVal = remove ( outName ); + ERROR_IF_NOT_ZERO ( retVal ); + } + } + deleteOutputOnInterrupt = False; + + if ( magicNumberOK ) { + if (verbosity >= 1) + fprintf ( stderr, "done\n" ); + } else { + setExit(2); + if (verbosity >= 1) + fprintf ( stderr, "not a bzip2 file.\n" ); else + fprintf ( stderr, + "%s: %s is not a bzip2 file.\n", + progName, inName ); + } + +} + + +/*---------------------------------------------*/ +static +void testf ( Char *name ) +{ + FILE *inStr; + Bool allOK; + struct MY_STAT statBuf; + + deleteOutputOnInterrupt = False; + + if (name == NULL && srcMode != SM_I2O) + panic ( "testf: bad modes\n" ); + + copyFileName ( outName, "(none)" ); + switch (srcMode) { + case SM_I2O: copyFileName ( inName, "(stdin)" ); break; + case SM_F2F: copyFileName ( inName, name ); break; + case SM_F2O: copyFileName ( inName, name ); break; + } + + if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) { + if (noisy) + fprintf ( stderr, "%s: There are no files matching `%s'.\n", + progName, inName ); + setExit(1); + return; + } + if ( srcMode != SM_I2O && !fileExists ( inName ) ) { + fprintf ( stderr, "%s: Can't open input %s: %s.\n", + progName, inName, strerror(errno) ); + setExit(1); + return; + } + if ( srcMode != SM_I2O ) { + MY_STAT(inName, &statBuf); + if ( MY_S_ISDIR(statBuf.st_mode) ) { + fprintf( stderr, + "%s: Input file %s is a directory.\n", + progName,inName); + setExit(1); + return; + } + } + + switch ( srcMode ) { + + case SM_I2O: + if ( isatty ( fileno ( stdin ) ) ) { + fprintf ( stderr, + "%s: I won't read compressed data from a terminal.\n", + progName ); + fprintf ( stderr, "%s: For help, type: `%s --help'.\n", + progName, progName ); + setExit(1); + return; + }; + inStr = stdin; + break; + + case SM_F2O: case SM_F2F: + inStr = fopen ( inName, "rb" ); + if ( inStr == NULL ) { + fprintf ( stderr, "%s: Can't open input file %s:%s.\n", + progName, inName, strerror(errno) ); + setExit(1); + return; + }; + break; + + default: + panic ( "testf: bad srcMode" ); + break; + } + + if (verbosity >= 1) { + fprintf ( stderr, " %s: ", inName ); + pad ( inName ); + fflush ( stderr ); + } + + /*--- Now the input handle is sane. Do the Biz. ---*/ + outputHandleJustInCase = NULL; + allOK = testStream ( inStr ); + + if (allOK && verbosity >= 1) fprintf ( stderr, "ok\n" ); + if (!allOK) testFailsExist = True; +} + + +/*---------------------------------------------*/ +static +void license ( void ) +{ + fprintf ( stderr, + + "bzip2, a block-sorting file compressor. " + "Version %s.\n" + " \n" + " Copyright (C) 1996-2005 by Julian Seward.\n" + " \n" + " This program is free software; you can redistribute it and/or modify\n" + " it under the terms set out in the LICENSE file, which is included\n" + " in the bzip2-1.0 source distribution.\n" + " \n" + " This program is distributed in the hope that it will be useful,\n" + " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + " LICENSE file for more details.\n" + " \n", + BZ2_bzlibVersion() + ); +} + + +/*---------------------------------------------*/ +static +void usage ( Char *fullProgName ) +{ + fprintf ( + stderr, + "bzip2, a block-sorting file compressor. " + "Version %s.\n" + "\n usage: %s [flags and input files in any order]\n" + "\n" + " -h --help print this message\n" + " -d --decompress force decompression\n" + " -z --compress force compression\n" + " -k --keep keep (don't delete) input files\n" + " -f --force overwrite existing output files\n" + " -t --test test compressed file integrity\n" + " -c --stdout output to standard out\n" + " -q --quiet suppress noncritical error messages\n" + " -v --verbose be verbose (a 2nd -v gives more)\n" + " -L --license display software version & license\n" + " -V --version display software version & license\n" + " -s --small use less memory (at most 2500k)\n" + " -1 .. -9 set block size to 100k .. 900k\n" + " --fast alias for -1\n" + " --best alias for -9\n" + "\n" + " If invoked as `bzip2', default action is to compress.\n" + " as `bunzip2', default action is to decompress.\n" + " as `bzcat', default action is to decompress to stdout.\n" + "\n" + " If no file names are given, bzip2 compresses or decompresses\n" + " from standard input to standard output. You can combine\n" + " short flags, so `-v -4' means the same as -v4 or -4v, &c.\n" +# if BZ_UNIX + "\n" +# endif + , + + BZ2_bzlibVersion(), + fullProgName + ); +} + + +/*---------------------------------------------*/ +static +void redundant ( Char* flag ) +{ + fprintf ( + stderr, + "%s: %s is redundant in versions 0.9.5 and above\n", + progName, flag ); +} + + +/*---------------------------------------------*/ +/*-- + All the garbage from here to main() is purely to + implement a linked list of command-line arguments, + into which main() copies argv[1 .. argc-1]. + + The purpose of this exercise is to facilitate + the expansion of wildcard characters * and ? in + filenames for OSs which don't know how to do it + themselves, like MSDOS, Windows 95 and NT. + + The actual Dirty Work is done by the platform- + specific macro APPEND_FILESPEC. +--*/ + +typedef + struct zzzz { + Char *name; + struct zzzz *link; + } + Cell; + + +/*---------------------------------------------*/ +static +void *myMalloc ( Int32 n ) +{ + void* p; + + p = malloc ( (size_t)n ); + if (p == NULL) outOfMemory (); + return p; +} + + +/*---------------------------------------------*/ +static +Cell *mkCell ( void ) +{ + Cell *c; + + c = (Cell*) myMalloc ( sizeof ( Cell ) ); + c->name = NULL; + c->link = NULL; + return c; +} + + +/*---------------------------------------------*/ +static +Cell *snocString ( Cell *root, Char *name ) +{ + if (root == NULL) { + Cell *tmp = mkCell(); + tmp->name = (Char*) myMalloc ( 5 + strlen(name) ); + strcpy ( tmp->name, name ); + return tmp; + } else { + Cell *tmp = root; + while (tmp->link != NULL) tmp = tmp->link; + tmp->link = snocString ( tmp->link, name ); + return root; + } +} + + +/*---------------------------------------------*/ +static +void addFlagsFromEnvVar ( Cell** argList, Char* varName ) +{ + Int32 i, j, k; + Char *envbase, *p; + + envbase = getenv(varName); + if (envbase != NULL) { + p = envbase; + i = 0; + while (True) { + if (p[i] == 0) break; + p += i; + i = 0; + while (isspace((Int32)(p[0]))) p++; + while (p[i] != 0 && !isspace((Int32)(p[i]))) i++; + if (i > 0) { + k = i; if (k > FILE_NAME_LEN-10) k = FILE_NAME_LEN-10; + for (j = 0; j < k; j++) tmpName[j] = p[j]; + tmpName[k] = 0; + APPEND_FLAG(*argList, tmpName); + } + } + } +} + + +/*---------------------------------------------*/ +#define ISFLAG(s) (strcmp(aa->name, (s))==0) + +IntNative main ( IntNative argc, Char *argv[] ) +{ + Int32 i, j; + Char *tmp; + Cell *argList; + Cell *aa; + Bool decode; + + /*-- Be really really really paranoid :-) --*/ + if (sizeof(Int32) != 4 || sizeof(UInt32) != 4 || + sizeof(Int16) != 2 || sizeof(UInt16) != 2 || + sizeof(Char) != 1 || sizeof(UChar) != 1) + configError(); + + /*-- Initialise --*/ + outputHandleJustInCase = NULL; + smallMode = False; + keepInputFiles = False; + forceOverwrite = False; + noisy = True; + verbosity = 0; + blockSize100k = 9; + testFailsExist = False; + unzFailsExist = False; + numFileNames = 0; + numFilesProcessed = 0; + workFactor = 30; + deleteOutputOnInterrupt = False; + exitValue = 0; + i = j = 0; /* avoid bogus warning from egcs-1.1.X */ + + /*-- Set up signal handlers for mem access errors --*/ + signal (SIGSEGV, mySIGSEGVorSIGBUScatcher); +# if BZ_UNIX +# ifndef __DJGPP__ + signal (SIGBUS, mySIGSEGVorSIGBUScatcher); +# endif +# endif + + copyFileName ( inName, "(none)" ); + copyFileName ( outName, "(none)" ); + + copyFileName ( progNameReally, argv[0] ); + progName = &progNameReally[0]; + for (tmp = &progNameReally[0]; *tmp != '\0'; tmp++) + if (*tmp == PATH_SEP) progName = tmp + 1; + + + /*-- Copy flags from env var BZIP2, and + expand filename wildcards in arg list. + --*/ + argList = NULL; + addFlagsFromEnvVar ( &argList, "BZIP2" ); + addFlagsFromEnvVar ( &argList, "BZIP" ); + for (i = 1; i <= argc-1; i++) + APPEND_FILESPEC(argList, argv[i]); + + + /*-- Find the length of the longest filename --*/ + longestFileName = 7; + numFileNames = 0; + decode = True; + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) { decode = False; continue; } + if (aa->name[0] == '-' && decode) continue; + numFileNames++; + if (longestFileName < (Int32)strlen(aa->name) ) + longestFileName = (Int32)strlen(aa->name); + } + + + /*-- Determine source modes; flag handling may change this too. --*/ + if (numFileNames == 0) + srcMode = SM_I2O; else srcMode = SM_F2F; + + + /*-- Determine what to do (compress/uncompress/test/cat). --*/ + /*-- Note that subsequent flag handling may change this. --*/ + opMode = OM_Z; + + if ( (strstr ( progName, "unzip" ) != 0) || + (strstr ( progName, "UNZIP" ) != 0) ) + opMode = OM_UNZ; + + if ( (strstr ( progName, "z2cat" ) != 0) || + (strstr ( progName, "Z2CAT" ) != 0) || + (strstr ( progName, "zcat" ) != 0) || + (strstr ( progName, "ZCAT" ) != 0) ) { + opMode = OM_UNZ; + srcMode = (numFileNames == 0) ? SM_I2O : SM_F2O; + } + + + /*-- Look at the flags. --*/ + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) break; + if (aa->name[0] == '-' && aa->name[1] != '-') { + for (j = 1; aa->name[j] != '\0'; j++) { + switch (aa->name[j]) { + case 'c': srcMode = SM_F2O; break; + case 'd': opMode = OM_UNZ; break; + case 'z': opMode = OM_Z; break; + case 'f': forceOverwrite = True; break; + case 't': opMode = OM_TEST; break; + case 'k': keepInputFiles = True; break; + case 's': smallMode = True; break; + case 'q': noisy = False; break; + case '1': blockSize100k = 1; break; + case '2': blockSize100k = 2; break; + case '3': blockSize100k = 3; break; + case '4': blockSize100k = 4; break; + case '5': blockSize100k = 5; break; + case '6': blockSize100k = 6; break; + case '7': blockSize100k = 7; break; + case '8': blockSize100k = 8; break; + case '9': blockSize100k = 9; break; + case 'V': + case 'L': license(); break; + case 'v': verbosity++; break; + case 'h': usage ( progName ); + exit ( 0 ); + break; + default: fprintf ( stderr, "%s: Bad flag `%s'\n", + progName, aa->name ); + usage ( progName ); + exit ( 1 ); + break; + } + } + } + } + + /*-- And again ... --*/ + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) break; + if (ISFLAG("--stdout")) srcMode = SM_F2O; else + if (ISFLAG("--decompress")) opMode = OM_UNZ; else + if (ISFLAG("--compress")) opMode = OM_Z; else + if (ISFLAG("--force")) forceOverwrite = True; else + if (ISFLAG("--test")) opMode = OM_TEST; else + if (ISFLAG("--keep")) keepInputFiles = True; else + if (ISFLAG("--small")) smallMode = True; else + if (ISFLAG("--quiet")) noisy = False; else + if (ISFLAG("--version")) license(); else + if (ISFLAG("--license")) license(); else + if (ISFLAG("--exponential")) workFactor = 1; else + if (ISFLAG("--repetitive-best")) redundant(aa->name); else + if (ISFLAG("--repetitive-fast")) redundant(aa->name); else + if (ISFLAG("--fast")) blockSize100k = 1; else + if (ISFLAG("--best")) blockSize100k = 9; else + if (ISFLAG("--verbose")) verbosity++; else + if (ISFLAG("--help")) { usage ( progName ); exit ( 0 ); } + else + if (strncmp ( aa->name, "--", 2) == 0) { + fprintf ( stderr, "%s: Bad flag `%s'\n", progName, aa->name ); + usage ( progName ); + exit ( 1 ); + } + } + + if (verbosity > 4) verbosity = 4; + if (opMode == OM_Z && smallMode && blockSize100k > 2) + blockSize100k = 2; + + if (opMode == OM_TEST && srcMode == SM_F2O) { + fprintf ( stderr, "%s: -c and -t cannot be used together.\n", + progName ); + exit ( 1 ); + } + + if (srcMode == SM_F2O && numFileNames == 0) + srcMode = SM_I2O; + + if (opMode != OM_Z) blockSize100k = 0; + + if (srcMode == SM_F2F) { + signal (SIGINT, mySignalCatcher); + signal (SIGTERM, mySignalCatcher); +# if BZ_UNIX + signal (SIGHUP, mySignalCatcher); +# endif + } + + if (opMode == OM_Z) { + if (srcMode == SM_I2O) { + compress ( NULL ); + } else { + decode = True; + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) { decode = False; continue; } + if (aa->name[0] == '-' && decode) continue; + numFilesProcessed++; + compress ( aa->name ); + } + } + } + else + + if (opMode == OM_UNZ) { + unzFailsExist = False; + if (srcMode == SM_I2O) { + uncompress ( NULL ); + } else { + decode = True; + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) { decode = False; continue; } + if (aa->name[0] == '-' && decode) continue; + numFilesProcessed++; + uncompress ( aa->name ); + } + } + if (unzFailsExist) { + setExit(2); + exit(exitValue); + } + } + + else { + testFailsExist = False; + if (srcMode == SM_I2O) { + testf ( NULL ); + } else { + decode = True; + for (aa = argList; aa != NULL; aa = aa->link) { + if (ISFLAG("--")) { decode = False; continue; } + if (aa->name[0] == '-' && decode) continue; + numFilesProcessed++; + testf ( aa->name ); + } + } + if (testFailsExist && noisy) { + fprintf ( stderr, + "\n" + "You can use the `bzip2recover' program to attempt to recover\n" + "data from undamaged sections of corrupted files.\n\n" + ); + setExit(2); + exit(exitValue); + } + } + + /* Free the argument list memory to mollify leak detectors + (eg) Purify, Checker. Serves no other useful purpose. + */ + aa = argList; + while (aa != NULL) { + Cell* aa2 = aa->link; + if (aa->name != NULL) free(aa->name); + free(aa); + aa = aa2; + } + + return exitValue; +} + + +/*-----------------------------------------------------------*/ +/*--- end bzip2.c ---*/ +/*-----------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/bzip2recover.c b/src/tools/stuffextract/StormLib/bzip2/bzip2recover.c new file mode 100644 index 0000000..5cd405d --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/bzip2recover.c @@ -0,0 +1,546 @@ + +/*-----------------------------------------------------------*/ +/*--- Block recoverer program for bzip2 ---*/ +/*--- bzip2recover.c ---*/ +/*-----------------------------------------------------------*/ + +/*-- + This program is bzip2recover, a program to attempt data + salvage from damaged files created by the accompanying + bzip2-1.0.3 program. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0.3 of 15 February 2005 +--*/ + +/*-- + This program is a complete hack and should be rewritten + properly. It isn't very complicated. +--*/ + +#include +#include +#include +#include + + +/* This program records bit locations in the file to be recovered. + That means that if 64-bit ints are not supported, we will not + be able to recover .bz2 files over 512MB (2^32 bits) long. + On GNU supported platforms, we take advantage of the 64-bit + int support to circumvent this problem. Ditto MSVC. + + This change occurred in version 1.0.2; all prior versions have + the 512MB limitation. +*/ +#ifdef __GNUC__ + typedef unsigned long long int MaybeUInt64; +# define MaybeUInt64_FMT "%Lu" +#else +#ifdef _MSC_VER + typedef unsigned __int64 MaybeUInt64; +# define MaybeUInt64_FMT "%I64u" +#else + typedef unsigned int MaybeUInt64; +# define MaybeUInt64_FMT "%u" +#endif +#endif + +typedef unsigned int UInt32; +typedef int Int32; +typedef unsigned char UChar; +typedef char Char; +typedef unsigned char Bool; +#define True ((Bool)1) +#define False ((Bool)0) + + +#define BZ_MAX_FILENAME 2000 + +Char inFileName[BZ_MAX_FILENAME]; +Char outFileName[BZ_MAX_FILENAME]; +Char progName[BZ_MAX_FILENAME]; + +MaybeUInt64 bytesOut = 0; +MaybeUInt64 bytesIn = 0; + + +/*---------------------------------------------------*/ +/*--- Header bytes ---*/ +/*---------------------------------------------------*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + + +/*---------------------------------------------------*/ +/*--- I/O errors ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------*/ +void readError ( void ) +{ + fprintf ( stderr, + "%s: I/O error reading `%s', possible reason follows.\n", + progName, inFileName ); + perror ( progName ); + fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", + progName ); + exit ( 1 ); +} + + +/*---------------------------------------------*/ +void writeError ( void ) +{ + fprintf ( stderr, + "%s: I/O error reading `%s', possible reason follows.\n", + progName, inFileName ); + perror ( progName ); + fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", + progName ); + exit ( 1 ); +} + + +/*---------------------------------------------*/ +void mallocFail ( Int32 n ) +{ + fprintf ( stderr, + "%s: malloc failed on request for %d bytes.\n", + progName, n ); + fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", + progName ); + exit ( 1 ); +} + + +/*---------------------------------------------*/ +void tooManyBlocks ( Int32 max_handled_blocks ) +{ + fprintf ( stderr, + "%s: `%s' appears to contain more than %d blocks\n", + progName, inFileName, max_handled_blocks ); + fprintf ( stderr, + "%s: and cannot be handled. To fix, increase\n", + progName ); + fprintf ( stderr, + "%s: BZ_MAX_HANDLED_BLOCKS in bzip2recover.c, and recompile.\n", + progName ); + exit ( 1 ); +} + + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +typedef + struct { + FILE* handle; + Int32 buffer; + Int32 buffLive; + Char mode; + } + BitStream; + + +/*---------------------------------------------*/ +BitStream* bsOpenReadStream ( FILE* stream ) +{ + BitStream *bs = malloc ( sizeof(BitStream) ); + if (bs == NULL) mallocFail ( sizeof(BitStream) ); + bs->handle = stream; + bs->buffer = 0; + bs->buffLive = 0; + bs->mode = 'r'; + return bs; +} + + +/*---------------------------------------------*/ +BitStream* bsOpenWriteStream ( FILE* stream ) +{ + BitStream *bs = malloc ( sizeof(BitStream) ); + if (bs == NULL) mallocFail ( sizeof(BitStream) ); + bs->handle = stream; + bs->buffer = 0; + bs->buffLive = 0; + bs->mode = 'w'; + return bs; +} + + +/*---------------------------------------------*/ +void bsPutBit ( BitStream* bs, Int32 bit ) +{ + if (bs->buffLive == 8) { + Int32 retVal = putc ( (UChar) bs->buffer, bs->handle ); + if (retVal == EOF) writeError(); + bytesOut++; + bs->buffLive = 1; + bs->buffer = bit & 0x1; + } else { + bs->buffer = ( (bs->buffer << 1) | (bit & 0x1) ); + bs->buffLive++; + }; +} + + +/*---------------------------------------------*/ +/*-- + Returns 0 or 1, or 2 to indicate EOF. +--*/ +Int32 bsGetBit ( BitStream* bs ) +{ + if (bs->buffLive > 0) { + bs->buffLive --; + return ( ((bs->buffer) >> (bs->buffLive)) & 0x1 ); + } else { + Int32 retVal = getc ( bs->handle ); + if ( retVal == EOF ) { + if (errno != 0) readError(); + return 2; + } + bs->buffLive = 7; + bs->buffer = retVal; + return ( ((bs->buffer) >> 7) & 0x1 ); + } +} + + +/*---------------------------------------------*/ +void bsClose ( BitStream* bs ) +{ + Int32 retVal; + + if ( bs->mode == 'w' ) { + while ( bs->buffLive < 8 ) { + bs->buffLive++; + bs->buffer <<= 1; + }; + retVal = putc ( (UChar) (bs->buffer), bs->handle ); + if (retVal == EOF) writeError(); + bytesOut++; + retVal = fflush ( bs->handle ); + if (retVal == EOF) writeError(); + } + retVal = fclose ( bs->handle ); + if (retVal == EOF) { + if (bs->mode == 'w') writeError(); else readError(); + } + free ( bs ); +} + + +/*---------------------------------------------*/ +void bsPutUChar ( BitStream* bs, UChar c ) +{ + Int32 i; + for (i = 7; i >= 0; i--) + bsPutBit ( bs, (((UInt32) c) >> i) & 0x1 ); +} + + +/*---------------------------------------------*/ +void bsPutUInt32 ( BitStream* bs, UInt32 c ) +{ + Int32 i; + + for (i = 31; i >= 0; i--) + bsPutBit ( bs, (c >> i) & 0x1 ); +} + + +/*---------------------------------------------*/ +Bool endsInBz2 ( Char* name ) +{ + Int32 n = strlen ( name ); + if (n <= 4) return False; + return + (name[n-4] == '.' && + name[n-3] == 'b' && + name[n-2] == 'z' && + name[n-1] == '2'); +} + + +/*---------------------------------------------------*/ +/*--- ---*/ +/*---------------------------------------------------*/ + +/* This logic isn't really right when it comes to Cygwin. */ +#ifdef _WIN32 +# define BZ_SPLIT_SYM '\\' /* path splitter on Windows platform */ +#else +# define BZ_SPLIT_SYM '/' /* path splitter on Unix platform */ +#endif + +#define BLOCK_HEADER_HI 0x00003141UL +#define BLOCK_HEADER_LO 0x59265359UL + +#define BLOCK_ENDMARK_HI 0x00001772UL +#define BLOCK_ENDMARK_LO 0x45385090UL + +/* Increase if necessary. However, a .bz2 file with > 50000 blocks + would have an uncompressed size of at least 40GB, so the chances + are low you'll need to up this. +*/ +#define BZ_MAX_HANDLED_BLOCKS 50000 + +MaybeUInt64 bStart [BZ_MAX_HANDLED_BLOCKS]; +MaybeUInt64 bEnd [BZ_MAX_HANDLED_BLOCKS]; +MaybeUInt64 rbStart[BZ_MAX_HANDLED_BLOCKS]; +MaybeUInt64 rbEnd [BZ_MAX_HANDLED_BLOCKS]; + +Int32 main ( Int32 argc, Char** argv ) +{ + FILE* inFile; + FILE* outFile; + BitStream* bsIn, *bsWr; + Int32 b, wrBlock, currBlock, rbCtr; + MaybeUInt64 bitsRead; + + UInt32 buffHi, buffLo, blockCRC; + Char* p; + + strcpy ( progName, argv[0] ); + inFileName[0] = outFileName[0] = 0; + + fprintf ( stderr, + "bzip2recover 1.0.3: extracts blocks from damaged .bz2 files.\n" ); + + if (argc != 2) { + fprintf ( stderr, "%s: usage is `%s damaged_file_name'.\n", + progName, progName ); + switch (sizeof(MaybeUInt64)) { + case 8: + fprintf(stderr, + "\trestrictions on size of recovered file: None\n"); + break; + case 4: + fprintf(stderr, + "\trestrictions on size of recovered file: 512 MB\n"); + fprintf(stderr, + "\tto circumvent, recompile with MaybeUInt64 as an\n" + "\tunsigned 64-bit int.\n"); + break; + default: + fprintf(stderr, + "\tsizeof(MaybeUInt64) is not 4 or 8 -- " + "configuration error.\n"); + break; + } + exit(1); + } + + if (strlen(argv[1]) >= BZ_MAX_FILENAME-20) { + fprintf ( stderr, + "%s: supplied filename is suspiciously (>= %d chars) long. Bye!\n", + progName, (int)strlen(argv[1]) ); + exit(1); + } + + strcpy ( inFileName, argv[1] ); + + inFile = fopen ( inFileName, "rb" ); + if (inFile == NULL) { + fprintf ( stderr, "%s: can't read `%s'\n", progName, inFileName ); + exit(1); + } + + bsIn = bsOpenReadStream ( inFile ); + fprintf ( stderr, "%s: searching for block boundaries ...\n", progName ); + + bitsRead = 0; + buffHi = buffLo = 0; + currBlock = 0; + bStart[currBlock] = 0; + + rbCtr = 0; + + while (True) { + b = bsGetBit ( bsIn ); + bitsRead++; + if (b == 2) { + if (bitsRead >= bStart[currBlock] && + (bitsRead - bStart[currBlock]) >= 40) { + bEnd[currBlock] = bitsRead-1; + if (currBlock > 0) + fprintf ( stderr, " block %d runs from " MaybeUInt64_FMT + " to " MaybeUInt64_FMT " (incomplete)\n", + currBlock, bStart[currBlock], bEnd[currBlock] ); + } else + currBlock--; + break; + } + buffHi = (buffHi << 1) | (buffLo >> 31); + buffLo = (buffLo << 1) | (b & 1); + if ( ( (buffHi & 0x0000ffff) == BLOCK_HEADER_HI + && buffLo == BLOCK_HEADER_LO) + || + ( (buffHi & 0x0000ffff) == BLOCK_ENDMARK_HI + && buffLo == BLOCK_ENDMARK_LO) + ) { + if (bitsRead > 49) { + bEnd[currBlock] = bitsRead-49; + } else { + bEnd[currBlock] = 0; + } + if (currBlock > 0 && + (bEnd[currBlock] - bStart[currBlock]) >= 130) { + fprintf ( stderr, " block %d runs from " MaybeUInt64_FMT + " to " MaybeUInt64_FMT "\n", + rbCtr+1, bStart[currBlock], bEnd[currBlock] ); + rbStart[rbCtr] = bStart[currBlock]; + rbEnd[rbCtr] = bEnd[currBlock]; + rbCtr++; + } + if (currBlock >= BZ_MAX_HANDLED_BLOCKS) + tooManyBlocks(BZ_MAX_HANDLED_BLOCKS); + currBlock++; + + bStart[currBlock] = bitsRead; + } + } + + bsClose ( bsIn ); + + /*-- identified blocks run from 1 to rbCtr inclusive. --*/ + + if (rbCtr < 1) { + fprintf ( stderr, + "%s: sorry, I couldn't find any block boundaries.\n", + progName ); + exit(1); + }; + + fprintf ( stderr, "%s: splitting into blocks\n", progName ); + + inFile = fopen ( inFileName, "rb" ); + if (inFile == NULL) { + fprintf ( stderr, "%s: can't open `%s'\n", progName, inFileName ); + exit(1); + } + bsIn = bsOpenReadStream ( inFile ); + + /*-- placate gcc's dataflow analyser --*/ + blockCRC = 0; bsWr = 0; + + bitsRead = 0; + outFile = NULL; + wrBlock = 0; + while (True) { + b = bsGetBit(bsIn); + if (b == 2) break; + buffHi = (buffHi << 1) | (buffLo >> 31); + buffLo = (buffLo << 1) | (b & 1); + if (bitsRead == 47+rbStart[wrBlock]) + blockCRC = (buffHi << 16) | (buffLo >> 16); + + if (outFile != NULL && bitsRead >= rbStart[wrBlock] + && bitsRead <= rbEnd[wrBlock]) { + bsPutBit ( bsWr, b ); + } + + bitsRead++; + + if (bitsRead == rbEnd[wrBlock]+1) { + if (outFile != NULL) { + bsPutUChar ( bsWr, 0x17 ); bsPutUChar ( bsWr, 0x72 ); + bsPutUChar ( bsWr, 0x45 ); bsPutUChar ( bsWr, 0x38 ); + bsPutUChar ( bsWr, 0x50 ); bsPutUChar ( bsWr, 0x90 ); + bsPutUInt32 ( bsWr, blockCRC ); + bsClose ( bsWr ); + } + if (wrBlock >= rbCtr) break; + wrBlock++; + } else + if (bitsRead == rbStart[wrBlock]) { + /* Create the output file name, correctly handling leading paths. + (31.10.2001 by Sergey E. Kusikov) */ + Char* split; + Int32 ofs, k; + for (k = 0; k < BZ_MAX_FILENAME; k++) + outFileName[k] = 0; + strcpy (outFileName, inFileName); + split = strrchr (outFileName, BZ_SPLIT_SYM); + if (split == NULL) { + split = outFileName; + } else { + ++split; + } + /* Now split points to the start of the basename. */ + ofs = split - outFileName; + sprintf (split, "rec%5d", wrBlock+1); + for (p = split; *p != 0; p++) if (*p == ' ') *p = '0'; + strcat (outFileName, inFileName + ofs); + + if ( !endsInBz2(outFileName)) strcat ( outFileName, ".bz2" ); + + fprintf ( stderr, " writing block %d to `%s' ...\n", + wrBlock+1, outFileName ); + + outFile = fopen ( outFileName, "wb" ); + if (outFile == NULL) { + fprintf ( stderr, "%s: can't write `%s'\n", + progName, outFileName ); + exit(1); + } + bsWr = bsOpenWriteStream ( outFile ); + bsPutUChar ( bsWr, BZ_HDR_B ); + bsPutUChar ( bsWr, BZ_HDR_Z ); + bsPutUChar ( bsWr, BZ_HDR_h ); + bsPutUChar ( bsWr, BZ_HDR_0 + 9 ); + bsPutUChar ( bsWr, 0x31 ); bsPutUChar ( bsWr, 0x41 ); + bsPutUChar ( bsWr, 0x59 ); bsPutUChar ( bsWr, 0x26 ); + bsPutUChar ( bsWr, 0x53 ); bsPutUChar ( bsWr, 0x59 ); + } + } + + fprintf ( stderr, "%s: finished\n", progName ); + return 0; +} + + + +/*-----------------------------------------------------------*/ +/*--- end bzip2recover.c ---*/ +/*-----------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/bzlib.c b/src/tools/stuffextract/StormLib/bzip2/bzlib.c new file mode 100644 index 0000000..195f51a --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/bzlib.c @@ -0,0 +1,1617 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +/*-- + CHANGES + ~~~~~~~ + 0.9.0 -- original version. + + 0.9.0a/b -- no changes in this file. + + 0.9.0c + * made zero-length BZ_FLUSH work correctly in bzCompress(). + * fixed bzWrite/bzRead to ignore zero-length requests. + * fixed bzread to correctly handle read requests after EOF. + * wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +--*/ + +#define _CRT_SECURE_NO_DEPRECATE +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 15 February 2005.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo + (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp), + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.0c". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include +# include +# define SET_BINARY_MODE(file) _setmode(_fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = _fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp = ((bzFile *)b)->handle; + + if (b==NULL) {return;} + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/bzlib.h b/src/tools/stuffextract/StormLib/bzip2/bzlib.h new file mode 100644 index 0000000..3237243 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/bzlib.h @@ -0,0 +1,323 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include +#endif + +#ifdef _WIN32 +# include +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo + (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp), + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/bzlib_private.h b/src/tools/stuffextract/StormLib/bzip2/bzlib_private.h new file mode 100644 index 0000000..ca76fe6 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/bzlib_private.h @@ -0,0 +1,537 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include + +#ifndef BZ_NO_STDIO +#include +#include +#include +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.3, 15-Feb-2005" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) +#else +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) /* */ +#define VPrintf0(zf) /* */ +#define VPrintf1(zf,za1) /* */ +#define VPrintf2(zf,za1,za2) /* */ +#define VPrintf3(zf,za1,za2,za3) /* */ +#define VPrintf4(zf,za1,za2,za3,za4) /* */ +#define VPrintf5(zf,za1,za2,za3,za4,za5) /* */ +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/compress.c b/src/tools/stuffextract/StormLib/bzip2/compress.c new file mode 100644 index 0000000..7e0c291 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/compress.c @@ -0,0 +1,716 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +/*-- + CHANGES + ~~~~~~~ + 0.9.0 -- original version. + + 0.9.0a/b -- no changes in this file. + + 0.9.0c + * changed setting of nGroups in sendMTFValues() so as to + do a bit better on small files +--*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/crctable.c b/src/tools/stuffextract/StormLib/bzip2/crctable.c new file mode 100644 index 0000000..b6dadfc --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/crctable.c @@ -0,0 +1,144 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/decompress.c b/src/tools/stuffextract/StormLib/bzip2/decompress.c new file mode 100644 index 0000000..81c3d2c --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/decompress.c @@ -0,0 +1,666 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/dlltest.c b/src/tools/stuffextract/StormLib/bzip2/dlltest.c new file mode 100644 index 0000000..2d7dcca --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/dlltest.c @@ -0,0 +1,176 @@ +/* + minibz2 + libbz2.dll test program. + by Yoshioka Tsuneo(QWF00133@nifty.ne.jp/tsuneo-y@is.aist-nara.ac.jp) + This file is Public Domain. + welcome any email to me. + + usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename] +*/ + +#define BZ_IMPORT +#include +#include +#include "bzlib.h" +#ifdef _WIN32 +#include +#endif + + +#ifdef _WIN32 + +#define BZ2_LIBNAME "libbz2-1.0.2.DLL" + +#include +static int BZ2DLLLoaded = 0; +static HINSTANCE BZ2DLLhLib; +int BZ2DLLLoadLibrary(void) +{ + HINSTANCE hLib; + + if(BZ2DLLLoaded==1){return 0;} + hLib=LoadLibrary(BZ2_LIBNAME); + if(hLib == NULL){ + fprintf(stderr,"Can't load %s\n",BZ2_LIBNAME); + return -1; + } + BZ2_bzlibVersion=GetProcAddress(hLib,"BZ2_bzlibVersion"); + BZ2_bzopen=GetProcAddress(hLib,"BZ2_bzopen"); + BZ2_bzdopen=GetProcAddress(hLib,"BZ2_bzdopen"); + BZ2_bzread=GetProcAddress(hLib,"BZ2_bzread"); + BZ2_bzwrite=GetProcAddress(hLib,"BZ2_bzwrite"); + BZ2_bzflush=GetProcAddress(hLib,"BZ2_bzflush"); + BZ2_bzclose=GetProcAddress(hLib,"BZ2_bzclose"); + BZ2_bzerror=GetProcAddress(hLib,"BZ2_bzerror"); + + if (!BZ2_bzlibVersion || !BZ2_bzopen || !BZ2_bzdopen + || !BZ2_bzread || !BZ2_bzwrite || !BZ2_bzflush + || !BZ2_bzclose || !BZ2_bzerror) { + fprintf(stderr,"GetProcAddress failed.\n"); + return -1; + } + BZ2DLLLoaded=1; + BZ2DLLhLib=hLib; + return 0; + +} +int BZ2DLLFreeLibrary(void) +{ + if(BZ2DLLLoaded==0){return 0;} + FreeLibrary(BZ2DLLhLib); + BZ2DLLLoaded=0; +} +#endif /* WIN32 */ + +void usage(void) +{ + puts("usage: minibz2 [-d] [-{1,2,..9}] [[srcfilename] destfilename]"); +} + +int main(int argc,char *argv[]) +{ + int decompress = 0; + int level = 9; + char *fn_r = NULL; + char *fn_w = NULL; + +#ifdef _WIN32 + if(BZ2DLLLoadLibrary()<0){ + fprintf(stderr,"Loading of %s failed. Giving up.\n", BZ2_LIBNAME); + exit(1); + } + printf("Loading of %s succeeded. Library version is %s.\n", + BZ2_LIBNAME, BZ2_bzlibVersion() ); +#endif + while(++argv,--argc){ + if(**argv =='-' || **argv=='/'){ + char *p; + + for(p=*argv+1;*p;p++){ + if(*p=='d'){ + decompress = 1; + }else if('1'<=*p && *p<='9'){ + level = *p - '0'; + }else{ + usage(); + exit(1); + } + } + }else{ + break; + } + } + if(argc>=1){ + fn_r = *argv; + argc--;argv++; + }else{ + fn_r = NULL; + } + if(argc>=1){ + fn_w = *argv; + argc--;argv++; + }else{ + fn_w = NULL; + } + { + int len; + char buff[0x1000]; + char mode[10]; + + if(decompress){ + BZFILE *BZ2fp_r = NULL; + FILE *fp_w = NULL; + + if(fn_w){ + if((fp_w = fopen(fn_w,"wb"))==NULL){ + printf("can't open [%s]\n",fn_w); + perror("reason:"); + exit(1); + } + }else{ + fp_w = stdout; + } + if((fn_r == NULL && (BZ2fp_r = BZ2_bzdopen(fileno(stdin),"rb"))==NULL) + || (fn_r != NULL && (BZ2fp_r = BZ2_bzopen(fn_r,"rb"))==NULL)){ + printf("can't bz2openstream\n"); + exit(1); + } + while((len=BZ2_bzread(BZ2fp_r,buff,0x1000))>0){ + fwrite(buff,1,len,fp_w); + } + BZ2_bzclose(BZ2fp_r); + if(fp_w != stdout) fclose(fp_w); + }else{ + BZFILE *BZ2fp_w = NULL; + FILE *fp_r = NULL; + + if(fn_r){ + if((fp_r = fopen(fn_r,"rb"))==NULL){ + printf("can't open [%s]\n",fn_r); + perror("reason:"); + exit(1); + } + }else{ + fp_r = stdin; + } + mode[0]='w'; + mode[1] = '0' + level; + mode[2] = '\0'; + + if((fn_w == NULL && (BZ2fp_w = BZ2_bzdopen(fileno(stdout),mode))==NULL) + || (fn_w !=NULL && (BZ2fp_w = BZ2_bzopen(fn_w,mode))==NULL)){ + printf("can't bz2openstream\n"); + exit(1); + } + while((len=fread(buff,1,0x1000,fp_r))>0){ + BZ2_bzwrite(BZ2fp_w,buff,len); + } + BZ2_bzclose(BZ2fp_w); + if(fp_r!=stdin)fclose(fp_r); + } + } +#ifdef _WIN32 + BZ2DLLFreeLibrary(); +#endif + return 0; +} diff --git a/src/tools/stuffextract/StormLib/bzip2/huffman.c b/src/tools/stuffextract/StormLib/bzip2/huffman.c new file mode 100644 index 0000000..5bf190b --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/huffman.c @@ -0,0 +1,245 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/mk251.c b/src/tools/stuffextract/StormLib/bzip2/mk251.c new file mode 100644 index 0000000..205778a --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/mk251.c @@ -0,0 +1,16 @@ + +/* Spew out a long sequence of the byte 251. When fed to bzip2 + versions 1.0.0 or 1.0.1, causes it to die with internal error + 1007 in blocksort.c. This assertion misses an extremely rare + case, which is fixed in this version (1.0.2) and above. +*/ + +#include + +int main () +{ + int i; + for (i = 0; i < 48500000 ; i++) + putchar(251); + return 0; +} diff --git a/src/tools/stuffextract/StormLib/bzip2/randtable.c b/src/tools/stuffextract/StormLib/bzip2/randtable.c new file mode 100644 index 0000000..940462d --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/randtable.c @@ -0,0 +1,124 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2005 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@bzip.org + bzip2/libbzip2 version 1.0 of 21 March 2000 + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/tools/stuffextract/StormLib/bzip2/spewG.c b/src/tools/stuffextract/StormLib/bzip2/spewG.c new file mode 100644 index 0000000..7934e76 --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/spewG.c @@ -0,0 +1,39 @@ + +/* spew out a thoroughly gigantic file designed so that bzip2 + can compress it reasonably rapidly. This is to help test + support for large files (> 2GB) in a reasonable amount of time. + I suggest you use the undocumented --exponential option to + bzip2 when compressing the resulting file; this saves a bit of + time. Note: *don't* bother with --exponential when compressing + Real Files; it'll just waste a lot of CPU time :-) + (but is otherwise harmless). +*/ + +#define _FILE_OFFSET_BITS 64 + +#include +#include + +/* The number of megabytes of junk to spew out (roughly) */ +#define MEGABYTES 5000 + +#define N_BUF 1000000 +char buf[N_BUF]; + +int main ( int argc, char** argv ) +{ + int ii, kk, p; + srandom(1); + setbuffer ( stdout, buf, N_BUF ); + for (kk = 0; kk < MEGABYTES * 515; kk+=3) { + p = 25+random()%50; + for (ii = 0; ii < p; ii++) + printf ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ); + for (ii = 0; ii < p-1; ii++) + printf ( "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" ); + for (ii = 0; ii < p+1; ii++) + printf ( "ccccccccccccccccccccccccccccccccccccc" ); + } + fflush(stdout); + return 0; +} diff --git a/src/tools/stuffextract/StormLib/bzip2/unzcrash.c b/src/tools/stuffextract/StormLib/bzip2/unzcrash.c new file mode 100644 index 0000000..f0f17fc --- /dev/null +++ b/src/tools/stuffextract/StormLib/bzip2/unzcrash.c @@ -0,0 +1,126 @@ + +/* A test program written to test robustness to decompression of + corrupted data. Usage is + unzcrash filename + and the program will read the specified file, compress it (in memory), + and then repeatedly decompress it, each time with a different bit of + the compressed data inverted, so as to test all possible one-bit errors. + This should not cause any invalid memory accesses. If it does, + I want to know about it! + + p.s. As you can see from the above description, the process is + incredibly slow. A file of size eg 5KB will cause it to run for + many hours. +*/ + +#include +#include +#include "bzlib.h" + +#define M_BLOCK 1000000 + +typedef unsigned char uchar; + +#define M_BLOCK_OUT (M_BLOCK + 1000000) +uchar inbuf[M_BLOCK]; +uchar outbuf[M_BLOCK_OUT]; +uchar zbuf[M_BLOCK + 600 + (M_BLOCK / 100)]; + +int nIn, nOut, nZ; + +static char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + +void flip_bit ( int bit ) +{ + int byteno = bit / 8; + int bitno = bit % 8; + uchar mask = 1 << bitno; + //fprintf ( stderr, "(byte %d bit %d mask %d)", + // byteno, bitno, (int)mask ); + zbuf[byteno] ^= mask; +} + +int main ( int argc, char** argv ) +{ + FILE* f; + int r; + int bit; + int i; + + if (argc != 2) { + fprintf ( stderr, "usage: unzcrash filename\n" ); + return 1; + } + + f = fopen ( argv[1], "r" ); + if (!f) { + fprintf ( stderr, "unzcrash: can't open %s\n", argv[1] ); + return 1; + } + + nIn = fread ( inbuf, 1, M_BLOCK, f ); + fprintf ( stderr, "%d bytes read\n", nIn ); + + nZ = M_BLOCK; + r = BZ2_bzBuffToBuffCompress ( + zbuf, &nZ, inbuf, nIn, 9, 0, 30 ); + + assert (r == BZ_OK); + fprintf ( stderr, "%d after compression\n", nZ ); + + for (bit = 0; bit < nZ*8; bit++) { + fprintf ( stderr, "bit %d ", bit ); + flip_bit ( bit ); + nOut = M_BLOCK_OUT; + r = BZ2_bzBuffToBuffDecompress ( + outbuf, &nOut, zbuf, nZ, 0, 0 ); + fprintf ( stderr, " %d %s ", r, bzerrorstrings[-r] ); + + if (r != BZ_OK) { + fprintf ( stderr, "\n" ); + } else { + if (nOut != nIn) { + fprintf(stderr, "nIn/nOut mismatch %d %d\n", nIn, nOut ); + return 1; + } else { + for (i = 0; i < nOut; i++) + if (inbuf[i] != outbuf[i]) { + fprintf(stderr, "mismatch at %d\n", i ); + return 1; + } + if (i == nOut) fprintf(stderr, "really ok!\n" ); + } + } + + flip_bit ( bit ); + } + +#if 0 + assert (nOut == nIn); + for (i = 0; i < nOut; i++) { + if (inbuf[i] != outbuf[i]) { + fprintf ( stderr, "difference at %d !\n", i ); + return 1; + } + } +#endif + + fprintf ( stderr, "all ok\n" ); + return 0; +} diff --git a/src/tools/stuffextract/StormLib/huffman/huff.cpp b/src/tools/stuffextract/StormLib/huffman/huff.cpp new file mode 100644 index 0000000..b67672f --- /dev/null +++ b/src/tools/stuffextract/StormLib/huffman/huff.cpp @@ -0,0 +1,1453 @@ +/*****************************************************************************/ +/* huffman.cpp Copyright (c) Ladislav Zezula 1998-2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains Huffmann (de)compression methods */ +/* */ +/* Authors : Ladislav Zezula (ladik.zezula.net) */ +/* ShadowFlare (BlakFlare@hotmail.com) */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad The first version of dcmp.cpp */ +/* 03.05.03 1.00 Lad Added compression methods */ +/* 19.11.03 1.01 Dan Big endian handling */ +/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ +/*****************************************************************************/ + +#include +#include + +#include "huff.h" + +// Special for Mac - we have to know if normal pointer greater or less +// than 0x80000000. This variable is used in the PTR_VALID and PTR_INVALID +// macros +static long mul = 1; + +#define PTR_VALID(ptr) (((LONG_PTR)(ptr) * mul) > 0) +#define PTR_INVALID(ptr) (((LONG_PTR)(ptr) * mul) < 0) +#define PTR_INVALID_OR_NULL(ptr) (((LONG_PTR)(ptr) * mul) <= 0) + + +//----------------------------------------------------------------------------- +// Methods of the THTreeItem struct + +// 1501DB70 +THTreeItem * THTreeItem::Call1501DB70(THTreeItem * pLast) +{ + if(pLast == NULL) + pLast = this + 1; + return pLast; +} + +// Gets previous Huffman tree item (?) +THTreeItem * THTreeItem::GetPrevItem(LONG_PTR value) +{ + if(PTR_INVALID(prev)) + return PTR_NOT(prev); + + if(value == -1 || PTR_INVALID(value)) + value = (long)(this - next->prev); + return prev + value; + +// OLD VERSION +// if(PTR_INT(value) < 0) +// value = PTR_INT((item - item->next->prev)); +// return (THTreeItem *)((char *)prev + value); +} + +// 1500F5E0 +void THTreeItem::ClearItemLinks() +{ + next = prev = NULL; +} + +// 1500BC90 +void THTreeItem::RemoveItem() +{ + THTreeItem * pTemp; // EDX + + if(next != NULL) + { + pTemp = prev; + + if(PTR_INVALID_OR_NULL(pTemp)) + pTemp = PTR_NOT(pTemp); + else + pTemp += (this - next->prev); + + pTemp->next = next; + next->prev = prev; + next = prev = NULL; + } +} + +/* +// OLD VERSION : Removes item from the tree (?) +static void RemoveItem(THTreeItem * item) +{ + THTreeItem * next = item->next; // ESI + THTreeItem * prev = item->prev; // EDX + + if(next == NULL) + return; + + if(PTR_INT(prev) < 0) + prev = PTR_NOT(prev); + else + // ??? usually item == next->prev, so what is it ? + prev = (THTreeItem *)((unsigned char *)prev + (unsigned long)((unsigned char *)item - (unsigned char *)(next->prev))); + + // Remove HTree item from the chain + prev->next = next; // Sets the 'first' pointer + next->prev = item->prev; + + // Invalidate pointers + item->next = NULL; + item->prev = NULL; +} +*/ + +//----------------------------------------------------------------------------- +// TOutputStream functions + +void TOutputStream::PutBits(unsigned long dwBuff, unsigned int nPutBits) +{ + dwBitBuff |= (dwBuff << nBits); + nBits += nPutBits; + + // Flush completed bytes + while(nBits >= 8) + { + if(dwOutSize != 0) + { + *pbOutPos++ = (unsigned char)dwBitBuff; + dwOutSize--; + } + + dwBitBuff >>= 8; + nBits -= 8; + } +} + +//----------------------------------------------------------------------------- +// TInputStream functions + +// Gets one bit from input stream +unsigned long TInputStream::GetBit() +{ + unsigned long dwBit = (dwBitBuff & 1); + + dwBitBuff >>= 1; + if(--nBits == 0) + { + dwBitBuff = BSWAP_INT32_UNSIGNED(*(unsigned long *)pbInBuffer); + pbInBuffer += sizeof(unsigned long); + nBits = 32; + } + return dwBit; +} + +// Gets 7 bits from the stream +unsigned long TInputStream::Get7Bits() +{ + if(nBits <= 7) + { + dwBitBuff |= BSWAP_INT16_UNSIGNED(*(unsigned short *)pbInBuffer) << nBits; + pbInBuffer += sizeof(unsigned short); + nBits += 16; + } + + // Get 7 bits from input stream + return (dwBitBuff & 0x7F); +} + +// Gets the whole byte from the input stream. +unsigned long TInputStream::Get8Bits() +{ + unsigned long dwOneByte; + + if(nBits <= 8) + { + dwBitBuff |= BSWAP_INT16_UNSIGNED(*(unsigned short *)pbInBuffer) << nBits; + pbInBuffer += sizeof(unsigned short); + nBits += 16; + } + + dwOneByte = (dwBitBuff & 0xFF); + dwBitBuff >>= 8; + nBits -= 8; + return dwOneByte; +} + +//----------------------------------------------------------------------------- +// Functions for huffmann tree items + +// Inserts item into the tree (?) +static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long where, THTreeItem * item2) +{ + THTreeItem * next = item->next; // EDI - next to the first item + THTreeItem * prev = item->prev; // ESI - prev to the first item + THTreeItem * prev2; // Pointer to previous item + LONG_PTR next2; // Pointer to the next item + + // The same code like in RemoveItem(item); + if(next != 0) // If the first item already has next one + { + if(PTR_INVALID(prev)) + prev = PTR_NOT(prev); + else + prev += (item - next->prev); + + // 150083C1 + // Remove the item from the tree + prev->next = next; + next->prev = prev; + + // Invalidate 'prev' and 'next' pointer + item->next = 0; + item->prev = 0; + } + + if(item2 == NULL) // EDX - If the second item is not entered, + item2 = PTR_PTR(&itemPtr[1]); // take the first tree item + + switch(where) + { + case SWITCH_ITEMS : // Switch the two items + item->next = item2->next; // item2->next (Pointer to pointer to first) + item->prev = item2->next->prev; + item2->next->prev = item; + item2->next = item; // Set the first item + return; + + case INSERT_ITEM: // Insert as the last item + item->next = item2; // Set next item (or pointer to pointer to first item) + item->prev = item2->prev; // Set prev item (or last item in the tree) + + next2 = PTR_INT(itemPtr[0]);// Usually NULL + prev2 = item2->prev; // Prev item to the second (or last tree item) + + if(PTR_INVALID(prev2)) + { + prev2 = PTR_NOT(prev); + + prev2->next = item; + item2->prev = item; // Next after last item + return; + } + + if(PTR_INVALID(next2)) + next2 = (long)(item2 - item2->next->prev); +// next2 = (THTreeItem *)(unsigned long)((unsigned char *)item2 - (unsigned char *)(item2->next->prev)); + +// prev2 = (THTreeItem *)((char *)prev2 + (unsigned long)next2);// ??? + prev2 += next2; + prev2->next = item; + item2->prev = item; // Set the next/last item + return; + + default: + return; + } +} + +//----------------------------------------------------------------------------- +// THuffmannTree class functions + +THuffmannTree::THuffmannTree() +{ + // We have to check if the "this" pointer is less than zero + if((LONG_PTR)this < 0) + mul = -1; +} + +void THuffmannTree::InitTree(bool bCompression) +{ + THTreeItem * pItem; + unsigned int nCount; + + // Clear links for all the items in the tree + for(pItem = items0008, nCount = 0x203; nCount != 0; pItem++, nCount--) + pItem->ClearItemLinks(); + + pItem3050 = NULL; + pItem3054 = PTR_PTR(&pItem3054); + pItem3058 = PTR_NOT(pItem3054); + + pItem305C = NULL; + pFirst = PTR_PTR(&pFirst); + pLast = PTR_NOT(pFirst); + + offs0004 = 1; + nItems = 0; + + // Clear all TQDecompress items. Do this only if preparing for decompression + if(bCompression == false) + { + for(nCount = 0; nCount < sizeof(qd3474) / sizeof(TQDecompress); nCount++) + qd3474[nCount].offs00 = 0; + } +} + +// Builds Huffman tree. Called with the first 8 bits loaded from input stream +void THuffmannTree::BuildTree(unsigned int nCmpType) +{ + unsigned long maxByte; // [ESP+10] - The greatest character found in table + THTreeItem ** itemPtr; // [ESP+14] - Pointer to Huffman tree item pointer array + unsigned char * byteArray; // [ESP+1C] - Pointer to unsigned char in Table1502A630 + THTreeItem * child1; + unsigned long i; // egcs in linux doesn't like multiple for loops without an explicit i + + // Loop while pointer has a valid value + while(PTR_VALID(pLast)) // ESI - Last entry + { + THTreeItem * temp; // EAX + + if(pLast->next != NULL) // ESI->next + pLast->RemoveItem(); + // EDI = &offs3054 + pItem3058 = PTR_PTR(&pItem3054);// [EDI+4] + pLast->prev = pItem3058; // EAX + + temp = PTR_PTR(&pItem3054)->GetPrevItem(PTR_INT(&pItem3050)); + + temp->next = pLast; + pItem3054 = pLast; + } + + // Clear all pointers in HTree item array + memset(items306C, 0, sizeof(items306C)); + + maxByte = 0; // Greatest character found init to zero. + itemPtr = (THTreeItem **)&items306C; // Pointer to current entry in HTree item pointer array + + // Ensure we have low 8 bits only + nCmpType &= 0xFF; + byteArray = Table1502A630 + nCmpType * 258; // EDI also + + for(i = 0; i < 0x100; i++, itemPtr++) + { + THTreeItem * item = pItem3058; // Item to be created + THTreeItem * pItem3 = pItem3058; + unsigned char oneByte = byteArray[i]; + + // Skip all the bytes which are zero. + if(byteArray[i] == 0) + continue; + + // If not valid pointer, take the first available item in the array + if(PTR_INVALID_OR_NULL(item)) + item = &items0008[nItems++]; + + // Insert this item as the top of the tree + InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); + + item->parent = NULL; // Invalidate child and parent + item->child = NULL; + *itemPtr = item; // Store pointer into pointer array + + item->dcmpByte = i; // Store counter + item->byteValue = oneByte; // Store byte value + if(oneByte >= maxByte) + { + maxByte = oneByte; + continue; + } + + // Find the first item which has byte value greater than current one byte + if(PTR_VALID(pItem3 = pLast)) // EDI - Pointer to the last item + { + // 15006AF7 + if(pItem3 != NULL) + { + do // 15006AFB + { + if(pItem3->byteValue >= oneByte) + goto _15006B09; + pItem3 = pItem3->prev; + } + while(PTR_VALID(pItem3)); + } + } + pItem3 = NULL; + + // 15006B09 + _15006B09: + if(item->next != NULL) + item->RemoveItem(); + + // 15006B15 + if(pItem3 == NULL) + pItem3 = PTR_PTR(&pFirst); + + // 15006B1F + item->next = pItem3->next; + item->prev = pItem3->next->prev; + pItem3->next->prev = item; + pItem3->next = item; + } + + // 15006B4A + for(; i < 0x102; i++) + { + THTreeItem ** itemPtr = &items306C[i]; // EDI + + // 15006B59 + THTreeItem * item = pItem3058; // ESI + if(PTR_INVALID_OR_NULL(item)) + item = &items0008[nItems++]; + + InsertItem(&pItem305C, item, INSERT_ITEM, NULL); + + // 15006B89 + item->dcmpByte = i; + item->byteValue = 1; + item->parent = NULL; + item->child = NULL; + *itemPtr++ = item; + } + + // 15006BAA + if(PTR_VALID(child1 = pLast)) // EDI - last item (first child to item + { + THTreeItem * child2; // EBP + THTreeItem * item; // ESI + + // 15006BB8 + while(PTR_VALID(child2 = child1->prev)) + { + if(PTR_INVALID_OR_NULL(item = pItem3058)) + item = &items0008[nItems++]; + + // 15006BE3 + InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); + + // 15006BF3 + item->parent = NULL; + item->child = NULL; + + //EDX = child2->byteValue + child1->byteValue; + //EAX = child1->byteValue; + //ECX = maxByte; // The greatest character (0xFF usually) + + item->byteValue = child1->byteValue + child2->byteValue; // 0x02 + item->child = child1; // Prev item in the + child1->parent = item; + child2->parent = item; + + // EAX = item->byteValue; + if(item->byteValue >= maxByte) + maxByte = item->byteValue; + else + { + THTreeItem * pItem2 = child2->prev; // EDI + + // 15006C2D + while(PTR_VALID(pItem2)) + { + if(pItem2->byteValue >= item->byteValue) + goto _15006C3B; + pItem2 = pItem2->prev; + } + pItem2 = NULL; + + _15006C3B: + if(item->next != 0) + { + THTreeItem * temp4 = item->GetPrevItem(-1); + + temp4->next = item->next; // The first item changed + item->next->prev = item->prev; // First->prev changed to negative value + item->next = NULL; + item->prev = NULL; + } + + // 15006C62 + if(pItem2 == NULL) + pItem2 = PTR_PTR(&pFirst); + + item->next = pItem2->next; // Set item with 0x100 byte value + item->prev = pItem2->next->prev; // Set item with 0x17 byte value + pItem2->next->prev = item; // Changed prev of item with + pItem2->next = item; + } + + // 15006C7B + if(PTR_INVALID_OR_NULL(child1 = child2->prev)) + break; + } + } + // 15006C88 + offs0004 = 1; +} +/* +// Modifies Huffman tree. Adds new item and changes +void THuffmannTree::ModifyTree(unsigned long dwIndex) +{ + THTreeItem * pItem1 = pItem3058; // ESI + THTreeItem * pSaveLast = (PTR_INT(pLast) <= 0) ? NULL : pLast; // EBX + THTreeItem * temp; // EAX + + // Prepare the first item to insert to the tree + if(PTR_INT(pItem1) <= 0) + pItem1 = &items0008[nItems++]; + + // If item has any next item, remove it from the chain + if(pItem1->next != NULL) + { + THTreeItem * temp = pItem1->GetPrevItem(-1); // EAX + + temp->next = pItem1->next; + pItem1->next->prev = pItem1->prev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + + pItem1->next = PTR_PTR(&pFirst); + pItem1->prev = pLast; + temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); + + // 150068E9 + temp->next = pItem1; + pLast = pItem1; + + pItem1->parent = NULL; + pItem1->child = NULL; + + // 150068F6 + pItem1->dcmpByte = pSaveLast->dcmpByte; // Copy item index + pItem1->byteValue = pSaveLast->byteValue; // Copy item byte value + pItem1->parent = pSaveLast; // Set parent to last item + items306C[pSaveLast->dcmpByte] = pItem1; // Insert item into item pointer array + + // Prepare the second item to insert into the tree + if(PTR_INT((pItem1 = pItem3058)) <= 0) + pItem1 = &items0008[nItems++]; + + // 1500692E + if(pItem1->next != NULL) + { + temp = pItem1->GetPrevItem(-1); // EAX + + temp->next = pItem1->next; + pItem1->next->prev = pItem1->prev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + // 1500694C + pItem1->next = PTR_PTR(&pFirst); + pItem1->prev = pLast; + temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); + + // 15006968 + temp->next = pItem1; + pLast = pItem1; + + // 1500696E + pItem1->child = NULL; + pItem1->dcmpByte = dwIndex; + pItem1->byteValue = 0; + pItem1->parent = pSaveLast; + pSaveLast->child = pItem1; + items306C[dwIndex] = pItem1; + + do + { + THTreeItem * pItem2 = pItem1; + THTreeItem * pItem3; + unsigned long byteValue; + + // 15006993 + byteValue = ++pItem1->byteValue; + + // Pass through all previous which have its value greater than byteValue + while(PTR_INT((pItem3 = pItem2->prev)) > 0) // EBX + { + if(pItem3->byteValue >= byteValue) + goto _150069AE; + + pItem2 = pItem2->prev; + } + // 150069AC + pItem3 = NULL; + + _150069AE: + if(pItem2 == pItem1) + continue; + + // 150069B2 + // Switch pItem2 with item + InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); + InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); + + // 150069D0 + // Switch parents of pItem1 and pItem2 + temp = pItem2->parent->child; + if(pItem1 == pItem1->parent->child) + pItem1->parent->child = pItem2; + + if(pItem2 == temp) + pItem2->parent->child = pItem1; + + // 150069ED + // Switch parents of pItem1 and pItem3 + temp = pItem1->parent; + pItem1 ->parent = pItem2->parent; + pItem2->parent = temp; + offs0004++; + } + while(PTR_INT((pItem1 = pItem1->parent)) > 0); +} + +void THuffmannTree::UninitTree() +{ + while(PTR_INT(pLast) > 0) + { + pItem = pItem305C->Call1501DB70(pLast); + pItem->RemoveItem(); + } + + for(pItem = pFirst; PTR_INT(pItem3058) > 0; pItem = pItem3058) + pItem->RemoveItem(); + PTR_PTR(&pItem3054)->RemoveItem(); + + for(pItem = items0008 + 0x203, nCount = 0x203; nCount != 0; nCount--) + { + pItem--; + pItem->RemoveItem(); + pItem->RemoveItem(); + } +} +*/ + +THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) +{ + THTreeItem * pItem1 = pItem3058; // EDX + THTreeItem * pItem2; // EAX + THTreeItem * pNext; + THTreeItem * pPrev; + THTreeItem ** ppItem; + + if(PTR_INVALID_OR_NULL(pItem1) || (pItem2 = pItem1) == NULL) + { + if((pItem2 = &items0008[nItems++]) != NULL) + pItem1 = pItem2; + else + pItem1 = pFirst; + } + else + pItem1 = pItem2; + + pNext = pItem1->next; + if(pNext != NULL) + { + pPrev = pItem1->prev; + if(PTR_INVALID_OR_NULL(pPrev)) + pPrev = PTR_NOT(pPrev); + else + pPrev += (pItem1 - pItem1->next->prev); + + pPrev->next = pNext; + pNext->prev = pPrev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + + ppItem = &pFirst; // esi + if(nValue > 1) + { + // ecx = pFirst->next; + pItem1->next = *ppItem; + pItem1->prev = (*ppItem)->prev; + + (*ppItem)->prev = pItem2; + *ppItem = pItem1; + + pItem2->parent = NULL; + pItem2->child = NULL; + } + else + { + pItem1->next = (THTreeItem *)ppItem; + pItem1->prev = ppItem[1]; + // edi = pItem305C; + pPrev = ppItem[1]; // ecx + if(PTR_INVALID_OR_NULL(pPrev)) + { + pPrev = PTR_NOT(pPrev); + pPrev->next = pItem1; + pPrev->prev = pItem2; + + pItem2->parent = NULL; + pItem2->child = NULL; + } + else + { + if(PTR_INVALID(pItem305C)) + pPrev += (THTreeItem *)ppItem - (*ppItem)->prev; + else + pPrev += PTR_INT(pItem305C); + + pPrev->next = pItem1; + ppItem[1] = pItem2; + pItem2->parent = NULL; + pItem2->child = NULL; + } + } + return pItem2; +} + +void THuffmannTree::Call1500E820(THTreeItem * pItem) +{ + THTreeItem * pItem1; // edi + THTreeItem * pItem2 = NULL; // eax + THTreeItem * pItem3; // edx + THTreeItem * pPrev; // ebx + + for(; pItem != NULL; pItem = pItem->parent) + { + pItem->byteValue++; + + for(pItem1 = pItem; ; pItem1 = pPrev) + { + pPrev = pItem1->prev; + if(PTR_INVALID_OR_NULL(pPrev)) + { + pPrev = NULL; + break; + } + + if(pPrev->byteValue >= pItem->byteValue) + break; + } + + if(pItem1 == pItem) + continue; + + if(pItem1->next != NULL) + { + pItem2 = pItem1->GetPrevItem(-1); + pItem2->next = pItem1->next; + pItem1->next->prev = pItem1->prev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + + pItem2 = pItem->next; + pItem1->next = pItem2; + pItem1->prev = pItem2->prev; + pItem2->prev = pItem1; + pItem->next = pItem1; + if((pItem2 = pItem1) != NULL) + { + pItem2 = pItem->GetPrevItem(-1); + pItem2->next = pItem->next; + pItem->next->prev = pItem->prev; + pItem->next = NULL; + pItem->prev = NULL; + } + + if(pPrev == NULL) + pPrev = PTR_PTR(&pFirst); + + pItem2 = pPrev->next; + pItem->next = pItem2; + pItem->prev = pItem2->prev; + pItem2->prev = pItem; + pPrev->next = pItem; + + pItem3 = pItem1->parent->child; + pItem2 = pItem->parent; + if(pItem2->child == pItem) + pItem2->child = pItem1; + if(pItem3 == pItem1) + pItem1->parent->child = pItem; + + pItem2 = pItem->parent; + pItem->parent = pItem1->parent; + pItem1->parent = pItem2; + offs0004++; + } +} + +// 1500E920 +unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType) +{ + THTreeItem * pItem1; + THTreeItem * pItem2; + THTreeItem * pItem3; + THTreeItem * pTemp; + unsigned long dwBitBuff; + unsigned int nBits; + unsigned int nBit; + + BuildTree(nCmpType); + bIsCmp0 = (nCmpType == 0); + + // Store the compression type into output buffer + os->dwBitBuff |= (nCmpType << os->nBits); + os->nBits += 8; + + // Flush completed bytes + while(os->nBits >= 8) + { + if(os->dwOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->dwOutSize--; + } + + os->dwBitBuff >>= 8; + os->nBits -= 8; + } + + for(; nInLength != 0; nInLength--) + { + unsigned char bOneByte = *pbInBuffer++; + + if((pItem1 = items306C[bOneByte]) == NULL) + { + pItem2 = items306C[0x101]; // ecx + pItem3 = pItem2->parent; // eax + dwBitBuff = 0; + nBits = 0; + + for(; pItem3 != NULL; pItem3 = pItem3->parent) + { + nBit = (pItem3->child != pItem2) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem2 = pItem3; + } + os->PutBits(dwBitBuff, nBits); + + // Store the loaded byte into output stream + os->dwBitBuff |= (bOneByte << os->nBits); + os->nBits += 8; + + // Flush the whole byte(s) + while(os->nBits >= 8) + { + if(os->dwOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->dwOutSize--; + } + os->dwBitBuff >>= 8; + os->nBits -= 8; + } + + pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; + pItem2 = Call1500E740(1); + pItem2->dcmpByte = pItem1->dcmpByte; + pItem2->byteValue = pItem1->byteValue; + pItem2->parent = pItem1; + items306C[pItem2->dcmpByte] = pItem2; + + pItem2 = Call1500E740(1); + pItem2->dcmpByte = bOneByte; + pItem2->byteValue = 0; + pItem2->parent = pItem1; + items306C[pItem2->dcmpByte] = pItem2; + pItem1->child = pItem2; + + Call1500E820(pItem2); + + if(bIsCmp0 != 0) + { + Call1500E820(items306C[bOneByte]); + continue; + } + + for(pItem1 = items306C[bOneByte]; pItem1 != NULL; pItem1 = pItem1->parent) + { + pItem1->byteValue++; + pItem2 = pItem1; + + for(;;) + { + pItem3 = pItem2->prev; + if(PTR_INVALID_OR_NULL(pItem3)) + { + pItem3 = NULL; + break; + } + if(pItem3->byteValue >= pItem1->byteValue) + break; + pItem2 = pItem3; + } + + if(pItem2 != pItem1) + { + InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); + InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); + + pItem3 = pItem2->parent->child; + if(pItem1->parent->child == pItem1) + pItem1->parent->child = pItem2; + + if(pItem3 == pItem2) + pItem2->parent->child = pItem1; + + pTemp = pItem1->parent; + pItem1->parent = pItem2->parent; + pItem2->parent = pTemp; + offs0004++; + } + } + } +// 1500EB62 + else + { + dwBitBuff = 0; + nBits = 0; + for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) + { + nBit = (pItem2->child != pItem1) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem1 = pItem2; + } + os->PutBits(dwBitBuff, nBits); + } + +// 1500EB98 + if(bIsCmp0 != 0) + Call1500E820(items306C[bOneByte]); // 1500EB9D +// 1500EBAF + } // for(; nInLength != 0; nInLength--) + +// 1500EBB8 + pItem1 = items306C[0x100]; + dwBitBuff = 0; + nBits = 0; + for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) + { + nBit = (pItem2->child != pItem1) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem1 = pItem2; + } + +// 1500EBE6 + os->PutBits(dwBitBuff, nBits); + +// 1500EBEF + // Flush the remaining bits + while(os->nBits != 0) + { + if(os->dwOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->dwOutSize--; + } + os->dwBitBuff >>= 8; + os->nBits -= ((os->nBits > 8) ? 8 : os->nBits); + } + + return (unsigned int)(os->pbOutPos - os->pbOutBuffer); +} + +// Decompression using Huffman tree (1500E450) +unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is) +{ + TQDecompress * qd; + THTreeItem * pItem1; + THTreeItem * pItem2; + unsigned char * pbOutPos = pbOutBuffer; + unsigned long nBitCount; + unsigned int nDcmpByte = 0; + unsigned int n8Bits; // 8 bits loaded from input stream + unsigned int n7Bits; // 7 bits loaded from input stream + bool bHasQdEntry; + + // Test the output length. Must not be NULL. + if(dwOutLength == 0) + return 0; + + // Get the compression type from the input stream + n8Bits = is->Get8Bits(); + + // Build the Huffman tree + BuildTree(n8Bits); + bIsCmp0 = (n8Bits == 0) ? 1 : 0; + + for(;;) + { + n7Bits = is->Get7Bits(); // Get 7 bits from input stream + + // Try to use quick decompression. Check TQDecompress array for corresponding item. + // If found, ise the result byte instead. + qd = &qd3474[n7Bits]; + + // If there is a quick-pass possible (ebx) + bHasQdEntry = (qd->offs00 >= offs0004) ? true : false; + + // If we can use quick decompress, use it. + if(bHasQdEntry) + { + if(qd->nBits > 7) + { + is->dwBitBuff >>= 7; + is->nBits -= 7; + pItem1 = qd->pItem; + goto _1500E549; + } + is->dwBitBuff >>= qd->nBits; + is->nBits -= qd->nBits; + nDcmpByte = qd->dcmpByte; + } + else + { + pItem1 = pFirst->next->prev; + if(PTR_INVALID_OR_NULL(pItem1)) + pItem1 = NULL; +_1500E549: + nBitCount = 0; + pItem2 = NULL; + + do + { + pItem1 = pItem1->child; // Move down by one level + if(is->GetBit()) // If current bit is set, move to previous + pItem1 = pItem1->prev; + + if(++nBitCount == 7) // If we are at 7th bit, save current HTree item. + pItem2 = pItem1; + } + while(pItem1->child != NULL); // Walk until tree has no deeper level + + if(bHasQdEntry == false) + { + if(nBitCount > 7) + { + qd->offs00 = offs0004; + qd->nBits = nBitCount; + qd->pItem = pItem2; + } + else + { + unsigned long nIndex = n7Bits & (0xFFFFFFFF >> (32 - nBitCount)); + unsigned long nAdd = (1 << nBitCount); + + for(qd = &qd3474[nIndex]; nIndex <= 0x7F; nIndex += nAdd, qd += nAdd) + { + qd->offs00 = offs0004; + qd->nBits = nBitCount; + qd->dcmpByte = pItem1->dcmpByte; + } + } + } + nDcmpByte = pItem1->dcmpByte; + } + + if(nDcmpByte == 0x101) // Huffman tree needs to be modified + { + n8Bits = is->Get8Bits(); + pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; + + pItem2 = Call1500E740(1); + pItem2->parent = pItem1; + pItem2->dcmpByte = pItem1->dcmpByte; + pItem2->byteValue = pItem1->byteValue; + items306C[pItem2->dcmpByte] = pItem2; + + pItem2 = Call1500E740(1); + pItem2->parent = pItem1; + pItem2->dcmpByte = n8Bits; + pItem2->byteValue = 0; + items306C[pItem2->dcmpByte] = pItem2; + + pItem1->child = pItem2; + Call1500E820(pItem2); + if(bIsCmp0 == 0) + Call1500E820(items306C[n8Bits]); + + nDcmpByte = n8Bits; + } + + if(nDcmpByte == 0x100) + break; + + *pbOutPos++ = (unsigned char)nDcmpByte; + if(--dwOutLength == 0) + break; + + if(bIsCmp0) + Call1500E820(items306C[nDcmpByte]); + } + + return (unsigned int)(pbOutPos - pbOutBuffer); +} + +/* OLD VERSION +unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is) +{ + THTreeItem * pItem1; // Current item if walking HTree + unsigned long bitCount; // Bit counter if walking HTree + unsigned long oneByte; // 8 bits from bit stream/Pointer to target + unsigned char * outPtr; // Current pointer to output buffer + bool hasQDEntry; // true if entry for quick decompression if filled + THTreeItem * itemAt7 = NULL; // HTree item found at 7th bit + THTreeItem * temp; // For every use + unsigned long dcmpByte = 0; // Decompressed byte value + bool bFlag = 0; + + // Test the output length. Must not be NULL. + if(dwOutLength == 0) + return 0; + + // If too few bits in input bit buffer, we have to load next 16 bits + is->EnsureHasMoreThan8Bits(); + + // Get 8 bits from input stream + oneByte = is->Get8Bits(); + + // Build the Huffman tree + BuildTree(oneByte); + + bIsCmp0 = (oneByte == 0) ? 1 : 0; + outPtr = pbOutBuffer; // Copy pointer to output data + + for(;;) + { + TQDecompress * qd; // For quick decompress + unsigned long sevenBits = is->Get7Bits();// 7 bits from input stream + + // Try to use quick decompression. Check TQDecompress array for corresponding item. + // If found, ise the result byte instead. + qd = &qd3474[sevenBits]; + + // If there is a quick-pass possible + hasQDEntry = (qd->offs00 == offs0004) ? 1 : 0; + + // Start passing the Huffman tree. Set item to tree root item + pItem1 = pFirst; + + // If we can use quick decompress, use it. + bFlag = 1; + if(hasQDEntry == 1) + { + // Check the bit count is greater than 7, move item to 7 levels deeper + if((bitCount = qd->bitCount) > 7) + { + is->dwBitBuff >>= 7; + is->nBits -= 7; + pItem1 = qd->item; // Don't start with root item, but with some deeper-laying + } + else + { + // If OK, use their byte value + is->dwBitBuff >>= bitCount; + is->nBits -= bitCount; + dcmpByte = qd->dcmpByte; + bFlag = 0; + } + } + else + { + pItem1 = pFirst->next->prev; + if(PTR_INT(pItem1) <= 0) + pItem1 = NULL; + } + + if(bFlag == 1) + { + // Walk through Huffman Tree + bitCount = 0; // Clear bit counter + do + { + pItem1 = pItem1->child; + if(is->GetBit() != 0) // If current bit is set, move to previous + pItem1 = pItem1->prev; // item in current level + + if(++bitCount == 7) // If we are at 7th bit, store current HTree item. + itemAt7 = pItem1; // Store Huffman tree item + } + while(pItem1->child != NULL); // Walk until tree has no deeper level + + // If quick decompress entry is not filled yet, fill it. + if(hasQDEntry == 0) + { + if(bitCount > 7) // If we passed more than 7 bits, store bitCount and item + { + qd->offs00 = offs0004; // Value indicates that entry is resolved + qd->bitCount = bitCount; // Number of bits passed + qd->item = itemAt7; // Store item at 7th bit + } + // If we passed less than 7 bits, fill entry and bit count multipliers + else + { + unsigned long index = sevenBits & (0xFFFFFFFF >> (32 - bitCount)); // Index for quick-decompress entry + unsigned long addIndex = (1 << bitCount); // Add value for index + + qd = &qd3474[index]; + + do + { + qd->offs00 = offs0004; + qd->bitCount = bitCount; + qd->dcmpByte = pItem1->dcmpByte; + + index += addIndex; + qd += addIndex; + } + while(index <= 0x7F); + } + } + dcmpByte = pItem1->dcmpByte; + } + + if(dcmpByte == 0x101) // Huffman tree needs to be modified + { + // Check if there is enough bits in the buffer + is->EnsureHasMoreThan8Bits(); + + // Get 8 bits from the buffer + oneByte = is->Get8Bits(); + + // Modify Huffman tree + ModifyTree(oneByte); + + // Get lastly added tree item + pItem1 = items306C[oneByte]; + + if(bIsCmp0 == 0 && pItem1 != NULL) + { + // 15006F15 + do + { + THTreeItem * pItem2 = pItem1; + THTreeItem * pItem3; + unsigned long byteValue; + + byteValue = ++pItem1->byteValue; + + while(PTR_INT((pItem3 = pItem2->prev)) > 0) + { + if(pItem3->byteValue >= byteValue) + goto _15006F30; + + pItem2 = pItem2->prev; + } + pItem3 = NULL; + + _15006F30: + if(pItem2 == pItem1) + continue; + + InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); + InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); + + temp = pItem2->parent->child; + if(pItem1 == pItem1->parent->child) + pItem1->parent->child = pItem2; + + if(pItem2 == temp) + pItem2->parent->child = pItem1; + + // Switch parents of pItem1 and pItem3 + temp = pItem1->parent; + pItem1->parent = pItem2->parent; + pItem2->parent = temp; + offs0004++; + } + while(PTR_INT((pItem1 = pItem1->parent)) > 0); + } + dcmpByte = oneByte; + } + + if(dcmpByte != 0x100) // Not at the end of data ? + { + *outPtr++ = (unsigned char)dcmpByte; + if(--dwOutLength > 0) + { + if(bIsCmp0 != 0) + Call1500E820(items306C[pItem1->byteValue]); + } + else + break; + } + else + break; + } + return (unsigned long)(outPtr - pbOutBuffer); +} +*/ + +// Table for (de)compression. Every compression type has 258 entries +unsigned char THuffmannTree::Table1502A630[] = +{ + // Data for compression type 0x00 + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, + + // Data for compression type 0x01 + 0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, + 0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, + 0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, + 0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, + 0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, + 0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03, + 0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, + 0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, + 0x00, 0x00, + + // Data for compression type 0x02 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04, + 0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, + 0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A, + 0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x03 + 0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, + 0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, + 0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, + 0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03, + 0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01, + 0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, + 0x00, 0x00, + + // Data for compression type 0x04 + 0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x05 + 0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, + 0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, + 0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D, + 0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x06 + 0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x07 + 0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x08 + 0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, + 0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11, + 0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; \ No newline at end of file diff --git a/src/tools/stuffextract/StormLib/huffman/huff.h b/src/tools/stuffextract/StormLib/huffman/huff.h new file mode 100644 index 0000000..fcad7c8 --- /dev/null +++ b/src/tools/stuffextract/StormLib/huffman/huff.h @@ -0,0 +1,142 @@ +/*****************************************************************************/ +/* huffman.h Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description : */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad The first version of huffman.h */ +/* 03.05.03 2.00 Lad Added compression */ +/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ +/*****************************************************************************/ + +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#include "../StormPort.h" + +//----------------------------------------------------------------------------- +// Defines + +#define INSERT_ITEM 1 +#define SWITCH_ITEMS 2 // Switch the item1 and item2 + +#define PTR_NOT(ptr) (THTreeItem *)(~(DWORD_PTR)(ptr)) +#define PTR_PTR(ptr) ((THTreeItem *)(ptr)) +#define PTR_INT(ptr) (LONG_PTR)(ptr) + +#ifndef NULL +#define NULL 0 +#endif + +//----------------------------------------------------------------------------- +// Structures and classes + +// Input stream for Huffmann decompression +class TInputStream +{ + public: + + unsigned long GetBit(); + unsigned long Get7Bits(); + unsigned long Get8Bits(); + + unsigned char * pbInBuffer; // 00 - Input data + unsigned long dwBitBuff; // 04 - Input bit buffer + unsigned int nBits; // 08 - Number of bits remaining in 'dwValue' +}; + +// Output stream for Huffmann compression +class TOutputStream +{ + public: + + void PutBits(unsigned long dwBuff, unsigned int nPutBits); + + unsigned char * pbOutBuffer; // 00 : Output buffer + unsigned long dwOutSize; // 04 : Size of output buffer + unsigned char * pbOutPos; // 08 : Current output position + unsigned long dwBitBuff; // 0C : Bit buffer + unsigned long nBits; // 10 : Number of bits in the bit buffer +}; + +// Huffmann tree item (?) +struct THTreeItem +{ + public: + + THTreeItem * Call1501DB70(THTreeItem * pLast); + THTreeItem * GetPrevItem(LONG_PTR value); + void ClearItemLinks(); + void RemoveItem(); + + THTreeItem * next; // 00 - Pointer to next THTreeItem + THTreeItem * prev; // 04 - Pointer to prev THTreeItem (< 0 if none) + unsigned long dcmpByte; // 08 - Index of this item in item pointer array, decompressed byte value + unsigned long byteValue; // 0C - Some byte value + THTreeItem * parent; // 10 - Pointer to parent THTreeItem (NULL if none) + THTreeItem * child; // 14 - Pointer to child THTreeItem + int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive +}; + +// Structure used for quick decompress. The 'bitCount' contains number of bits +// and byte value contains result decompressed byte value. +// After each walk through Huffman tree are filled all entries which are +// multiplies of number of bits loaded from input stream. These entries +// contain number of bits and result value. At the next 7 bits is tested this +// structure first. If corresponding entry found, decompression routine will +// not walk through Huffman tree and directly stores output byte to output stream. +struct TQDecompress +{ + unsigned long offs00; // 00 - 1 if resolved + unsigned long nBits; // 04 - Bit count + union + { + unsigned long dcmpByte; // 08 - Byte value for decompress (if bitCount <= 7) + THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7 + }; +}; + +// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert +// for the decompression, I do not know actually if the class is really a Hufmann +// tree. If someone knows the decompression details, please let me know +class THuffmannTree +{ + public: + + THuffmannTree(); + void InitTree(bool bCompression); + void BuildTree(unsigned int nCmpType); +// void ModifyTree(unsigned long dwIndex); +// void UninitTree(); + +// void Call15007010(Bit32 dwInLength, THTreeItem * item); + THTreeItem * Call1500E740(unsigned int nValue); + void Call1500E820(THTreeItem * pItem); + unsigned int DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType); + unsigned int DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is); + + unsigned long bIsCmp0; // 0000 - 1 if compression type 0 + unsigned long offs0004; // 0004 - Some flag + THTreeItem items0008[0x203]; // 0008 - HTree items + + //- Sometimes used as HTree item ----------- + THTreeItem * pItem3050; // 3050 - Always NULL (?) + THTreeItem * pItem3054; // 3054 - Pointer to Huffman tree item + THTreeItem * pItem3058; // 3058 - Pointer to Huffman tree item (< 0 if invalid) + + //- Sometimes used as HTree item ----------- + THTreeItem * pItem305C; // 305C - Usually NULL + THTreeItem * pFirst; // 3060 - Pointer to top (first) Huffman tree item + THTreeItem * pLast; // 3064 - Pointer to bottom (last) Huffman tree item (< 0 if invalid) + unsigned long nItems; // 3068 - Number of used HTree items + + //------------------------------------------- + THTreeItem * items306C[0x102]; // 306C - THTreeItem pointer array + TQDecompress qd3474[0x80]; // 3474 - Array for quick decompression + int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive + + static unsigned char Table1502A630[];// Some table +}; + +#endif // __HUFFMAN_H__ diff --git a/src/tools/stuffextract/StormLib/pklib/crc32_pk.c b/src/tools/stuffextract/StormLib/pklib/crc32_pk.c new file mode 100644 index 0000000..a155513 --- /dev/null +++ b/src/tools/stuffextract/StormLib/pklib/crc32_pk.c @@ -0,0 +1,72 @@ +/*****************************************************************************/ +/* crc32.c Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Pkware Data Compression Library Version 1.11 */ +/* Dissassembled method crc32 - cdecl version */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 09.04.03 1.00 Lad The first version of crc32.c */ +/* 02.05.03 1.00 Lad Stress test done */ +/*****************************************************************************/ + +#include "pklib.h" + +static char CopyRight[] = "PKWARE Data Compression Library for Win32\r\n" + "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n" + "Patent No. 5,051,745\r\n" + "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" + "Version 1.11\r\n"; + +static unsigned long crc_table[] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + + +unsigned long PKEXPORT crc32pk(char * buffer, unsigned int * psize, unsigned long * old_crc) +{ + unsigned int size = *psize; + unsigned long ch; + unsigned long crc_value = *old_crc; + + while(size-- != 0) + { + ch = *buffer++ ^ (char)crc_value; + crc_value >>= 8; + + crc_value = crc_table[ch & 0x0FF] ^ crc_value; + } + return crc_value; +} diff --git a/src/tools/stuffextract/StormLib/pklib/explode.c b/src/tools/stuffextract/StormLib/pklib/explode.c new file mode 100644 index 0000000..b0a3aeb --- /dev/null +++ b/src/tools/stuffextract/StormLib/pklib/explode.c @@ -0,0 +1,480 @@ +/*****************************************************************************/ +/* explode.c Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Implode function of PKWARE Data Compression library */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */ +/* 08.04.03 1.01 Lad Renamed to explode.c to be compatible with pklib */ +/* 02.05.03 1.01 Lad Stress test done */ +/*****************************************************************************/ + +#include +#include + +#include "pklib.h" + +//----------------------------------------------------------------------------- +// Tables + +static unsigned char DistBits[] = +{ + 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +static unsigned char DistCode[] = +{ + 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, + 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, + 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, + 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 +}; + +static unsigned char ExLenBits[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 +}; + +static unsigned short LenBase[] = +{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106 +}; + +static unsigned char LenBits[] = +{ + 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 +}; + +static unsigned char LenCode[] = +{ + 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 +}; + +static unsigned char ChBitsAsc[] = +{ + 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, + 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, + 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, + 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, + 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, + 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, + 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D +}; + +static unsigned short ChCodeAsc[] = +{ + 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, + 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, + 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, + 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, + 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, + 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, + 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, + 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, + 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, + 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, + 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, + 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, + 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, + 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, + 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, + 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, + 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, + 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, + 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, + 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, + 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, + 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, + 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, + 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, + 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, + 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, + 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, + 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, + 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, + 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, + 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, + 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 +}; + +//----------------------------------------------------------------------------- +// Local variables + +static char Copyright[] = "PKWARE Data Compression Library for Win32\r\n" + "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n" + "Patent No. 5,051,745\r\n" + "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" + "Version 1.11\r\n"; + +//----------------------------------------------------------------------------- +// Local functions + +// Copies a block to another location +static void lmemcpy(void * trg, const void * src, size_t count) +{ + memcpy(trg, src, count); +} + +static void GenDecodeTabs(long count, unsigned char * bits, unsigned char * pCode, unsigned char * buffer2) +{ + long i; + + for(i = count-1; i >= 0; i--) // EBX - count + { + unsigned long idx1 = pCode[i]; + unsigned long idx2 = 1 << bits[i]; + + do + { + buffer2[idx1] = (unsigned char)i; + idx1 += idx2; + } + while(idx1 < 0x100); + } +} + +static void GenAscTabs(TDcmpStruct * pWork) +{ + unsigned short * pChCodeAsc = &ChCodeAsc[0xFF]; + unsigned long acc, add; + unsigned short count; + + for(count = 0x00FF; pChCodeAsc >= ChCodeAsc; pChCodeAsc--, count--) + { + unsigned char * pChBitsAsc = pWork->ChBitsAsc + count; + unsigned char bits_asc = *pChBitsAsc; + + if(bits_asc <= 8) + { + add = (1 << bits_asc); + acc = *pChCodeAsc; + + do + { + pWork->offs2C34[acc] = (unsigned char)count; + acc += add; + } + while(acc < 0x100); + } + else if((acc = (*pChCodeAsc & 0xFF)) != 0) + { + pWork->offs2C34[acc] = 0xFF; + + if(*pChCodeAsc & 0x3F) + { + bits_asc -= 4; + *pChBitsAsc = bits_asc; + + add = (1 << bits_asc); + acc = *pChCodeAsc >> 4; + do + { + pWork->offs2D34[acc] = (unsigned char)count; + acc += add; + } + while(acc < 0x100); + } + else + { + bits_asc -= 6; + *pChBitsAsc = bits_asc; + + add = (1 << bits_asc); + acc = *pChCodeAsc >> 6; + do + { + pWork->offs2E34[acc] = (unsigned char)count; + acc += add; + } + while(acc < 0x80); + } + } + else + { + bits_asc -= 8; + *pChBitsAsc = bits_asc; + + add = (1 << bits_asc); + acc = *pChCodeAsc >> 8; + do + { + pWork->offs2EB4[acc] = (unsigned char)count; + acc += add; + } + while(acc < 0x100); + } + } +} + +//----------------------------------------------------------------------------- +// Skips given number of bits in bit buffer. Result is stored in pWork->bit_buff +// If no data in input buffer, returns true + +static int WasteBits(TDcmpStruct * pWork, unsigned long nBits) +{ + // If number of bits required is less than number of (bits in the buffer) ? + if(nBits <= pWork->extra_bits) + { + pWork->extra_bits -= nBits; + pWork->bit_buff >>= nBits; + return 0; + } + + // Load input buffer if necessary + pWork->bit_buff >>= pWork->extra_bits; + if(pWork->in_pos == pWork->in_bytes) + { + pWork->in_pos = sizeof(pWork->in_buff); + if((pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param)) == 0) + return 1; + pWork->in_pos = 0; + } + + // Update bit buffer + pWork->bit_buff |= (pWork->in_buff[pWork->in_pos++] << 8); + pWork->bit_buff >>= (nBits - pWork->extra_bits); + pWork->extra_bits = (pWork->extra_bits - nBits) + 8; + return 0; +} + +//----------------------------------------------------------------------------- +// Returns : 0x000 - 0x0FF : One byte from compressed file. +// 0x100 - 0x305 : Copy previous block (0x100 = 1 byte) +// 0x306 : Out of buffer (?) + +static unsigned long DecodeLit(TDcmpStruct * pWork) +{ + unsigned long nBits; // Number of bits to skip + unsigned long value; // Position in buffers + + // Test the current bit in byte buffer. If is not set, simply return the next byte. + if(pWork->bit_buff & 1) + { + // Skip current bit in the buffer + if(WasteBits(pWork, 1)) + return 0x306; + + // The next bits are position in buffers + value = pWork->position2[(pWork->bit_buff & 0xFF)]; + + // Get number of bits to skip + if(WasteBits(pWork, pWork->LenBits[value])) + return 0x306; + + if((nBits = pWork->ExLenBits[value]) != 0) + { + unsigned long val2 = pWork->bit_buff & ((1 << nBits) - 1); + + if(WasteBits(pWork, nBits)) + { + if((value + val2) != 0x10E) + return 0x306; + } + value = pWork->LenBase[value] + val2; + } + return value + 0x100; // Return number of bytes to repeat + } + + // Waste one bit + if(WasteBits(pWork, 1)) + return 0x306; + + // If the binary compression type, read 8 bits and return them as one byte. + if(pWork->ctype == CMP_BINARY) + { + value = pWork->bit_buff & 0xFF; + if(WasteBits(pWork, 8)) + return 0x306; + return value; + } + + // When ASCII compression ... + if(pWork->bit_buff & 0xFF) + { + value = pWork->offs2C34[pWork->bit_buff & 0xFF]; + + if(value == 0xFF) + { + if(pWork->bit_buff & 0x3F) + { + if(WasteBits(pWork, 4)) + return 0x306; + + value = pWork->offs2D34[pWork->bit_buff & 0xFF]; + } + else + { + if(WasteBits(pWork, 6)) + return 0x306; + + value = pWork->offs2E34[pWork->bit_buff & 0x7F]; + } + } + } + else + { + if(WasteBits(pWork, 8)) + return 0x306; + + value = pWork->offs2EB4[pWork->bit_buff & 0xFF]; + } + + return WasteBits(pWork, pWork->ChBitsAsc[value]) ? 0x306 : value; +} + +//----------------------------------------------------------------------------- +// Retrieves the number of bytes to move back + +static unsigned long DecodeDist(TDcmpStruct * pWork, unsigned long dwLength) +{ + unsigned long pos = pWork->position1[(pWork->bit_buff & 0xFF)]; + unsigned long nSkip = pWork->DistBits[pos]; // Number of bits to skip + + // Skip the appropriate number of bits + if(WasteBits(pWork, nSkip) == 1) + return 0; + + if(dwLength == 2) + { + pos = (pos << 2) | (pWork->bit_buff & 0x03); + + if(WasteBits(pWork, 2) == 1) + return 0; + } + else + { + pos = (pos << pWork->dsize_bits) | (pWork->bit_buff & pWork->dsize_mask); + + // Skip the bits + if(WasteBits(pWork, pWork->dsize_bits) == 1) + return 0; + } + return pos+1; +} + +static unsigned long Expand(TDcmpStruct * pWork) +{ + unsigned int copyBytes; // Number of bytes to copy + unsigned long oneByte; // One byte from compressed file + unsigned long dwResult; + + pWork->outputPos = 0x1000; // Initialize output buffer position + + // If end of data or error, terminate decompress + while((dwResult = oneByte = DecodeLit(pWork)) < 0x305) + { + // If one byte is greater than 0x100, means "Repeat n - 0xFE bytes" + if(oneByte >= 0x100) + { + unsigned char * source; // ECX + unsigned char * target; // EDX + unsigned long copyLength = oneByte - 0xFE; + unsigned long moveBack; + + // Get length of data to copy + if((moveBack = DecodeDist(pWork, copyLength)) == 0) + { + dwResult = 0x306; + break; + } + + // Target and source pointer + target = &pWork->out_buff[pWork->outputPos]; + source = target - moveBack; + pWork->outputPos += copyLength; + + while(copyLength-- > 0) + *target++ = *source++; + } + else + pWork->out_buff[pWork->outputPos++] = (unsigned char)oneByte; + + // If number of extracted bytes has reached 1/2 of output buffer, + // flush output buffer. + if(pWork->outputPos >= 0x2000) + { + // Copy decompressed data into user buffer + copyBytes = 0x1000; + pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); + + // If there are some data left, keep them alive + lmemcpy(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000); + pWork->outputPos -= 0x1000; + } + } + + copyBytes = pWork->outputPos - 0x1000; + pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); + return dwResult; +} + + +//----------------------------------------------------------------------------- +// Main exploding function. + +unsigned int explode( + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), + void (*write_buf)(char *buf, unsigned int *size, void *param), + char *work_buf, + void *param) +{ + TDcmpStruct * pWork = (TDcmpStruct *)work_buf; + + // Set the whole work buffer to zeros + memset(pWork, 0, sizeof(TDcmpStruct)); + + // Initialize work struct and load compressed data + pWork->read_buf = read_buf; + pWork->write_buf = write_buf; + pWork->param = param; + pWork->in_pos = sizeof(pWork->in_buff); + pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param); + if(pWork->in_bytes <= 4) + return CMP_BAD_DATA; + + pWork->ctype = pWork->in_buff[0]; // Get the compression type + pWork->dsize_bits = pWork->in_buff[1]; // Get the dictionary size + pWork->bit_buff = pWork->in_buff[2]; // Initialize 16-bit bit buffer + pWork->extra_bits = 0; // Extra (over 8) bits + pWork->in_pos = 3; // Position in input buffer + + // Test for the valid dictionary size + if(4 > pWork->dsize_bits || pWork->dsize_bits > 6) + return CMP_INVALID_DICTSIZE; + + pWork->dsize_mask = 0xFFFF >> (0x10 - pWork->dsize_bits); // Shifted by 'sar' instruction + + if(pWork->ctype != CMP_BINARY) + { + if(pWork->ctype != CMP_ASCII) + return CMP_INVALID_MODE; + + lmemcpy(pWork->ChBitsAsc, ChBitsAsc, sizeof(pWork->ChBitsAsc)); + GenAscTabs(pWork); + } + + lmemcpy(pWork->LenBits, LenBits, sizeof(pWork->LenBits)); + GenDecodeTabs(0x10, pWork->LenBits, LenCode, pWork->position2); + lmemcpy(pWork->ExLenBits, ExLenBits, sizeof(pWork->ExLenBits)); + lmemcpy(pWork->LenBase, LenBase, sizeof(pWork->LenBase)); + lmemcpy(pWork->DistBits, DistBits, sizeof(pWork->DistBits)); + GenDecodeTabs(0x40, pWork->DistBits, DistCode, pWork->position1); + if(Expand(pWork) != 0x306) + return CMP_NO_ERROR; + + return CMP_ABORT; +} diff --git a/src/tools/stuffextract/StormLib/pklib/implode.c b/src/tools/stuffextract/StormLib/pklib/implode.c new file mode 100644 index 0000000..be6aecb --- /dev/null +++ b/src/tools/stuffextract/StormLib/pklib/implode.c @@ -0,0 +1,674 @@ +/*****************************************************************************/ +/* implode.c Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Implode function of PKWARE Data Compression library */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 11.04.03 1.00 Lad First version of implode.c */ +/* 02.05.03 1.00 Lad Stress test done */ +/*****************************************************************************/ + +#include +#include + +#include "pklib.h" + +#if ((1200 < _MSC_VER) && (_MSC_VER < 1400)) +#pragma optimize("", off) // Fucking Microsoft VS.NET 2003 compiler !!! + // (_MSC_VER=1310) +#endif + +//----------------------------------------------------------------------------- +// Defines + +#define DICT_OFFSET 0x204 +#define UNCMP_OFFSET (pWork->dsize_bytes + DICT_OFFSET) + +//----------------------------------------------------------------------------- +// Tables + +static unsigned char DistBits[] = +{ + 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +static unsigned char DistCode[] = +{ + 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, + 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, + 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, + 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 +}; + +static unsigned char ExLenBits[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 +}; + +static unsigned char LenBits[] = +{ + 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 +}; + +static unsigned char LenCode[] = +{ + 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 +}; + +static unsigned char ChBitsAsc[] = +{ + 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, + 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, + 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, + 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, + 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, + 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, + 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D +}; + +static unsigned short ChCodeAsc[] = +{ + 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, + 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, + 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, + 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, + 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, + 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, + 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, + 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, + 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, + 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, + 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, + 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, + 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, + 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, + 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, + 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, + 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, + 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, + 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, + 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, + 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, + 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, + 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, + 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, + 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, + 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, + 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, + 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, + 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, + 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, + 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, + 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 +}; + +//----------------------------------------------------------------------------- +// Local variables + +static char Copyright[] = "PKWARE Data Compression Library for Win32\r\n" + "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n" + "Patent No. 5,051,745\r\n" + "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" + "Version 1.11\r\n"; + +//----------------------------------------------------------------------------- +// Local functions + +// Fills memory block with a character +static void lmemset(void * buff, int c, size_t count) +{ + memset(buff, c, count); +} + +// Copies memory block to another location +static void lmemcpy(void * trg, const void * src, size_t count) +{ + memcpy(trg, src, count); +} + +static void SortBuffer(TCmpStruct * pWork, unsigned char * uncmp_data, unsigned char * work_end) +{ + unsigned short * pin0DC8; + unsigned char * puncmp; + unsigned long offs1, offs2; + unsigned long ndwords; + unsigned int add; + + // Fill 0x480 dwords (0x1200 bytes) + ndwords = (unsigned long)((pWork->out_buff - (char *)pWork->offs0DC8 + 1) >> 2); + if(ndwords <= 1) + ndwords = 1; + memset(pWork->offs0DC8, 0, ndwords << 2); + + for(puncmp = uncmp_data; work_end > puncmp; puncmp++) + pWork->offs0DC8[(puncmp[0] * 4) + (puncmp[1] * 5)]++; + + add = 0; + for(pin0DC8 = pWork->offs0DC8; pin0DC8 < &pWork->offs1FC8; pin0DC8++) + { + add += *pin0DC8; + *pin0DC8 = (unsigned short)add; + } + + for(work_end--; work_end >= uncmp_data; work_end--) + { + offs1 = (work_end[0] * 4) + (work_end[1] * 5); // EAX + offs2 = (unsigned long)(work_end - pWork->work_buff); // EDI + + pWork->offs0DC8[offs1]--; + pWork->offs49D0[pWork->offs0DC8[offs1]] = (unsigned short)offs2; + } +} + +static void FlushBuf(TCmpStruct * pWork) +{ + unsigned char save_ch1; + unsigned char save_ch2; + unsigned int size = 0x800; + + pWork->write_buf(pWork->out_buff, &size, pWork->param); + + save_ch1 = pWork->out_buff[0x800]; + save_ch2 = pWork->out_buff[pWork->out_bytes]; + pWork->out_bytes -= 0x800; + + lmemset(pWork->out_buff, 0, 0x802); + + if(pWork->out_bytes != 0) + pWork->out_buff[0] = save_ch1; + if(pWork->out_bits != 0) + pWork->out_buff[pWork->out_bytes] = save_ch2; +} + +static void OutputBits(TCmpStruct * pWork, unsigned int nbits, unsigned long bit_buff) +{ + unsigned int out_bits; + + // If more than 8 bits to output, do recursion + if(nbits > 8) + { + OutputBits(pWork, 8, bit_buff); + bit_buff >>= 8; + nbits -= 8; + } + + // Add bits to the last out byte in out_buff; + out_bits = pWork->out_bits; + pWork->out_buff[pWork->out_bytes] |= (unsigned char)(bit_buff << out_bits); + pWork->out_bits += nbits; + + // If 8 or more bits, increment number of bytes + if(pWork->out_bits > 8) + { + pWork->out_bytes++; + bit_buff >>= (8 - out_bits); + + pWork->out_buff[pWork->out_bytes] = (unsigned char)bit_buff; + pWork->out_bits &= 7; + } + else + { + pWork->out_bits &= 7; + if(pWork->out_bits == 0) + pWork->out_bytes++; + } + + // If there is enough compressed bytes, flush them + if(pWork->out_bytes >= 0x800) + FlushBuf(pWork); +} + +static unsigned long FindRep(TCmpStruct * pWork, unsigned char * srcbuff) +{ + unsigned short esp12; + unsigned char * esp14; + unsigned short esp18; + unsigned char * srcbuff2; + unsigned char esp20; + + unsigned char * srcbuff3; + unsigned short * pin0DC8; + unsigned char * pin27CC; + unsigned short * pin49D0; + unsigned long nreps = 1; // EAX + unsigned long ebx, esi; + unsigned short di; + + pin0DC8 = pWork->offs0DC8 + (srcbuff[0] * 4) + (srcbuff[1] * 5); + esi = (unsigned long)(srcbuff - pWork->dsize_bytes - pWork->work_buff + 1); + esp18 = *pin0DC8; + pin49D0 = pWork->offs49D0 + esp18; + + if(*pin49D0 < esi) + { + while(*pin49D0 < esi) + { + pin49D0++; + esp18++; + } + *pin0DC8 = esp18; + } +//--------------------------------------------------------------------------- + srcbuff2 = srcbuff - 1; + pin49D0 = pWork->offs49D0 + esp18; + pin27CC = pWork->work_buff + *pin49D0; + if(srcbuff2 <= pin27CC) + return 0; +//--------------------------------------------------------------------------- + srcbuff3 = srcbuff; + for(;;) + { + if(srcbuff3[nreps-1] == pin27CC[nreps-1] && *srcbuff3 == *pin27CC) + { + // + // The following code does not work when compiled with MSVC.NET 2003 + // optimizing compiler. We have to switch the optimizations off to make it work + // I found that in debug version (where the optimizations are off), the value + // of "pin27CC" gets incremented twice (once at below, once in the "for" loop) + // + + pin27CC++; + srcbuff3++; + + for(ebx = 2; ebx < DICT_OFFSET; ebx++) + { + pin27CC++; + srcbuff3++; + if(*pin27CC != *srcbuff3) + break; + } + + srcbuff3 = srcbuff; + if(ebx >= nreps) + { + pWork->offs0000 = (unsigned int)(srcbuff3 - pin27CC + ebx - 1); + if((nreps = ebx) > 10) + break; + } + } + + pin49D0++; + esp18++; + pin27CC = pWork->work_buff + *pin49D0; + + if(srcbuff2 > pin27CC) + continue; + + return (nreps >= 2) ? nreps : 0; + } +//--------------------------------------------------------------------------- + if(ebx == DICT_OFFSET) + { + pWork->offs0000--; + return ebx; + } +//--------------------------------------------------------------------------- + pin49D0 = pWork->offs49D0 + esp18; + if(pWork->work_buff + pin49D0[1] >= srcbuff2) + return nreps; +//--------------------------------------------------------------------------- + di = 0; + pWork->offs09BC[0] = 0xFFFF; + pWork->offs09BC[1] = di; + esp12 = 1; + + do + { + esi = di; + if(srcbuff[esp12] != srcbuff[esi]) + { + di = pWork->offs09BC[esi]; + if(di != 0xFFFF) + continue; + } + pWork->offs09BC[++esp12] = ++di; + } + while(esp12 < nreps); +//--------------------------------------------------------------------------- + esi = nreps; + pin27CC = pWork->work_buff + pin49D0[0] + nreps; + esp14 = pin27CC; + + for(;;) // 0040268B + { + esi = pWork->offs09BC[esi]; + if(esi == 0xFFFF) + esi = 0; + + pin49D0 = pWork->offs49D0 + esp18; + do + { + pin49D0++; + esp18++; + pin27CC = pWork->work_buff + pin49D0[0]; + if(pin27CC >= srcbuff2) + return nreps; + } + while(pin27CC + esi < esp14); +//--------------------------------------------------------------------------- + esp20 = srcbuff[nreps - 2]; + if(esp20 == pin27CC[nreps - 2]) + { + if(pin27CC + esi != esp14) + { + esp14 = pin27CC; + esi = 0; + } + } + else + { + pin49D0 = pWork->offs49D0 + esp18; + do + { + pin49D0++; + esp18++; + pin27CC = pWork->work_buff + pin49D0[0]; + if(pin27CC >= srcbuff2) + return nreps; + } + while(pin27CC[nreps - 2] != esp20 || pin27CC[0] != *srcbuff); + + esp14 = pin27CC + 2; + esi = 2; + } +//--------------------------------------------------------------------------- + for(; esp14[0] == srcbuff[esi]; esp14++) + { + if(++esi >= DICT_OFFSET) + break; + } + + if(esi < nreps) + continue; + pWork->offs0000 = (unsigned int)(srcbuff - pin27CC - 1); + if(esi <= nreps) + continue; + nreps = esi; + if(esi == DICT_OFFSET) + return nreps; + + do + { + if(srcbuff[esp12] != srcbuff[di]) + { + di = pWork->offs09BC[di]; + if(di != 0xFFFF) + continue; + } + pWork->offs09BC[++esp12] = ++di; + } + while(esp12 < esi); + } +} + +static void WriteCmpData(TCmpStruct * pWork) +{ + unsigned int nreps = 0; // ESP+10 : Number of repeats + unsigned char * uncmp_end; // ESP+14 : End of uncompressed data + unsigned int esp18 = 0; // ESP+18 : + unsigned int bytes_required; // ESP+1C : Number of bytes required to read + unsigned int esp20 = 0; // ESP+20 : + unsigned char * uncmp_begin = pWork->work_buff + UNCMP_OFFSET; // EDI + unsigned long nreps1; + unsigned long save_offs0000 = 0; + + // Store the compression type and dictionary size + pWork->out_buff[0] = (char)pWork->ctype; + pWork->out_buff[1] = (char)pWork->dsize_bits; + pWork->out_bytes = 2; + + // Reset output buffer to zero + lmemset(&pWork->out_buff[2], 0, sizeof(pWork->out_buff) - 2); + pWork->out_bits = 0; + + do + { + int total_loaded = 0; + + for(bytes_required = 0x1000; bytes_required != 0; ) + { + int loaded = pWork->read_buf((char *)pWork->work_buff + UNCMP_OFFSET + total_loaded, + &bytes_required, pWork->param); + + if(loaded == 0) + { + if(total_loaded == 0 && esp20 == 0) + goto __Exit; + esp18 = 1; + break; + } + else + { + total_loaded += loaded; + bytes_required -= loaded; + } + } + + uncmp_end = pWork->work_buff + pWork->dsize_bytes + total_loaded; + if(esp18 != 0) + uncmp_end += DICT_OFFSET; + + // + // Warning: Passing "uncmp_end + 1" to the SortBuffer function may cause + // the output to be unpredictable in Storm.dll's compression. Because Storm.dll + // does not pass the zeroed buffer to the "implode" function, the byte after + // uncmp_end contains random data. This causes difference within dictionary + // created in SortBuffer function and may also cause different compressed output. + // We always zero the data before compression, so this thing never occurs. + // Funny is that it is actually not a bug, because if we decompress the data back, + // we'll get the identical data with the original input. + // + switch(esp20) + { + case 0: + SortBuffer(pWork, uncmp_begin, uncmp_end + 1); + esp20++; + if(pWork->dsize_bytes != 0x1000) + esp20++; + break; + + case 1: + SortBuffer(pWork, uncmp_begin - pWork->dsize_bytes + DICT_OFFSET, uncmp_end + 1); + esp20++; + break; + + default: + SortBuffer(pWork, uncmp_begin - pWork->dsize_bytes, uncmp_end + 1); + break; + } + + while(uncmp_end > uncmp_begin) + { + nreps1 = FindRep(pWork, uncmp_begin); + while(nreps1 != 0) + { + if(nreps1 == 2 && pWork->offs0000 >= 0x100) + break; + + if(esp18 != 0 && uncmp_begin + nreps1 > uncmp_end) + goto _004022DB; + + if(nreps1 >= 8 || uncmp_begin + 1 >= uncmp_end) + goto _004022FF; + + save_offs0000 = pWork->offs0000; // ebp + nreps = nreps1; + nreps1 = FindRep(pWork, uncmp_begin + 1); + + if(nreps >= nreps1) + goto _004022F9; + + if(nreps + 1 >= nreps1 && save_offs0000 <= 0x80) + goto _004022F9; + + OutputBits(pWork, pWork->nChBits[*uncmp_begin], pWork->nChCodes[*uncmp_begin]); + uncmp_begin++; + } + +_0040222F: + OutputBits(pWork, pWork->nChBits[*uncmp_begin], pWork->nChCodes[*uncmp_begin]); + uncmp_begin++; +_00402252:; + } + + if(esp18 == 0) + { + uncmp_begin -= 0x1000; + lmemcpy(pWork->work_buff, pWork->work_buff + 0x1000, pWork->dsize_bytes + DICT_OFFSET); + } + } + while(esp18 == 0); + +__Exit: + OutputBits(pWork, pWork->nChBits[0x305], pWork->nChCodes[0x305]); + if(pWork->out_bits != 0) + pWork->out_bytes++; + pWork->write_buf(pWork->out_buff, &pWork->out_bytes, pWork->param); + return; + +_004022DB: + nreps1 = (unsigned long)(uncmp_end - uncmp_begin); + if(nreps1 < 2) + goto _0040222F; + + if(nreps1 != 2 || pWork->offs0000 < 0x100) + goto _004022FF; + goto _0040222F; + +_004022F9: + nreps1 = nreps; + pWork->offs0000 = save_offs0000; + +_004022FF: + OutputBits(pWork, pWork->nChBits[nreps1 + 0xFE], pWork->nChCodes[nreps1 + 0xFE]); + + if(nreps1 == 2) + { + OutputBits(pWork, pWork->dist_bits[pWork->offs0000 >> 2], + pWork->dist_codes[pWork->offs0000 >> 2]); + OutputBits(pWork, 2, pWork->offs0000 & 3); + } + else + { + OutputBits(pWork, pWork->dist_bits[pWork->offs0000 >> pWork->dsize_bits], + pWork->dist_codes[pWork->offs0000 >> pWork->dsize_bits]); + OutputBits(pWork, pWork->dsize_bits, pWork->dsize_mask & pWork->offs0000); + } + uncmp_begin += nreps1; + goto _00402252; +} + +//----------------------------------------------------------------------------- +// Main imploding function + +unsigned int PKEXPORT implode( + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), + void (*write_buf)(char *buf, unsigned int *size, void *param), + char *work_buf, + void *param, + unsigned int *type, + unsigned int *dsize) +{ + TCmpStruct * pWork = (TCmpStruct *)work_buf; + unsigned int nChCode; + unsigned int nCount; + unsigned int i; + + // Initialize the work buffer. This is not in the Pklib, + // but it seems to be a bug. Storm always pre-fills the data with zeros, + // and always compresses one block only. So the bug will not appear. + // But when a larger data block (size > 0x1000) is compressed, + // it may fail. + memset(pWork, 0, sizeof(TCmpStruct)); + + // Fill the work buffer information + pWork->read_buf = read_buf; + pWork->write_buf = write_buf; + pWork->dsize_bytes = *dsize; + pWork->ctype = *type; + pWork->param = param; + pWork->dsize_bits = 4; + pWork->dsize_mask = 0x0F; + + // Test dictionary size + switch(*dsize) + { + case 0x1000 : + pWork->dsize_bits++; + pWork->dsize_mask |= 0x20; + // No break here !!! + + case 0x0800 : + pWork->dsize_bits++; + pWork->dsize_mask |= 0x10; + // No break here !!! + + case 0x0400 : + break; + + default: + return CMP_INVALID_DICTSIZE; + } + + // Test the compression type + switch(*type) + { + case CMP_BINARY: // We will compress data with binary compression type + for(nChCode = 0, nCount = 0; nCount < 0x100; nCount++) + { + pWork->nChBits[nCount] = 9; + pWork->nChCodes[nCount] = (unsigned short)nChCode; + nChCode = (nChCode & 0x0000FFFF) + 2; + } + break; + + + case CMP_ASCII: // We will compress data with ASCII compression type + for(nCount = 0; nCount < 0x100; nCount++) + { + pWork->nChBits[nCount] = (unsigned char )(ChBitsAsc[nCount] + 1); + pWork->nChCodes[nCount] = (unsigned short)(ChCodeAsc[nCount] * 2); + } + break; + + default: + return CMP_INVALID_MODE; + } + + for(i = 0; i < 0x10; i++) + { + int nCount2 = 0; // EBX + + if((1 << ExLenBits[i]) == 0) + continue; + + do + { + pWork->nChBits[nCount] = (unsigned char)(ExLenBits[i] + LenBits[i] + 1); + pWork->nChCodes[nCount] = (unsigned short)((nCount2 << (LenBits[i] + 1)) | ((LenCode[i] & 0xFFFF00FF) * 2) | 1); + + nCount2++; + nCount++; + } + while((1 << ExLenBits[i]) > nCount2); + } + + // Copy the distance codes and distance bits and perform the compression + lmemcpy(&pWork->dist_codes, DistCode, sizeof(DistCode)); + lmemcpy(&pWork->dist_bits, DistBits, sizeof(DistBits)); + WriteCmpData(pWork); + return CMP_NO_ERROR; +} diff --git a/src/tools/stuffextract/StormLib/pklib/pklib.h b/src/tools/stuffextract/StormLib/pklib/pklib.h new file mode 100644 index 0000000..5bb8ffc --- /dev/null +++ b/src/tools/stuffextract/StormLib/pklib/pklib.h @@ -0,0 +1,137 @@ +/*****************************************************************************/ +/* pklib.h Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Header file for PKWARE Data Compression Library */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 31.03.03 1.00 Lad The first version of pkware.h */ +/*****************************************************************************/ + +#ifndef __PKLIB_H__ +#define __PKLIB_H__ + +#include "../StormPort.h" + +//----------------------------------------------------------------------------- +// Defines + +#define CMP_BINARY 0 // Binary compression +#define CMP_ASCII 1 // Ascii compression + +#define CMP_NO_ERROR 0 +#define CMP_INVALID_DICTSIZE 1 +#define CMP_INVALID_MODE 2 +#define CMP_BAD_DATA 3 +#define CMP_ABORT 4 + +//----------------------------------------------------------------------------- +// Define calling convention + +#ifndef PKEXPORT +#define PKEXPORT //__cdecl // Use for normal __cdecl calling +#endif +//#define PKEXPORT __stdcall +//#define PKEXPORT __fastcall + +//----------------------------------------------------------------------------- +// Internal structures + +// Compression structure +typedef struct +{ + unsigned int offs0000; // 0000 : + unsigned int out_bytes; // 0004 : # bytes available in out_buff + unsigned int out_bits; // 0008 : # of bits available in the last out byte + unsigned int dsize_bits; // 000C : Dict size : 4=0x400, 5=0x800, 6=0x1000 + unsigned int dsize_mask; // 0010 : Dict size : 0x0F=0x400, 0x1F=0x800, 0x3F=0x1000 + unsigned int ctype; // 0014 : Compression type (Ascii or binary) + unsigned int dsize_bytes; // 0018 : Dictionary size in bytes + unsigned char dist_bits[0x40]; // 001C : Distance bits + unsigned char dist_codes[0x40]; // 005C : Distance codes + unsigned char nChBits[0x306]; // 009C : + unsigned short nChCodes[0x306]; // 03A2 : + unsigned short offs09AE; // 09AE : + + void * param; // 09B0 : User parameter + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // 9B4 + void (*write_buf)(char *buf, unsigned int *size, void *param); // 9B8 + + unsigned short offs09BC[0x204]; // 09BC : + unsigned long offs0DC4; // 0DC4 : + unsigned short offs0DC8[0x900]; // 0DC8 : + unsigned short offs1FC8; // 1FC8 : + char out_buff[0x802]; // 1FCA : Output (compressed) data + unsigned char work_buff[0x2204]; // 27CC : Work buffer + // + DICT_OFFSET => Dictionary + // + UNCMP_OFFSET => Uncompressed data + unsigned short offs49D0[0x2000]; // 49D0 : +} TCmpStruct; + +#define CMP_BUFFER_SIZE sizeof(TCmpStruct) // Size of compression buffer + + +// Decompression structure +typedef struct +{ + unsigned long offs0000; // 0000 + unsigned long ctype; // 0004 - Compression type (CMP_BINARY or CMP_ASCII) + unsigned long outputPos; // 0008 - Position in output buffer + unsigned long dsize_bits; // 000C - Dict size (4, 5, 6 for 0x400, 0x800, 0x1000) + unsigned long dsize_mask; // 0010 - Dict size bitmask (0x0F, 0x1F, 0x3F for 0x400, 0x800, 0x1000) + unsigned long bit_buff; // 0014 - 16-bit buffer for processing input data + unsigned long extra_bits; // 0018 - Number of extra (above 8) bits in bit buffer + unsigned int in_pos; // 001C - Position in in_buff + unsigned long in_bytes; // 0020 - Number of bytes in input buffer + void * param; // 0024 - Custom parameter + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // 0028 + void (*write_buf)(char *buf, unsigned int *size, void *param);// 002C + unsigned char out_buff[0x2000]; // 0030 - Output circle buffer. Starting position is 0x1000 + unsigned char offs2030[0x204]; // 2030 - ??? + unsigned char in_buff[0x800]; // 2234 - Buffer for data to be decompressed + unsigned char position1[0x100]; // 2A34 - Positions in buffers + unsigned char position2[0x100]; // 2B34 - Positions in buffers + unsigned char offs2C34[0x100]; // 2C34 - Buffer for + unsigned char offs2D34[0x100]; // 2D34 - Buffer for + unsigned char offs2E34[0x80]; // 2EB4 - Buffer for + unsigned char offs2EB4[0x100]; // 2EB4 - Buffer for + unsigned char ChBitsAsc[0x100]; // 2FB4 - Buffer for + unsigned char DistBits[0x40]; // 30B4 - Numbers of bytes to skip copied block length + unsigned char LenBits[0x10]; // 30F4 - Numbers of bits for skip copied block length + unsigned char ExLenBits[0x10]; // 3104 - Number of valid bits for copied block + unsigned short LenBase[0x10]; // 3114 - Buffer for +} TDcmpStruct; + +#define EXP_BUFFER_SIZE sizeof(TDcmpStruct) // Size of decompress buffer + +//----------------------------------------------------------------------------- +// Public functions + +#ifdef __cplusplus + extern "C" { +#endif + +unsigned int PKEXPORT implode( + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), + void (*write_buf)(char *buf, unsigned int *size, void *param), + char *work_buf, + void *param, + unsigned int *type, + unsigned int *dsize); + + +unsigned int PKEXPORT explode( + unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), + void (*write_buf)(char *buf, unsigned int *size, void *param), + char *work_buf, + void *param); + +// The original name "crc32" was changed to "crc32pk" due +// to compatibility with zlib +unsigned long PKEXPORT crc32pk(char *buffer, unsigned int *size, unsigned long *old_crc); + +#ifdef __cplusplus + } // End of 'extern "C"' declaration +#endif + +#endif // __PKLIB_H__ diff --git a/src/tools/stuffextract/StormLib/wave/wave.cpp b/src/tools/stuffextract/StormLib/wave/wave.cpp new file mode 100644 index 0000000..2b6c12b --- /dev/null +++ b/src/tools/stuffextract/StormLib/wave/wave.cpp @@ -0,0 +1,356 @@ +/*****************************************************************************/ +/* wave.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains decompression methods used by Storm.dll to decompress*/ +/* WAVe files. Thanks to Tom Amigo for releasing his sources. */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */ +/* 20.05.03 2.00 Lad Added compression */ +/* 19.11.03 2.01 Dan Big endian handling */ +/*****************************************************************************/ + +#include "wave.h" + +//------------------------------------------------------------------------------ +// Structures + +union TByteAndWordPtr +{ + short * pw; + unsigned char * pb; +}; + +union TWordAndByteArray +{ + short w; + unsigned char b[2]; +}; + +//----------------------------------------------------------------------------- +// Tables necessary dor decompression + +static long Table1503F120[] = +{ + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006, + 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, + 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, + 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008 +}; + +static long Table1503F1A0[] = +{ + 0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E, + 0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F, + 0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042, + 0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F, + 0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133, + 0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292, + 0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583, + 0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0, + 0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954, + 0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B, + 0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462, + 0x00007FFF +}; + +//---------------------------------------------------------------------------- +// CompressWave + +// 1500EF70 +int CompressWave(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel) +// ECX EDX +{ + TWordAndByteArray Wcmp; + TByteAndWordPtr out; // Pointer to the output buffer + long SInt32Array1[2]; + long SInt32Array2[2]; + long SInt32Array3[2]; + long nBytesRemains = dwOutLength; // Number of bytes remaining + long nWordsRemains; // Number of words remaining +// unsigned char * pbSaveOutBuffer; // Copy of output buffer (actually not used) + unsigned long dwBitBuff; + unsigned long dwStopBit; + unsigned long dwBit; + unsigned long ebx; + unsigned long esi; + long nTableValue; + long nOneWord; + long var_1C; + long var_2C; + int nLength; + int nIndex; + int nValue; + + // If less than 2 bytes remain, don't decompress anything +// pbSaveOutBuffer = pbOutBuffer; + out.pb = pbOutBuffer; + if(nBytesRemains < 2) + return 2; + + Wcmp.b[1] = (unsigned char)(nCmpLevel - 1); + Wcmp.b[0] = (unsigned char)0; + + *out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w); + if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains) + return (int)(out.pb - pbOutBuffer + (nChannels * 2)); + + SInt32Array1[0] = SInt32Array1[1] = 0x2C; + + for(int i = 0; i < nChannels; i++) + { + nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); + *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); + SInt32Array2[i] = nOneWord; + } + + // Weird. But it's there + nLength = dwInLength; + if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx; + nLength++; + + nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer); + nLength = (nLength < 0) ? 0 : nLength; + + nIndex = nChannels - 1; // edi + nWordsRemains = dwInLength / 2; // eax + + // ebx - nChannels + // ecx - pwOutPos + for(int chnl = nChannels; chnl < nWordsRemains; chnl++) + { + // 1500F030 + if((out.pb - pbOutBuffer + 2) > nBytesRemains) + return (int)(out.pb - pbOutBuffer + 2); + + // Switch index + if(nChannels == 2) + nIndex = (nIndex == 0) ? 1 : 0; + + // Load one word from the input stream + nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord + SInt32Array3[nIndex] = nOneWord; + + // esi - SInt32Array2[nIndex] + // eax - nValue + nValue = nOneWord - SInt32Array2[nIndex]; + nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue; + + ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40; + + // esi - SInt32Array2[nIndex] + // edx - Table1503F1A0[SInt32Array2[nIndex]] + // edi - (Table1503F1A0[SInt32Array1[nIndex]] >> nCmpLevel) + nTableValue = Table1503F1A0[SInt32Array1[nIndex]]; + dwStopBit = (unsigned long)nCmpLevel; + + // edi - nIndex; + if(nValue < (nTableValue >> nCmpLevel)) + { + if(SInt32Array1[nIndex] != 0) + SInt32Array1[nIndex]--; + *out.pb++ = 0x80; + } + else + { + while(nValue > nTableValue * 2) + { + if(SInt32Array1[nIndex] >= 0x58 || nLength == 0) + break; + + SInt32Array1[nIndex] += 8; + if(SInt32Array1[nIndex] > 0x58) + SInt32Array1[nIndex] = 0x58; + + nTableValue = Table1503F1A0[SInt32Array1[nIndex]]; + *out.pb++ = 0x81; + nLength--; + } + + var_2C = nTableValue >> Wcmp.b[1]; + dwBitBuff = 0; + + esi = (1 << (dwStopBit - 2)); + dwStopBit = (esi <= 0x20) ? esi : 0x20; + + for(var_1C = 0, dwBit = 1; ; dwBit <<= 1) + { +// esi = var_1C + nTableValue; + if((var_1C + nTableValue) <= nValue) + { + var_1C += nTableValue; + dwBitBuff |= dwBit; + } + if(dwBit == dwStopBit) + break; + + nTableValue >>= 1; + } + + nValue = SInt32Array2[nIndex]; + if(ebx != 0) + { + nValue -= (var_1C + var_2C); + if(nValue < -32768) + nValue = -32768; + } + else + { + nValue += (var_1C + var_2C); + if(nValue > 32767) + nValue = 32767; + } + + SInt32Array2[nIndex] = nValue; + *out.pb++ = (unsigned char)(dwBitBuff | ebx); + nTableValue = Table1503F120[dwBitBuff & 0x1F]; + SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue; + if(SInt32Array1[nIndex] < 0) + SInt32Array1[nIndex] = 0; + else if(SInt32Array1[nIndex] > 0x58) + SInt32Array1[nIndex] = 0x58; + } + } + + return (int)(out.pb - pbOutBuffer); +} + +//---------------------------------------------------------------------------- +// DecompressWave + +// 1500F230 +int DecompressWave(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels) +{ + TByteAndWordPtr out; // Output buffer + TByteAndWordPtr in; + unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength); + long SInt32Array1[2]; + long SInt32Array2[2]; + long nOneWord; + int dwOutLengthCopy = dwOutLength; + int nIndex; + + SInt32Array1[0] = SInt32Array1[1] = 0x2C; + out.pb = pbOutBuffer; + in.pb = pbInBuffer; + in.pw++; + + // Fill the Uint32Array2 array by channel values. + for(int i = 0; i < nChannels; i++) + { + nOneWord = BSWAP_INT16_SIGNED(*in.pw++); + SInt32Array2[i] = nOneWord; + if(dwOutLengthCopy < 2) + return (int)(out.pb - pbOutBuffer); + + *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); + dwOutLengthCopy -= sizeof(short); + } + + // Get the initial index + nIndex = nChannels - 1; + + // Perform the decompression + while(in.pb < pbInBufferEnd) + { + unsigned char nOneByte = *in.pb++; + + // Switch index + if(nChannels == 2) + nIndex = (nIndex == 0) ? 1 : 0; + + // 1500F2A2: Get one byte from input buffer + if(nOneByte & 0x80) + { + switch(nOneByte & 0x7F) + { + case 0: // 1500F315 + if(SInt32Array1[nIndex] != 0) + SInt32Array1[nIndex]--; + + if(dwOutLengthCopy < 2) + return (int)(out.pb - pbOutBuffer); + + *out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]); + dwOutLength -= sizeof(unsigned short); + break; + + case 1: // 1500F2E8 + SInt32Array1[nIndex] += 8; + if(SInt32Array1[nIndex] > 0x58) + SInt32Array1[nIndex] = 0x58; + + if(nChannels == 2) + nIndex = (nIndex == 0) ? 1 : 0; + break; + + case 2: // 1500F41E + break; + + default: // 1500F2C4 + SInt32Array1[nIndex] -= 8; + if(SInt32Array1[nIndex] < 0) + SInt32Array1[nIndex] = 0; + + if(nChannels == 2) + nIndex = (nIndex == 0) ? 1 : 0; + break; + } + } + else + { + // 1500F349 + long temp1 = Table1503F1A0[SInt32Array1[nIndex]]; // EDI + long temp2 = temp1 >> pbInBuffer[1]; // ESI + long temp3 = SInt32Array2[nIndex]; // ECX + + if(nOneByte & 0x01) // EBX = nOneByte + temp2 += (temp1 >> 0); + + if(nOneByte & 0x02) + temp2 += (temp1 >> 1); + + if(nOneByte & 0x04) + temp2 += (temp1 >> 2); + + if(nOneByte & 0x08) + temp2 += (temp1 >> 3); + + if(nOneByte & 0x10) + temp2 += (temp1 >> 4); + + if(nOneByte & 0x20) + temp2 += (temp1 >> 5); + + if(nOneByte & 0x40) + { + temp3 = temp3 - temp2; + if(temp3 <= -32768) + temp3 = -32768; + } + else + { + temp3 = temp3 + temp2; + if(temp3 >= 32767) + temp3 = 32767; + } + + SInt32Array2[nIndex] = temp3; + if(dwOutLength < 2) + break; + + // Store the output 16-bit value + *out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]); + dwOutLength -= 2; + + SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F]; + + if(SInt32Array1[nIndex] < 0) + SInt32Array1[nIndex] = 0; + else if(SInt32Array1[nIndex] > 0x58) + SInt32Array1[nIndex] = 0x58; + } + } + return (int)(out.pb - pbOutBuffer); +} diff --git a/src/tools/stuffextract/StormLib/wave/wave.h b/src/tools/stuffextract/StormLib/wave/wave.h new file mode 100644 index 0000000..96bdf43 --- /dev/null +++ b/src/tools/stuffextract/StormLib/wave/wave.h @@ -0,0 +1,22 @@ +/*****************************************************************************/ +/* Wave.h Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Header file for WAVe unplode functions */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 31.03.03 1.00 Lad The first version of Wave.h */ +/*****************************************************************************/ + +#ifndef __WAVE_H__ +#define __WAVE_H__ + +//----------------------------------------------------------------------------- +// Functions + +#include "../StormPort.h" + +int CompressWave (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels); +int DecompressWave(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels); + +#endif // __WAVE_H__ diff --git a/src/tools/stuffextract/StuffExtract.cpp b/src/tools/stuffextract/StuffExtract.cpp new file mode 100644 index 0000000..578a151 --- /dev/null +++ b/src/tools/stuffextract/StuffExtract.cpp @@ -0,0 +1,163 @@ +#include +#define _COMMON_NO_THREADS +#include "common.h" +#include "MPQHelper.h" +#include "dbcfile.h" +#include "StuffExtract.h" +#include "DBCFieldData.h" +int main(int argc, char *argv[]) +{ + printf("StuffExtract [version %u]\n",SE_VERSION); + ConvertDBC(); + + + printf("\n -- finished --\n"); + char crap[200]; + fgets(crap,sizeof(crap),stdin); + + //while(true); + return 0; +} + +// be careful using this, that you supply correct format string +std::string AutoGetDataString(DBCFile::Iterator& it, const char* format, uint32 field) +{ + if(format[field]=='i' || format[field]=='f') + return toString( (*it).getInt(field) ); + if(format[field]=='s') + return (*it).getString(field); + return ""; +} + + +// output a formatted scp file +void OutSCP(char *fn, SCPStorageMap& scp) +{ + std::fstream f; + f.open(fn, std::ios_base::out); + if(f.is_open()) + { + for(SCPStorageMap::iterator mi = scp.begin(); mi != scp.end(); mi++) + { + f << "[" << mi->first << "]\n"; + for(std::list::iterator li = mi->second.begin(); li != mi->second.end(); li++) + { + f << *li << "\n"; + } + f << "\n"; + } + f.close(); + } + else + { + printf("OutSCP: unable to write '%s'\n",fn); + } +} + + +bool ConvertDBC(void) +{ + std::map racemap; // needed to extract other dbc files correctly + SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage; // will store the converted data from dbc files + DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries; + printf("Opening DBC archive...\n"); + MPQHelper mpq; + if(!mpq.AssignArchive("Data/dbc.MPQ")) + { + printf("ConvertDBC: Could not open 'Data/dbc.MPQ'\n"); + return false; + } + printf("Opening DBC files...\n"); + EmotesText.openmem(mpq.ExtractFile("DBFilesClient\\EmotesText.dbc")); + EmotesTextData.openmem(mpq.ExtractFile("DBFilesClient\\EmotesTextData.dbc")); + EmotesTextSound.openmem(mpq.ExtractFile("DBFilesClient\\EmotesTextSound.dbc")); + ChrRaces.openmem(mpq.ExtractFile("DBFilesClient\\ChrRaces.dbc")); + SoundEntries.openmem(mpq.ExtractFile("DBFilesClient\\SoundEntries.dbc")); + //... + printf("DBC files opened.\n"); + //... + printf("Reading data: races.."); + for(DBCFile::Iterator it = ChrRaces.begin(); it != ChrRaces.end(); ++it) + { + uint32 id = (*it).getUInt(CHRRACES_RACEID); + racemap[id] = (*it).getString(CHRRACES_NAME_GENERAL); // for later use + for(uint32 field=CHRRACES_RACEID; field < CHARRACES_END; field++) + { + if(strlen(ChrRacesFieldNames[field])) + { + std::string value = AutoGetDataString(it,ChrRacesFormat,field); + RaceDataStorage[id].push_back(std::string(ChrRacesFieldNames[field]).append("=").append(value)); + } + } + } + + printf("emotes.."); + for(DBCFile::Iterator it = EmotesText.begin(); it != EmotesText.end(); ++it) + { + uint32 em = (*it).getUInt(EMOTESTEXT_EMOTE_ID); + EmoteDataStorage[em].push_back(std::string("name=") + (*it).getString(EMOTESTEXT_EMOTE_STRING)); + EmoteDataStorage[em].push_back(std::string("anim=") + toString( (*it).getUInt(EMOTESTEXT_ANIM)) ); + for(uint32 field=EMOTESTEXT_EMOTE_ID; field > SCPStorageMap; + +int main(int argc, char *argv[]); +void OutSCP(char*, SCPStorageMap&); +bool ConvertDBC(void); + + +#endif \ No newline at end of file diff --git a/src/tools/stuffextract/dbcfile.cpp b/src/tools/stuffextract/dbcfile.cpp new file mode 100644 index 0000000..e4b85ea --- /dev/null +++ b/src/tools/stuffextract/dbcfile.cpp @@ -0,0 +1,103 @@ +#define _COMMON_NO_THREADS +#include "common.h" +#include +#include "dbcfile.h" + +DBCFile::DBCFile() +{ + data = NULL; +} + +DBCFile::DBCFile(const std::string &fn) +{ + DBCFile(); + filename = fn; +} + +bool DBCFile::open() +{ + std::fstream f; + f.open(filename.c_str(), std::ios_base::binary | std::ios_base::in); + if(!f.is_open()) + { + printf("DBC: %s failed to open!\n",filename.c_str()); + f.close(); + return false; + } + char header[4]; + unsigned int na,nb,es,ss; + + f.read(header,4); // Number of records + if(!(header[0]=='W' && header[1]=='D' && header[2]=='B' && header[3] == 'C')) + { + printf("DBC: %s is no DBC file!\n",filename.c_str()); + f.close(); + return false; + } + f.read((char*)&na,4); // Number of records + f.read((char*)&nb,4); // Number of fields + f.read((char*)&es,4); // Size of a record + f.read((char*)&ss,4); // String size + + recordSize = es; + recordCount = na; + fieldCount = nb; + stringSize = ss; + if(fieldCount*4 != recordSize) + { + printf("DBC: %s is corrupt!\n",filename.c_str()); + f.close(); + return false; + } + + data = new unsigned char[recordSize*recordCount+stringSize]; + stringTable = data + recordSize*recordCount; + f.read((char*)data,recordSize*recordCount+stringSize); + f.close(); + return true; +} + +bool DBCFile::openmem(ByteBuffer bb) +{ + + uint32 hdr; + bb >> hdr; + if(memcmp(&hdr,"WDBC",4)) // check if its a valid dbc file + { + return false; + } + + bb >> recordCount >> fieldCount >> recordSize >> stringSize; + + if(fieldCount*4 != recordSize) + { + return false; + } + + data = new unsigned char[recordSize*recordCount+stringSize]; + stringTable = data + recordSize*recordCount; + memcpy(data,bb.contents()+bb.rpos(),recordSize*recordCount+stringSize); + return true; +} +DBCFile::~DBCFile() +{ + delete [] data; +} + +DBCFile::Record DBCFile::getRecord(size_t id) +{ + assert(data); + return Record(*this, data + id*recordSize); +} + +DBCFile::Iterator DBCFile::begin() +{ + assert(data); + return Iterator(*this, data); +} +DBCFile::Iterator DBCFile::end() +{ + assert(data); + return Iterator(*this, stringTable); +} + diff --git a/src/tools/stuffextract/dbcfile.h b/src/tools/stuffextract/dbcfile.h new file mode 100644 index 0000000..7bf08fd --- /dev/null +++ b/src/tools/stuffextract/dbcfile.h @@ -0,0 +1,121 @@ +// thx to WoWMapView for this! (original version) + +#ifndef DBCFILE_H +#define DBCFILE_H +#include +#include + +class DBCFile +{ +public: + DBCFile(const std::string &fn); + DBCFile(); + ~DBCFile(); + + // Open database. It must be openened before it can be used. + bool open(); + bool openmem(ByteBuffer); + + // Database exceptions + class Exception + { + public: + Exception(const std::string &message): message(message) + { } + virtual ~Exception() + { } + const std::string &getMessage() {return message;} + private: + std::string message; + }; + class NotFound: public Exception + { + public: + NotFound(): Exception("Key was not found") + { } + }; + // Iteration over database + class Iterator; + class Record + { + public: + float getFloat(size_t field) const + { + assert(field < file.fieldCount); + return *reinterpret_cast(offset+field*4); + } + unsigned int getUInt(size_t field) const + { + assert(field < file.fieldCount); + return *reinterpret_cast(offset+field*4); + } + int getInt(size_t field) const + { + assert(field < file.fieldCount); + return *reinterpret_cast(offset+field*4); + } + const char *getString(size_t field) const + { + assert(field < file.fieldCount); + size_t stringOffset = getUInt(field); + assert(stringOffset < file.stringSize); + return reinterpret_cast(file.stringTable + stringOffset); + } + private: + Record(DBCFile &file, unsigned char *offset): file(file), offset(offset) {} + unsigned char *offset; + DBCFile &file; + + friend class DBCFile; + friend class DBCFile::Iterator; + }; + /** Iterator that iterates over records + */ + class Iterator + { + public: + Iterator(DBCFile &file, unsigned char *offset): + record(file, offset) {} + /// Advance (prefix only) + Iterator & operator++() { + record.offset += record.file.recordSize; + return *this; + } + /// Return address of current instance + Record const & operator*() const { return record; } + const Record* operator->() const { + return &record; + } + /// Comparison + bool operator==(const Iterator &b) const + { + return record.offset == b.record.offset; + } + bool operator!=(const Iterator &b) const + { + return record.offset != b.record.offset; + } + private: + Record record; + }; + + // Get record by id + Record getRecord(size_t id); + /// Get begin iterator over records + Iterator begin(); + /// Get begin iterator over records + Iterator end(); + /// Trivial + size_t getRecordCount() const { return recordCount;} + size_t getFieldCount() const { return fieldCount; } +private: + std::string filename; + uint32 recordSize; + uint32 recordCount; + uint32 fieldCount; + uint32 stringSize; + unsigned char *data; + unsigned char *stringTable; +}; + +#endif