view unrarlib.c @ 9076:92014b66ed3d

ability to disable the nonsense expand filter is a must! otherwise it's impossible to render subtitles earlier in the filter chain and then scale them down with a scale filter; huge subs will get rendered again on top!! (think dvd/vobsub where you can't just use smaller font size) if anyone has a better way to handle this, do it! (e.g. make it so that the first expand filter disabled osd for the rest of the filter chain)
author rfelker
date Fri, 24 Jan 2003 01:04:50 +0000
parents fb88ccbc5ccc
children 31126ea5cd36
line wrap: on
line source

/* ***************************************************************************
 **
 **  This file is part of the UniquE RAR File Library.
 **
 **  Copyright (C) 2000-2002 by Christian Scheurer (www.ChristianScheurer.ch)
 **  UNIX port copyright (c) 2000-2002 by Johannes Winkelmann (jw@tks6.net)
 **
 **  The contents of this file are subject to the UniquE RAR File Library
 **  License (the "unrarlib-license.txt"). You may not use this file except
 **  in compliance with the License. You may obtain a copy of the License
 **  at http://www.unrarlib.org/license.html.
 **  Software distributed under the License is distributed on an "AS IS"
 **  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied warranty.
 **
 **  Alternatively, the contents of this file may be used under the terms
 **  of the GNU General Public License Version 2 or later (the "GPL"), in
 **  which case the provisions of the GPL are applicable instead of those
 **  above. If you wish to allow use of your version of this file only
 **  under the terms of the GPL and not to allow others to use your version
 **  of this file under the terms of the UniquE RAR File Library License,
 **  indicate your decision by deleting the provisions above and replace
 **  them with the notice and other provisions required by the GPL. If you
 **  do not delete the provisions above, a recipient may use your version
 **  of this file under the terms of the GPL or the UniquE RAR File Library
 **  License.
 **
 ************************************************************************** */

/* ***************************************************************************
 **
 **                           UniquE RAR File Library
 **                     The free file lib for the demoscene
 **                   multi-OS version (Win32, Linux and SunOS)
 **
 *****************************************************************************
 **
 **   ==> Please configure the program in "unrarlib.h". <==
 **
 **   RAR decompression code:
 **    (C) Eugene Roshal
 **   Modifications to a FileLib:
 **    (C) 2000-2002 Christian Scheurer aka. UniquE/Vantage (cs@unrarlib.org)
 **   Linux port:
 **    (C) 2000-2002 Johannes Winkelmann (jw@tks6.net)
 **
 **  The UniquE RAR File Library gives you the ability to access RAR archives
 **  (any compression method supported in RAR v2.0 including Multimedia
 **  Compression and encryption) directly from your program with ease an by
 **  adding only 12kB (6kB UPX-compressed) additional code to your program.
 **  Both solid and normal (recommended for fast random access to the files!)
 **  archives are supported. This FileLib is made for the Demo scene, so it's
 **  designed for easy use within your demos and intros.
 **  Please read "licence.txt" to learn more about how you may use URARFileLib
 **  in your productions.
 **
 *****************************************************************************
 **
 **  ==> see the "CHANGES" file to see what's new
 **
 ************************************************************************** */

/* -- include files ------------------------------------------------------- */
#include "unrarlib.h"                       /* include global configuration */
/* ------------------------------------------------------------------------ */



/* -- global stuff -------------------------------------------------------- */
#ifdef _WIN_32

#include <windows.h>                        /* WIN32 definitions            */
#include <stdio.h>
#include <string.h>


#define ENABLE_ACCESS

#define HOST_OS     WIN_32

#define FM_NORMAL   0x00
#define FM_RDONLY   0x01
#define FM_HIDDEN   0x02
#define FM_SYSTEM   0x04
#define FM_LABEL    0x08
#define FM_DIREC    0x10
#define FM_ARCH     0x20

#define PATHDIVIDER  "\\"
#define CPATHDIVIDER '\\'
#define MASKALL      "*.*"

#define READBINARY   "rb"
#define READTEXT     "rt"
#define UPDATEBINARY "r+b"
#define CREATEBINARY "w+b"
#define CREATETEXT   "w"
#define APPENDTEXT   "at"

#endif

#ifdef _UNIX

#include <stdio.h>                          /* LINUX/UNIX definitions       */
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define ENABLE_ACCESS

#define HOST_OS     UNIX

#define FM_LABEL    0x0000
#define FM_DIREC    0x4000

#define PATHDIVIDER  "/"
#define CPATHDIVIDER '/'
#define MASKALL      "*.*"

#define READBINARY   "r"
#define READTEXT     "r"
#define UPDATEBINARY "r+"
#define CREATEBINARY "w+"
#define CREATETEXT   "w"
#define APPENDTEXT   "a"


/* emulation of the windows API and data types                              */
/* 20-08-2000 Johannes Winkelmann, jw@tks6.net                              */

typedef long    DWORD;
typedef short   BOOL;
#define TRUE    1
#define FALSE   0


#ifdef _DEBUG_LOG                           /* define macros for debugging  */
#include <unistd.h>
#include <sys/time.h>

DWORD GetTickCount()
{
    struct timeval tv;
    gettimeofday( &tv, 0 );
    return (tv.tv_usec / 1000);
}
#endif

#endif





#ifdef _DEBUG_LOG                           /* define macros for debugging  */

BOOL debug_log_first_start = TRUE;

#define debug_log(a); debug_log_proc(a, __FILE__, __LINE__);
#define debug_init(a); debug_init_proc(a);

void debug_log_proc(char *text, char *sourcefile, int sourceline);
void debug_init_proc(char *file_name);

#else
#define debug_log(a);   /* no debug this time */
#define debug_init(a);  /* no debug this time */
#endif





#define MAXWINSIZE      0x100000
#define MAXWINMASK      (MAXWINSIZE-1)
#define UNP_MEMORY      MAXWINSIZE
#define Min(x,y) (((x)<(y)) ? (x):(y))
#define Max(x,y) (((x)>(y)) ? (x):(y))
#define NM  260

#define SIZEOF_MARKHEAD         7
#define SIZEOF_OLDMHD           7
#define SIZEOF_NEWMHD          13
#define SIZEOF_OLDLHD          21
#define SIZEOF_NEWLHD          32
#define SIZEOF_SHORTBLOCKHEAD   7
#define SIZEOF_LONGBLOCKHEAD   11
#define SIZEOF_COMMHEAD        13
#define SIZEOF_PROTECTHEAD     26


#define PACK_VER       20                   /* version of decompression code*/
#define UNP_VER        20
#define PROTECT_VER    20


enum { M_DENYREAD,M_DENYWRITE,M_DENYNONE,M_DENYALL };
enum { FILE_EMPTY,FILE_ADD,FILE_UPDATE,FILE_COPYOLD,FILE_COPYBLOCK };
enum { SUCCESS,WARNING,FATAL_ERROR,CRC_ERROR,LOCK_ERROR,WRITE_ERROR,
       OPEN_ERROR,USER_ERROR,MEMORY_ERROR,USER_BREAK=255,IMM_ABORT=0x8000 };
enum { EN_LOCK=1,EN_VOL=2 };
enum { SD_MEMORY=1,SD_FILES=2 };
enum { NAMES_DONTCHANGE };
enum { LOG_ARC=1,LOG_FILE=2 };
enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 };
enum { OLD_UNPACK,NEW_UNPACK };


#define MHD_COMMENT        2
#define MHD_LOCK           4
#define MHD_PACK_COMMENT   16
#define MHD_AV             32
#define MHD_PROTECT        64

#define LHD_SPLIT_BEFORE   1
#define LHD_SPLIT_AFTER    2
#define LHD_PASSWORD       4
#define LHD_COMMENT        8
#define LHD_SOLID          16

#define LHD_WINDOWMASK     0x00e0
#define LHD_WINDOW64       0
#define LHD_WINDOW128      32
#define LHD_WINDOW256      64
#define LHD_WINDOW512      96
#define LHD_WINDOW1024     128
#define LHD_DIRECTORY      0x00e0

#define LONG_BLOCK         0x8000
#define READSUBBLOCK       0x8000

enum { ALL_HEAD=0,MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,
       COMM_HEAD=0x75,AV_HEAD=0x76,SUB_HEAD=0x77,PROTECT_HEAD=0x78};
enum { EA_HEAD=0x100 };
enum { MS_DOS=0,OS2=1,WIN_32=2,UNIX=3 };


struct MarkHeader
{
  UBYTE Mark[7];
};


struct NewMainArchiveHeader
{
  UWORD HeadCRC;
  UBYTE HeadType;
  UWORD Flags;
  UWORD HeadSize;
  UWORD Reserved;
  UDWORD Reserved1;
};


struct NewFileHeader
{
  UWORD HeadCRC;
  UBYTE HeadType;
  UWORD Flags;
  UWORD HeadSize;
  UDWORD PackSize;
  UDWORD UnpSize;
  UBYTE HostOS;
  UDWORD FileCRC;
  UDWORD FileTime;
  UBYTE UnpVer;
  UBYTE Method;
  UWORD NameSize;
  UDWORD FileAttr;
};


struct BlockHeader
{
  UWORD HeadCRC;
  UBYTE HeadType;
  UWORD Flags;
  UWORD HeadSize;
  UDWORD DataSize;
};


struct Decode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[2];
};


struct MarkHeader MarkHead;
struct NewMainArchiveHeader NewMhd;
struct NewFileHeader NewLhd;
struct BlockHeader BlockHead;

UBYTE *TempMemory;                          /* temporary unpack-buffer      */
char *CommMemory;


UBYTE *UnpMemory;
char ArgName[NM];                           /* current file in rar archive  */
char ArcFileName[NM];                       /* file to decompress           */

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION  /* mem-to-mem decompression     */
  MemoryFile *MemRARFile;                   /* pointer to RAR file in memory*/
#else
  char ArcName[255];                        /* RAR archive name             */
  FILE *ArcPtr;                             /* input RAR file handler       */
#endif
char Password[255];                         /* password to decrypt files    */

unsigned char *temp_output_buffer;          /* extract files to this pointer*/
unsigned long *temp_output_buffer_offset;   /* size of temp. extract buffer */

BOOL FileFound;                             /* TRUE=use current extracted   */
                                            /* data FALSE=throw data away,  */
                                            /* wrong file                   */
int MainHeadSize;
long CurBlockPos,NextBlockPos;

unsigned long CurUnpRead, CurUnpWrite;
long UnpPackedSize;
long DestUnpSize;

UDWORD HeaderCRC;
int Encryption;

unsigned int UnpWrSize;
unsigned char *UnpWrAddr;
unsigned int UnpPtr,WrPtr;

unsigned char PN1,PN2,PN3;
unsigned short OldKey[4];



/* function header definitions                                              */
int ReadHeader(int BlockType);
BOOL ExtrFile(void);
BOOL ListFile(void);
int tread(void *stream,void *buf,unsigned len);
int tseek(void *stream,long offset,int fromwhere);
BOOL UnstoreFile(void);
int IsArchive(void);
int ReadBlock(int BlockType);
unsigned int UnpRead(unsigned char *Addr,unsigned int Count);
void UnpInitData(void);
void Unpack(unsigned char *UnpAddr);
UBYTE DecodeAudio(int Delta);
static void DecodeNumber(struct Decode *Dec);
void UpdKeys(UBYTE *Buf);
void SetCryptKeys(char *Password);
void SetOldKeys(char *Password);
void DecryptBlock(unsigned char *Buf);
void InitCRC(void);
UDWORD CalcCRC32(UDWORD StartCRC,UBYTE *Addr,UDWORD Size);
void UnpReadBuf(int FirstBuf);
void ReadTables(void);
static void ReadLastTables(void);
static void MakeDecodeTables(unsigned char *LenTab,
                             struct Decode *Dec,
                             int Size);
int stricomp(char *Str1,char *Str2);
/* ------------------------------------------------------------------------ */


/* -- global functions ---------------------------------------------------- */

int urarlib_get(void *output,
                unsigned long *size,
                char *filename,
                void *rarfile,
                char *libpassword)
/* Get a file from a RAR file to the "output" buffer. The UniquE RAR FileLib
 * does everything from allocating memory, decrypting and unpacking the file
 * from the archive. TRUE is returned if the file could be successfully
 * extracted, else a FALSE indicates a failure.
 */
{
  BOOL  retcode = FALSE;

#ifdef _DEBUG_LOG
  int  str_offs;                            /* used for debug-strings       */
  char DebugMsg[500];                       /* used to compose debug msg    */

  if(debug_log_first_start)
  {
    debug_log_first_start=FALSE;            /* only create a new log file   */
    debug_init(_DEBUG_LOG_FILE);            /* on startup                   */
  }

#endif

  InitCRC();                                /* init some vars               */

  strcpy(ArgName, filename);                /* set file(s) to extract       */
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  MemRARFile = rarfile;                     /* set pointer to mem-RAR file  */
#else
  strcpy(ArcName, rarfile);                 /* set RAR file name            */
#endif
  if(libpassword != NULL)
    strcpy(Password, libpassword);          /* init password                */

  temp_output_buffer = NULL;
  temp_output_buffer_offset=size;           /* set size of the temp buffer  */

#ifdef _DEBUG_LOG
  sprintf(DebugMsg, "Extracting >%s< from >%s< (password is >%s<)...",
          filename, (char*)rarfile, libpassword);
  debug_log(DebugMsg);
#endif

  retcode = ExtrFile();                     /* unpack file now!             */

  memset(Password,0,sizeof(Password));      /* clear password               */

#ifndef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  if (ArcPtr!=NULL){
      fclose(ArcPtr);
      ArcPtr = NULL;
  }
#endif

  free(UnpMemory);                          /* free memory                  */
  free(TempMemory);
  free(CommMemory);
  UnpMemory=NULL;
  TempMemory=NULL;
  CommMemory=NULL;


  if(retcode == FALSE)
  {
    free(temp_output_buffer);               /* free memory and return NULL  */
    temp_output_buffer=NULL;
    *(DWORD*)output=0;                      /* pointer on errors            */
    *size=0;
#ifdef _DEBUG_LOG


   /* sorry for this ugly code, but older SunOS gcc compilers don't support */
   /* white spaces within strings                                           */
   str_offs  = sprintf(DebugMsg, "Error - couldn't extract ");
   str_offs += sprintf(DebugMsg + str_offs, ">%s<", filename);
   str_offs += sprintf(DebugMsg + str_offs, " and allocated ");
   str_offs += sprintf(DebugMsg + str_offs, "%u Bytes", (unsigned int)*size);
   str_offs += sprintf(DebugMsg + str_offs, " of unused memory!");

  } else
  {
    sprintf(DebugMsg, "Extracted %u Bytes.", (unsigned int)*size);
  }
  debug_log(DebugMsg);
#else
  }
#endif
  *(DWORD*)output=(DWORD)temp_output_buffer;/* return pointer for unpacked*/
                                            /* data                       */

  return retcode;
}


int urarlib_list(void *rarfile, ArchiveList_struct *list)
{
  ArchiveList_struct *tmp_List = NULL;
  int NoOfFilesInArchive       = 0;         /* number of files in archive   */

#ifdef _DEBUG_LOG
  if(debug_log_first_start)
  {
    debug_log_first_start=FALSE;            /* only create a new log file   */
    debug_init(_DEBUG_LOG_FILE);            /* on startup                   */
  }
#endif

  InitCRC();                                /* init some vars               */

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  MemRARFile         = rarfile;             /* assign pointer to RAR file   */
  MemRARFile->offset = 0;
  if (!IsArchive())
  {
    debug_log("Not a RAR file");
    return NoOfFilesInArchive;              /* error => exit!               */
  }
#else
  /* open and identify archive                                              */
  if ((ArcPtr=fopen(rarfile,READBINARY))!=NULL)
  {
    if (!IsArchive())
    {
      debug_log("Not a RAR file");
      fclose(ArcPtr);
      ArcPtr = NULL;
      return NoOfFilesInArchive;            /* error => exit!               */
    }
  }
  else {
    debug_log("Error opening file.");
    return NoOfFilesInArchive;
  }
#endif

  if ((UnpMemory=malloc(UNP_MEMORY))==NULL)
  {
    debug_log("Can't allocate memory for decompression!");
    return NoOfFilesInArchive;
  }

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  MemRARFile->offset+=NewMhd.HeadSize-MainHeadSize;
#else
  tseek(ArcPtr,NewMhd.HeadSize-MainHeadSize,SEEK_CUR);
#endif
  (*(DWORD*)list) = (DWORD)NULL;            /* init file list               */
  /* do while file is not extracted and there's no error                    */
  while (TRUE)
  {
    if (ReadBlock(FILE_HEAD | READSUBBLOCK) <= 0) /* read name of the next  */
    {                                       /* file within the RAR archive  */
      debug_log("Couldn't read next filename from archive (I/O error).");
      break;                                /* error, file not found in     */
    }                                       /* archive or I/O error         */
    if (BlockHead.HeadType==SUB_HEAD)
    {
      debug_log("Sorry, sub-headers not supported.");
      break;                                /* error => exit                */
    }

    if((void*)(*(DWORD*)list) == NULL)      /* first entry                  */
    {
      tmp_List = malloc(sizeof(ArchiveList_struct));
      tmp_List->next = NULL;

      (*(DWORD*)list) = (DWORD)tmp_List;

    } else                                  /* add entry                    */
    {
      tmp_List->next = malloc(sizeof(ArchiveList_struct));
      tmp_List = (ArchiveList_struct*) tmp_List->next;
      tmp_List->next = NULL;
    }

    tmp_List->item.Name = malloc(NewLhd.NameSize + 1);
    strcpy(tmp_List->item.Name, ArcFileName);
    tmp_List->item.NameSize = NewLhd.NameSize;
    tmp_List->item.PackSize = NewLhd.PackSize;
    tmp_List->item.UnpSize = NewLhd.UnpSize;
    tmp_List->item.HostOS = NewLhd.HostOS;
    tmp_List->item.FileCRC = NewLhd.FileCRC;
    tmp_List->item.FileTime = NewLhd.FileTime;
    tmp_List->item.UnpVer = NewLhd.UnpVer;
    tmp_List->item.Method = NewLhd.Method;
    tmp_List->item.FileAttr = NewLhd.FileAttr;

    NoOfFilesInArchive++;                   /* count files                  */

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
    MemRARFile->offset = NextBlockPos;
#else
    if (ArcPtr!=NULL) tseek(ArcPtr,NextBlockPos,SEEK_SET);
#endif

  };

  /* free memory, clear password and close archive                          */
  memset(Password,0,sizeof(Password));      /* clear password               */
#ifndef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  if (ArcPtr!=NULL){
      fclose(ArcPtr);
      ArcPtr = NULL;
  }
#endif

  free(UnpMemory);                          /* free memory                  */
  free(TempMemory);
  free(CommMemory);
  UnpMemory=NULL;
  TempMemory=NULL;
  CommMemory=NULL;

  return NoOfFilesInArchive;
}



