⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unrarlib.c

📁 RAR的解压缩算法
💻 C
📖 第 1 页 / 共 4 页
字号:
/* Copyright (C) 2004  Jeroen Dekkers <jeroen@dekkers.cx>
   Copyright (C) 2000-2002  Christian Scheurer (www.ChristianScheurer.ch)
   Copyright (C) 2000-2002  Johannes Winkelmann (jw@tks6.net)
   RAR decompression code:
   Copyright (c) 1993-2002  Eugene Roshal

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2
   of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

#include "unrarlib.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>

#define FM_LABEL    0x0000
#define FM_DIREC    0x4000

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

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

typedef long DWORD;

#ifdef _DEBUG_LOG               /* define macros for debugging  */

#define debug_log(a) puts (a);
#define debug_init(a);

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

#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 mark_header
{
  uint16_t head_crc;
  uint8_t head_type;
  uint16_t head_flags;
  uint16_t head_size;
} __attribute__ ((packed));


struct archive_header
{
  uint16_t head_crc;
  uint8_t head_type;
  uint16_t head_flags;
  uint16_t head_size;
  uint16_t reserved1;
  uint32_t reserved2;
} __attribute__ ((packed));

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;
  unsigned int *DecodePos;
  unsigned int *DecodeNum;
};


struct mark_header markheader;
struct archive_header archiveheader;
struct NewFileHeader NewLhd;
struct BlockHeader BlockHead;

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


UBYTE *UnpMemory;
char *ArgName = NULL;           /* current file in rar archive  */
char *ArcFileName;              /* file to decompress           */

char *ArcName = NULL;           /* RAR archive name             */
FILE *inputfile;                /* input RAR file handler       */

char *Password = NULL;          /* 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  */
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);
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);
uint32_t CalcCRC32 (uint32_t 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,
             const char *filename,
             const char *rarfile, const 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;

#ifdef _DEBUG_LOG
  char DebugMsg[500];           /* used to compose debug msg    */
#endif

  InitCRC ();                   /* init some vars               */

  /* set file(s) to extract       */
  if (ArgName)
    free (ArgName);
  ArgName = strdup (filename);
  /* set RAR file name            */
  if (ArcName)
    free (ArcName);
  ArcName = strdup (rarfile);

  if (Password)
    free (Password);
  if (libpassword != NULL)
    /* init password                */
    Password = strdup (libpassword);
  else
    Password = strdup ("");

  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 ? libpassword : "");
  debug_log (DebugMsg);
#endif

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

  /* clear password               */
  if (Password)
    free (Password);
  Password = strdup ("");

  if (inputfile != NULL)
  {
    fclose (inputfile);
    inputfile = NULL;
  }

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


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

    sprintf (DebugMsg,
             "Error - couldn't extract \"%s\" and allocated "
             "%u Bytes of unused memory!", filename, (unsigned int) *size);
  }
  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 (const char *rarfile, ArchiveList_struct ** list)
{
  ArchiveList_struct *tmp_List = NULL;
  int filecount = 0;            /* number of files in archive   */

  InitCRC ();                   /* init some vars */

  /* open and identify archive */
  inputfile = fopen (rarfile, "r");
  if (inputfile)
  {
    if (!IsArchive ())
    {
      debug_log ("Not a RAR file");
      fclose (inputfile);
      inputfile = NULL;
      return 0;                 /* error => exit!               */
    }
  }
  else
  {
    debug_log ("Error opening file.");
    return 0;
  }

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

  fseek (inputfile, archiveheader.head_size - SIZEOF_NEWMHD, SEEK_CUR);

  *list = NULL;                 /* init file list */
  /* do while file is not extracted and there's no error */
  while (1)
  {
    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 */
    }

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

      *list = tmp_List;
    }
    else                        /* add entry */
    {
      tmp_List->next = malloc (sizeof (ArchiveList_struct));
      tmp_List = 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;

    filecount++;                /* count files                  */

    if (inputfile != NULL)
      fseek (inputfile, NextBlockPos, SEEK_SET);
  }

  /* free memory, clear password and close archive                          */
  /* clear password               */
  if (Password)
    free (Password);
  Password = strdup ("");

  if (inputfile != NULL)
  {
    fclose (inputfile);
    inputfile = NULL;
  }


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

  return filecount;
}



/* 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, ReadSubBlock = 0;
  static int LastBlock;

  memcpy (&SaveFileHead, &NewLhd, sizeof (SaveFileHead));
  if (BlockType & READSUBBLOCK)
    ReadSubBlock = 1;
  BlockType &= 0xff;
  while (1)
  {
    CurBlockPos = ftell (inputfile);
    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;
    fseek (inputfile, NextBlockPos, SEEK_SET);
  }

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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -