This commit is contained in:
2026-03-23 12:11:07 +01:00
commit e64eb40b38
4573 changed files with 3117439 additions and 0 deletions

711
external/unarr/rar/filter-rar.c vendored Normal file
View File

@@ -0,0 +1,711 @@
/* Copyright 2018 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "rar.h"
#include "rarvm.h"
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRARVirtualMachine.m */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR30Filter.m */
struct MemBitReader {
const uint8_t *bytes;
size_t length;
size_t offset;
uint64_t bits;
int available;
bool at_eof;
};
struct RARProgramCode {
RARProgram *prog;
uint8_t *staticdata;
uint32_t staticdatalen;
uint8_t *globalbackup;
uint32_t globalbackuplen;
uint64_t fingerprint;
uint32_t usagecount;
uint32_t oldfilterlength;
struct RARProgramCode *next;
};
struct RARFilter {
struct RARProgramCode *prog;
uint32_t initialregisters[8];
uint8_t *globaldata;
uint32_t globaldatalen;
size_t blockstartpos;
uint32_t blocklength;
uint32_t filteredblockaddress;
uint32_t filteredblocklength;
struct RARFilter *next;
};
static bool br_fill(struct MemBitReader *br, int bits)
{
while (br->available < bits && br->offset < br->length) {
br->bits = (br->bits << 8) | br->bytes[br->offset++];
br->available += 8;
}
if (bits > br->available) {
br->at_eof = true;
return false;
}
return true;
}
static inline uint32_t br_bits(struct MemBitReader *br, int bits)
{
if (bits > br->available && (br->at_eof || !br_fill(br, bits)))
return 0;
return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1));
}
static inline bool br_available(struct MemBitReader *br, int bits)
{
return !br->at_eof && (bits <= br->available || br_fill(br, bits));
}
static uint32_t br_next_rarvm_number(struct MemBitReader *br)
{
uint32_t val;
switch (br_bits(br, 2)) {
case 0:
return br_bits(br, 4);
case 1:
val = br_bits(br, 8);
if (val >= 16)
return val;
return 0xFFFFFF00 | (val << 4) | br_bits(br, 4);
case 2:
return br_bits(br, 16);
default:
return br_bits(br, 32);
}
}
static void bw_write32le(uint8_t *dst, uint32_t value)
{
dst[0] = value & 0xFF;
dst[1] = (value >> 8) & 0xFF;
dst[2] = (value >> 16) & 0xFF;
dst[3] = (value >> 24) & 0xFF;
}
static void rar_delete_program(struct RARProgramCode *prog)
{
while (prog) {
struct RARProgramCode *next = prog->next;
RARDeleteProgram(prog->prog);
free(prog->staticdata);
free(prog->globalbackup);
free(prog);
prog = next;
}
}
static bool rar_parse_operand(struct MemBitReader *br, uint8_t instruction, bool bytemode, uint32_t instrcount, uint8_t *addressmode, uint32_t *value)
{
if (br_bits(br, 1)) {
*addressmode = RARRegisterAddressingMode((uint8_t)br_bits(br, 3));
*value = 0;
}
else if (br_bits(br, 1)) {
if (br_bits(br, 1)) {
if (br_bits(br, 1))
*addressmode = RARAbsoluteAddressingMode;
else
*addressmode = RARIndexedAbsoluteAddressingMode((uint8_t)br_bits(br, 3));
*value = br_next_rarvm_number(br);
}
else {
*addressmode = RARRegisterIndirectAddressingMode((uint8_t)br_bits(br, 3));
*value = 0;
}
}
else {
*addressmode = RARImmediateAddressingMode;
if (!bytemode)
*value = br_next_rarvm_number(br);
else
*value = br_bits(br, 8);
if (instrcount != (uint32_t)-1 && RARInstructionIsRelativeJump(instruction)) {
if (*value >= 256) /* absolute address */
*value -= 256;
else { /* relative address */
if (*value >= 136)
*value -= 264;
else if (*value >= 16)
*value -= 8;
else if (*value >= 8)
*value -= 16;
*value += instrcount;
}
}
}
return !br->at_eof;
}
static struct RARProgramCode *rar_compile_program(const uint8_t *bytes, size_t length)
{
struct MemBitReader br = { 0 };
struct RARProgramCode *prog;
uint32_t instrcount = 0;
uint8_t xor;
size_t i;
xor = 0;
for (i = 1; i < length; i++)
xor ^= bytes[i];
if (!length || xor != bytes[0])
return NULL;
br.bytes = bytes;
br.length = length;
br.offset = 1;
prog = calloc(1, sizeof(*prog));
if (!prog)
return NULL;
prog->prog = RARCreateProgram();
if (!prog->prog) {
rar_delete_program(prog);
return NULL;
}
prog->fingerprint = ar_crc32(0, bytes, length) | ((uint64_t)length << 32);
if (br_bits(&br, 1)) {
prog->staticdatalen = br_next_rarvm_number(&br) + 1;
prog->staticdata = malloc(prog->staticdatalen);
if (!prog->staticdata) {
rar_delete_program(prog);
return NULL;
}
for (i = 0; i < prog->staticdatalen; i++)
prog->staticdata[i] = (uint8_t)br_bits(&br, 8);
}
while (br_available(&br, 8)) {
bool ok = true;
uint8_t instruction = (uint8_t)br_bits(&br, 4);
bool bytemode = false;
int numargs = 0;
uint8_t addrmode1 = 0, addrmode2 = 0;
uint32_t value1 = 0, value2 = 0;
if ((instruction & 0x08))
instruction = ((instruction << 2) | (uint8_t)br_bits(&br, 2)) - 24;
if (RARInstructionHasByteMode(instruction))
bytemode = br_bits(&br, 1) != 0;
ok = RARProgramAddInstr(prog->prog, instruction, bytemode);
numargs = NumberOfRARInstructionOperands(instruction);
if (ok && numargs >= 1)
ok = rar_parse_operand(&br, instruction, bytemode, instrcount, &addrmode1, &value1);
if (ok && numargs == 2)
ok = rar_parse_operand(&br, instruction, bytemode, (uint32_t)-1, &addrmode2, &value2);
if (ok)
ok = RARSetLastInstrOperands(prog->prog, addrmode1, value1, addrmode2, value2);
if (!ok) {
warn("Invalid RAR program instruction");
rar_delete_program(prog);
return NULL;
}
instrcount++;
}
if (!RARIsProgramTerminated(prog->prog)) {
if (!RARProgramAddInstr(prog->prog, RARRetInstruction, false)) {
rar_delete_program(prog);
return NULL;
}
}
return prog;
}
static bool rar_execute_filter_prog(struct RARFilter *filter, RARVirtualMachine *vm)
{
uint32_t newgloballength;
uint32_t globallength = filter->globaldatalen;
if (globallength > RARProgramSystemGlobalSize)
globallength = RARProgramSystemGlobalSize;
memcpy(&vm->memory[RARProgramSystemGlobalAddress], filter->globaldata, globallength);
if (filter->prog->staticdata) {
uint32_t staticlength = filter->prog->staticdatalen;
if (staticlength > RARProgramUserGlobalSize - globallength)
staticlength = RARProgramUserGlobalSize - globallength;
memcpy(&vm->memory[RARProgramUserGlobalAddress], filter->prog->staticdata, staticlength);
}
RARSetVirtualMachineRegisters(vm, filter->initialregisters);
if (!RARExecuteProgram(vm, filter->prog->prog)) {
warn("Error while executing program in RAR VM");
return false;
}
newgloballength = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x30);
if (newgloballength > RARProgramUserGlobalSize)
newgloballength = RARProgramUserGlobalSize;
if (newgloballength > 0) {
uint32_t newglobaldatalength = RARProgramSystemGlobalSize + newgloballength;
if (newglobaldatalength > filter->globaldatalen) {
uint8_t *newglobaldata = malloc(newglobaldatalength);
if (!newglobaldata)
return false;
free(filter->globaldata);
filter->globaldata = newglobaldata;
}
filter->globaldatalen = newglobaldatalength;
memcpy(filter->globaldata, &vm->memory[RARProgramSystemGlobalAddress], filter->globaldatalen);
}
else
filter->globaldatalen = 0;
return true;
}
static struct RARFilter *rar_create_filter(struct RARProgramCode *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length)
{
struct RARFilter *filter;
filter = calloc(1, sizeof(*filter));
if (!filter)
return NULL;
filter->prog = prog;
filter->globaldatalen = globaldatalen > RARProgramSystemGlobalSize ? globaldatalen : RARProgramSystemGlobalSize;
filter->globaldata = calloc(1, filter->globaldatalen);
if (!filter->globaldata) {
free(filter);
return NULL;
}
if (globaldata)
memcpy(filter->globaldata, globaldata, globaldatalen);
if (registers)
memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters));
filter->blockstartpos = startpos;
filter->blocklength = length;
return filter;
}
static void rar_delete_filter(struct RARFilter *filter)
{
while (filter) {
struct RARFilter *next = filter->next;
free(filter->globaldata);
free(filter);
filter = next;
}
}
static bool rar_execute_filter_delta(struct RARFilter *filter, RARVirtualMachine *vm)
{
uint32_t length = filter->initialregisters[4];
uint32_t numchannels = filter->initialregisters[0];
uint8_t *src, *dst;
uint32_t i, idx;
if (length > RARProgramWorkSize / 2)
return false;
src = &vm->memory[0];
dst = &vm->memory[length];
for (i = 0; i < numchannels; i++) {
uint8_t lastbyte = 0;
for (idx = i; idx < length; idx += numchannels)
lastbyte = dst[idx] = lastbyte - *src++;
}
filter->filteredblockaddress = length;
filter->filteredblocklength = length;
return true;
}
static bool rar_execute_filter_e8(struct RARFilter *filter, RARVirtualMachine *vm, size_t pos, bool e9also)
{
uint32_t length = filter->initialregisters[4];
uint32_t filesize = 0x1000000;
if (length > RARProgramWorkSize || length < 5)
return false;
for (uint32_t i = 0; i <= length - 5; i++) {
if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9)) {
uint32_t currpos = (uint32_t)pos + i + 1;
int32_t address = (int32_t)RARVirtualMachineRead32(vm, i + 1);
if (address < 0 && currpos >= (uint32_t)-address)
RARVirtualMachineWrite32(vm, i + 1, address + filesize);
else if (address >= 0 && (uint32_t)address < filesize)
RARVirtualMachineWrite32(vm, i + 1, address - currpos);
i += 4;
}
}
filter->filteredblockaddress = 0;
filter->filteredblocklength = length;
return true;
}
static bool rar_execute_filter_rgb(struct RARFilter *filter, RARVirtualMachine *vm)
{
uint32_t stride = filter->initialregisters[0];
uint32_t byteoffset = filter->initialregisters[1];
uint32_t blocklength = filter->initialregisters[4];
uint8_t *src, *dst;
if (blocklength < 2 || blocklength > (RARProgramWorkSize / 2) || stride > blocklength)
return false;
src = &vm->memory[0];
dst = &vm->memory[blocklength];
for (int i = 0; i < 3; i++) {
uint8_t byte = 0;
uint8_t *prev = dst + i - stride;
for (uint32_t j = i; j < blocklength; j += 3) {
if (prev >= dst) {
uint32_t delta1 = abs(prev[3] - prev[0]);
uint32_t delta2 = abs(byte - prev[0]);
uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]);
if (delta1 > delta2 || delta1 > delta3)
byte = delta2 <= delta3 ? prev[3] : prev[0];
}
byte -= *src++;
dst[j] = byte;
prev += 3;
}
}
for (uint32_t i = byteoffset; i < blocklength - 2; i += 3) {
dst[i] += dst[i + 1];
dst[i + 2] += dst[i + 1];
}
filter->filteredblockaddress = blocklength;
filter->filteredblocklength = blocklength;
return true;
}
static bool rar_execute_filter_audio(struct RARFilter *filter, RARVirtualMachine *vm)
{
uint32_t length = filter->initialregisters[4];
uint32_t numchannels = filter->initialregisters[0];
uint8_t *src, *dst;
uint32_t i, j;
if (length > RARProgramWorkSize / 2)
return false;
src = &vm->memory[0];
dst = &vm->memory[length];
for (i = 0; i < numchannels; i++) {
struct AudioState state;
memset(&state, 0, sizeof(state));
for (j = i; j < length; j += numchannels) {
int8_t delta = (int8_t)*src++;
uint8_t predbyte, byte;
int prederror;
state.delta[2] = state.delta[1];
state.delta[1] = state.lastdelta - state.delta[0];
state.delta[0] = state.lastdelta;
predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
byte = (predbyte - delta) & 0xFF;
prederror = delta << 3;
state.error[0] += abs(prederror);
state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]);
state.lastdelta = (int8_t)(byte - state.lastbyte);
dst[j] = state.lastbyte = byte;
if (!(state.count++ & 0x1F)) {
uint8_t k, idx = 0;
for (k = 1; k < 7; k++) {
if (state.error[k] < state.error[idx])
idx = k;
}
memset(state.error, 0, sizeof(state.error));
switch (idx) {
case 1: if (state.weight[0] >= -16) state.weight[0]--; break;
case 2: if (state.weight[0] < 16) state.weight[0]++; break;
case 3: if (state.weight[1] >= -16) state.weight[1]--; break;
case 4: if (state.weight[1] < 16) state.weight[1]++; break;
case 5: if (state.weight[2] >= -16) state.weight[2]--; break;
case 6: if (state.weight[2] < 16) state.weight[2]++; break;
}
}
}
}
filter->filteredblockaddress = length;
filter->filteredblocklength = length;
return true;
}
static bool rar_execute_filter(struct RARFilter *filter, RARVirtualMachine *vm, size_t pos)
{
if (filter->prog->fingerprint == 0x1D0E06077D)
return rar_execute_filter_delta(filter, vm);
if (filter->prog->fingerprint == 0x35AD576887)
return rar_execute_filter_e8(filter, vm, pos, false);
if (filter->prog->fingerprint == 0x393CD7E57E)
return rar_execute_filter_e8(filter, vm, pos, true);
if (filter->prog->fingerprint == 0x951C2C5DC8)
return rar_execute_filter_rgb(filter, vm);
if (filter->prog->fingerprint == 0xD8BC85E701)
return rar_execute_filter_audio(filter, vm);
log("Unknown parsing filter 0x%x%08x", (uint32_t)(filter->prog->fingerprint >> 32), (uint32_t)filter->prog->fingerprint);
/* XADRAR30Filter.m @executeOnVirtualMachine claims that this is required */
if (filter->prog->globalbackuplen > RARProgramSystemGlobalSize) {
uint8_t *newglobaldata = malloc(filter->prog->globalbackuplen);
if (newglobaldata) {
free(filter->globaldata);
filter->globaldata = newglobaldata;
filter->globaldatalen = filter->prog->globalbackuplen;
memcpy(filter->globaldata, filter->prog->globalbackup, filter->prog->globalbackuplen);
}
}
filter->initialregisters[6] = (uint32_t)pos;
bw_write32le(&filter->globaldata[0x24], (uint32_t)pos);
bw_write32le(&filter->globaldata[0x28], (uint32_t)((uint64_t)pos >> 32));
if (!rar_execute_filter_prog(filter, vm))
return false;
filter->filteredblockaddress = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x20) & RARProgramMemoryMask;
filter->filteredblocklength = RARVirtualMachineRead32(vm, RARProgramSystemGlobalAddress + 0x1C) & RARProgramMemoryMask;
if (filter->filteredblockaddress + filter->filteredblocklength >= RARProgramMemorySize) {
filter->filteredblockaddress = filter->filteredblocklength = 0;
return false;
}
if (filter->globaldatalen > RARProgramSystemGlobalSize) {
uint8_t *newglobalbackup = malloc(filter->globaldatalen);
if (newglobalbackup) {
free(filter->prog->globalbackup);
filter->prog->globalbackup = newglobalbackup;
filter->prog->globalbackuplen = filter->globaldatalen;
memcpy(filter->prog->globalbackup, filter->globaldata, filter->globaldatalen);
}
}
else
filter->prog->globalbackuplen = 0;
return true;
}
bool rar_parse_filter(ar_archive_rar *rar, const uint8_t *bytes, uint16_t length, uint8_t flags)
{
struct ar_archive_rar_uncomp_v3 *uncomp = &rar->uncomp.state.v3;
struct ar_archive_rar_filters *filters = &uncomp->filters;
struct MemBitReader br = { 0 };
struct RARProgramCode *prog;
struct RARFilter *filter, **nextfilter;
uint32_t numprogs, num, blocklength, globaldatalen;
uint8_t *globaldata;
size_t blockstartpos;
uint32_t registers[8] = { 0 };
uint32_t i;
br.bytes = bytes;
br.length = length;
numprogs = 0;
for (prog = filters->progs; prog; prog = prog->next)
numprogs++;
if ((flags & 0x80)) {
num = br_next_rarvm_number(&br);
if (num == 0) {
rar_delete_filter(filters->stack);
filters->stack = NULL;
rar_delete_program(filters->progs);
filters->progs = NULL;
}
else
num--;
if (num > numprogs) {
warn("Invalid program number");
return false;
}
filters->lastfilternum = num;
}
else
num = filters->lastfilternum;
prog = filters->progs;
for (i = 0; i < num; i++) {
if (prog) {
prog = prog->next;
}
else {
warn("Invalid filter programm");
return false;
}
}
if (prog)
prog->usagecount++;
blockstartpos = br_next_rarvm_number(&br) + (size_t)lzss_position(&rar->uncomp.lzss);
if ((flags & 0x40))
blockstartpos += 258;
if ((flags & 0x20))
blocklength = br_next_rarvm_number(&br);
else
blocklength = prog ? prog->oldfilterlength : 0;
registers[3] = RARProgramSystemGlobalAddress;
registers[4] = blocklength;
registers[5] = prog ? prog->usagecount : 0;
registers[7] = RARProgramMemorySize;
if ((flags & 0x10)) {
uint8_t mask = (uint8_t)br_bits(&br, 7);
for (i = 0; i < 7; i++) {
if ((mask & (1 << i)))
registers[i] = br_next_rarvm_number(&br);
}
}
if (!prog) {
uint32_t len = br_next_rarvm_number(&br);
uint8_t *bytecode;
struct RARProgramCode **next;
if (len == 0 || len > 0x10000) {
warn("Invalid RARVM bytecode length");
return false;
}
bytecode = malloc(len);
if (!bytecode)
return false;
for (i = 0; i < len; i++)
bytecode[i] = (uint8_t)br_bits(&br, 8);
prog = rar_compile_program(bytecode, len);
if (!prog) {
free(bytecode);
return false;
}
free(bytecode);
next = &filters->progs;
while (*next)
next = &(*next)->next;
*next = prog;
}
prog->oldfilterlength = blocklength;
globaldata = NULL;
globaldatalen = 0;
if ((flags & 0x08)) {
globaldatalen = br_next_rarvm_number(&br);
if (globaldatalen > RARProgramUserGlobalSize) {
warn("Invalid RARVM data length");
return false;
}
globaldata = malloc(globaldatalen + RARProgramSystemGlobalSize);
if (!globaldata)
return false;
for (i = 0; i < globaldatalen; i++)
globaldata[i + RARProgramSystemGlobalSize] = (uint8_t)br_bits(&br, 8);
}
if (br.at_eof) {
free(globaldata);
return false;
}
filter = rar_create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength);
free(globaldata);
if (!filter)
return false;
for (i = 0; i < 7; i++)
bw_write32le(&filter->globaldata[i * 4], registers[i]);
bw_write32le(&filter->globaldata[0x1C], blocklength);
bw_write32le(&filter->globaldata[0x20], 0);
bw_write32le(&filter->globaldata[0x2C], prog->usagecount);
nextfilter = &filters->stack;
while (*nextfilter)
nextfilter = &(*nextfilter)->next;
*nextfilter = filter;
if (!filters->stack->next)
filters->filterstart = blockstartpos;
return true;
}
bool rar_run_filters(ar_archive_rar *rar)
{
struct ar_archive_rar_filters *filters = &rar->uncomp.state.v3.filters;
struct RARFilter *filter = filters->stack;
size_t start = filters->filterstart;
size_t end = start + filter->blocklength;
uint32_t lastfilteraddress;
uint32_t lastfilterlength;
filters->filterstart = SIZE_MAX;
if ((size_t)rar_expand(rar, end) != end) {
warn("Failed to expand the expected amout of bytes");
return false;
}
if (!filters->vm) {
filters->vm = calloc(1, sizeof(*filters->vm));
if (!filters->vm)
return false;
}
lzss_copy_bytes_from_window(&rar->uncomp.lzss, filters->vm->memory, start, filter->blocklength);
if (!rar_execute_filter(filter, filters->vm, rar->progress.bytes_done)) {
warn("Failed to execute parsing filter");
return false;
}
lastfilteraddress = filter->filteredblockaddress;
lastfilterlength = filter->filteredblocklength;
filters->stack = filter->next;
filter->next = NULL;
rar_delete_filter(filter);
while ((filter = filters->stack) != NULL && filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength) {
memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength);
if (!rar_execute_filter(filter, filters->vm, rar->progress.bytes_done)) {
warn("Failed to execute parsing filter");
return false;
}
lastfilteraddress = filter->filteredblockaddress;
lastfilterlength = filter->filteredblocklength;
filters->stack = filter->next;
filter->next = NULL;
rar_delete_filter(filter);
}
if (filters->stack) {
if (filters->stack->blockstartpos < end) {
warn("Bad filter order");
return false;
}
filters->filterstart = filters->stack->blockstartpos;
}
filters->lastend = end;
filters->bytes = &filters->vm->memory[lastfilteraddress];
filters->bytes_ready = lastfilterlength;
return true;
}
void rar_clear_filters(struct ar_archive_rar_filters *filters)
{
rar_delete_filter(filters->stack);
rar_delete_program(filters->progs);
free(filters->vm);
}

