Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
338
Src/external_dependencies/openmpt-trunk/include/unrar/archive.cpp
vendored
Normal file
338
Src/external_dependencies/openmpt-trunk/include/unrar/archive.cpp
vendored
Normal file
|
@ -0,0 +1,338 @@
|
|||
#include "rar.hpp"
|
||||
|
||||
#include "arccmt.cpp"
|
||||
|
||||
|
||||
Archive::Archive(RAROptions *InitCmd)
|
||||
{
|
||||
Cmd=NULL; // Just in case we'll have an exception in 'new' below.
|
||||
|
||||
DummyCmd=(InitCmd==NULL);
|
||||
Cmd=DummyCmd ? (new RAROptions):InitCmd;
|
||||
|
||||
OpenShared=Cmd->OpenShared;
|
||||
Format=RARFMT15;
|
||||
Solid=false;
|
||||
Volume=false;
|
||||
MainComment=false;
|
||||
Locked=false;
|
||||
Signed=false;
|
||||
FirstVolume=false;
|
||||
NewNumbering=false;
|
||||
SFXSize=0;
|
||||
LatestTime.Reset();
|
||||
Protected=false;
|
||||
Encrypted=false;
|
||||
FailedHeaderDecryption=false;
|
||||
BrokenHeader=false;
|
||||
LastReadBlock=0;
|
||||
|
||||
CurBlockPos=0;
|
||||
NextBlockPos=0;
|
||||
|
||||
|
||||
memset(&MainHead,0,sizeof(MainHead));
|
||||
memset(&CryptHead,0,sizeof(CryptHead));
|
||||
memset(&EndArcHead,0,sizeof(EndArcHead));
|
||||
|
||||
VolNumber=0;
|
||||
VolWrite=0;
|
||||
AddingFilesSize=0;
|
||||
AddingHeadersSize=0;
|
||||
*FirstVolumeName=0;
|
||||
|
||||
Splitting=false;
|
||||
NewArchive=false;
|
||||
|
||||
SilentOpen=false;
|
||||
|
||||
#ifdef USE_QOPEN
|
||||
ProhibitQOpen=false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
Archive::~Archive()
|
||||
{
|
||||
if (DummyCmd)
|
||||
delete Cmd;
|
||||
}
|
||||
|
||||
|
||||
void Archive::CheckArc(bool EnableBroken)
|
||||
{
|
||||
if (!IsArchive(EnableBroken))
|
||||
{
|
||||
// If FailedHeaderDecryption is set, we already reported that archive
|
||||
// password is incorrect.
|
||||
if (!FailedHeaderDecryption)
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void Archive::CheckOpen(const wchar *Name)
|
||||
{
|
||||
TOpen(Name);
|
||||
CheckArc(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool Archive::WCheckOpen(const wchar *Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
return false;
|
||||
if (!IsArchive(false))
|
||||
{
|
||||
uiMsg(UIERROR_BADARCHIVE,FileName);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
|
||||
{
|
||||
RARFORMAT Type=RARFMT_NONE;
|
||||
if (Size>=1 && D[0]==0x52)
|
||||
#ifndef SFX_MODULE
|
||||
if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
|
||||
Type=RARFMT14;
|
||||
else
|
||||
#endif
|
||||
if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
|
||||
{
|
||||
// We check the last signature byte, so we can return a sensible
|
||||
// warning in case we'll want to change the archive format
|
||||
// sometimes in the future.
|
||||
if (D[6]==0)
|
||||
Type=RARFMT15;
|
||||
else
|
||||
if (D[6]==1)
|
||||
Type=RARFMT50;
|
||||
else
|
||||
if (D[6]>1 && D[6]<5)
|
||||
Type=RARFMT_FUTURE;
|
||||
}
|
||||
return Type;
|
||||
}
|
||||
|
||||
|
||||
bool Archive::IsArchive(bool EnableBroken)
|
||||
{
|
||||
Encrypted=false;
|
||||
BrokenHeader=false; // Might be left from previous volume.
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
if (IsDevice())
|
||||
{
|
||||
uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
|
||||
return false;
|
||||
SFXSize=0;
|
||||
|
||||
RARFORMAT Type;
|
||||
if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14)
|
||||
Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<char> Buffer(MAXSFXSIZE);
|
||||
long CurPos=(long)Tell();
|
||||
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
|
||||
for (int I=0;I<ReadSize;I++)
|
||||
if (Buffer[I]==0x52 && (Type=IsSignature((byte *)&Buffer[I],ReadSize-I))!=RARFMT_NONE)
|
||||
{
|
||||
Format=Type;
|
||||
if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
|
||||
{
|
||||
char *D=&Buffer[28-CurPos];
|
||||
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
|
||||
continue;
|
||||
}
|
||||
SFXSize=CurPos+I;
|
||||
Seek(SFXSize,SEEK_SET);
|
||||
if (Format==RARFMT15 || Format==RARFMT50)
|
||||
Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
|
||||
break;
|
||||
}
|
||||
if (SFXSize==0)
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT_FUTURE)
|
||||
{
|
||||
uiMsg(UIERROR_NEWRARFORMAT,FileName);
|
||||
return false;
|
||||
}
|
||||
if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
|
||||
{
|
||||
if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
|
||||
return false;
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD5;
|
||||
}
|
||||
else
|
||||
MarkHead.HeadSize=SIZEOF_MARKHEAD3;
|
||||
|
||||
#ifdef RARDLL
|
||||
// If callback function is not set, we cannot get the password,
|
||||
// so we skip the initial header processing for encrypted header archive.
|
||||
// It leads to skipped archive comment, but the rest of archive data
|
||||
// is processed correctly.
|
||||
if (Cmd->Callback==NULL)
|
||||
SilentOpen=true;
|
||||
#endif
|
||||
|
||||
bool HeadersLeft; // Any headers left to read.
|
||||
bool StartFound=false; // Main or encryption headers found.
|
||||
// Skip the archive encryption header if any and read the main header.
|
||||
while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
|
||||
{
|
||||
SeekToNext();
|
||||
|
||||
HEADER_TYPE Type=GetHeaderType();
|
||||
// In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
|
||||
// avoid the password prompt.
|
||||
StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
|
||||
if (StartFound)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// We should not do it for EnableBroken or we'll get 'not RAR archive'
|
||||
// messages when extracting encrypted archives with wrong password.
|
||||
if (FailedHeaderDecryption && !EnableBroken)
|
||||
return false;
|
||||
|
||||
if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
|
||||
{
|
||||
if (!FailedHeaderDecryption) // If not reported a wrong password already.
|
||||
uiMsg(UIERROR_MHEADERBROKEN,FileName);
|
||||
if (!EnableBroken)
|
||||
return false;
|
||||
}
|
||||
|
||||
MainComment=MainHead.CommentInHeader;
|
||||
|
||||
// If we process non-encrypted archive or can request a password,
|
||||
// we set 'first volume' flag based on file attributes below.
|
||||
// It is necessary for RAR 2.x archives, which did not have 'first volume'
|
||||
// flag in main header. Also for all RAR formats we need to scan until
|
||||
// first file header to set "comment" flag when reading service header.
|
||||
// Unless we are in silent mode, we need to know about presence of comment
|
||||
// immediately after IsArchive call.
|
||||
if (HeadersLeft && (!SilentOpen || !Encrypted) && IsSeekable())
|
||||
{
|
||||
int64 SavePos=Tell();
|
||||
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
|
||||
HEADER_TYPE SaveCurHeaderType=CurHeaderType;
|
||||
|
||||
while (ReadHeader()!=0)
|
||||
{
|
||||
HEADER_TYPE HeaderType=GetHeaderType();
|
||||
if (HeaderType==HEAD_SERVICE)
|
||||
{
|
||||
// If we have a split service headers, it surely indicates non-first
|
||||
// volume. But not split service header does not guarantee the first
|
||||
// volume, because we can have split file after non-split archive
|
||||
// comment. So we do not quit from loop here.
|
||||
FirstVolume=Volume && !SubHead.SplitBefore;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_FILE)
|
||||
{
|
||||
FirstVolume=Volume && !FileHead.SplitBefore;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
|
||||
break;
|
||||
SeekToNext();
|
||||
}
|
||||
CurBlockPos=SaveCurBlockPos;
|
||||
NextBlockPos=SaveNextBlockPos;
|
||||
CurHeaderType=SaveCurHeaderType;
|
||||
Seek(SavePos,SEEK_SET);
|
||||
}
|
||||
if (!Volume || FirstVolume)
|
||||
wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Archive::SeekToNext()
|
||||
{
|
||||
Seek(NextBlockPos,SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Calculate the block size including encryption fields and padding if any.
|
||||
uint Archive::FullHeaderSize(size_t Size)
|
||||
{
|
||||
if (Encrypted)
|
||||
{
|
||||
Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
|
||||
if (Format == RARFMT50)
|
||||
Size += SIZE_INITV;
|
||||
else
|
||||
Size += SIZE_SALT30;
|
||||
}
|
||||
return uint(Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef USE_QOPEN
|
||||
bool Archive::Open(const wchar *Name,uint Mode)
|
||||
{
|
||||
// Important if we reuse Archive object and it has virtual QOpen
|
||||
// file position not matching real. For example, for 'l -v volname'.
|
||||
QOpen.Unload();
|
||||
|
||||
return File::Open(Name,Mode);
|
||||
}
|
||||
|
||||
|
||||
int Archive::Read(void *Data,size_t Size)
|
||||
{
|
||||
size_t Result;
|
||||
if (QOpen.Read(Data,Size,Result))
|
||||
return (int)Result;
|
||||
return File::Read(Data,Size);
|
||||
}
|
||||
|
||||
|
||||
void Archive::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!QOpen.Seek(Offset,Method))
|
||||
File::Seek(Offset,Method);
|
||||
}
|
||||
|
||||
|
||||
int64 Archive::Tell()
|
||||
{
|
||||
int64 QPos;
|
||||
if (QOpen.Tell(&QPos))
|
||||
return QPos;
|
||||
return File::Tell();
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue