using System;
using System.Collections.Generic;

namespace SharpCompress.Compressor.Rar.VM
{
    internal class RarVM : BitInput
    {
        //private void  InitBlock()
        //{
        //    Mem.set_Renamed(offset + 0, Byte.valueOf((sbyte) (value_Renamed & 0xff)));
        //    Mem.set_Renamed(offset + 1, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 8)) & 0xff)));
        //    Mem.set_Renamed(offset + 2, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 16)) & 0xff)));
        //    Mem.set_Renamed(offset + 3, Byte.valueOf((sbyte) ((Utility.URShift(value_Renamed, 24)) & 0xff)));


        //}
        internal byte[] Mem
        {
            get;
            private set;
        }

        public const int VM_MEMSIZE = 0x40000;

        //UPGRADE_NOTE: Final was removed from the declaration of 'VM_MEMMASK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
        public static readonly int VM_MEMMASK = (VM_MEMSIZE - 1);

        public const int VM_GLOBALMEMADDR = 0x3C000;

        public const int VM_GLOBALMEMSIZE = 0x2000;

        public const int VM_FIXEDGLOBALSIZE = 64;

        private const int regCount = 8;

        private const long UINT_MASK = 0xffffFFFF; //((long)2*(long)Integer.MAX_VALUE);

        private int[] R = new int[regCount];

        private VMFlags flags;

        private int maxOpCount = 25000000;

        private int codeSize;

        private int IP;

        internal RarVM()
        {
            //InitBlock();
            Mem = null;
        }

        internal void init()
        {
            if (Mem == null)
            {
                Mem = new byte[VM_MEMSIZE + 4];
            }
        }

        private bool IsVMMem(byte[] mem)
        {
            return this.Mem == mem;
        }

        private int GetValue(bool byteMode, byte[] mem, int offset)
        {
            if (byteMode)
            {
                if (IsVMMem(mem))
                {
                    return (mem[offset]);
                }
                else
                {
                    return (mem[offset] & 0xff);
                }
            }
            else
            {
                if (IsVMMem(mem))
                {
                    return Utility.readIntLittleEndian(mem, offset);
                }
                else
                {
                    return Utility.readIntBigEndian(mem, offset);
                }
            }
        }