149
external/unarr/rar/huffman-rar.c vendored Normal file
View File

@@ -0,0 +1,149 @@
/* Copyright 2018 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADPrefixCode.m */
#include "rar.h"
bool rar_new_node(struct huffman_code *code)
{
if (!code->tree) {
code->minlength = INT_MAX;
code->maxlength = INT_MIN;
}
if (code->numentries + 1 >= code->capacity) {
/* in my small file sample, 1024 is the value needed most often */
int new_capacity = code->capacity ? code->capacity * 2 : 1024;
void *new_tree = calloc(new_capacity, sizeof(*code->tree));
if (!new_tree) {
warn("OOM during decompression");
return false;
}
if (code->tree) {
memcpy(new_tree, code->tree, code->capacity * sizeof(*code->tree));
free(code->tree);
}
code->tree = new_tree;
code->capacity = new_capacity;
}
/* if we have no code->tree at this point something went wrong */
if (!code->tree) {
warn("Invalid huffman code tree, aborting");
return false;
}
code->tree[code->numentries].branches[0] = -1;
code->tree[code->numentries].branches[1] = -2;
code->numentries++;
return true;
}
bool rar_add_value(struct huffman_code *code, int value, int codebits, int length)
{
int lastnode, bitpos, bit;
free(code->table);
code->table = NULL;
if (length > code->maxlength)
code->maxlength = length;
if (length < code->minlength)
code->minlength = length;
lastnode = 0;
for (bitpos = length - 1; bitpos >= 0; bitpos--) {
bit = (codebits >> bitpos) & 1;
if (rar_is_leaf_node(code, lastnode)) {
warn("Invalid data in bitstream"); /* prefix found */
return false;
}
if (code->tree[lastnode].branches[bit] < 0) {
if (!rar_new_node(code))
return false;
code->tree[lastnode].branches[bit] = code->numentries - 1;
}
lastnode = code->tree[lastnode].branches[bit];
}
if (code->tree[lastnode].branches[0] != -1 || code->tree[lastnode].branches[1] != -2) {
warn("Invalid data in bitstream"); /* prefix found */
return false;
}
code->tree[lastnode].branches[0] = code->tree[lastnode].branches[1] = value;
return true;
}
bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols)
{
int symbolsleft = numsymbols;
int codebits = 0;
int i, j;
if (!rar_new_node(code))
return false;
for (i = 1; i <= 0x0F; i++) {
for (j = 0; j < numsymbols; j++) {
if (lengths[j] != i)
continue;
if (!rar_add_value(code, j, codebits, i))
return false;
if (--symbolsleft <= 0)
return true;
codebits++;
}
codebits <<= 1;
}
return true;
}
static bool rar_make_table_rec(struct huffman_code *code, int node, int offset, int depth, int maxdepth)
{
int currtablesize = 1 << (maxdepth - depth);
if (node < 0 || code->numentries <= node) {
warn("Invalid data in bitstream"); /* invalid location to Huffman tree specified */
return false;
}
if (rar_is_leaf_node(code, node)) {
int i;
for (i = 0; i < currtablesize; i++) {
code->table[offset + i].length = depth;
code->table[offset + i].value = code->tree[node].branches[0];
}
}
else if (depth == maxdepth) {
code->table[offset].length = maxdepth + 1;
code->table[offset].value = node;
}
else {
if (!rar_make_table_rec(code, code->tree[node].branches[0], offset, depth + 1, maxdepth))
return false;
if (!rar_make_table_rec(code, code->tree[node].branches[1], offset + currtablesize / 2, depth + 1, maxdepth))
return false;
}
return true;
}
bool rar_make_table(struct huffman_code *code)
{
if (code->minlength <= code->maxlength && code->maxlength <= 10)
code->tablesize = code->maxlength;
else
code->tablesize = 10;
code->table = calloc(1ULL << code->tablesize, sizeof(*code->table));
if (!code->table) {
warn("OOM during decompression");
return false;
}
return rar_make_table_rec(code, 0, 0, 0, code->tablesize);
}
void rar_free_code(struct huffman_code *code)
{
free(code->tree);
free(code->table);
memset(code, 0, sizeof(*code));
}

88
external/unarr/rar/lzss.h vendored Normal file
View File

