📄 unrarlib.c
字号:
/* 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 + -