]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Implement support for RAR files compressed using "best" and "good" method.
authorAndres Mejia <amejia004@gmail.com>
Sat, 6 Aug 2011 21:54:00 +0000 (17:54 -0400)
committerAndres Mejia <amejia004@gmail.com>
Sat, 6 Aug 2011 21:54:00 +0000 (17:54 -0400)
PPMd code used is public domain code from p7zip. It seems 7z and RAR "good" and
"best" compressed files had a lot in common. Very little changes were needed
from original code so that it would work with RAR files and work with
libarchive. Currently, this will not work correctly with largefiles compressed
using PPMd.

SVN-Revision: 3543

Makefile.am
libarchive/archive_ppmd.h [new file with mode: 0644]
libarchive/archive_ppmd7.c [new file with mode: 0644]
libarchive/archive_ppmd7.h [new file with mode: 0644]
libarchive/archive_ppmd7dec.c [new file with mode: 0644]
libarchive/archive_read_support_format_rar.c
libarchive/test/test_fuzz.c
libarchive/test/test_read_format_rar.c
libarchive/test/test_read_format_rar_compress_best.rar.uu [new file with mode: 0644]

index 53eabe90ad08877a5d104d92464bcd5a4e527ec1..154c98e18ca3224d661a1bf137e702d40563ae39 100644 (file)
@@ -104,6 +104,10 @@ libarchive_la_SOURCES=                                             \
        libarchive/archive_options.c                            \
        libarchive/archive_options_private.h                    \
        libarchive/archive_platform.h                           \
+       libarchive/archive_ppmd.h                               \
+       libarchive/archive_ppmd7.c                              \
+       libarchive/archive_ppmd7.h                              \
+       libarchive/archive_ppmd7dec.c                           \
        libarchive/archive_private.h                            \
        libarchive/archive_rb.c                                 \
        libarchive/archive_rb.h                                 \
@@ -486,6 +490,7 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_read_format_mtree.mtree.uu                 \
        libarchive/test/test_read_format_mtree_nomagic.mtree.uu         \
        libarchive/test/test_read_format_rar.rar.uu                     \
+       libarchive/test/test_read_format_rar_compress_best.rar.uu       \
        libarchive/test/test_read_format_rar_compress_normal.rar.uu     \
        libarchive/test/test_read_format_rar_multi_lzss_blocks.rar.uu   \
        libarchive/test/test_read_format_rar_noeof.rar.uu               \
