/******    *****   ******
 **   **  **   **  **   **      unRAR utility version 1.01
 ******   *******  ******       ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 **   **  **   **  **   **         FREE portable version
 **   **  **   **  **   **         ~~~~~~~~~~~~~~~~~~~~~

         Main code

   YOU CAN CHANGE FOLLOWING CODE IN ORDER TO ACHIEVE
   COMPATIBILITY WITH YOUR OPERATING MEDIA.

   PLEASE SEND ALL NOTES, PATCHES TO andrey@vybor.chel.su
   OR TO Andrey Spasibozhko, 2:5010/23@fidonet.
   VOICE PHONE: +7-3512-130-231
*/

#include "unrar.h"          /* definitions */
#include "unpack.c"         /* unpacking procedures */

void   SplitCommandLine();  /* parses command string */
int    CmpName();           /* checks name for mask */
char*  PointToName();       /* returns pathname */
void   NextVolumeName();    /* gets next volume name */
void   SplitName();         /* splits pathname */
void   ExecuteCommand();    /* executes extr. command */
void   ListArchive();       /* lists archive contents */
int    ExtrFile();          /* extracts single file */
void   Help();              /* prints usage help */
void   ShutDown();          /* stops working */
void   ErrExit();           /* exits with error code */
void   CreatePath();        /* creates path */
int    tread();             /* reads with checking */
void   tclose();            /* closes with checking */
void   MergeArc();          /* to next volume */
void   UnstoreFile();       /* unpacks non-compressed file */
int    IsArchive();         /* checks for RAR archive signature */
void   CheckArc();          /* similar but with exit */
int    strnicomp();         /* compares strings */
char*  strtolwr();          /* convert string to lowercase */
int    ToPercent();         /* calculates percentage */
int    ReadBlock();         /* reads archive block */
int    IsProcessFile();     /* should file be processed */
int    UnpRead();           /* reading while unpacking */
int    UnpWrite();          /* writing while unpacking */
void   InitCRC();           /* initializes CRC table */
UDWORD CRC();               /* calculates CRC */

struct MarkHeader MarkHead;
struct ArchiveHeader Mhd;
struct FileHeader Lhd;

UDWORD CRC32_Table[256],UnpFileCRC;
HPBYTE TmpMemory;

int ArgCount=0;
char ArgNames[16][80],MainCommand;
char CurExtrFile[80]={0},ArcName[80],ArcFileName[80],ExtrPath[80];
int SolidType,UnpVolume,TestMode,ExitCode=0;
FILE *ArcFPtr=NULL,*FileFPtr=NULL,*RdUnpFPtr,*WrUnpFPtr;
long NextBlockPos,UnpPackedSize;


main(Argc,Argv)
int Argc;
char *Argv[];
{
  printf("\n UNRAR 1.01 freeware portable version      (C) 1994-95 Eugene Roshal\n");
  if ((TmpMemory=(HPBYTE)MEMALLOC(UNP_MEMORY))==NULL)
    ErrExit(EMEMORY,MEMORY_ERROR);
  MakeTbl();
  SplitCommandLine(Argc,Argv);
  ExecuteCommand();
  ShutDown(SD_MEMORY);
  exit(ExitCode);
}


int CmpName(Mask,Name)
char *Mask;
char *Name;
{
  while (1)
  {
    if (*Mask=='*')
    {
      while (*Mask!='.' && *Mask!=0)
        Mask++;
      while (*Name!='.' && *Name!=0)
        Name++;
    }
    if (*Mask==0)
      return(*Name==0);
    if (*Name==0 && *Mask=='.')
    {
      Mask++;
      continue;
    }
    if (toupper(*Mask)==toupper(*Name) || *Mask=='?' && *Name!=0)
    {
      Mask++;
      Name++;
    }
    else
      return(0);
  }
}