@@ -0,0 +1,88 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/LZSS.h */
#ifndef rar_lzss_h
#define rar_lzss_h
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#if defined(_MSC_VER) && !defined(inline)
#define inline __inline
#endif
typedef struct {
uint8_t *window;
int mask;
int64_t position;
} LZSS;
static inline int64_t lzss_position(LZSS *self) { return self->position; }
static inline int lzss_mask(LZSS *self) { return self->mask; }
static inline int lzss_size(LZSS *self) { return self->mask + 1; }
static inline uint8_t *lzss_window_pointer(LZSS *self) { return self->window; }
static inline int lzss_offset_for_position(LZSS *self, int64_t pos) { return (int)(pos & self->mask); }
static inline uint8_t *lzss_window_pointer_for_position(LZSS *self, int64_t pos) { return &self->window[lzss_offset_for_position(self, pos)]; }
static inline int lzss_current_window_offset(LZSS *self) { return lzss_offset_for_position(self, self->position); }
static inline uint8_t *lzss_current_window_pointer(LZSS *self) { return lzss_window_pointer_for_position(self, self->position); }
static inline int64_t lzss_next_window_edge_after_position(LZSS *self, int64_t pos) { return (pos + lzss_size(self)) & ~(int64_t)lzss_mask(self); }
static inline int64_t lzss_next_window_edge(LZSS *self) { return lzss_next_window_edge_after_position(self, self->position); }
static inline uint8_t lzss_get_byte_from_window(LZSS *self, int64_t pos) { return *lzss_window_pointer_for_position(self, pos); }
static inline void lzss_emit_literal(LZSS *self, uint8_t literal) {
/* self->window[(self->position & self->mask)] = literal; */
*lzss_current_window_pointer(self) = literal;
self->position++;
}
static inline void lzss_emit_match(LZSS *self, int offset, int length) {
int windowoffs = lzss_current_window_offset(self);
int i;
for (i = 0; i < length; i++) {
self->window[(windowoffs + i) & lzss_mask(self)] = self->window[(windowoffs + i - offset) & lzss_mask(self)];
}
self->position += length;
}
static inline void lzss_copy_bytes_from_window(LZSS *self, uint8_t *buffer, int64_t startpos, int length) {
int windowoffs = lzss_offset_for_position(self, startpos);
int firstpart = lzss_size(self) - windowoffs;
if (length <= firstpart) {
/* Request fits inside window */
memcpy(buffer, &self->window[windowoffs], length);
}
else {
/* Request wraps around window */
memcpy(buffer, &self->window[windowoffs], firstpart);
memcpy(buffer + firstpart, &self->window[0], length - firstpart);
}
}
static inline bool lzss_initialize(LZSS *self, int windowsize) {
self->window = malloc(windowsize);
if (!self->window)
return false;
self->mask = windowsize - 1; /* Assume windows are power-of-two sized! */
memset(self->window, 0, lzss_size(self));
self->position = 0;
return true;
}
static inline void lzss_cleanup(LZSS *self) { free(self->window); }
#endif

239
external/unarr/rar/parse-rar.c vendored Normal file
View File

@@ -0,0 +1,239 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRARParser.m */
#include "rar.h"
static inline uint8_t uint8le(unsigned char *data) { return data[0]; }
static inline uint16_t uint16le(unsigned char *data) { return data[0] | data[1] << 8; }
static inline uint32_t uint32le(unsigned char *data) { return data[0] | data[1] << 8 | data[2] << 16 | (uint32_t)data[3] << 24; }
bool rar_parse_header(ar_archive *ar, struct rar_header *header)
{
unsigned char header_data[7];
size_t read = ar_read(ar->stream, header_data, sizeof(header_data));
if (read == 0) {
ar->at_eof = true;
return false;
}
if (read < sizeof(header_data))
return false;
header->crc = uint16le(header_data + 0);
header->type = uint8le(header_data + 2);
header->flags = uint16le(header_data + 3);
header->size = uint16le(header_data + 5);
header->datasize = 0;
if ((header->flags & LHD_LONG_BLOCK) || header->type == 0x74) {
unsigned char size_data[4];
if (!(header->flags & LHD_LONG_BLOCK))
log("File header without LHD_LONG_BLOCK set");
read += ar_read(ar->stream, size_data, sizeof(size_data));
if (read < sizeof(header_data) + sizeof(size_data))
return false;
header->datasize = uint32le(size_data);
}
if (header->size < read) {
warn("Invalid header size %d", header->size);
return false;
}
return true;
}
bool rar_check_header_crc(ar_archive *ar)
{
unsigned char buffer[256];
uint16_t crc16, size;
uint32_t crc32;
if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET))
return false;
if (ar_read(ar->stream, buffer, 7) != 7)
return false;
crc16 = uint16le(buffer + 0);
size = uint16le(buffer + 5);
if (size < 7)
return false;
size -= 7;
crc32 = ar_crc32(0, buffer + 2, 5);
while (size > 0) {
if (ar_read(ar->stream, buffer, smin(size, sizeof(buffer))) != smin(size, sizeof(buffer)))
return false;
crc32 = ar_crc32(crc32, buffer, smin(size, sizeof(buffer)));
size -= (uint16_t)smin(size, sizeof(buffer));
}
return (crc32 & 0xFFFF) == crc16;
}
bool rar_parse_header_entry(ar_archive_rar *rar, struct rar_header *header, struct rar_entry *entry)
{
unsigned char data[21];
if (ar_read(rar->super.stream, data, sizeof(data)) != sizeof(data))
return false;
entry->size = uint32le(data + 0);
entry->os = uint8le(data + 4);
entry->crc = uint32le(data + 5);
entry->dosdate = uint32le(data + 9);
entry->version = uint8le(data + 13);
entry->method = uint8le(data + 14);
entry->namelen = uint16le(data + 15);
entry->attrs = uint32le(data + 17);
if ((header->flags & LHD_LARGE)) {
unsigned char more_data[8];
if (ar_read(rar->super.stream, more_data, sizeof(more_data)) != sizeof(more_data))
return false;
header->datasize += (uint64_t)uint32le(more_data + 0);
entry->size += (uint64_t)uint32le(more_data + 4);
}
if (!ar_skip(rar->super.stream, entry->namelen))
return false;
if ((header->flags & LHD_SALT)) {
log("Skipping LHD_SALT");
ar_skip(rar->super.stream, 8);
}
rar->entry.version = entry->version;
rar->entry.method = entry->method;
rar->entry.crc = entry->crc;
rar->entry.header_size = header->size;
rar->entry.solid = entry->version < 20 ? (rar->archive_flags & MHD_SOLID) : (header->flags & LHD_SOLID);
free(rar->entry.name);
rar->entry.name = NULL;
return true;
}
/* this seems to be what RAR considers "Unicode" */
static char *rar_conv_unicode_to_utf8(const char *data, uint16_t len)
{
#define Check(cond) if (!(cond)) { free(str); return NULL; } else ((void)0)
uint8_t highbyte, flagbyte, flagbits, size, length, i;
const uint8_t *in = (const uint8_t *)data + strlen(data) + 1;
const uint8_t *end_in = (const uint8_t *)data + len;
char *str = calloc(len + 1, 3);
char *out = str;
char *end_out = str + len * 3;
if (!str)
return NULL;
if (end_in - in <= 1) {
memcpy(str, data, len);
return str;
}
highbyte = *in++;
flagbyte = 0;
flagbits = 0;
size = 0;
while (in < end_in && out < end_out) {
if (flagbits == 0) {
flagbyte = *in++;
flagbits = 8;
}
flagbits -= 2;
switch ((flagbyte >> flagbits) & 3) {
case 0:
Check(in + 1 <= end_in);
out += ar_conv_rune_to_utf8(*in++, out, end_out - out);
size++;
break;
case 1:
Check(in + 1 <= end_in);
out += ar_conv_rune_to_utf8(((uint16_t)highbyte << 8) | *in++, out, end_out - out);
size++;
break;
case 2:
Check(in + 2 <= end_in);
out += ar_conv_rune_to_utf8(((uint16_t)*(in + 1) << 8) | *in, out, end_out - out);
in += 2;
size++;
break;
case 3:
Check(in + 1 <= end_in);
length = *in++;
if ((length & 0x80)) {
uint8_t correction = *in++;
for (i = 0; i < (length & 0x7F) + 2; i++) {
Check(size < len);
out += ar_conv_rune_to_utf8(((uint16_t)highbyte << 8) | (data[size] + (correction & 0xFF)), out, end_out - out);
size++;
}
}
else {
for (i = 0; i < (length & 0x7F) + 2; i++) {
Check(size < len);
out += ar_conv_rune_to_utf8(data[size], out, end_out - out);
size++;
}
}
break;
}
}
return str;
#undef Check
}
const char *rar_get_name(ar_archive *ar, bool raw)
{
if (raw)
return NULL;
ar_archive_rar *rar = (ar_archive_rar *)ar;
if (!rar->entry.name) {
unsigned char data[21];
uint16_t namelen;
char *name;
struct rar_header header;
if (!ar_seek(ar->stream, ar->entry_offset, SEEK_SET))
return NULL;
if (!rar_parse_header(ar, &header))
return NULL;
if (ar_read(ar->stream, data, sizeof(data)) != sizeof(data))
return NULL;
if ((header.flags & LHD_LARGE) && !ar_skip(ar->stream, 8))
return NULL;
namelen = uint16le(data + 15);
name = malloc(namelen + 1);
if (!name || ar_read(ar->stream, name, namelen) != namelen) {
free(name);
return NULL;
}
name[namelen] = '\0';
if (!(header.flags & LHD_UNICODE)) {
rar->entry.name = ar_conv_dos_to_utf8(name);
free(name);
}
else if (namelen == strlen(name)) {
rar->entry.name = name;
}
else {
rar->entry.name = rar_conv_unicode_to_utf8(name, namelen);
free(name);
}
/* normalize path separators */
if (rar->entry.name) {
char *p = rar->entry.name;
while ((p = strchr(p, '\\')) != NULL) {
*p = '/';
}
}
if (!ar_seek(ar->stream, ar->entry_offset + rar->entry.header_size, SEEK_SET))
warn("Couldn't seek back to the end of the entry header");
}
return rar->entry.name;
}

223
external/unarr/rar/rar.c vendored Normal file
View File

