RarVM.java
/*
* Copyright (c) 2007 innoSysTec (R) GmbH, Germany. All rights reserved.
* Original author: Edmund Wagner
* Creation date: 31.05.2007
*
* Source: $HeadURL$
* Last changed: $LastChangedDate$
*
* the unrar licence applies to all junrar source and binary distributions
* you are not allowed to use this source to re-create the RAR compression algorithm
*
* Here some html entities which can be used for escaping javadoc tags:
* "&": "&" or "&"
* "<": "<" or "<"
* ">": ">" or ">"
* "@": "@"
*/
package com.github.junrar.unpack.vm;
import com.github.junrar.crc.RarCRC;
import com.github.junrar.io.Raw;
import java.util.List;
import java.util.Vector;
/**
* DOCUMENT ME
*
* @author $LastChangedBy$
* @version $LastChangedRevision$
*/
public class RarVM extends BitInput {
public static final int VM_MEMSIZE = 0x40000;
public static final int VM_MEMMASK = (VM_MEMSIZE - 1);
public static final int VM_GLOBALMEMADDR = 0x3C000;
public static final int VM_GLOBALMEMSIZE = 0x2000;
public static final int VM_FIXEDGLOBALSIZE = 64;
private static final int regCount = 8;
private static final long UINT_MASK = 0xffffFFFF; //((long)2*(long)Integer.MAX_VALUE);
private byte[] mem;
private final int[] R = new int[regCount];
private int flags;
private int maxOpCount = 25000000;
private int codeSize;
private int IP;
public RarVM() {
mem = null;
}
public void init() {
if (mem == null) {
mem = new byte[VM_MEMSIZE + 4];
}
}
private boolean isVMMem(byte[] mem) {
return this.mem == mem;
}
private int getValue(boolean byteMode, byte[] mem, int offset) {
if (byteMode) {
if (isVMMem(mem)) {
return (mem[offset]);
} else {
return (mem[offset] & 0xff);
}
} else {
if (isVMMem(mem)) {
//little
return Raw.readIntLittleEndian(mem, offset);
} else {
//big endian
return Raw.readIntBigEndian(mem, offset);
}
}
}
private void setValue(boolean byteMode, byte[] mem, int offset, int value) {
if (byteMode) {
if (isVMMem(mem)) {
mem[offset] = (byte) value;
} else {
mem[offset] = (byte) ((mem[offset] & 0x00) | (byte) (value & 0xff));
}
} else {
if (isVMMem(mem)) {
Raw.writeIntLittleEndian(mem, offset, value);
// mem[offset + 0] = (byte) value;
// mem[offset + 1] = (byte) (value >>> 8);
// mem[offset + 2] = (byte) (value >>> 16);
// mem[offset + 3] = (byte) (value >>> 24);
} else {
Raw.writeIntBigEndian(mem, offset, value);
// mem[offset + 3] = (byte) value;
// mem[offset + 2] = (byte) (value >>> 8);
// mem[offset + 1] = (byte) (value >>> 16);
// mem[offset + 0] = (byte) (value >>> 24);
}
}
// #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint
// *)Addr,Value)
}
public void setLowEndianValue(byte[] mem, int offset, int value) {
Raw.writeIntLittleEndian(mem, offset, value);
// mem[offset + 0] = (byte) (value&0xff);
// mem[offset + 1] = (byte) ((value >>> 8)&0xff);
// mem[offset + 2] = (byte) ((value >>> 16)&0xff);
// mem[offset + 3] = (byte) ((value >>> 24)&0xff);
}
public void setLowEndianValue(Vector<Byte> mem, int offset, int value) {
mem.set(offset + 0, (byte) (value & 0xff));
mem.set(offset + 1, (byte) ((value >>> 8) & 0xff));
mem.set(offset + 2, (byte) ((value >>> 16) & 0xff));
mem.set(offset + 3, (byte) ((value >>> 24) & 0xff));
}
private int getOperand(VMPreparedOperand cmdOp) {
int ret = 0;
if (cmdOp.getType() == VMOpType.VM_OPREGMEM) {
int pos = (cmdOp.getOffset() + cmdOp.getBase()) & VM_MEMMASK;
ret = Raw.readIntLittleEndian(mem, pos);
} else {
int pos = cmdOp.getOffset();
ret = Raw.readIntLittleEndian(mem, pos);
}
return ret;
}
public void execute(VMPreparedProgram prg) {
for (int i = 0; i < prg.getInitR().length; i++) { // memcpy(R,Prg->InitR,sizeof(Prg->InitR));
R[i] = prg.getInitR()[i];
}
long globalSize = Math
.min(prg.getGlobalData().size(), VM_GLOBALMEMSIZE) & 0xffFFffFF;
if (globalSize != 0) {
for (int i = 0; i < globalSize; i++) { // memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize);
mem[VM_GLOBALMEMADDR + i] = prg.getGlobalData().get(i);
}
}
long staticSize = Math.min(prg.getStaticData().size(), VM_GLOBALMEMSIZE
- globalSize) & 0xffFFffFF;
if (staticSize != 0) {
for (int i = 0; i < staticSize; i++) { // memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize);
mem[VM_GLOBALMEMADDR + (int) globalSize + i] = prg
.getStaticData().get(i);
}
}
R[7] = VM_MEMSIZE;
flags = 0;
List<VMPreparedCommand> preparedCode = prg.getAltCmd().size() != 0 ? prg
.getAltCmd()
: prg.getCmd();
if (!ExecuteCode(preparedCode, prg.getCmdCount())) {
preparedCode.get(0).setOpCode(VMCommands.VM_RET);
}
int newBlockPos = getValue(false, mem, VM_GLOBALMEMADDR + 0x20)
& VM_MEMMASK;
int newBlockSize = getValue(false, mem, VM_GLOBALMEMADDR + 0x1c)
& VM_MEMMASK;
if ((newBlockPos + newBlockSize) >= VM_MEMSIZE) {
newBlockPos = 0;
newBlockSize = 0;
}
prg.setFilteredDataOffset(newBlockPos);
prg.setFilteredDataSize(newBlockSize);
prg.getGlobalData().clear();
int dataSize = Math.min(getValue(false, mem, VM_GLOBALMEMADDR + 0x30),
VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE);
if (dataSize != 0) {
prg.getGlobalData().setSize(dataSize + VM_FIXEDGLOBALSIZE);
// ->GlobalData.Add(dataSize+VM_FIXEDGLOBALSIZE);
for (int i = 0; i < dataSize + VM_FIXEDGLOBALSIZE; i++) { // memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE);
prg.getGlobalData().set(i, mem[VM_GLOBALMEMADDR + i]);
}
}
}
public byte[] getMem() {
return mem;
}
private boolean setIP(int ip) {
if ((ip) >= codeSize) {
return (true);
}
if (--maxOpCount <= 0) {
return (false);
}
IP = ip;
return true;
}
private boolean ExecuteCode(List<VMPreparedCommand> preparedCode,
int cmdCount) {
maxOpCount = 25000000;
this.codeSize = cmdCount;
this.IP = 0;
while (true) {
VMPreparedCommand cmd = preparedCode.get(IP);
int op1 = getOperand(cmd.getOp1());
int op2 = getOperand(cmd.getOp2());
switch (cmd.getOpCode()) {
case VM_MOV:
setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
mem, op2)); // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
break;
case VM_MOVB:
setValue(true, mem, op1, getValue(true, mem, op2));
break;
case VM_MOVD:
setValue(false, mem, op1, getValue(false, mem, op2));
break;
case VM_CMP: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = value1 - getValue(cmd.isByteMode(), mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_CMPB: {
int value1 = getValue(true, mem, op1);
int result = value1 - getValue(true, mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_CMPD: {
int value1 = getValue(false, mem, op1);
int result = value1 - getValue(false, mem, op2);
if (result == 0) {
flags = VMFlags.VM_FZ.getFlag();
} else {
flags = (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
}
}
break;
case VM_ADD: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = (int) ((((long) value1 + (long) getValue(cmd
.isByteMode(), mem, op2))) & 0xffffffff);
if (cmd.isByteMode()) {
result &= 0xff;
flags = (result < value1) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: ((result & 0x80) != 0) ? VMFlags.VM_FS
.getFlag() : 0);
// Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ?
// VM_FS:0));
} else {
flags = (result < value1) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
}
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_ADDB:
setValue(true, mem, op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff
+ (long) getValue(true, mem, op2) & 0xFFffFFff));
break;
case VM_ADDD:
setValue(
false,
mem,
op1,
(int) ((long) getValue(false, mem, op1) & 0xFFffFFff
+ (long) getValue(false, mem, op2) & 0xFFffFFff));
break;
case VM_SUB: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int result = (int) ((long) value1 & 0xffFFffFF
- (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff);
flags = (result == 0) ? VMFlags.VM_FZ.getFlag()
: (result > value1) ? 1 : 0 | (result & VMFlags.VM_FS
.getFlag());
setValue(cmd.isByteMode(), mem, op1, result); // (Cmd->ByteMode,Op1,Result);
}
break;
case VM_SUBB:
setValue(true, mem, op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff
- (long) getValue(true, mem, op2) & 0xFFffFFff));
break;
case VM_SUBD:
setValue(
false,
mem,
op1,
(int) ((long) getValue(false, mem, op1) & 0xFFffFFff
- (long) getValue(false, mem, op2) & 0xFFffFFff));
break;
case VM_JZ:
if ((flags & VMFlags.VM_FZ.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JNZ:
if ((flags & VMFlags.VM_FZ.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_INC: {
int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff + 1);
if (cmd.isByteMode()) {
result &= 0xff;
}
setValue(cmd.isByteMode(), mem, op1, result);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_INCB:
setValue(
true,
mem,
op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff + 1));
break;
case VM_INCD:
setValue(false, mem, op1, (int) ((long) getValue(false, mem,
op1) & 0xFFffFFff + 1));
break;
case VM_DEC: {
int result = (int) ((long) getValue(cmd.isByteMode(), mem, op1) & 0xFFffFFff - 1);
setValue(cmd.isByteMode(), mem, op1, result);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_DECB:
setValue(
true,
mem,
op1,
(int) ((long) getValue(true, mem, op1) & 0xFFffFFff - 1));
break;
case VM_DECD:
setValue(false, mem, op1, (int) ((long) getValue(false, mem,
op1) & 0xFFffFFff - 1));
break;
case VM_JMP:
setIP(getValue(false, mem, op1));
continue;
case VM_XOR: {
int result = getValue(cmd.isByteMode(), mem, op1)
^ getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_AND: {
int result = getValue(cmd.isByteMode(), mem, op1)
& getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_OR: {
int result = getValue(cmd.isByteMode(), mem, op1)
| getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_TEST: {
int result = getValue(cmd.isByteMode(), mem, op1)
& getValue(cmd.isByteMode(), mem, op2);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : result
& VMFlags.VM_FS.getFlag();
}
break;
case VM_JS:
if ((flags & VMFlags.VM_FS.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JNS:
if ((flags & VMFlags.VM_FS.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JB:
if ((flags & VMFlags.VM_FC.getFlag()) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JBE:
if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) != 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JA:
if ((flags & (VMFlags.VM_FC.getFlag() | VMFlags.VM_FZ.getFlag())) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_JAE:
if ((flags & VMFlags.VM_FC.getFlag()) == 0) {
setIP(getValue(false, mem, op1));
continue;
}
break;
case VM_PUSH:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, getValue(false, mem,
op1));
break;
case VM_POP:
setValue(false, mem, op1, getValue(false, mem, R[7]
& VM_MEMMASK));
R[7] += 4;
break;
case VM_CALL:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, IP + 1);
setIP(getValue(false, mem, op1));
continue;
case VM_NOT:
setValue(cmd.isByteMode(), mem, op1, ~getValue(
cmd.isByteMode(), mem, op1));
break;
case VM_SHL: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = value1 << value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| (((value1 << (value2 - 1)) & 0x80000000) != 0 ? VMFlags.VM_FC
.getFlag()
: 0);
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SHR: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = value1 >>> value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| ((value1 >>> (value2 - 1)) & VMFlags.VM_FC.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SAR: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int value2 = getValue(cmd.isByteMode(), mem, op2);
int result = ((int) value1) >>> value2;
flags = (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()))
| ((value1 >>> (value2 - 1)) & VMFlags.VM_FC.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_NEG: {
int result = -getValue(cmd.isByteMode(), mem, op1);
flags = result == 0 ? VMFlags.VM_FZ.getFlag() : VMFlags.VM_FC
.getFlag()
| (result & VMFlags.VM_FS.getFlag());
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_NEGB:
setValue(true, mem, op1, -getValue(true, mem, op1));
break;
case VM_NEGD:
setValue(false, mem, op1, -getValue(false, mem, op1));
break;
case VM_PUSHA: {
for (int i = 0, SP = R[7] - 4; i < regCount; i++, SP -= 4) {
setValue(false, mem, SP & VM_MEMMASK, R[i]);
}
R[7] -= regCount * 4;
}
break;
case VM_POPA: {
for (int i = 0, SP = R[7]; i < regCount; i++, SP += 4) {
R[7 - i] = getValue(false, mem, SP & VM_MEMMASK);
}
}
break;
case VM_PUSHF:
R[7] -= 4;
setValue(false, mem, R[7] & VM_MEMMASK, flags);
break;
case VM_POPF:
flags = getValue(false, mem, R[7] & VM_MEMMASK);
R[7] += 4;
break;
case VM_MOVZX:
setValue(false, mem, op1, getValue(true, mem, op2));
break;
case VM_MOVSX:
setValue(false, mem, op1, (byte) getValue(true, mem, op2));
break;
case VM_XCHG: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
setValue(cmd.isByteMode(), mem, op1, getValue(cmd.isByteMode(),
mem, op2));
setValue(cmd.isByteMode(), mem, op2, value1);
}
break;
case VM_MUL: {
int result = (int) (((long) getValue(cmd.isByteMode(), mem, op1)
& 0xFFffFFff
* (long) getValue(cmd.isByteMode(), mem, op2) & 0xFFffFFff) & 0xFFffFFff);
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_DIV: {
int divider = getValue(cmd.isByteMode(), mem, op2);
if (divider != 0) {
int result = getValue(cmd.isByteMode(), mem, op1) / divider;
setValue(cmd.isByteMode(), mem, op1, result);
}
}
break;
case VM_ADC: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int FC = (flags & VMFlags.VM_FC.getFlag());
int result = (int) ((long) value1 & 0xFFffFFff
+ (long) getValue(cmd.isByteMode(), mem, op2)
& 0xFFffFFff + (long) FC & 0xFFffFFff);
if (cmd.isByteMode()) {
result &= 0xff;
}
flags = (result < value1 || result == value1 && FC != 0) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_SBB: {
int value1 = getValue(cmd.isByteMode(), mem, op1);
int FC = (flags & VMFlags.VM_FC.getFlag());
int result = (int) ((long) value1 & 0xFFffFFff
- (long) getValue(cmd.isByteMode(), mem, op2)
& 0xFFffFFff - (long) FC & 0xFFffFFff);
if (cmd.isByteMode()) {
result &= 0xff;
}
flags = (result > value1 || result == value1 && FC != 0) ? 1
: 0 | (result == 0 ? VMFlags.VM_FZ.getFlag()
: (result & VMFlags.VM_FS.getFlag()));
setValue(cmd.isByteMode(), mem, op1, result);
}
break;
case VM_RET:
if (R[7] >= VM_MEMSIZE) {
return (true);
}
setIP(getValue(false, mem, R[7] & VM_MEMMASK));
R[7] += 4;
continue;
case VM_STANDARD:
ExecuteStandardFilter(VMStandardFilters.findFilter(cmd.getOp1()
.getData()));
break;
case VM_PRINT:
break;
}
IP++;
--maxOpCount;
}
}
public void prepare(byte[] code, int codeSize, VMPreparedProgram prg) {
InitBitInput();
int cpLength = Math.min(MAX_SIZE, codeSize);
for (int i = 0; i < cpLength; i++) { // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE));
inBuf[i] |= code[i];
}
byte xorSum = 0;
for (int i = 1; i < codeSize; i++) {
xorSum ^= code[i];
}
faddbits(8);
prg.setCmdCount(0);
if (xorSum == code[0]) {
VMStandardFilters filterType = IsStandardFilter(code, codeSize);
if (filterType != VMStandardFilters.VMSF_NONE) {
VMPreparedCommand curCmd = new VMPreparedCommand();
curCmd.setOpCode(VMCommands.VM_STANDARD);
curCmd.getOp1().setData(filterType.getFilter());
curCmd.getOp1().setType(VMOpType.VM_OPNONE);
curCmd.getOp2().setType(VMOpType.VM_OPNONE);
codeSize = 0;
prg.getCmd().add(curCmd);
prg.setCmdCount(prg.getCmdCount() + 1);
// TODO
// curCmd->Op1.Data=FilterType;
// >>>>>> CurCmd->Op1.Addr=&CurCmd->Op1.Data; <<<<<<<<<< not set
// do i need to ?
// >>>>>> CurCmd->Op2.Addr=&CurCmd->Op2.Data; <<<<<<<<<< "
// CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE;
// CodeSize=0;
}
int dataFlag = fgetbits();
faddbits(1);
// Read static data contained in DB operators. This data cannot be
// changed,
// it is a part of VM code, not a filter parameter.
if ((dataFlag & 0x8000) != 0) {
long dataSize = (long) ((long) ReadData(this) & 0xffFFffFF + 1);
for (int i = 0; inAddr < codeSize && i < dataSize; i++) {
prg.getStaticData().add(
(byte) (fgetbits() >>> 8));
faddbits(8);
}
}
while (inAddr < codeSize) {
VMPreparedCommand curCmd = new VMPreparedCommand();
int data = fgetbits();
if ((data & 0x8000) == 0) {
curCmd.setOpCode(VMCommands.findVMCommand((data >>> 12)));
faddbits(4);
} else {
curCmd.setOpCode(VMCommands
.findVMCommand((data >>> 10) - 24));
faddbits(6);
}
if ((VMCmdFlags.VM_CmdFlags[curCmd.getOpCode().getVMCommand()] & VMCmdFlags.VMCF_BYTEMODE) != 0) {
curCmd.setByteMode((fgetbits() >>> 15) == 1 ? true : false);
faddbits(1);
} else {
curCmd.setByteMode(false);
}
curCmd.getOp1().setType(VMOpType.VM_OPNONE);
curCmd.getOp2().setType(VMOpType.VM_OPNONE);
int opNum = (VMCmdFlags.VM_CmdFlags[curCmd.getOpCode()
.getVMCommand()] & VMCmdFlags.VMCF_OPMASK);
// TODO >>> CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; <<<???
if (opNum > 0) {
decodeArg(curCmd.getOp1(), curCmd.isByteMode());
if (opNum == 2) {
decodeArg(curCmd.getOp2(), curCmd.isByteMode());
} else {
if (curCmd.getOp1().getType() == VMOpType.VM_OPINT
&& (VMCmdFlags.VM_CmdFlags[curCmd.getOpCode()
.getVMCommand()] & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC)) != 0) {
int distance = curCmd.getOp1().getData();
if (distance >= 256) {
distance -= 256;
} else {
if (distance >= 136) {
distance -= 264;
} else {
if (distance >= 16) {
distance -= 8;
} else {
if (distance >= 8) {
distance -= 16;
}
}
}
distance += prg.getCmdCount();
}
curCmd.getOp1().setData(distance);
}
}
}
prg.setCmdCount(prg.getCmdCount() + 1);
prg.getCmd().add(curCmd);
}
}
VMPreparedCommand curCmd = new VMPreparedCommand();
curCmd.setOpCode(VMCommands.VM_RET);
// TODO CurCmd->Op1.Addr=&CurCmd->Op1.Data;
// CurCmd->Op2.Addr=&CurCmd->Op2.Data;
curCmd.getOp1().setType(VMOpType.VM_OPNONE);
curCmd.getOp2().setType(VMOpType.VM_OPNONE);
// for (int i=0;i<prg.getCmdCount();i++)
// {
// VM_PreparedCommand *Cmd=&Prg->Cmd[I];
// if (Cmd->Op1.Addr==NULL)
// Cmd->Op1.Addr=&Cmd->Op1.Data;
// if (Cmd->Op2.Addr==NULL)
// Cmd->Op2.Addr=&Cmd->Op2.Data;
// }
prg.getCmd().add(curCmd);
prg.setCmdCount(prg.getCmdCount() + 1);
// #ifdef VM_OPTIMIZE
if (codeSize != 0) {
optimize(prg);
}
}
private void decodeArg(VMPreparedOperand op, boolean byteMode) {
int data = fgetbits();
if ((data & 0x8000) != 0) {
op.setType(VMOpType.VM_OPREG);
op.setData((data >>> 12) & 7);
op.setOffset(op.getData());
faddbits(4);
} else {
if ((data & 0xc000) == 0) {
op.setType(VMOpType.VM_OPINT);
if (byteMode) {
op.setData((data >>> 6) & 0xff);
faddbits(10);
} else {
faddbits(2);
op.setData(ReadData(this));
}
} else {
op.setType(VMOpType.VM_OPREGMEM);
if ((data & 0x2000) == 0) {
op.setData((data >>> 10) & 7);
op.setOffset(op.getData());
op.setBase(0);
faddbits(6);
} else {
if ((data & 0x1000) == 0) {
op.setData((data >>> 9) & 7);
op.setOffset(op.getData());
faddbits(7);
} else {
op.setData(0);
faddbits(4);
}
op.setBase(ReadData(this));
}
}
}
}
@SuppressWarnings("incomplete-switch")
private void optimize(VMPreparedProgram prg) {
List<VMPreparedCommand> commands = prg.getCmd();
for (VMPreparedCommand cmd : commands) {
switch (cmd.getOpCode()) {
case VM_MOV:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_MOVB
: VMCommands.VM_MOVD);
continue;
case VM_CMP:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_CMPB
: VMCommands.VM_CMPD);
continue;
}
if ((VMCmdFlags.VM_CmdFlags[cmd.getOpCode().getVMCommand()] & VMCmdFlags.VMCF_CHFLAGS) == 0) {
continue;
}
boolean flagsRequired = false;
for (int i = commands.indexOf(cmd) + 1; i < commands.size(); i++) {
int flags = VMCmdFlags.VM_CmdFlags[commands.get(i).getOpCode()
.getVMCommand()];
if ((flags & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC | VMCmdFlags.VMCF_USEFLAGS)) != 0) {
flagsRequired = true;
break;
}
if ((flags & VMCmdFlags.VMCF_CHFLAGS) != 0) {
break;
}
}
if (flagsRequired) {
continue;
}
switch (cmd.getOpCode()) {
case VM_ADD:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_ADDB
: VMCommands.VM_ADDD);
continue;
case VM_SUB:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_SUBB
: VMCommands.VM_SUBD);
continue;
case VM_INC:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_INCB
: VMCommands.VM_INCD);
continue;
case VM_DEC:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_DECB
: VMCommands.VM_DECD);
continue;
case VM_NEG:
cmd.setOpCode(cmd.isByteMode() ? VMCommands.VM_NEGB
: VMCommands.VM_NEGD);
continue;
}
}
}
public static int ReadData(BitInput rarVM) {
int data = rarVM.fgetbits();
switch (data & 0xc000) {
case 0:
rarVM.faddbits(6);
return ((data >>> 10) & 0xf);
case 0x4000:
if ((data & 0x3c00) == 0) {
data = 0xffffff00 | ((data >>> 2) & 0xff);
rarVM.faddbits(14);
} else {
data = (data >>> 6) & 0xff;
rarVM.faddbits(10);
}
return (data);
case 0x8000:
rarVM.faddbits(2);
data = rarVM.fgetbits();
rarVM.faddbits(16);
return (data);
default:
rarVM.faddbits(2);
data = (rarVM.fgetbits() << 16);
rarVM.faddbits(16);
data |= rarVM.fgetbits();
rarVM.faddbits(16);
return (data);
}
}
private VMStandardFilters IsStandardFilter(byte[] code, int codeSize) {
VMStandardFilterSignature[] stdList = {
new VMStandardFilterSignature(53, 0xad576887, VMStandardFilters.VMSF_E8),
new VMStandardFilterSignature(57, 0x3cd7e57e, VMStandardFilters.VMSF_E8E9),
new VMStandardFilterSignature(120, 0x3769893f, VMStandardFilters.VMSF_ITANIUM),
new VMStandardFilterSignature(29, 0x0e06077d, VMStandardFilters.VMSF_DELTA),
new VMStandardFilterSignature(149, 0x1c2c5dc8, VMStandardFilters.VMSF_RGB),
new VMStandardFilterSignature(216, 0xbc85e701, VMStandardFilters.VMSF_AUDIO),
new VMStandardFilterSignature(40, 0x46b9c560, VMStandardFilters.VMSF_UPCASE)
};
int CodeCRC = RarCRC.checkCrc(0xffffffff, code, 0, code.length) ^ 0xffffffff;
for (int i = 0; i < stdList.length; i++) {
if (stdList[i].getCRC() == CodeCRC && stdList[i].getLength() == code.length) {
return (stdList[i].getType());
}
}
return (VMStandardFilters.VMSF_NONE);
}
@SuppressWarnings("incomplete-switch")
private void ExecuteStandardFilter(VMStandardFilters filterType) {
switch (filterType) {
case VMSF_E8:
case VMSF_E8E9: {
int dataSize = R[4];
long fileOffset = R[6] & 0xFFffFFff;
if (dataSize >= VM_GLOBALMEMADDR) {
break;
}
int fileSize = 0x1000000;
byte cmpByte2 = (byte) ((filterType == VMStandardFilters.VMSF_E8E9) ? 0xe9 : 0xe8);
for (int curPos = 0; curPos < dataSize - 4;) {
byte curByte = mem[curPos++];
if (curByte == ((byte) 0xe8) || curByte == cmpByte2) {
// #ifdef PRESENT_INT32
// sint32 Offset=CurPos+FileOffset;
// sint32 Addr=GET_VALUE(false,Data);
// if (Addr<0)
// {
// if (Addr+Offset>=0)
// SET_VALUE(false,Data,Addr+FileSize);
// }
// else
// if (Addr<FileSize)
// SET_VALUE(false,Data,Addr-Offset);
// #else
long offset = curPos + fileOffset;
long Addr = getValue(false, mem, curPos);
if ((Addr & 0x80000000) != 0) {
if (((Addr + offset) & 0x80000000) == 0) {
setValue(false, mem, curPos, (int) Addr + fileSize);
}
} else {
if (((Addr - fileSize) & 0x80000000) != 0) {
setValue(false, mem, curPos, (int) (Addr - offset));
}
}
// #endif
curPos += 4;
}
}
break;
}
case VMSF_ITANIUM: {
int dataSize = R[4];
long fileOffset = R[6] & 0xFFffFFff;
if (dataSize >= VM_GLOBALMEMADDR) {
break;
}
int curPos = 0;
final byte[] Masks = {4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0};
fileOffset >>>= 4;
while (curPos < dataSize - 21) {
int Byte = (mem[curPos] & 0x1f) - 0x10;
if (Byte >= 0) {
byte cmdMask = Masks[Byte];
if (cmdMask != 0) {
for (int i = 0; i <= 2; i++) {
if ((cmdMask & (1 << i)) != 0) {
int startPos = i * 41 + 5;
int opType = filterItanium_GetBits(curPos, startPos + 37, 4);
if (opType == 5) {
int offset = filterItanium_GetBits(curPos, startPos + 13, 20);
filterItanium_SetBits(curPos, (int) (offset - fileOffset) & 0xfffff, startPos + 13, 20);
}
}
}
}
}
curPos += 16;
fileOffset++;
}
}
break;
case VMSF_DELTA: {
int dataSize = R[4] & 0xFFffFFff;
int channels = R[0] & 0xFFffFFff;
int srcPos = 0;
int border = (dataSize * 2) & 0xFFffFFff;
setValue(false, mem, VM_GLOBALMEMADDR + 0x20, (int) dataSize);
if (dataSize >= VM_GLOBALMEMADDR / 2) {
break;
}
// bytes from same channels are grouped to continual data blocks,
// so we need to place them back to their interleaving positions
for (int curChannel = 0; curChannel < channels; curChannel++) {
byte PrevByte = 0;
for (int destPos = dataSize + curChannel; destPos < border; destPos += channels) {
mem[destPos] = (PrevByte -= mem[srcPos++]);
}
}
}
break;
case VMSF_RGB: {
// byte *SrcData=Mem,*DestData=SrcData+DataSize;
int dataSize = R[4], width = R[0] - 3, posR = R[1];
int channels = 3;
int srcPos = 0;
int destDataPos = dataSize;
setValue(false, mem, VM_GLOBALMEMADDR + 0x20, dataSize);
if (dataSize >= VM_GLOBALMEMADDR / 2 || posR < 0) {
break;
}
for (int curChannel = 0; curChannel < channels; curChannel++) {
long prevByte = 0;
for (int i = curChannel; i < dataSize; i += channels) {
long predicted;
int upperPos = i - width;
if (upperPos >= 3) {
int upperDataPos = destDataPos + upperPos;
int upperByte = mem[(int) upperDataPos] & 0xff;
int upperLeftByte = mem[upperDataPos - 3] & 0xff;
predicted = prevByte + upperByte - upperLeftByte;
int pa = Math.abs((int) (predicted - prevByte));
int pb = Math.abs((int) (predicted - upperByte));
int pc = Math.abs((int) (predicted - upperLeftByte));
if (pa <= pb && pa <= pc) {
predicted = prevByte;
} else {
if (pb <= pc) {
predicted = upperByte;
} else {
predicted = upperLeftByte;
}
}
} else {
predicted = prevByte;
}
prevByte = (predicted - mem[srcPos++] & 0xff) & 0xff;
mem[destDataPos + i] = (byte) (prevByte & 0xff);
}
}
for (int i = posR, border = dataSize - 2; i < border; i += 3) {
byte G = mem[destDataPos + i + 1];
mem[destDataPos + i] += G;
mem[destDataPos + i + 2] += G;
}
}
break;
case VMSF_AUDIO: {
int dataSize = R[4], channels = R[0];
int srcPos = 0;
int destDataPos = dataSize;
//byte *SrcData=Mem,*DestData=SrcData+DataSize;
setValue(false, mem, VM_GLOBALMEMADDR + 0x20, dataSize);
if (dataSize >= VM_GLOBALMEMADDR / 2) {
break;
}
for (int curChannel = 0; curChannel < channels; curChannel++) {
long prevByte = 0;
long prevDelta = 0;
long[] Dif = new long[7];
int D1 = 0, D2 = 0, D3;
int K1 = 0, K2 = 0, K3 = 0;
for (int i = curChannel, byteCount = 0; i < dataSize; i += channels, byteCount++) {
D3 = D2;
D2 = (int) prevDelta - D1;
D1 = (int) prevDelta;
long predicted = 8 * prevByte + K1 * D1 + K2 * D2 + K3 * D3;
predicted = (predicted >>> 3) & 0xff;
long curByte = mem[srcPos++] & 0xff;
predicted = (predicted - curByte) & UINT_MASK;
mem[destDataPos + i] = (byte) predicted;
prevDelta = (byte) (predicted - prevByte);
prevByte = predicted;
int D = ((byte) curByte) << 3;
Dif[0] += Math.abs(D);
Dif[1] += Math.abs(D - D1);
Dif[2] += Math.abs(D + D1);
Dif[3] += Math.abs(D - D2);
Dif[4] += Math.abs(D + D2);
Dif[5] += Math.abs(D - D3);
Dif[6] += Math.abs(D + D3);
if ((byteCount & 0x1f) == 0) {
long minDif = Dif[0], numMinDif = 0;
Dif[0] = 0;
for (int j = 1; j < Dif.length; j++) {
if (Dif[j] < minDif) {
minDif = Dif[j];
numMinDif = j;
}
Dif[j] = 0;
}
switch ((int) numMinDif) {
case 1:
if (K1 >= -16) {
K1--;
}
break;
case 2:
if (K1 < 16) {
K1++;
}
break;
case 3:
if (K2 >= -16) {
K2--;
}
break;
case 4:
if (K2 < 16) {
K2++;
}
break;
case 5:
if (K3 >= -16) {
K3--;
}
break;
case 6:
if (K3 < 16) {
K3++;
}
break;
}
}
}
}
}
break;
case VMSF_UPCASE: {
int dataSize = R[4], srcPos = 0, destPos = dataSize;
if (dataSize >= VM_GLOBALMEMADDR / 2) {
break;
}
while (srcPos < dataSize) {
byte curByte = mem[srcPos++];
if (curByte == 2 && (curByte = mem[srcPos++]) != 2) {
curByte -= 32;
}
mem[destPos++] = curByte;
}
setValue(false, mem, VM_GLOBALMEMADDR + 0x1c, destPos - dataSize);
setValue(false, mem, VM_GLOBALMEMADDR + 0x20, dataSize);
}
break;
}
}
private void filterItanium_SetBits(int curPos, int bitField, int bitPos, int bitCount) {
int inAddr = bitPos / 8;
int inBit = bitPos & 7;
int andMask = 0xffffffff >>> (32 - bitCount);
andMask = ~(andMask << inBit);
bitField <<= inBit;
for (int i = 0; i < 4; i++) {
mem[curPos + inAddr + i] &= andMask;
mem[curPos + inAddr + i] |= bitField;
andMask = (andMask >>> 8) | 0xff000000;
bitField >>>= 8;
}
}
private int filterItanium_GetBits(int curPos, int bitPos, int bitCount) {
int inAddr = bitPos / 8;
int inBit = bitPos & 7;
int bitField = (int) (mem[curPos + inAddr++] & 0xff);
bitField |= (int) ((mem[curPos + inAddr++] & 0xff) << 8);
bitField |= (int) ((mem[curPos + inAddr++] & 0xff) << 16);
bitField |= (int) ((mem[curPos + inAddr] & 0xff) << 24);
bitField >>>= inBit;
return (bitField & (0xffffffff >>> (32 - bitCount)));
}
public void setMemory(int pos, byte[] data, int offset, int dataSize) {
if (pos < VM_MEMSIZE) { //&& data!=Mem+Pos)
//memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos));
for (int i = 0; i < Math.min(data.length - offset, dataSize); i++) {
if ((VM_MEMSIZE - pos) < i) {
break;
}
mem[pos + i] = data[offset + i];
}
}
}
}