void ErrExit(ErrCode,Code)
int ErrCode;
int Code;
{
  char ErrMsg[80];
  switch(ErrCode)
  {
    case EEMPTY:
      strcpy(ErrMsg,"");
      break;
    case EWRITE:
      strcpy(ErrMsg,"Write error. Disk full ?");
      break;
    case EREAD:
      strcpy(ErrMsg,"Read error");
      break;
    case EOPEN:
      strcpy(ErrMsg,"File open error");
      break;
    case ECLOSE:
      strcpy(ErrMsg,"File close error");
      break;
    case EMEMORY:
      strcpy(ErrMsg,"Not enough memory");
      break;
    case EARCH:
      strcpy(ErrMsg,"Broken archive");
      break;
  }
  if (ErrCode!=EEMPTY)
    printf("\n Program aborted\n %s",ErrMsg);
  ShutDown(SD_FILES | SD_MEMORY);
  exit(Code);
}


void CreatePath(fpath)
char *fpath;
{
  char *ChPtr;
  ChPtr=fpath;
  while(*ChPtr!=0 && (ChPtr=strchr(ChPtr,PATHDIV))!=NULL)
  {
    *ChPtr=0;
    if (MAKEDIR(fpath)==0)
      printf("\n Creating    %-55s",fpath);
    *ChPtr=PATHDIV;
    ChPtr++;
  }
}


void NextVolumeName()
{
  char *ChPtr;
  ChPtr=strrchr(ArcName,'.');
  if (!isdigit(*(ChPtr+2)) || !isdigit(*(ChPtr+3)))
    strcpy(ChPtr+2,"00");
  else
  {
    ChPtr+=3;
    while ((++(*ChPtr))=='9'+1)
    {
      if (*(ChPtr-1)=='.')
      {
        *ChPtr='A';
        break;
      }
      else
      {
        *ChPtr='0';
        ChPtr--;
      }
    }
  }
}


void MergeArc(ShowFileName)
int ShowFileName;
{
  int Ch;
  tclose(ArcFPtr);
  NextVolumeName();
  while ((ArcFPtr=fopen(ArcName,FOPENREADMODE))==NULL)
  {
    printf("\n Disk with %s required. Continue ? ",ArcName);
    Ch=getchar();
    if (toupper(Ch)=='N')
      ErrExit(EEMPTY,USER_BREAK);
  }
  if (MainCommand=='T')
    printf("\n\n Testing archive %s",ArcName);
  else
    if (MainCommand!='P')
      printf("\n\n Extracting from %s",ArcName);
  CheckArc();
  ReadBlock(FILE_HEAD);
  if (ShowFileName)
    printf("\n     ...     %-55s",ArcFileName);
  UnpVolume=(Lhd.Flags & LHD_SPLIT_AFTER);
  fseek(ArcFPtr,NextBlockPos-Lhd.PackSize,SEEK_SET);
  UnpPackedSize=Lhd.PackSize;
  RdUnpFPtr=ArcFPtr;
}


void UnstoreFile()
{
  int Code;
  while ( 1 )
  {
    if ((Code=UnpRead((UBYTE *)TmpMemory,0x7f00))==-1)
      ErrExit(EWRITE,WRITE_ERROR);
    if (Code==0)
      break;
    if (UnpWrite((UBYTE *)TmpMemory,(UWORD)Code)==-1)
      ErrExit(EWRITE,WRITE_ERROR);
  }
}


int IsArchive()
{
  UBYTE Mark[7],Header[13];
  SolidType=0;
  if (tread(ArcFPtr,Mark,7)!=7)
    return(0);
  if (Mark[0]!=0x52 || Mark[1]!=0x61 || Mark[2]!=0x72 || Mark[3]!=0x21 ||
      Mark[4]!=0x1a || Mark[5]!=0x07 || Mark[6]!=0x00)
    return(0);
  if (tread(ArcFPtr,Header,13) != 13)
    return(0);
  Mhd.HeadCRC  = Header[0]+(UWORD)Header[1]*0x100;
  Mhd.HeadType = Header[2];
  Mhd.Flags    = Header[3]+(UWORD)Header[4]*0x100;
  Mhd.HeadSize = Header[5]+(UWORD)Header[6]*0x100;
  if (!(Mhd.HeadCRC==(UWORD)~CRC(0xFFFFFFFFL,&Header[2],11)))
    printf("\n Archive header broken");
  SolidType=(Mhd.Flags & MHD_SOLID);
  fseek(ArcFPtr,Mhd.HeadSize-13,SEEK_CUR);
  return(1);
}