/* urarlib_freelist:
 * (after the suggestion and code of Duy Nguyen, Sean O'Blarney
 * and Johannes Winkelmann who independently wrote a patch)
 * free the memory of a ArchiveList_struct created by urarlib_list.
 *
 *    input: *list          pointer to an ArchiveList_struct
 *    output: -
 */

void urarlib_freelist(ArchiveList_struct *list)
{
    ArchiveList_struct* tmp = list;

    while ( list ) {
        tmp = list->next;
        free( list->item.Name );
        free( list );
        list = tmp;
    }
}


/* ------------------------------------------------------------------------ */

















/****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 *******                    B L O C K   I / O                         *******
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************/



#define GetHeaderByte(N) Header[N]

#define GetHeaderWord(N) (Header[N]+((UWORD)Header[N+1]<<8))

#define GetHeaderDword(N) (Header[N]+((UWORD)Header[N+1]<<8)+\
                          ((UDWORD)Header[N+2]<<16)+\
                          ((UDWORD)Header[N+3]<<24))


int ReadBlock(int BlockType)
{
  struct NewFileHeader SaveFileHead;
  int Size=0,ReadSubBlock=0;
  static int LastBlock;
  memcpy(&SaveFileHead,&NewLhd,sizeof(SaveFileHead));
  if (BlockType & READSUBBLOCK)
    ReadSubBlock=1;
  BlockType &= 0xff;
  {
    while (1)
    {
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
      CurBlockPos=MemRARFile->offset;       /* get offset of mem-file       */
#else
      CurBlockPos=ftell(ArcPtr);
#endif
      Size=ReadHeader(FILE_HEAD);
      if (Size!=0)
      {
        if (NewLhd.HeadSize<SIZEOF_SHORTBLOCKHEAD)
          return(0);
        NextBlockPos=CurBlockPos+NewLhd.HeadSize;
        if (NewLhd.Flags & LONG_BLOCK)
          NextBlockPos+=NewLhd.PackSize;
        if (NextBlockPos<=CurBlockPos)
          return(0);
      }

      if (Size > 0 && BlockType!=SUB_HEAD)
        LastBlock=BlockType;
      if (Size==0 || BlockType==ALL_HEAD || NewLhd.HeadType==BlockType ||
          (NewLhd.HeadType==SUB_HEAD && ReadSubBlock && LastBlock==BlockType))
        break;
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
      MemRARFile->offset = NextBlockPos;
#else
      tseek(ArcPtr, NextBlockPos, SEEK_SET);
#endif
    }
  }

  BlockHead.HeadCRC=NewLhd.HeadCRC;
  BlockHead.HeadType=NewLhd.HeadType;
  BlockHead.Flags=NewLhd.Flags;
  BlockHead.HeadSize=NewLhd.HeadSize;
  BlockHead.DataSize=NewLhd.PackSize;

  if (BlockType!=NewLhd.HeadType) BlockType=ALL_HEAD;

  if((FILE_HEAD == BlockType) && (Size>0))
  {
    NewLhd.NameSize=Min(NewLhd.NameSize,sizeof(ArcFileName)-1);
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
    tread(MemRARFile, ArcFileName, NewLhd.NameSize);
#else
    tread(ArcPtr,ArcFileName,NewLhd.NameSize);
#endif
    ArcFileName[NewLhd.NameSize]=0;
#ifdef _DEBUG_LOG
    if (NewLhd.HeadCRC!=(UWORD)~CalcCRC32(HeaderCRC,(UBYTE*)&ArcFileName[0],
                                          NewLhd.NameSize))
    {
      debug_log("file header broken");
    }
#endif
    Size+=NewLhd.NameSize;
  } else
  {
    memcpy(&NewLhd,&SaveFileHead,sizeof(NewLhd));
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
    MemRARFile->offset = CurBlockPos;
#else
    tseek(ArcPtr,CurBlockPos,SEEK_SET);
#endif
  }


  return(Size);
}


int ReadHeader(int BlockType)
{
  int Size = 0;
  unsigned char Header[64];
  switch(BlockType)
  {
    case MAIN_HEAD:
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
        Size=tread(MemRARFile, Header, SIZEOF_NEWMHD);
#else
        Size=tread(ArcPtr,Header,SIZEOF_NEWMHD);
#endif
        NewMhd.HeadCRC=(unsigned short)GetHeaderWord(0);
        NewMhd.HeadType=GetHeaderByte(2);
        NewMhd.Flags=(unsigned short)GetHeaderWord(3);
        NewMhd.HeadSize=(unsigned short)GetHeaderWord(5);
        NewMhd.Reserved=(unsigned short)GetHeaderWord(7);
        NewMhd.Reserved1=GetHeaderDword(9);
        HeaderCRC=CalcCRC32(0xFFFFFFFFL,&Header[2],SIZEOF_NEWMHD-2);
      break;
    case FILE_HEAD:
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
        Size=tread(MemRARFile, Header, SIZEOF_NEWLHD);
#else
        Size=tread(ArcPtr,Header,SIZEOF_NEWLHD);
#endif
        NewLhd.HeadCRC=(unsigned short)GetHeaderWord(0);
        NewLhd.HeadType=GetHeaderByte(2);
        NewLhd.Flags=(unsigned short)GetHeaderWord(3);
        NewLhd.HeadSize=(unsigned short)GetHeaderWord(5);
        NewLhd.PackSize=GetHeaderDword(7);
        NewLhd.UnpSize=GetHeaderDword(11);
        NewLhd.HostOS=GetHeaderByte(15);
        NewLhd.FileCRC=GetHeaderDword(16);
        NewLhd.FileTime=GetHeaderDword(20);
        NewLhd.UnpVer=GetHeaderByte(24);
        NewLhd.Method=GetHeaderByte(25);
        NewLhd.NameSize=(unsigned short)GetHeaderWord(26);
        NewLhd.FileAttr=GetHeaderDword(28);
        HeaderCRC=CalcCRC32(0xFFFFFFFFL,&Header[2],SIZEOF_NEWLHD-2);
      break;

#ifdef _DEBUG_LOG
  case COMM_HEAD:                           /* log errors in case of debug  */
        debug_log("Comment headers not supported! "\
                  "Please create archives without comments.");
      break;
  case PROTECT_HEAD:
        debug_log("Protected headers not supported!");
      break;
  case ALL_HEAD:
        debug_log("ShortBlockHeader not supported!");
      break;
  default:
        debug_log("Unknown//unsupported !");
#else
  default:                                  /* else do nothing              */
        break;
#endif
  }
  return(Size);
}

/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */

















/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 *******                  E X T R A C T   L O O P                     *******
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


