bsnes/snesreader/unrar/arcread.cpp

315 lines
7.5 KiB
C++
Executable File

#include "rar.hpp"
#include "unrar.h"
#include "unicode.hpp"
#include "encname.hpp"
// arcread.cpp
unrar_err_t Archive::ReadHeader()
{
CurBlockPos=Tell();
#ifndef SFX_MODULE
if (OldFormat)
{
ReadOldHeader();
if ( Raw.Size() == 0 )
return unrar_err_arc_eof; // right at end of file
if ( Raw.PaddedSize() > 0 ) // added check
return unrar_err_corrupt; // missing data
return unrar_ok;
}
#endif
Raw.Reset();
// (removed decryption)
Raw.Read(SIZEOF_SHORTBLOCKHEAD);
if (Raw.Size()==0)
{
return unrar_err_arc_eof; // right at end of file
}
Raw.Get(ShortBlock.HeadCRC);
byte HeadType;
Raw.Get(HeadType);
ShortBlock.HeadType=(HEADER_TYPE)HeadType;
Raw.Get(ShortBlock.Flags);
Raw.Get(ShortBlock.HeadSize);
if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
{
return unrar_err_corrupt; // invalid HeadSize
}
// catches unknown block types and reading something that isn't even a header
check( MARK_HEAD <= ShortBlock.HeadType && ShortBlock.HeadType <= ENDARC_HEAD );
if (ShortBlock.HeadType==COMM_HEAD)
Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
else
if (ShortBlock.HeadType==MAIN_HEAD && (ShortBlock.Flags & MHD_COMMENT)!=0)
Raw.Read(SIZEOF_NEWMHD-SIZEOF_SHORTBLOCKHEAD);
else
Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
if ( Raw.PaddedSize() > 0 ) // fewer than requested bytes read above?
return unrar_err_corrupt; // missing data
NextBlockPos=CurBlockPos+ShortBlock.HeadSize;
switch(ShortBlock.HeadType)
{
case MAIN_HEAD:
*(BaseBlock *)&NewMhd=ShortBlock;
Raw.Get(NewMhd.HighPosAV);
Raw.Get(NewMhd.PosAV);
check( Raw.ReadPos == Raw.DataSize ); // we should have read all fields
break;
case FILE_HEAD:
case NEWSUB_HEAD:
{
FileHeader *hd=ShortBlock.HeadType==FILE_HEAD ? &NewLhd:&SubHead;
*(BaseBlock *)hd=ShortBlock;
Raw.Get(hd->PackSize);
Raw.Get(hd->UnpSize);
Raw.Get(hd->HostOS);
Raw.Get(hd->FileCRC);
Raw.Get(hd->FileTime);
Raw.Get(hd->UnpVer);
Raw.Get(hd->Method);
Raw.Get(hd->NameSize);
Raw.Get(hd->FileAttr);
if (hd->Flags & LHD_LARGE)
{
Raw.Get(hd->HighPackSize);
Raw.Get(hd->HighUnpSize);
}
else
{
hd->HighPackSize=hd->HighUnpSize=0;
if (hd->UnpSize==0xffffffff)
{
// TODO: what the heck is this for anyway?
hd->UnpSize=0;
hd->HighUnpSize=0x7fffffff;
}
}
hd->FullPackSize=int32to64(hd->HighPackSize,hd->PackSize);
hd->FullUnpSize=int32to64(hd->HighUnpSize,hd->UnpSize);
if ( int32to64( 1, 0 ) == 0 && (hd->HighPackSize || hd->HighUnpSize) )
return unrar_err_huge;
char (&FileName) [sizeof hd->FileName] = hd->FileName; // eliminated local buffer
int NameSize=Min(hd->NameSize,sizeof(FileName)-1);
Raw.Get((byte *)FileName,NameSize);
FileName[NameSize]=0;
if (hd->HeadType==NEWSUB_HEAD)
{
// have to adjust this, even through we're ignoring this block
NextBlockPos+=hd->FullPackSize;
break;
}
else
if (hd->HeadType==FILE_HEAD)
{
if (hd->Flags & LHD_UNICODE)
{
EncodeFileName NameCoder;
int Length=strlen(FileName);
if (Length==hd->NameSize)
{
UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1);
WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1);
ExtToInt(hd->FileName,hd->FileName);
}
else
{
Length++;
NameCoder.Decode(FileName,(byte *)FileName+Length,
hd->NameSize-Length,hd->FileNameW,
sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0]));
}
if (*hd->FileNameW==0)
hd->Flags &= ~LHD_UNICODE;
}
else
*hd->FileNameW=0;
ConvertUnknownHeader();
}
if (hd->Flags & LHD_SALT)
Raw.Get(hd->Salt,SALT_SIZE);
hd->mtime.SetDos(hd->FileTime);
if (hd->Flags & LHD_EXTTIME)
{
ushort Flags;
Raw.Get(Flags);
// Ignore additional time information
for (int I=0;I<4;I++)
{
uint rmode=Flags>>(3-I)*4;
if ((rmode & 8)==0)
continue;
if (I!=0)
{
uint DosTime;
Raw.Get(DosTime);
}
// skip time info
int count=rmode&3;
for (int J=0;J<count;J++)
{
byte CurByte;
Raw.Get(CurByte);
}
}
}
NextBlockPos+=hd->FullPackSize;
bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0;
HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff;
if (hd->HeadCRC!=HeaderCRC)
return unrar_err_corrupt;
check( CRCProcessedOnly == false ); // I need to test on archives where this doesn't hold
check( Raw.ReadPos == Raw.DataSize ); // we should have read all fields
}
break;
#ifndef SFX_MODULE
// Handle these block types just so we can adjust NextBlockPos properly
case PROTECT_HEAD:
Raw.Get(ProtectHead.DataSize);
NextBlockPos+=ProtectHead.DataSize;
break;
case SUB_HEAD:
Raw.Get(SubBlockHead.DataSize);
NextBlockPos+=SubBlockHead.DataSize;
break;
#endif
default:
if (ShortBlock.Flags & LONG_BLOCK)
{
uint DataSize;
Raw.Get(DataSize);
NextBlockPos+=DataSize;
}
break;
}
HeaderCRC=~Raw.GetCRC(false)&0xffff;
CurHeaderType=ShortBlock.HeadType;
// (removed decryption)
if (NextBlockPos<CurBlockPos+Raw.Size())
return unrar_err_corrupt; // next block isn't past end of current block's header
// If pos went negative, then unrar_pos_t is only 32 bits and it overflowed
if ( NextBlockPos < 0 )
return unrar_err_huge;
return unrar_ok;
}
// Rar.Read()s are checked by caller of ReadOldHeader() (see above)
#ifndef SFX_MODULE
int Archive::ReadOldHeader()
{
Raw.Reset();
if (CurBlockPos<=SFXSize)
{
Raw.Read(SIZEOF_OLDMHD);
Raw.Get(OldMhd.Mark,4);
Raw.Get(OldMhd.HeadSize);
Raw.Get(OldMhd.Flags);
NextBlockPos=CurBlockPos+OldMhd.HeadSize;
CurHeaderType=MAIN_HEAD;
}
else
{
OldFileHeader OldLhd;
Raw.Read(SIZEOF_OLDLHD);
NewLhd.HeadType=FILE_HEAD;
Raw.Get(NewLhd.PackSize);
Raw.Get(NewLhd.UnpSize);
Raw.Get(OldLhd.FileCRC);
Raw.Get(NewLhd.HeadSize);
Raw.Get(NewLhd.FileTime);
Raw.Get(OldLhd.FileAttr);
Raw.Get(OldLhd.Flags);
Raw.Get(OldLhd.UnpVer);
Raw.Get(OldLhd.NameSize);
Raw.Get(OldLhd.Method);
NewLhd.Flags=OldLhd.Flags|LONG_BLOCK;
NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10;
NewLhd.Method=OldLhd.Method+0x30;
NewLhd.NameSize=OldLhd.NameSize;
NewLhd.FileAttr=OldLhd.FileAttr;
NewLhd.FileCRC=OldLhd.FileCRC;
NewLhd.FullPackSize=NewLhd.PackSize;
NewLhd.FullUnpSize=NewLhd.UnpSize;
NewLhd.mtime.SetDos(NewLhd.FileTime);
// (removed other times)
Raw.Read(OldLhd.NameSize);
Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize);
NewLhd.FileName[OldLhd.NameSize]=0;
// (removed name case conversion)
*NewLhd.FileNameW=0;
if (Raw.Size()!=0)
NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize;
CurHeaderType=FILE_HEAD;
}
return(NextBlockPos>CurBlockPos ? Raw.Size():0);
}
#endif
// (removed name case and attribute conversion)
bool Archive::IsArcDir()
{
return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY);
}
bool Archive::IsArcLabel()
{
return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8));
}
// TODO: use '\\' on Windows?
char const CPATHDIVIDER = '/';
#define charnext(s) ((s)+1)
void Archive::ConvertUnknownHeader()
{
if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10))
NewLhd.Flags|=LHD_DIRECTORY;
if (NewLhd.HostOS>=HOST_MAX)
{
if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
NewLhd.FileAttr=0x10;
else
NewLhd.FileAttr=0x20;
}
{
for (char *s=NewLhd.FileName;*s!=0;s=charnext(s))
{
if (*s=='/' || *s=='\\')
*s=CPATHDIVIDER;
}
}
// (removed Apple Unicode handling)
for (wchar *s=NewLhd.FileNameW;*s!=0;s++)
{
if (*s=='/' || *s=='\\')
*s=CPATHDIVIDER;
}
}