void CheckArc()
{
  if (!IsArchive())
  {
    printf("\nBad archive %s",ArcName);
    ErrExit(EEMPTY,FATAL_ERROR);
  }
}


int tread(FPtr,buf,len)
FILE *FPtr;
void *buf;
unsigned len;
{
  int Code;
  Code=fread(buf,1,len,FPtr);
  if (Code==-1)
    ErrExit(EREAD,FATAL_ERROR);
  return(Code);
}


void tclose(FPtr)
FILE *FPtr;
{
  if (fclose(FPtr)==EOF)
    ErrExit(ECLOSE,FATAL_ERROR);
}


char* PointToName(Path)
char *Path;
{
  char *ChPtr;
  if ((ChPtr=strrchr(Path,PATHDIV))!=NULL)
    return(ChPtr+1);
  else
    if ((ChPtr=strrchr(Path,':'))!=NULL)
      return(ChPtr+1);
    else
      return(Path);
}


int strnicomp(Str1,Str2,MaxLen)
char *Str1;
char *Str2;
int MaxLen;
{
  if (MaxLen==0)
    return(0);
  while (MaxLen-- > 0)
  {
    if (toupper(*Str1)!=toupper(*Str2))
      return(1);
    if (*Str1==0)
      return(0);
    Str1++;
    Str2++;
  }
  return(0);
}


char* strtolwr(Str)
char *Str;
{
  char *ChPtr;
  for (ChPtr=Str;*ChPtr!=0;ChPtr++)
    *ChPtr=tolower(*ChPtr);
  return(Str);
}


void SplitName(Path,Dir,Name)
char *Path;
char *Dir;
char *Name;
{
  char *ChPtr,*ChPtr1;
  if ((ChPtr=strrchr(Path,':'))!=NULL)
    ChPtr++;
  else
    ChPtr=Path;
  if ((ChPtr1=strrchr(ChPtr,PATHDIV))!=NULL)
  {
    *ChPtr1=0;
    strcpy(Dir,ChPtr);
    *ChPtr1=PATHDIV;
    ChPtr=ChPtr1+1;
  }
  else
    *Dir=0;
  strcpy(Name,ChPtr);
}


int ToPercent(N1,N2)
long N1;
long N2;
{
  if (N1 > 10000)
  {
    N1/=100;
    N2/=100;
  }
  if (N2==0)
    return(0);
  if (N2<N1)
    return(100);
  return((int)(N1*100/N2));
}


void SplitCommandLine(Argc,Argv)
int Argc;
char *Argv[];
{
  int I,Len;

  ArgCount = MainCommand = *ArcName = *ExtrPath = 0;

  if (Argc==2)
  {
    MainCommand='X';
    strcpy(ArcName,Argv[1]);
  }
  else
    for (I=1;I<Argc;I++)
    {
      if (MainCommand==0)
        MainCommand=toupper(Argv[I][0]);
      else
      {
        if (*ArcName==0)
          strncpy(ArcName,Argv[I],80);
        else
        {
          Len=strlen(Argv[I]);
          if (Len>0 && (Argv[I][Len-1]==':' || (Argv[I][Len-1]=='\\' || Argv[I][Len-1]=='/')))
          {
            strcpy(ExtrPath,Argv[I]);
            ExtrPath[Len-1]=PATHDIV;
          }
          else
            strncpy(ArgNames[(ArgCount++) & 0x0f],Argv[I],80);
        }
      }
    }

  if (ArgCount==0 && *ArcName!=0)
    strcpy(ArgNames[(ArgCount++) & 0x0f],"*.*");
  if (strrchr(PointToName(ArcName),'.')==NULL)
    strcat(ArcName,isupper(*ArcName) ? ".RAR":".rar");
  ArgCount &= 0xF;
}


