// 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 &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 }}}