Fuck git
This commit is contained in:
711
external/unarr/rar/filter-rar.c
vendored
Normal file
711
external/unarr/rar/filter-rar.c
vendored
Normal 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
149
external/unarr/rar/huffman-rar.c
vendored
Normal 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
88
external/unarr/rar/lzss.h
vendored
Normal 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
239
external/unarr/rar/parse-rar.c
vendored
Normal 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
223
external/unarr/rar/rar.c
vendored
Normal 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
243
external/unarr/rar/rar.h
vendored
Normal 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
619
external/unarr/rar/rarvm.c
vendored
Normal 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
117
external/unarr/rar/rarvm.h
vendored
Normal 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
984
external/unarr/rar/uncompress-rar.c
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user