void ExecuteCommand()
{
  switch(MainCommand)
  {
    case 'E':
    case 'X':
    case 'T':
      ExtrFile();
      break;
    case 'V':
    case 'L':
      ListArchive();
      break;
    case 0:
      Help();
      exit(0);
    default:
      Help();
      exit(USER_ERROR);
  }
}

void ShutDown(Mode)
int Mode;
{
  if (Mode & SD_FILES)
  {
    if (ArcFPtr!=NULL)
      fclose(ArcFPtr);
    if (FileFPtr!=NULL)
      fclose(FileFPtr);
  }
  if (Mode & SD_MEMORY)
  {
    if (TmpMemory!=NULL)
      MEMFREE(TmpMemory);
    printf("\n");
  }
}


void Help()
{
  printf("\n Usage:     UNRAR <command> <archive> <files...>\n");
  printf("\n <Commands>\n");
  printf("\n x       Extract files with full path");
  printf("\n e       Extract files to current directory");
  printf("\n t       Test archive files");
  printf("\n v       Verbosely list contents of archive");
  printf("\n l       List contents of archive");
  printf("\n");
}


int ExtrFile()
{
  char DestFileName[80];
  long FileCount=0,TotalFileCount=0,DirCount=0,ErrCount=0;
  int ExtrFile=0,Size,SkipSolid=0,UnpSolid;

  if ((ArcFPtr=fopen(ArcName,FOPENREADMODE))==NULL)
    ErrExit(EOPEN,FATAL_ERROR);

  CheckArc();
  CreateEncTbl(TmpMemory);
  UnpVolume=UnpSolid=0;
  if (MainCommand=='T')
    printf("\n Testing archive %s\n",ArcName);
  else
    printf("\n Extracting from %s\n",ArcName);

  while (1)
  {
    Size=ReadBlock(FILE_HEAD);

    if (Size<=0 && UnpVolume==0)
      break;
    if ((Lhd.Flags & LHD_SPLIT_BEFORE) && SolidType)
    {
      printf("\nSolid archive: first volume required");
      ErrExit(EEMPTY,FATAL_ERROR);
    }
    if (UnpVolume && Size==0)
      MergeArc(0);
    UnpVolume=(Lhd.Flags & LHD_SPLIT_AFTER);
    fseek(ArcFPtr,NextBlockPos-Lhd.PackSize,SEEK_SET);

    TestMode=0;
    ExtrFile=0;
    SkipSolid=0;

    if (IsProcessFile(COMPARE_PATH) && (Lhd.Flags & LHD_SPLIT_BEFORE)==0
        || (SkipSolid=SolidType)!=0)
    {

      strcpy(DestFileName,ExtrPath);
      strcat(DestFileName,(MainCommand!='E') ? ArcFileName : PointToName(ArcFileName));

      ExtrFile=!SkipSolid;

      if (Lhd.UnpVer<15 || Lhd.UnpVer>UNP_VER)
      {
        printf("\n %s: unknown method",ArcFileName);
        ExtrFile=0;
        ErrCount++;
        ExitCode=WARNING;
      }

      if (Lhd.Flags & LHD_PASSWORD)
      {
        printf("\n %s: cannot process encrypted file",ArcFileName);
        if (SolidType)
          ErrExit(EEMPTY,FATAL_ERROR);
        ExtrFile=0;
        ErrCount++;
        ExitCode=WARNING;
      }

      if (Lhd.HostOS==MS_DOS && (Lhd.FileAttr & DOSFA_DIREC))
      {
        if (MainCommand=='E')
          continue;
        if (SkipSolid)
        {
          printf("\n Skipping    %-55s Ok",ArcFileName);
          continue;
        }
        if (MainCommand=='T')
        {
          printf("\n Testing     %-55s Ok",ArcFileName);
          continue;
        }
        CreatePath(DestFileName);
        if (MAKEDIR(DestFileName)==0)
          printf("\n Creating    %-55s",ArcFileName);
        continue;
      }
      else
      {
        if (MainCommand=='T' && ExtrFile)
          TestMode=1;
        if ((MainCommand=='E' || MainCommand=='X') && ExtrFile)
        {
          CreatePath(DestFileName);
          if ((FileFPtr=fopen(DestFileName,FOPENWRITEMODE))==NULL)
          {
            printf("\n Cannot create %s",DestFileName);
            ExitCode=WARNING;
            ExtrFile=0;
          }
        }
      }

      if (!ExtrFile && SolidType)
        SkipSolid=TestMode=ExtrFile=1;
      if (ExtrFile)
      {
        TotalFileCount++;
        if (SkipSolid)
          printf("\n Skipping    %-55s",ArcFileName);
        else
        {
          FileCount++;
          switch(MainCommand)
          {
            case 'T':
              printf("\n Testing     %-55s",ArcFileName);
              break;
            case 'X':
            case 'E':
              printf("\n Extracting  %-55s",DestFileName);
              break;
          }
        }
        strcpy(CurExtrFile,DestFileName);
        UnpFileCRC=0xFFFFFFFFL;
        UnpPackedSize=Lhd.PackSize;
        DestUnpSize=Lhd.UnpSize;
        RdUnpFPtr=ArcFPtr;
        WrUnpFPtr=FileFPtr;
        if (Lhd.Method==0x30)
          UnstoreFile();
        else
          if (unpack(TmpMemory,UnpRead,UnpWrite,UnpSolid)==-1)
            ErrExit(EWRITE,WRITE_ERROR);
        if (TotalFileCount>0 && SolidType)
          UnpSolid=1;
        if (UnpFileCRC==~Lhd.FileCRC)
        {
          if (MainCommand!='P')
            printf(" Ok");
        }
        else
        {
          fseek(ArcFPtr,NextBlockPos,SEEK_SET);
          printf("\n %-15s : CRC failed",ArcFileName);
          ExitCode=CRC_ERROR;
          ErrCount++;
        }
        if (!TestMode)
        {
          SETFILETIME(FileFPtr,(void *)&Lhd.FileTime);
          tclose(FileFPtr);
        }
        TestMode=0;
        *CurExtrFile=0;
      }
    }
    if (!ExtrFile && !SolidType)
      fseek(ArcFPtr,NextBlockPos,SEEK_SET);
  }
  tclose(ArcFPtr);
  if ((FileCount+DirCount)==0)
  {
    printf("\n No files");
    ExitCode=WARNING;
  }
  else
    if (ErrCount==0)
      printf("\n  All OK");
    else
      printf("\n  Total errors: %ld",ErrCount);
  return(0);
}