int IsArchive(void)
{
#ifdef _DEBUG_LOG
  int  str_offs;                            /* used for debug-strings       */
  char DebugMsg[500];                       /* used to compose debug msg    */
#endif

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  if (tread(MemRARFile, MarkHead.Mark, SIZEOF_MARKHEAD) != SIZEOF_MARKHEAD)
    return(FALSE);
#else
  if (tread(ArcPtr,MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD)
    return(FALSE);
#endif
  /* Old archive => error                                                   */
  if (MarkHead.Mark[0]==0x52 && MarkHead.Mark[1]==0x45 &&
      MarkHead.Mark[2]==0x7e && MarkHead.Mark[3]==0x5e)
  {
    debug_log("Attention: format as OLD detected! Can't handel archive!");
  }
  else
      /* original RAR v2.0                                                  */
      if ((MarkHead.Mark[0]==0x52 && MarkHead.Mark[1]==0x61 && /* original  */
           MarkHead.Mark[2]==0x72 && MarkHead.Mark[3]==0x21 && /* RAR header*/
           MarkHead.Mark[4]==0x1a && MarkHead.Mark[5]==0x07 &&
           MarkHead.Mark[6]==0x00) ||
     /* "UniquE!" - header                                                  */
          (MarkHead.Mark[0]=='U' && MarkHead.Mark[1]=='n' &&   /* "UniquE!" */
           MarkHead.Mark[2]=='i' && MarkHead.Mark[3]=='q' &&   /* header    */
           MarkHead.Mark[4]=='u' && MarkHead.Mark[5]=='E' &&
           MarkHead.Mark[6]=='!'))

    {
      if (ReadHeader(MAIN_HEAD)!=SIZEOF_NEWMHD)
        return(FALSE);
    } else
    {

#ifdef _DEBUG_LOG
     /* sorry for this ugly code, but older SunOS gcc compilers don't       */
     /* support white spaces within strings                                 */
     str_offs  = sprintf(DebugMsg, "unknown archive type (only plain RAR ");
     str_offs += sprintf(DebugMsg + str_offs, "supported (normal and solid ");
     str_offs += sprintf(DebugMsg + str_offs, "archives), SFX and Volumes ");
     str_offs += sprintf(DebugMsg + str_offs, "are NOT supported!)");

     debug_log(DebugMsg);
#endif

    }


  MainHeadSize=SIZEOF_NEWMHD;

  return(TRUE);
}


BOOL ExtrFile(void)
{
  BOOL ReturnCode=TRUE;
  FileFound=FALSE;                          /* no file found by default     */

#ifdef  _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  MemRARFile->offset = 0;                   /* start reading from offset 0  */
  if (!IsArchive())
  {
    debug_log("Not a RAR file");
    return FALSE;                           /* error => exit!               */
  }

#else
  /* open and identify archive                                              */
  if ((ArcPtr=fopen(ArcName,READBINARY))!=NULL)
  {
    if (!IsArchive())
    {
      debug_log("Not a RAR file");
      fclose(ArcPtr);
      ArcPtr = NULL;
      return FALSE;                         /* error => exit!               */
    }
  } else
  {
    debug_log("Error opening file.");
    return FALSE;
  }
#endif


  if ((UnpMemory=malloc(UNP_MEMORY))==NULL)
  {
    debug_log("Can't allocate memory for decompression!");
    return FALSE;
  }

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  MemRARFile->offset+=NewMhd.HeadSize-MainHeadSize;
#else
  tseek(ArcPtr,NewMhd.HeadSize-MainHeadSize,SEEK_CUR);
#endif

  /* do while file is not extracted and there's no error                    */
  do
  {

    if (ReadBlock(FILE_HEAD | READSUBBLOCK) <= 0) /* read name of the next  */
    {                                       /* file within the RAR archive  */
/*
 *
 * 21.11.2000  UnQ  There's a problem with some linux distros when a file
 *                  can not be found in an archive.
 *
 *    debug_log("Couldn't read next filename from archive (I/O error).");
 *
*/
      ReturnCode=FALSE;
      break;                                /* error, file not found in     */
    }                                       /* archive or I/O error         */
    if (BlockHead.HeadType==SUB_HEAD)
    {
      debug_log("Sorry, sub-headers not supported.");
      ReturnCode=FALSE;
      break;                                /* error => exit                */
    }


    if(TRUE == (FileFound=(stricomp(ArgName, ArcFileName) == 0)))
    /* *** file found! ***                                                  */
    {
      {
        temp_output_buffer=malloc(NewLhd.UnpSize);/* allocate memory for the*/
      }
      *temp_output_buffer_offset=0;         /* file. The default offset     */
                                            /* within the buffer is 0       */

      if(temp_output_buffer == NULL)
      {
        debug_log("can't allocate memory for the file decompression");
        ReturnCode=FALSE;
        break;                              /* error, can't extract file!   */
      }


    }

    /* in case of a solid archive, we need to decompress any single file till
     * we have found the one we are looking for. In case of normal archives
     * (recommended!!), we skip the files until we are sure that it is the
     * one we want.
     */
    if((NewMhd.Flags & 0x08) || FileFound)
    {
      if (NewLhd.UnpVer<13 || NewLhd.UnpVer>UNP_VER)
      {
        debug_log("unknown compression method");
        ReturnCode=FALSE;
        break;                              /* error, can't extract file!   */
      }

      CurUnpRead=CurUnpWrite=0;
      if ((*Password!=0) && (NewLhd.Flags & LHD_PASSWORD))
        Encryption=NewLhd.UnpVer;
      else
        Encryption=0;
      if (Encryption) SetCryptKeys(Password);

      UnpPackedSize=NewLhd.PackSize;
      DestUnpSize=NewLhd.UnpSize;

      if (NewLhd.Method==0x30)
      {
        UnstoreFile();
      } else
      {
        Unpack(UnpMemory);
      }



#ifdef _DO_CRC32_CHECK                      /* calculate CRC32              */
      if((UBYTE*)temp_output_buffer != NULL)
      {
        if(NewLhd.FileCRC!=~CalcCRC32(0xFFFFFFFFL,
                                      (UBYTE*)temp_output_buffer,
                                      NewLhd.UnpSize))
        {
          debug_log("CRC32 error - file couldn't be decompressed correctly!");
          ReturnCode=FALSE;
          break;                              /* error, can't extract file! */
        }
      }
#endif

    }

#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
    MemRARFile->offset = NextBlockPos;
#else
    if (ArcPtr!=NULL) tseek(ArcPtr,NextBlockPos,SEEK_SET);
#endif
  } while(stricomp(ArgName, ArcFileName) != 0);/* exit if file is extracted */

  /* free memory, clear password and close archive                          */
  free(UnpMemory);
  UnpMemory=NULL;
#ifndef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
  if (ArcPtr!=NULL){
      fclose(ArcPtr);
      ArcPtr = NULL;
  }
#endif

  return ReturnCode;                        /* file extracted successful!   */
}

/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


















/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 *******             G L O B A L   F U N C T I O N S                  *******
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


int tread(void *stream,void *buf,unsigned len)
{
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION

  if(((MemRARFile->offset + len) > MemRARFile->size) || (len == 0))
     return 0;

  memcpy(buf,
         (BYTE*)(((MemoryFile*)stream)->data)+((MemoryFile*)stream)->offset,
         len % ((((MemoryFile*)stream)->size) - 1));

  MemRARFile->offset+=len;                  /* update read pointer          */
  return len % ((((MemoryFile*)stream)->size) - 1);
#else
  return(fread(buf,1,len,(FILE*)stream));
#endif
}


#ifndef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
int tseek(void *stream,long offset,int fromwhere)
{
  return(fseek((FILE*)stream,offset,fromwhere));
}
#endif


char* strupper(char *Str)
{
  char *ChPtr;
  for (ChPtr=Str;*ChPtr;ChPtr++)
    *ChPtr=(char)toupper(*ChPtr);
  return(Str);
}


int stricomp(char *Str1,char *Str2)
/* compare strings without regard of '\' and '/'                            */
{
  char S1[512],S2[512];
  char *chptr;

  strncpy(S1,Str1,sizeof(S1));
  strncpy(S2,Str2,sizeof(S2));

  while((chptr = strchr(S1, '\\')) != NULL) /* ignore backslash             */
  {
    *chptr = '_';
  }

  while((chptr = strchr(S2, '\\')) != NULL) /* ignore backslash             */
  {
    *chptr = '_';
  }

  while((chptr = strchr(S1, '/')) != NULL)  /* ignore slash                 */
  {
    *chptr = '_';
  }

  while((chptr = strchr(S2, '/')) != NULL)  /* ignore slash                 */
  {
    *chptr = '_';
  }

  return(strcmp(strupper(S1),strupper(S2)));
}


/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


















/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 *******                   U N P A C K   C O D E                      *******
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


/* *****************************
 * ** unpack stored RAR files **
 * *****************************/

BOOL UnstoreFile(void)
{
  if ((long)(*temp_output_buffer_offset=UnpRead(temp_output_buffer,
                                                NewLhd.UnpSize))==-1)
  {
    debug_log("Read error of stored file!");
    return FALSE;
  }
  return TRUE;
}




/* ****************************************
 * ** RAR decompression code starts here **
 * ****************************************/

#define NC 298                              /* alphabet = {0,1,2, .,NC - 1} */
#define DC 48
#define RC 28
#define BC 19
#define MC 257

enum {CODE_HUFFMAN=0,CODE_LZ=1,CODE_LZ2=2,CODE_REPEATLZ=3,CODE_CACHELZ=4,
      CODE_STARTFILE=5,CODE_ENDFILE=6,CODE_STARTMM=8,CODE_ENDMM=7,
      CODE_MMDELTA=9};

struct AudioVariables
{
  int K1,K2,K3,K4,K5;
  int D1,D2,D3,D4;
  int LastDelta;
  unsigned int Dif[11];
  unsigned int ByteCount;
  int LastChar;
};


#define NC 298  /* alphabet = {0, 1, 2, ..., NC - 1} */
#define DC 48
#define RC 28
#define BC 19
#define MC 257


struct AudioVariables AudV[4];

#define GetBits()                                                 \
        BitField = ( ( ( (UDWORD)InBuf[InAddr]   << 16 ) |        \
                       ( (UWORD) InBuf[InAddr+1] <<  8 ) |        \
                       (         InBuf[InAddr+2]       ) )        \
                       >> (8-InBit) ) & 0xffff;


#define AddBits(Bits)                          \
        InAddr += ( InBit + (Bits) ) >> 3;     \
        InBit  =  ( InBit + (Bits) ) &  7;

static unsigned char *UnpBuf;
static unsigned int BitField;
static unsigned int Number;

unsigned char InBuf[8192];                  /* input read buffer            */

unsigned char UnpOldTable[MC*4];

unsigned int InAddr,InBit,ReadTop;

unsigned int LastDist,LastLength;
static unsigned int Length,Distance;

unsigned int OldDist[4],OldDistPtr;


struct LitDecode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[NC];
} LD;

struct DistDecode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[DC];
} DD;

struct RepDecode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[RC];
} RD;

struct MultDecode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[MC];
} MD[4];

struct BitDecode
{
  unsigned int MaxNum;
  unsigned int DecodeLen[16];
  unsigned int DecodePos[16];
  unsigned int DecodeNum[BC];
} BD;

static struct MultDecode *MDPtr[4]={&MD[0],&MD[1],&MD[2],&MD[3]};

int UnpAudioBlock,UnpChannels,CurChannel,ChannelDelta;


