libclamunrar/unrar20.c
5ca7fd18
 /*
  *  Extract RAR archives
  *
  *  Copyright (C) 2005 trog@uncon.org
3d8a0c1d
  *  Patches added by Sourcefire, Inc. Copyright (C) 2007-2013
5ca7fd18
  *
  *  This code is based on the work of Alexander L. Roshal (C)
  *
  *  The unRAR sources may be used in any software to handle RAR
  *  archives without limitations free of charge, but cannot be used
  *  to re-create the RAR compression algorithm, which is proprietary.
  *  Distribution of modified unRAR sources in separate form or as a
  *  part of other software is permitted, provided that it is clearly
  *  stated in the documentation and source comments that the code may
  *  not be used to develop a RAR (WinRAR) compatible archiver.
  *
  */
 
a8693be4
 #include <stdio.h>
5ca7fd18
 #include <string.h>
 
 #include "libclamunrar/unrar.h"
 #include "libclamunrar/unrar20.h"
 
 #ifdef RAR_HIGH_DEBUG
 #define rar_dbgmsg printf
 #else
a7e14794
 static void rar_dbgmsg(const char* fmt,...){(void)fmt;}
5ca7fd18
 #endif
 
 void unpack_init_data20(int solid, unpack_data_t *unpack_data)
 {
 	if (!solid) {
 		unpack_data->unp_channel_delta = 0;
 		unpack_data->unp_cur_channel = 0;
d076ad02
 		unpack_data->unp_audio_block = 0;
5ca7fd18
 		unpack_data->unp_channels = 1;
 		memset(unpack_data->audv, 0, sizeof(unpack_data->audv));
 		memset(unpack_data->unp_old_table20, 0, sizeof(unpack_data->unp_old_table20));
d076ad02
 		memset(unpack_data->MD, 0, sizeof(unpack_data->MD));
5ca7fd18
 	}
 }
 
 static void copy_string20(unpack_data_t *unpack_data, unsigned int length, unsigned int distance)
 {
 	unsigned int dest_ptr;
 	
 	unpack_data->last_dist = unpack_data->old_dist[unpack_data->old_dist_ptr++ & 3] = distance;
 	unpack_data->last_length = length;
 	unpack_data->dest_unp_size -= length;
 	
 	dest_ptr = unpack_data->unp_ptr - distance;
 	if (dest_ptr < MAXWINSIZE-300 && unpack_data->unp_ptr < MAXWINSIZE-300) {
 		unpack_data->window[unpack_data->unp_ptr++] = unpack_data->window[dest_ptr++];
 		unpack_data->window[unpack_data->unp_ptr++] = unpack_data->window[dest_ptr++];
 		while (length > 2) {
 			length--;
 			unpack_data->window[unpack_data->unp_ptr++] = unpack_data->window[dest_ptr++];
 		}
 	} else while (length--) {
 		unpack_data->window[unpack_data->unp_ptr] = unpack_data->window[dest_ptr++ & MAXWINMASK];
 		unpack_data->unp_ptr = (unpack_data->unp_ptr+1) & MAXWINMASK;
 	}
 }
 			
 static int read_tables20(int fd, unpack_data_t *unpack_data)
 {
 	unsigned char bit_length[BC20];
 	unsigned char table[MC20 * 4];
 	int table_size, n, i, number;
 	unsigned int bit_field;
 	
 	rar_dbgmsg("in read_tables20\n");
 	
 	if (unpack_data->in_addr > unpack_data->read_top-25) {
 		if (!rar_unp_read_buf(fd, unpack_data)) {
 			return FALSE;
 		}
 	}
 	bit_field = rar_getbits(unpack_data);
 	unpack_data->unp_audio_block = (bit_field & 0x8000);
 	
 	if (!(bit_field & 0x4000)) {
 		memset(unpack_data->unp_old_table20, 0, sizeof(unpack_data->unp_old_table20));
 	}
 	rar_addbits(unpack_data, 2);
 	
 	if (unpack_data->unp_audio_block) {
 		unpack_data->unp_channels = ((bit_field>>12) & 3) + 1;
 		if (unpack_data->unp_cur_channel >= unpack_data->unp_channels) {
 			unpack_data->unp_cur_channel = 0;
 		}
 		rar_addbits(unpack_data, 2);
 		table_size = MC20 * unpack_data->unp_channels;
 	} else {
 		table_size = NC20+DC20+RC20;
 	}
 	
 	for (i=0 ; i < BC20 ; i++) {
 		bit_length[i] = (unsigned char) (rar_getbits(unpack_data) >> 12);
 		rar_addbits(unpack_data, 4);
 	}
 	rar_make_decode_tables(bit_length, (struct Decode *)&unpack_data->BD, BC20);
3d8a0c1d
 
 	memset(table, 0, sizeof(table));
 	for (i=0; i<table_size;) {
5ca7fd18
 		if (unpack_data->in_addr > unpack_data->read_top-5) {
 			if (!rar_unp_read_buf(fd, unpack_data)) {
 				return FALSE;
 			}
 		}
 		number = rar_decode_number(unpack_data, (struct Decode *)&unpack_data->BD);
 		if (number < 16) {
 			table[i] = (number + unpack_data->unp_old_table20[i]) & 0xf;
 			i++;
 		} else if (number == 16) {
 			n = (rar_getbits(unpack_data) >> 14) + 3;
 			rar_addbits(unpack_data, 2);
da6a045e
 			if (i == 0) {
 				/* We cannot have "repeat previous" code at the first position */
 				return FALSE;
 			}
5ca7fd18
 			while ((n-- > 0) && (i < table_size)) {
da6a045e
 				table[i] = table[i-1];
5ca7fd18
 				i++;
 			}
 		} else {
 			if (number == 17) {
 				n = (rar_getbits(unpack_data) >> 13) + 3;
 				rar_addbits(unpack_data, 3);
 			} else {
 				n = (rar_getbits(unpack_data) >> 9) + 11;
 				rar_addbits(unpack_data, 7);
 			}
 			while ((n-- > 0) && (i < table_size)) {
 				table[i++] = 0;
 			}
 		}
 	}
 	if (unpack_data->in_addr > unpack_data->read_top) {
 		return TRUE;
 	}
 	if (unpack_data->unp_audio_block) {
 		for (i=0 ; i < unpack_data->unp_channels ; i++) {
 			rar_make_decode_tables(&table[i*MC20], (struct Decode *)&unpack_data->MD[i], MC20);
 		}
 	} else {
 		rar_make_decode_tables(&table[0], (struct Decode *)&unpack_data->LD, NC20);
 		rar_make_decode_tables(&table[NC20], (struct Decode *)&unpack_data->DD, DC20);
 		rar_make_decode_tables(&table[NC20+DC20], (struct Decode *)&unpack_data->RD, RC20);
 	}
 	memcpy(unpack_data->unp_old_table20, table, sizeof(unpack_data->unp_old_table20));
 	return TRUE;
 }
 
 static void read_last_tables(int fd, unpack_data_t *unpack_data)
 {
 	if (unpack_data->read_top >= unpack_data->in_addr+5) {
 		if (unpack_data->unp_audio_block) {
 			if (rar_decode_number(unpack_data,
 				(struct Decode *)&unpack_data->MD[unpack_data->unp_cur_channel]) == 256) {
 				read_tables20(fd, unpack_data);
 			}
 		} else if (rar_decode_number(unpack_data, (struct Decode *)&unpack_data->LD) == 269) {
 			read_tables20(fd, unpack_data);
 		}
 	}
 }
 
 static unsigned char decode_audio(unpack_data_t *unpack_data, int delta)
 {
 	struct AudioVariables *v;
 	int pch, d, i;
 	unsigned int ch, mindif, num_min_dif;
 	
 	v = &unpack_data->audv[unpack_data->unp_cur_channel];
 	v->byte_count++;
 	v->D4 = v->D3;
 	v->D3 = v->D2;
 	v->D2 = v->last_delta - v->D1;
 	v->D1 = v->last_delta;
 	
 	pch = 8 * v->last_char + v->K1 * v->D1 + v->K2 * v->D2 + v->K3 *
 		v->D3 + v->K4 * v->D4 + v->K5 * unpack_data->unp_channel_delta;
 	pch = (pch >> 3) & 0xff;
 	
 	ch = pch - delta;
 	
 	d = ((signed char) delta) << 3;
 	
 	v->dif[0] += abs(d);
 	v->dif[1] += abs(d - v->D1);
 	v->dif[2] += abs(d + v->D1);
 	v->dif[3] += abs(d - v->D2);
 	v->dif[4] += abs(d + v->D2);
 	v->dif[5] += abs(d - v->D3);
 	v->dif[6] += abs(d + v->D3);
 	v->dif[7] += abs(d - v->D4);
 	v->dif[8] += abs(d + v->D4);
 	v->dif[9] += abs(d - unpack_data->unp_channel_delta);
 	v->dif[10] += abs(d + unpack_data->unp_channel_delta);
 
 	unpack_data->unp_channel_delta = v->last_delta = (signed char) (ch - v->last_char);
 	v->last_char = ch;
 	
 	if ((v->byte_count & 0x1f) == 0) {
 		mindif = v->dif[0];
 		num_min_dif = 0;
 		v->dif[0] = 0;
 		for (i = 1 ; i < 11 ; i++) {
 			if (v->dif[i] < mindif) {
 				mindif = v->dif[i];
 				num_min_dif = i;
 			}
 			v->dif[i]=0; /* ?????? looks wrong to me */
 		}
 		switch(num_min_dif) {
 			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 ((unsigned char) ch);
 }
 
 int rar_unpack20(int fd, int solid, unpack_data_t *unpack_data)
 {
 	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};
 	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};
 	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};
 	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};
 	unsigned char sddecode[]={0,4,8,16,32,64,128,192};
 	unsigned char sdbits[]={2,2,3,4,5,6,6,6};
 	unsigned int bits, distance;
 	int retval=TRUE, audio_number, number, length, dist_number, length_number;
 	
 	rar_dbgmsg("in rar_unpack20\n");
 
 	rar_unpack_init_data(solid, unpack_data);
 	if (!rar_unp_read_buf(fd, unpack_data)) {
 		rar_dbgmsg("rar_unp_read_buf 1 failed\n");
 		return FALSE;
 	}
 	if (!solid) {
 		if (!read_tables20(fd, unpack_data)) {
 			rar_dbgmsg("read_tables20 failed\n");
 			return FALSE;
 		}
 	}
 	--unpack_data->dest_unp_size;
 	
 	while (unpack_data->dest_unp_size >= 0) {
 		rar_dbgmsg("dest_unp_size = %ld\n", unpack_data->dest_unp_size);
 		unpack_data->unp_ptr &= MAXWINMASK;
 		
 		if (unpack_data->in_addr > unpack_data->read_top-30) {
 			if (!rar_unp_read_buf(fd, unpack_data)) {
 				rar_dbgmsg("rar_unp_read_buf 2 failed\n");
 				break;
 			}
 		}
 		if (((unpack_data->wr_ptr - unpack_data->unp_ptr) & MAXWINMASK) < 270 &&
 				(unpack_data->wr_ptr != unpack_data->unp_ptr)) {
 			rar_unp_write_buf_old(unpack_data);
 		}
 		if (unpack_data->unp_audio_block) {
 			audio_number = rar_decode_number(unpack_data,
 				(struct Decode *)&unpack_data->MD[unpack_data->unp_cur_channel]);
 			if (audio_number == 256) {
 				if (!read_tables20(fd, unpack_data)) {
 					retval = FALSE;
 					break;
 				}
 				continue;
 			}
 			unpack_data->window[unpack_data->unp_ptr++] =
 					decode_audio(unpack_data, audio_number);
 			if (++unpack_data->unp_cur_channel == unpack_data->unp_channels) {
 				unpack_data->unp_cur_channel = 0;
 			}
 			--unpack_data->dest_unp_size;
 			continue;
 		}
 		
 		number = rar_decode_number(unpack_data, (struct Decode *)&unpack_data->LD);
 		if (number < 256) {
 			unpack_data->window[unpack_data->unp_ptr++] = (unsigned char) number;
 			--unpack_data->dest_unp_size;
 			continue;
 		}
 		if (number > 269) {
6d79990f
 			/* If number is higher or equal to 298 in this instance something has likely
 			 * gone horribly wrong and/or this is a RAR 5 file that Clam does not yet
 			 * support parsing. Either way, this is a total failure case. */
 			if (number < 298) {
 				length = ldecode[number-=270]+3;
 			} else {
 				retval = FALSE;
 				break;
 			}
 
5ca7fd18
 			if ((bits = lbits[number]) > 0) {
 				length += rar_getbits(unpack_data) >> (16-bits);
 				rar_addbits(unpack_data, bits);
 			}
 			
 			dist_number = rar_decode_number(unpack_data, (struct Decode *)&unpack_data->DD);
6d79990f
 			if (dist_number > 47 || dist_number < 0) {
 				retval = FALSE;
 				break;
 			}
5ca7fd18
 			distance = ddecode[dist_number] + 1;
 			if ((bits = dbits[dist_number]) > 0) {
 				distance += rar_getbits(unpack_data)>>(16-bits);
 				rar_addbits(unpack_data, bits);
 			}
 			
 			if (distance >= 0x2000) {
 				length++;
 				if (distance >= 0x40000L) {
 					length++;
 				}
 			}
 			
 			copy_string20(unpack_data, length, distance);
 			continue;
 		}
 		if (number == 269) {
 			if (!read_tables20(fd, unpack_data)) {
 				retval = FALSE;
 				break;
 			}
 			continue;
 		}
 		if (number == 256) {
 			copy_string20(unpack_data, unpack_data->last_length, unpack_data->last_dist);
 			continue;
 		}
 		if (number < 261) {
 			distance = unpack_data->old_dist[(unpack_data->old_dist_ptr-(number-256)) & 3];
 			length_number = rar_decode_number(unpack_data, (struct Decode *)&unpack_data->RD);
 			length = ldecode[length_number]+2;
 			if ((bits = lbits[length_number]) > 0) {
 				length += rar_getbits(unpack_data) >> (16-bits);
 				rar_addbits(unpack_data, bits);
 			}
 			if (distance >= 0x101) {
 				length++;
 				if (distance >= 0x2000) {
 					length++;
 					if (distance >= 0x40000) {
 						length++;
 					}
 				}
 			}
 			copy_string20(unpack_data, length, distance);
 			continue;
 		}
 		if (number < 270) {
 			distance = sddecode[number-=261]+1;
 			if ((bits=sdbits[number]) > 0) {
 				distance += rar_getbits(unpack_data) >> (16-bits);
 				rar_addbits(unpack_data, bits);
 			}
 			copy_string20(unpack_data, 2, distance);
 			continue;
 		}
 	}
 	if (retval) {
 		read_last_tables(fd, unpack_data);
 		rar_unp_write_buf_old(unpack_data);
 	}
 	return retval;
 }