void ListArchive()
{
  unsigned long TotalPackSize,TotalUnpSize,FileCount;
  int I;
  TotalPackSize=TotalUnpSize=FileCount=0;
  if ((ArcFPtr=fopen(ArcName,FOPENREADMODE))==NULL)
    ErrExit(EOPEN,FATAL_ERROR);
  CheckArc();
  printf("\n ");
  if (SolidType)
    printf("Solid ");
  if (Mhd.Flags & MHD_MULT_VOL)
    printf("%colume ",(SolidType) ? 'v':'V');
  else
    printf("%crchive ",(SolidType) ? 'a':'A');
  printf("%s\n",ArcName);
  if (MainCommand=='V')
    printf("\n Pathname/Comment\n%12.12s","");
  else
    printf("\n Name       ");
  printf("      Size   Packed  Ratio   Date   Time  Attr   CRC-32  Meth Ver\n");
  for (I=0;I<77;I++)
    printf("-");
  while(ReadBlock(FILE_HEAD) > 0)
  {
    if (IsProcessFile(NOT_COMPARE_PATH))
    {
      printf("\n%c",(Lhd.Flags & LHD_PASSWORD) ? '*' : ' ');
      if (MainCommand=='V')
      {
        printf("%-s",ArcFileName);
        printf("\n%12s ","");
      }
      else
        printf("%-12s",PointToName(ArcFileName));

      printf(" %8ld %8ld ",Lhd.UnpSize,Lhd.PackSize);
      if (Lhd.Flags & (LHD_SPLIT_AFTER | LHD_SPLIT_BEFORE))
        printf(" Split");
      else
        printf(" %3d%% ",ToPercent(Lhd.PackSize,Lhd.UnpSize));

      printf(" %02d-%02d-%02d %02d:%02d ",(int)(Lhd.FileTime>>16) & 0x1f,
        (int)(Lhd.FileTime>>21) & 0xf,(int)((Lhd.FileTime>>25)+1980)%100,
        (int)(Lhd.FileTime>>11) & 0x1f,(int)(Lhd.FileTime>>5) & 0x3f);

      if (Lhd.HostOS==MS_DOS)
        printf("%c%c%c%c%c",
          (Lhd.FileAttr & DOSFA_DIREC ) ? 'D' : '.',
          (Lhd.FileAttr & DOSFA_RDONLY) ? 'R' : '.',
          (Lhd.FileAttr & DOSFA_HIDDEN) ? 'H' : '.',
          (Lhd.FileAttr & DOSFA_SYSTEM) ? 'S' : '.',
          (Lhd.FileAttr & DOSFA_ARCH  ) ? 'A' : '.');
      else
        printf("     ");
      printf(" %8.8lX  m%d  %d.%d",Lhd.FileCRC,Lhd.Method-0x30,Lhd.UnpVer/10,Lhd.UnpVer%10);
      if (!(Lhd.Flags & LHD_SPLIT_BEFORE))
      {
        TotalUnpSize+=Lhd.UnpSize;
        FileCount++;
      }
      TotalPackSize+=Lhd.PackSize;
    }
    fseek(ArcFPtr,NextBlockPos,SEEK_SET);
  }
  printf("\n");
  for (I=0;I<77;I++)
    printf("-");
  printf("\n%5ld %16ld %8ld %4d%%\n",FileCount,TotalUnpSize,TotalPackSize,ToPercent(TotalPackSize,TotalUnpSize));
  tclose(ArcFPtr);
}


int IsProcessFile(ComparePath)
int ComparePath;
{
  int NumName,WildCards;
  char ArgName[80],dir1[80],name1[15],dir2[80],name2[15];
  for (NumName=0;NumName<ArgCount;NumName++)
  {
    memcpy((void *)ArgName,(void *)ArgNames[NumName],sizeof(ArgName));
    WildCards=(strchr(ArgName,'?')!=NULL || strchr(ArgName,'*')!=NULL);
    SplitName(ArgName,dir1,name1);
    SplitName(ArcFileName,dir2,name2);
    if (CmpName(name1,name2) && ((ComparePath==NOT_COMPARE_PATH && *dir1==0) ||
          WildCards && strnicomp(dir1,dir2,strlen(dir1))==0 ||
          strnicomp(dir1,dir2,1000)==0))
      return(1);
  }
  return(0);
}

int ReadBlock(BlockType)
int BlockType;
{
  UDWORD HeadCRC;
  UBYTE Header[32];
  int Size,I;
  memset(&Lhd,0,sizeof(Lhd));
  memset(Header,0,sizeof(Header));
  while (1)
  {
    Size=tread(ArcFPtr,Header,32);
    Lhd.HeadCRC  = Header[0] +(UWORD)Header[1]*0x100;
    Lhd.HeadType = Header[2];
    Lhd.Flags    = Header[3] +(UWORD)Header[4]*0x100;
    Lhd.HeadSize = Header[5] +(UWORD)Header[6]*0x100;;
    Lhd.PackSize = Header[7] +(UWORD)Header[8]*0x100+(UDWORD)Header[9]*0x10000L+(UDWORD)Header[10]*0x1000000L;
    Lhd.UnpSize  = Header[11]+(UWORD)Header[12]*0x100+(UDWORD)Header[13]*0x10000L+(UDWORD)Header[14]*0x1000000L;
    Lhd.HostOS   = Header[15];
    Lhd.FileCRC  = Header[16]+(UWORD)Header[17]*0x100+(UDWORD)Header[18]*0x10000L+(UDWORD)Header[19]*0x1000000L;
    Lhd.FileTime = Header[20]+(UWORD)Header[21]*0x100+(UDWORD)Header[22]*0x10000L+(UDWORD)Header[23]*0x1000000L;
    Lhd.UnpVer   = Header[24];
    Lhd.Method   = Header[25];
    Lhd.NameSize = Header[26]+(UWORD)Header[27]*0x100;
    Lhd.FileAttr = Header[28]+(UWORD)Header[29]*0x100+(UDWORD)Header[30]*0x10000L+(UDWORD)Header[31]*0x1000000L;
    if (Size != 0 && (Size<7 || Lhd.HeadSize<7))
      ErrExit(EARCH,FATAL_ERROR);
    NextBlockPos=ftell(ArcFPtr)-Size+Lhd.HeadSize;
    if (Lhd.Flags & LONG_BLOCK)
      NextBlockPos+=Lhd.PackSize;
    if (Size==0 || BlockType==ALL_HEAD || Lhd.HeadType==BlockType)
      break;
    fseek(ArcFPtr,NextBlockPos,SEEK_SET);
  }
  if (Size>0 && BlockType==FILE_HEAD)
  {
    tread(ArcFPtr,ArcFileName,Lhd.NameSize);
    ArcFileName[Lhd.NameSize]=0;
    Size+=Lhd.NameSize;
    HeadCRC=CRC(0xFFFFFFFFL,&Header[2],30);
    if (!(Lhd.HeadCRC==(UWORD)~CRC(HeadCRC,&ArcFileName[0],Lhd.NameSize)))
      printf("\n %s: file header broken\n",ArcFileName);
    for (I=0;ArcFileName[I];I++)
      if (ArcFileName[I]=='\\' || ArcFileName[I]=='/')
        ArcFileName[I]=PATHDIV;
    if (Lhd.HostOS==MS_DOS)
      strtolwr(ArcFileName);
  }
  return(Size);
}