void Unpack(unsigned char *UnpAddr)
/* *** 38.3% of all CPU time is spent within this function!!!               */
{
  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 int 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};
  unsigned int Bits;


  UnpBuf=UnpAddr;                           /* UnpAddr is a pointer to the  */
  UnpInitData();                            /* unpack buffer                */
  UnpReadBuf(1);
  if (!(NewLhd.Flags & LHD_SOLID))
     ReadTables();
   DestUnpSize--;

  while (DestUnpSize>=0)
  {
    UnpPtr&=MAXWINMASK;

    if (InAddr>sizeof(InBuf)-30)
      UnpReadBuf(0);
    if (((WrPtr-UnpPtr) & MAXWINMASK)<270 && WrPtr!=UnpPtr)
    {


      if (FileFound)
      {

        if (UnpPtr<WrPtr)
        {
                        if((*temp_output_buffer_offset + UnpPtr) > NewLhd.UnpSize)
                        {
                           debug_log("Fatal! Buffer overrun during decompression!");
                          DestUnpSize=-1;

                    } else
                        {
              /* copy extracted data to output buffer                         */
              memcpy(temp_output_buffer + *temp_output_buffer_offset,
                     &UnpBuf[WrPtr], (0-WrPtr) & MAXWINMASK);
              /* update offset within buffer                                  */
              *temp_output_buffer_offset+= (0-WrPtr) & MAXWINMASK;
              /* copy extracted data to output buffer                         */
              memcpy(temp_output_buffer + *temp_output_buffer_offset, UnpBuf,
                     UnpPtr);
              /* update offset within buffer                                  */
              *temp_output_buffer_offset+=UnpPtr;
                        }
        } else
        {
                        if((*temp_output_buffer_offset + (UnpPtr-WrPtr)) > NewLhd.UnpSize)
                        {
                           debug_log("Fatal! Buffer overrun during decompression!");
                          DestUnpSize=-1;
                    } else
                        {
                  /* copy extracted data to output buffer                       */
              memcpy(temp_output_buffer + *temp_output_buffer_offset,
                     &UnpBuf[WrPtr], UnpPtr-WrPtr);
              *temp_output_buffer_offset+=UnpPtr-WrPtr;                                                /* update offset within buffer */
                    }

            }
      }

      WrPtr=UnpPtr;
    }

    if (UnpAudioBlock)
    {
      DecodeNumber((struct Decode *)MDPtr[CurChannel]);
      if (Number==256)
      {
        ReadTables();
        continue;
      }
      UnpBuf[UnpPtr++]=DecodeAudio(Number);
      if (++CurChannel==UnpChannels)
        CurChannel=0;
      DestUnpSize--;
      continue;
    }

    DecodeNumber((struct Decode *)&LD);
    if (Number<256)
    {
      UnpBuf[UnpPtr++]=(UBYTE)Number;
      DestUnpSize--;
      continue;
    }
    if (Number>269)
    {
      Length=LDecode[Number-=270]+3;
      if ((Bits=LBits[Number])>0)
      {
        GetBits();
        Length+=BitField>>(16-Bits);
        AddBits(Bits);
      }

      DecodeNumber((struct Decode *)&DD);
      Distance=DDecode[Number]+1;
      if ((Bits=DBits[Number])>0)
      {
        GetBits();
        Distance+=BitField>>(16-Bits);
        AddBits(Bits);
      }

      if (Distance>=0x40000L)
        Length++;

      if (Distance>=0x2000)
        Length++;

       LastDist=OldDist[OldDistPtr++ & 3]=Distance;
       DestUnpSize-=(LastLength=Length);
       while (Length--)
       {
         UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK];
         UnpPtr=(UnpPtr+1) & MAXWINMASK;
       }

      continue;
    }
    if (Number==269)
    {
      ReadTables();
      continue;
    }
    if (Number==256)
    {
      Length=LastLength;
      Distance=LastDist;
       LastDist=OldDist[OldDistPtr++ & 3]=Distance;
       DestUnpSize-=(LastLength=Length);
       while (Length--)
       {
         UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK];
         UnpPtr=(UnpPtr+1) & MAXWINMASK;
       }
      continue;
    }
    if (Number<261)
    {
      Distance=OldDist[(OldDistPtr-(Number-256)) & 3];
      DecodeNumber((struct Decode *)&RD);
      Length=LDecode[Number]+2;
      if ((Bits=LBits[Number])>0)
      {
        GetBits();
        Length+=BitField>>(16-Bits);
        AddBits(Bits);
      }
      if (Distance>=0x40000)
        Length++;
      if (Distance>=0x2000)
        Length++;
      if (Distance>=0x101)
        Length++;
       LastDist=OldDist[OldDistPtr++ & 3]=Distance;
       DestUnpSize-=(LastLength=Length);
       while (Length--)
       {
         UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK];
         UnpPtr=(UnpPtr+1) & MAXWINMASK;
       }
      continue;
    }
    if (Number<270)
    {
      Distance=SDDecode[Number-=261]+1;
      if ((Bits=SDBits[Number])>0)
      {
        GetBits();
        Distance+=BitField>>(16-Bits);
        AddBits(Bits);
      }
      Length=2;
       LastDist=OldDist[OldDistPtr++ & 3]=Distance;
       DestUnpSize-=(LastLength=Length);
       while (Length--)
       {
         UnpBuf[UnpPtr]=UnpBuf[(UnpPtr-Distance) & MAXWINMASK];
         UnpPtr=(UnpPtr+1) & MAXWINMASK;
       }
      continue;
   }
  }
  ReadLastTables();

  if (FileFound)                            /* flush buffer                 */
  {

    if (UnpPtr<WrPtr)
    {
          if((*temp_output_buffer_offset + UnpPtr) > NewLhd.UnpSize)
          {
            debug_log("Fatal! Buffer overrun during decompression!");
                DestUnpSize=-1;
          } else
          {
        /* copy extracted data to output buffer                             */
        memcpy(temp_output_buffer + *temp_output_buffer_offset, &UnpBuf[WrPtr],
               (0-WrPtr) & MAXWINMASK);
        /* update offset within buffer                                      */
        *temp_output_buffer_offset+= (0-WrPtr) & MAXWINMASK;
        /* copy extracted data to output buffer                             */
        memcpy(temp_output_buffer + *temp_output_buffer_offset, UnpBuf, UnpPtr);
        /* update offset within buffer                                      */
        *temp_output_buffer_offset+=UnpPtr;
          }
    } else
    {
          if((*temp_output_buffer_offset + (UnpPtr-WrPtr)) > NewLhd.UnpSize)
          {
                 debug_log("Fatal! Buffer overrun during decompression!");
                DestUnpSize=-1;
          } else
          {
        /* copy extracted data to output buffer                             */
        memcpy(temp_output_buffer + *temp_output_buffer_offset, &UnpBuf[WrPtr],
               UnpPtr-WrPtr);
        /* update offset within buffer                                      */
        *temp_output_buffer_offset+=UnpPtr-WrPtr;
          }
    }
  }

  WrPtr=UnpPtr;
}


unsigned int UnpRead(unsigned char *Addr,unsigned int Count)
{
  int RetCode=0;
  unsigned int I,ReadSize,TotalRead=0;
  unsigned char *ReadAddr;
  ReadAddr=Addr;
  while (Count > 0)
  {
    ReadSize=(unsigned int)((Count>(unsigned long)UnpPackedSize) ?
                                                  UnpPackedSize : Count);
#ifdef _USE_MEMORY_TO_MEMORY_DECOMPRESSION
    if(MemRARFile->data == NULL)
      return(0);
    RetCode=tread(MemRARFile, ReadAddr, ReadSize);
#else
    if (ArcPtr==NULL)
      return(0);
    RetCode=tread(ArcPtr,ReadAddr,ReadSize);
#endif
    CurUnpRead+=RetCode;
    ReadAddr+=RetCode;
    TotalRead+=RetCode;
    Count-=RetCode;
    UnpPackedSize-=RetCode;
      break;
  }
  if (RetCode!= -1)
  {
    RetCode=TotalRead;
    if (Encryption)
    {
      if (Encryption<20)
          {
            debug_log("Old Crypt() not supported!");
          }
      else
      {
        for (I=0;I<(unsigned int)RetCode;I+=16)
          DecryptBlock(&Addr[I]);
      }
    }
  }
  return(RetCode);
}


void UnpReadBuf(int FirstBuf)
{
  int RetCode;
  if (FirstBuf)
  {
    ReadTop=UnpRead(InBuf,sizeof(InBuf));
    InAddr=0;
  }
  else
  {
    memcpy(InBuf,&InBuf[sizeof(InBuf)-32],32);
    InAddr&=0x1f;
    RetCode=UnpRead(&InBuf[32],sizeof(InBuf)-32);
    if (RetCode>0)
      ReadTop=RetCode+32;
    else
      ReadTop=InAddr;
  }
}


void ReadTables(void)
{
  UBYTE BitLength[BC];
  unsigned char Table[MC*4];
  int TableSize,N,I;
  if (InAddr>sizeof(InBuf)-25)
    UnpReadBuf(0);
  GetBits();
  UnpAudioBlock=(BitField & 0x8000);

  if (!(BitField & 0x4000))
    memset(UnpOldTable,0,sizeof(UnpOldTable));
  AddBits(2);


  if (UnpAudioBlock)
  {
    UnpChannels=((BitField>>12) & 3)+1;
    if (CurChannel>=UnpChannels)
      CurChannel=0;
    AddBits(2);
    TableSize=MC*UnpChannels;
  }
  else
    TableSize=NC+DC+RC;


  for (I=0;I<BC;I++)
  {
    GetBits();
    BitLength[I]=(UBYTE)(BitField >> 12);
    AddBits(4);
  }
  MakeDecodeTables(BitLength,(struct Decode *)&BD,BC);
  I=0;
  while (I<TableSize)
  {
    if (InAddr>sizeof(InBuf)-5)
      UnpReadBuf(0);
    DecodeNumber((struct Decode *)&BD);
    if (Number<16)
    {
      Table[I]=(Number+UnpOldTable[I]) & 0xf;
      I++;
    }
    else
      if (Number==16)
      {
        GetBits();
        N=(BitField >> 14)+3;
        AddBits(2);
        while (N-- > 0 && I<TableSize)
        {
          Table[I]=Table[I-1];
          I++;
        }
      }
      else
      {
        if (Number==17)
        {
          GetBits();
          N=(BitField >> 13)+3;
          AddBits(3);
        }
        else
        {
          GetBits();
          N=(BitField >> 9)+11;
          AddBits(7);
        }
        while (N-- > 0 && I<TableSize)
          Table[I++]=0;
      }
  }
  if (UnpAudioBlock)
    for (I=0;I<UnpChannels;I++)
      MakeDecodeTables(&Table[I*MC],(struct Decode *)MDPtr[I],MC);
  else
  {
    MakeDecodeTables(&Table[0],(struct Decode *)&LD,NC);
    MakeDecodeTables(&Table[NC],(struct Decode *)&DD,DC);
    MakeDecodeTables(&Table[NC+DC],(struct Decode *)&RD,RC);
  }
  memcpy(UnpOldTable,Table,sizeof(UnpOldTable));
}