@@ -0,0 +1,223 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "rar.h"
static void rar_close(ar_archive *ar)
{
ar_archive_rar *rar = (ar_archive_rar *)ar;
free(rar->entry.name);
rar_clear_uncompress(&rar->uncomp);
}
static bool rar_parse_entry(ar_archive *ar, off64_t offset)
{
ar_archive_rar *rar = (ar_archive_rar *)ar;
struct rar_header header;
struct rar_entry entry;
bool out_of_order = offset != ar->entry_offset_next;
if (!ar_seek(ar->stream, offset, SEEK_SET)) {
warn("Couldn't seek to offset %" PRIi64, offset);
return false;
}
for (;;) {
ar->entry_offset = ar_tell(ar->stream);
ar->entry_size_uncompressed = 0;
if (!rar_parse_header(ar, &header))
return false;
ar->entry_offset_next = ar->entry_offset + header.size + header.datasize;
if (ar->entry_offset_next < ar->entry_offset + header.size) {
warn("Integer overflow due to overly large data size");
return false;
}
switch (header.type) {
case TYPE_MAIN_HEADER:
if ((header.flags & MHD_PASSWORD)) {
warn("Encrypted archives aren't supported");
return false;
}
ar_skip(ar->stream, 6 /* reserved data */);
if ((header.flags & MHD_ENCRYPTVER)) {
log("MHD_ENCRYPTVER is set");
ar_skip(ar->stream, 1);
}
if ((header.flags & MHD_COMMENT))
log("MHD_COMMENT is set");
if (ar_tell(ar->stream) - ar->entry_offset > header.size) {
warn("Invalid RAR header size: %d", header.size);
return false;
}
rar->archive_flags = header.flags;
break;
case TYPE_FILE_ENTRY:
if (!rar_parse_header_entry(rar, &header, &entry))
return false;
if ((header.flags & LHD_PASSWORD))
warn("Encrypted entries will fail to uncompress");
if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) {
if (header.datasize == 0) {
log("Skipping directory entry \"%s\"", rar_get_name(ar, false));
break;
}
warn("Can't skip directory entries containing data");
}
if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER)))
warn("Splitting files isn't really supported");
ar->entry_size_uncompressed = (size_t)entry.size;
ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
if (!rar->entry.solid || rar->entry.method == METHOD_STORE || out_of_order) {
rar_clear_uncompress(&rar->uncomp);
memset(&rar->solid, 0, sizeof(rar->solid));
}
else {
br_clear_leftover_bits(&rar->uncomp);
}
rar->solid.restart = rar->entry.solid && (out_of_order || !rar->solid.part_done);
rar->solid.part_done = !ar->entry_size_uncompressed;
rar->progress.data_left = (size_t)header.datasize;
rar->progress.bytes_done = 0;
rar->progress.crc = 0;
/* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
if (!rar_check_header_crc(ar))
warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
if (ar_tell(ar->stream) != ar->entry_offset + rar->entry.header_size) {
warn("Couldn't seek to offset %" PRIi64, ar->entry_offset + rar->entry.header_size);
return false;
}
return true;
case TYPE_NEWSUB:
log("Skipping newsub header @%" PRIi64, ar->entry_offset);
break;
case TYPE_END_OF_ARCHIVE:
ar->at_eof = true;
return false;
default:
log("Unknown RAR header type %02x", header.type);
break;
}
/* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
if (!rar_check_header_crc(ar))
warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
if (!ar_seek(ar->stream, ar->entry_offset_next, SEEK_SET)) {
warn("Couldn't seek to offset %" PRIi64, ar->entry_offset_next);
return false;
}
}
}
static bool rar_copy_stored(ar_archive_rar *rar, void *buffer, size_t count)
{
if (count > rar->progress.data_left) {
warn("Unexpected EOS in stored data");
return false;
}
if (ar_read(rar->super.stream, buffer, count) != count) {
warn("Unexpected EOF in stored data");
return false;
}
rar->progress.data_left -= count;
rar->progress.bytes_done += count;
return true;
}
static bool rar_restart_solid(ar_archive *ar)
{
ar_archive_rar *rar = (ar_archive_rar *)ar;
off64_t current_offset = ar->entry_offset;
log("Restarting decompression for solid entry");
if (!ar_parse_entry_at(ar, ar->entry_offset_first)) {
ar_parse_entry_at(ar, current_offset);
return false;
}
while (ar->entry_offset < current_offset) {
size_t size = ar->entry_size_uncompressed;
rar->solid.restart = false;
while (size > 0) {
unsigned char buffer[1024];
size_t count = smin(size, sizeof(buffer));
if (!ar_entry_uncompress(ar, buffer, count)) {
ar_parse_entry_at(ar, current_offset);
return false;
}
size -= count;
}
if (!ar_parse_entry(ar)) {
ar_parse_entry_at(ar, current_offset);
return false;
}
}
rar->solid.restart = false;
return true;
}
static bool rar_uncompress(ar_archive *ar, void *buffer, size_t count)
{
ar_archive_rar *rar = (ar_archive_rar *)ar;
if (count > ar->entry_size_uncompressed - rar->progress.bytes_done) {
warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - rar->progress.bytes_done, count);
return false;
}
if (rar->entry.method == METHOD_STORE) {
if (!rar_copy_stored(rar, buffer, count))
return false;
}
else if (rar->entry.method == METHOD_FASTEST || rar->entry.method == METHOD_FAST ||
rar->entry.method == METHOD_NORMAL || rar->entry.method == METHOD_GOOD ||
rar->entry.method == METHOD_BEST) {
if (rar->solid.restart && !rar_restart_solid(ar)) {
warn("Failed to produce the required solid decompression state");
return false;
}
if (!rar_uncompress_part(rar, buffer, count))
return false;
}
else {
warn("Unknown compression method %#02x", rar->entry.method);
return false;
}
rar->progress.crc = ar_crc32(rar->progress.crc, buffer, count);
if (rar->progress.bytes_done < ar->entry_size_uncompressed)
return true;
if (rar->progress.data_left)
log("Compressed block has more data than required");
rar->solid.part_done = true;
rar->solid.size_total += rar->progress.bytes_done;
if (rar->progress.crc != rar->entry.crc) {
warn("Checksum of extracted data doesn't match");
return false;
}
return true;
}
ar_archive *ar_open_rar_archive(ar_stream *stream)
{
char signature[FILE_SIGNATURE_SIZE];
if (!ar_seek(stream, 0, SEEK_SET))
return NULL;
if (ar_read(stream, signature, sizeof(signature)) != sizeof(signature))
return NULL;
if (memcmp(signature, "Rar!\x1A\x07\x00", sizeof(signature)) != 0) {
if (memcmp(signature, "Rar!\x1A\x07\x01", sizeof(signature)) == 0)
warn("RAR 5 format isn't supported");
else if (memcmp(signature, "RE~^", 4) == 0)
warn("Ancient RAR format isn't supported");
else if (memcmp(signature, "MZ", 2) == 0 || memcmp(signature, "\x7F\x45LF", 4) == 0)
warn("SFX archives aren't supported");
return NULL;
}
return ar_open_archive(stream, sizeof(ar_archive_rar), rar_close, rar_parse_entry, rar_get_name, rar_uncompress, NULL, FILE_SIGNATURE_SIZE);
}

243
external/unarr/rar/rar.h vendored Normal file
View File

@@ -0,0 +1,243 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#ifndef rar_rar_h
#define rar_rar_h
#include "../common/unarr-imp.h"
#include "lzss.h"
#include "../lzmasdk/Ppmd7.h"
#include <limits.h>
static inline size_t smin(size_t a, size_t b) { return a < b ? a : b; }
typedef struct ar_archive_rar_s ar_archive_rar;
/***** parse-rar *****/
#define FILE_SIGNATURE_SIZE 7
enum block_types {
TYPE_FILE_SIGNATURE = 0x72, TYPE_MAIN_HEADER = 0x73, TYPE_FILE_ENTRY = 0x74,
TYPE_NEWSUB = 0x7A, TYPE_END_OF_ARCHIVE = 0x7B,
};
enum archive_flags {
MHD_VOLUME = 1 << 0, MHD_COMMENT = 1 << 1, MHD_LOCK = 1 << 2,
MHD_SOLID = 1 << 3, MHD_PACK_COMMENT = 1 << 4, MHD_AV = 1 << 5,
MHD_PROTECT = 1 << 6, MHD_PASSWORD = 1 << 7, MHD_FIRSTVOLUME = 1 << 8,
MHD_ENCRYPTVER = 1 << 9,
MHD_LONG_BLOCK = 1 << 15,
};
enum entry_flags {
LHD_SPLIT_BEFORE = 1 << 0, LHD_SPLIT_AFTER = 1 << 1, LHD_PASSWORD = 1 << 2,
LHD_COMMENT = 1 << 3, LHD_SOLID = 1 << 4,
LHD_DIRECTORY = (1 << 5) | (1 << 6) | (1 << 7),
LHD_LARGE = 1 << 8, LHD_UNICODE = 1 << 9, LHD_SALT = 1 << 10,
LHD_VERSION = 1 << 11, LHD_EXTTIME = 1 << 12, LHD_EXTFLAGS = 1 << 13,
LHD_LONG_BLOCK = 1 << 15,
};
enum compression_method {
METHOD_STORE = 0x30,
METHOD_FASTEST = 0x31, METHOD_FAST = 0x32, METHOD_NORMAL = 0x33,
METHOD_GOOD = 0x34, METHOD_BEST = 0x35,
};
struct rar_header {
uint16_t crc;
uint8_t type;
uint16_t flags;
uint16_t size;
uint64_t datasize;
};
struct rar_entry {
uint64_t size;
uint8_t os;
uint32_t crc;
uint32_t dosdate;
uint8_t version;
uint8_t method;
uint16_t namelen;
uint32_t attrs;
};
struct ar_archive_rar_entry {
uint8_t version;
uint8_t method;
uint32_t crc;
uint16_t header_size;
bool solid;
char *name;
};
bool rar_parse_header(ar_archive *ar, struct rar_header *header);
bool rar_check_header_crc(ar_archive *ar);
bool rar_parse_header_entry(ar_archive_rar *rar, struct rar_header *header, struct rar_entry *entry);
const char *rar_get_name(ar_archive *ar, bool raw);
/***** filter-rar *****/
struct RARVirtualMachine;
struct RARProgramCode;
struct RARFilter;
struct ar_archive_rar_filters {
struct RARVirtualMachine *vm;
struct RARProgramCode *progs;
struct RARFilter *stack;
size_t filterstart;
uint32_t lastfilternum;
size_t lastend;
uint8_t *bytes;
size_t bytes_ready;
};
bool rar_parse_filter(ar_archive_rar *rar, const uint8_t *bytes, uint16_t length, uint8_t flags);
bool rar_run_filters(ar_archive_rar *rar);
void rar_clear_filters(struct ar_archive_rar_filters *filters);
/***** huffman-rar *****/
struct huffman_code {
struct {
int branches[2];
} *tree;
int numentries;
int capacity;
int minlength;
int maxlength;
struct {
int length;
int value;
} *table;
int tablesize;
};
bool rar_new_node(struct huffman_code *code);
bool rar_add_value(struct huffman_code *code, int value, int codebits, int length);
bool rar_create_code(struct huffman_code *code, uint8_t *lengths, int numsymbols);
bool rar_make_table(struct huffman_code *code);
void rar_free_code(struct huffman_code *code);
static inline bool rar_is_leaf_node(struct huffman_code *code, int node) { return code->tree[node].branches[0] == code->tree[node].branches[1]; }
/***** uncompress-rar *****/
#define LZSS_WINDOW_SIZE 0x400000
#define LZSS_OVERFLOW_SIZE 288
#define MAINCODE_SIZE 299
#define OFFSETCODE_SIZE 60
#define LOWOFFSETCODE_SIZE 17
#define LENGTHCODE_SIZE 28
#define HUFFMAN_TABLE_SIZE MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE
struct ByteReader {
IByteIn super;
ar_archive_rar *rar;
};
struct ar_archive_rar_uncomp_v3 {
struct huffman_code maincode;
struct huffman_code offsetcode;
struct huffman_code lowoffsetcode;
struct huffman_code lengthcode;
uint8_t lengthtable[HUFFMAN_TABLE_SIZE];
uint32_t lastlength;
uint32_t lastoffset;
uint32_t oldoffset[4];
uint32_t lastlowoffset;
uint32_t numlowoffsetrepeats;
bool is_ppmd_block;
int ppmd_escape;
CPpmd7 ppmd7_context;
struct ByteReader bytein;
struct ar_archive_rar_filters filters;
};
#define MAINCODE_SIZE_20 298
#define OFFSETCODE_SIZE_20 48
#define LENGTHCODE_SIZE_20 28
#define HUFFMAN_TABLE_SIZE_20 4 * 257
struct AudioState {
int8_t weight[5];
int16_t delta[4];
int8_t lastdelta;
int error[11];
int count;
uint8_t lastbyte;
};
struct ar_archive_rar_uncomp_v2 {
struct huffman_code maincode;
struct huffman_code offsetcode;
struct huffman_code lengthcode;
struct huffman_code audiocode[4];
uint8_t lengthtable[HUFFMAN_TABLE_SIZE_20];
uint32_t lastoffset;
uint32_t lastlength;
uint32_t oldoffset[4];
uint32_t oldoffsetindex;
bool audioblock;
uint8_t channel;
uint8_t numchannels;
struct AudioState audiostate[4];
int8_t channeldelta;
};
struct ar_archive_rar_uncomp {
uint8_t version;
LZSS lzss;
size_t bytes_ready;
bool start_new_table;
union {
struct ar_archive_rar_uncomp_v3 v3;
struct ar_archive_rar_uncomp_v2 v2;
} state;
struct StreamBitReader {
uint64_t bits;
int available;
bool at_eof;
} br;
};
bool rar_uncompress_part(ar_archive_rar *rar, void *buffer, size_t buffer_size);
int64_t rar_expand(ar_archive_rar *rar, int64_t end);
void rar_clear_uncompress(struct ar_archive_rar_uncomp *uncomp);
static inline void br_clear_leftover_bits(struct ar_archive_rar_uncomp *uncomp) { uncomp->br.available &= ~0x07; }
/***** rar *****/
struct ar_archive_rar_progress {
size_t data_left;
size_t bytes_done;
uint32_t crc;
};
struct ar_archive_rar_solid {
size_t size_total;
bool part_done;
bool restart;
};
struct ar_archive_rar_s {
ar_archive super;
uint16_t archive_flags;
struct ar_archive_rar_entry entry;
struct ar_archive_rar_uncomp uncomp;
struct ar_archive_rar_progress progress;
struct ar_archive_rar_solid solid;
};
#endif

619
external/unarr/rar/rarvm.c vendored Normal file
View File

