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 "&"
 * "<":  "&#060;" or "&lt;"
 * ">":  "&#062;" or "&gt;"
 * "@":  "&#064;"
 */
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];
            }
        }
    }


}