int UnpRead(Addr,Count)
UBYTE *Addr;
UWORD Count;
{
  int RetCode;
  unsigned int ReadSize,TotalRead=0;
  UBYTE *ReadAddr;
  ReadAddr=Addr;
  while (Count > 0)
  {
    ReadSize=(unsigned int)((Count>UnpPackedSize) ? UnpPackedSize : Count);
    if ((RetCode=fread(ReadAddr,1,ReadSize,RdUnpFPtr))!=ReadSize)
      break;
    TotalRead+=RetCode;
    ReadAddr+=RetCode;
    Count-=RetCode;
    UnpPackedSize-=RetCode;
    if (UnpPackedSize == 0 && UnpVolume)
      MergeArc(1);
    else
      break;
  }
  if (RetCode!=-1)
    RetCode=(int)TotalRead;
  return(RetCode);
}


int UnpWrite(Addr,Count)
UBYTE *Addr;
UWORD Count;
{
  int RetCode;
  if (TestMode)
    RetCode=(int)Count;
  else
    if ((RetCode=fwrite(Addr,1,Count,WrUnpFPtr))!=Count)
      RetCode = -1;
  if (RetCode!=-1)
    UnpFileCRC=CRC(UnpFileCRC,Addr,(UWORD)RetCode);
  return(RetCode);
}


UDWORD CRC(StartCRC,Addr,Size)
UDWORD StartCRC;
UBYTE *Addr;
UWORD Size;
{
  UWORD I;
  if (!CRC32_Table[1])
    InitCRC();
  for (I=0;I<Size;I++)
    StartCRC = CRC32_Table[(UBYTE)StartCRC ^ Addr[I]] ^ (StartCRC >> 8);
  return(StartCRC);
}

void InitCRC()
{
  int I, J;
  UDWORD C;
  for (I=0;I<256;I++)
  {
    for (C=I,J=0;J<8;J++)
      C=(C & 1) ? (C>>1)^0xEDB88320L : (C>>1);
    CRC32_Table[I]=C;
  }
}