        private void SetValue(bool 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))
                {
                    Utility.WriteLittleEndian(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
                {
                    Utility.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)
        }

        internal void SetLowEndianValue(List<byte> mem, int offset, int value)
        {
            mem[offset + 0] = (byte)(value & 0xff);
            mem[offset + 1] = (byte)(Utility.URShift(value, 8) & 0xff);
            mem[offset + 2] = (byte)(Utility.URShift(value, 16) & 0xff);
            mem[offset + 3] = (byte)(Utility.URShift(value, 24) & 0xff);
        }

        private int GetOperand(VMPreparedOperand cmdOp)
        {
            int ret = 0;
            if (cmdOp.Type == VMOpType.VM_OPREGMEM)
            {
                int pos = (cmdOp.Offset + cmdOp.Base) & VM_MEMMASK;
                ret = Utility.readIntLittleEndian(Mem, pos);
            }
            else
            {
                int pos = cmdOp.Offset;
                ret = Utility.readIntLittleEndian(Mem, pos);
            }
            return ret;
        }

        public void execute(VMPreparedProgram prg)
        {
            for (int i = 0; i < prg.InitR.Length; i++)
            // memcpy(R,Prg->InitR,sizeof(Prg->InitR));
            {
                R[i] = prg.InitR[i];
            }

            long globalSize = (long)(Math.Min(prg.GlobalData.Count, 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.GlobalData[i];
                }
            }
            long staticSize = (long)(Math.Min(prg.StaticData.Count, 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.StaticData[i];
                }
            }
            R[7] = VM_MEMSIZE;
            flags = 0;

            //UPGRADE_NOTE: There is an untranslated Statement.  Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'"
            List<VMPreparedCommand> preparedCode = prg.AltCommands.Count != 0 ? prg
                .AltCommands
                : prg.Commands;

            if (!ExecuteCode(preparedCode, prg.CommandCount))
            {
                preparedCode[0].OpCode = 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.FilteredDataOffset = newBlockPos;
            prg.FilteredDataSize = newBlockSize;

            prg.GlobalData.Clear();

            int dataSize = Math.Min(GetValue(false, Mem, VM_GLOBALMEMADDR + 0x30), VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE);
            if (dataSize != 0)
            {
                //prg.GlobalData.Clear();
                // ->GlobalData.Add(dataSize+VM_FIXEDGLOBALSIZE);
                prg.GlobalData.SetSize(dataSize + VM_FIXEDGLOBALSIZE);
                for (int i = 0; i < dataSize + VM_FIXEDGLOBALSIZE; i++)
                // memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE);
                {
                    prg.GlobalData[i] = Mem[VM_GLOBALMEMADDR + i];
                }
            }
        }

        private bool setIP(int ip)
        {
            if ((ip) >= codeSize)
            {
                return (true);
            }

            if (--maxOpCount <= 0)
            {
                return (false);
            }

            IP = ip;
            return true;
        }

        private bool ExecuteCode(List<VMPreparedCommand> preparedCode,
        int cmdCount)
        {
            maxOpCount = 25000000;
            this.codeSize = cmdCount;
            this.IP = 0;

            while (true)
            {
                VMPreparedCommand cmd = preparedCode[IP];
                int op1 = GetOperand(cmd.Op1);
                int op2 = GetOperand(cmd.Op2);
                switch (cmd.OpCode)
                {

                    case VMCommands.VM_MOV:
                        SetValue(cmd.IsByteMode, Mem, op1, GetValue(cmd.IsByteMode, Mem, op2)); // SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2));
                        break;

                    case VMCommands.VM_MOVB:
                        SetValue(true, Mem, op1, GetValue(true, Mem, op2));
                        break;

                    case VMCommands.VM_MOVD:
                        SetValue(false, Mem, op1, GetValue(false, Mem, op2));
                        break;


                    case VMCommands.VM_CMP:
                        {
                            VMFlags value1 = (VMFlags)GetValue(cmd.IsByteMode, Mem, op1);
                            VMFlags result = value1 - GetValue(cmd.IsByteMode, Mem, op2);

                            if (result == 0)
                            {
                                flags = VMFlags.VM_FZ;
                            }
                            else
                            {
                                flags = (VMFlags)((result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS));
                            }
                        }
                        break;


                    case VMCommands.VM_CMPB:
                        {
                            VMFlags value1 = (VMFlags)GetValue(true, Mem, op1);
                            VMFlags result = value1 - GetValue(true, Mem, op2);
                            if (result == 0)
                            {
                                flags = VMFlags.VM_FZ;
                            }
                            else
                            {
                                flags = (VMFlags)((result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS));
                            }
                        }
                        break;

                    case VMCommands.VM_CMPD:
                        {
                            VMFlags value1 = (VMFlags)GetValue(false, Mem, op1);
                            VMFlags result = value1 - GetValue(false, Mem, op2);
                            if (result == 0)
                            {
                                flags = VMFlags.VM_FZ;
                            }
                            else
                            {
                                flags = (VMFlags)((result > value1) ? 1 : 0 | (int)(result & VMFlags.VM_FS));
                            }
                        }
                        break;


                    case VMCommands.VM_ADD:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int result = (int)((((long)value1 + (long)GetValue(cmd.IsByteMode, Mem, op2))) & unchecked((int)0xffffffff));
                            if (cmd.IsByteMode)
                            {
                                result &= 0xff;
                                flags = (VMFlags)((result < value1) ? 1 : 0 | (result == 0 ? (int)VMFlags.VM_FZ : (((result & 0x80) != 0) ? (int)VMFlags.VM_FS : 0)));
                                // Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ?
                                // VM_FS:0));
                            }
                            else
                                flags = (VMFlags)((result < value1) ? 1 : 0 | (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;


                    case VMCommands.VM_ADDB:
                        SetValue(true, Mem, op1, (int)((long)GetValue(true, Mem, op1) & 0xFFffFFff + (long)GetValue(true, Mem, op2) & unchecked((int)0xFFffFFff)));
                        break;

                    case VMCommands.VM_ADDD:
                        SetValue(false, Mem, op1, (int)((long)GetValue(false, Mem, op1) & 0xFFffFFff + (long)GetValue(false, Mem, op2) & unchecked((int)0xFFffFFff)));
                        break;


                    case VMCommands.VM_SUB:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int result = (int)((long)value1 & 0xffFFffFF - (long)GetValue(cmd.IsByteMode, Mem, op2) & unchecked((int)0xFFffFFff));
                            flags = (VMFlags)((result == 0) ? (int)VMFlags.VM_FZ : ((result > value1) ? 1 : 0 | (result & (int)VMFlags.VM_FS)));
                            SetValue(cmd.IsByteMode, Mem, op1, result); // (Cmd->ByteMode,Op1,Result);
                        }
                        break;


                    case VMCommands.VM_SUBB:
                        SetValue(true, Mem, op1, (int)((long)GetValue(true, Mem, op1) & 0xFFffFFff - (long)GetValue(true, Mem, op2) & unchecked((int)0xFFffFFff)));
                        break;

                    case VMCommands.VM_SUBD:
                        SetValue(false, Mem, op1, (int)((long)GetValue(false, Mem, op1) & 0xFFffFFff - (long)GetValue(false, Mem, op2) & unchecked((int)0xFFffFFff)));
                        break;


                    case VMCommands.VM_JZ:
                        if ((flags & VMFlags.VM_FZ) != 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JNZ:
                        if ((flags & VMFlags.VM_FZ) == 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_INC:
                        {
                            int result = (int)((long)GetValue(cmd.IsByteMode, Mem, op1) & 0xFFffFFffL + 1L);
                            if (cmd.IsByteMode)
                            {
                                result &= 0xff;
                            }

                            SetValue(cmd.IsByteMode, Mem, op1, result);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                        }
                        break;


                    case VMCommands.VM_INCB:
                        SetValue(true, Mem, op1, (int)((long)GetValue(true, Mem, op1) & 0xFFffFFffL + 1L));
                        break;

                    case VMCommands.VM_INCD:
                        SetValue(false, Mem, op1, (int)((long)GetValue(false, Mem, op1) & 0xFFffFFffL + 1L));
                        break;


                    case VMCommands.VM_DEC:
                        {
                            int result = (int)((long)GetValue(cmd.IsByteMode, Mem, op1) & 0xFFffFFff - 1);
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                        }
                        break;


                    case VMCommands.VM_DECB:
                        SetValue(true, Mem, op1, (int)((long)GetValue(true, Mem, op1) & 0xFFffFFff - 1));
                        break;

                    case VMCommands.VM_DECD:
                        SetValue(false, Mem, op1, (int)((long)GetValue(false, Mem, op1) & 0xFFffFFff - 1));
                        break;


                    case VMCommands.VM_JMP:
                        setIP(GetValue(false, Mem, op1));
                        continue;

                    case VMCommands.VM_XOR:
                        {
                            int result = GetValue(cmd.IsByteMode, Mem, op1) ^ GetValue(cmd.IsByteMode, Mem, op2);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_AND:
                        {
                            int result = GetValue(cmd.IsByteMode, Mem, op1) & GetValue(cmd.IsByteMode, Mem, op2);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_OR:
                        {
                            int result = GetValue(cmd.IsByteMode, Mem, op1) | GetValue(cmd.IsByteMode, Mem, op2);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_TEST:
                        {
                            int result = GetValue(cmd.IsByteMode, Mem, op1) & GetValue(cmd.IsByteMode, Mem, op2);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : result & (int)VMFlags.VM_FS);
                        }
                        break;

                    case VMCommands.VM_JS:
                        if ((flags & VMFlags.VM_FS) != 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JNS:
                        if ((flags & VMFlags.VM_FS) == 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JB:
                        if ((flags & VMFlags.VM_FC) != 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JBE:
                        if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) != 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JA:
                        if ((flags & (VMFlags.VM_FC | VMFlags.VM_FZ)) == 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_JAE:
                        if ((flags & VMFlags.VM_FC) == 0)
                        {
                            setIP(GetValue(false, Mem, op1));
                            continue;
                        }
                        break;

                    case VMCommands.VM_PUSH:
                        R[7] -= 4;
                        SetValue(false, Mem, R[7] & VM_MEMMASK, GetValue(false, Mem, op1));
                        break;

                    case VMCommands.VM_POP:
                        SetValue(false, Mem, op1, GetValue(false, Mem, R[7] & VM_MEMMASK));
                        R[7] += 4;
                        break;

                    case VMCommands.VM_CALL:
                        R[7] -= 4;
                        SetValue(false, Mem, R[7] & VM_MEMMASK, IP + 1);
                        setIP(GetValue(false, Mem, op1));
                        continue;

                    case VMCommands.VM_NOT:
                        SetValue(cmd.IsByteMode, Mem, op1, ~GetValue(cmd.IsByteMode, Mem, op1));
                        break;

                    case VMCommands.VM_SHL:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int value2 = GetValue(cmd.IsByteMode, Mem, op2);
                            int result = value1 << value2;
                            flags = (VMFlags)((result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) | (((value1 << (value2 - 1)) & unchecked((int)0x80000000)) != 0 ? (int)VMFlags.VM_FC : 0));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_SHR:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int value2 = GetValue(cmd.IsByteMode, Mem, op2);
                            int result = Utility.URShift(value1, value2);
                            flags = (VMFlags)((result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) | ((Utility.URShift(value1, (value2 - 1))) & (int)VMFlags.VM_FC));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_SAR:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int value2 = GetValue(cmd.IsByteMode, Mem, op2);
                            int result = ((int)value1) >> value2;
                            flags = (VMFlags)((result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)) | ((value1 >> (value2 - 1)) & (int)VMFlags.VM_FC));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_NEG:
                        {
                            int result = -GetValue(cmd.IsByteMode, Mem, op1);
                            flags = (VMFlags)(result == 0 ? (int)VMFlags.VM_FZ : (int)VMFlags.VM_FC | (result & (int)VMFlags.VM_FS));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;


                    case VMCommands.VM_NEGB:
                        SetValue(true, Mem, op1, -GetValue(true, Mem, op1));
                        break;

                    case VMCommands.VM_NEGD:
                        SetValue(false, Mem, op1, -GetValue(false, Mem, op1));
                        break;

                    case VMCommands.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 VMCommands.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 VMCommands.VM_PUSHF:
                        R[7] -= 4;
                        SetValue(false, Mem, R[7] & VM_MEMMASK, (int)flags);
                        break;

                    case VMCommands.VM_POPF:
                        flags = (VMFlags)GetValue(false, Mem, R[7] & VM_MEMMASK);
                        R[7] += 4;
                        break;

                    case VMCommands.VM_MOVZX:
                        SetValue(false, Mem, op1, GetValue(true, Mem, op2));
                        break;

                    case VMCommands.VM_MOVSX:
                        SetValue(false, Mem, op1, (byte)GetValue(true, Mem, op2));
                        break;

                    case VMCommands.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 VMCommands.VM_MUL:
                        {
                            int result = (int)(((long)GetValue(cmd.IsByteMode, Mem, op1) & 0xFFffFFff * (long)GetValue(cmd.IsByteMode, Mem, op2) & unchecked((int)0xFFffFFff)) & unchecked((int)0xFFffFFff));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.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 VMCommands.VM_ADC:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int FC = (int)(flags & VMFlags.VM_FC);
                            int result = (int)((long)value1 & 0xFFffFFff + (long)GetValue(cmd.IsByteMode, Mem, op2) & 0xFFffFFff + (long)FC & unchecked((int)0xFFffFFff));
                            if (cmd.IsByteMode)
                            {
                                result &= 0xff;
                            }

                            flags = (VMFlags)((result < value1 || result == value1 && FC != 0) ? 1 : 0 | (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;

                    case VMCommands.VM_SBB:
                        {
                            int value1 = GetValue(cmd.IsByteMode, Mem, op1);
                            int FC = (int)(flags & VMFlags.VM_FC);
                            int result = (int)((long)value1 & 0xFFffFFff - (long)GetValue(cmd.IsByteMode, Mem, op2) & 0xFFffFFff - (long)FC & unchecked((int)0xFFffFFff));
                            if (cmd.IsByteMode)
                            {
                                result &= 0xff;
                            }
                            flags = (VMFlags)((result > value1 || result == value1 && FC != 0) ? 1 : 0 | (result == 0 ? (int)VMFlags.VM_FZ : (result & (int)VMFlags.VM_FS)));
                            SetValue(cmd.IsByteMode, Mem, op1, result);
                        }
                        break;


                    case VMCommands.VM_RET:
                        if (R[7] >= VM_MEMSIZE)
                        {
                            return (true);
                        }
                        setIP(GetValue(false, Mem, R[7] & VM_MEMMASK));
                        R[7] += 4;
                        continue;


                    case VMCommands.VM_STANDARD:
                        ExecuteStandardFilter((VMStandardFilters)(cmd.Op1.Data));
                        break;

                    case VMCommands.VM_PRINT:
                        break;
                }
                IP++;
                --maxOpCount;
            }
        }

        public void prepare(byte[] code, int codeSize, VMPreparedProgram prg)
        {
            InitBitInput();
            int cpLength = System.Math.Min(MAX_SIZE, codeSize);
            // memcpy(inBuf,Code,Min(CodeSize,BitInput::MAX_SIZE));
#if !PORTABLE
            Buffer.BlockCopy(code, 0, InBuf, 0, cpLength);
#else
            Array.Copy(code, 0, InBuf, 0, cpLength);
#endif
            byte xorSum = 0;
            for (int i = 1; i < codeSize; i++)
            {
                xorSum ^= code[i];
            }

            AddBits(8);

            prg.CommandCount = 0;
            if (xorSum == code[0])
            {
                VMStandardFilters filterType = IsStandardFilter(code, codeSize);
                if (filterType != VMStandardFilters.VMSF_NONE)
                {

                    VMPreparedCommand curCmd = new VMPreparedCommand();
                    curCmd.OpCode = VMCommands.VM_STANDARD;
                    curCmd.Op1.Data = (int)filterType;
                    curCmd.Op1.Type = VMOpType.VM_OPNONE;
                    curCmd.Op2.Type = VMOpType.VM_OPNONE;
                    codeSize = 0;
                    prg.Commands.Add(curCmd);
                    prg.CommandCount = prg.CommandCount + 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 = GetBits();
                AddBits(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) & 0xffFFffFFL + 1L);
                    for (int i = 0; inAddr < codeSize && i < dataSize; i++)
                    {
                        prg.StaticData.Add((byte)(GetBits() >> 8));
                        AddBits(8);
                    }
                }

                while (inAddr < codeSize)
                {
                    VMPreparedCommand curCmd = new VMPreparedCommand();
                    int data = GetBits();
                    if ((data & 0x8000) == 0)
                    {
                        curCmd.OpCode = (VMCommands)((data >> 12));
                        AddBits(4);
                    }
                    else
                    {
                        curCmd.OpCode = (VMCommands)((data >> 10) - 24);
                        AddBits(6);
                    }
                    if ((VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_BYTEMODE) != 0)
                    {
                        curCmd.IsByteMode = (GetBits() >> 15) == 1 ? true : false;
                        AddBits(1);
                    }
                    else
                    {
                        curCmd.IsByteMode = false;
                    }
                    curCmd.Op1.Type = VMOpType.VM_OPNONE;
                    curCmd.Op2.Type = VMOpType.VM_OPNONE;

                    int opNum = (VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & VMCmdFlags.VMCF_OPMASK);
                    // TODO >>> CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; <<<???
                    if (opNum > 0)
                    {
                        decodeArg(curCmd.Op1, curCmd.IsByteMode);
                        if (opNum == 2)
                            decodeArg(curCmd.Op2, curCmd.IsByteMode);
                        else
                        {
                            if (curCmd.Op1.Type == VMOpType.VM_OPINT && (VMCmdFlags.VM_CmdFlags[(int)curCmd.OpCode] & (VMCmdFlags.VMCF_JUMP | VMCmdFlags.VMCF_PROC)) != 0)
                            {
                                int distance = curCmd.Op1.Data;
                                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.CommandCount;
                                }
                                curCmd.Op1.Data = distance;
                            }
                        }
                    }
                    prg.CommandCount = (prg.CommandCount + 1);
                    prg.Commands.Add(curCmd);
                }
            }
            VMPreparedCommand curCmd2 = new VMPreparedCommand();
            curCmd2.OpCode = VMCommands.VM_RET;
            // TODO CurCmd->Op1.Addr=&CurCmd->Op1.Data;
            // CurCmd->Op2.Addr=&CurCmd->Op2.Data;
            curCmd2.Op1.Type = VMOpType.VM_OPNONE;
            curCmd2.Op2.Type = VMOpType.VM_OPNONE;

            // for (int i=0;i<prg.CmdCount;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.Commands.Add(curCmd2);
            prg.CommandCount = prg.CommandCount + 1;
            // #ifdef VM_OPTIMIZE
            if (codeSize != 0)
            {
                optimize(prg);
            }
        }

        private void decodeArg(VMPreparedOperand op, bool byteMode)
        {
            int data = GetBits();
            if ((data & 0x8000) != 0)
            {
                op.Type = VMOpType.VM_OPREG;
                op.Data = (data >> 12) & 7;
                op.Offset = op.Data;
                AddBits(4);
            }
            else
            {
                if ((data & 0xc000) == 0)
                {
                    op.Type = VMOpType.VM_OPINT;
                    if (byteMode)
                    {
                        op.Data = (data >> 6) & 0xff;
                        AddBits(10);
                    }
                    else
                    {
                        AddBits(2);
                        op.Data = ReadData(this);
                    }
                }
                else
                {
                    op.Type = VMOpType.VM_OPREGMEM;
                    if ((data & 0x2000) == 0)
                    {
                        op.Data = (data >> 10) & 7;
                        op.Offset = op.Data;
                        op.Base = 0;
                        AddBits(6);
                    }
                    else
                    {
                        if ((data & 0x1000) == 0)
                        {
                            op.Data = (data >> 9) & 7;
                            op.Offset = op.Data;
                            AddBits(7);
                        }
                        else
                        {
                            op.Data = 0;
                            AddBits(4);
                        }
                        op.Base = ReadData(this);
                    }
                }
            }
        }

        private void optimize(VMPreparedProgram prg)
        {
            //UPGRADE_NOTE: There is an untranslated Statement.  Please refer to original code. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1153'"

            List<VMPreparedCommand> commands = prg.Commands;
            //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'"
            foreach (VMPreparedCommand cmd in commands)
            {
                switch (cmd.OpCode)
                {

                    case VMCommands.VM_MOV:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_MOVB : VMCommands.VM_MOVD;
                        continue;

                    case VMCommands.VM_CMP:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_CMPB : VMCommands.VM_CMPD;
                        continue;
                }
                if ((VMCmdFlags.VM_CmdFlags[(int)cmd.OpCode] & VMCmdFlags.VMCF_CHFLAGS) == 0)
                {
                    continue;
                }
                bool flagsRequired = false;

                for (int i = commands.IndexOf(cmd) + 1; i < commands.Count; i++)
                {
                    int flags = VMCmdFlags.VM_CmdFlags[(int)commands[i].OpCode];
                    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.OpCode)
                {

                    case VMCommands.VM_ADD:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_ADDB : VMCommands.VM_ADDD;
                        continue;

                    case VMCommands.VM_SUB:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_SUBB : VMCommands.VM_SUBD;
                        continue;

                    case VMCommands.VM_INC:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_INCB : VMCommands.VM_INCD;
                        continue;

                    case VMCommands.VM_DEC:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_DECB : VMCommands.VM_DECD;
                        continue;

                    case VMCommands.VM_NEG:
                        cmd.OpCode = cmd.IsByteMode ? VMCommands.VM_NEGB : VMCommands.VM_NEGD;
                        continue;
                }
            }
        }

        internal static int ReadData(BitInput rarVM)
        {
            int data = rarVM.GetBits();
            switch (data & 0xc000)
            {

                case 0:
                    rarVM.AddBits(6);
                    return ((data >> 10) & 0xf);

                case 0x4000:
                    if ((data & 0x3c00) == 0)
                    {
                        data = unchecked((int)0xffffff00) | ((data >> 2) & 0xff);
                        rarVM.AddBits(14);
                    }
                    else
                    {
                        data = (data >> 6) & 0xff;
                        rarVM.AddBits(10);
                    }
                    return (data);

                case 0x8000:
                    rarVM.AddBits(2);
                    data = rarVM.GetBits();
                    rarVM.AddBits(16);
                    return (data);

                default:
                    rarVM.AddBits(2);
                    data = (rarVM.GetBits() << 16);
                    rarVM.AddBits(16);
                    data |= rarVM.GetBits();
                    rarVM.AddBits(16);
                    return (data);

            }
        }

        private VMStandardFilters IsStandardFilter(byte[] code, int codeSize)
        {
            VMStandardFilterSignature[] stdList = new VMStandardFilterSignature[]{
                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)};
            uint CodeCRC = RarCRC.CheckCrc(0xffffffff, code, 0, code.Length) ^ 0xffffffff;
            for (int i = 0; i < stdList.Length; i++)
            {
                if (stdList[i].CRC == CodeCRC && stdList[i].Length == code.Length)
                {
                    return (stdList[i].Type);
                }
            }
            return (VMStandardFilters.VMSF_NONE);
        }

        private void ExecuteStandardFilter(VMStandardFilters filterType)
        {
            switch (filterType)
            {

                case VMStandardFilters.VMSF_E8:
                case VMStandardFilters.VMSF_E8E9:
                    {
                        int dataSize = R[4];
                        long fileOffset = R[6] & unchecked((int)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 == 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 & unchecked((int)0x80000000)) != 0)
                                {
                                    if (((Addr + offset) & unchecked((int)0x80000000)) == 0)
                                        SetValue(false, Mem, curPos, (int)Addr + fileSize);
                                }
                                else
                                {
                                    if (((Addr - fileSize) & unchecked((int)0x80000000)) != 0)
                                    {
                                        SetValue(false, Mem, curPos, (int)(Addr - offset));
                                    }
                                }
                                //		#endif
                                curPos += 4;
                            }
                        }
                    }
                    break;

                case VMStandardFilters.VMSF_ITANIUM:
                    {

                        int dataSize = R[4];
                        long fileOffset = R[6] & unchecked((int)0xFFffFFff);

                        if (dataSize >= VM_GLOBALMEMADDR)
                        {
                            break;
                        }
                        int curPos = 0;
                        //UPGRADE_NOTE: Final was removed from the declaration of 'Masks '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
                        byte[] Masks = new byte[] { 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0 };
                        fileOffset = Utility.URShift(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 VMStandardFilters.VMSF_DELTA:
                    {
                        int dataSize = R[4] & unchecked((int)0xFFffFFff);
                        int channels = R[0] & unchecked((int)0xFFffFFff);
                        int srcPos = 0;
                        int border = (dataSize * 2) & unchecked((int)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 = (byte)(PrevByte - Mem[srcPos++]));
                            }
                        }
                    }
                    break;

                case VMStandardFilters.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 = System.Math.Abs((int)(predicted - prevByte));
                                    int pb = System.Math.Abs((int)(predicted - upperByte));
                                    int pc = System.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] = (byte)(Mem[destDataPos + i] + G);
                            Mem[destDataPos + i + 2] = (byte)(Mem[destDataPos + i + 2] + G);
                        }
                    }
                    break;

                case VMStandardFilters.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 = Utility.URShift(predicted, 3) & 0xff;

                                long curByte = (long)(Mem[srcPos++]);

                                predicted -= curByte;
                                Mem[destDataPos + i] = (byte)predicted;
                                prevDelta = (byte)(predicted - prevByte);
                                //fix java byte
                                if (prevDelta >= 128)
                                {
                                    prevDelta = 0 - (256 - prevDelta);
                                }
                                prevByte = predicted;
                                //fix java byte
                                if (curByte >= 128)
                                {
                                    curByte = 0 - (256 - curByte);
                                }
                                int D = ((int)curByte) << 3;

                                Dif[0] += System.Math.Abs(D);
                                Dif[1] += System.Math.Abs(D - D1);
                                Dif[2] += System.Math.Abs(D + D1);
                                Dif[3] += System.Math.Abs(D - D2);
                                Dif[4] += System.Math.Abs(D + D2);
                                Dif[5] += System.Math.Abs(D - D3);
                                Dif[6] += System.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 VMStandardFilters.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 = (byte)(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 = Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount));
            andMask = ~(andMask << inBit);

            bitField <<= inBit;

            for (int i = 0; i < 4; i++)
            {
                Mem[curPos + inAddr + i] &= (byte)(andMask);
                Mem[curPos + inAddr + i] |= (byte)(bitField);
                andMask = (Utility.URShift(andMask, 8)) | unchecked((int)0xff000000);
                bitField = Utility.URShift(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 = Utility.URShift(bitField, inBit);
            return (bitField & (Utility.URShift(unchecked((int)0xffffffff), (32 - bitCount))));
        }


        public virtual 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 < System.Math.Min(data.Length - offset, dataSize); i++)
                {
                    if ((VM_MEMSIZE - pos) < i)
                    {
                        break;
                    }
                    Mem[pos + i] = data[offset + i];
                }
            }
        }
    }

    //
}