* huge changes:

* few script changes; added new func "ScriptExists".
* started an extractor tool; using StormLib (thx to Ladislaw Zezula!). for now convert some dbc files to scp. more stuff will follow.
* some changes to the realm/world connect code; still needs a rewrite.
* support conf parameters for GUI (check conf/gui.conf/gui.conf[.default] )
This commit is contained in:
False.Genesis 2007-04-10 22:42:42 +00:00
parent 71e2f7f224
commit c92f1ea824
83 changed files with 20707 additions and 105 deletions

View File

@ -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

View File

@ -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

24
bin/conf/gui.conf.default Normal file
View File

@ -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

View File

@ -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

35
bin/data/scp/race.scp Normal file
View File

@ -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

View File

@ -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!
//REPLY,0 [BOT] No need to whisper, nobody is here who could read what you wrote.

View File

@ -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

View File

@ -0,0 +1,6 @@
#permission=255
if ?{fileexists ${@def}}
loadscp,{${@0}} ${@def}
else
logdebug skipped loading of non-existent file '${@def}'
endif

View File

@ -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.

View File

@ -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}

View File

@ -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}'

View File

@ -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}

View File

@ -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))

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<s32>(_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();

View File

@ -3,11 +3,10 @@
#include <time.h>
#include <openssl/rand.h>
#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
{

View File

@ -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:

View File

@ -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"

View File

@ -2,7 +2,7 @@
#define _WORLDPACKET_H
#include "SysDefs.h"
#include "Auth/ByteBuffer.h"
#include "ByteBuffer.h"
class WorldPacket : public ByteBuffer
{

View File

@ -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;

View File

@ -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();
}

View File

@ -216,9 +216,6 @@
<File
RelativePath=".\Client\Auth\BigNumber.h">
</File>
<File
RelativePath=".\Client\Auth\ByteBuffer.h">
</File>
<File
RelativePath=".\Client\Auth\md5.c">
</File>

View File

@ -2,7 +2,7 @@
#ifndef _ZCOMPRESSOR_H
#define _ZCOMPRESSOR_H
#include "Auth/ByteBuffer.h"
#include "ByteBuffer.h"
class ZCompressor : public ByteBuffer

View File

@ -16,18 +16,19 @@
#include <deque>
#include <vector>
#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

View File

@ -3,6 +3,7 @@
#include <sstream>
#include <string>
#include <time.h>
#include <fstream>
#include "tools.h"
#ifndef _WIN32
@ -128,9 +129,22 @@ std::deque<std::string> 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;
}

View File

@ -16,5 +16,6 @@ std::string getDateString(void);
uint64 toInt(std::string);
std::string toHexDump(uint8* array,uint32 size,bool spaces=true);
std::deque<std::string> GetFileList(std::string);
bool FileExists(char*);
#endif

View File

@ -0,0 +1,298 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="StuffExtract"
ProjectGUID="{EFFE60F4-DA39-41E8-9E53-E462000A2D91}"
RootNamespace="StuffExtract"
SccProjectName=""
SccLocalPath="">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)/bin"
IntermediateDirectory="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2"
WholeProgramOptimization="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="3"
GlobalOptimizations="TRUE"
InlineFunctionExpansion="2"
ImproveFloatingPointConsistency="FALSE"
FavorSizeOrSpeed="1"
AdditionalIncludeDirectories="../shared;../dep/include;../dep/include/zlib;StormLib"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
StringPooling="TRUE"
RuntimeLibrary="0"
BufferSecurityCheck="FALSE"
EnableFunctionLevelLinking="FALSE"
EnableEnhancedInstructionSet="0"
PrecompiledHeaderFile=".\Release/StuffExtract.pch"
AssemblerListingLocation="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
ObjectFile="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
ProgramDataBaseFileName="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
WarningLevel="3"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="odbc32.lib odbccp32.lib libeay32MT.lib WS2_32.LIB"
ShowProgress="0"
OutputFile="$(OutDir)/$(InputName).exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories=""
IgnoreAllDefaultLibraries="FALSE"
AssemblyDebug="0"
ProgramDatabaseFile="$(SolutionDir)/temp/$(ProjectName)/Release/StuffExtract.pdb"
SubSystem="1"
OptimizeReferences="2"
SetChecksum="TRUE"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/StuffExtract.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="0"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)/bin"
IntermediateDirectory="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)"
ConfigurationType="1"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="FALSE"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
ImproveFloatingPointConsistency="FALSE"
OptimizeForProcessor="0"
AdditionalIncludeDirectories="../shared;../dep/include;../dep/include/zlib;StormLib"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
PrecompiledHeaderFile=""
AssemblerListingLocation="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
ObjectFile="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
ProgramDataBaseFileName="$(SolutionDir)/temp/$(ProjectName)/$(ConfigurationName)/"
BrowseInformation="1"
WarningLevel="3"
SuppressStartupBanner="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="odbc32.lib odbccp32.lib libeay32MT.lib WS2_32.LIB"
OutputFile="$(OutDir)/$(InputName)_debug.exe"
LinkIncremental="2"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories=""
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(SolutionDir)/temp/$(ProjectName)/StuffExtract.pdb"
SubSystem="1"
OptimizeReferences="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/StuffExtract.tlb"
HeaderFileName=""/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="0"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
<File
RelativePath=".\stuffextract\DBCFieldData.h">
</File>
<File
RelativePath=".\stuffextract\dbcfile.cpp">
</File>
<File
RelativePath=".\stuffextract\dbcfile.h">
</File>
<File
RelativePath=".\stuffextract\MPQFile.cpp">
</File>
<File
RelativePath=".\stuffextract\MPQFile.h">
</File>
<File
RelativePath=".\stuffextract\MPQHelper.cpp">
</File>
<File
RelativePath=".\stuffextract\MPQHelper.h">
</File>
<File
RelativePath=".\stuffextract\StuffExtract.cpp">
</File>
<File
RelativePath=".\stuffextract\StuffExtract.h">
</File>
</Filter>
<Filter
Name="StormLib"
Filter="">
<File
RelativePath=".\stuffextract\StormLib\SCommon.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SCommon.h">
</File>
<File
RelativePath=".\stuffextract\StormLib\SCompression.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileCompactArchive.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileCreateArchiveEx.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileExtractFile.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileFindFile.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileOpenArchive.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileOpenFileEx.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SFileReadFile.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\SListFile.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\StormLib.h">
</File>
<File
RelativePath=".\stuffextract\StormLib\StormPort.h">
</File>
<Filter
Name="wave"
Filter="">
<File
RelativePath=".\stuffextract\StormLib\wave\wave.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\wave\wave.h">
</File>
</Filter>
<Filter
Name="bzip2"
Filter="">
<File
RelativePath=".\stuffextract\StormLib\bzip2\blocksort.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\bzlib.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\bzlib.h">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\bzlib_private.h">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\compress.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\crctable.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\decompress.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\huffman.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\bzip2\randtable.c">
</File>
</Filter>
<Filter
Name="pklib"
Filter="">
<File
RelativePath=".\stuffextract\StormLib\pklib\crc32_pk.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\pklib\explode.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\pklib\implode.c">
</File>
<File
RelativePath=".\stuffextract\StormLib\pklib\pklib.h">
</File>
</Filter>
<Filter
Name="huffman"
Filter="">
<File
RelativePath=".\stuffextract\StormLib\huffman\huff.cpp">
</File>
<File
RelativePath=".\stuffextract\StormLib\huffman\huff.h">
</File>
</Filter>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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<MAX_PATCH_NUMBER; i++)
{
char buf[20];
sprintf(buf,"patch-%u.MPQ",i);
if(FileExists(buf))
_patches.push_front(buf);
}
// then assign the original archive name
_archive = fn;
return FileExists(fn);
}
ByteBuffer MPQHelper::ExtractFile(char* fn)
{
ByteBuffer bb;
for(std::list<std::string>::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;
}

View File

@ -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<std::string> _patches; // patch.mpq - patch-9.mpq
std::string _archive;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -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__

View File

@ -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 <string.h>
// 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 <zlib.h> // 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 <bzlib.h> // 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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 '<htm'
{0x4D54483C, "html"}, // HTML '<HTM
{0x216F6F57, "tbl"}, // Table files
{0x31504C42, "blp"}, // BLP textures
{0x32504C42, "blp"}, // BLP textures (v2)
{0x584C444D, "mdx"}, // MDX files
{0x45505954, "pud"}, // Warcraft II maps
{0x38464947, "gif"}, // GIF images 'GIF8'
{0x3032444D, "m2"}, // WoW ??? .m2
{0x43424457, "dbc"}, // ??? .dbc
{0x47585053, "bls"}, // WoW pixel shaders
{0, NULL} // Terminator
};
// TODO: Test for archives > 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;
}