diff --git a/libarchive/archive_ppmd.h b/libarchive/archive_ppmd.h
new file mode 100644 (file)
index 0000000..fb17b94
--- /dev/null
@@ -0,0 +1,160 @@
+/* Ppmd.h -- PPMD codec common code
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#ifndef ARCHIVE_PPMD_H_INCLUDED
+#define ARCHIVE_PPMD_H_INCLUDED
+
+#include <stddef.h>
+
+#include "archive_read_private.h"
+
+/*** Begin defined in Types.h ***/
+
+typedef unsigned char Byte;
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+   NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+  struct archive_read *a;
+  Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
+} IByteIn;
+
+typedef struct
+{
+  struct archive_read *a;
+  void (*Write)(void *p, Byte b);
+} IByteOut;
+
+
+typedef struct
+{
+  void *(*Alloc)(void *p, size_t size);
+  void (*Free)(void *p, void *address); /* address can be 0 */
+} ISzAlloc;
+
+/*** End defined in Types.h ***/
+/*** Begin defined in CpuArch.h ***/
+
+#if defined(_M_IX86) || defined(__i386__)
+#define MY_CPU_X86
+#endif
+
+#if defined(MY_CPU_X86) || defined(_M_ARM)
+#define MY_CPU_32BIT
+#endif
+
+#ifdef MY_CPU_32BIT
+#define PPMD_32BIT
+#endif
+
+/*** End defined in CpuArch.h ***/
+
+#define PPMD_INT_BITS 7
+#define PPMD_PERIOD_BITS 7
+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))
+
+#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))
+
+#define PPMD_N1 4
+#define PPMD_N2 4
+#define PPMD_N3 4
+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)
+
+#pragma pack(push,1)
+
+/* SEE-contexts for PPM-contexts with masked symbols */
+typedef struct
+{
+  UInt16 Summ; /* Freq */
+  Byte Shift;  /* Speed of Freq change; low Shift is for fast change */
+  Byte Count;  /* Count to next change of Shift */
+} CPpmd_See;
+
+#define Ppmd_See_Update(p)  if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
+    { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }
+
+typedef struct
+{
+  Byte Symbol;
+  Byte Freq;
+  UInt16 SuccessorLow;
+  UInt16 SuccessorHigh;
+} CPpmd_State;
+
+#pragma pack(pop)
+
+typedef
+  #ifdef PPMD_32BIT
+    CPpmd_State *
+  #else
+    UInt32
+  #endif
+  CPpmd_State_Ref;
+
+typedef
+  #ifdef PPMD_32BIT
+    void *
+  #else
+    UInt32
+  #endif
+  CPpmd_Void_Ref;
+
+typedef
+  #ifdef PPMD_32BIT
+    Byte *
+  #else
+    UInt32
+  #endif
+  CPpmd_Byte_Ref;
+
+#define PPMD_SetAllBitsIn256Bytes(p) \
+  { unsigned i; for (i = 0; i < 256 / sizeof(p[0]); i += 8) { \
+  p[i+7] = p[i+6] = p[i+5] = p[i+4] = p[i+3] = p[i+2] = p[i+1] = p[i+0] = ~(size_t)0; }}
+
+#endif
diff --git a/libarchive/archive_ppmd7.c b/libarchive/archive_ppmd7.c
new file mode 100644 (file)
index 0000000..af9928a
--- /dev/null
@@ -0,0 +1,710 @@
+/* Ppmd7.c -- PPMdH codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <memory.h>
+
+#include "archive_ppmd7.h"
+
+const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+  #define REF(ptr) (ptr)
+#else
+  #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
+#define STATS(ctx) Ppmd7_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+typedef CPpmd7_Context * CTX_PTR;
+
+struct CPpmd7_Node_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd7_Node_ *
+  #else
+    UInt32
+  #endif
+  CPpmd7_Node_Ref;
+
+typedef struct CPpmd7_Node_
+{
+  UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
+  UInt16 NU;
+  CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
+  CPpmd7_Node_Ref Prev;
+} CPpmd7_Node;
+
+#ifdef PPMD_32BIT
+  #define NODE(ptr) (ptr)
+#else
+  #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
+#endif
+
+void Ppmd7_Construct(CPpmd7 *p)
+{
+  unsigned i, k, m;
+
+  p->Base = 0;
+
+  for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+    do { p->Units2Indx[k++] = (Byte)i; } while(--step);
+    p->Indx2Units[i] = (Byte)k;
+  }
+
+  p->NS2BSIndx[0] = (0 << 1);
+  p->NS2BSIndx[1] = (1 << 1);
+  memset(p->NS2BSIndx + 2, (2 << 1), 9);
+  memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+  for (i = 0; i < 3; i++)
+    p->NS2Indx[i] = (Byte)i;
+  for (m = i, k = 1; i < 256; i++)
+  {
+    p->NS2Indx[i] = (Byte)m;
+    if (--k == 0)
+      k = (++m) - 2;
+  }
+
+  memset(p->HB2Flag, 0, 0x40);
+  memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
+}
+
+void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->Base);
+  p->Size = 0;
+  p->Base = 0;
+}
+
+Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc)
+{
+  if (p->Base == 0 || p->Size != size)
+  {
+    Ppmd7_Free(p, alloc);
+    p->AlignOffset =
+      #ifdef PPMD_32BIT
+        (4 - size) & 3;
+      #else
+        4 - (size & 3);
+      #endif
+    if ((p->Base = (Byte *)alloc->Alloc(alloc, p->AlignOffset + size
+        #ifndef PPMD_32BIT
+        + UNIT_SIZE
+        #endif
+        )) == 0)
+      return False;
+    p->Size = size;
+  }
+  return True;
+}
+
+static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
+{
+  *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
+  p->FreeList[indx] = REF(node);
+}
+
+static void *RemoveNode(CPpmd7 *p, unsigned indx)
+{
+  CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
+  p->FreeList[indx] = *node;
+  return node;
+}
+
+static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+  unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+  ptr = (Byte *)ptr + U2B(I2U(newIndx));
+  if (I2U(i = U2I(nu)) != nu)
+  {
+    unsigned k = I2U(--i);
+    InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+  }
+  InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd7 *p)
+{
+  #ifdef PPMD_32BIT
+  CPpmd7_Node headItem;
+  CPpmd7_Node_Ref head = &headItem;
+  #else
+  CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
+  #endif
+  
+  CPpmd7_Node_Ref n = head;
+  unsigned i;
+
+  p->GlueCount = 255;
+
+  /* create doubly-linked list of free blocks */
+  for (i = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    UInt16 nu = I2U(i);
+    CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
+    p->FreeList[i] = 0;
+    while (next != 0)
+    {
+      CPpmd7_Node *node = NODE(next);
+      node->Next = n;
+      n = NODE(n)->Prev = next;
+      next = *(const CPpmd7_Node_Ref *)node;
+      node->Stamp = 0;
+      node->NU = (UInt16)nu;
+    }
+  }
+  NODE(head)->Stamp = 1;
+  NODE(head)->Next = n;
+  NODE(n)->Prev = head;
+  if (p->LoUnit != p->HiUnit)
+    ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;
+  
+  /* Glue free blocks */
+  while (n != head)
+  {
+    CPpmd7_Node *node = NODE(n);
+    UInt32 nu = (UInt32)node->NU;
+    for (;;)
+    {
+      CPpmd7_Node *node2 = NODE(n) + nu;
+      nu += node2->NU;
+      if (node2->Stamp != 0 || nu >= 0x10000)
+        break;
+      NODE(node2->Prev)->Next = node2->Next;
+      NODE(node2->Next)->Prev = node2->Prev;
+      node->NU = (UInt16)nu;
+    }
+    n = node->Next;
+  }
+  
+  /* Fill lists of free blocks */
+  for (n = NODE(head)->Next; n != head;)
+  {
+    CPpmd7_Node *node = NODE(n);
+    unsigned nu;
+    CPpmd7_Node_Ref next = node->Next;
+    for (nu = node->NU; nu > 128; nu -= 128, node += 128)
+      InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+    if (I2U(i = U2I(nu)) != nu)
+    {
+      unsigned k = I2U(--i);
+      InsertNode(p, node + k, nu - k - 1);
+    }
+    InsertNode(p, node, i);
+    n = next;
+  }
+}
+
+static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
+{
+  unsigned i;
+  void *retVal;
+  if (p->GlueCount == 0)
+  {
+    GlueFreeBlocks(p);
+    if (p->FreeList[indx] != 0)
+      return RemoveNode(p, indx);
+  }
+  i = indx;
+  do
+  {
+    if (++i == PPMD_NUM_INDEXES)
+    {
+      UInt32 numBytes = U2B(I2U(indx));
+      p->GlueCount--;
+      return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+    }
+  }
+  while (p->FreeList[i] == 0);
+  retVal = RemoveNode(p, i);
+  SplitBlock(p, retVal, i, indx);
+  return retVal;
+}
+
+static void *AllocUnits(CPpmd7 *p, unsigned indx)
+{
+  UInt32 numBytes;
+  if (p->FreeList[indx] != 0)
+    return RemoveNode(p, indx);
+  numBytes = U2B(I2U(indx));
+  if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+  {
+    void *retVal = p->LoUnit;
+    p->LoUnit += numBytes;
+    return retVal;
+  }
+  return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+  { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
+    do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }
+
+static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+  unsigned i0 = U2I(oldNU);
+  unsigned i1 = U2I(newNU);
+  if (i0 == i1)
+    return oldPtr;
+  if (p->FreeList[i1] != 0)
+  {
+    void *ptr = RemoveNode(p, i1);
+    MyMem12Cpy(ptr, oldPtr, newNU);
+    InsertNode(p, oldPtr, i0);
+    return ptr;
+  }
+  SplitBlock(p, oldPtr, i0, i1);
+  return oldPtr;
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+  (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+  (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+static void RestartModel(CPpmd7 *p)
+{
+  unsigned i, k, m;
+
+  memset(p->FreeList, 0, sizeof(p->FreeList));
+  p->Text = p->Base + p->AlignOffset;
+  p->HiUnit = p->Text + p->Size;
+  p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+  p->GlueCount = 0;
+
+  p->OrderFall = p->MaxOrder;
+  p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+  p->PrevSuccess = 0;
+
+  p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+  p->MinContext->Suffix = 0;
+  p->MinContext->NumStats = 256;
+  p->MinContext->SummFreq = 256 + 1;
+  p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+  p->LoUnit += U2B(256 / 2);
+  p->MinContext->Stats = REF(p->FoundState);
+  for (i = 0; i < 256; i++)
+  {
+    CPpmd_State *s = &p->FoundState[i];
+    s->Symbol = (Byte)i;
+    s->Freq = 1;
+    SetSuccessor(s, 0);
+  }
+
+  for (i = 0; i < 128; i++)
+    for (k = 0; k < 8; k++)
+    {
+      UInt16 *dest = p->BinSumm[i] + k;
+      UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
+      for (m = 0; m < 64; m += 8)
+        dest[m] = val;
+    }
+  
+  for (i = 0; i < 25; i++)
+    for (k = 0; k < 16; k++)
+    {
+      CPpmd_See *s = &p->See[i][k];
+      s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
+      s->Count = 4;
+    }
+}
+
+void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
+{
+  p->MaxOrder = maxOrder;
+  RestartModel(p);
+  p->DummySee.Shift = PPMD_PERIOD_BITS;
+  p->DummySee.Summ = 0; /* unused */
+  p->DummySee.Count = 64; /* unused */
+}
+
+static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
+{
+  CPpmd_State upState;
+  CTX_PTR c = p->MinContext;
+  CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+  CPpmd_State *ps[PPMD7_MAX_ORDER];
+  unsigned numPs = 0;
+  
+  if (!skip)
+    ps[numPs++] = p->FoundState;
+  
+  while (c->Suffix)
+  {
+    CPpmd_Void_Ref successor;
+    CPpmd_State *s;
+    c = SUFFIX(c);
+    if (c->NumStats != 1)
+    {
+      for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+    }
+    else
+      s = ONE_STATE(c);
+    successor = SUCCESSOR(s);
+    if (successor != upBranch)
+    {
+      c = CTX(successor);
+      if (numPs == 0)
+        return c;
+      break;
+    }
+    ps[numPs++] = s;
+  }
+  
+  upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
+  SetSuccessor(&upState, upBranch + 1);
+  
+  if (c->NumStats == 1)
+    upState.Freq = ONE_STATE(c)->Freq;
+  else
+  {
+    UInt32 cf, s0;
+    CPpmd_State *s;
+    for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+    cf = s->Freq - 1;
+    s0 = c->SummFreq - c->NumStats - cf;
+    upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+  }
+
+  do
+  {
+    /* Create Child */
+    CTX_PTR c1; /* = AllocContext(p); */
+    if (p->HiUnit != p->LoUnit)
+      c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+    else if (p->FreeList[0] != 0)
+      c1 = (CTX_PTR)RemoveNode(p, 0);
+    else
+    {
+      c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+      if (!c1)
+        return NULL;
+    }
+    c1->NumStats = 1;
+    *ONE_STATE(c1) = upState;
+    c1->Suffix = REF(c);
+    SetSuccessor(ps[--numPs], REF(c1));
+    c = c1;
+  }
+  while (numPs != 0);
+  
+  return c;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+  CPpmd_State tmp = *t1;
+  *t1 = *t2;
+  *t2 = tmp;
+}
+
+static void UpdateModel(CPpmd7 *p)
+{
+  CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+  CTX_PTR c;
+  unsigned s0, ns;
+  
+  if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+  {
+    c = SUFFIX(p->MinContext);
+    
+    if (c->NumStats == 1)
+    {
+      CPpmd_State *s = ONE_STATE(c);
+      if (s->Freq < 32)
+        s->Freq++;
+    }
+    else
+    {
+      CPpmd_State *s = STATS(c);
+      if (s->Symbol != p->FoundState->Symbol)
+      {
+        do { s++; } while (s->Symbol != p->FoundState->Symbol);
+        if (s[0].Freq >= s[-1].Freq)
+        {
+          SwapStates(&s[0], &s[-1]);
+          s--;
+        }
+      }
+      if (s->Freq < MAX_FREQ - 9)
+      {
+        s->Freq += 2;
+        c->SummFreq += 2;
+      }
+    }
+  }
+
+  if (p->OrderFall == 0)
+  {
+    p->MinContext = p->MaxContext = CreateSuccessors(p, True);
+    if (p->MinContext == 0)
+    {
+      RestartModel(p);
+      return;
+    }
+    SetSuccessor(p->FoundState, REF(p->MinContext));
+    return;
+  }
+  
+  *p->Text++ = p->FoundState->Symbol;
+  successor = REF(p->Text);
+  if (p->Text >= p->UnitsStart)
+  {
+    RestartModel(p);
+    return;
+  }
+  
+  if (fSuccessor)
+  {
+    if (fSuccessor <= successor)
+    {
+      CTX_PTR cs = CreateSuccessors(p, False);
+      if (cs == NULL)
+      {
+        RestartModel(p);
+        return;
+      }
+      fSuccessor = REF(cs);
+    }
+    if (--p->OrderFall == 0)
+    {
+      successor = fSuccessor;
+      p->Text -= (p->MaxContext != p->MinContext);
+    }
+  }
+  else
+  {
+    SetSuccessor(p->FoundState, successor);
+    fSuccessor = REF(p->MinContext);
+  }
+  
+  s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);
+  
+  for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
+  {
+    unsigned ns1;
+    UInt32 cf, sf;
+    if ((ns1 = c->NumStats) != 1)
+    {
+      if ((ns1 & 1) == 0)
+      {
+        /* Expand for one UNIT */
+        unsigned oldNU = ns1 >> 1;
+        unsigned i = U2I(oldNU);
+        if (i != U2I(oldNU + 1))
+        {
+          void *ptr = AllocUnits(p, i + 1);
+          void *oldPtr;
+          if (!ptr)
+          {
+            RestartModel(p);
+            return;
+          }
+          oldPtr = STATS(c);
+          MyMem12Cpy(ptr, oldPtr, oldNU);
+          InsertNode(p, oldPtr, i);
+          c->Stats = STATS_REF(ptr);
+        }
+      }
+      c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
+    }
+    else
+    {
+      CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
+      if (!s)
+      {
+        RestartModel(p);
+        return;
+      }
+      *s = *ONE_STATE(c);
+      c->Stats = REF(s);
+      if (s->Freq < MAX_FREQ / 4 - 1)
+        s->Freq <<= 1;
+      else
+        s->Freq = MAX_FREQ - 4;
+      c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
+    }
+    cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
+    sf = (UInt32)s0 + c->SummFreq;
+    if (cf < 6 * sf)
+    {
+      cf = 1 + (cf > sf) + (cf >= 4 * sf);
+      c->SummFreq += 3;
+    }
+    else
+    {
+      cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
+      c->SummFreq = (UInt16)(c->SummFreq + cf);
+    }
+    {
+      CPpmd_State *s = STATS(c) + ns1;
+      SetSuccessor(s, successor);
+      s->Symbol = p->FoundState->Symbol;
+      s->Freq = (Byte)cf;
+      c->NumStats = (UInt16)(ns1 + 1);
+    }
+  }
+  p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+  
+static void Rescale(CPpmd7 *p)
+{
+  unsigned i, adder, sumFreq, escFreq;
+  CPpmd_State *stats = STATS(p->MinContext);
+  CPpmd_State *s = p->FoundState;
+  {
+    CPpmd_State tmp = *s;
+    for (; s != stats; s--)
+      s[0] = s[-1];
+    *s = tmp;
+  }
+  escFreq = p->MinContext->SummFreq - s->Freq;
+  s->Freq += 4;
+  adder = (p->OrderFall != 0);
+  s->Freq = (Byte)((s->Freq + adder) >> 1);
+  sumFreq = s->Freq;
+  
+  i = p->MinContext->NumStats - 1;
+  do
+  {
+    escFreq -= (++s)->Freq;
+    s->Freq = (Byte)((s->Freq + adder) >> 1);
+    sumFreq += s->Freq;
+    if (s[0].Freq > s[-1].Freq)
+    {
+      CPpmd_State *s1 = s;
+      CPpmd_State tmp = *s1;
+      do
+        s1[0] = s1[-1];
+      while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+      *s1 = tmp;
+    }
+  }
+  while (--i);
+  
+  if (s->Freq == 0)
+  {
+    unsigned numStats = p->MinContext->NumStats;
+    unsigned n0, n1;
+    do { i++; } while ((--s)->Freq == 0);
+    escFreq += i;
+    p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
+    if (p->MinContext->NumStats == 1)
+    {
+      CPpmd_State tmp = *stats;
+      do
+      {
+        tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
+        escFreq >>= 1;
+      }
+      while (escFreq > 1);
+      InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
+      *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+      return;
+    }
+    n0 = (numStats + 1) >> 1;
+    n1 = (p->MinContext->NumStats + 1) >> 1;
+    if (n0 != n1)
+      p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+  }
+  p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+  p->FoundState = STATS(p->MinContext);
+}
+
+CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
+{
+  CPpmd_See *see;
+  unsigned nonMasked = p->MinContext->NumStats - numMasked;
+  if (p->MinContext->NumStats != 256)
+  {
+    see = p->See[p->NS2Indx[nonMasked - 1]] +
+        (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
+        2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
+        4 * (numMasked > nonMasked) +
+        p->HiBitsFlag;
+    {
+      unsigned r = (see->Summ >> see->Shift);
+      see->Summ = (UInt16)(see->Summ - r);
+      *escFreq = r + (r == 0);
+    }
+  }
+  else
+  {
+    see = &p->DummySee;
+    *escFreq = 1;
+  }
+  return see;
+}
+
+static void NextContext(CPpmd7 *p)
+{
+  CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+  if (p->OrderFall == 0 && (Byte *)c > p->Text)
+    p->MinContext = p->MaxContext = c;
+  else
+    UpdateModel(p);
+}
+
+void Ppmd7_Update1(CPpmd7 *p)
+{
+  CPpmd_State *s = p->FoundState;
+  s->Freq += 4;
+  p->MinContext->SummFreq += 4;
+  if (s[0].Freq > s[-1].Freq)
+  {
+    SwapStates(&s[0], &s[-1]);
+    p->FoundState = --s;
+    if (s->Freq > MAX_FREQ)
+      Rescale(p);
+  }
+  NextContext(p);
+}
+
+void Ppmd7_Update1_0(CPpmd7 *p)
+{
+  p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
+  p->RunLength += p->PrevSuccess;
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  NextContext(p);
+}
+
+void Ppmd7_UpdateBin(CPpmd7 *p)
+{
+  p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
+  p->PrevSuccess = 1;
+  p->RunLength++;
+  NextContext(p);
+}
+
+void Ppmd7_Update2(CPpmd7 *p)
+{
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  p->RunLength = p->InitRL;
+  UpdateModel(p);
+}
diff --git a/libarchive/archive_ppmd7.h b/libarchive/archive_ppmd7.h
new file mode 100644 (file)
index 0000000..1e7c77a
--- /dev/null
@@ -0,0 +1,144 @@
+/* Ppmd7.h -- PPMdH compression codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+/* This code supports virtual RangeDecoder and includes the implementation
+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#ifndef ARCHIVE_PPMD7_H_INCLUDED
+#define ARCHIVE_PPMD7_H_INCLUDED
+
+#include "archive_ppmd.h"
+
+#define PPMD7_MIN_ORDER 2
+#define PPMD7_MAX_ORDER 64
+
+#define PPMD7_MIN_MEM_SIZE (1 << 11)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFF - 12 * 3)
+
+struct CPpmd7_Context_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd7_Context_ *
+  #else
+    UInt32
+  #endif
+  CPpmd7_Context_Ref;
+
+typedef struct CPpmd7_Context_
+{
+  UInt16 NumStats;
+  UInt16 SummFreq;
+  CPpmd_State_Ref Stats;
+  CPpmd7_Context_Ref Suffix;
+} CPpmd7_Context;
+
+#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+typedef struct
+{
+  CPpmd7_Context *MinContext, *MaxContext;
+  CPpmd_State *FoundState;
+  unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
+  Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+  UInt32 Size;
+  UInt32 GlueCount;
+  Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+  UInt32 AlignOffset;
+
+  Byte Indx2Units[PPMD_NUM_INDEXES];
+  Byte Units2Indx[128];
+  CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+  Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+  CPpmd_See DummySee, See[25][16];
+  UInt16 BinSumm[128][64];
+} CPpmd7;
+
+void Ppmd7_Construct(CPpmd7 *p);
+Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size, ISzAlloc *alloc);
+void Ppmd7_Free(CPpmd7 *p, ISzAlloc *alloc);
+void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder);
+#define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+
+/* ---------- Internal Functions ---------- */
+
+extern const Byte PPMD7_kExpEscape[16];
+
+#ifdef PPMD_32BIT
+  #define Ppmd7_GetPtr(p, ptr) (ptr)
+  #define Ppmd7_GetContext(p, ptr) (ptr)
+  #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
+#else
+  #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+  #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
+  #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+void Ppmd7_Update1(CPpmd7 *p);
+void Ppmd7_Update1_0(CPpmd7 *p);
+void Ppmd7_Update2(CPpmd7 *p);
+void Ppmd7_UpdateBin(CPpmd7 *p);
+
+#define Ppmd7_GetBinSumm(p) \
+    &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
+    p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
+    (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
+    2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
+    ((p->RunLength >> 26) & 0x20)]
+
+CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *scale);
+
+
+/* ---------- Decode ---------- */
+
+typedef struct
+{
+  UInt32 (*GetThreshold)(void *p, UInt32 total);
+  void (*Decode)(void *p, UInt32 start, UInt32 size);
+  UInt32 (*DecodeBit)(void *p, UInt32 size0);
+} IPpmd7_RangeDec;
+
+typedef struct
+{
+  IPpmd7_RangeDec p;
+  UInt32 Range;
+  UInt32 Code;
+  UInt32 Low;
+  UInt32 Bottom;
+  IByteIn *Stream;
+} CPpmd7z_RangeDec;
+
+void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p);
+void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p);
+Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p);
+Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p);
+#define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+
+int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc);
+
+
+/* ---------- Encode ---------- */
+
+/*typedef struct
+{
+  UInt64 Low;
+  UInt32 Range;
+  Byte Cache;
+  UInt64 CacheSize;
+  IByteOut *Stream;
+} CPpmd7z_RangeEnc;
+
+void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p);
+void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p);
+
+void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);
+*/
+#endif
diff --git a/libarchive/archive_ppmd7dec.c b/libarchive/archive_ppmd7dec.c
new file mode 100644 (file)
index 0000000..6aa8a27
--- /dev/null
@@ -0,0 +1,236 @@
+/* Ppmd7Dec.c -- PPMdH Decoder
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include "archive_ppmd7.h"
+
+#define kTopValue (1 << 24)
+
+static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  unsigned i;
+  p->Low = p->Bottom = 0;
+  p->Range = 0xFFFFFFFF;
+  for (i = 0; i < 4; i++)
+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+  return (p->Code < 0xFFFFFFFF);
+}
+
+Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  if (p->Stream->Read((void *)p->Stream) != 0)
+    return False;
+  return Ppmd_RangeDec_Init(p);
+}
+
+Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  if (!Ppmd_RangeDec_Init(p))
+    return False;
+  p->Bottom = 0x8000;
+  return True;
+}
+
+static UInt32 Range_GetThreshold(void *pp, UInt32 total)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  return (p->Code - p->Low) / (p->Range /= total);
+}
+
+static void Range_Normalize(CPpmd7z_RangeDec *p)
+{
+  while (1)
+  {
+    if((p->Low ^ (p->Low + p->Range)) >= kTopValue)
+    {
+      if(p->Range >= p->Bottom)
+        break;
+      else
+        p->Range = -p->Low & (p->Bottom - 1);
+    }
+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+    p->Range <<= 8;
+    p->Low <<= 8;
+  }
+}
+
+static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  p->Code -= start * p->Range;
+  p->Range *= size;
+  Range_Normalize(p);
+}
+
+static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  p->Low += start * p->Range;
+  p->Range *= size;
+  Range_Normalize(p);
+}
+
+static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  UInt32 newBound = (p->Range >> 14) * size0;
+  UInt32 symbol;
+  if (p->Code < newBound)
+  {
+    symbol = 0;
+    p->Range = newBound;
+  }
+  else
+  {
+    symbol = 1;
+    p->Code -= newBound;
+    p->Range -= newBound;
+  }
+  Range_Normalize(p);
+  return symbol;
+}
+
+static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);
+  if(value < size0)
+  {
+    bit = 0;
+    p->p.Decode(p, 0, size0);
+  }
+  else
+  {
+    bit = 1;
+    p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);
+  }
+  return bit;
+}
+
+void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+  p->p.GetThreshold = Range_GetThreshold;
+  p->p.Decode = Range_Decode_7z;
+  p->p.DecodeBit = Range_DecodeBit_7z;
+}
+
+void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+  p->p.GetThreshold = Range_GetThreshold;
+  p->p.Decode = Range_Decode_RAR;
+  p->p.DecodeBit = Range_DecodeBit_RAR;
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
+{
+  size_t charMask[256 / sizeof(size_t)];
+  if (p->MinContext->NumStats != 1)
+  {
+    CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+    unsigned i;
+    UInt32 count, hiCnt;
+    if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+    {
+      Byte symbol;
+      rc->Decode(rc, 0, s->Freq);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd7_Update1_0(p);
+      return symbol;
+    }
+    p->PrevSuccess = 0;
+    i = p->MinContext->NumStats - 1;
+    do
+    {
+      if ((hiCnt += (++s)->Freq) > count)
+      {
+        Byte symbol;
+        rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+        p->FoundState = s;
+        symbol = s->Symbol;
+        Ppmd7_Update1(p);
+        return symbol;
+      }
+    }
+    while (--i);
+    if (count >= p->MinContext->SummFreq)
+      return -2;
+    p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+    rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(s->Symbol) = 0;
+    i = p->MinContext->NumStats - 1;
+    do { MASK((--s)->Symbol) = 0; } while (--i);
+  }
+  else
+  {
+    UInt16 *prob = Ppmd7_GetBinSumm(p);
+    if (rc->DecodeBit(rc, *prob) == 0)
+    {
+      Byte symbol;
+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+      symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
+      Ppmd7_UpdateBin(p);
+      return symbol;
+    }
+    *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+    p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
+    p->PrevSuccess = 0;
+  }
+  for (;;)
+  {
+    CPpmd_State *ps[256], *s;
+    UInt32 freqSum, count, hiCnt;
+    CPpmd_See *see;
+    unsigned i, num, numMasked = p->MinContext->NumStats;
+    do
+    {
+      p->OrderFall++;
+      if (!p->MinContext->Suffix)
+        return -1;
+      p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+    }
+    while (p->MinContext->NumStats == numMasked);
+    hiCnt = 0;
+    s = Ppmd7_GetStats(p, p->MinContext);
+    i = 0;
+    num = p->MinContext->NumStats - numMasked;
+    do
+    {
+      int k = (int)(MASK(s->Symbol));
+      hiCnt += (s->Freq & k);
+      ps[i] = s++;
+      i -= k;
+    }
+    while (i != num);
+    
+    see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
+    freqSum += hiCnt;
+    count = rc->GetThreshold(rc, freqSum);
+    
+    if (count < hiCnt)
+    {
+      Byte symbol;
+      CPpmd_State **pps = ps;
+      for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+      s = *pps;
+      rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+      Ppmd_See_Update(see);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd7_Update2(p);
+      return symbol;
+    }
+    if (count >= freqSum)
+      return -2;
+    rc->Decode(rc, hiCnt, freqSum - hiCnt);
+    see->Summ = (UInt16)(see->Summ + freqSum);
+    do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+  }
+}
index efc9e82807be33debc811fa180f7684f73bb6b90..2b2238af81399f03f0e54d3caa05127373461d65 100644 (file)
@@ -36,6 +36,7 @@
 #include "archive_endian.h"
 #include "archive_entry.h"
 #include "archive_entry_locale.h"
+#include "archive_ppmd7.h"
 #include "archive_private.h"
 #include "archive_read_private.h"
 
@@ -216,14 +217,17 @@ struct rar
   int64_t bytes_uncopied;
   int64_t offset;
   char valid;
+  unsigned char *unp_buffer;
+  unsigned int dictionary_size;
+  char start_new_block;
+
+  /* LZSS members */
   struct huffman_code maincode;
   struct huffman_code offsetcode;
   struct huffman_code lowoffsetcode;
   struct huffman_code lengthcode;
   unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
