libclamunrar/unrarppm.c
5ca7fd18
 /*
  *  Extract RAR archives
  *
  *  Copyright (C) 2005-2006 trog@uncon.org
  *
  *  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.
  */
 
081f6473
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
5ca7fd18
 #include <stdio.h>
 #include <string.h>
 
 #include "libclamunrar/unrar.h"
 #include "libclamunrar/unrarppm.h"
 
 #ifdef RAR_HIGH_DEBUG
 #define rar_dbgmsg printf
 #else
 static void rar_dbgmsg(const char* fmt,...){}
 #endif
 
 #define MAX_O 64
 
 const unsigned int UNIT_SIZE=MAX(sizeof(struct ppm_context), sizeof(struct rar_mem_blk_tag));
 const unsigned int FIXED_UNIT_SIZE=12;
 const int INT_BITS=7, PERIOD_BITS=7, TOT_BITS=14;
 const int INTERVAL=1 << 7, BIN_SCALE=1 << 14, MAX_FREQ=124;
 const unsigned int TOP=1 << 24, BOT=1 << 15;
 
 /************* Start of Allocator code block ********************/
 static void sub_allocator_init(sub_allocator_t *sub_alloc)
 {
 	sub_alloc->sub_allocator_size = 0;
 }
 
 static void sub_allocator_insert_node(sub_allocator_t *sub_alloc, void *p, int indx)
 {
 	((struct rar_node *) p)->next = sub_alloc->free_list[indx].next;
 	sub_alloc->free_list[indx].next = (struct rar_node *) p;
 }
 
 static void *sub_allocator_remove_node(sub_allocator_t *sub_alloc, int indx)
 {
 	struct rar_node *ret_val;
 	
 	ret_val = sub_alloc->free_list[indx].next;
 	sub_alloc->free_list[indx].next = ret_val->next;
 	return ret_val;
 }
 
 static int sub_allocator_u2b(int nu)
 {
 	return UNIT_SIZE*nu;
 }
 
 static rar_mem_blk_t* sub_allocator_mbptr(rar_mem_blk_t* base_ptr, int items)
 {
         return ((rar_mem_blk_t*) (((unsigned char *)(base_ptr)) + sub_allocator_u2b(items) ));
 }
 
 static void sub_allocator_split_block(sub_allocator_t *sub_alloc, void *pv,
 				int old_indx, int new_indx)
 {
 	int i, udiff;
 	uint8_t *p;
 	
 	udiff = sub_alloc->indx2units[old_indx] - sub_alloc->indx2units[new_indx];
 	p = ((uint8_t *) pv) + sub_allocator_u2b(sub_alloc->indx2units[new_indx]);
 	if (sub_alloc->indx2units[i=sub_alloc->units2indx[udiff-1]] != udiff) {
 		sub_allocator_insert_node(sub_alloc, p, --i);
 		p += sub_allocator_u2b(i=sub_alloc->indx2units[i]);
 		udiff -= i;
 	}
 	sub_allocator_insert_node(sub_alloc, p, sub_alloc->units2indx[udiff-1]);
 }
 
 static long sub_allocator_get_allocated_memory(sub_allocator_t *sub_alloc)
 {
 	return sub_alloc->sub_allocator_size;
 }
 
 static void sub_allocator_stop_sub_allocator(sub_allocator_t *sub_alloc)
 {
 	if (sub_alloc->sub_allocator_size) {
 		sub_alloc->sub_allocator_size = 0;
 		free(sub_alloc->heap_start);
 	}
 }
 
 static int sub_allocator_start_sub_allocator(sub_allocator_t *sub_alloc, int sa_size)
 {
 	unsigned int t, alloc_size;
 	
 	t = sa_size << 20;
 	if (sub_alloc->sub_allocator_size == t) {
 		return TRUE;
 	}
 	sub_allocator_stop_sub_allocator(sub_alloc);
fee30f6e
 	if (t>138412020) {
 		rar_dbgmsg("too much memory needed for uncompressing this file\n");
 		return FALSE;
 	}
5ca7fd18
 	alloc_size = t/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE;
 #if defined(__sparc) || defined(sparc) || defined(__sparcv9)
 	/* Allow for aligned access requirements */
 	alloc_size += UNIT_SIZE;
 #endif
fee30f6e
 	if ((sub_alloc->heap_start = (uint8_t *) malloc(alloc_size)) == NULL) {
5ca7fd18
 		rar_dbgmsg("sub_alloc start failed\n");
 		return FALSE;
 	}
 	sub_alloc->heap_end = sub_alloc->heap_start + alloc_size - UNIT_SIZE;
 	sub_alloc->sub_allocator_size = t;
 	return TRUE;
 }
 
 static void sub_allocator_init_sub_allocator(sub_allocator_t *sub_alloc)
 {
 	int i, k;
 	unsigned int size1, real_size1, size2, real_size2;
 
 	memset(sub_alloc->free_list, 0, sizeof(sub_alloc->free_list));
 	sub_alloc->ptext = sub_alloc->heap_start;
 	
 	size2 = FIXED_UNIT_SIZE*(sub_alloc->sub_allocator_size/8/FIXED_UNIT_SIZE*7);
 	real_size2 = size2/FIXED_UNIT_SIZE*UNIT_SIZE;
 	size1 = sub_alloc->sub_allocator_size - size2;
 	real_size1 = size1/FIXED_UNIT_SIZE*UNIT_SIZE+size1%FIXED_UNIT_SIZE;
 #if defined(__sparc) || defined(sparc) || defined(__sparcv9)
 	/* Allow for aligned access requirements */
 	if (size1%FIXED_UNIT_SIZE != 0) {
 		real_size1 += UNIT_SIZE - size1%FIXED_UNIT_SIZE;
 	}
 #endif
 	sub_alloc->hi_unit = sub_alloc->heap_start + sub_alloc->sub_allocator_size;
 	sub_alloc->lo_unit = sub_alloc->units_start = sub_alloc->heap_start + real_size1;
 	sub_alloc->fake_units_start = sub_alloc->heap_start + size1;
 	sub_alloc->hi_unit = sub_alloc->lo_unit + real_size2;
 	
 	for (i=0,k=1; i < N1 ; i++, k+=1) {
 		sub_alloc->indx2units[i] = k;
 	}
 	for (k++; i < N1+N2 ; i++, k+=2) {
 		sub_alloc->indx2units[i] = k;
 	}
 	for (k++; i < N1+N2+N3 ; i++, k+=3) {
 		sub_alloc->indx2units[i] = k;
 	}
 	for (k++; i < N1+N2+N3+N4 ; i++, k+=4) {
 		sub_alloc->indx2units[i] = k;
 	}
 	
 	for (sub_alloc->glue_count=k=i=0; k < 128; k++) {
 		i += (sub_alloc->indx2units[i] < k+1);
 		sub_alloc->units2indx[k] = i;
 	}
 }
 
 static void rar_mem_blk_insertAt(rar_mem_blk_t *a, rar_mem_blk_t *p)
 {
 	a->next = (a->prev=p)->next;
 	p->next = a->next->prev = a;
 }
 
 static void rar_mem_blk_remove(rar_mem_blk_t *a)
 {
 	a->prev->next = a->next;
 	a->next->prev = a->prev;
 }
 
 static void sub_allocator_glue_free_blocks(sub_allocator_t *sub_alloc)
 {
 	rar_mem_blk_t s0, *p, *p1;
 	int i, k, sz;
 	
 	if (sub_alloc->lo_unit != sub_alloc->hi_unit) {
 		*sub_alloc->lo_unit = 0;
 	}
 	for (i=0, s0.next=s0.prev=&s0; i < N_INDEXES; i++) {
 		while (sub_alloc->free_list[i].next) {
 			p = (rar_mem_blk_t *) sub_allocator_remove_node(sub_alloc, i);
 			rar_mem_blk_insertAt(p, &s0);
 			p->stamp = 0xFFFF;
 			p->nu = sub_alloc->indx2units[i];
 		}
 	}
 	
 	for (p=s0.next ; p != &s0 ; p=p->next) {
 		while ((p1 = sub_allocator_mbptr(p,p->nu))->stamp == 0xFFFF &&
 				((int)p->nu)+p1->nu < 0x10000) {
 			rar_mem_blk_remove(p1);
 			p->nu += p1->nu;
 		}
 	}
 	
 	while ((p=s0.next) != &s0) {
 		for (rar_mem_blk_remove(p), sz=p->nu; sz > 128; sz-=128, p=sub_allocator_mbptr(p, 128)) {
 			sub_allocator_insert_node(sub_alloc, p, N_INDEXES-1);
 		}
 		if (sub_alloc->indx2units[i=sub_alloc->units2indx[sz-1]] != sz) {
 			k = sz-sub_alloc->indx2units[--i];
 			sub_allocator_insert_node(sub_alloc, sub_allocator_mbptr(p,sz-k), k-1);
 		}
 		sub_allocator_insert_node(sub_alloc, p, i);
 	}
 }
 
 static void *sub_allocator_alloc_units_rare(sub_allocator_t *sub_alloc, int indx)
 {
 	int i, j;
 	void *ret_val;
 	
 	if (!sub_alloc->glue_count) {
 		sub_alloc->glue_count = 255;
 		sub_allocator_glue_free_blocks(sub_alloc);
 		if (sub_alloc->free_list[indx].next) {
 			return sub_allocator_remove_node(sub_alloc, indx);
 		}
 	}
 	i=indx;
 	do {
 		if (++i == N_INDEXES) {
 			sub_alloc->glue_count--;
 			i = sub_allocator_u2b(sub_alloc->indx2units[indx]);
 			j = 12 * sub_alloc->indx2units[indx];
 			if (sub_alloc->fake_units_start - sub_alloc->ptext > j) {
 				sub_alloc->fake_units_start -= j;
 				sub_alloc->units_start -= i;
 				return sub_alloc->units_start;
 			}
 			return NULL;
 		}
 	} while ( !sub_alloc->free_list[i].next);
 	ret_val = sub_allocator_remove_node(sub_alloc, i);
 	sub_allocator_split_block(sub_alloc, ret_val, i, indx);
 	return ret_val;
 }
 
 static void *sub_allocator_alloc_units(sub_allocator_t *sub_alloc, int nu)
 {
 	int indx;
 	void *ret_val;
 	
 	indx = sub_alloc->units2indx[nu-1];
 	if (sub_alloc->free_list[indx].next) {
 		return sub_allocator_remove_node(sub_alloc, indx);
 	}
 	ret_val = sub_alloc->lo_unit;
 	sub_alloc->lo_unit += sub_allocator_u2b(sub_alloc->indx2units[indx]);
 	if (sub_alloc->lo_unit <= sub_alloc->hi_unit) {
 		return ret_val;
 	}
 	sub_alloc->lo_unit -= sub_allocator_u2b(sub_alloc->indx2units[indx]);
 	return sub_allocator_alloc_units_rare(sub_alloc, indx);
 }
 
 static void *sub_allocator_alloc_context(sub_allocator_t *sub_alloc)
 {
 	if (sub_alloc->hi_unit != sub_alloc->lo_unit) {
 		return (sub_alloc->hi_unit -= UNIT_SIZE);
 	}
 	if (sub_alloc->free_list->next) {
 		return sub_allocator_remove_node(sub_alloc, 0);
 	}
 	return sub_allocator_alloc_units_rare(sub_alloc, 0);
 }
 
 static void *sub_allocator_expand_units(sub_allocator_t *sub_alloc, void *old_ptr, int old_nu)
 {
 	int i0, i1;
 	void *ptr;
 	
 	i0 = sub_alloc->units2indx[old_nu-1];
 	i1 = sub_alloc->units2indx[old_nu];
 	if (i0 == i1) {
 		return old_ptr;
 	}
 	ptr = sub_allocator_alloc_units(sub_alloc, old_nu+1);
 	if (ptr) {
 		memcpy(ptr, old_ptr, sub_allocator_u2b(old_nu));
 		sub_allocator_insert_node(sub_alloc, old_ptr, i0);
 	}
 	return ptr;
 }
 
 static void *sub_allocator_shrink_units(sub_allocator_t *sub_alloc, void *old_ptr,
 			int old_nu, int new_nu)
 {
 	int i0, i1;
 	void *ptr;
 	
 	i0 = sub_alloc->units2indx[old_nu-1];
 	i1 = sub_alloc->units2indx[new_nu-1];
 	if (i0 == i1) {
 		return old_ptr;
 	}
 	if (sub_alloc->free_list[i1].next) {
 		ptr = sub_allocator_remove_node(sub_alloc, i1);
 		memcpy(ptr, old_ptr, sub_allocator_u2b(new_nu));
 		sub_allocator_insert_node(sub_alloc, old_ptr, i0);
 		return ptr;
 	} else {
 		sub_allocator_split_block(sub_alloc, old_ptr, i0, i1);
 		return old_ptr;
 	}
 }
 
 static void  sub_allocator_free_units(sub_allocator_t *sub_alloc, void *ptr, int old_nu)
 {
 	sub_allocator_insert_node(sub_alloc, ptr, sub_alloc->units2indx[old_nu-1]);
 }
 
 /************** End of Allocator code block *********************/
 
 /************** Start of Range Coder code block *********************/
 static void range_coder_init_decoder(range_coder_t *coder, int fd,
 			unpack_data_t *unpack_data)
 {
 	int i;
 	coder->low = coder->code = 0;
 	coder->range = (unsigned int) -1;
 	
 	for (i=0; i < 4 ; i++) {
 		coder->code = (coder->code << 8) | rar_get_char(fd, unpack_data);
 	}
 }
 
 static int coder_get_current_count(range_coder_t *coder)
 {
 	return (coder->code - coder->low) / (coder->range /= coder->scale);
 }
 
 static unsigned int  coder_get_current_shift_count(range_coder_t *coder, unsigned int shift)
 {
 	return (coder->code - coder->low) / (coder->range >>= shift);
 }
 
 #define ARI_DEC_NORMALISE(fd, unpack_data, code, low, range)					\
 {												\
 	while ((low^(low+range)) < TOP || (range < BOT && ((range=-low&(BOT-1)),1))) {		\
 		code = (code << 8) | rar_get_char(fd, unpack_data);				\
 		range <<= 8;									\
 		low <<= 8;									\
 	}											\
 }
 
 static void coder_decode(range_coder_t *coder)
 {
 	coder->low += coder->range * coder->low_count;
 	coder->range *= coder->high_count - coder->low_count;
 }
 
 /******(******** End of Range Coder code block ***********(**********/
 
 static void see2_init(struct see2_context_tag *see2_cont, int init_val)
 {
 	see2_cont->summ = init_val << (see2_cont->shift=PERIOD_BITS-4);
 	see2_cont->count = 4;
 }
 
 static unsigned int get_mean(struct see2_context_tag *see2_cont)
 {
 	unsigned int ret_val;
 	
 	ret_val = see2_cont->summ >> see2_cont->shift;
 	see2_cont->summ -= ret_val;
 	return ret_val + (ret_val == 0);
 }
 
 static void update(struct see2_context_tag *see2_cont)
 {
 	if (see2_cont->shift < PERIOD_BITS && --see2_cont->count == 0) {
 		see2_cont->summ += see2_cont->summ;
 		see2_cont->count = 3 << see2_cont->shift++;
 	}
 }
 
 static int restart_model_rare(ppm_data_t *ppm_data)
 {
 	int i, k, m;
 	static const uint16_t init_bin_esc[] = {
 		0x3cdd, 0x1f3f, 0x59bf, 0x48f3, 0x64a1, 0x5abc, 0x6632, 0x6051
 	};
 	rar_dbgmsg("in restart_model_rare\n");
 	memset(ppm_data->char_mask, 0, sizeof(ppm_data->char_mask));
 	
 	sub_allocator_init_sub_allocator(&ppm_data->sub_alloc);
 	
 	ppm_data->init_rl=-(ppm_data->max_order < 12 ? ppm_data->max_order:12)-1;
 	ppm_data->min_context = ppm_data->max_context =
 		(struct ppm_context *) sub_allocator_alloc_context(&ppm_data->sub_alloc);
 	if(!ppm_data->min_context) {
 	    rar_dbgmsg("unrar: restart_model_rare: sub_allocator_alloc_context failed\n"); /* FIXME: cli_errmsg */
 	    return FALSE;
 	}
 	ppm_data->min_context->suffix = NULL;
 	ppm_data->order_fall = ppm_data->max_order;
 	ppm_data->min_context->con_ut.u.summ_freq = (ppm_data->min_context->num_stats=256)+1;
 	ppm_data->found_state = ppm_data->min_context->con_ut.u.stats=
 		(struct state_tag *)sub_allocator_alloc_units(&ppm_data->sub_alloc, 256/2);
 	if(!ppm_data->found_state) {
 	    rar_dbgmsg("unrar: restart_model_rare: sub_allocator_alloc_units failed\n"); /* FIXME: cli_errmsg */
 	    return FALSE;
 	}
 	for (ppm_data->run_length = ppm_data->init_rl, ppm_data->prev_success=i=0; i < 256 ; i++) {
 		ppm_data->min_context->con_ut.u.stats[i].symbol = i;
 		ppm_data->min_context->con_ut.u.stats[i].freq = 1;
 		ppm_data->min_context->con_ut.u.stats[i].successor = NULL;
 	}
 	
 	for (i=0 ; i < 128 ; i++) {
 		for (k=0 ; k < 8 ; k++) {
 			for (m=0 ; m < 64 ; m+=8) {
 				ppm_data->bin_summ[i][k+m]=BIN_SCALE-init_bin_esc[k]/(i+2);
 			}
 		}
 	}
 	for (i=0; i < 25; i++) {
 		for (k=0 ; k < 16 ; k++) {
 			see2_init(&ppm_data->see2cont[i][k], 5*i+10);
 		}
 	}
 
 	return TRUE;
 }
 	
 static int start_model_rare(ppm_data_t *ppm_data, int max_order)
 {
7959343d
 	int i, k, m, step;
5ca7fd18
 	
 	ppm_data->esc_count = 1;
 	ppm_data->max_order = max_order;
 	
 	if (!restart_model_rare(ppm_data)) {
 	    rar_dbgmsg("unrar: start_model_rare: restart_model_rare failed\n");
 	    return FALSE;
 	}
 	
 	ppm_data->ns2bsindx[0] = 2*0;
 	ppm_data->ns2bsindx[1] = 2*1;
 	
 	memset(ppm_data->ns2bsindx+2, 2*2, 9);
 	memset(ppm_data->ns2bsindx+11, 2*3, 256-11);
 	
 	for (i=0 ; i < 3; i++) {
 		ppm_data->ns2indx[i] = i;
 	}
 	for (m=i, k=step=1; i < 256; i++) {
 		ppm_data->ns2indx[i]=m;
 		if (!--k) {
 			k = ++step;
 			m++;
 		}
 	}
 	memset(ppm_data->hb2flag, 0, 0x40);
 	memset(ppm_data->hb2flag+0x40, 0x08, 0x100-0x40);
 	ppm_data->dummy_sse2cont.shift = PERIOD_BITS;
 	return TRUE;
 }
 
 	
 /* ****************** PPM Code ***************/
 
 static void ppmd_swap(struct state_tag *p0, struct state_tag *p1)
 {
 	struct state_tag tmp;
 	
 	tmp = *p0;
 	*p0 = *p1;
 	*p1 = tmp;
 }
 
 static void rescale(ppm_data_t *ppm_data, struct ppm_context *context)
 {
 	int old_ns, i, adder, esc_freq, n0, n1;
 	struct state_tag *p1, *p;
 	
 	rar_dbgmsg("in rescale\n");
 	old_ns = context->num_stats;
 	i = context->num_stats-1;
 	
 	for (p=ppm_data->found_state ; p != context->con_ut.u.stats ; p--) {
 		ppmd_swap(&p[0], &p[-1]);
 	}
 	context->con_ut.u.stats->freq += 4;
 	context->con_ut.u.summ_freq += 4;
 	esc_freq = context->con_ut.u.summ_freq - p->freq;
 	adder = (ppm_data->order_fall != 0);
 	context->con_ut.u.summ_freq = (p->freq = (p->freq+adder) >> 1);
 	do {
 		esc_freq -= (++p)->freq;
 		context->con_ut.u.summ_freq += (p->freq = (p->freq + adder) >> 1);
 		if (p[0].freq > p[-1].freq) {
 			struct state_tag tmp = *(p1=p);
 			do {
 				p1[0] = p1[-1];
 			} while (--p1 != context->con_ut.u.stats && tmp.freq > p1[-1].freq);
 			*p1 = tmp;
 		}
 	} while (--i);
 	
 	if (p->freq == 0) {
 		do {
 			i++;
 		} while ((--p)->freq == 0);
 		esc_freq += i;
 		if ((context->num_stats -= i) == 1) {
 			struct state_tag tmp = *context->con_ut.u.stats;
 			do {
 				tmp.freq -= (tmp.freq >> 1);
 				esc_freq >>= 1;
 			} while (esc_freq > 1);
 			sub_allocator_free_units(&ppm_data->sub_alloc,
 					context->con_ut.u.stats, (old_ns+1)>>1);
 			*(ppm_data->found_state=&context->con_ut.one_state)=tmp;
 			return;
 		}
 	}
 	context->con_ut.u.summ_freq += (esc_freq -= (esc_freq >> 1));
 	n0 = (old_ns+1) >> 1;
 	n1 = (context->num_stats+1) >> 1;
 	if (n0 != n1) {
 		context->con_ut.u.stats = (struct state_tag *) sub_allocator_shrink_units(&ppm_data->sub_alloc,
 						context->con_ut.u.stats, n0, n1);
 	}
 	ppm_data->found_state = context->con_ut.u.stats;
 }
 
 static struct ppm_context *create_child(ppm_data_t *ppm_data, struct ppm_context *context,
 				struct state_tag *pstats, struct state_tag *first_state)
 {
 	struct ppm_context *pc;
 	rar_dbgmsg("in create_child\n");
 	pc = (struct ppm_context *) sub_allocator_alloc_context(&ppm_data->sub_alloc);
 	if (pc) {
 		pc->num_stats = 1;
 		pc->con_ut.one_state = *first_state;
 		pc->suffix = context;
 		pstats->successor = pc;
 	}
 	return pc;
 }
 
 static struct ppm_context *create_successors(ppm_data_t *ppm_data,
 			int skip, struct state_tag *p1)
 {
 	struct state_tag up_state;
 	struct ppm_context *pc, *up_branch;
 	struct state_tag *p, *ps[MAX_O], **pps;
 	unsigned int cf, s0;
 	
 	rar_dbgmsg("in create_successors\n");
 	pc = ppm_data->min_context;
 	up_branch = ppm_data->found_state->successor;
 	pps = ps;
 	
 	if (!skip) {
 		*pps++ = ppm_data->found_state;
 		if (!pc->suffix) {
 			goto NO_LOOP;
 		}
 	}
 	if (p1) {
 		p = p1;
 		pc = pc->suffix;
 		goto LOOP_ENTRY;
 	}
 	do {
 		pc = pc->suffix;
 		if (pc->num_stats != 1) {
 			if ((p=pc->con_ut.u.stats)->symbol != ppm_data->found_state->symbol) {
 				do {
 					p++;
 				} while (p->symbol != ppm_data->found_state->symbol);
 			}
 		} else {
 			p = &(pc->con_ut.one_state);
 		}
 LOOP_ENTRY:
 		if (p->successor != up_branch) {
 			pc = p->successor;
 			break;
 		}
 		*pps++ = p;
 	} while (pc->suffix);
 NO_LOOP:
 	if (pps == ps) {
 		return pc;
 	}
 	up_state.symbol= *(uint8_t *) up_branch;
 	up_state.successor = (struct ppm_context *) (((uint8_t *) up_branch)+1);
 	if (pc->num_stats != 1) {
 		if ((uint8_t *) pc <= ppm_data->sub_alloc.ptext) {
 			return NULL;
 		}
 		if ((p=pc->con_ut.u.stats)->symbol != up_state.symbol) {
 			do {
 				p++;
d076ad02
 				if ((void *)p > (void *) ppm_data->sub_alloc.heap_end) {
 					return NULL;
 				}
5ca7fd18
 			} while (p->symbol != up_state.symbol);
 		}
 		cf = p->freq - 1;
 		s0 = pc->con_ut.u.summ_freq - pc->num_stats - cf;
 		up_state.freq = 1 + ((2*cf <= s0)?(5*cf > s0):((2*cf+3*s0-1)/(2*s0)));
 	} else {
 		up_state.freq = pc->con_ut.one_state.freq;
 	}
 	do {
 		pc = create_child(ppm_data, pc, *--pps, &up_state);
 		if (!pc) {
 			rar_dbgmsg("create_child failed\n");
 			return NULL;
 		}
 	} while (pps != ps);
 	return pc;
 }
 
 static int update_model(ppm_data_t *ppm_data)
 {
 	struct state_tag fs, *p;
 	struct ppm_context *pc, *successor;
 	unsigned int ns1, ns, cf, sf, s0;
7959343d
 
5ca7fd18
 	rar_dbgmsg("in update_model\n");
 	fs = *ppm_data->found_state;
 	p = NULL;
 
 	if (fs.freq < MAX_FREQ/4 && (pc=ppm_data->min_context->suffix) != NULL) {
 		if (pc->num_stats != 1) {
 			if ((p=pc->con_ut.u.stats)->symbol != fs.symbol) {
 				do {
 					p++;
 				} while (p->symbol != fs.symbol);
 				if (p[0].freq >= p[-1].freq) {
 					ppmd_swap(&p[0], &p[-1]);
 					p--;
 				}
 			}
 			if (p->freq < MAX_FREQ-9) {
 				p->freq += 2;
 				pc->con_ut.u.summ_freq += 2;
 			}
 		} else {
 			p = &(pc->con_ut.one_state);
 			p->freq += (p->freq < 32);
 		}
 	}
 	if (!ppm_data->order_fall) {
 		ppm_data->min_context = ppm_data->max_context =
 			ppm_data->found_state->successor = create_successors(ppm_data, TRUE, p);
 		if (!ppm_data->min_context) {
 			goto RESTART_MODEL;
 		}
 		return TRUE;
 	}
 	*ppm_data->sub_alloc.ptext++ = fs.symbol;
 	successor = (struct ppm_context *) ppm_data->sub_alloc.ptext;
 	if (ppm_data->sub_alloc.ptext >= ppm_data->sub_alloc.fake_units_start) {
 		goto RESTART_MODEL;
 	}
 	if (fs.successor) {
 		if ((uint8_t *)fs.successor <= ppm_data->sub_alloc.ptext &&
 				(fs.successor = create_successors(ppm_data, FALSE, p)) == NULL) {
 			goto RESTART_MODEL;
 		}
 		if (!--ppm_data->order_fall) {
 			successor = fs.successor;
 			ppm_data->sub_alloc.ptext -= (ppm_data->max_context != ppm_data->min_context);
 		}
 	} else {
 		ppm_data->found_state->successor = successor;
 		fs.successor = ppm_data->min_context;
 	}
 	s0 = ppm_data->min_context->con_ut.u.summ_freq-(ns=ppm_data->min_context->num_stats)-(fs.freq-1);
 	for (pc=ppm_data->max_context; pc != ppm_data->min_context ; pc=pc->suffix) {
 		if ((ns1=pc->num_stats) != 1) {
 			if ((ns1 & 1) == 0) {
 				pc->con_ut.u.stats = (struct state_tag *)
 					sub_allocator_expand_units(&ppm_data->sub_alloc,
 								pc->con_ut.u.stats, ns1>>1);
 				if (!pc->con_ut.u.stats) {
 					goto RESTART_MODEL;
 				}
 			}
 			pc->con_ut.u.summ_freq += (2*ns1 < ns)+2*((4*ns1 <= ns) & (pc->con_ut.u.summ_freq <= 8*ns1));
 		} else {
 			p = (struct state_tag *) sub_allocator_alloc_units(&ppm_data->sub_alloc, 1);
 			if (!p) {
 				goto RESTART_MODEL;
 			}
 			*p = pc->con_ut.one_state;
 			pc->con_ut.u.stats = p;
 			if (p->freq < MAX_FREQ/4-1) {
 				p->freq += p->freq;
 			} else {
 				p->freq = MAX_FREQ - 4;
 			}
 			pc->con_ut.u.summ_freq = p->freq + ppm_data->init_esc + (ns > 3);
 		}
 		cf = 2*fs.freq*(pc->con_ut.u.summ_freq+6);
 		sf = s0 + pc->con_ut.u.summ_freq;
 		if (cf < 6*sf) {
 			cf = 1 + (cf > sf) + (cf >= 4*sf);
 			pc->con_ut.u.summ_freq += 3;
 		} else {
 			cf = 4 + (cf >= 9*sf) + (cf >= 12*sf) + (cf >= 15*sf);
 			pc->con_ut.u.summ_freq += cf;
 		}
 		p = pc->con_ut.u.stats + ns1;
 		p->successor = successor;
 		p->symbol = fs.symbol;
 		p->freq = cf;
 		pc->num_stats = ++ns1;
 	}
 	ppm_data->max_context = ppm_data->min_context = fs.successor;
 	return TRUE;
 	
 RESTART_MODEL:
 	if (!restart_model_rare(ppm_data)) {
 	    rar_dbgmsg("unrar: update_model: restart_model_rare: failed\n");
 	    return FALSE;
 	}
 	ppm_data->esc_count = 0;
 	return TRUE;
 }
 
 static void update1(ppm_data_t *ppm_data, struct state_tag *p, struct ppm_context *context)
 {
 	rar_dbgmsg("in update1\n");
 	(ppm_data->found_state=p)->freq += 4;
 	context->con_ut.u.summ_freq += 4;
 	if (p[0].freq > p[-1].freq) {
 		ppmd_swap(&p[0], &p[-1]);
 		ppm_data->found_state = --p;
 		if (p->freq > MAX_FREQ) {
 			rescale(ppm_data, context);
 		}
 	}
 }
 
 static int ppm_decode_symbol1(ppm_data_t *ppm_data, struct ppm_context *context)
 {
 	struct state_tag *p;
 	int i, hi_cnt, count;
 	
 	rar_dbgmsg("in ppm_decode_symbol1\n");
 	ppm_data->coder.scale = context->con_ut.u.summ_freq;
 	p = context->con_ut.u.stats;
 	count = coder_get_current_count(&ppm_data->coder);
 	if (count >= ppm_data->coder.scale) {
 		return FALSE;
 	}
 	if (count < (hi_cnt = p->freq)) {
 		ppm_data->prev_success = (2 * (ppm_data->coder.high_count=hi_cnt) >
 						ppm_data->coder.scale);
 		ppm_data->run_length += ppm_data->prev_success;
 		(ppm_data->found_state=p)->freq=(hi_cnt += 4);
 		context->con_ut.u.summ_freq += 4;
 		if (hi_cnt > MAX_FREQ) {
 			rescale(ppm_data, context);
 		}
 		ppm_data->coder.low_count = 0;
 		return TRUE;
 	} else if (ppm_data->found_state == NULL) {
 		return FALSE;
 	}
 	ppm_data->prev_success = 0;
 	i = context->num_stats-1;
 	while ((hi_cnt += (++p)->freq) <= count) {
 		if (--i == 0) {
 			ppm_data->hi_bits_flag = ppm_data->hb2flag[ppm_data->found_state->symbol];
 			ppm_data->coder.low_count = hi_cnt;
 			ppm_data->char_mask[p->symbol] = ppm_data->esc_count;
 			i = (ppm_data->num_masked=context->num_stats) - 1;
 			ppm_data->found_state = NULL;
 			do {
 				ppm_data->char_mask[(--p)->symbol] = ppm_data->esc_count;
 			} while (--i);
 			ppm_data->coder.high_count = ppm_data->coder.scale;
 			return TRUE;
 		}
 	}
 	ppm_data->coder.low_count = (ppm_data->coder.high_count = hi_cnt) - p->freq;
 	update1(ppm_data, p, context);
 	return TRUE;
 }
 
 static const uint8_t ExpEscape[16]={ 25,14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
 #define GET_MEAN(SUMM,SHIFT,ROUND) ((SUMM+(1 << (SHIFT-ROUND))) >> (SHIFT))
 
 static void ppm_decode_bin_symbol(ppm_data_t *ppm_data, struct ppm_context *context)
 {
 	struct state_tag *rs;
 	uint16_t *bs;
 	
 	rar_dbgmsg("in ppm_decode_bin_symbol\n");
 	
 	rs = &context->con_ut.one_state;
 	
 	ppm_data->hi_bits_flag = ppm_data->hb2flag[ppm_data->found_state->symbol];
 	bs = &ppm_data->bin_summ[rs->freq-1][ppm_data->prev_success +
 		ppm_data->ns2bsindx[context->suffix->num_stats-1] +
 		ppm_data->hi_bits_flag+2*ppm_data->hb2flag[rs->symbol] +
 		((ppm_data->run_length >> 26) & 0x20)];
 	if (coder_get_current_shift_count(&ppm_data->coder, TOT_BITS) < *bs) {
 		ppm_data->found_state = rs;
 		rs->freq += (rs->freq < 128);
 		ppm_data->coder.low_count = 0;
 		ppm_data->coder.high_count = *bs;
 		*bs = (uint16_t) (*bs + INTERVAL - GET_MEAN(*bs, PERIOD_BITS, 2));
 		ppm_data->prev_success = 1;
 		ppm_data->run_length++;
 	} else {
 		ppm_data->coder.low_count = *bs;
 		*bs = (uint16_t) (*bs - GET_MEAN(*bs, PERIOD_BITS, 2));
 		ppm_data->coder.high_count = BIN_SCALE;
 		ppm_data->init_esc = ExpEscape[*bs >> 10];
 		ppm_data->num_masked = 1;
 		ppm_data->char_mask[rs->symbol] = ppm_data->esc_count;
 		ppm_data->prev_success = 0;
 		ppm_data->found_state = NULL;
 	}
 }
 
 static void update2(ppm_data_t *ppm_data, struct state_tag *p, struct ppm_context *context)
 {
 	rar_dbgmsg("in update2\n");
 	(ppm_data->found_state = p)->freq += 4;
 	context->con_ut.u.summ_freq += 4;
 	if (p->freq > MAX_FREQ) {
 		rescale(ppm_data, context);
 	}
 	ppm_data->esc_count++;
 	ppm_data->run_length = ppm_data->init_rl;
 }
 
 static struct see2_context_tag *make_esc_freq(ppm_data_t *ppm_data,
 			struct ppm_context *context, int diff)
 {
 	struct see2_context_tag *psee2c;
 	
 	if (context->num_stats != 256) {
 		psee2c = ppm_data->see2cont[ppm_data->ns2indx[diff-1]] +
 			(diff < context->suffix->num_stats-context->num_stats) +
 			2 * (context->con_ut.u.summ_freq < 11*context->num_stats)+4*
 			(ppm_data->num_masked > diff) +	ppm_data->hi_bits_flag;
 		ppm_data->coder.scale = get_mean(psee2c);
 	} else {
 		psee2c = &ppm_data->dummy_sse2cont;
 		ppm_data->coder.scale = 1;
 	}
 	return psee2c;
 }
 
 static int ppm_decode_symbol2(ppm_data_t *ppm_data, struct ppm_context *context)
 {
 	int count, hi_cnt, i;
 	struct see2_context_tag *psee2c;
 	struct state_tag *ps[256], **pps, *p;
 	
 	rar_dbgmsg("in ppm_decode_symbol2\n");
 	i = context->num_stats - ppm_data->num_masked;
 	psee2c = make_esc_freq(ppm_data, context, i);
 	pps = ps;
 	p = context->con_ut.u.stats - 1;
 	hi_cnt = 0;
 	
 	do {
 		do {
 			p++;
 		} while (ppm_data->char_mask[p->symbol] == ppm_data->esc_count);
 		hi_cnt += p->freq;
 		*pps++ = p;
 	} while (--i);
 	ppm_data->coder.scale += hi_cnt;
 	count = coder_get_current_count(&ppm_data->coder);
 	if (count >= ppm_data->coder.scale) {
 		return FALSE;
 	}
 	p=*(pps=ps);
 	if (count < hi_cnt) {
 		hi_cnt = 0;
 		while ((hi_cnt += p->freq) <= count) {
 			p=*++pps;
 		}
 		ppm_data->coder.low_count = (ppm_data->coder.high_count=hi_cnt) - p->freq;
 		update(psee2c);
 		update2(ppm_data, p, context);
 	} else {
 		ppm_data->coder.low_count = hi_cnt;
 		ppm_data->coder.high_count = ppm_data->coder.scale;
 		i = context->num_stats - ppm_data->num_masked;
 		pps--;
 		do {
 			ppm_data->char_mask[(*++pps)->symbol] = ppm_data->esc_count;
 		} while (--i);
 		psee2c->summ += ppm_data->coder.scale;
 		ppm_data->num_masked = context->num_stats;
 	}
 	return TRUE;
 }
 
 static void clear_mask(ppm_data_t *ppm_data)
 {
 	ppm_data->esc_count = 1;
 	memset(ppm_data->char_mask, 0, sizeof(ppm_data->char_mask));
 }
 
 void ppm_constructor(ppm_data_t *ppm_data)
 {
 	sub_allocator_init(&ppm_data->sub_alloc);
 	ppm_data->min_context = NULL;
 	ppm_data->max_context = NULL;
 }
 
 void ppm_destructor(ppm_data_t *ppm_data)
 {
 	sub_allocator_stop_sub_allocator(&ppm_data->sub_alloc);
 }
 
d076ad02
 void ppm_cleanup(ppm_data_t *ppm_data)
 {
 	sub_allocator_stop_sub_allocator(&ppm_data->sub_alloc);
 	sub_allocator_start_sub_allocator(&ppm_data->sub_alloc, 1);
 	start_model_rare(ppm_data, 2);
 }
 
5ca7fd18
 int ppm_decode_init(ppm_data_t *ppm_data, int fd, unpack_data_t *unpack_data, int *EscChar)
 {
 	int max_order, Reset, MaxMB;
 	
 	max_order = rar_get_char(fd, unpack_data);
 	rar_dbgmsg("ppm_decode_init max_order=%d\n", max_order);
 	Reset = (max_order & 0x20) ? 1 : 0;
 	rar_dbgmsg("ppm_decode_init Reset=%d\n", Reset);
 	if (Reset) {
 		MaxMB = rar_get_char(fd, unpack_data);
 		rar_dbgmsg("ppm_decode_init MaxMB=%d\n", MaxMB);
 	} else {
 		if (sub_allocator_get_allocated_memory(&ppm_data->sub_alloc) == 0) {
 			return FALSE;
 		}
 	}
 	if (max_order & 0x40) {
 		*EscChar = rar_get_char(fd, unpack_data);
 		rar_dbgmsg("ppm_decode_init EscChar=%d\n", *EscChar);
 	}
 	range_coder_init_decoder(&ppm_data->coder, fd, unpack_data);
 	if (Reset) {
 		max_order = (max_order & 0x1f) + 1;
 		if (max_order > 16) {
 			max_order = 16 + (max_order - 16) * 3;
 		}
 		if (max_order == 1) {
 			sub_allocator_stop_sub_allocator(&ppm_data->sub_alloc);
 			return FALSE;
 		}
 		if(!sub_allocator_start_sub_allocator(&ppm_data->sub_alloc, MaxMB+1)) {
 		    sub_allocator_stop_sub_allocator(&ppm_data->sub_alloc);
 		    return FALSE;
 		}
 		if (!start_model_rare(ppm_data, max_order)) {
 		    sub_allocator_stop_sub_allocator(&ppm_data->sub_alloc);
 		    return FALSE;
 		}
 	}
 	rar_dbgmsg("ppm_decode_init done: %d\n", ppm_data->min_context != NULL);
 	return (ppm_data->min_context != NULL);
 }
 
 int ppm_decode_char(ppm_data_t *ppm_data, int fd, unpack_data_t *unpack_data)
 {
 	int symbol;
 
 	if ((uint8_t *) ppm_data->min_context <= ppm_data->sub_alloc.ptext ||
 			(uint8_t *)ppm_data->min_context > ppm_data->sub_alloc.heap_end) {
 		return -1;
 	}
 	if (ppm_data->min_context->num_stats != 1) {
 		if ((uint8_t *) ppm_data->min_context->con_ut.u.stats <= ppm_data->sub_alloc.ptext ||
 			(uint8_t *) ppm_data->min_context->con_ut.u.stats > ppm_data->sub_alloc.heap_end) {
 			return -1;
 		}
 		if (!ppm_decode_symbol1(ppm_data, ppm_data->min_context)) {
 			return -1;
 		}
 	} else {
 		ppm_decode_bin_symbol(ppm_data, ppm_data->min_context);
 	}
 	coder_decode(&ppm_data->coder);
 	
 	while (!ppm_data->found_state) {
 		ARI_DEC_NORMALISE(fd, unpack_data, ppm_data->coder.code, 
 				ppm_data->coder.low, ppm_data->coder.range);
 		do {
 			ppm_data->order_fall++;
 			ppm_data->min_context = ppm_data->min_context->suffix;
 			if ((uint8_t *)ppm_data->min_context <= ppm_data->sub_alloc.ptext ||
 					(uint8_t *)ppm_data->min_context >
 					ppm_data->sub_alloc.heap_end) {
 				return -1;
 			}
 		} while (ppm_data->min_context->num_stats == ppm_data->num_masked);
 		if (!ppm_decode_symbol2(ppm_data, ppm_data->min_context)) {
 			return -1;
 		}
 		coder_decode(&ppm_data->coder);
 	}
 	
 	symbol = ppm_data->found_state->symbol;
 	if (!ppm_data->order_fall && (uint8_t *) ppm_data->found_state->successor > ppm_data->sub_alloc.ptext) {
 		ppm_data->min_context = ppm_data->max_context = ppm_data->found_state->successor;
 	} else {
 		if(!update_model(ppm_data)) {
 		    rar_dbgmsg("unrar: ppm_decode_char: update_model failed\n");
 		    return -1;
 		}
 
 		if (ppm_data->esc_count == 0) {
 			clear_mask(ppm_data);
 		}
 	}
 	ARI_DEC_NORMALISE(fd, unpack_data, ppm_data->coder.code, ppm_data->coder.low,
 				ppm_data->coder.range);
 	return symbol;
 }