static void ReadLastTables(void)
{
  if (ReadTop>=InAddr+5)
  {
    if (UnpAudioBlock)
    {
      DecodeNumber((struct Decode *)MDPtr[CurChannel]);
      if (Number==256)
        ReadTables();
    }
    else
    {
      DecodeNumber((struct Decode *)&LD);
      if (Number==269)
        ReadTables();
    }
  }
}


static void MakeDecodeTables(unsigned char *LenTab,
                             struct Decode *Dec,
                             int Size)
{
  int LenCount[16],TmpPos[16],I;
  long M,N;
  memset(LenCount,0,sizeof(LenCount));
  for (I=0;I<Size;I++)
    LenCount[LenTab[I] & 0xF]++;

  LenCount[0]=0;
  for (TmpPos[0]=Dec->DecodePos[0]=Dec->DecodeLen[0]=0,N=0,I=1;I<16;I++)
  {
    N=2*(N+LenCount[I]);
    M=N<<(15-I);
    if (M>0xFFFF)
      M=0xFFFF;
    Dec->DecodeLen[I]=(unsigned int)M;
    TmpPos[I]=Dec->DecodePos[I]=Dec->DecodePos[I-1]+LenCount[I-1];
  }

  for (I=0;I<Size;I++)
    if (LenTab[I]!=0)
      Dec->DecodeNum[TmpPos[LenTab[I] & 0xF]++]=I;
  Dec->MaxNum=Size;
}


static void DecodeNumber(struct Decode *Deco)
/* *** 52.6% of all CPU time is spent within this function!!!               */
{
  unsigned int I;
  register unsigned int N;
  GetBits();

#ifdef _USE_ASM

#ifdef _WIN_32
 __asm {

    xor eax, eax
    mov eax, BitField                       // N=BitField & 0xFFFE;
    and eax, 0xFFFFFFFE
    mov [N], eax
    mov edx, [Deco]                         // EAX=N, EDX=Deco

          cmp  eax, dword ptr[edx + 8*4 + 4]// if (N<Dec->DecodeLen[8])
          jae  else_G

             cmp  eax, dword ptr[edx + 4*4 + 4]// if (N<Dec->DecodeLen[4])
             jae  else_F


                cmp  eax, dword ptr[edx + 2*4 + 4]// if (N<Dec->DecodeLen[2])
                jae  else_C

                   cmp  eax, dword ptr[edx + 1*4 + 4]// if (N<Dec->DecodeLen[1])
                   jae  else_1
                   mov  I, 1                         //  I=1;
                   jmp  next_1
                 else_1:                             // else
                   mov  I, 2                         //  I=2;
                 next_1:

                jmp  next_C
              else_C:                             // else

                   cmp  eax, dword ptr[edx + 3*4 + 4]// if (N<Dec->DecodeLen[3])
                   jae  else_2
                   mov  I, 3                         //  I=3;
                   jmp  next_2
                 else_2:                             // else
                   mov  I, 4                         //  I=4;
                 next_2:

              next_C:                             // else

             jmp  next_F
           else_F:


             cmp  eax, dword ptr[edx + 6*4 + 4]// if (N<Dec->DecodeLen[6])
             jae  else_E

                cmp  eax, dword ptr[edx + 5*4 + 4]// if (N<Dec->DecodeLen[5])
                jae  else_3
                mov  I, 5                         //  I=5;
                jmp  next_3
              else_3:                             // else
                mov  I, 6                         //  I=6;
              next_3:

             jmp  next_E
           else_E:                             // else

                cmp  eax, dword ptr[edx + 7*4 + 4]// if (N<Dec->DecodeLen[7])
                jae  else_4
                mov  I, 7                         //  I=7;
                jmp  next_4
              else_4:                             // else
                mov  I, 8                         //  I=8;
              next_4:

           next_E:

           next_F:

          jmp  next_G
        else_G:

          cmp  eax, dword ptr[edx + 12*4 + 4] // if (N<Dec->DecodeLen[12])
          jae  else_D

             cmp  eax, dword ptr[edx + 10*4 + 4]// if (N<Dec->DecodeLen[10])
             jae  else_B

                cmp  eax, dword ptr[edx + 9*4 + 4]// if (N<Dec->DecodeLen[9])
                jae  else_5
                mov  I, 9                         //  I=9;
                jmp  next_5
              else_5:                             // else
                mov  I, 10                         //  I=10;
              next_5:

             jmp  next_B
           else_B:                             // else

                cmp  eax, dword ptr[edx + 11*4 + 4]// if (N<Dec->DecodeLen[11])
                jae  else_6
                mov  I, 11                         //  I=11;
                jmp  next_6
              else_6:                             // else
                mov  I, 12                         //  I=12;
              next_6:

           next_B:


          jmp  next_D
        else_D:                             // else

               cmp  eax, dword ptr[edx + 14*4 + 4]// if (N<Dec->DecodeLen[14])
               jae  else_A

                  cmp  eax, dword ptr[edx + 13*4 + 4]// if (N<Dec->DecodeLen[13])
                  jae  else_7
                  mov  I, 13                         //  I=13;
                  jmp  next_7
                 else_7:                             // else
                  mov  I, 14                         //  I=14;
                 next_7:

               jmp  next_A
              else_A:                             // else
               mov  I, 15                         //  I=15;
              next_A:

        next_D:
    next_G:
}
#else
 __asm__ __volatile__ (
     "andl $0xFFFFFFFE, %%eax"
"      movl %%eax, %1"
"          cmpl 8*4(%%edx), %%eax /* 5379 */"
"          jae  else_G"
""
"             cmpl 4*4(%%edx), %%eax"
"             jae  else_F"
""
"                cmpl 2*4(%%edx), %%eax"
"                jae  else_C"
""
"                   cmpl 1*4(%%edx), %%eax"
""
"                   jae  else_1"
"                   movl $1, %0"
"                   jmp  next_1"
"                 else_1:       "
"                   movl  $2, %0"
"                 next_1:"
"                "
"                jmp  next_C"
"              else_C:          "
""
"                   cmpl 3*4(%%edx), %%eax "
"                   jae  else_2"
"                   movl  $3, %0"
"                   jmp  next_2"
"                 else_2:       "
"                   movl  $4, %0"
"                 next_2:"
""
"              next_C:          "
""
"             jmp  next_F"
"           else_F:"
""
"             cmpl 6*4(%%edx), %%eax"
"             jae  else_E"
""
"                cmpl 5*4(%%edx), %%eax"
"                jae  else_3"
"                movl  $5, %0   "
"                jmp  next_3"
"              else_3:          "
"                movl  $6, %0   "
"              next_3:"
""
"             jmp  next_E"
"           else_E:             "
""
"                cmpl 7*4(%%edx), %%eax"
"                jae  else_4"
"                movl  $7, %0   "
"                jmp  next_4"
"              else_4:          "
"                movl  $8, %0   "
"              next_4:"
""
"           next_E:"
""
"           next_F:"
""
"          jmp  next_G"
"        else_G:"
""
"          cmpl 12*4(%%edx), %%eax"
"          jae  else_D"
""
"             cmpl 10*4(%%edx), %%eax"
"             jae  else_B"
""
"                cmpl 9*4(%%edx), %%eax"
"                jae  else_5"
"                movl  $9, %0   "
"                jmp  next_5"
"              else_5:          "
"                movl  $10, %0  "
"              next_5:"
""
"             jmp  next_B"
"           else_B:             "
""
"                cmpl 11*4(%%edx), %%eax"
" "
"                jae  else_6"
"                movl  $11, %0  "
"                jmp  next_6"
"              else_6:          "
"                movl  $12, %0  "
"              next_6:"
""
"           next_B:"
"      "
"        "
"          jmp  next_D"
"        else_D:                "
""
"               cmpl 14*4(%%edx), %%eax"
"               jae  else_A"
""
"                  cmpl 13*4(%%edx), %%eax"
"                  jae  else_7"
"                  movl  $13, %0"
"                  jmp  next_7"
"                 else_7:       "
"                  movl  $14, %0"
"                 next_7:"
""
"               jmp  next_A"
"              else_A:          "
"               movl  $15, %0   "
"              next_A:"
"          "
"        next_D:                             "
"    next_G:"
     : "=g" (I), "=r"(N)
     : "eax" ((long)BitField), "edx"((long)Deco->DecodeLen)
      : "memory"
     );
#endif /* #ifdef _WIN_32 ... #elif defined _X86_ASM_ */

#else
  N=BitField & 0xFFFE;
  if (N<Deco->DecodeLen[8])  {
    if (N<Deco->DecodeLen[4]) {
      if (N<Deco->DecodeLen[2]) {
        if (N<Deco->DecodeLen[1])
          I=1;
        else
          I=2;
      } else {
        if (N<Deco->DecodeLen[3])
          I=3;
        else
          I=4;
      }
    } else {
      if (N<Deco->DecodeLen[6])  {
        if (N<Deco->DecodeLen[5])
          I=5;
        else
          I=6;
      } else {
        if (N<Deco->DecodeLen[7])
          I=7;
        else
          I=8;
      }
   }
  } else {
    if (N<Deco->DecodeLen[12]) {
      if (N<Deco->DecodeLen[10]) {
        if (N<Deco->DecodeLen[9])
          I=9;
        else
          I=10;
      } else {
        if (N<Deco->DecodeLen[11])
          I=11;
        else
          I=12;
      }
    } else {
      if (N<Deco->DecodeLen[14]) {
        if (N<Deco->DecodeLen[13])
          I=13;
        else
          I=14;

      } else {
          I=15;
      }
    }

  }
#endif

  AddBits(I);
  if ((N=Deco->DecodePos[I]+((N-Deco->DecodeLen[I-1])>>(16-I)))>=Deco->MaxNum)
      N=0;
  Number=Deco->DecodeNum[N];
}