-  unsigned char *unp_buffer;
   struct lzss lzss;
-  unsigned int dictionary_size;
   char output_last_match;
   unsigned int lastlength;
   unsigned int lastoffset;
@@ -231,9 +235,14 @@ struct rar
   unsigned int lastlowoffset;
   unsigned int numlowoffsetrepeats;
   int64_t filterstart;
-  char start_new_block;
   char start_new_table;
 
+  /* PPMd Variant H members */
+  char is_ppmd_block;
+  CPpmd7 ppmd7_context;
+  CPpmd7z_RangeDec range_dec;
+  IByteIn bytein;
+
   /*
    * Bit stream reader.
    */
@@ -265,7 +274,7 @@ static int read_symlink_stored(struct archive_read *, struct archive_entry *,
                                struct archive_string_conv *);
 static int read_data_stored(struct archive_read *, const void **, size_t *,
                             int64_t *);
-static int read_data_lzss(struct archive_read *, const void **, size_t *,
+static int read_data_compressed(struct archive_read *, const void **, size_t *,
                           int64_t *);
 static int rar_br_preparation(struct archive_read *, struct rar_br *);
 static int parse_codes(struct archive_read *);
@@ -283,6 +292,188 @@ static int64_t expand(struct archive_read *, int64_t);
 static int copy_from_lzss_window(struct archive_read *, const void **,
                                    int64_t, int);
 
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define rar_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define rar_br_bits(br, n)        \
+  (((uint32_t)((br)->cache_buffer >>    \
+    ((br)->cache_avail - (n)))) & cache_masks[n])
+#define rar_br_bits_forced(br, n)     \
+  (((uint32_t)((br)->cache_buffer <<    \
+    ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ *  True  : completed, there is enough data in the cache buffer.
+ *  False : there is no data in the stream. */
+#define rar_br_read_ahead(a, br, n) \
+  ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
+/* Notify how man bits we consumed. */
+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
+
+static const uint32_t cache_masks[] = {
+  0x00000000, 0x00000001, 0x00000003, 0x00000007,
+  0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+  0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+  0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+  0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+  0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+  0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+  0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+  0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+rar_br_fillup(struct archive_read *a, struct rar_br *br)
+{
+/*
+ * x86 proccessor family can read misaligned data without an access error.
+ */
+#if defined(__i386__) || (defined(_MSC_VER) && defined(_M_IX86))
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define rar_be16dec(p) _byteswap_ushort(*(const uint16_t *)(p))
+#  elif defined(be16toh)
+#    define rar_be16dec(p) be16toh(*(const uint16_t *)(p))
+#  elif defined(betoh16)
+#    define rar_be16dec(p) betoh16(*(const uint16_t *)(p))
+#  else
+#    define rar_be16dec archive_be16dec
+#  endif
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define rar_be32dec(p) _byteswap_ulong(*(const uint32_t *)(p))
+#  elif defined(be32toh)
+#    define rar_be32dec(p) be32toh(*(const uint32_t *)(p))
+#  elif defined(betoh32)
+#    define rar_be32dec(p) betoh32(*(const uint32_t *)(p))
+#  else
+#    define rar_be32dec archive_be32dec
+#  endif
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    define rar_be64dec(p) _byteswap_uint64(*(const uint64_t *)(p))
+#  elif defined(be64toh)
+#    define rar_be64dec(p) be64toh(*(const uint64_t *)(p))
+#  elif defined(betoh64)
+#    define rar_be64dec(p) betoh64(*(const uint64_t *)(p))
+#  else
+#    define rar_be64dec archive_be64dec
+#  endif
+#else
+#  define rar_be16dec archive_be16dec
+#  define rar_be32dec archive_be32dec
+#  define rar_be64dec archive_be64dec
+#endif
+  struct rar *rar = (struct rar *)(a->format->data);
+  int n = CACHE_BITS - br->cache_avail;
+
+  for (;;) {
+    switch (n >> 3) {
+    case 8:
+      if (br->avail_in >= 8) {
+        br->cache_buffer =
+            rar_be64dec(br->next_in);
+        br->next_in += 8;
+        br->avail_in -= 8;
+        br->cache_avail += 8 * 8;
+        rar->bytes_unconsumed += 8;
+        rar->bytes_remaining -= 8;
+        return (1);
+      }
+      break;
+    case 7:
+      if (br->avail_in >= 8) {/* Read extra one. */
+        br->cache_buffer =
+           (br->cache_buffer << 56) |
+            rar_be64dec(br->next_in) >> 8;
+        br->next_in += 7;
+        br->avail_in -= 7;
+        br->cache_avail += 7 * 8;
+        rar->bytes_unconsumed += 7;
+        rar->bytes_remaining -= 7;
+        return (1);
+      }
+      break;
+    case 6:
+      if (br->avail_in >= 6) {
+        br->cache_buffer =
+           (br->cache_buffer << 48) |
+           (((uint64_t)rar_be32dec(
+               br->next_in)) << 16) |
+            rar_be16dec(&br->next_in[4]);
+        br->next_in += 6;
+        br->avail_in -= 6;
+        br->cache_avail += 6 * 8;
+        rar->bytes_unconsumed += 6;
+        rar->bytes_remaining -= 6;
+        return (1);
+      }
+      break;
+    case 0:
+      /* We have enough compressed data in
+       * the cache buffer.*/
+      return (1);
+    default:
+      break;
+    }
+    if (br->avail_in <= 0) {
+
+      if (rar->bytes_unconsumed > 0) {
+        /* Consume as much as the decompressor
+         * actually used. */
+        __archive_read_consume(a, rar->bytes_unconsumed);
+        rar->bytes_unconsumed = 0;
+      }
+      br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
+      if (br->next_in == NULL)
+        return (0);
+      if (br->avail_in > rar->bytes_remaining)
+        br->avail_in = rar->bytes_remaining;
+      if (br->avail_in == 0)
+        return (0);
+    }
+    br->cache_buffer =
+       (br->cache_buffer << 8) | *br->next_in++;
+    br->avail_in--;
+    br->cache_avail += 8;
+    n -= 8;
+    rar->bytes_unconsumed++;
+    rar->bytes_remaining--;
+  }
+#undef rar_be16dec
+#undef rar_be32dec
+#undef rar_be64dec
+}
+
+static int
+rar_br_preparation(struct archive_read *a, struct rar_br *br)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  if (rar->bytes_remaining > 0) {
+    br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
+    if (br->next_in == NULL) {
+      archive_set_error(&a->archive,
+          ARCHIVE_ERRNO_FILE_FORMAT,
+          "Truncated RAR file data");
+      return (ARCHIVE_FATAL);
+    }
+    if (br->avail_in > rar->bytes_remaining)
+      br->avail_in = rar->bytes_remaining;
+    if (br->cache_avail == 0)
+      (void)rar_br_fillup(a, br);
+  }
+  return (ARCHIVE_OK);
+}
+
 /* Find last bit set */
 static inline int
 rar_fls(unsigned int word)
@@ -357,6 +548,39 @@ lzss_emit_match(struct rar *rar, int offset, int length)
   rar->lzss.position += length;
 }
 
+static void *
+ppmd_alloc(void *p, size_t size)
+{
+  (void)p;
+  return malloc(size);
+}
+static void
+ppmd_free(void *p, void *address)
+{
+  (void)p;
+  free(address);
+}
+static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free };
+
+static Byte
+ppmd_read(void *p)
+{
+  struct archive_read *a = ((IByteIn*)p)->a;
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_br *br = &(rar->br);
+  Byte b;
+  if (!rar_br_read_ahead(a, br, 1))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Truncated RAR file data");
+    rar->valid = 0;
+    return 0;
+  }
+  b = rar_br_bits(br, 8);
+  rar_br_consume(br, 8);
+  return b;
+}
+
 int
 archive_read_support_format_rar(struct archive *_a)
 {
@@ -559,10 +783,10 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
   case COMPRESS_METHOD_FASTEST:
   case COMPRESS_METHOD_FAST:
   case COMPRESS_METHOD_NORMAL:
-    return read_data_lzss(a, buff, size, offset);
-
   case COMPRESS_METHOD_GOOD:
   case COMPRESS_METHOD_BEST:
+    return read_data_compressed(a, buff, size, offset);
+
   default:
     archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
                       "Unsupported compression method for RAR file.");
@@ -601,6 +825,7 @@ archive_read_format_rar_cleanup(struct archive_read *a)
   free_codes(a);
   free(rar->unp_buffer);
   free(rar->lzss.window);
+  Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
   free(rar);
   (a->format->data) = NULL;
   return (ARCHIVE_OK);
@@ -879,10 +1104,12 @@ read_header(struct archive_read *a, struct archive_entry *entry,
   rar->br.cache_avail = 0;
   rar->br.avail_in = 0;
   rar->valid = 1;
+  rar->is_ppmd_block = 0;
   rar->start_new_table = 1;
   free(rar->unp_buffer);
   rar->unp_buffer = NULL;
   memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+  Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
 
   /* Don't set any archive entries for non-file header types */
   if (head_type == NEWSUB_HEAD)
@@ -1076,12 +1303,12 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size,
 }
 
 static int
-read_data_lzss(struct archive_read *a, const void **buff, size_t *size,
+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
                int64_t *offset)
 {
   struct rar *rar;
   int64_t start, end, actualend;
-  int ret = (ARCHIVE_OK);
+  int ret = (ARCHIVE_OK), sym;
 
   rar = (struct rar *)(a->format->data);
   if (!rar->valid)
@@ -1092,10 +1319,11 @@ read_data_lzss(struct archive_read *a, const void **buff, size_t *size,
     *buff = NULL;
     *size = 0;
     *offset = rar->offset;
+    Ppmd7_Free(&rar->ppmd7_context, &g_szalloc);
     return (ARCHIVE_EOF);
   }
 
-  if (rar->dictionary_size && rar->bytes_uncopied > 0)
+  if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
   {
     *offset = rar->offset;
     if (rar->offset + rar->bytes_uncopied > rar->unp_size)
@@ -1113,221 +1341,66 @@ read_data_lzss(struct archive_read *a, const void **buff, size_t *size,
   if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
     return (ret);
 
-  start = rar->offset;
-  end = start + rar->dictionary_size;
-  rar->filterstart = INT64_MAX;
-
-  if ((actualend = expand(a, end)) < 0)
-    return ((int)actualend);
-
-  rar->bytes_uncopied = actualend - start;
-  if (rar->bytes_uncopied == 0) {
-      /* Broken RAR files cause this case.
-       * NOTE: If this case were possible on a normal RAR file
-       * we would find out where it was actually bad and
-       * what we would do to solve it. */
+  if (rar->is_ppmd_block)
+  {
+    if (!rar->unp_buffer)
+    {
+      if ((rar->unp_buffer = malloc(1)) == NULL)
+      {
+        archive_set_error(&a->archive, ENOMEM,
+                          "Unable to allocate memory for uncompressed data.");
+        return (ARCHIVE_FATAL);
+      }
+    }
+    if ((sym = Ppmd7_DecodeSymbol(&rar->ppmd7_context, &rar->range_dec.p)) < 0)
+    {
       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-                        "Internal error extracting RAR file.");
-      return (ARCHIVE_FAILED);
+                        "Invalid symbol");
+      return (ARCHIVE_FATAL);
+    }
+    *offset = rar->offset++;
+    *size = 1;
+    *rar->unp_buffer = sym;
+    *buff = rar->unp_buffer;
+    return (ret);
   }
-  *offset = rar->offset;
-  if (rar->offset + rar->bytes_uncopied > rar->unp_size)
-    *size = rar->unp_size - rar->offset;
   else
-    *size = rar->bytes_uncopied;
-  ret = copy_from_lzss_window(a, buff, *offset, *size);
-  rar->offset += *size;
-  rar->bytes_uncopied -= *size;
+  {
+    start = rar->offset;
+    end = start + rar->dictionary_size;
+    rar->filterstart = INT64_MAX;
+
+    if ((actualend = expand(a, end)) < 0)
+      return ((int)actualend);
+
+    rar->bytes_uncopied = actualend - start;
+    if (rar->bytes_uncopied == 0) {
+        /* Broken RAR files cause this case.
+        * NOTE: If this case were possible on a normal RAR file
+        * we would find out where it was actually bad and
+        * what we would do to solve it. */
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Internal error extracting RAR file.");
+        return (ARCHIVE_FAILED);
+    }
+    *offset = rar->offset;
+    if (rar->offset + rar->bytes_uncopied > rar->unp_size)
+      *size = rar->unp_size - rar->offset;
+    else
+      *size = rar->bytes_uncopied;
+    ret = copy_from_lzss_window(a, buff, *offset, *size);
+    rar->offset += *size;
+    rar->bytes_uncopied -= *size;
+  }
   return ret;
 }
 
-/*
- * Bit stream reader.
- */
-/* Check that the cache buffer has enough bits. */
-#define rar_br_has(br, n)      ((br)->cache_avail >= n)
-/* Get compressed data by bit. */
-#define rar_br_bits(br, n)                             \
-       (((uint32_t)((br)->cache_buffer >>              \
-               ((br)->cache_avail - (n)))) & cache_masks[n])
-#define rar_br_bits_forced(br, n)                      \
-       (((uint32_t)((br)->cache_buffer <<              \
-               ((n) - (br)->cache_avail))) & cache_masks[n])
-/* Read ahead to make sure the cache buffer has enough compressed data we
- * will use.
- *  True  : completed, there is enough data in the cache buffer.
- *  False : there is no data in the stream. */
-#define rar_br_read_ahead(a, br, n)    \
-       ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
-/* Notify how man bits we consumed. */
-#define rar_br_consume(br, n)  ((br)->cache_avail -= (n))
-#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
-
-static const uint32_t cache_masks[] = {
-       0x00000000, 0x00000001, 0x00000003, 0x00000007,
-       0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
-       0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
-       0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
-       0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
-       0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
-       0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
-       0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
-       0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
-};
-
-/*
- * Shift away used bits in the cache data and fill it up with following bits.
- * Call this when cache buffer does not have enough bits you need.
- *
- * Returns 1 if the cache buffer is full.
- * Returns 0 if the cache buffer is not full; input buffer is empty.
- */
-static int
-rar_br_fillup(struct archive_read *a, struct rar_br *br)
-{
-/*
- * x86 proccessor family can read misaligned data without an access error.
- */
-#if defined(__i386__) || (defined(_MSC_VER) && defined(_M_IX86))
-#  if defined(_WIN32) && !defined(__CYGWIN__)
-#    define rar_be16dec(p) _byteswap_ushort(*(const uint16_t *)(p))
-#  elif defined(be16toh)
-#    define rar_be16dec(p) be16toh(*(const uint16_t *)(p))
-#  elif defined(betoh16)
-#    define rar_be16dec(p) betoh16(*(const uint16_t *)(p))
-#  else
-#    define rar_be16dec        archive_be16dec
-#  endif
-#  if defined(_WIN32) && !defined(__CYGWIN__)
-#    define rar_be32dec(p) _byteswap_ulong(*(const uint32_t *)(p))
-#  elif defined(be32toh)
-#    define rar_be32dec(p) be32toh(*(const uint32_t *)(p))
-#  elif defined(betoh32)
-#    define rar_be32dec(p) betoh32(*(const uint32_t *)(p))
-#  else
-#    define rar_be32dec        archive_be32dec
-#  endif
-#  if defined(_WIN32) && !defined(__CYGWIN__)
-#    define rar_be64dec(p) _byteswap_uint64(*(const uint64_t *)(p))
-#  elif defined(be64toh)
-#    define rar_be64dec(p) be64toh(*(const uint64_t *)(p))
-#  elif defined(betoh64)
-#    define rar_be64dec(p) betoh64(*(const uint64_t *)(p))
-#  else
-#    define rar_be64dec        archive_be64dec
-#  endif
-#else
-#  define rar_be16dec  archive_be16dec
-#  define rar_be32dec  archive_be32dec
-#  define rar_be64dec  archive_be64dec
-#endif
-       struct rar *rar = (struct rar *)(a->format->data);
-       int n = CACHE_BITS - br->cache_avail;
-
-       for (;;) {
-               switch (n >> 3) {
-               case 8:
-                       if (br->avail_in >= 8) {
-                               br->cache_buffer =
-                                   rar_be64dec(br->next_in);
-                               br->next_in += 8;
-                               br->avail_in -= 8;
-                               br->cache_avail += 8 * 8;
-                               rar->bytes_unconsumed += 8;
-                               rar->bytes_remaining -= 8;
-                               return (1);
-                       }
-                       break;
-               case 7:
-                       if (br->avail_in >= 8) {/* Read extra one. */
-                               br->cache_buffer =
-                                  (br->cache_buffer << 56) |
-                                   rar_be64dec(br->next_in) >> 8;
-                               br->next_in += 7;
-                               br->avail_in -= 7;
-                               br->cache_avail += 7 * 8;
-                               rar->bytes_unconsumed += 7;
-                               rar->bytes_remaining -= 7;
-                               return (1);
-                       }
-                       break;
-               case 6:
-                       if (br->avail_in >= 6) {
-                               br->cache_buffer =
-                                  (br->cache_buffer << 48) |
-                                  (((uint64_t)rar_be32dec(
-                                      br->next_in)) << 16) |
-                                   rar_be16dec(&br->next_in[4]);
-                               br->next_in += 6;
-                               br->avail_in -= 6;
-                               br->cache_avail += 6 * 8;
-                               rar->bytes_unconsumed += 6;
-                               rar->bytes_remaining -= 6;
-                               return (1);
-                       }
-                       break;
-               case 0:
-                       /* We have enough compressed data in
-                        * the cache buffer.*/ 
-                       return (1);
-               default:
-                       break;
-               }
-               if (br->avail_in <= 0) {
-
-                       if (rar->bytes_unconsumed > 0) {
-                               /* Consume as much as the decompressor
-                                * actually used. */
-                               __archive_read_consume(a, rar->bytes_unconsumed);
-                               rar->bytes_unconsumed = 0;
-                       }
-                       br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
-                       if (br->next_in == NULL)
-                               return (0);
-                       if (br->avail_in > rar->bytes_remaining)
-                               br->avail_in = rar->bytes_remaining;
-                       if (br->avail_in == 0)
-                               return (0);
-               }
-               br->cache_buffer =
-                  (br->cache_buffer << 8) | *br->next_in++;
-               br->avail_in--;
-               br->cache_avail += 8;
-               n -= 8;
-               rar->bytes_unconsumed++;
-               rar->bytes_remaining--;
-       }
-#undef rar_be16dec
-#undef rar_be32dec
-#undef rar_be64dec
-}
-
-static int
-rar_br_preparation(struct archive_read *a, struct rar_br *br)
-{
-       struct rar *rar = (struct rar *)(a->format->data);
-
-       if (rar->bytes_remaining > 0) {
-               br->next_in = __archive_read_ahead(a, 1, &(br->avail_in));
-               if (br->next_in == NULL) {
-                       archive_set_error(&a->archive,
-                           ARCHIVE_ERRNO_FILE_FORMAT,
-                           "Truncated RAR file data");
-                       return (ARCHIVE_FATAL);
-               }
-               if (br->avail_in > rar->bytes_remaining)
-                       br->avail_in = rar->bytes_remaining;
-               if (br->cache_avail == 0)
-                       (void)rar_br_fillup(a, br);
-       }
-       return (ARCHIVE_OK);
-}
-
 static int
 parse_codes(struct archive_read *a)
 {
   int i, j, val, n, r;
-  unsigned char bitlengths[MAX_SYMBOLS], zerocount;
+  unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
+  unsigned int maxorder;
   struct huffman_code precode;
   struct rar *rar = (struct rar *)(a->format->data);
   struct rar_br *br = &(rar->br);
@@ -1340,12 +1413,69 @@ parse_codes(struct archive_read *a)
   /* PPMd block flag */
   if (!rar_br_read_ahead(a, br, 1))
     goto truncated_data;
-  if (rar_br_bits(br, 1))
+  if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
   {
     rar_br_consume(br, 1);
-    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-                      "Unsupported compression method for RAR file.");
-    return (ARCHIVE_FAILED);
+    ppmd_flags = rar_br_bits(br, 7);
+    rar_br_consume(br, 7);
+
+    /* Memory is allocated in MB */
+    if (ppmd_flags & 0x20)
+    {
+      rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
+      rar_br_consume(br, 8);
+    }
+
+    if (ppmd_flags & 0x40)
+    {
+      rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
+      rar_br_consume(br, 8);
+    }
+
+    if (ppmd_flags & 0x20)
+    {
+      maxorder = (ppmd_flags & 0x1F) + 1;
+      if(maxorder > 16)
+        maxorder = 16 + (maxorder - 16) * 3;
+
+      if (maxorder == 1)
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Truncated RAR file data");
+        return (ARCHIVE_FATAL);
+      }
+
+      rar->bytein.a = a;
+      rar->bytein.Read = &ppmd_read;
+      PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);
+      rar->range_dec.Stream = &rar->bytein;
+      Ppmd7_Construct(&rar->ppmd7_context);
+
+      if (!Ppmd7_Alloc(&rar->ppmd7_context, rar->dictionary_size, &g_szalloc))
+      {
+        archive_set_error(&a->archive, ENOMEM,
+                          "Out of memory");
+        return (ARCHIVE_FATAL);
+      }
+      if (!PpmdRAR_RangeDec_Init(&rar->range_dec))
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to initialize PPMd range decoder");
+        return (ARCHIVE_FATAL);
+      }
+      Ppmd7_Init(&rar->ppmd7_context, maxorder);
+    }
+    else
+    {
+      if (!PpmdRAR_RangeDec_Init(&rar->range_dec))
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to initialize PPMd range decoder");
+        return (ARCHIVE_FATAL);
+      }
+    }
+    rar->start_new_table = 0;
+    return (ARCHIVE_OK);
   }
   rar_br_consume(br, 1);
 