View File

@ -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 <assert.h>
//-----------------------------------------------------------------------------
// 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;
}

View File

@ -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 <windows.h>
#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__

View File

@ -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 <pshpack1.h>
#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 <poppack.h>
#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_

View File

@ -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 <marko.friedemann@bmx-chemnitz.de> */
/* 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 <assert.h>
#include <stdio.h>
#include <windows.h>
#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 <Carbon/Carbon.h> // 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#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__

View File

@ -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 <marko.friedemann@bmx-chemnitz.de>
* 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

View File

@ -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 <marko.friedemann@bmx-chemnitz.de>
* StormPort.cpp for Linux
*
* Author: Daniel Chiaramello <daniel@chiaramello.net>
*
* Carbonized by: Sam Wilkins <swilkins1337@gmail.com>
*
********************************************************************/
#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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
/* 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 ---*/
/*-----------------------------------------------------------*/

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdio.h>
#endif
#ifdef _WIN32
# include <windows.h>
# 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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 <stdlib.h>
#ifndef BZ_NO_STDIO
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include "bzlib.h"
#ifdef _WIN32
#include <io.h>
#endif
#ifdef _WIN32
#define BZ2_LIBNAME "libbz2-1.0.2.DLL"
#include <windows.h>
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;
}

View File

@ -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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 <stdio.h>
int main ()
{
int i;
for (i = 0; i < 48500000 ; i++)
putchar(251);
return 0;
}