void UnpInitData()
{
  InAddr=InBit=0;
  if (!(NewLhd.Flags & LHD_SOLID))
  {
    ChannelDelta=CurChannel=0;

#ifdef _USE_ASM

#ifdef _WIN_32                              /* Win32 with VisualC           */

    __asm {
        push edi
        push eax
        push ecx

        cld                                 /* increment EDI and ESI        */
        mov  al, 0x00
        mov  ecx, SIZE AudV
        mov  edi, Offset AudV
        rep  stosb                          /* clear memory                 */

        mov  ecx, SIZE OldDist
        mov  edi, Offset OldDist
        rep  stosb                          /* clear memory                 */

        mov  ecx, SIZE UnpOldTable
        mov  edi, Offset UnpOldTable
        rep  stosb                          /* clear memory                 */

        pop  ecx
        pop  eax
        pop  edi


        mov  [OldDistPtr], 0
        mov  [LastDist], 0
        mov  [LastLength], 0
        mov  [UnpPtr], 0
        mov  [WrPtr], 0
        mov  [OldDistPtr], 0
        mov  [LastLength], 0
        mov  [LastDist], 0
        mov  [UnpPtr], 0
        mov  [WrPtr], 0

    }
    memset(UnpBuf,0,MAXWINSIZE);


#else                    /* unix/linux on i386 cpus */
    __asm__ __volatile (
"        cld                                 /* increment EDI and ESI        */"
"        movb $0x00, %%al"
"        movl %0, %%ecx"
"        movl %1, %%edi"
"        rep  "
"        stosb                              /* clear memory                 */"
""
"        movl %2, %%ecx"
"        mov  %3, %%edi"
"        rep  "
"        stosb                              /* clear memory                 */"
""
"        movl %4, %%ecx"
"        movl %5, %%edi"
"        rep  "
"        stosb                              /* clear memory                 */"
""
"        movl $0, (OldDistPtr)"
"        movl $0, (LastDist)"
"        movl $0, (LastLength)"
"        movl $0, (UnpPtr)"
"        movl $0, (WrPtr)"
"        movl $0, (OldDistPtr)"
"        movl $0, (LastLength)"
"        movl $0, (LastDist)"
"        movl $0, (UnpPtr)"
"        movl $0, (WrPtr)"
        :
        : "m" ((long)sizeof(AudV)),
          "m" ((long)AudV),
          "m" ((long)sizeof(OldDist)),
          "m" ((long)OldDist),
          "m" ((long)sizeof(UnpOldTable)),
          "m" ((long)UnpOldTable)
        : "memory", "edi", "eax", "ecx"
    );
    memset(UnpBuf,0,MAXWINSIZE);
#endif

#else                                       /* unix/linux on non-i386 cpu  */
    memset(AudV,0,sizeof(AudV));
    memset(OldDist,0,sizeof(OldDist));
    OldDistPtr=0;
    LastDist=LastLength=0;
    memset(UnpBuf,0,MAXWINSIZE);
    memset(UnpOldTable,0,sizeof(UnpOldTable));
    UnpPtr=WrPtr=0;
#endif

  }
}