@@ -0,0 +1,619 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.c */
#include "rarvm.h"
#include "../common/allocator.h"
#include <stdlib.h>
#include <string.h>
typedef struct RAROpcode_s RAROpcode;
struct RAROpcode_s {
uint8_t instruction;
uint8_t bytemode;
uint8_t addressingmode1;
uint8_t addressingmode2;
uint32_t value1;
uint32_t value2;
};
struct RARProgram_s {
RAROpcode *opcodes;
uint32_t length;
uint32_t capacity;
};
/* Program building */
RARProgram *RARCreateProgram(void)
{
return calloc(1, sizeof(RARProgram));
}
void RARDeleteProgram(RARProgram *prog)
{
if (prog)
free(prog->opcodes);
free(prog);
}
bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode)
{
if (instruction >= RARNumberOfInstructions)
return false;
if (bytemode && !RARInstructionHasByteMode(instruction))
return false;
if (prog->length + 1 >= prog->capacity) {
/* in my small file sample, 16 is the value needed most often */
uint32_t newCapacity = prog->capacity ? prog->capacity * 4 : 32;
RAROpcode *newCodes = calloc(newCapacity, sizeof(*prog->opcodes));
if (!newCodes) {
return false;
}
if (prog->opcodes) {
memcpy(newCodes, prog->opcodes, prog->capacity * sizeof(*prog->opcodes));
free(prog->opcodes);
}
prog->opcodes = newCodes;
prog->capacity = newCapacity;
}
memset(&prog->opcodes[prog->length], 0, sizeof(prog->opcodes[prog->length]));
prog->opcodes[prog->length].instruction = instruction;
if (instruction == RARMovzxInstruction || instruction == RARMovsxInstruction)
prog->opcodes[prog->length].bytemode = 2; /* second argument only */
else if (bytemode)
prog->opcodes[prog->length].bytemode = (1 | 2);
else
prog->opcodes[prog->length].bytemode = 0;
prog->length++;
return true;
}
bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2)
{
RAROpcode *opcode = &prog->opcodes[prog->length - 1];
int numoperands;
if (addressingmode1 >= RARNumberOfAddressingModes || addressingmode2 >= RARNumberOfAddressingModes)
return false;
if (!prog->length || opcode->addressingmode1 || opcode->value1 || opcode->addressingmode2 || opcode->value2)
return false;
numoperands = NumberOfRARInstructionOperands(opcode->instruction);
if (numoperands == 0)
return true;
if (addressingmode1 == RARImmediateAddressingMode && RARInstructionWritesFirstOperand(opcode->instruction))
return false;
opcode->addressingmode1 = addressingmode1;
opcode->value1 = value1;
if (numoperands == 2) {
if (addressingmode2 == RARImmediateAddressingMode && RARInstructionWritesSecondOperand(opcode->instruction))
return false;
opcode->addressingmode2 = addressingmode2;
opcode->value2 = value2;
}
return true;
}
bool RARIsProgramTerminated(RARProgram *prog)
{
return prog->length > 0 && RARInstructionIsUnconditionalJump(prog->opcodes[prog->length - 1].instruction);
}
/* Execution */
#define EXTMACRO_BEGIN do {
#ifdef _MSC_VER
#define EXTMACRO_END } __pragma(warning(push)) __pragma(warning(disable:4127)) while (0) __pragma(warning(pop))
#else
#define EXTMACRO_END } while (0)
#endif
#define CarryFlag 1
#define ZeroFlag 2
#define SignFlag 0x80000000
#define SignExtend(a) ((uint32_t)((int8_t)(a)))
static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode);
static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data);
#define GetOperand1() _RARGetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1)
#define GetOperand2() _RARGetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2)
#define SetOperand1(data) _RARSetOperand(vm, opcode->addressingmode1, opcode->value1, opcode->bytemode & 1, data)
#define SetOperand2(data) _RARSetOperand(vm, opcode->addressingmode2, opcode->value2, opcode->bytemode & 2, data)
#define SetFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t result = (res); flags = (result == 0 ? ZeroFlag : (result & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END
#define SetByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t result = (res); flags = (result == 0 ? ZeroFlag : (SignExtend(result) & SignFlag)) | ((carry) ? CarryFlag : 0); EXTMACRO_END
#define SetFlags(res) SetFlagsWithCarry(res, 0)
#define SetOperand1AndFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint32_t r = (res); SetFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END
#define SetOperand1AndByteFlagsWithCarry(res, carry) EXTMACRO_BEGIN uint8_t r = (res); SetByteFlagsWithCarry(r, carry); SetOperand1(r); EXTMACRO_END
#define SetOperand1AndFlags(res) EXTMACRO_BEGIN uint32_t r = (res); SetFlags(r); SetOperand1(r); EXTMACRO_END
#define NextInstruction() { opcode++; continue; }
#define Jump(offs) { uint32_t o = (offs); if (o >= prog->length) return false; opcode = &prog->opcodes[o]; continue; }
bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog)
{
RAROpcode *opcode = prog->opcodes;
uint32_t flags = 0;
uint32_t op1, op2, carry, i;
uint32_t counter = 0;
if (!RARIsProgramTerminated(prog))
return false;
while ((uint32_t)(opcode - prog->opcodes) < prog->length && counter++ < RARRuntimeMaxInstructions) {
switch (opcode->instruction) {
case RARMovInstruction:
SetOperand1(GetOperand2());
NextInstruction();
case RARCmpInstruction:
op1 = GetOperand1();
SetFlagsWithCarry(op1 - GetOperand2(), result > op1);
NextInstruction();
case RARAddInstruction:
op1 = GetOperand1();
if (opcode->bytemode)
SetOperand1AndByteFlagsWithCarry((op1 + GetOperand2()) & 0xFF, result < op1);
else
SetOperand1AndFlagsWithCarry(op1 + GetOperand2(), result < op1);
NextInstruction();
case RARSubInstruction:
op1 = GetOperand1();
#if 0 /* apparently not correctly implemented in the RAR VM */
if (opcode->bytemode)
SetOperand1AndByteFlagsWithCarry((op1 - GetOperand2()) & 0xFF, result > op1);
else
#endif
SetOperand1AndFlagsWithCarry(op1 - GetOperand2(), result > op1);
NextInstruction();
case RARJzInstruction:
if ((flags & ZeroFlag))
Jump(GetOperand1());
NextInstruction();
case RARJnzInstruction:
if (!(flags & ZeroFlag))
Jump(GetOperand1());
NextInstruction();
case RARIncInstruction:
if (opcode->bytemode)
SetOperand1AndFlags((GetOperand1() + 1) & 0xFF);
else
SetOperand1AndFlags(GetOperand1() + 1);
NextInstruction();
case RARDecInstruction:
if (opcode->bytemode)
SetOperand1AndFlags((GetOperand1() - 1) & 0xFF);
else
SetOperand1AndFlags(GetOperand1() - 1);
NextInstruction();
case RARJmpInstruction:
Jump(GetOperand1());
case RARXorInstruction:
SetOperand1AndFlags(GetOperand1() ^ GetOperand2());
NextInstruction();
case RARAndInstruction:
SetOperand1AndFlags(GetOperand1() & GetOperand2());
NextInstruction();
case RAROrInstruction:
SetOperand1AndFlags(GetOperand1() | GetOperand2());
NextInstruction();
case RARTestInstruction:
SetFlags(GetOperand1() & GetOperand2());
NextInstruction();
case RARJsInstruction:
if ((flags & SignFlag))
Jump(GetOperand1());
NextInstruction();
case RARJnsInstruction:
if (!(flags & SignFlag))
Jump(GetOperand1());
NextInstruction();
case RARJbInstruction:
if ((flags & CarryFlag))
Jump(GetOperand1());
NextInstruction();
case RARJbeInstruction:
if ((flags & (CarryFlag | ZeroFlag)))
Jump(GetOperand1());
NextInstruction();
case RARJaInstruction:
if (!(flags & (CarryFlag | ZeroFlag)))
Jump(GetOperand1());
NextInstruction();
case RARJaeInstruction:
if (!(flags & CarryFlag))
Jump(GetOperand1());
NextInstruction();
case RARPushInstruction:
vm->registers[7] -= 4;
RARVirtualMachineWrite32(vm, vm->registers[7], GetOperand1());
NextInstruction();
case RARPopInstruction:
SetOperand1(RARVirtualMachineRead32(vm, vm->registers[7]));
vm->registers[7] += 4;
NextInstruction();
case RARCallInstruction:
vm->registers[7] -= 4;
RARVirtualMachineWrite32(vm, vm->registers[7], (uint32_t)(opcode - prog->opcodes + 1));
Jump(GetOperand1());
case RARRetInstruction:
if (vm->registers[7] >= RARProgramMemorySize)
return true;
i = RARVirtualMachineRead32(vm, vm->registers[7]);
vm->registers[7] += 4;
Jump(i);
case RARNotInstruction:
SetOperand1(~GetOperand1());
NextInstruction();
case RARShlInstruction:
op1 = GetOperand1();
op2 = GetOperand2();
SetOperand1AndFlagsWithCarry(op1 << op2, ((op1 << (op2 - 1)) & 0x80000000) != 0);
NextInstruction();
case RARShrInstruction:
op1 = GetOperand1();
op2 = GetOperand2();
SetOperand1AndFlagsWithCarry(op1 >> op2, ((op1 >> (op2 - 1)) & 1) != 0);
NextInstruction();
case RARSarInstruction:
op1 = GetOperand1();
op2 = GetOperand2();
SetOperand1AndFlagsWithCarry(((int32_t)op1) >> op2, ((op1 >> (op2 - 1)) & 1) != 0);
NextInstruction();
case RARNegInstruction:
SetOperand1AndFlagsWithCarry(-(int32_t)GetOperand1(), result != 0);
NextInstruction();
case RARPushaInstruction:
vm->registers[7] -= 32;
for (i = 0; i < 8; i++)
RARVirtualMachineWrite32(vm, vm->registers[7] + (7 - i) * 4, vm->registers[i]);
NextInstruction();
case RARPopaInstruction:
for (i = 0; i < 8; i++)
vm->registers[i] = RARVirtualMachineRead32(vm, vm->registers[7] + (7 - i) * 4);
vm->registers[7] += 32;
NextInstruction();
case RARPushfInstruction:
vm->registers[7] -= 4;
RARVirtualMachineWrite32(vm, vm->registers[7], flags);
NextInstruction();
case RARPopfInstruction:
flags = RARVirtualMachineRead32(vm, vm->registers[7]);
vm->registers[7] += 4;
NextInstruction();
case RARMovzxInstruction:
SetOperand1(GetOperand2());
NextInstruction();
case RARMovsxInstruction:
SetOperand1(SignExtend(GetOperand2()));
NextInstruction();
case RARXchgInstruction:
op1 = GetOperand1();
op2 = GetOperand2();
SetOperand1(op2);
SetOperand2(op1);
NextInstruction();
case RARMulInstruction:
SetOperand1(GetOperand1() * GetOperand2());
NextInstruction();
case RARDivInstruction:
op2 = GetOperand2();
if (op2 != 0)
SetOperand1(GetOperand1() / op2);
NextInstruction();
case RARAdcInstruction:
op1 = GetOperand1();
carry = (flags & CarryFlag);
if (opcode->bytemode)
SetOperand1AndFlagsWithCarry((op1 + GetOperand2() + carry) & 0xFF, result < op1 || (result == op1 && carry)); /* does not correctly set sign bit */
else
SetOperand1AndFlagsWithCarry(op1 + GetOperand2() + carry, result < op1 || (result == op1 && carry));
NextInstruction();
case RARSbbInstruction:
op1 = GetOperand1();
carry = (flags & CarryFlag);
if (opcode->bytemode)
SetOperand1AndFlagsWithCarry((op1 - GetOperand2() - carry) & 0xFF, result > op1 || (result == op1 && carry)); /* does not correctly set sign bit */
else
SetOperand1AndFlagsWithCarry(op1 - GetOperand2() - carry, result > op1 || (result == op1 && carry));
NextInstruction();
case RARPrintInstruction:
/* TODO: ??? */
NextInstruction();
}
}
return false;
}
/* Memory and register access */
static uint32_t _RARRead32(const uint8_t *b)
{
return ((uint32_t)b[3] << 24) | ((uint32_t)b[2] << 16) | ((uint32_t)b[1] << 8) | (uint32_t)b[0];
}
static void _RARWrite32(uint8_t *b, uint32_t n)
{
b[3] = (n >> 24) & 0xFF;
b[2] = (n >> 16) & 0xFF;
b[1] = (n >> 8) & 0xFF;
b[0] = n & 0xFF;
}
void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8])
{
if (registers)
memcpy(vm->registers, registers, sizeof(vm->registers));
else
memset(vm->registers, 0, sizeof(vm->registers));
}
uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address)
{
return _RARRead32(&vm->memory[address & RARProgramMemoryMask]);
}
void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val)
{
_RARWrite32(&vm->memory[address & RARProgramMemoryMask], val);
}
uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address)
{
return vm->memory[address & RARProgramMemoryMask];
}
void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val)
{
vm->memory[address & RARProgramMemoryMask] = val;
}
static uint32_t _RARGetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode)
{
if (/*RARRegisterAddressingMode(0) <= addressingmode && */addressingmode <= RARRegisterAddressingMode(7)) {
uint32_t result = vm->registers[addressingmode % 8];
if (bytemode)
result = result & 0xFF;
return result;
}
if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) {
if (bytemode)
return RARVirtualMachineRead8(vm, vm->registers[addressingmode % 8]);
return RARVirtualMachineRead32(vm, vm->registers[addressingmode % 8]);
}
if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) {
if (bytemode)
return RARVirtualMachineRead8(vm, value + vm->registers[addressingmode % 8]);
return RARVirtualMachineRead32(vm, value + vm->registers[addressingmode % 8]);
}
if (addressingmode == RARAbsoluteAddressingMode) {
if (bytemode)
return RARVirtualMachineRead8(vm, value);
return RARVirtualMachineRead32(vm, value);
}
/* if (addressingmode == RARImmediateAddressingMode) */
return value;
}
static void _RARSetOperand(RARVirtualMachine *vm, uint8_t addressingmode, uint32_t value, bool bytemode, uint32_t data)
{
if (/*RARRegisterAddressingMode(0) <= addressingmode &&*/ addressingmode <= RARRegisterAddressingMode(7)) {
if (bytemode)
data = data & 0xFF;
vm->registers[addressingmode % 8] = data;
}
else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7)) {
if (bytemode)
RARVirtualMachineWrite8(vm, vm->registers[addressingmode % 8], (uint8_t)data);
else
RARVirtualMachineWrite32(vm, vm->registers[addressingmode % 8], data);
}
else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7)) {
if (bytemode)
RARVirtualMachineWrite8(vm, value + vm->registers[addressingmode % 8], (uint8_t)data);
else
RARVirtualMachineWrite32(vm, value + vm->registers[addressingmode % 8], data);
}
else if (addressingmode == RARAbsoluteAddressingMode) {
if (bytemode)
RARVirtualMachineWrite8(vm, value, (uint8_t)data);
else
RARVirtualMachineWrite32(vm, value, data);
}
}
/* Instruction properties */
#define RAR0OperandsFlag 0
#define RAR1OperandFlag 1
#define RAR2OperandsFlag 2
#define RAROperandsFlag 3
#define RARHasByteModeFlag 4
#define RARIsUnconditionalJumpFlag 8
#define RARIsRelativeJumpFlag 16
#define RARWritesFirstOperandFlag 32
#define RARWritesSecondOperandFlag 64
#define RARReadsStatusFlag 128
#define RARWritesStatusFlag 256
static const int InstructionFlags[RARNumberOfInstructions] = {
/*RARMovInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag,
/*RARCmpInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag,
/*RARAddInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARSubInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARJzInstruction*/ RAR1OperandFlag | RARIsUnconditionalJumpFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJnzInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARIncInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARDecInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARJmpInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag,
/*RARXorInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARAndInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RAROrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARTestInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesStatusFlag,
/*RARJsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJnsInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJbInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJbeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJaInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARJaeInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag | RARReadsStatusFlag,
/*RARPushInstruction*/ RAR1OperandFlag,
/*RARPopInstruction*/ RAR1OperandFlag,
/*RARCallInstruction*/ RAR1OperandFlag | RARIsRelativeJumpFlag,
/*RARRetInstruction*/ RAR0OperandsFlag | RARIsUnconditionalJumpFlag,
/*RARNotInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag,
/*RARShlInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARShrInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARSarInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARNegInstruction*/ RAR1OperandFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARWritesStatusFlag,
/*RARPushaInstruction*/ RAR0OperandsFlag,
/*RARPopaInstruction*/ RAR0OperandsFlag,
/*RARPushfInstruction*/ RAR0OperandsFlag | RARReadsStatusFlag,
/*RARPopfInstruction*/ RAR0OperandsFlag | RARWritesStatusFlag,
/*RARMovzxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag,
/*RARMovsxInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag,
/*RARXchgInstruction*/ RAR2OperandsFlag | RARWritesFirstOperandFlag | RARWritesSecondOperandFlag | RARHasByteModeFlag,
/*RARMulInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag,
/*RARDivInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag,
/*RARAdcInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag,
/*RARSbbInstruction*/ RAR2OperandsFlag | RARHasByteModeFlag | RARWritesFirstOperandFlag | RARReadsStatusFlag | RARWritesStatusFlag,
/*RARPrintInstruction*/ RAR0OperandsFlag
};
int NumberOfRARInstructionOperands(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return 0;
return InstructionFlags[instruction] & RAROperandsFlag;
}
bool RARInstructionHasByteMode(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return false;
return (InstructionFlags[instruction] & RARHasByteModeFlag)!=0;
}
bool RARInstructionIsUnconditionalJump(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return false;
return (InstructionFlags[instruction] & RARIsUnconditionalJumpFlag) != 0;
}
bool RARInstructionIsRelativeJump(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return false;
return (InstructionFlags[instruction] & RARIsRelativeJumpFlag) != 0;
}
bool RARInstructionWritesFirstOperand(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return false;
return (InstructionFlags[instruction] & RARWritesFirstOperandFlag) != 0;
}
bool RARInstructionWritesSecondOperand(uint8_t instruction)
{
if (instruction >= RARNumberOfInstructions)
return false;
return (InstructionFlags[instruction] & RARWritesSecondOperandFlag) != 0;
}
/* Program debugging */
#ifndef NDEBUG
#include <stdio.h>
static void RARPrintOperand(uint8_t addressingmode, uint32_t value)
{
if (/*RARRegisterAddressingMode(0) <= addressingmode && */addressingmode <= RARRegisterAddressingMode(7))
printf("r%d", addressingmode % 8);
else if (RARRegisterIndirectAddressingMode(0) <= addressingmode && addressingmode <= RARRegisterIndirectAddressingMode(7))
printf("@(r%d)", addressingmode % 8);
else if (RARIndexedAbsoluteAddressingMode(0) <= addressingmode && addressingmode <= RARIndexedAbsoluteAddressingMode(7))
printf("@(r%d+$%02x)", addressingmode % 8, value);
else if (addressingmode == RARAbsoluteAddressingMode)
printf("@($%02x)", value);
else if (addressingmode == RARImmediateAddressingMode)
printf("$%02x", value);
}
void RARPrintProgram(RARProgram *prog)
{
static const char *instructionNames[RARNumberOfInstructions] = {
"Mov", "Cmp", "Add", "Sub", "Jz", "Jnz", "Inc", "Dec", "Jmp", "Xor",
"And", "Or", "Test", "Js", "Jns", "Jb", "Jbe", "Ja", "Jae", "Push",
"Pop", "Call", "Ret", "Not", "Shl", "Shr", "Sar", "Neg", "Pusha", "Popa",
"Pushf", "Popf", "Movzx", "Movsx", "Xchg", "Mul", "Div", "Adc", "Sbb", "Print",
};
uint32_t i;
for (i = 0; i < prog->length; i++) {
RAROpcode *opcode = &prog->opcodes[i];
int numoperands = NumberOfRARInstructionOperands(opcode->instruction);
printf(" %02x: %s", i, instructionNames[opcode->instruction]);
if (opcode->bytemode)
printf("B");
if (numoperands >= 1) {
printf(" ");
RARPrintOperand(opcode->addressingmode1, opcode->value1);
}
if (numoperands == 2) {
printf(", ");
RARPrintOperand(opcode->addressingmode2, opcode->value2);
}
printf("\n");
}
}
#endif

