blob: 001363090bc3966c0017fc1467070de844a169ed [file] [log] [blame]
#include "rar.hpp"
ComprDataIO::ComprDataIO()
{
#ifndef RAR_NOCRYPT
Crypt=new CryptData;
Decrypt=new CryptData;
#endif
Init();
}
void ComprDataIO::Init()
{
UnpackFromMemory=false;
UnpackToMemory=false;
UnpPackedSize=0;
ShowProgress=true;
TestMode=false;
SkipUnpCRC=false;
NoFileHeader=false;
PackVolume=false;
UnpVolume=false;
NextVolumeMissing=false;
SrcFile=NULL;
DestFile=NULL;
UnpWrSize=0;
Command=NULL;
Encryption=false;
Decryption=false;
CurPackRead=CurPackWrite=CurUnpRead=CurUnpWrite=0;
LastPercent=-1;
SubHead=NULL;
SubHeadPos=NULL;
CurrentCommand=0;
ProcessedArcSize=TotalArcSize=0;
}
ComprDataIO::~ComprDataIO()
{
#ifndef RAR_NOCRYPT
delete Crypt;
delete Decrypt;
#endif
}
int ComprDataIO::UnpRead(byte *Addr,size_t Count)
{
#ifndef RAR_NOCRYPT
// In case of encryption we need to align read size to encryption
// block size. We can do it by simple masking, because unpack read code
// always reads more than CRYPT_BLOCK_SIZE, so we do not risk to make it 0.
if (Decryption)
Count &= ~CRYPT_BLOCK_MASK;
#endif
int ReadSize=0,TotalRead=0;
byte *ReadAddr;
ReadAddr=Addr;
while (Count > 0)
{
Archive *SrcArc=(Archive *)SrcFile;
if (UnpackFromMemory)
{
memcpy(Addr,UnpackFromMemoryAddr,UnpackFromMemorySize);
ReadSize=(int)UnpackFromMemorySize;
UnpackFromMemorySize=0;
}
else
{
size_t SizeToRead=((int64)Count>UnpPackedSize) ? (size_t)UnpPackedSize:Count;
if (SizeToRead > 0)
{
if (UnpVolume && Decryption && (int64)Count>UnpPackedSize)
{
// We need aligned blocks for decryption and we want "Keep broken
// files" to work efficiently with missing encrypted volumes.
// So for last data block in volume we adjust the size to read to
// next equal or smaller block producing aligned total block size.
// So we'll ask for next volume only when processing few unaligned
// bytes left in the end, when most of data is already extracted.
size_t NewTotalRead = TotalRead + SizeToRead;
size_t Adjust = NewTotalRead - (NewTotalRead & ~CRYPT_BLOCK_MASK);
size_t NewSizeToRead = SizeToRead - Adjust;
if ((int)NewSizeToRead > 0)
SizeToRead = NewSizeToRead;
}
if (!SrcFile->IsOpened())
return -1;
ReadSize=SrcFile->Read(ReadAddr,SizeToRead);
FileHeader *hd=SubHead!=NULL ? SubHead:&SrcArc->FileHead;
if (!NoFileHeader && hd->SplitAfter)
PackedDataHash.Update(ReadAddr,ReadSize);
}
}
CurUnpRead+=ReadSize;
TotalRead+=ReadSize;
#ifndef NOVOLUME
// These variable are not used in NOVOLUME mode, so it is better
// to exclude commands below to avoid compiler warnings.
ReadAddr+=ReadSize;
Count-=ReadSize;
#endif
UnpPackedSize-=ReadSize;
// Do not ask for next volume if we read something from current volume.
// If next volume is missing, we need to process all data from current
// volume before aborting. It helps to recover all possible data
// in "Keep broken files" mode. But if we process encrypted data,
// we ask for next volume also if we have non-aligned encryption block.
// Since we adjust data size for decryption earlier above,
// it does not hurt "Keep broken files" mode efficiency.
if (UnpVolume && UnpPackedSize == 0 &&
(ReadSize==0 || (Decryption && (TotalRead & CRYPT_BLOCK_MASK) != 0)) )
{
#ifndef NOVOLUME
if (!MergeArchive(*SrcArc,this,true,CurrentCommand))
#endif
{
NextVolumeMissing=true;
return -1;
}
}
else
break;
}
Archive *SrcArc=(Archive *)SrcFile;
if (SrcArc!=NULL)
ShowUnpRead(SrcArc->CurBlockPos+CurUnpRead,UnpArcSize);
if (ReadSize!=-1)
{
ReadSize=TotalRead;
#ifndef RAR_NOCRYPT
if (Decryption)
Decrypt->DecryptBlock(Addr,ReadSize);
#endif
}
Wait();
return ReadSize;
}
#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
// Disable the run time stack check for unrar.dll, so we can manipulate
// with ProcessDataProc call type below. Run time check would intercept
// a wrong ESP before we restore it.
#pragma runtime_checks( "s", off )
#endif
void ComprDataIO::UnpWrite(byte *Addr,size_t Count)
{
#ifdef RARDLL
RAROptions *Cmd=((Archive *)SrcFile)->GetRAROptions();
if (Cmd->DllOpMode!=RAR_SKIP)
{
if (Cmd->Callback!=NULL &&
Cmd->Callback(UCM_PROCESSDATA,Cmd->UserData,(LPARAM)Addr,Count)==-1)
ErrHandler.Exit(RARX_USERBREAK);
if (Cmd->ProcessDataProc!=NULL)
{
// Here we preserve ESP value. It is necessary for those developers,
// who still define ProcessDataProc callback as "C" type function,
// even though in year 2001 we announced in unrar.dll whatsnew.txt
// that it will be PASCAL type (for compatibility with Visual Basic).
#if defined(_MSC_VER)
#ifndef _WIN_64
__asm mov ebx,esp
#endif
#elif defined(_WIN_ALL) && defined(__BORLANDC__)
_EBX=_ESP;
#endif
int RetCode=Cmd->ProcessDataProc(Addr,(int)Count);
// Restore ESP after ProcessDataProc with wrongly defined calling
// convention broken it.
#if defined(_MSC_VER)
#ifndef _WIN_64
__asm mov esp,ebx
#endif
#elif defined(_WIN_ALL) && defined(__BORLANDC__)
_ESP=_EBX;
#endif
if (RetCode==0)
ErrHandler.Exit(RARX_USERBREAK);
}
}
#endif // RARDLL
UnpWrAddr=Addr;
UnpWrSize=Count;
if (UnpackToMemory)
{
if (Count <= UnpackToMemorySize)
{
memcpy(UnpackToMemoryAddr,Addr,Count);
UnpackToMemoryAddr+=Count;
UnpackToMemorySize-=Count;
}
}
else
if (!TestMode)
DestFile->Write(Addr,Count);
CurUnpWrite+=Count;
if (!SkipUnpCRC)
UnpHash.Update(Addr,Count);
ShowUnpWrite();
Wait();
}
#if defined(RARDLL) && defined(_MSC_VER) && !defined(_WIN_64)
// Restore the run time stack check for unrar.dll.
#pragma runtime_checks( "s", restore )
#endif
void ComprDataIO::ShowUnpRead(int64 ArcPos,int64 ArcSize)
{
if (ShowProgress && SrcFile!=NULL)
{
if (TotalArcSize!=0)
{
// important when processing several archives or multivolume archive
ArcSize=TotalArcSize;
ArcPos+=ProcessedArcSize;
}
Archive *SrcArc=(Archive *)SrcFile;
RAROptions *Cmd=SrcArc->GetRAROptions();
int CurPercent=ToPercent(ArcPos,ArcSize);
if (!Cmd->DisablePercentage && CurPercent!=LastPercent)
{
uiExtractProgress(CurUnpWrite,SrcArc->FileHead.UnpSize,ArcPos,ArcSize);
LastPercent=CurPercent;
}
}
}
void ComprDataIO::ShowUnpWrite()
{
}
void ComprDataIO::SetFiles(File *SrcFile,File *DestFile)
{
if (SrcFile!=NULL)
ComprDataIO::SrcFile=SrcFile;
if (DestFile!=NULL)
ComprDataIO::DestFile=DestFile;
LastPercent=-1;
}
void ComprDataIO::GetUnpackedData(byte **Data,size_t *Size)
{
*Data=UnpWrAddr;
*Size=UnpWrSize;
}
void ComprDataIO::SetEncryption(bool Encrypt,CRYPT_METHOD Method,
SecPassword *Password,const byte *Salt,const byte *InitV,
uint Lg2Cnt,byte *HashKey,byte *PswCheck)
{
#ifndef RAR_NOCRYPT
if (Encrypt)
Encryption=Crypt->SetCryptKeys(true,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
else
Decryption=Decrypt->SetCryptKeys(false,Method,Password,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
#endif
}
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetAV15Encryption()
{
Decryption=true;
Decrypt->SetAV15Encryption();
}
#endif
#if !defined(SFX_MODULE) && !defined(RAR_NOCRYPT)
void ComprDataIO::SetCmt13Encryption()
{
Decryption=true;
Decrypt->SetCmt13Encryption();
}
#endif
void ComprDataIO::SetUnpackToMemory(byte *Addr,uint Size)
{
UnpackToMemory=true;
UnpackToMemoryAddr=Addr;
UnpackToMemorySize=Size;
}