summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp')
-rw-r--r--src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp1091
1 files changed, 1091 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp b/src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp
new file mode 100644
index 000000000..74051dd79
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Compress/Rar3Vm.cpp
@@ -0,0 +1,1091 @@
+// Rar3Vm.cpp
+// According to unRAR license, this code may not be used to develop
+// a program that creates RAR archives
+
+/*
+Note:
+ Due to performance considerations Rar VM may set Flags C incorrectly
+ for some operands (SHL x, 0, ... ).
+ Check implementation of concrete VM command
+ to see if it sets flags right.
+*/
+
+#include "StdAfx.h"
+
+#include "../../../C/7zCrc.h"
+#include "../../../C/Alloc.h"
+
+#include "Rar3Vm.h"
+
+namespace NCompress {
+namespace NRar3 {
+
+UInt32 CMemBitDecoder::ReadBits(int numBits)
+{
+ UInt32 res = 0;
+ for (;;)
+ {
+ Byte b = _bitPos < _bitSize ? _data[_bitPos >> 3] : 0;
+ int avail = (int)(8 - (_bitPos & 7));
+ if (numBits <= avail)
+ {
+ _bitPos += numBits;
+ return res | (b >> (avail - numBits)) & ((1 << numBits) - 1);
+ }
+ numBits -= avail;
+ res |= (UInt32)(b & ((1 << avail) - 1)) << numBits;
+ _bitPos += avail;
+ }
+}
+
+UInt32 CMemBitDecoder::ReadBit() { return ReadBits(1); }
+
+namespace NVm {
+
+static const UInt32 kStackRegIndex = kNumRegs - 1;
+
+static const UInt32 FLAG_C = 1;
+static const UInt32 FLAG_Z = 2;
+static const UInt32 FLAG_S = 0x80000000;
+
+static const Byte CF_OP0 = 0;
+static const Byte CF_OP1 = 1;
+static const Byte CF_OP2 = 2;
+static const Byte CF_OPMASK = 3;
+static const Byte CF_BYTEMODE = 4;
+static const Byte CF_JUMP = 8;
+static const Byte CF_PROC = 16;
+static const Byte CF_USEFLAGS = 32;
+static const Byte CF_CHFLAGS = 64;
+
+static Byte kCmdFlags[]=
+{
+ /* CMD_MOV */ CF_OP2 | CF_BYTEMODE,
+ /* CMD_CMP */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_ADD */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_SUB */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_JZ */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JNZ */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_INC */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_DEC */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_JMP */ CF_OP1 | CF_JUMP,
+ /* CMD_XOR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_AND */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_OR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_TEST */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_JS */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JNS */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JB */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JBE */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JA */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_JAE */ CF_OP1 | CF_JUMP | CF_USEFLAGS,
+ /* CMD_PUSH */ CF_OP1,
+ /* CMD_POP */ CF_OP1,
+ /* CMD_CALL */ CF_OP1 | CF_PROC,
+ /* CMD_RET */ CF_OP0 | CF_PROC,
+ /* CMD_NOT */ CF_OP1 | CF_BYTEMODE,
+ /* CMD_SHL */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_SHR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_SAR */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_NEG */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,
+ /* CMD_PUSHA */ CF_OP0,
+ /* CMD_POPA */ CF_OP0,
+ /* CMD_PUSHF */ CF_OP0 | CF_USEFLAGS,
+ /* CMD_POPF */ CF_OP0 | CF_CHFLAGS,
+ /* CMD_MOVZX */ CF_OP2,
+ /* CMD_MOVSX */ CF_OP2,
+ /* CMD_XCHG */ CF_OP2 | CF_BYTEMODE,
+ /* CMD_MUL */ CF_OP2 | CF_BYTEMODE,
+ /* CMD_DIV */ CF_OP2 | CF_BYTEMODE,
+ /* CMD_ADC */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,
+ /* CMD_SBB */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,
+ /* CMD_PRINT */ CF_OP0
+};
+
+CVm::CVm(): Mem(NULL) {}
+
+bool CVm::Create()
+{
+ if (Mem == NULL)
+ Mem = (Byte *)::MyAlloc(kSpaceSize + 4);
+ return (Mem != NULL);
+}
+
+CVm::~CVm()
+{
+ ::MyFree(Mem);
+}
+
+// CVm::Execute can change CProgram object: it clears progarm if VM returns error.
+
+bool CVm::Execute(CProgram *prg, const CProgramInitState *initState,
+ CBlockRef &outBlockRef, CRecordVector<Byte> &outGlobalData)
+{
+ memcpy(R, initState->InitR, sizeof(initState->InitR));
+ R[kStackRegIndex] = kSpaceSize;
+ R[kNumRegs] = 0;
+ Flags = 0;
+
+ UInt32 globalSize = MyMin((UInt32)initState->GlobalData.Size(), kGlobalSize);
+ if (globalSize != 0)
+ memcpy(Mem + kGlobalOffset, &initState->GlobalData[0], globalSize);
+ UInt32 staticSize = MyMin((UInt32)prg->StaticData.Size(), kGlobalSize - globalSize);
+ if (staticSize != 0)
+ memcpy(Mem + kGlobalOffset + globalSize, &prg->StaticData[0], staticSize);
+
+ bool res = true;
+ #ifdef RARVM_STANDARD_FILTERS
+ if (prg->StandardFilterIndex >= 0)
+ ExecuteStandardFilter(prg->StandardFilterIndex);
+ else
+ #endif
+ {
+ res = ExecuteCode(prg);
+ if (!res)
+ prg->Commands[0].OpCode = CMD_RET;
+ }
+ UInt32 newBlockPos = GetFixedGlobalValue32(NGlobalOffset::kBlockPos) & kSpaceMask;
+ UInt32 newBlockSize = GetFixedGlobalValue32(NGlobalOffset::kBlockSize) & kSpaceMask;
+ if (newBlockPos + newBlockSize >= kSpaceSize)
+ newBlockPos = newBlockSize = 0;
+ outBlockRef.Offset = newBlockPos;
+ outBlockRef.Size = newBlockSize;
+
+ outGlobalData.Clear();
+ UInt32 dataSize = GetFixedGlobalValue32(NGlobalOffset::kGlobalMemOutSize);
+ dataSize = MyMin(dataSize, kGlobalSize - kFixedGlobalSize);
+ if (dataSize != 0)
+ {
+ dataSize += kFixedGlobalSize;
+ outGlobalData.Reserve(dataSize);
+ for (UInt32 i = 0; i < dataSize; i++)
+ outGlobalData.Add(Mem[kGlobalOffset + i]);
+ }
+ return res;
+}
+
+
+#define SET_IP(IP) \
+ if ((IP) >= numCommands) return true; \
+ if (--maxOpCount <= 0) return false; \
+ cmd = commands + (IP);
+
+#define GET_FLAG_S_B(res) (((res) & 0x80) ? FLAG_S : 0)
+#define SET_IP_OP1 { UInt32 val = GetOperand32(&cmd->Op1); SET_IP(val); }
+#define FLAGS_UPDATE_SZ Flags = res == 0 ? FLAG_Z : res & FLAG_S
+#define FLAGS_UPDATE_SZ_B Flags = (res & 0xFF) == 0 ? FLAG_Z : GET_FLAG_S_B(res)
+
+UInt32 CVm::GetOperand32(const COperand *op) const
+{
+ switch(op->Type)
+ {
+ case OP_TYPE_REG: return R[op->Data];
+ case OP_TYPE_REGMEM: return GetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask]);
+ default: return op->Data;
+ }
+}
+
+void CVm::SetOperand32(const COperand *op, UInt32 val)
+{
+ switch(op->Type)
+ {
+ case OP_TYPE_REG: R[op->Data] = val; return;
+ case OP_TYPE_REGMEM: SetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask], val); return;
+ }
+}
+
+Byte CVm::GetOperand8(const COperand *op) const
+{
+ switch(op->Type)
+ {
+ case OP_TYPE_REG: return (Byte)R[op->Data];
+ case OP_TYPE_REGMEM: return Mem[(op->Base + R[op->Data]) & kSpaceMask];;
+ default: return (Byte)op->Data;
+ }
+}
+
+void CVm::SetOperand8(const COperand *op, Byte val)
+{
+ switch(op->Type)
+ {
+ case OP_TYPE_REG: R[op->Data] = (R[op->Data] & 0xFFFFFF00) | val; return;
+ case OP_TYPE_REGMEM: Mem[(op->Base + R[op->Data]) & kSpaceMask] = val; return;
+ }
+}
+
+UInt32 CVm::GetOperand(bool byteMode, const COperand *op) const
+{
+ if (byteMode)
+ return GetOperand8(op);
+ return GetOperand32(op);
+}
+
+void CVm::SetOperand(bool byteMode, const COperand *op, UInt32 val)
+{
+ if (byteMode)
+ SetOperand8(op, (Byte)(val & 0xFF));
+ else
+ SetOperand32(op, val);
+}
+
+bool CVm::ExecuteCode(const CProgram *prg)
+{
+ Int32 maxOpCount = 25000000;
+ const CCommand *commands = &prg->Commands[0];
+ const CCommand *cmd = commands;
+ UInt32 numCommands = prg->Commands.Size();
+ for (;;)
+ {
+ switch(cmd->OpCode)
+ {
+ #ifndef RARVM_NO_VM
+
+ case CMD_MOV:
+ SetOperand32(&cmd->Op1, GetOperand32(&cmd->Op2));
+ break;
+ case CMD_MOVB:
+ SetOperand8(&cmd->Op1, GetOperand8(&cmd->Op2));
+ break;
+ case CMD_CMP:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ UInt32 res = v1 - GetOperand32(&cmd->Op2);
+ Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
+ }
+ break;
+ case CMD_CMPB:
+ {
+ Byte v1 = GetOperand8(&cmd->Op1);
+ Byte res = v1 - GetOperand8(&cmd->Op2);
+ res &= 0xFF;
+ Flags = res == 0 ? FLAG_Z : (res > v1) | GET_FLAG_S_B(res);
+ }
+ break;
+ case CMD_ADD:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ UInt32 res = v1 + GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ Flags = (res < v1) | (res == 0 ? FLAG_Z : (res & FLAG_S));
+ }
+ break;
+ case CMD_ADDB:
+ {
+ Byte v1 = GetOperand8(&cmd->Op1);
+ Byte res = v1 + GetOperand8(&cmd->Op2);
+ res &= 0xFF;
+ SetOperand8(&cmd->Op1, (Byte)res);
+ Flags = (res < v1) | (res == 0 ? FLAG_Z : GET_FLAG_S_B(res));
+ }
+ break;
+ case CMD_ADC:
+ {
+ UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);
+ UInt32 FC = (Flags & FLAG_C);
+ UInt32 res = v1 + GetOperand(cmd->ByteMode, &cmd->Op2) + FC;
+ if (cmd->ByteMode)
+ res &= 0xFF;
+ SetOperand(cmd->ByteMode, &cmd->Op1, res);
+ Flags = (res < v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));
+ }
+ break;
+ case CMD_SUB:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ UInt32 res = v1 - GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
+ }
+ break;
+ case CMD_SUBB:
+ {
+ UInt32 v1 = GetOperand8(&cmd->Op1);
+ UInt32 res = v1 - GetOperand8(&cmd->Op2);
+ SetOperand8(&cmd->Op1, (Byte)res);
+ Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);
+ }
+ break;
+ case CMD_SBB:
+ {
+ UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);
+ UInt32 FC = (Flags & FLAG_C);
+ UInt32 res = v1 - GetOperand(cmd->ByteMode, &cmd->Op2) - FC;
+ // Flags = res == 0 ? FLAG_Z : (res > v1 || res == v1 && FC) | (res & FLAG_S);
+ if (cmd->ByteMode)
+ res &= 0xFF;
+ SetOperand(cmd->ByteMode, &cmd->Op1, res);
+ Flags = (res > v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));
+ }
+ break;
+ case CMD_INC:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) + 1;
+ SetOperand32(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_INCB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) + 1;
+ SetOperand8(&cmd->Op1, res);;
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_DEC:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) - 1;
+ SetOperand32(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_DECB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) - 1;
+ SetOperand8(&cmd->Op1, res);;
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_XOR:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) ^ GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_XORB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) ^ GetOperand8(&cmd->Op2);
+ SetOperand8(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_AND:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) & GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_ANDB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) & GetOperand8(&cmd->Op2);
+ SetOperand8(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_OR:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) | GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_ORB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) | GetOperand8(&cmd->Op2);
+ SetOperand8(&cmd->Op1, res);
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_TEST:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) & GetOperand32(&cmd->Op2);
+ FLAGS_UPDATE_SZ;
+ }
+ break;
+ case CMD_TESTB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) & GetOperand8(&cmd->Op2);
+ FLAGS_UPDATE_SZ_B;
+ }
+ break;
+ case CMD_NOT:
+ SetOperand(cmd->ByteMode, &cmd->Op1, ~GetOperand(cmd->ByteMode, &cmd->Op1));
+ break;
+ case CMD_NEG:
+ {
+ UInt32 res = 0 - GetOperand32(&cmd->Op1);
+ SetOperand32(&cmd->Op1, res);
+ Flags = res == 0 ? FLAG_Z : FLAG_C | (res & FLAG_S);
+ }
+ break;
+ case CMD_NEGB:
+ {
+ Byte res = (Byte)(0 - GetOperand8(&cmd->Op1));
+ SetOperand8(&cmd->Op1, res);
+ Flags = res == 0 ? FLAG_Z : FLAG_C | GET_FLAG_S_B(res);
+ }
+ break;
+
+ case CMD_SHL:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ int v2 = (int)GetOperand32(&cmd->Op2);
+ UInt32 res = v1 << v2;
+ SetOperand32(&cmd->Op1, res);
+ Flags = (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 << (v2 - 1)) & 0x80000000 ? FLAG_C : 0);
+ }
+ break;
+ case CMD_SHLB:
+ {
+ Byte v1 = GetOperand8(&cmd->Op1);
+ int v2 = (int)GetOperand8(&cmd->Op2);
+ Byte res = (Byte)(v1 << v2);
+ SetOperand8(&cmd->Op1, res);
+ Flags = (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 << (v2 - 1)) & 0x80 ? FLAG_C : 0);
+ }
+ break;
+ case CMD_SHR:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ int v2 = (int)GetOperand32(&cmd->Op2);
+ UInt32 res = v1 >> v2;
+ SetOperand32(&cmd->Op1, res);
+ Flags = (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 >> (v2 - 1)) & FLAG_C);
+ }
+ break;
+ case CMD_SHRB:
+ {
+ Byte v1 = GetOperand8(&cmd->Op1);
+ int v2 = (int)GetOperand8(&cmd->Op2);
+ Byte res = (Byte)(v1 >> v2);
+ SetOperand8(&cmd->Op1, res);
+ Flags = (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 >> (v2 - 1)) & FLAG_C);
+ }
+ break;
+ case CMD_SAR:
+ {
+ UInt32 v1 = GetOperand32(&cmd->Op1);
+ int v2 = (int)GetOperand32(&cmd->Op2);
+ UInt32 res = UInt32(((Int32)v1) >> v2);
+ SetOperand32(&cmd->Op1, res);
+ Flags= (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 >> (v2 - 1)) & FLAG_C);
+ }
+ break;
+ case CMD_SARB:
+ {
+ Byte v1 = GetOperand8(&cmd->Op1);
+ int v2 = (int)GetOperand8(&cmd->Op2);
+ Byte res = (Byte)(((signed char)v1) >> v2);
+ SetOperand8(&cmd->Op1, res);
+ Flags= (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 >> (v2 - 1)) & FLAG_C);
+ }
+ break;
+
+ case CMD_JMP:
+ SET_IP_OP1;
+ continue;
+ case CMD_JZ:
+ if ((Flags & FLAG_Z) != 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JNZ:
+ if ((Flags & FLAG_Z) == 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JS:
+ if ((Flags & FLAG_S) != 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JNS:
+ if ((Flags & FLAG_S) == 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JB:
+ if ((Flags & FLAG_C) != 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JBE:
+ if ((Flags & (FLAG_C | FLAG_Z)) != 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JA:
+ if ((Flags & (FLAG_C | FLAG_Z)) == 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+ case CMD_JAE:
+ if ((Flags & FLAG_C) == 0)
+ {
+ SET_IP_OP1;
+ continue;
+ }
+ break;
+
+ case CMD_PUSH:
+ R[kStackRegIndex] -= 4;
+ SetValue32(&Mem[R[kStackRegIndex] & kSpaceMask], GetOperand32(&cmd->Op1));
+ break;
+ case CMD_POP:
+ SetOperand32(&cmd->Op1, GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]));
+ R[kStackRegIndex] += 4;
+ break;
+ case CMD_CALL:
+ R[kStackRegIndex] -= 4;
+ SetValue32(&Mem[R[kStackRegIndex] & kSpaceMask], (UInt32)(cmd - commands + 1));
+ SET_IP_OP1;
+ continue;
+
+ case CMD_PUSHA:
+ {
+ for (UInt32 i = 0, SP = R[kStackRegIndex] - 4; i < kNumRegs; i++, SP -= 4)
+ SetValue32(&Mem[SP & kSpaceMask], R[i]);
+ R[kStackRegIndex] -= kNumRegs * 4;
+ }
+ break;
+ case CMD_POPA:
+ {
+ for (UInt32 i = 0, SP = R[kStackRegIndex]; i < kNumRegs; i++, SP += 4)
+ R[kStackRegIndex - i] = GetValue32(&Mem[SP & kSpaceMask]);
+ }
+ break;
+ case CMD_PUSHF:
+ R[kStackRegIndex] -= 4;
+ SetValue32(&Mem[R[kStackRegIndex]&kSpaceMask], Flags);
+ break;
+ case CMD_POPF:
+ Flags = GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]);
+ R[kStackRegIndex] += 4;
+ break;
+
+ case CMD_MOVZX:
+ SetOperand32(&cmd->Op1, GetOperand8(&cmd->Op2));
+ break;
+ case CMD_MOVSX:
+ SetOperand32(&cmd->Op1, (UInt32)(Int32)(signed char)GetOperand8(&cmd->Op2));
+ break;
+ case CMD_XCHG:
+ {
+ UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);
+ SetOperand(cmd->ByteMode, &cmd->Op1, GetOperand(cmd->ByteMode, &cmd->Op2));
+ SetOperand(cmd->ByteMode, &cmd->Op2, v1);
+ }
+ break;
+ case CMD_MUL:
+ {
+ UInt32 res = GetOperand32(&cmd->Op1) * GetOperand32(&cmd->Op2);
+ SetOperand32(&cmd->Op1, res);
+ }
+ break;
+ case CMD_MULB:
+ {
+ Byte res = GetOperand8(&cmd->Op1) * GetOperand8(&cmd->Op2);
+ SetOperand8(&cmd->Op1, res);
+ }
+ break;
+ case CMD_DIV:
+ {
+ UInt32 divider = GetOperand(cmd->ByteMode, &cmd->Op2);
+ if (divider != 0)
+ {
+ UInt32 res = GetOperand(cmd->ByteMode, &cmd->Op1) / divider;
+ SetOperand(cmd->ByteMode, &cmd->Op1, res);
+ }
+ }
+ break;
+
+ #endif
+
+ case CMD_RET:
+ {
+ if (R[kStackRegIndex] >= kSpaceSize)
+ return true;
+ UInt32 ip = GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]);
+ SET_IP(ip);
+ R[kStackRegIndex] += 4;
+ continue;
+ }
+ case CMD_PRINT:
+ break;
+ }
+ cmd++;
+ --maxOpCount;
+ }
+}
+
+
+//////////////////////////////////////////////////////
+// Read program
+
+UInt32 ReadEncodedUInt32(CMemBitDecoder &inp)
+{
+ switch(inp.ReadBits(2))
+ {
+ case 0:
+ return inp.ReadBits(4);
+ case 1:
+ {
+ UInt32 v = inp.ReadBits(4);
+ if (v == 0)
+ return 0xFFFFFF00 | inp.ReadBits(8);
+ else
+ return (v << 4) | inp.ReadBits(4);
+ }
+ case 2:
+ return inp.ReadBits(16);
+ default:
+ return inp.ReadBits(32);
+ }
+}
+
+void CVm::DecodeArg(CMemBitDecoder &inp, COperand &op, bool byteMode)
+{
+ if (inp.ReadBit())
+ {
+ op.Type = OP_TYPE_REG;
+ op.Data = inp.ReadBits(kNumRegBits);
+ }
+ else if (inp.ReadBit() == 0)
+ {
+ op.Type = OP_TYPE_INT;
+ if (byteMode)
+ op.Data = inp.ReadBits(8);
+ else
+ op.Data = ReadEncodedUInt32(inp);
+ }
+ else
+ {
+ op.Type = OP_TYPE_REGMEM;
+ if (inp.ReadBit() == 0)
+ {
+ op.Data = inp.ReadBits(kNumRegBits);
+ op.Base = 0;
+ }
+ else
+ {
+ if (inp.ReadBit() == 0)
+ op.Data = inp.ReadBits(kNumRegBits);
+ else
+ op.Data = kNumRegs;
+ op.Base = ReadEncodedUInt32(inp);
+ }
+ }
+}
+
+void CVm::ReadVmProgram(const Byte *code, UInt32 codeSize, CProgram *prg)
+{
+ CMemBitDecoder inp;
+ inp.Init(code, codeSize);
+
+ prg->StaticData.Clear();
+ if (inp.ReadBit())
+ {
+ UInt32 dataSize = ReadEncodedUInt32(inp) + 1;
+ for (UInt32 i = 0; inp.Avail() && i < dataSize; i++)
+ prg->StaticData.Add((Byte)inp.ReadBits(8));
+ }
+ while (inp.Avail())
+ {
+ prg->Commands.Add(CCommand());
+ CCommand *cmd = &prg->Commands.Back();
+ if (inp.ReadBit() == 0)
+ cmd->OpCode = (ECommand)inp.ReadBits(3);
+ else
+ cmd->OpCode = (ECommand)(8 + inp.ReadBits(5));
+ if (kCmdFlags[cmd->OpCode] & CF_BYTEMODE)
+ cmd->ByteMode = (inp.ReadBit()) ? true : false;
+ else
+ cmd->ByteMode = 0;
+ int opNum = (kCmdFlags[cmd->OpCode] & CF_OPMASK);
+ if (opNum > 0)
+ {
+ DecodeArg(inp, cmd->Op1, cmd->ByteMode);
+ if (opNum == 2)
+ DecodeArg(inp, cmd->Op2, cmd->ByteMode);
+ else
+ {
+ if (cmd->Op1.Type == OP_TYPE_INT && (kCmdFlags[cmd->OpCode] & (CF_JUMP | CF_PROC)))
+ {
+ int Distance = cmd->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->Commands.Size() - 1;
+ }
+ cmd->Op1.Data = Distance;
+ }
+ }
+ }
+ if (cmd->ByteMode)
+ {
+ switch (cmd->OpCode)
+ {
+ case CMD_MOV: cmd->OpCode = CMD_MOVB; break;
+ case CMD_CMP: cmd->OpCode = CMD_CMPB; break;
+ case CMD_ADD: cmd->OpCode = CMD_ADDB; break;
+ case CMD_SUB: cmd->OpCode = CMD_SUBB; break;
+ case CMD_INC: cmd->OpCode = CMD_INCB; break;
+ case CMD_DEC: cmd->OpCode = CMD_DECB; break;
+ case CMD_XOR: cmd->OpCode = CMD_XORB; break;
+ case CMD_AND: cmd->OpCode = CMD_ANDB; break;
+ case CMD_OR: cmd->OpCode = CMD_ORB; break;
+ case CMD_TEST: cmd->OpCode = CMD_TESTB; break;
+ case CMD_NEG: cmd->OpCode = CMD_NEGB; break;
+ case CMD_SHL: cmd->OpCode = CMD_SHLB; break;
+ case CMD_SHR: cmd->OpCode = CMD_SHRB; break;
+ case CMD_SAR: cmd->OpCode = CMD_SARB; break;
+ case CMD_MUL: cmd->OpCode = CMD_MULB; break;
+ }
+ }
+ }
+}
+
+#ifdef RARVM_STANDARD_FILTERS
+
+enum EStandardFilter
+{
+ SF_E8,
+ SF_E8E9,
+ SF_ITANIUM,
+ SF_RGB,
+ SF_AUDIO,
+ SF_DELTA,
+ SF_UPCASE
+};
+
+struct StandardFilterSignature
+{
+ UInt32 Length;
+ UInt32 CRC;
+ EStandardFilter Type;
+}
+kStdFilters[]=
+{
+ { 53, 0xad576887, SF_E8 },
+ { 57, 0x3cd7e57e, SF_E8E9 },
+ { 120, 0x3769893f, SF_ITANIUM },
+ { 29, 0x0e06077d, SF_DELTA },
+ { 149, 0x1c2c5dc8, SF_RGB },
+ { 216, 0xbc85e701, SF_AUDIO },
+ { 40, 0x46b9c560, SF_UPCASE }
+};
+
+static int FindStandardFilter(const Byte *code, UInt32 codeSize)
+{
+ UInt32 crc = CrcCalc(code, codeSize);
+ for (int i = 0; i < sizeof(kStdFilters) / sizeof(kStdFilters[0]); i++)
+ {
+ StandardFilterSignature &sfs = kStdFilters[i];
+ if (sfs.CRC == crc && sfs.Length == codeSize)
+ return i;
+ }
+ return -1;
+}
+
+#endif
+
+void CVm::PrepareProgram(const Byte *code, UInt32 codeSize, CProgram *prg)
+{
+ Byte xorSum = 0;
+ for (UInt32 i = 1; i < codeSize; i++)
+ xorSum ^= code[i];
+
+ prg->Commands.Clear();
+ #ifdef RARVM_STANDARD_FILTERS
+ prg->StandardFilterIndex = -1;
+ #endif
+
+ if (xorSum == code[0] && codeSize > 0)
+ {
+ #ifdef RARVM_STANDARD_FILTERS
+ prg->StandardFilterIndex = FindStandardFilter(code, codeSize);
+ if (prg->StandardFilterIndex >= 0)
+ return;
+ #endif
+ // 1 byte for checksum
+ ReadVmProgram(code + 1, codeSize - 1, prg);
+ }
+ prg->Commands.Add(CCommand());
+ CCommand *cmd = &prg->Commands.Back();
+ cmd->OpCode = CMD_RET;
+}
+
+void CVm::SetMemory(UInt32 pos, const Byte *data, UInt32 dataSize)
+{
+ if (pos < kSpaceSize && data != Mem + pos)
+ memmove(Mem + pos, data, MyMin(dataSize, kSpaceSize - pos));
+}
+
+#ifdef RARVM_STANDARD_FILTERS
+
+static void E8E9Decode(Byte *data, UInt32 dataSize, UInt32 fileOffset, bool e9)
+{
+ if (dataSize <= 4)
+ return;
+ dataSize -= 4;
+ const UInt32 kFileSize = 0x1000000;
+ Byte cmpByte2 = (e9 ? 0xE9 : 0xE8);
+ for (UInt32 curPos = 0; curPos < dataSize;)
+ {
+ Byte curByte = *(data++);
+ curPos++;
+ if (curByte == 0xE8 || curByte == cmpByte2)
+ {
+ UInt32 offset = curPos + fileOffset;
+ UInt32 addr = (Int32)GetValue32(data);
+ if (addr < kFileSize)
+ SetValue32(data, addr - offset);
+ else if ((Int32)addr < 0 && (Int32)(addr + offset) >= 0)
+ SetValue32(data, addr + kFileSize);
+ data += 4;
+ curPos += 4;
+ }
+ }
+}
+
+static inline UInt32 ItaniumGetOpType(const Byte *data, int bitPos)
+{
+ return (data[(unsigned int)bitPos >> 3] >> (bitPos & 7)) & 0xF;
+}
+
+
+static void ItaniumDecode(Byte *data, UInt32 dataSize, UInt32 fileOffset)
+{
+ UInt32 curPos = 0;
+ fileOffset >>= 4;
+ while (curPos < dataSize - 21)
+ {
+ int b = (data[0] & 0x1F) - 0x10;
+ if (b >= 0)
+ {
+ static Byte kCmdMasks[16] = {4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};
+ Byte cmdMask = kCmdMasks[b];
+ if (cmdMask != 0)
+ for (int i = 0; i < 3; i++)
+ if (cmdMask & (1 << i))
+ {
+ int startPos = i * 41 + 18;
+ if (ItaniumGetOpType(data, startPos + 24) == 5)
+ {
+ const UInt32 kMask = 0xFFFFF;
+ Byte *p = data + ((unsigned int)startPos >> 3);
+ UInt32 bitField = ((UInt32)p[0]) | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16);
+ int inBit = (startPos & 7);
+ UInt32 offset = (bitField >> inBit) & kMask;
+ UInt32 andMask = ~(kMask << inBit);
+ bitField = ((offset - fileOffset) & kMask) << inBit;
+ for (int j = 0; j < 3; j++)
+ {
+ p[j] &= andMask;
+ p[j] |= bitField;
+ andMask >>= 8;
+ bitField >>= 8;
+ }
+ }
+ }
+ }
+ data += 16;
+ curPos += 16;
+ fileOffset++;
+ }
+}
+
+static void DeltaDecode(Byte *data, UInt32 dataSize, UInt32 numChannels)
+{
+ UInt32 srcPos = 0;
+ UInt32 border = dataSize * 2;
+ for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)
+ {
+ Byte prevByte = 0;
+ for (UInt32 destPos = dataSize + curChannel; destPos < border; destPos += numChannels)
+ data[destPos] = (prevByte = prevByte - data[srcPos++]);
+ }
+}
+
+static void RgbDecode(Byte *srcData, UInt32 dataSize, UInt32 width, UInt32 posR)
+{
+ Byte *destData = srcData + dataSize;
+ const UInt32 numChannels = 3;
+ for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)
+ {
+ Byte prevByte = 0;
+
+ for (UInt32 i = curChannel; i < dataSize; i+= numChannels)
+ {
+ unsigned int predicted;
+ if (i < width)
+ predicted = prevByte;
+ else
+ {
+ unsigned int upperLeftByte = destData[i - width];
+ unsigned int upperByte = destData[i - width + 3];
+ predicted = prevByte + upperByte - upperLeftByte;
+ int pa = abs((int)(predicted - prevByte));
+ int pb = abs((int)(predicted - upperByte));
+ int pc = abs((int)(predicted - upperLeftByte));
+ if (pa <= pb && pa <= pc)
+ predicted = prevByte;
+ else
+ if (pb <= pc)
+ predicted = upperByte;
+ else
+ predicted = upperLeftByte;
+ }
+ destData[i] = prevByte = (Byte)(predicted - *(srcData++));
+ }
+ }
+ if (dataSize < 3)
+ return;
+ for (UInt32 i = posR, border = dataSize - 2; i < border; i += 3)
+ {
+ Byte g = destData[i + 1];
+ destData[i] = destData[i] + g;
+ destData[i + 2] = destData[i + 2] + g;
+ }
+}
+
+static void AudioDecode(Byte *srcData, UInt32 dataSize, UInt32 numChannels)
+{
+ Byte *destData = srcData + dataSize;
+ for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)
+ {
+ UInt32 prevByte = 0, prevDelta = 0, dif[7];
+ Int32 D1 = 0, D2 = 0, D3;
+ Int32 K1 = 0, K2 = 0, K3 = 0;
+ memset(dif, 0, sizeof(dif));
+
+ for (UInt32 i = curChannel, byteCount = 0; i < dataSize; i += numChannels, byteCount++)
+ {
+ D3 = D2;
+ D2 = prevDelta - D1;
+ D1 = prevDelta;
+
+ UInt32 predicted = 8 * prevByte + K1 * D1 + K2 * D2 + K3 * D3;
+ predicted = (predicted >> 3) & 0xFF;
+
+ UInt32 curByte = *(srcData++);
+
+ predicted -= curByte;
+ destData[i] = (Byte)predicted;
+ prevDelta = (UInt32)(Int32)(signed char)(predicted - prevByte);
+ prevByte = predicted;
+
+ Int32 D = ((Int32)(signed char)curByte) << 3;
+
+ dif[0] += abs(D);
+ dif[1] += abs(D - D1);
+ dif[2] += abs(D + D1);
+ dif[3] += abs(D - D2);
+ dif[4] += abs(D + D2);
+ dif[5] += abs(D - D3);
+ dif[6] += abs(D + D3);
+
+ if ((byteCount & 0x1F) == 0)
+ {
+ UInt32 minDif = dif[0], numMinDif = 0;
+ dif[0] = 0;
+ for (int j = 1; j < sizeof(dif) / sizeof(dif[0]); j++)
+ {
+ if (dif[j] < minDif)
+ {
+ minDif = dif[j];
+ numMinDif = j;
+ }
+ dif[j] = 0;
+ }
+ switch (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;
+ }
+ }
+ }
+ }
+}
+
+static UInt32 UpCaseDecode(Byte *data, UInt32 dataSize)
+{
+ UInt32 srcPos = 0, destPos = dataSize;
+ while (srcPos < dataSize)
+ {
+ Byte curByte = data[srcPos++];
+ if (curByte == 2 && (curByte = data[srcPos++]) != 2)
+ curByte -= 32;
+ data[destPos++] = curByte;
+ }
+ return destPos - dataSize;
+}
+
+void CVm::ExecuteStandardFilter(int filterIndex)
+{
+ UInt32 dataSize = R[4];
+ if (dataSize >= kGlobalOffset)
+ return;
+ EStandardFilter filterType = kStdFilters[filterIndex].Type;
+
+ switch (filterType)
+ {
+ case SF_E8:
+ case SF_E8E9:
+ E8E9Decode(Mem, dataSize, R[6], (filterType == SF_E8E9));
+ break;
+ case SF_ITANIUM:
+ ItaniumDecode(Mem, dataSize, R[6]);
+ break;
+ case SF_DELTA:
+ if (dataSize >= kGlobalOffset / 2)
+ break;
+ SetBlockPos(dataSize);
+ DeltaDecode(Mem, dataSize, R[0]);
+ break;
+ case SF_RGB:
+ if (dataSize >= kGlobalOffset / 2)
+ break;
+ {
+ UInt32 width = R[0];
+ if (width <= 3)
+ break;
+ SetBlockPos(dataSize);
+ RgbDecode(Mem, dataSize, width, R[1]);
+ }
+ break;
+ case SF_AUDIO:
+ if (dataSize >= kGlobalOffset / 2)
+ break;
+ SetBlockPos(dataSize);
+ AudioDecode(Mem, dataSize, R[0]);
+ break;
+ case SF_UPCASE:
+ if (dataSize >= kGlobalOffset / 2)
+ break;
+ UInt32 destSize = UpCaseDecode(Mem, dataSize);
+ SetBlockSize(destSize);
+ SetBlockPos(dataSize);
+ break;
+ }
+}
+
+#endif
+
+}}}