117
external/unarr/rar/rarvm.h vendored Normal file
View File

@@ -0,0 +1,117 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/RARVirtualMachine.h */
#ifndef rar_vm_h
#define rar_vm_h
#include <stdint.h>
#include <stdbool.h>
#define RARProgramMemorySize 0x40000
#define RARProgramMemoryMask (RARProgramMemorySize - 1)
#define RARProgramWorkSize 0x3c000
#define RARProgramGlobalSize 0x2000
#define RARProgramSystemGlobalAddress RARProgramWorkSize
#define RARProgramSystemGlobalSize 64
#define RARProgramUserGlobalAddress (RARProgramSystemGlobalAddress + RARProgramSystemGlobalSize)
#define RARProgramUserGlobalSize (RARProgramGlobalSize - RARProgramSystemGlobalSize)
#define RARRuntimeMaxInstructions 250000000
#define RARRegisterAddressingMode(n) (0 + (n))
#define RARRegisterIndirectAddressingMode(n) (8 + (n))
#define RARIndexedAbsoluteAddressingMode(n) (16 + (n))
#define RARAbsoluteAddressingMode 24
#define RARImmediateAddressingMode 25
#define RARNumberOfAddressingModes 26
typedef struct RARVirtualMachine RARVirtualMachine;
struct RARVirtualMachine {
uint32_t registers[8];
uint8_t memory[RARProgramMemorySize + sizeof(uint32_t) /* overflow sentinel */];
};
typedef struct RARProgram_s RARProgram;
/* Program building */
enum {
RARMovInstruction = 0,
RARCmpInstruction = 1,
RARAddInstruction = 2,
RARSubInstruction = 3,
RARJzInstruction = 4,
RARJnzInstruction = 5,
RARIncInstruction = 6,
RARDecInstruction = 7,
RARJmpInstruction = 8,
RARXorInstruction = 9,
RARAndInstruction = 10,
RAROrInstruction = 11,
RARTestInstruction = 12,
RARJsInstruction = 13,
RARJnsInstruction = 14,
RARJbInstruction = 15,
RARJbeInstruction = 16,
RARJaInstruction = 17,
RARJaeInstruction = 18,
RARPushInstruction = 19,
RARPopInstruction = 20,
RARCallInstruction = 21,
RARRetInstruction = 22,
RARNotInstruction = 23,
RARShlInstruction = 24,
RARShrInstruction = 25,
RARSarInstruction = 26,
RARNegInstruction = 27,
RARPushaInstruction = 28,
RARPopaInstruction = 29,
RARPushfInstruction = 30,
RARPopfInstruction = 31,
RARMovzxInstruction = 32,
RARMovsxInstruction = 33,
RARXchgInstruction = 34,
RARMulInstruction = 35,
RARDivInstruction = 36,
RARAdcInstruction = 37,
RARSbbInstruction = 38,
RARPrintInstruction = 39,
RARNumberOfInstructions = 40,
};
RARProgram *RARCreateProgram(void);
void RARDeleteProgram(RARProgram *prog);
bool RARProgramAddInstr(RARProgram *prog, uint8_t instruction, bool bytemode);
bool RARSetLastInstrOperands(RARProgram *prog, uint8_t addressingmode1, uint32_t value1, uint8_t addressingmode2, uint32_t value2);
bool RARIsProgramTerminated(RARProgram *prog);
/* Execution */
bool RARExecuteProgram(RARVirtualMachine *vm, RARProgram *prog);
/* Memory and register access (convenience) */
void RARSetVirtualMachineRegisters(RARVirtualMachine *vm, uint32_t registers[8]);
uint32_t RARVirtualMachineRead32(RARVirtualMachine *vm, uint32_t address);
void RARVirtualMachineWrite32(RARVirtualMachine *vm, uint32_t address, uint32_t val);
uint8_t RARVirtualMachineRead8(RARVirtualMachine *vm, uint32_t address);
void RARVirtualMachineWrite8(RARVirtualMachine *vm, uint32_t address, uint8_t val);
/* Instruction properties */
int NumberOfRARInstructionOperands(uint8_t instruction);
bool RARInstructionHasByteMode(uint8_t instruction);
bool RARInstructionIsUnconditionalJump(uint8_t instruction);
bool RARInstructionIsRelativeJump(uint8_t instruction);
bool RARInstructionWritesFirstOperand(uint8_t instruction);
bool RARInstructionWritesSecondOperand(uint8_t instruction);
/* Program debugging */
#ifndef NDEBUG
void RARPrintProgram(RARProgram *prog);
#endif
#endif

984
external/unarr/rar/uncompress-rar.c vendored Normal file
View File