UBYTE DecodeAudio(int Delta)
{
  struct AudioVariables *V;
  unsigned int Ch;
  unsigned int NumMinDif,MinDif;
  int PCh,I;

  V=&AudV[CurChannel];
  V->ByteCount++;
  V->D4=V->D3;
  V->D3=V->D2;
  V->D2=V->LastDelta-V->D1;
  V->D1=V->LastDelta;
  PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+
           V->K3*V->D3+V->K4*V->D4+V->K5*ChannelDelta;
  PCh=(PCh>>3) & 0xFF;

  Ch=PCh-Delta;

  I=((signed char)Delta)<<3;

  V->Dif[0]+=abs(I);
  V->Dif[1]+=abs(I-V->D1);
  V->Dif[2]+=abs(I+V->D1);
  V->Dif[3]+=abs(I-V->D2);
  V->Dif[4]+=abs(I+V->D2);
  V->Dif[5]+=abs(I-V->D3);
  V->Dif[6]+=abs(I+V->D3);
  V->Dif[7]+=abs(I-V->D4);
  V->Dif[8]+=abs(I+V->D4);
  V->Dif[9]+=abs(I-ChannelDelta);
  V->Dif[10]+=abs(I+ChannelDelta);

  ChannelDelta=V->LastDelta=(signed char)(Ch-V->LastChar);
  V->LastChar=Ch;

  if ((V->ByteCount & 0x1F)==0)
  {
    MinDif=V->Dif[0];
    NumMinDif=0;
    V->Dif[0]=0;
    for (I=1;(unsigned int)I<sizeof(V->Dif)/sizeof(V->Dif[0]);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((UBYTE)Ch);
}







/* ***************************************************
 * ** CRCCrypt Code - decryption engine starts here **
 * ***************************************************/


#define NROUNDS 32

#define rol(x,n)  (((x)<<(n)) | ((x)>>(8*sizeof(x)-(n))))
#define ror(x,n)  (((x)>>(n)) | ((x)<<(8*sizeof(x)-(n))))

#define substLong(t) ( (UDWORD)SubstTable[(int)t&255] | \
           ((UDWORD)SubstTable[(int)(t>> 8)&255]<< 8) | \
           ((UDWORD)SubstTable[(int)(t>>16)&255]<<16) | \
           ((UDWORD)SubstTable[(int)(t>>24)&255]<<24) )


UDWORD CRCTab[256];

UBYTE SubstTable[256];
UBYTE InitSubstTable[256]={
  215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221,  2, 42,
  232,  1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
  255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86,  6,
   71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
  107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
  158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
   97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
  164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
  207,120,189,210,  8,226, 41, 72,183,203,135,165,166, 60, 98,  7,
  122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
  131,  3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
  224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
  118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45,  4,148,108,
  161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
    0, 46,169,186, 68, 95,237, 65, 53,208,253,168,  9, 18,100, 52,
  116,184,160, 96,109, 37, 30,106,140,104,150,  5,204,117,112, 84
};

UDWORD Key[4];


void EncryptBlock(UBYTE *Buf)
{
  int I;

  UDWORD A,B,C,D,T,TA,TB;
#ifdef NON_INTEL_BYTE_ORDER
  A=((UDWORD)Buf[0]|((UDWORD)Buf[1]<<8)|((UDWORD)Buf[2]<<16)|
      ((UDWORD)Buf[3]<<24))^Key[0];
  B=((UDWORD)Buf[4]|((UDWORD)Buf[5]<<8)|((UDWORD)Buf[6]<<16)|
      ((UDWORD)Buf[7]<<24))^Key[1];
  C=((UDWORD)Buf[8]|((UDWORD)Buf[9]<<8)|((UDWORD)Buf[10]<<16)|
      ((UDWORD)Buf[11]<<24))^Key[2];
  D=((UDWORD)Buf[12]|((UDWORD)Buf[13]<<8)|((UDWORD)Buf[14]<<16)|
      ((UDWORD)Buf[15]<<24))^Key[3];
#else
  UDWORD *BufPtr;
  BufPtr=(UDWORD *)Buf;
  A=BufPtr[0]^Key[0];
  B=BufPtr[1]^Key[1];
  C=BufPtr[2]^Key[2];
  D=BufPtr[3]^Key[3];
#endif
  for(I=0;I<NROUNDS;I++)
  {
    T=((C+rol(D,11))^Key[I&3]);
    TA=A^substLong(T);
    T=((D^rol(C,17))+Key[I&3]);
    TB=B^substLong(T);
    A=C;
    B=D;
    C=TA;
    D=TB;
  }
#ifdef NON_INTEL_BYTE_ORDER
  C^=Key[0];
  Buf[0]=(UBYTE)C;
  Buf[1]=(UBYTE)(C>>8);
  Buf[2]=(UBYTE)(C>>16);
  Buf[3]=(UBYTE)(C>>24);
  D^=Key[1];
  Buf[4]=(UBYTE)D;
  Buf[5]=(UBYTE)(D>>8);
  Buf[6]=(UBYTE)(D>>16);
  Buf[7]=(UBYTE)(D>>24);
  A^=Key[2];
  Buf[8]=(UBYTE)A;
  Buf[9]=(UBYTE)(A>>8);
  Buf[10]=(UBYTE)(A>>16);
  Buf[11]=(UBYTE)(A>>24);
  B^=Key[3];
  Buf[12]=(UBYTE)B;
  Buf[13]=(UBYTE)(B>>8);
  Buf[14]=(UBYTE)(B>>16);
  Buf[15]=(UBYTE)(B>>24);
#else
  BufPtr[0]=C^Key[0];
  BufPtr[1]=D^Key[1];
  BufPtr[2]=A^Key[2];
  BufPtr[3]=B^Key[3];
#endif
  UpdKeys(Buf);
}


void DecryptBlock(UBYTE *Buf)
{
  int I;
  UBYTE InBuf[16];
  UDWORD A,B,C,D,T,TA,TB;
#ifdef NON_INTEL_BYTE_ORDER
  A=((UDWORD)Buf[0]|((UDWORD)Buf[1]<<8)|((UDWORD)Buf[2]<<16)|
      ((UDWORD)Buf[3]<<24))^Key[0];
  B=((UDWORD)Buf[4]|((UDWORD)Buf[5]<<8)|((UDWORD)Buf[6]<<16)|
      ((UDWORD)Buf[7]<<24))^Key[1];
  C=((UDWORD)Buf[8]|((UDWORD)Buf[9]<<8)|((UDWORD)Buf[10]<<16)|
      ((UDWORD)Buf[11]<<24))^Key[2];
  D=((UDWORD)Buf[12]|((UDWORD)Buf[13]<<8)|((UDWORD)Buf[14]<<16)|
      ((UDWORD)Buf[15]<<24))^Key[3];
#else
  UDWORD *BufPtr;
  BufPtr=(UDWORD *)Buf;
  A=BufPtr[0]^Key[0];                       /* xxx may be this can be       */
  B=BufPtr[1]^Key[1];                       /* optimized in assembler       */
  C=BufPtr[2]^Key[2];
  D=BufPtr[3]^Key[3];
#endif
  memcpy(InBuf,Buf,sizeof(InBuf));
  for(I=NROUNDS-1;I>=0;I--)
  {
    T=((C+rol(D,11))^Key[I&3]);
    TA=A^substLong(T);
    T=((D^rol(C,17))+Key[I&3]);
    TB=B^substLong(T);
    A=C;
    B=D;
    C=TA;
    D=TB;
  }
#ifdef NON_INTEL_BYTE_ORDER
  C^=Key[0];
  Buf[0]=(UBYTE)C;
  Buf[1]=(UBYTE)(C>>8);
  Buf[2]=(UBYTE)(C>>16);
  Buf[3]=(UBYTE)(C>>24);
  D^=Key[1];
  Buf[4]=(UBYTE)D;
  Buf[5]=(UBYTE)(D>>8);
  Buf[6]=(UBYTE)(D>>16);
  Buf[7]=(UBYTE)(D>>24);
  A^=Key[2];
  Buf[8]=(UBYTE)A;
  Buf[9]=(UBYTE)(A>>8);
  Buf[10]=(UBYTE)(A>>16);
  Buf[11]=(UBYTE)(A>>24);
  B^=Key[3];
  Buf[12]=(UBYTE)B;
  Buf[13]=(UBYTE)(B>>8);
  Buf[14]=(UBYTE)(B>>16);
  Buf[15]=(UBYTE)(B>>24);
#else
  BufPtr[0]=C^Key[0];
  BufPtr[1]=D^Key[1];
  BufPtr[2]=A^Key[2];
  BufPtr[3]=B^Key[3];
#endif
  UpdKeys(InBuf);
}


void UpdKeys(UBYTE *Buf)
{
  int I;
  for (I=0;I<16;I+=4)
  {
    Key[0]^=CRCTab[Buf[I]];                 /* xxx may be I'll rewrite this */
    Key[1]^=CRCTab[Buf[I+1]];               /* in asm for speedup           */
    Key[2]^=CRCTab[Buf[I+2]];
    Key[3]^=CRCTab[Buf[I+3]];
  }
}

void SetCryptKeys(char *Password)
{
  unsigned int I,J,K,PswLength;
  unsigned char N1,N2;
  unsigned char Psw[256];

#if !defined _USE_ASM
  UBYTE Ch;
#endif

  SetOldKeys(Password);

  Key[0]=0xD3A3B879L;
  Key[1]=0x3F6D12F7L;
  Key[2]=0x7515A235L;
  Key[3]=0xA4E7F123L;
  memset(Psw,0,sizeof(Psw));
  strcpy((char *)Psw,Password);
  PswLength=strlen(Password);
  memcpy(SubstTable,InitSubstTable,sizeof(SubstTable));

  for (J=0;J<256;J++)
    for (I=0;I<PswLength;I+=2)
    {
      N2=(unsigned char)CRCTab[(Psw[I+1]+J)&0xFF];
      for (K=1, N1=(unsigned char)CRCTab[(Psw[I]-J)&0xFF];
           (N1!=N2) && (N1 < 256);      /* I had to add "&& (N1 < 256)",    */
           N1++, K++)                   /* because the system crashed with  */
          {                             /* encrypted RARs                   */
#ifdef _USE_ASM

#ifdef _WIN_32
          __asm {

                    mov ebx, Offset SubstTable
                    mov edx, ebx

                    xor ecx, ecx            // read SubstTable[N1]...
                    mov cl, N1
                    add ebx, ecx
                    mov al, byte ptr[ebx]

                    mov cl, N1              // read SubstTable[(N1+I+K)&0xFF]...
                    add ecx, I
                    add ecx, K
                    and ecx, 0xFF
                    add edx, ecx
                    mov ah, byte ptr[edx]

                    mov  byte ptr[ebx], ah  // and write back
                    mov  byte ptr[edx], al

               }
#else
                     __asm__ __volatile__ (
"                    xorl %%ecx, %%ecx"
"                    movl %2, %%ecx                     /* ecx = N1 */"
"                    mov %%ebx, %%edx"
"                    addl %%ecx, %%ebx"
""
"                    addl %0, %%ecx"
"                    addl %1, %%ecx"
"                    andl $0x000000FF, %%ecx"
"                    addl %%ecx, %%edx"
"                    "
"                    movb (%%ebx), %%al"
"                    movb (%%edx), %%ah"
""
"                    movb  %%ah, (%%ebx)     /* and write back */"
"                    movb  %%al, (%%edx)"
                    : : "g" ((long)I),
                        "g" ((long)K),
                        "g" ((long)N1),
                        "ebx"((long)SubstTable)
                    : "ecx", "edx"

              );
#endif

#else
            /* Swap(&SubstTable[N1],&SubstTable[(N1+I+K)&0xFF]);            */
             Ch=SubstTable[N1];
             SubstTable[N1]=SubstTable[(N1+I+K)&0xFF];
             SubstTable[(N1+I+K)&0xFF]=Ch;
#endif
          }
    }
  for (I=0;I<PswLength;I+=16)
    EncryptBlock(&Psw[I]);
}


void SetOldKeys(char *Password)
{
  UDWORD PswCRC;
  UBYTE Ch;
  PswCRC=CalcCRC32(0xFFFFFFFFL,(UBYTE*)Password,strlen(Password));
  OldKey[0]=(UWORD)PswCRC;
  OldKey[1]=(UWORD)(PswCRC>>16);
  OldKey[2]=OldKey[3]=0;
  PN1=PN2=PN3=0;
  while ((Ch=*Password)!=0)
  {
    PN1+=Ch;
    PN2^=Ch;
    PN3+=Ch;
    PN3=(UBYTE)rol(PN3,1);
    OldKey[2]^=((UWORD)(Ch^CRCTab[Ch]));
    OldKey[3]+=((UWORD)(Ch+(CRCTab[Ch]>>16)));
    Password++;
  }
}

void InitCRC(void)
{
  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);
    CRCTab[I]=C;
  }
}


UDWORD CalcCRC32(UDWORD StartCRC,UBYTE *Addr,UDWORD Size)
{
  unsigned int I;
  for (I=0; I<Size; I++)
    StartCRC = CRCTab[(UBYTE)StartCRC ^ Addr[I]] ^ (StartCRC >> 8);
  return(StartCRC);
}


/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */

























/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 *******              D E B U G    F U N C T I O N S                  *******
 *******                                                              *******
 *******                                                              *******
 *******                                                              *******
 ****************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */
#ifdef _DEBUG_LOG


/* -- global stuff -------------------------------------------------------- */
char  log_file_name[256];                   /* file name for the log file   */
DWORD debug_start_time;                     /* starttime of debug           */
BOOL  debug_started = FALSE;                /* debug_log writes only if     */
                                            /* this is TRUE                 */
/* ------------------------------------------------------------------------ */


/* -- global functions ---------------------------------------------------- */
void debug_init_proc(char *file_name)
/* Create/Rewrite a log file                                                */
{
  FILE *fp;
  char date[] = __DATE__;
  char time[] = __TIME__;

  debug_start_time = GetTickCount();        /* get start time               */
  strcpy(log_file_name, file_name);         /* save file name               */

  if((fp = fopen(log_file_name, CREATETEXT)) != NULL)
  {
    debug_started = TRUE;                   /* enable debug                 */
    fprintf(fp, "Debug log of UniquE's RARFileLib\n"\
                "~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~\n");
    fprintf(fp, "(executable compiled on %s at %s)\n\n", date, time);
    fclose(fp);
  }
}


void debug_log_proc(char *text, char *sourcefile, int sourceline)
/* add a line to the log file                                               */
{
  FILE *fp;

  if(debug_started == FALSE) return;        /* exit if not initialized      */

  if((fp = fopen(log_file_name, APPENDTEXT)) != NULL) /* append to logfile  */

  {
    fprintf(fp, " %8u ms (line %u in %s):\n              - %s\n",
            (unsigned int)(GetTickCount() - debug_start_time),
            sourceline, sourcefile, text);
    fclose(fp);
  }
}

/* ------------------------------------------------------------------------ */
#endif
/* **************************************************************************
 ****************************************************************************
 ****************************************************************************
 ************************************************************************** */


/* end of file urarlib.c */