#include "rar.hpp"

RawRead::RawRead()
{
  RawRead::SrcFile=NULL;
  Reset();
}


RawRead::RawRead(File *SrcFile)
{
  RawRead::SrcFile=SrcFile;
  Reset();
}


void RawRead::Reset()
{
  Data.SoftReset();
  ReadPos=0;
  DataSize=0;
  Crypt=NULL;
}


size_t RawRead::Read(size_t Size)
{
  size_t ReadSize=0;
#if !defined(RAR_NOCRYPT)
  if (Crypt!=NULL)
  {
    // Full size of buffer with already read data including data read 
    // for encryption block alignment.
    size_t FullSize=Data.Size();

    // Data read for alignment and not processed yet.
    size_t DataLeft=FullSize-DataSize;

    if (Size>DataLeft) // Need to read more than we already have.
    {
      size_t SizeToRead=Size-DataLeft;
      size_t AlignedReadSize=SizeToRead+((~SizeToRead+1) & CRYPT_BLOCK_MASK);
      Data.Add(AlignedReadSize);
      ReadSize=SrcFile->Read(&Data[FullSize],AlignedReadSize);
      Crypt->DecryptBlock(&Data[FullSize],AlignedReadSize);
      DataSize+=ReadSize==0 ? 0:Size;
    }
    else // Use buffered data, no real read.
    {
      ReadSize=Size;
      DataSize+=Size;
    }
  }
  else
#endif
    if (Size!=0)
    {
      Data.Add(Size);
      ReadSize=SrcFile->Read(&Data[DataSize],Size);
      DataSize+=ReadSize;
    }
  return ReadSize;
}


void RawRead::Read(byte *SrcData,size_t Size)
{
  if (Size!=0)
  {
    Data.Add(Size);
    memcpy(&Data[DataSize],SrcData,Size);
    DataSize+=Size;
  }
}


byte RawRead::Get1()
{
  return ReadPos<DataSize ? Data[ReadPos++]:0;
}


ushort RawRead::Get2()
{
  if (ReadPos+1<DataSize)
  {
    ushort Result=Data[ReadPos]+(Data[ReadPos+1]<<8);
    ReadPos+=2;
    return Result;
  }
  return 0;
}


uint RawRead::Get4()
{
  if (ReadPos+3<DataSize)
  {
    uint Result=Data[ReadPos]+(Data[ReadPos+1]<<8)+(Data[ReadPos+2]<<16)+
                (Data[ReadPos+3]<<24);
    ReadPos+=4;
    return Result;
  }
  return 0;
}


uint64 RawRead::Get8()
{
  uint Low=Get4(),High=Get4();
  return INT32TO64(High,Low);
}


uint64 RawRead::GetV()
{
  uint64 Result=0;
  // Need to check Shift<64, because for shift greater than or equal to
  // the width of the promoted left operand, the behavior is undefined.
  for (uint Shift=0;ReadPos<DataSize && Shift<64;Shift+=7)
  {
    byte CurByte=Data[ReadPos++];
    Result+=uint64(CurByte & 0x7f)<<Shift;
    if ((CurByte & 0x80)==0)
      return Result; // Decoded successfully.
  }
  return 0; // Out of buffer border.
}


// Return a number of bytes in current variable length integer.
uint RawRead::GetVSize(size_t Pos)
{
  for (size_t CurPos=Pos;CurPos<DataSize;CurPos++)
    if ((Data[CurPos] & 0x80)==0)
      return int(CurPos-Pos+1);
  return 0; // Buffer overflow.
}


size_t RawRead::GetB(void *Field,size_t Size)
{
  byte *F=(byte *)Field;
  size_t CopySize=Min(DataSize-ReadPos,Size);
  if (CopySize>0)
    memcpy(F,&Data[ReadPos],CopySize);
  if (Size>CopySize)
    memset(F+CopySize,0,Size-CopySize);
  ReadPos+=CopySize;
  return CopySize;
}


void RawRead::GetW(wchar *Field,size_t Size)
{
  if (ReadPos+2*Size-1<DataSize)
  {
    RawToWide(&Data[ReadPos],Field,Size);
    ReadPos+=sizeof(wchar)*Size;
  }
  else
    memset(Field,0,sizeof(wchar)*Size);
}


uint RawRead::GetCRC15(bool ProcessedOnly) // RAR 1.5 block CRC.
{
  if (DataSize<=2)
    return 0;
  uint HeaderCRC=CRC32(0xffffffff,&Data[2],(ProcessedOnly ? ReadPos:DataSize)-2);
  return ~HeaderCRC & 0xffff;
}


uint RawRead::GetCRC50() // RAR 5.0 block CRC.
{
  if (DataSize<=4)
    return 0xffffffff;
  return CRC32(0xffffffff,&Data[4],DataSize-4) ^ 0xffffffff;
}


// Read vint from arbitrary byte array.
uint64 RawGetV(const byte *Data,uint &ReadPos,uint DataSize,bool &Overflow)
{
  Overflow=false;
  uint64 Result=0;
  for (uint Shift=0;ReadPos<DataSize;Shift+=7)
  {
    byte CurByte=Data[ReadPos++];
    Result+=uint64(CurByte & 0x7f)<<Shift;
    if ((CurByte & 0x80)==0)
      return Result; // Decoded successfully.
  }
  Overflow=true;
  return 0; // Out of buffer border.
}