301 lines
6.7 KiB
C++
301 lines
6.7 KiB
C++
|
#include "rar.hpp"
|
||
|
|
||
|
QuickOpen::QuickOpen()
|
||
|
{
|
||
|
Buf=NULL;
|
||
|
Init(NULL,false);
|
||
|
}
|
||
|
|
||
|
|
||
|
QuickOpen::~QuickOpen()
|
||
|
{
|
||
|
Close();
|
||
|
delete[] Buf;
|
||
|
}
|
||
|
|
||
|
|
||
|
void QuickOpen::Init(Archive *Arc,bool WriteMode)
|
||
|
{
|
||
|
if (Arc!=NULL) // Unless called from constructor.
|
||
|
Close();
|
||
|
|
||
|
QuickOpen::Arc=Arc;
|
||
|
QuickOpen::WriteMode=WriteMode;
|
||
|
|
||
|
ListStart=NULL;
|
||
|
ListEnd=NULL;
|
||
|
|
||
|
if (Buf==NULL)
|
||
|
Buf=new byte[MaxBufSize];
|
||
|
|
||
|
CurBufSize=0; // Current size of buffered data in write mode.
|
||
|
|
||
|
Loaded=false;
|
||
|
}
|
||
|
|
||
|
|
||
|
void QuickOpen::Close()
|
||
|
{
|
||
|
QuickOpenItem *Item=ListStart;
|
||
|
while (Item!=NULL)
|
||
|
{
|
||
|
QuickOpenItem *Next=Item->Next;
|
||
|
delete[] Item->Header;
|
||
|
delete Item;
|
||
|
Item=Next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void QuickOpen::Load(uint64 BlockPos)
|
||
|
{
|
||
|
if (!Loaded)
|
||
|
{
|
||
|
// If loading for the first time, perform additional intialization.
|
||
|
SeekPos=Arc->Tell();
|
||
|
UnsyncSeekPos=false;
|
||
|
|
||
|
int64 SavePos=SeekPos;
|
||
|
Arc->Seek(BlockPos,SEEK_SET);
|
||
|
|
||
|
// If BlockPos points to original main header, we'll have the infinite
|
||
|
// recursion, because ReadHeader() for main header will attempt to load
|
||
|
// QOpen and call QuickOpen::Load again. If BlockPos points to long chain
|
||
|
// of other main headers, we'll have multiple recursive calls of this
|
||
|
// function wasting resources. So we prohibit QOpen temporarily to
|
||
|
// prevent this. ReadHeader() calls QOpen.Init and sets MainHead Locator
|
||
|
// and QOpenOffset fields, so we cannot use them to prohibit QOpen.
|
||
|
Arc->SetProhibitQOpen(true);
|
||
|
size_t ReadSize=Arc->ReadHeader();
|
||
|
Arc->SetProhibitQOpen(false);
|
||
|
|
||
|
if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
|
||
|
!Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
|
||
|
{
|
||
|
Arc->Seek(SavePos,SEEK_SET);
|
||
|
return;
|
||
|
}
|
||
|
QOHeaderPos=Arc->CurBlockPos;
|
||
|
RawDataStart=Arc->Tell();
|
||
|
RawDataSize=Arc->SubHead.UnpSize;
|
||
|
Arc->Seek(SavePos,SEEK_SET);
|
||
|
|
||
|
Loaded=true; // Set only after all file processing calls like Tell, Seek, ReadHeader.
|
||
|
}
|
||
|
|
||
|
if (Arc->SubHead.Encrypted)
|
||
|
{
|
||
|
RAROptions *Cmd=Arc->GetRAROptions();
|
||
|
#ifndef RAR_NOCRYPT
|
||
|
if (Cmd->Password.IsSet())
|
||
|
Crypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,Arc->SubHead.Salt,
|
||
|
Arc->SubHead.InitV,Arc->SubHead.Lg2Count,
|
||
|
Arc->SubHead.HashKey,Arc->SubHead.PswCheck);
|
||
|
else
|
||
|
#endif
|
||
|
{
|
||
|
Loaded=false;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RawDataPos=0;
|
||
|
ReadBufSize=0;
|
||
|
ReadBufPos=0;
|
||
|
LastReadHeader.Reset();
|
||
|
LastReadHeaderPos=0;
|
||
|
|
||
|
ReadBuffer();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool QuickOpen::Read(void *Data,size_t Size,size_t &Result)
|
||
|
{
|
||
|
if (!Loaded)
|
||
|
return false;
|
||
|
// Find next suitable cached block.
|
||
|
while (LastReadHeaderPos+LastReadHeader.Size()<=SeekPos)
|
||
|
if (!ReadNext())
|
||
|
break;
|
||
|
if (!Loaded)
|
||
|
{
|
||
|
// If something wrong happened, let's set the correct file pointer
|
||
|
// and stop further quick open processing.
|
||
|
if (UnsyncSeekPos)
|
||
|
Arc->File::Seek(SeekPos,SEEK_SET);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (SeekPos>=LastReadHeaderPos && SeekPos+Size<=LastReadHeaderPos+LastReadHeader.Size())
|
||
|
{
|
||
|
memcpy(Data,LastReadHeader+size_t(SeekPos-LastReadHeaderPos),Size);
|
||
|
Result=Size;
|
||
|
SeekPos+=Size;
|
||
|
UnsyncSeekPos=true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (UnsyncSeekPos)
|
||
|
{
|
||
|
Arc->File::Seek(SeekPos,SEEK_SET);
|
||
|
UnsyncSeekPos=false;
|
||
|
}
|
||
|
int ReadSize=Arc->File::Read(Data,Size);
|
||
|
if (ReadSize<0)
|
||
|
{
|
||
|
Loaded=false;
|
||
|
return false;
|
||
|
}
|
||
|
Result=ReadSize;
|
||
|
SeekPos+=ReadSize;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool QuickOpen::Seek(int64 Offset,int Method)
|
||
|
{
|
||
|
if (!Loaded)
|
||
|
return false;
|
||
|
|
||
|
// Normally we process an archive sequentially from beginning to end,
|
||
|
// so we read quick open data sequentially. But some operations like
|
||
|
// archive updating involve several passes. So if we detect that file
|
||
|
// pointer is moved back, we reload quick open data from beginning.
|
||
|
if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
|
||
|
Load(QOHeaderPos);
|
||
|
|
||
|
if (Method==SEEK_SET)
|
||
|
SeekPos=Offset;
|
||
|
if (Method==SEEK_CUR)
|
||
|
SeekPos+=Offset;
|
||
|
UnsyncSeekPos=true;
|
||
|
|
||
|
if (Method==SEEK_END)
|
||
|
{
|
||
|
Arc->File::Seek(Offset,SEEK_END);
|
||
|
SeekPos=Arc->File::Tell();
|
||
|
UnsyncSeekPos=false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool QuickOpen::Tell(int64 *Pos)
|
||
|
{
|
||
|
if (!Loaded)
|
||
|
return false;
|
||
|
*Pos=SeekPos;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint QuickOpen::ReadBuffer()
|
||
|
{
|
||
|
int64 SavePos=Arc->Tell();
|
||
|
Arc->File::Seek(RawDataStart+RawDataPos,SEEK_SET);
|
||
|
size_t SizeToRead=(size_t)Min(RawDataSize-RawDataPos,MaxBufSize-ReadBufSize);
|
||
|
if (Arc->SubHead.Encrypted)
|
||
|
SizeToRead &= ~CRYPT_BLOCK_MASK;
|
||
|
int ReadSize=0;
|
||
|
if (SizeToRead!=0)
|
||
|
{
|
||
|
ReadSize=Arc->File::Read(Buf+ReadBufSize,SizeToRead);
|
||
|
if (ReadSize<=0)
|
||
|
ReadSize=0;
|
||
|
else
|
||
|
{
|
||
|
#ifndef RAR_NOCRYPT
|
||
|
if (Arc->SubHead.Encrypted)
|
||
|
Crypt.DecryptBlock(Buf+ReadBufSize,ReadSize & ~CRYPT_BLOCK_MASK);
|
||
|
#endif
|
||
|
RawDataPos+=ReadSize;
|
||
|
ReadBufSize+=ReadSize;
|
||
|
}
|
||
|
}
|
||
|
Arc->Seek(SavePos,SEEK_SET);
|
||
|
return ReadSize;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Fill RawRead object from buffer.
|
||
|
bool QuickOpen::ReadRaw(RawRead &Raw)
|
||
|
{
|
||
|
if (MaxBufSize-ReadBufPos<0x100) // We are close to end of buffer.
|
||
|
{
|
||
|
// Ensure that we have enough data to read CRC and header size.
|
||
|
size_t DataLeft=ReadBufSize-ReadBufPos;
|
||
|
memcpy(Buf,Buf+ReadBufPos,DataLeft);
|
||
|
ReadBufPos=0;
|
||
|
ReadBufSize=DataLeft;
|
||
|
ReadBuffer();
|
||
|
}
|
||
|
const size_t FirstReadSize=7;
|
||
|
if (ReadBufPos+FirstReadSize>ReadBufSize)
|
||
|
return false;
|
||
|
Raw.Read(Buf+ReadBufPos,FirstReadSize);
|
||
|
ReadBufPos+=FirstReadSize;
|
||
|
|
||
|
uint SavedCRC=Raw.Get4();
|
||
|
uint SizeBytes=Raw.GetVSize(4);
|
||
|
uint64 BlockSize=Raw.GetV();
|
||
|
int SizeToRead=int(BlockSize);
|
||
|
SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
|
||
|
if (SizeToRead<0 || SizeBytes==0 || BlockSize==0)
|
||
|
{
|
||
|
Loaded=false; // Invalid data.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// If rest of block data crosses Buf boundary, read it in loop.
|
||
|
while (SizeToRead>0)
|
||
|
{
|
||
|
size_t DataLeft=ReadBufSize-ReadBufPos;
|
||
|
size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
|
||
|
Raw.Read(Buf+ReadBufPos,CurSizeToRead);
|
||
|
ReadBufPos+=CurSizeToRead;
|
||
|
SizeToRead-=int(CurSizeToRead);
|
||
|
if (SizeToRead>0) // We read the entire buffer and still need more data.
|
||
|
{
|
||
|
ReadBufPos=0;
|
||
|
ReadBufSize=0;
|
||
|
if (ReadBuffer()==0)
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return SavedCRC==Raw.GetCRC50();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Read next cached header.
|
||
|
bool QuickOpen::ReadNext()
|
||
|
{
|
||
|
RawRead Raw(NULL);
|
||
|
if (!ReadRaw(Raw)) // Read internal quick open header preceding stored block.
|
||
|
return false;
|
||
|
uint Flags=(uint)Raw.GetV();
|
||
|
uint64 Offset=Raw.GetV();
|
||
|
size_t HeaderSize=(size_t)Raw.GetV();
|
||
|
if (HeaderSize>MAX_HEADER_SIZE_RAR5)
|
||
|
return false;
|
||
|
LastReadHeader.Alloc(HeaderSize);
|
||
|
Raw.GetB(&LastReadHeader[0],HeaderSize);
|
||
|
// Calculate the absolute position as offset from quick open service header.
|
||
|
LastReadHeaderPos=QOHeaderPos-Offset;
|
||
|
return true;
|
||
|
}
|