@@ -0,0 +1,984 @@
/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR30Handle.m */
/* adapted from https://code.google.com/p/theunarchiver/source/browse/XADMaster/XADRAR20Handle.m */
#include "rar.h"
static void *gSzAlloc_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
static void gSzAlloc_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
static ISzAlloc gSzAlloc = { gSzAlloc_Alloc, gSzAlloc_Free };
static bool br_fill(ar_archive_rar *rar, int bits)
{
uint8_t bytes[8];
int count, i;
/* read as many bits as possible */
count = (64 - rar->uncomp.br.available) / 8;
if (rar->progress.data_left < (size_t)count)
count = (int)rar->progress.data_left;
if (bits > rar->uncomp.br.available + 8 * count || ar_read(rar->super.stream, bytes, count) != (size_t)count) {
if (!rar->uncomp.br.at_eof) {
warn("Unexpected EOF during decompression (truncated file?)");
rar->uncomp.br.at_eof = true;
}
return false;
}
rar->progress.data_left -= count;
for (i = 0; i < count; i++) {
rar->uncomp.br.bits = (rar->uncomp.br.bits << 8) | bytes[i];
}
rar->uncomp.br.available += 8 * count;
return true;
}
static inline bool br_check(ar_archive_rar *rar, int bits)
{
return bits <= rar->uncomp.br.available || br_fill(rar, bits);
}
static inline uint64_t br_bits(ar_archive_rar *rar, int bits)
{
return (rar->uncomp.br.bits >> (rar->uncomp.br.available -= bits)) & (((uint64_t)1 << bits) - 1);
}
static Byte ByteIn_Read(const IByteIn *p)
{
struct ByteReader *self = (struct ByteReader *) p;
return br_check(self->rar, 8) ? (Byte)br_bits(self->rar, 8) : 0xFF;
}
static void ByteIn_CreateVTable(struct ByteReader *br, ar_archive_rar *rar)
{
br->super.Read = ByteIn_Read;
br->rar = rar;
}
static bool rar_init_uncompress(struct ar_archive_rar_uncomp *uncomp, uint8_t version)
{
/* per XADRARParser.m @handleForSolidStreamWithObject these versions are identical */
if (version == 29 || version == 36)
version = 3;
else if (version == 20 || version == 26)
version = 2;
else {
warn("Unsupported compression version: %d", version);
return false;
}
if (uncomp->version) {
if (uncomp->version != version) {
warn("Compression version mismatch: %d != %d", version, uncomp->version);
return false;
}
return true;
}
memset(uncomp, 0, sizeof(*uncomp));
uncomp->start_new_table = true;
if (!lzss_initialize(&uncomp->lzss, LZSS_WINDOW_SIZE)) {
warn("OOM during decompression");
return false;
}
if (version == 3) {
uncomp->state.v3.ppmd_escape = 2;
uncomp->state.v3.filters.filterstart = SIZE_MAX;
}
uncomp->version = version;
return true;
}
static void rar_free_codes(struct ar_archive_rar_uncomp *uncomp);
void rar_clear_uncompress(struct ar_archive_rar_uncomp *uncomp)
{
if (!uncomp->version)
return;
rar_free_codes(uncomp);
lzss_cleanup(&uncomp->lzss);
if (uncomp->version == 3) {
Ppmd7_Free(&uncomp->state.v3.ppmd7_context, &gSzAlloc);
rar_clear_filters(&uncomp->state.v3.filters);
}
uncomp->version = 0;
}
static int rar_read_next_symbol(ar_archive_rar *rar, struct huffman_code *code)
{
int node = 0;
if (!code->table && !rar_make_table(code))
return -1;
/* performance optimization */
if (code->tablesize <= rar->uncomp.br.available) {
uint16_t bits = (uint16_t)br_bits(rar, code->tablesize);
int length = code->table[bits].length;
int value = code->table[bits].value;
if (length < 0) {
warn("Invalid data in bitstream"); /* invalid prefix code in bitstream */
return -1;
}
if (length <= code->tablesize) {
/* Skip only length bits */
rar->uncomp.br.available += code->tablesize - length;
return value;
}
node = value;
}
while (!rar_is_leaf_node(code, node)) {
uint8_t bit;
if (!br_check(rar, 1))
return -1;
bit = (uint8_t)br_bits(rar, 1);
if (code->tree[node].branches[bit] < 0) {
warn("Invalid data in bitstream"); /* invalid prefix code in bitstream */
return -1;
}
node = code->tree[node].branches[bit];
}
return code->tree[node].branches[0];
}
/***** RAR version 2 decompression *****/
static void rar_free_codes_v2(struct ar_archive_rar_uncomp_v2 *uncomp_v2)
{
int i;
rar_free_code(&uncomp_v2->maincode);
rar_free_code(&uncomp_v2->offsetcode);
rar_free_code(&uncomp_v2->lengthcode);
for (i = 0; i < 4; i++)
rar_free_code(&uncomp_v2->audiocode[i]);
}
static bool rar_parse_codes_v2(ar_archive_rar *rar)
{
struct ar_archive_rar_uncomp_v2 *uncomp_v2 = &rar->uncomp.state.v2;
struct huffman_code precode;
uint8_t prelengths[19];
uint16_t i, count;
int j, val, n;
bool ok = false;
rar_free_codes_v2(uncomp_v2);
if (!br_check(rar, 2))
return false;
uncomp_v2->audioblock = br_bits(rar, 1) != 0;
if (!br_bits(rar, 1))
memset(uncomp_v2->lengthtable, 0, sizeof(uncomp_v2->lengthtable));
if (uncomp_v2->audioblock) {
if (!br_check(rar, 2))
return false;
uncomp_v2->numchannels = (uint8_t)br_bits(rar, 2) + 1;
count = uncomp_v2->numchannels * 257;
if (uncomp_v2->channel > uncomp_v2->numchannels)
uncomp_v2->channel = 0;
}
else
count = MAINCODE_SIZE_20 + OFFSETCODE_SIZE_20 + LENGTHCODE_SIZE_20;
for (i = 0; i < 19; i++) {
if (!br_check(rar, 4))
return false;
prelengths[i] = (uint8_t)br_bits(rar, 4);
}
memset(&precode, 0, sizeof(precode));
if (!rar_create_code(&precode, prelengths, 19))
goto PrecodeError;
for (i = 0; i < count; ) {
val = rar_read_next_symbol(rar, &precode);
if (val < 0)
goto PrecodeError;
if (val < 16) {
uncomp_v2->lengthtable[i] = (uncomp_v2->lengthtable[i] + val) & 0x0F;
i++;
}
else if (val == 16) {
if (i == 0) {
warn("Invalid data in bitstream");
goto PrecodeError;
}
if (!br_check(rar, 2))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 2) + 3;
for (j = 0; j < n && i < count; i++, j++) {
uncomp_v2->lengthtable[i] = uncomp_v2->lengthtable[i - 1];
}
}
else {
if (val == 17) {
if (!br_check(rar, 3))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 3) + 3;
}
else {
if (!br_check(rar, 7))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 7) + 11;
}
for (j = 0; j < n && i < count; i++, j++) {
uncomp_v2->lengthtable[i] = 0;
}
}
}
ok = true;
PrecodeError:
rar_free_code(&precode);
if (!ok)
return false;
if (uncomp_v2->audioblock) {
for (i = 0; i < uncomp_v2->numchannels; i++) {
if (!rar_create_code(&uncomp_v2->audiocode[i], uncomp_v2->lengthtable + i * 257, 257))
return false;
}
}
else {
if (!rar_create_code(&uncomp_v2->maincode, uncomp_v2->lengthtable, MAINCODE_SIZE_20))
return false;
if (!rar_create_code(&uncomp_v2->offsetcode, uncomp_v2->lengthtable + MAINCODE_SIZE_20, OFFSETCODE_SIZE_20))
return false;
if (!rar_create_code(&uncomp_v2->lengthcode, uncomp_v2->lengthtable + MAINCODE_SIZE_20 + OFFSETCODE_SIZE_20, LENGTHCODE_SIZE_20))
return false;
}
rar->uncomp.start_new_table = false;
return true;
}
static uint8_t rar_decode_audio(struct AudioState *state, int8_t *channeldelta, int8_t delta)
{
uint8_t predbyte, byte;
int prederror;
state->delta[3] = state->delta[2];
state->delta[2] = state->delta[1];
state->delta[1] = state->lastdelta - state->delta[0];
state->delta[0] = state->lastdelta;
predbyte = ((8 * state->lastbyte + state->weight[0] * state->delta[0] + state->weight[1] * state->delta[1] + state->weight[2] * state->delta[2] + state->weight[3] * state->delta[3] + state->weight[4] * *channeldelta) >> 3) & 0xFF;
byte = (predbyte - delta) & 0xFF;
prederror = delta << 3;
state->error[0] += abs(prederror);
state->error[1] += abs(prederror - state->delta[0]); state->error[2] += abs(prederror + state->delta[0]);
state->error[3] += abs(prederror - state->delta[1]); state->error[4] += abs(prederror + state->delta[1]);
state->error[5] += abs(prederror - state->delta[2]); state->error[6] += abs(prederror + state->delta[2]);
state->error[7] += abs(prederror - state->delta[3]); state->error[8] += abs(prederror + state->delta[3]);
state->error[9] += abs(prederror - *channeldelta); state->error[10] += abs(prederror + *channeldelta);
*channeldelta = state->lastdelta = (int8_t)(byte - state->lastbyte);
state->lastbyte = byte;
if (!(++state->count & 0x1F)) {
uint8_t i, idx = 0;
for (i = 1; i < 11; i++) {
if (state->error[i] < state->error[idx])
idx = i;
}
memset(state->error, 0, sizeof(state->error));
switch (idx) {
case 1: if (state->weight[0] >= -16) state->weight[0]--; break;
case 2: if (state->weight[0] < 16) state->weight[0]++; break;
case 3: if (state->weight[1] >= -16) state->weight[1]--; break;
case 4: if (state->weight[1] < 16) state->weight[1]++; break;
case 5: if (state->weight[2] >= -16) state->weight[2]--; break;
case 6: if (state->weight[2] < 16) state->weight[2]++; break;
case 7: if (state->weight[3] >= -16) state->weight[3]--; break;
case 8: if (state->weight[3] < 16) state->weight[3]++; break;
case 9: if (state->weight[4] >= -16) state->weight[4]--; break;
case 10: if (state->weight[4] < 16) state->weight[4]++; break;
}
}
return byte;
}
static int64_t rar_expand_v2(ar_archive_rar *rar, int64_t end)
{
static const uint8_t lengthbases[] =
{ 0, 1, 2, 3, 4, 5, 6,
7, 8, 10, 12, 14, 16, 20,
24, 28, 32, 40, 48, 56, 64,
80, 96, 112, 128, 160, 192, 224 };
static const uint8_t lengthbits[] =
{ 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 5 };
static const int32_t offsetbases[] =
{ 0, 1, 2, 3, 4, 6,
8, 12, 16, 24, 32, 48,
64, 96, 128, 192, 256, 384,
512, 768, 1024, 1536, 2048, 3072,
4096, 6144, 8192, 12288, 16384, 24576,
32768, 49152, 65536, 98304, 131072, 196608,
262144, 327680, 393216, 458752, 524288, 589824,
655360, 720896, 786432, 851968, 917504, 983040 };
static const uint8_t offsetbits[] =
{ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 };
static const uint8_t shortbases[] =
{ 0, 4, 8, 16, 32, 64, 128, 192 };
static const uint8_t shortbits[] =
{ 2, 2, 3, 4, 5, 6, 6, 6 };
struct ar_archive_rar_uncomp_v2 *uncomp_v2 = &rar->uncomp.state.v2;
LZSS *lzss = &rar->uncomp.lzss;
int symbol, offs, len;
if ((uint64_t)end > rar->super.entry_size_uncompressed + rar->solid.size_total)
end = rar->super.entry_size_uncompressed + rar->solid.size_total;
for (;;) {
if (lzss_position(lzss) >= end)
return end;
if (uncomp_v2->audioblock) {
uint8_t byte;
symbol = rar_read_next_symbol(rar, &uncomp_v2->audiocode[uncomp_v2->channel]);
if (symbol < 0)
return -1;
if (symbol == 256) {
rar->uncomp.start_new_table = true;
return lzss_position(lzss);
}
byte = rar_decode_audio(&uncomp_v2->audiostate[uncomp_v2->channel], &uncomp_v2->channeldelta, (int8_t)(uint8_t)symbol);
uncomp_v2->channel++;
if (uncomp_v2->channel == uncomp_v2->numchannels)
uncomp_v2->channel = 0;
lzss_emit_literal(lzss, byte);
continue;
}
symbol = rar_read_next_symbol(rar, &uncomp_v2->maincode);
if (symbol < 0)
return -1;
if (symbol < 256) {
lzss_emit_literal(lzss, (uint8_t)symbol);
continue;
}
if (symbol == 256) {
offs = uncomp_v2->lastoffset;
len = uncomp_v2->lastlength;
}
else if (symbol <= 260) {
int idx = symbol - 256;
int lensymbol = rar_read_next_symbol(rar, &uncomp_v2->lengthcode);
offs = uncomp_v2->oldoffset[(uncomp_v2->oldoffsetindex - idx) & 0x03];
if (lensymbol < 0 || lensymbol > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || lensymbol > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
len = lengthbases[lensymbol] + 2;
if (lengthbits[lensymbol] > 0) {
if (!br_check(rar, lengthbits[lensymbol]))
return -1;
len += (uint8_t)br_bits(rar, lengthbits[lensymbol]);
}
if (offs >= 0x40000)
len++;
if (offs >= 0x2000)
len++;
if (offs >= 0x101)
len++;
}
else if (symbol <= 268) {
int idx = symbol - 261;
offs = shortbases[idx] + 1;
if (shortbits[idx] > 0) {
if (!br_check(rar, shortbits[idx]))
return -1;
offs += (uint8_t)br_bits(rar, shortbits[idx]);
}
len = 2;
}
else if (symbol == 269) {
rar->uncomp.start_new_table = true;
return lzss_position(lzss);
}
else {
int idx = symbol - 270;
int offssymbol;
if (idx > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || idx > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
len = lengthbases[idx] + 3;
if (lengthbits[idx] > 0) {
if (!br_check(rar, lengthbits[idx]))
return -1;
len += (uint8_t)br_bits(rar, lengthbits[idx]);
}
offssymbol = rar_read_next_symbol(rar, &uncomp_v2->offsetcode);
if (offssymbol < 0 || offssymbol > (int)(sizeof(offsetbases) / sizeof(offsetbases[0])) || offssymbol > (int)(sizeof(offsetbits) / sizeof(offsetbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
offs = offsetbases[offssymbol] + 1;
if (offsetbits[offssymbol] > 0) {
if (!br_check(rar, offsetbits[offssymbol]))
return -1;
offs += (int)br_bits(rar, offsetbits[offssymbol]);
}
if (offs >= 0x40000)
len++;
if (offs >= 0x2000)
len++;
}
uncomp_v2->lastoffset = uncomp_v2->oldoffset[uncomp_v2->oldoffsetindex++ & 0x03] = offs;
uncomp_v2->lastlength = len;
lzss_emit_match(lzss, offs, len);
}
}
/***** RAR version 3 decompression *****/
static void rar_free_codes(struct ar_archive_rar_uncomp *uncomp)
{
struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &uncomp->state.v3;
if (uncomp->version == 2) {
rar_free_codes_v2(&uncomp->state.v2);
return;
}
rar_free_code(&uncomp_v3->maincode);
rar_free_code(&uncomp_v3->offsetcode);
rar_free_code(&uncomp_v3->lowoffsetcode);
rar_free_code(&uncomp_v3->lengthcode);
}
static bool rar_parse_codes(ar_archive_rar *rar)
{
struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3;
if (rar->uncomp.version == 2)
return rar_parse_codes_v2(rar);
rar_free_codes(&rar->uncomp);
br_clear_leftover_bits(&rar->uncomp);
if (!br_check(rar, 1))
return false;
uncomp_v3->is_ppmd_block = br_bits(rar, 1) != 0;
if (uncomp_v3->is_ppmd_block) {
uint8_t ppmd_flags;
uint32_t max_alloc = 0;
if (!br_check(rar, 7))
return false;
ppmd_flags = (uint8_t)br_bits(rar, 7);
if ((ppmd_flags & 0x20)) {
if (!br_check(rar, 8))
return false;
max_alloc = ((uint8_t)br_bits(rar, 8) + 1) << 20;
}
if ((ppmd_flags & 0x40)) {
if (!br_check(rar, 8))
return false;
uncomp_v3->ppmd_escape = (uint8_t)br_bits(rar, 8);
}
if ((ppmd_flags & 0x20)) {
uint32_t maxorder = (ppmd_flags & 0x1F) + 1;
if (maxorder == 1)
return false;
if (maxorder > 16)
maxorder = 16 + (maxorder - 16) * 3;
Ppmd7_Free(&uncomp_v3->ppmd7_context, &gSzAlloc);
Ppmd7_Construct(&uncomp_v3->ppmd7_context);
if (!Ppmd7_Alloc(&uncomp_v3->ppmd7_context, max_alloc, &gSzAlloc)) {
warn("OOM during decompression");
return false;
}
ByteIn_CreateVTable(&uncomp_v3->bytein, rar);
// We need to set the stream before calling RangeDec_Init
uncomp_v3->ppmd7_context.rc.dec.Stream = &uncomp_v3->bytein.super;
Ppmd7a_RangeDec_Init(&uncomp_v3->ppmd7_context.rc.dec);
Ppmd7_Init(&uncomp_v3->ppmd7_context, maxorder);
}
else {
if (!Ppmd7_WasAllocated(&uncomp_v3->ppmd7_context)) {
warn("Invalid data in bitstream"); /* invalid PPMd sequence */
return false;
}
Ppmd7a_RangeDec_Init(&uncomp_v3->ppmd7_context.rc.dec);
}
}
else {
struct huffman_code precode;
uint8_t bitlengths[20];
uint8_t zerocount;
int i, j, val, n;
bool ok = false;
if (!br_check(rar, 1))
return false;
if (!br_bits(rar, 1))
memset(uncomp_v3->lengthtable, 0, sizeof(uncomp_v3->lengthtable));
memset(&bitlengths, 0, sizeof(bitlengths));
for (i = 0; i < (int)sizeof(bitlengths); i++) {
if (!br_check(rar, 4))
return false;
bitlengths[i] = (uint8_t)br_bits(rar, 4);
if (bitlengths[i] == 0x0F) {
if (!br_check(rar, 4))
return false;
zerocount = (uint8_t)br_bits(rar, 4);
if (zerocount) {
for (j = 0; j < zerocount + 2 && i < (int)sizeof(bitlengths); j++) {
bitlengths[i++] = 0;
}
i--;
}
}
}
memset(&precode, 0, sizeof(precode));
if (!rar_create_code(&precode, bitlengths, sizeof(bitlengths)))
goto PrecodeError;
for (i = 0; i < HUFFMAN_TABLE_SIZE; ) {
val = rar_read_next_symbol(rar, &precode);
if (val < 0)
goto PrecodeError;
if (val < 16) {
uncomp_v3->lengthtable[i] = (uncomp_v3->lengthtable[i] + val) & 0x0F;
i++;
}
else if (val < 18) {
if (i == 0) {
warn("Invalid data in bitstream");
goto PrecodeError;
}
if (val == 16) {
if (!br_check(rar, 3))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 3) + 3;
}
else {
if (!br_check(rar, 7))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 7) + 11;
}
for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; i++, j++) {
uncomp_v3->lengthtable[i] = uncomp_v3->lengthtable[i - 1];
}
}
else {
if (val == 18) {
if (!br_check(rar, 3))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 3) + 3;
}
else {
if (!br_check(rar, 7))
goto PrecodeError;
n = (uint8_t)br_bits(rar, 7) + 11;
}
for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; i++, j++) {
uncomp_v3->lengthtable[i] = 0;
}
}
}
ok = true;
PrecodeError:
rar_free_code(&precode);
if (!ok)
return false;
if (!rar_create_code(&uncomp_v3->maincode, uncomp_v3->lengthtable, MAINCODE_SIZE))
return false;
if (!rar_create_code(&uncomp_v3->offsetcode, uncomp_v3->lengthtable + MAINCODE_SIZE, OFFSETCODE_SIZE))
return false;
if (!rar_create_code(&uncomp_v3->lowoffsetcode, uncomp_v3->lengthtable + MAINCODE_SIZE + OFFSETCODE_SIZE, LOWOFFSETCODE_SIZE))
return false;
if (!rar_create_code(&uncomp_v3->lengthcode, uncomp_v3->lengthtable + MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE, LENGTHCODE_SIZE))
return false;
}
rar->uncomp.start_new_table = false;
return true;
}
static bool rar_read_filter(ar_archive_rar *rar, bool (* decode_byte)(ar_archive_rar *rar, uint8_t *byte), int64_t *end)
{
uint8_t flags, val, *code;
uint16_t length, i;
if (!decode_byte(rar, &flags))
return false;
length = (flags & 0x07) + 1;
if (length == 7) {
if (!decode_byte(rar, &val))
return false;
length = val + 7;
}
else if (length == 8) {
if (!decode_byte(rar, &val))
return false;
length = val << 8;
if (!decode_byte(rar, &val))
return false;
length |= val;
}
code = malloc(length);
if (!code) {
warn("OOM during decompression");
return false;
}
for (i = 0; i < length; i++) {
if (!decode_byte(rar, &code[i])) {
free(code);
return false;
}
}
if (!rar_parse_filter(rar, code, length, flags)) {
free(code);
return false;
}
free(code);
if (rar->uncomp.state.v3.filters.filterstart < (size_t)*end)
*end = rar->uncomp.state.v3.filters.filterstart;
return true;
}
static inline bool rar_decode_ppmd7_symbol(struct ar_archive_rar_uncomp_v3 *uncomp_v3, Byte *symbol)
{
int value = Ppmd7a_DecodeSymbol(&uncomp_v3->ppmd7_context);
if (value < 0) {
warn("Invalid data in bitstream"); /* invalid PPMd symbol */
return false;
}
*symbol = (Byte)value;
return true;
}
static bool rar_decode_byte(ar_archive_rar *rar, uint8_t *byte)
{
if (!br_check(rar, 8))
return false;
*byte = (uint8_t)br_bits(rar, 8);
return true;
}
static bool rar_decode_ppmd7_byte(ar_archive_rar *rar, uint8_t *byte)
{
return rar_decode_ppmd7_symbol(&rar->uncomp.state.v3, byte);
}
static bool rar_handle_ppmd_sequence(ar_archive_rar *rar, int64_t *end)
{
struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3;
LZSS *lzss = &rar->uncomp.lzss;
Byte sym, code, length;
int lzss_offset;
if (!rar_decode_ppmd7_symbol(uncomp_v3, &sym))
return false;
if (sym != uncomp_v3->ppmd_escape) {
lzss_emit_literal(lzss, sym);
return true;
}
if (!rar_decode_ppmd7_symbol(uncomp_v3, &code))
return false;
switch (code) {
case 0:
return rar_parse_codes(rar);
case 2:
rar->uncomp.start_new_table = true;
return true;
case 3:
return rar_read_filter(rar, rar_decode_ppmd7_byte, end);
case 4:
if (!rar_decode_ppmd7_symbol(uncomp_v3, &code))
return false;
lzss_offset = code << 16;
if (!rar_decode_ppmd7_symbol(uncomp_v3, &code))
return false;
lzss_offset |= code << 8;
if (!rar_decode_ppmd7_symbol(uncomp_v3, &code))
return false;
lzss_offset |= code;
if (!rar_decode_ppmd7_symbol(uncomp_v3, &length))
return false;
lzss_emit_match(lzss, lzss_offset + 2, length + 32);
return true;
case 5:
if (!rar_decode_ppmd7_symbol(uncomp_v3, &length))
return false;
lzss_emit_match(lzss, 1, length + 4);
return true;
default:
lzss_emit_literal(lzss, sym);
return true;
}
}
int64_t rar_expand(ar_archive_rar *rar, int64_t end)
{
static const uint8_t lengthbases[] =
{ 0, 1, 2, 3, 4, 5, 6,
7, 8, 10, 12, 14, 16, 20,
24, 28, 32, 40, 48, 56, 64,
80, 96, 112, 128, 160, 192, 224 };
static const uint8_t lengthbits[] =
{ 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 2, 2,
2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 5 };
static const int32_t offsetbases[] =
{ 0, 1, 2, 3, 4, 6,
8, 12, 16, 24, 32, 48,
64, 96, 128, 192, 256, 384,
512, 768, 1024, 1536, 2048, 3072,
4096, 6144, 8192, 12288, 16384, 24576,
32768, 49152, 65536, 98304, 131072, 196608,
262144, 327680, 393216, 458752, 524288, 589824,
655360, 720896, 786432, 851968, 917504, 983040,
1048576, 1310720, 1572864, 1835008, 2097152, 2359296,
2621440, 2883584, 3145728, 3407872, 3670016, 3932160 };
static const uint8_t offsetbits[] =
{ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };
static const uint8_t shortbases[] =
{ 0, 4, 8, 16, 32, 64, 128, 192 };
static const uint8_t shortbits[] =
{ 2, 2, 3, 4, 5, 6, 6, 6 };
struct ar_archive_rar_uncomp_v3 *uncomp_v3 = &rar->uncomp.state.v3;
LZSS *lzss = &rar->uncomp.lzss;
int symbol, offs, len, i;
if (rar->uncomp.version == 2)
return rar_expand_v2(rar, end);
for (;;) {
if (lzss_position(lzss) >= end)
return end;
if (uncomp_v3->is_ppmd_block) {
if (!rar_handle_ppmd_sequence(rar, &end))
return -1;
if (rar->uncomp.start_new_table)
return lzss_position(lzss);
continue;
}
symbol = rar_read_next_symbol(rar, &uncomp_v3->maincode);
if (symbol < 0)
return -1;
if (symbol < 256) {
lzss_emit_literal(lzss, (uint8_t)symbol);
continue;
}
if (symbol == 256) {
if (!br_check(rar, 1))
return -1;
if (!br_bits(rar, 1)) {
if (!br_check(rar, 1))
return -1;
rar->uncomp.start_new_table = br_bits(rar, 1) != 0;
return lzss_position(lzss);
}
if (!rar_parse_codes(rar))
return -1;
continue;
}
if (symbol == 257) {
if (!rar_read_filter(rar, rar_decode_byte, &end))
return -1;
continue;
}
if (symbol == 258) {
if (uncomp_v3->lastlength == 0)
continue;
offs = uncomp_v3->lastoffset;
len = uncomp_v3->lastlength;
}
else if (symbol <= 262) {
int idx = symbol - 259;
int lensymbol = rar_read_next_symbol(rar, &uncomp_v3->lengthcode);
offs = uncomp_v3->oldoffset[idx];
if (lensymbol < 0 || lensymbol > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || lensymbol > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
len = lengthbases[lensymbol] + 2;
if (lengthbits[lensymbol] > 0) {
if (!br_check(rar, lengthbits[lensymbol]))
return -1;
len += (uint8_t)br_bits(rar, lengthbits[lensymbol]);
}
for (i = idx; i > 0; i--)
uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1];
uncomp_v3->oldoffset[0] = offs;
}
else if (symbol <= 270) {
int idx = symbol - 263;
offs = shortbases[idx] + 1;
if (shortbits[idx] > 0) {
if (!br_check(rar, shortbits[idx]))
return -1;
offs += (uint8_t)br_bits(rar, shortbits[idx]);
}
len = 2;
for (i = 3; i > 0; i--)
uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1];
uncomp_v3->oldoffset[0] = offs;
}
else {
int idx = symbol - 271;
int offssymbol;
if (idx > (int)(sizeof(lengthbases) / sizeof(lengthbases[0])) || idx > (int)(sizeof(lengthbits) / sizeof(lengthbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
len = lengthbases[idx] + 3;
if (lengthbits[idx] > 0) {
if (!br_check(rar, lengthbits[idx]))
return -1;
len += (uint8_t)br_bits(rar, lengthbits[idx]);
}
offssymbol = rar_read_next_symbol(rar, &uncomp_v3->offsetcode);
if (offssymbol < 0 || offssymbol > (int)(sizeof(offsetbases) / sizeof(offsetbases[0])) || offssymbol > (int)(sizeof(offsetbits) / sizeof(offsetbits[0]))) {
warn("Invalid data in bitstream");
return -1;
}
offs = offsetbases[offssymbol] + 1;
if (offsetbits[offssymbol] > 0) {
if (offssymbol > 9) {
if (offsetbits[offssymbol] > 4) {
if (!br_check(rar, offsetbits[offssymbol] - 4))
return -1;
offs += (int)br_bits(rar, offsetbits[offssymbol] - 4) << 4;
}
if (uncomp_v3->numlowoffsetrepeats > 0) {
uncomp_v3->numlowoffsetrepeats--;
offs += uncomp_v3->lastlowoffset;
}
else {
int lowoffsetsymbol = rar_read_next_symbol(rar, &uncomp_v3->lowoffsetcode);
if (lowoffsetsymbol < 0)
return -1;
if (lowoffsetsymbol == 16) {
uncomp_v3->numlowoffsetrepeats = 15;
offs += uncomp_v3->lastlowoffset;
}
else {
offs += lowoffsetsymbol;
uncomp_v3->lastlowoffset = lowoffsetsymbol;
}
}
}
else {
if (!br_check(rar, offsetbits[offssymbol]))
return -1;
offs += (int)br_bits(rar, offsetbits[offssymbol]);
}
}
if (offs >= 0x40000)
len++;
if (offs >= 0x2000)
len++;
for (i = 3; i > 0; i--)
uncomp_v3->oldoffset[i] = uncomp_v3->oldoffset[i - 1];
uncomp_v3->oldoffset[0] = offs;
}
uncomp_v3->lastoffset = offs;
uncomp_v3->lastlength = len;
lzss_emit_match(lzss, offs, len);
}
}
bool rar_uncompress_part(ar_archive_rar *rar, void *buffer, size_t buffer_size)
{
struct ar_archive_rar_uncomp *uncomp = &rar->uncomp;
struct ar_archive_rar_uncomp_v3 *uncomp_v3 = NULL;
size_t end;
if (!rar_init_uncompress(uncomp, rar->entry.version))
return false;
if (uncomp->version == 3)
uncomp_v3 = &uncomp->state.v3;
for (;;) {
if (uncomp_v3 && uncomp_v3->filters.bytes_ready > 0) {
size_t count = smin(uncomp_v3->filters.bytes_ready, buffer_size);
memcpy(buffer, uncomp_v3->filters.bytes, count);
uncomp_v3->filters.bytes_ready -= count;
uncomp_v3->filters.bytes += count;
rar->progress.bytes_done += count;
buffer_size -= count;
buffer = (uint8_t *)buffer + count;
if (rar->progress.bytes_done == rar->super.entry_size_uncompressed)
goto FinishBlock;
}
else if (uncomp->bytes_ready > 0) {
int count = (int)smin(uncomp->bytes_ready, buffer_size);
lzss_copy_bytes_from_window(&uncomp->lzss, buffer, rar->progress.bytes_done + rar->solid.size_total, count);
uncomp->bytes_ready -= count;
rar->progress.bytes_done += count;
buffer_size -= count;
buffer = (uint8_t *)buffer + count;
}
if (buffer_size == 0)
return true;
if (uncomp->br.at_eof)
return false;
if (uncomp_v3 && uncomp_v3->filters.lastend == uncomp_v3->filters.filterstart) {
if (!rar_run_filters(rar))
return false;
continue;
}
FinishBlock:
if (uncomp->start_new_table && !rar_parse_codes(rar))
return false;
end = rar->progress.bytes_done + rar->solid.size_total + LZSS_WINDOW_SIZE - LZSS_OVERFLOW_SIZE;
if (uncomp_v3 && uncomp_v3->filters.filterstart < end)
end = uncomp_v3->filters.filterstart;
end = (size_t)rar_expand(rar, end);
if (end == (size_t)-1 || end < rar->progress.bytes_done + rar->solid.size_total)
return false;
uncomp->bytes_ready = end - rar->progress.bytes_done - rar->solid.size_total;
if (uncomp_v3)
uncomp_v3->filters.lastend = end;
if (uncomp_v3 && uncomp_v3->is_ppmd_block && uncomp->start_new_table)
goto FinishBlock;
}
}