mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1039 lines
29 KiB
1039 lines
29 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#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);
|
|
if (t>138412020) {
|
|
rar_dbgmsg("too much memory needed for uncompressing this file\n");
|
|
return FALSE;
|
|
}
|
|
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
|
|
if ((sub_alloc->heap_start = (uint8_t *) malloc(alloc_size)) == NULL) {
|
|
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)
|
|
{
|
|
int i, k, m, step;
|
|
|
|
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++;
|
|
if ((void *)p > (void *) ppm_data->sub_alloc.heap_end) {
|
|
return NULL;
|
|
}
|
|
} 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;
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|