mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
libretro-common: rrar_archive, a RAR reader
A reader for RAR archives in the shape of rzip_archive and r7z_archive: rrar_archive_open() over the whole file in memory, the entries, and rrar_archive_extract() to a malloc'd buffer, checked against the archive's CRC-32. What it reads: the RAR 1.5 to 4.x container, stored members, and members packed by RAR 2.9 to 4.x with any of the five methods - the LZ coder and its Huffman tables, PPMd blocks, and the standard filters (delta, x86 E8 and E8/E9, RGB, audio). Names come out as UTF-8, including the packed UTF-16 form. What it does not, and says so (RRAR_ERROR_UNSUPPORTED, or an entry's "supported" at 0): the RAR 5 container, members packed by RAR 1.5 or 2.x, solid archives, encryption, multi-volume and self-extracting archives, and filter programs other than the standard ones - there is no RAR virtual machine in it. Where it comes from. The data format - block headers, code tables, the LZ symbols, filters and how the standard ones are told and what they do - follows libarchive's archive_read_support_format_rar.c (Tim Kientzle, Andres Mejia; 2-clause BSD, its notice is in rrar_archive.c). The PPMd model and RAR's range decoder (rrar_ppmd7.c) are Igor Pavlov's public-domain Ppmd7 as libarchive carries it, decoder only, left in its own style. Nothing is taken from unrar or from GPL code. How it is put together is its own. libarchive unpacks through a sliding window and runs each filter as its block goes by. Here a member is unpacked whole into the buffer it is returned in, and that buffer is the window: a match is a copy from earlier in it. Filters are noted as they are met and run when the member is all there, since they change bytes that later matches still copy from as they were. Huffman codes are decoded from a 10-bit table with a short search behind it. Both files are C89 and build with -pedantic -Wall -Wextra. Tested (tools/archive/rar_test.c, with four of libarchive's test archives in tools/archive/rar; the next patch runs it from tools/archive/run.sh): members with LZ blocks, with PPMd blocks, with delta and E8/E9 filters, a stored one, directories and Japanese names come out with the size, CRC-32 and name they are known to have; and 1200 copies of the archives, cut short or with bits changed, are opened and unpacked under ASan and UBSan without a fault. Beyond those: the other valid archives of libarchive's RAR tests (a 241 MB member that goes between PPMd and LZ, 20 MB of many LZ blocks, random data) unpack to their CRC-32, its malformed ones are refused, and a 176 MB arcade image packed with "best" - 6156 LZ blocks, 238 PPMd blocks, 4204 delta, 747 audio and 2 RGB filters - comes out byte for byte as The Unarchiver makes it, in 2.8 s. Not exercised by any archive at hand: the plain E8 filter (E8/E9 is).
This commit is contained in:
1 parent
23a95e5ee6
commit
708513c69f
10 files changed
+3181
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,974 @@
|
||||
/* PPMd var.H model and the range decoder RAR pairs it with.
|
||||
*
|
||||
* This is Igor Pavlov's Ppmd7 from the LZMA SDK (2010-03-12, public
|
||||
* domain), itself based on Dmitry Shkarin's PPMd var.H (2001, public
|
||||
* domain), as libarchive carries it - the model, and of the two range
|
||||
* decoders only RAR's. The encoder is left out. It is kept in the SDK's
|
||||
* own style so that it can be compared with its source line by line.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rrar_ppmd7.h"
|
||||
|
||||
#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
|
||||
|
||||
#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)]
|
||||
|
||||
#define kTopValue (1 << 24)
|
||||
#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)
|
||||
|
||||
static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
|
||||
static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
|
||||
|
||||
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
|
||||
|
||||
static void Ppmd7_Update1(CPpmd7 *p);
|
||||
static void Ppmd7_Update1_0(CPpmd7 *p);
|
||||
static void Ppmd7_Update2(CPpmd7 *p);
|
||||
static void Ppmd7_UpdateBin(CPpmd7 *p);
|
||||
static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked,
|
||||
UInt32 *scale);
|
||||
|
||||
/* ----------- Base ----------- */
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static void Ppmd7_Free(CPpmd7 *p)
|
||||
{
|
||||
free(p->Base);
|
||||
p->Size = 0;
|
||||
p->Base = 0;
|
||||
}
|
||||
|
||||
static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size)
|
||||
{
|
||||
if (p->Base == 0 || p->Size != size)
|
||||
{
|
||||
/* RestartModel() below assumes that p->Size >= UNIT_SIZE
|
||||
(see the calculation of m->MinContext). */
|
||||
if (size < UNIT_SIZE) {
|
||||
return False;
|
||||
}
|
||||
Ppmd7_Free(p);
|
||||
p->AlignOffset =
|
||||
#ifdef PPMD_32BIT
|
||||
(4 - size) & 3;
|
||||
#else
|
||||
4 - (size & 3);
|
||||
#endif
|
||||
if ((p->Base = malloc(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) do { \
|
||||
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); \
|
||||
} while (0)
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static 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))));
|
||||
}
|
||||
|
||||
while (numPs != 0)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static void Ppmd7_Update2(CPpmd7 *p)
|
||||
{
|
||||
p->MinContext->SummFreq += 4;
|
||||
if ((p->FoundState->Freq += 4) > MAX_FREQ)
|
||||
Rescale(p);
|
||||
p->RunLength = p->InitRL;
|
||||
UpdateModel(p);
|
||||
}
|
||||
|
||||
/* ---------- Decode ---------- */
|
||||
|
||||
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(p->Stream->ud);
|
||||
return (p->Code < 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
|
||||
static 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 = ((UInt32)(0 - p->Low)) & (p->Bottom - 1);
|
||||
}
|
||||
p->Code = (p->Code << 8) | p->Stream->Read(p->Stream->ud);
|
||||
p->Range <<= 8;
|
||||
p->Low <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
||||
static 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]
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
void rrar_ppmd7_construct(CPpmd7 *p)
|
||||
{
|
||||
Ppmd7_Construct(p);
|
||||
}
|
||||
|
||||
Bool rrar_ppmd7_alloc(CPpmd7 *p, UInt32 size)
|
||||
{
|
||||
return Ppmd7_Alloc(p, size);
|
||||
}
|
||||
|
||||
void rrar_ppmd7_free(CPpmd7 *p)
|
||||
{
|
||||
Ppmd7_Free(p);
|
||||
}
|
||||
|
||||
void rrar_ppmd7_init(CPpmd7 *p, unsigned max_order)
|
||||
{
|
||||
Ppmd7_Init(p, max_order);
|
||||
}
|
||||
|
||||
Bool rrar_ppmd7_range_init(CPpmd7z_RangeDec *rc, IByteIn *stream)
|
||||
{
|
||||
PpmdRAR_RangeDec_CreateVTable(rc);
|
||||
rc->Stream = stream;
|
||||
rc->Code = 0;
|
||||
return PpmdRAR_RangeDec_Init(rc);
|
||||
}
|
||||
|
||||
int rrar_ppmd7_decode_symbol(CPpmd7 *p, CPpmd7z_RangeDec *rc)
|
||||
{
|
||||
return Ppmd7_DecodeSymbol(p, &rc->p);
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/* PPMd var.H model and the range decoder RAR pairs it with.
|
||||
*
|
||||
* This is Igor Pavlov's Ppmd7 from the LZMA SDK (2010-03-12, public
|
||||
* domain), itself based on Dmitry Shkarin's PPMd var.H (2001, public
|
||||
* domain), as libarchive carries it - the model, and of the two range
|
||||
* decoders only RAR's. The encoder is left out. It is kept in the SDK's
|
||||
* own style so that it can be compared with its source line by line.
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO_SDK_RRAR_PPMD7_H
|
||||
#define __LIBRETRO_SDK_RRAR_PPMD7_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef unsigned char Byte;
|
||||
typedef uint16_t UInt16;
|
||||
typedef int32_t Int32;
|
||||
typedef uint32_t UInt32;
|
||||
typedef int Bool;
|
||||
#define True 1
|
||||
#define False 0
|
||||
|
||||
/* Where the range decoder gets its bytes. */
|
||||
typedef struct
|
||||
{
|
||||
void *ud;
|
||||
Byte (*Read)(void *ud); /* reads one byte, returns 0 in case of EOF or error */
|
||||
} IByteIn;
|
||||
|
||||
/* References into the model's memory are pointers where a pointer is 32
|
||||
* bits, and offsets from its base elsewhere. */
|
||||
#if (defined(_M_IX86) || defined(__i386__) || defined(_M_ARM) || defined(__arm__)) && !defined(__aarch64__)
|
||||
#define PPMD_32BIT
|
||||
#endif
|
||||
|
||||
#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)
|
||||
|
||||
/* 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) do { \
|
||||
if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) { \
|
||||
(p)->Summ <<= 1; \
|
||||
(p)->Count = (Byte)(3 << (p)->Shift++); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Byte Symbol;
|
||||
Byte Freq;
|
||||
UInt16 SuccessorLow;
|
||||
UInt16 SuccessorHigh;
|
||||
} CPpmd_State;
|
||||
|
||||
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) do { \
|
||||
unsigned j; \
|
||||
for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \
|
||||
p[j+7] = p[j+6] = p[j+5] = p[j+4] = \
|
||||
p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PPMD7_MIN_ORDER 2
|
||||
#define PPMD7_MAX_ORDER 64
|
||||
|
||||
#define PPMD7_MIN_MEM_SIZE (1 << 11)
|
||||
#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 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;
|
||||
|
||||
/* ---------- 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 rrar_ppmd7_construct(CPpmd7 *p);
|
||||
Bool rrar_ppmd7_alloc(CPpmd7 *p, UInt32 size);
|
||||
void rrar_ppmd7_free(CPpmd7 *p);
|
||||
void rrar_ppmd7_init(CPpmd7 *p, unsigned max_order);
|
||||
/* The four bytes a block's data starts with. False if they cannot be. */
|
||||
Bool rrar_ppmd7_range_init(CPpmd7z_RangeDec *rc, IByteIn *stream);
|
||||
/* The next byte, or a negative number if the data is not PPMd's. */
|
||||
int rrar_ppmd7_decode_symbol(CPpmd7 *p, CPpmd7z_RangeDec *rc);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (rrar_archive.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* RAR archive reader for libretro-common.
|
||||
*
|
||||
* The container and the decoder follow libarchive's RAR reader
|
||||
* (archive_read_support_format_rar.c, Tim Kientzle and Andres Mejia,
|
||||
* 2-clause BSD - see rrar_archive.c for its notice); the PPMd model next
|
||||
* to it is Igor Pavlov's public-domain Ppmd7.
|
||||
*
|
||||
* Deliberately not a general RAR implementation. libretro-common opens
|
||||
* archives of ROMs, so this covers what those are packed as and rejects
|
||||
* everything else rather than half-supporting it:
|
||||
*
|
||||
* - the RAR 1.5 to 4.x container ("Rar!\x1a\x07\x00")
|
||||
* - stored members of any version
|
||||
* - members packed by RAR 2.9 to 4.x (unpack version 29 and 36), all
|
||||
* five methods: the LZ coder with its Huffman tables, PPMd blocks,
|
||||
* and the standard filters (delta, x86 E8 and E8/E9, RGB, audio)
|
||||
*
|
||||
* Not supported, and said so per entry or at open: the RAR 5 container,
|
||||
* members packed by RAR 1.5 or 2.x, solid archives, encryption,
|
||||
* multi-volume archives, self-extracting archives, and filter programs
|
||||
* other than the standard ones (there is no RAR virtual machine here).
|
||||
*
|
||||
* ------------------------------------------------------------------
|
||||
* On untrusted input
|
||||
* ------------------------------------------------------------------
|
||||
*
|
||||
* Everything here comes from the file. Header sizes, member sizes,
|
||||
* match distances and lengths, filter positions and the code tables are
|
||||
* all the archive's to claim. So:
|
||||
*
|
||||
* - every header is checked against the archive's length before it is
|
||||
* read, and against its own checksum
|
||||
* - a member is decoded into a buffer of the size its header gives and
|
||||
* never past it; a match that reaches back before the start of the
|
||||
* member reads zeros
|
||||
* - a code table that cannot be a prefix code is refused
|
||||
* - filters are checked against the member's size before they run
|
||||
* - what comes out is checked against the member's CRC-32
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO_SDK_RRAR_ARCHIVE_H
|
||||
#define __LIBRETRO_SDK_RRAR_ARCHIVE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#define RRAR_OK 0
|
||||
#define RRAR_ERROR_DATA (-1)
|
||||
#define RRAR_ERROR_PARAM (-2)
|
||||
#define RRAR_ERROR_MEM (-3)
|
||||
#define RRAR_ERROR_UNSUPPORTED (-4)
|
||||
#define RRAR_ERROR_CRC (-5)
|
||||
|
||||
/* Signature length at the head of every archive. */
|
||||
#define RRAR_SIGNATURE_SIZE 7
|
||||
|
||||
/* One entry in the archive. */
|
||||
typedef struct rrar_entry
|
||||
{
|
||||
/* UTF-8 name, '/' between its parts, NUL terminated. Owned by the
|
||||
* archive. */
|
||||
const char *name;
|
||||
uint64_t size; /* unpacked */
|
||||
uint64_t packed_size;
|
||||
uint64_t data_offset; /* of the packed bytes in the archive */
|
||||
uint32_t crc;
|
||||
uint8_t method; /* 0x30 stored, 0x31 to 0x35 packed */
|
||||
uint8_t version; /* of RAR needed to unpack: 29 is 2.9 */
|
||||
uint8_t is_dir;
|
||||
/* 0: this reader cannot extract the entry (see the list above) */
|
||||
uint8_t supported;
|
||||
} rrar_entry_t;
|
||||
|
||||
typedef struct rrar_archive rrar_archive_t;
|
||||
|
||||
/**
|
||||
* rrar_archive_open:
|
||||
* @out : receives the opened archive
|
||||
* @data : the whole archive file in memory
|
||||
* @len : length of @data
|
||||
*
|
||||
* Parses the archive's headers. @data must stay valid and unmodified
|
||||
* until rrar_archive_close(); the archive borrows it rather than
|
||||
* copying.
|
||||
*
|
||||
* Returns: RRAR_OK, or a negative RRAR_ERROR_* code. A malformed archive
|
||||
* is an error, never a partial success; an archive of a kind this reader
|
||||
* does not read (RAR 5, multi-volume, encrypted headers) is
|
||||
* RRAR_ERROR_UNSUPPORTED.
|
||||
*/
|
||||
int rrar_archive_open(rrar_archive_t **out,
|
||||
const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* rrar_archive_close:
|
||||
* @a : archive, may be NULL
|
||||
*
|
||||
* Releases everything rrar_archive_open() allocated.
|
||||
*/
|
||||
void rrar_archive_close(rrar_archive_t *a);
|
||||
|
||||
/**
|
||||
* rrar_archive_num_entries:
|
||||
* @a : opened archive
|
||||
*
|
||||
* Returns: the number of entries, including directories.
|
||||
*/
|
||||
uint32_t rrar_archive_num_entries(const rrar_archive_t *a);
|
||||
|
||||
/**
|
||||
* rrar_archive_entry:
|
||||
* @a : opened archive
|
||||
* @index : entry index, below rrar_archive_num_entries()
|
||||
*
|
||||
* Returns: the entry, or NULL if @index is out of range.
|
||||
*/
|
||||
const rrar_entry_t *rrar_archive_entry(const rrar_archive_t *a,
|
||||
uint32_t index);
|
||||
|
||||
/**
|
||||
* rrar_archive_extract:
|
||||
* @a : opened archive
|
||||
* @index : entry index
|
||||
* @out : receives a malloc'd buffer holding the entry's data
|
||||
* @out_len : receives the entry's size
|
||||
*
|
||||
* Unpacks one entry and checks it against the archive's CRC-32. The
|
||||
* caller frees *@out. An empty entry yields a valid one-byte buffer and
|
||||
* a length of zero.
|
||||
*
|
||||
* Returns: RRAR_OK, or a negative RRAR_ERROR_* code, in which case
|
||||
* *@out is NULL.
|
||||
*/
|
||||
int rrar_archive_extract(rrar_archive_t *a, uint32_t index,
|
||||
uint8_t **out, size_t *out_len);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
RAR archives for tools/archive/rar_test.c.
|
||||
|
||||
They are test data of libarchive (libarchive/test/test_read_format_rar*.rar.uu,
|
||||
2-clause BSD), decoded and otherwise as they come:
|
||||
|
||||
lowdist_reset.rar RAR 2.9, one LZ block whose low distance bits repeat
|
||||
compress_best.rar RAR 2.9 "best": PPMd blocks; a stored member; directories
|
||||
filter.rar RAR 2.9: a Windows program, with delta and x86 (E8/E9) filters
|
||||
unicode.rar names in the packed UTF-16 form, Japanese
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,235 @@
|
||||
/* libretro-common's RAR reader on real archives.
|
||||
*
|
||||
* rar_test <dir with the archives of tools/archive/rar>
|
||||
*
|
||||
* Every member that the reader says it can unpack has to come out, which
|
||||
* means with the CRC-32 the archive gives for it: the reader checks that
|
||||
* itself. Sizes, checksums and names are checked here against what they
|
||||
* are known to be. Then each archive is cut short and has bytes changed,
|
||||
* a few hundred times over: whatever the reader makes of that, it must
|
||||
* not read or write where it should not (the test is built with ASan and
|
||||
* UBSan), and what it does hand out still has the archive's CRC-32.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rar/rrar_archive.h>
|
||||
#include <encodings/crc32.h>
|
||||
|
||||
static int failures;
|
||||
|
||||
#define CHECK(cond, what) \
|
||||
do { if (!(cond)) { printf("FAIL %s:%d: %s (%s)\n", __FILE__, __LINE__, #cond, what); failures++; } } while (0)
|
||||
|
||||
static uint8_t *read_file(const char *dir, const char *name, size_t *len)
|
||||
{
|
||||
char path[1024];
|
||||
FILE *f;
|
||||
uint8_t *buf;
|
||||
long n;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
if (!(f = fopen(path, "rb")))
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buf = (uint8_t *)malloc(n > 0 ? (size_t)n : 1);
|
||||
if (buf && fread(buf, 1, (size_t)n, f) != (size_t)n)
|
||||
{
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
fclose(f);
|
||||
*len = (size_t)n;
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct expect
|
||||
{
|
||||
const char *name;
|
||||
uint64_t size;
|
||||
uint32_t crc;
|
||||
int is_dir;
|
||||
};
|
||||
|
||||
/* Opens the archive, unpacks everything in it that can be, and finds
|
||||
* each of @want in it. */
|
||||
static void check(const char *dir, const char *file,
|
||||
const struct expect *want, unsigned num_want)
|
||||
{
|
||||
size_t len = 0;
|
||||
uint8_t *data = read_file(dir, file, &len);
|
||||
rrar_archive_t *a = NULL;
|
||||
uint32_t i;
|
||||
unsigned w;
|
||||
|
||||
CHECK(data != NULL, file);
|
||||
if (!data)
|
||||
return;
|
||||
CHECK(rrar_archive_open(&a, data, len) == RRAR_OK, file);
|
||||
if (a)
|
||||
{
|
||||
for (i = 0; i < rrar_archive_num_entries(a); i++)
|
||||
{
|
||||
const rrar_entry_t *e = rrar_archive_entry(a, i);
|
||||
uint8_t *out = NULL;
|
||||
size_t out_len = 0;
|
||||
|
||||
if (e->is_dir)
|
||||
continue;
|
||||
CHECK(e->supported, e->name);
|
||||
CHECK(rrar_archive_extract(a, i, &out, &out_len) == RRAR_OK, e->name);
|
||||
CHECK(out != NULL && out_len == e->size, e->name);
|
||||
if (out)
|
||||
CHECK(encoding_crc32(0, out, out_len) == e->crc, e->name);
|
||||
free(out);
|
||||
}
|
||||
for (w = 0; w < num_want; w++)
|
||||
{
|
||||
const rrar_entry_t *e = NULL;
|
||||
for (i = 0; i < rrar_archive_num_entries(a); i++)
|
||||
{
|
||||
e = rrar_archive_entry(a, i);
|
||||
if (!strcmp(e->name, want[w].name))
|
||||
break;
|
||||
e = NULL;
|
||||
}
|
||||
CHECK(e != NULL, want[w].name);
|
||||
if (e)
|
||||
{
|
||||
CHECK((e->is_dir != 0) == (want[w].is_dir != 0), want[w].name);
|
||||
if (!e->is_dir)
|
||||
{
|
||||
CHECK(e->size == want[w].size, want[w].name);
|
||||
CHECK(e->crc == want[w].crc, want[w].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK(rrar_archive_entry(a, rrar_archive_num_entries(a)) == NULL, file);
|
||||
}
|
||||
rrar_archive_close(a);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* The archive cut short and with bytes changed. */
|
||||
static void mangle(const char *dir, const char *file)
|
||||
{
|
||||
size_t len = 0;
|
||||
uint8_t *data = read_file(dir, file, &len);
|
||||
uint8_t *copy;
|
||||
unsigned round;
|
||||
uint32_t seed = 12345;
|
||||
|
||||
if (!data || !(copy = (uint8_t *)malloc(len)))
|
||||
{
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
for (round = 0; round < 300; round++)
|
||||
{
|
||||
rrar_archive_t *a = NULL;
|
||||
size_t use = len;
|
||||
uint32_t i;
|
||||
|
||||
memcpy(copy, data, len);
|
||||
seed = seed * 1103515245u + 12345u;
|
||||
if (round % 3 == 0)
|
||||
use = (seed >> 8) % (len + 1); /* cut short */
|
||||
else
|
||||
{
|
||||
unsigned k, changes = 1 + (seed >> 28);
|
||||
for (k = 0; k < changes; k++)
|
||||
{
|
||||
seed = seed * 1103515245u + 12345u;
|
||||
copy[(seed >> 8) % len] ^= (uint8_t)(1u << (seed & 7));
|
||||
}
|
||||
}
|
||||
if (rrar_archive_open(&a, copy, use) != RRAR_OK)
|
||||
continue;
|
||||
for (i = 0; i < rrar_archive_num_entries(a); i++)
|
||||
{
|
||||
const rrar_entry_t *e = rrar_archive_entry(a, i);
|
||||
uint8_t *out = NULL;
|
||||
size_t out_len = 0;
|
||||
|
||||
/* (a size that was changed into gigabytes is only refused for
|
||||
* want of memory: not what this is after) */
|
||||
if (e->is_dir || e->size > 64u * 1024 * 1024)
|
||||
continue;
|
||||
if (rrar_archive_extract(a, i, &out, &out_len) == RRAR_OK)
|
||||
{
|
||||
CHECK(out_len == e->size, file);
|
||||
CHECK(encoding_crc32(0, out, out_len) == e->crc, file);
|
||||
}
|
||||
else
|
||||
CHECK(out == NULL, file);
|
||||
free(out);
|
||||
}
|
||||
rrar_archive_close(a);
|
||||
}
|
||||
free(copy);
|
||||
free(data);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const struct expect lowdist[] = {
|
||||
{ "lowdist-reset.bin", 64, 0x6ff838dcu, 0 },
|
||||
};
|
||||
static const struct expect best[] = {
|
||||
{ "LibarchiveAddingTest.html", 20111, 0x5e05a663u, 0 },
|
||||
{ "testlink", 25, 0x11fcd3f1u, 0 },
|
||||
{ "testdir/test.txt", 20, 0xbec8a242u, 0 },
|
||||
{ "testdir/LibarchiveAddingTest.html", 20111, 0x5e05a663u, 0 },
|
||||
{ "testdir", 0, 0, 1 },
|
||||
{ "testemptydir", 0, 0, 1 },
|
||||
};
|
||||
static const struct expect filter[] = {
|
||||
{ "bsdcat.exe", 204288, 0x4db10349u, 0 },
|
||||
};
|
||||
static const struct expect unicode[] = {
|
||||
{ "\xe8\xa1\xa8\xe3\x81\xa0\xe3\x82\x88/\xe6\x96\xb0\xe3\x81\x97\xe3\x81\x84\xe3\x83\x95\xe3\x82\xa9\xe3\x83\xab\xe3\x83\x80/"
|
||||
"\xe6\x96\xb0\xe8\xa6\x8f\xe3\x83\x86\xe3\x82\xad\xe3\x82\xb9\xe3\x83\x88 \xe3\x83\x89\xe3\x82\xad\xe3\x83\xa5\xe3\x83\xa1\xe3\x83\xb3\xe3\x83\x88.txt",
|
||||
0, 0, 0 },
|
||||
{ "\xe8\xa1\xa8\xe3\x81\xa0\xe3\x82\x88/\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab", 54, 0x5dfb8225u, 0 },
|
||||
{ "abcdefghijklmnopqrs\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt", 16, 0x15b6d005u, 0 },
|
||||
{ "\xe8\xa1\xa8\xe3\x81\xa0\xe3\x82\x88", 0, 0, 1 },
|
||||
};
|
||||
const char *dir;
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
fprintf(stderr, "usage: rar_test <dir>\n");
|
||||
return 2;
|
||||
}
|
||||
dir = argv[1];
|
||||
|
||||
check(dir, "lowdist_reset.rar", lowdist, sizeof(lowdist) / sizeof(lowdist[0]));
|
||||
check(dir, "compress_best.rar", best, sizeof(best) / sizeof(best[0]));
|
||||
check(dir, "filter.rar", filter, sizeof(filter) / sizeof(filter[0]));
|
||||
check(dir, "unicode.rar", unicode, sizeof(unicode) / sizeof(unicode[0]));
|
||||
|
||||
mangle(dir, "lowdist_reset.rar");
|
||||
mangle(dir, "compress_best.rar");
|
||||
mangle(dir, "filter.rar");
|
||||
mangle(dir, "unicode.rar");
|
||||
|
||||
{
|
||||
/* what is no RAR, and what is one of a kind not read */
|
||||
rrar_archive_t *a = (rrar_archive_t *)1;
|
||||
static const uint8_t rar5[] = { 'R', 'a', 'r', '!', 0x1a, 0x07, 0x01, 0x00, 0, 0, 0, 0 };
|
||||
CHECK(rrar_archive_open(&a, (const uint8_t *)"PK\3\4....", 8) == RRAR_ERROR_DATA && !a, "zip");
|
||||
CHECK(rrar_archive_open(&a, rar5, sizeof(rar5)) == RRAR_ERROR_UNSUPPORTED && !a, "rar5");
|
||||
CHECK(rrar_archive_open(&a, rar5, 3) == RRAR_ERROR_PARAM && !a, "short");
|
||||
}
|
||||
|
||||
if (failures)
|
||||
{
|
||||
printf("%d checks failed\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("rar_test: PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user