index 0bc9efda04bd71bb500c10cd492930ce9ac4628f..19e702c8006a28c38daaae8225dbc3c989d66c79 100644 (file)
@@ -66,6 +66,7 @@ static struct {
        {0, "test_read_format_cpio_bin_be.cpio"},
        {0, "test_read_format_cpio_svr4_gzip_rpm.rpm"}, /* Test RPM unwrapper */
        {0, "test_read_format_rar.rar"}, /* Uncompressed RAR test */
+       {0, "test_read_format_rar_compress_best.rar"}, /* Best Compressed RAR test */
        {0, "test_read_format_rar_compress_normal.rar"}, /* Normal Compressed RAR
                                                          * test */
        {0, "test_read_format_rar_multi_lzss_blocks.rar"}, /* Normal Compressed Multi
index 344509a0feea76ab5c46bb2ae34bc6861300fc69..2ed683c78e38eb885427aaefe225b28f47615e4e 100644 (file)
@@ -322,13 +322,13 @@ static void
 test_compress_normal(void)
 {
   const char reffile[] = "test_read_format_rar_compress_normal.rar";
-#define file1_size 20111
+  const int file1_size = 20111;
   char file1_buff[file1_size];
   const char file1_test_txt[] = "<P STYLE=\"margin-bottom: 0in\"><BR>\n"
                                 "</P>\n"
                                 "</BODY>\n"
                                 "</HTML>";
-#define file2_size 20
+  const int file2_size = 20;
   char file2_buff[file2_size];
   const char file2_test_txt[] = "test text document\r\n";
   struct archive_entry *ae;
@@ -454,6 +454,100 @@ test_multi_lzss_blocks(void)
   assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
 
+static void
+test_compress_best(void)
+{
+  const char reffile[] = "test_read_format_rar_compress_best.rar";
+  const int file1_size = 20111;
+  char file1_buff[file1_size];
+  const char file1_test_txt[] = "<P STYLE=\"margin-bottom: 0in\"><BR>\n"
+                                "</P>\n"
+                                "</BODY>\n"
+                                "</HTML>";
+  const int file2_size = 20;
+  char file2_buff[file2_size];
+  const char file2_test_txt[] = "test text document\r\n";
+  struct archive_entry *ae;
+  struct archive *a;
+
+  extract_reference_file(reffile);
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_support_filter_all(a));
+  assertA(0 == archive_read_support_format_all(a));
+  assertA(0 == archive_read_open_file(a, reffile, 10240));
+
+  /* First header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(file1_size, archive_entry_size(ae));
+  assertEqualInt(33188, archive_entry_mode(ae));
+  assertA(file1_size == archive_read_data(a, file1_buff, file1_size));
+  assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1],
+                 file1_test_txt, sizeof(file1_test_txt) - 1);
+
+    /* Second header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testlink", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(25, archive_entry_size(ae));
+  assertEqualInt(41471, archive_entry_mode(ae));
+  assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae));
+
+  /* Third header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testdir/test.txt", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(file2_size, archive_entry_size(ae));
+  assertEqualInt(33188, archive_entry_mode(ae));
+  assertA(file2_size == archive_read_data(a, file2_buff, file2_size));
+  assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)],
+                 file2_test_txt, sizeof(file2_test_txt) - 1);
+
+  /* Fourth header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testdir/LibarchiveAddingTest.html",
+                    archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(file1_size, archive_entry_size(ae));
+  assertEqualInt(33188, archive_entry_mode(ae));
+  assertA(file1_size == archive_read_data(a, file1_buff, file1_size));
+  assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1],
+                 file1_test_txt, sizeof(file1_test_txt) - 1);
+
+  /* Fifth header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testdir", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(0, archive_entry_size(ae));
+  assertEqualInt(16877, archive_entry_mode(ae));
+
+  /* Sixth header. */
+  assertA(0 == archive_read_next_header(a, &ae));
+  assertEqualString("testemptydir", archive_entry_pathname(ae));
+  assertA((int)archive_entry_mtime(ae));
+  assertA((int)archive_entry_ctime(ae));
+  assertA((int)archive_entry_atime(ae));
+  assertEqualInt(0, archive_entry_size(ae));
+  assertEqualInt(16877, archive_entry_mode(ae));
+
+  /* Test EOF */
+  assertA(1 == archive_read_next_header(a, &ae));
+  assertEqualInt(6, archive_file_count(a));
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
 DEFINE_TEST(test_read_format_rar)
 {
   test_basic();
@@ -463,4 +557,5 @@ DEFINE_TEST(test_read_format_rar)
   test_unicode_CP932();
   test_compress_normal();
   test_multi_lzss_blocks();
+  test_compress_best();
 }
diff --git a/libarchive/test/test_read_format_rar_compress_best.rar.uu b/libarchive/test/test_read_format_rar_compress_best.rar.uu
new file mode 100644 (file)
index 0000000..22fde0f
--- /dev/null
@@ -0,0 +1,274 @@
+begin 644 -
+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
+D,`P`[4$``'1E<W1E;7!T>61I<H#,G:O5/CUZ]3[$/7L`0`<`
+`
+end