libclamunrar/unpack20.cpp
d39cb658
 #include "rar.hpp"
 
 void Unpack::CopyString20(uint Length,uint Distance)
 {
   LastDist=OldDist[OldDistPtr++]=Distance;
   OldDistPtr = OldDistPtr & 3; // Needed if RAR 1.5 file is called after RAR 2.0.
   LastLength=Length;
   DestUnpSize-=Length;
   CopyString(Length,Distance);
 }
 
 
 void Unpack::Unpack20(bool Solid)
 {
   static unsigned char LDecode[]={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224};
   static unsigned char LBits[]=  {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5};
   static uint DDecode[]={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040};
   static unsigned char DBits[]=  {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5,  6,  6,  7,  7,  8,  8,   9,   9,  10,  10,  11,  11,  12,   12,   13,   13,    14,    14,   15,   15,    16,    16,    16,    16,    16,    16,    16,    16,    16,    16,    16,    16,    16,    16};
   static unsigned char SDDecode[]={0,4,8,16,32,64,128,192};
   static unsigned char SDBits[]=  {2,2,3, 4, 5, 6,  6,  6};
   uint Bits;
 
   if (Suspended)
     UnpPtr=WrPtr;
   else
   {
     UnpInitData(Solid);
     if (!UnpReadBuf())
       return;
     if ((!Solid || !TablesRead2) && !ReadTables20())
       return;
     --DestUnpSize;
   }
 
   while (DestUnpSize>=0)
   {
     UnpPtr&=MaxWinMask;
 
     if (Inp.InAddr>ReadTop-30)
       if (!UnpReadBuf())
         break;
     if (((WrPtr-UnpPtr) & MaxWinMask)<270 && WrPtr!=UnpPtr)
     {
       UnpWriteBuf20();
       if (Suspended)
         return;
     }
     if (UnpAudioBlock)
     {
       uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]);
 
       if (AudioNumber==256)
       {
         if (!ReadTables20())
           break;
         continue;
       }
       Window[UnpPtr++]=DecodeAudio((int)AudioNumber);
       if (++UnpCurChannel==UnpChannels)
         UnpCurChannel=0;
       --DestUnpSize;
       continue;
     }
 
     uint Number=DecodeNumber(Inp,&BlockTables.LD);
     if (Number<256)
     {
       Window[UnpPtr++]=(byte)Number;
       --DestUnpSize;
       continue;
     }
     if (Number>269)
     {
       uint Length=LDecode[Number-=270]+3;
       if ((Bits=LBits[Number])>0)
       {
         Length+=Inp.getbits()>>(16-Bits);
         Inp.addbits(Bits);
       }
 
       uint DistNumber=DecodeNumber(Inp,&BlockTables.DD);
       uint Distance=DDecode[DistNumber]+1;
       if ((Bits=DBits[DistNumber])>0)
       {
         Distance+=Inp.getbits()>>(16-Bits);
         Inp.addbits(Bits);
       }
 
       if (Distance>=0x2000)
       {
         Length++;
         if (Distance>=0x40000L)
           Length++;
       }
 
       CopyString20(Length,Distance);
       continue;
     }
     if (Number==269)
     {
       if (!ReadTables20())
         break;
       continue;
     }
     if (Number==256)
     {
       CopyString20(LastLength,LastDist);
       continue;
     }
     if (Number<261)
     {
       uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3];
       uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD);
       uint Length=LDecode[LengthNumber]+2;
       if ((Bits=LBits[LengthNumber])>0)
       {
         Length+=Inp.getbits()>>(16-Bits);
         Inp.addbits(Bits);
       }
       if (Distance>=0x101)
       {
         Length++;
         if (Distance>=0x2000)
         {
           Length++;
           if (Distance>=0x40000)
             Length++;
         }
       }
       CopyString20(Length,Distance);
       continue;
     }
     if (Number<270)
     {
       uint Distance=SDDecode[Number-=261]+1;
       if ((Bits=SDBits[Number])>0)
       {
         Distance+=Inp.getbits()>>(16-Bits);
         Inp.addbits(Bits);
       }
       CopyString20(2,Distance);
       continue;
    }
   }
   ReadLastTables();
   UnpWriteBuf20();
 }
 
 
 void Unpack::UnpWriteBuf20()
 {
   if (UnpPtr!=WrPtr)
     UnpSomeRead=true;
   if (UnpPtr<WrPtr)
   {
     UnpIO->UnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask);
     UnpIO->UnpWrite(Window,UnpPtr);
     UnpAllBuf=true;
   }
   else
     UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr);
   WrPtr=UnpPtr;
 }
 
 
 bool Unpack::ReadTables20()
 {
   byte BitLength[BC20];
   byte Table[MC20*4];
   if (Inp.InAddr>ReadTop-25)
     if (!UnpReadBuf())
       return false;
   uint BitField=Inp.getbits();
   UnpAudioBlock=(BitField & 0x8000)!=0;
 
   if (!(BitField & 0x4000))
     memset(UnpOldTable20,0,sizeof(UnpOldTable20));
   Inp.addbits(2);
 
   uint TableSize;
   if (UnpAudioBlock)
   {
     UnpChannels=((BitField>>12) & 3)+1;
     if (UnpCurChannel>=UnpChannels)
       UnpCurChannel=0;
     Inp.addbits(2);
     TableSize=MC20*UnpChannels;
   }
   else
     TableSize=NC20+DC20+RC20;
 
   for (uint I=0;I<BC20;I++)
   {
     BitLength[I]=(byte)(Inp.getbits() >> 12);
     Inp.addbits(4);
   }
   MakeDecodeTables(BitLength,&BlockTables.BD,BC20);
   for (uint I=0;I<TableSize;)
   {
     if (Inp.InAddr>ReadTop-5)
       if (!UnpReadBuf())
         return false;
     uint Number=DecodeNumber(Inp,&BlockTables.BD);
     if (Number<16)
     {
       Table[I]=(Number+UnpOldTable20[I]) & 0xf;
       I++;
     }
     else
       if (Number==16)
       {
         uint N=(Inp.getbits() >> 14)+3;
         Inp.addbits(2);
         if (I==0)
           return false; // We cannot have "repeat previous" code at the first position.
         else
           while (N-- > 0 && I<TableSize)
           {
             Table[I]=Table[I-1];
             I++;
           }
       }
       else
       {
         uint N;
         if (Number==17)
         {
           N=(Inp.getbits() >> 13)+3;
           Inp.addbits(3);
         }
         else
         {
           N=(Inp.getbits() >> 9)+11;
           Inp.addbits(7);
         }
         while (N-- > 0 && I<TableSize)
           Table[I++]=0;
       }
   }
   TablesRead2=true;
   if (Inp.InAddr>ReadTop)
     return true;
   if (UnpAudioBlock)
     for (uint I=0;I<UnpChannels;I++)
       MakeDecodeTables(&Table[I*MC20],&MD[I],MC20);
   else
   {
     MakeDecodeTables(&Table[0],&BlockTables.LD,NC20);
     MakeDecodeTables(&Table[NC20],&BlockTables.DD,DC20);
     MakeDecodeTables(&Table[NC20+DC20],&BlockTables.RD,RC20);
   }
   memcpy(UnpOldTable20,Table,TableSize);
   return true;
 }
 
 
 void Unpack::ReadLastTables()
 {
   if (ReadTop>=Inp.InAddr+5)
     if (UnpAudioBlock)
     {
       if (DecodeNumber(Inp,&MD[UnpCurChannel])==256)
         ReadTables20();
     }
     else
       if (DecodeNumber(Inp,&BlockTables.LD)==269)
         ReadTables20();
 }
 
 
 void Unpack::UnpInitData20(int Solid)
 {
   if (!Solid)
   {
     TablesRead2=false;
     UnpAudioBlock=false;
     UnpChannelDelta=0;
     UnpCurChannel=0;
     UnpChannels=1;
 
     memset(AudV,0,sizeof(AudV));
     memset(UnpOldTable20,0,sizeof(UnpOldTable20));
     memset(MD,0,sizeof(MD));
   }
 }
 
 
 byte Unpack::DecodeAudio(int Delta)
 {
   struct AudioVariables *V=&AudV[UnpCurChannel];
   V->ByteCount++;
   V->D4=V->D3;
   V->D3=V->D2;
   V->D2=V->LastDelta-V->D1;
   V->D1=V->LastDelta;
   int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta;
   PCh=(PCh>>3) & 0xFF;
 
   uint Ch=PCh-Delta;
 
   int D=(signed char)Delta;
   // Left shift of negative value is undefined behavior in C++,
   // so we cast it to unsigned to follow the standard.
   D=(uint)D<<3;
 
   V->Dif[0]+=abs(D);
   V->Dif[1]+=abs(D-V->D1);
   V->Dif[2]+=abs(D+V->D1);
   V->Dif[3]+=abs(D-V->D2);
   V->Dif[4]+=abs(D+V->D2);
   V->Dif[5]+=abs(D-V->D3);
   V->Dif[6]+=abs(D+V->D3);
   V->Dif[7]+=abs(D-V->D4);
   V->Dif[8]+=abs(D+V->D4);
   V->Dif[9]+=abs(D-UnpChannelDelta);
   V->Dif[10]+=abs(D+UnpChannelDelta);
 
   UnpChannelDelta=V->LastDelta=(signed char)(Ch-V->LastChar);
   V->LastChar=Ch;
 
   if ((V->ByteCount & 0x1F)==0)
   {
     uint MinDif=V->Dif[0],NumMinDif=0;
     V->Dif[0]=0;
     for (uint I=1;I<ASIZE(V->Dif);I++)
     {
       if (V->Dif[I]<MinDif)
       {
         MinDif=V->Dif[I];
         NumMinDif=I;
       }
       V->Dif[I]=0;
     }
     switch(NumMinDif)
     {
       case 1:
         if (V->K1>=-16)
           V->K1--;
         break;
       case 2:
         if (V->K1<16)
           V->K1++;
         break;
       case 3:
         if (V->K2>=-16)
           V->K2--;
         break;
       case 4:
         if (V->K2<16)
           V->K2++;
         break;
       case 5:
         if (V->K3>=-16)
           V->K3--;
         break;
       case 6:
         if (V->K3<16)
           V->K3++;
         break;
       case 7:
         if (V->K4>=-16)
           V->K4--;
         break;
       case 8:
         if (V->K4<16)
           V->K4++;
         break;
       case 9:
         if (V->K5>=-16)
           V->K5--;
         break;
       case 10:
         if (V->K5<16)
           V->K5++;
         break;
     }
   }
   return (byte)Ch;
 }