View File

@ -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 ---*/
/*-------------------------------------------------------------*/

View File

@ -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 <stdio.h>
#include <stdlib.h>
/* 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;
}

View File

@ -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 <stdio.h>
#include <assert.h>
#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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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__

View File

@ -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;
}

View File

@ -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 <assert.h>
#include <string.h>
#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], &copyBytes, 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], &copyBytes, 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;
}

View File

@ -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 <assert.h>
#include <string.h>
#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;
}

View File

@ -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__

View File

@ -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);
}

View File

@ -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__

View File

@ -0,0 +1,163 @@
#include <fstream>
#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<std::string>::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<uint8,std::string> 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<EMOTESTEXT_END;field++)
{
if((*it).getInt(field) && strlen(EmotesTextFieldNames[field]))
{
uint32 textid;
std::string fname;
for(DBCFile::Iterator ix = EmotesTextData.begin(); ix != EmotesTextData.end(); ++ix)
{
textid = (*ix).getUInt(EMOTESTEXTDATA_TEXTID);
if(textid == (*it).getInt(field))
{
fname = EmotesTextFieldNames[field];
EmoteDataStorage[em].push_back( fname + "=" + (*ix).getString(EMOTESTEXTDATA_STRING) );
break;
}
}
}
}
for(DBCFile::Iterator is = EmotesTextSound.begin(); is != EmotesTextSound.end(); ++is)
{
if(em == (*is).getUInt(EMOTESTEXTSOUND_EMOTEID))
{
std::string record = "Sound";
record += racemap[ (*is).getUInt(EMOTESTEXTSOUND_RACE) ];
record += ((*is).getUInt(EMOTESTEXTSOUND_ISFEMALE) ? "Female" : "Male");
record += "=";
record += toString( (*is).getUInt(EMOTESTEXTSOUND_SOUNDID) );
EmoteDataStorage[em].push_back(record);
}
}
}
printf("sound entries..");
for(DBCFile::Iterator it = SoundEntries.begin(); it != SoundEntries.end(); ++it)
{
uint32 id = (*it).getUInt(SOUNDENTRY_SOUNDID);
for(uint32 field=SOUNDENTRY_SOUNDID; field < SOUNDENTRY_END; field++)
{
if(strlen(SoundEntriesFieldNames[field]))
{
std::string value = AutoGetDataString(it,SoundEntriesFormat,field);
if(value.size()) // only store if a file exists in that field
SoundDataStorage[id].push_back(std::string(SoundEntriesFieldNames[field]) + "=" + value);
}
}
}
//...
printf("DONE!\n");
//...
printf("Writing SCP files:\n");
printf("emote.."); OutSCP(SCPDIR "/emote.scp",EmoteDataStorage);
printf("race.."); OutSCP(SCPDIR "/race.scp",RaceDataStorage);
printf("sound.."); OutSCP(SCPDIR "/sound.scp",SoundDataStorage);
//...
printf("DONE!\n");
return true;
}

View File

@ -0,0 +1,19 @@
#ifndef STUFFEXTRACT_H
#define STUFFEXTRACT_H
#define _COMMON_SKIP_THREADS
#include "common.h"
#define SE_VERSION 1
#define MAPS_VERSION ((uint32)0)
#define OUTDIR "stuffextract"
#define SCPDIR OUTDIR "/scp"
typedef std::map< uint32,std::list<std::string> > SCPStorageMap;
int main(int argc, char *argv[]);
void OutSCP(char*, SCPStorageMap&);
bool ConvertDBC(void);
#endif

View File

@ -0,0 +1,103 @@
#define _COMMON_NO_THREADS
#include "common.h"
#include <fstream>
#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);
}

View File

@ -0,0 +1,121 @@
// thx to WoWMapView for this! (original version)
#ifndef DBCFILE_H
#define DBCFILE_H
#include <cassert>
#include <string>
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<float*>(offset+field*4);
}
unsigned int getUInt(size_t field) const
{
assert(field < file.fieldCount);
return *reinterpret_cast<unsigned int*>(offset+field*4);
}
int getInt(size_t field) const
{
assert(field < file.fieldCount);
return *reinterpret_cast<int*>(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<char*>(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