diff options
author | Jorge Ruesga <jorge@ruesga.com> | 2013-03-15 19:59:52 +0100 |
---|---|---|
committer | Jorge Ruesga <jorge@ruesga.com> | 2013-03-15 19:59:52 +0100 |
commit | 755c335580361c20d92e32d6a4a1e294e872b304 (patch) | |
tree | acd4e1b1395dfafa6980ef24e641c980f3a5c624 /src/rarvm.cpp | |
download | android_external_unrar-755c335580361c20d92e32d6a4a1e294e872b304.tar.gz android_external_unrar-755c335580361c20d92e32d6a4a1e294e872b304.tar.bz2 android_external_unrar-755c335580361c20d92e32d6a4a1e294e872b304.zip |
Initial commit - UNRAR 4.20
Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
Diffstat (limited to 'src/rarvm.cpp')
-rw-r--r-- | src/rarvm.cpp | 1139 |
1 files changed, 1139 insertions, 0 deletions
diff --git a/src/rarvm.cpp b/src/rarvm.cpp new file mode 100644 index 0000000..8b85800 --- /dev/null +++ b/src/rarvm.cpp @@ -0,0 +1,1139 @@ +#include "rar.hpp" + +#include "rarvmtbl.cpp" + +RarVM::RarVM() +{ + Mem=NULL; +} + + +RarVM::~RarVM() +{ + delete[] Mem; +} + + +void RarVM::Init() +{ + if (Mem==NULL) + Mem=new byte[VM_MEMSIZE+4]; +} + +/********************************************************************* + IS_VM_MEM macro checks if address belongs to VM memory pool (Mem). + Only Mem data are always low endian regardless of machine architecture, + so we need to convert them to native format when reading or writing. + VM registers have endianness of host machine. +**********************************************************************/ +#define IS_VM_MEM(a) (((byte*)a)>=Mem && ((byte*)a)<Mem+VM_MEMSIZE) + +inline uint RarVM::GetValue(bool ByteMode,uint *Addr) +{ + if (ByteMode) + { +#ifdef BIG_ENDIAN + if (IS_VM_MEM(Addr)) + return(*(byte *)Addr); + else + return(*Addr & 0xff); +#else + return(*(byte *)Addr); +#endif + } + else + { +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) + if (IS_VM_MEM(Addr)) + { + byte *B=(byte *)Addr; + return GET_UINT32((uint)B[0]|((uint)B[1]<<8)|((uint)B[2]<<16)|((uint)B[3]<<24)); + } + else + return GET_UINT32(*Addr); +#else + return GET_UINT32(*Addr); +#endif + } +} + +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) + #define GET_VALUE(ByteMode,Addr) GetValue(ByteMode,(uint *)Addr) +#else + #define GET_VALUE(ByteMode,Addr) ((ByteMode) ? (*(byte *)(Addr)):GET_UINT32(*(uint *)(Addr))) +#endif + + +inline void RarVM::SetValue(bool ByteMode,uint *Addr,uint Value) +{ + if (ByteMode) + { +#ifdef BIG_ENDIAN + if (IS_VM_MEM(Addr)) + *(byte *)Addr=Value; + else + *Addr=(*Addr & ~0xff)|(Value & 0xff); +#else + *(byte *)Addr=Value; +#endif + } + else + { +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) + if (IS_VM_MEM(Addr)) + { + ((byte *)Addr)[0]=(byte)Value; + ((byte *)Addr)[1]=(byte)(Value>>8); + ((byte *)Addr)[2]=(byte)(Value>>16); + ((byte *)Addr)[3]=(byte)(Value>>24); + } + else + *(uint *)Addr=Value; +#else + *(uint32 *)Addr=Value; +#endif + } +} + +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) + #define SET_VALUE(ByteMode,Addr,Value) SetValue(ByteMode,(uint *)Addr,Value) +#else + #define SET_VALUE(ByteMode,Addr,Value) ((ByteMode) ? (*(byte *)(Addr)=((byte)(Value))):(*(uint32 *)(Addr)=((uint32)(Value)))) +#endif + + +void RarVM::SetLowEndianValue(uint *Addr,uint Value) +{ +#if defined(BIG_ENDIAN) || !defined(ALLOW_NOT_ALIGNED_INT) || !defined(PRESENT_INT32) + ((byte *)Addr)[0]=(byte)Value; + ((byte *)Addr)[1]=(byte)(Value>>8); + ((byte *)Addr)[2]=(byte)(Value>>16); + ((byte *)Addr)[3]=(byte)(Value>>24); +#else + *(uint32 *)Addr=Value; +#endif +} + + +inline uint* RarVM::GetOperand(VM_PreparedOperand *CmdOp) +{ + if (CmdOp->Type==VM_OPREGMEM) + return((uint *)&Mem[(*CmdOp->Addr+CmdOp->Base)&VM_MEMMASK]); + else + return(CmdOp->Addr); +} + + +void RarVM::Execute(VM_PreparedProgram *Prg) +{ + memcpy(R,Prg->InitR,sizeof(Prg->InitR)); + size_t GlobalSize=Min(Prg->GlobalData.Size(),VM_GLOBALMEMSIZE); + if (GlobalSize) + memcpy(Mem+VM_GLOBALMEMADDR,&Prg->GlobalData[0],GlobalSize); + size_t StaticSize=Min(Prg->StaticData.Size(),VM_GLOBALMEMSIZE-GlobalSize); + if (StaticSize) + memcpy(Mem+VM_GLOBALMEMADDR+GlobalSize,&Prg->StaticData[0],StaticSize); + + R[7]=VM_MEMSIZE; + Flags=0; + + VM_PreparedCommand *PreparedCode=Prg->AltCmd ? Prg->AltCmd:&Prg->Cmd[0]; + if (Prg->CmdCount>0 && !ExecuteCode(PreparedCode,Prg->CmdCount)) + { + // Invalid VM program. Let's replace it with 'return' command. + PreparedCode[0].OpCode=VM_RET; + } + uint NewBlockPos=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20])&VM_MEMMASK; + uint NewBlockSize=GET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c])&VM_MEMMASK; + if (NewBlockPos+NewBlockSize>=VM_MEMSIZE) + NewBlockPos=NewBlockSize=0; + Prg->FilteredData=Mem+NewBlockPos; + Prg->FilteredDataSize=NewBlockSize; + + Prg->GlobalData.Reset(); + + uint DataSize=Min(GET_VALUE(false,(uint*)&Mem[VM_GLOBALMEMADDR+0x30]),VM_GLOBALMEMSIZE-VM_FIXEDGLOBALSIZE); + if (DataSize!=0) + { + Prg->GlobalData.Add(DataSize+VM_FIXEDGLOBALSIZE); + memcpy(&Prg->GlobalData[0],&Mem[VM_GLOBALMEMADDR],DataSize+VM_FIXEDGLOBALSIZE); + } +} + + +/* +Note: + Due to performance considerations RAR VM may set VM_FS, VM_FC, VM_FZ + incorrectly for byte operands. These flags are always valid only + for 32-bit operands. Check implementation of concrete VM command + to see if it sets flags right. +*/ + +#define SET_IP(IP) \ + if ((IP)>=CodeSize) \ + return(true); \ + if (--MaxOpCount<=0) \ + return(false); \ + Cmd=PreparedCode+(IP); + +bool RarVM::ExecuteCode(VM_PreparedCommand *PreparedCode,uint CodeSize) +{ + int MaxOpCount=25000000; + VM_PreparedCommand *Cmd=PreparedCode; + while (1) + { +#ifndef NORARVM + // Get addresses to quickly access operands. + uint *Op1=GetOperand(&Cmd->Op1); + uint *Op2=GetOperand(&Cmd->Op2); +#endif + switch(Cmd->OpCode) + { +#ifndef NORARVM + case VM_MOV: + SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); + break; +#ifdef VM_OPTIMIZE + case VM_MOVB: + SET_VALUE(true,Op1,GET_VALUE(true,Op2)); + break; + case VM_MOVD: + SET_VALUE(false,Op1,GET_VALUE(false,Op2)); + break; +#endif + case VM_CMP: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=GET_UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; +#ifdef VM_OPTIMIZE + case VM_CMPB: + { + uint Value1=GET_VALUE(true,Op1); + uint Result=GET_UINT32(Value1-GET_VALUE(true,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; + case VM_CMPD: + { + uint Value1=GET_VALUE(false,Op1); + uint Result=GET_UINT32(Value1-GET_VALUE(false,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + } + break; +#endif + case VM_ADD: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=GET_UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)); + if (Cmd->ByteMode) + { + Result&=0xff; + Flags=(Result<Value1)|(Result==0 ? VM_FZ:((Result&0x80) ? VM_FS:0)); + } + else + Flags=(Result<Value1)|(Result==0 ? VM_FZ:(Result&VM_FS)); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_ADDB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)+GET_VALUE(true,Op2)); + break; + case VM_ADDD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)+GET_VALUE(false,Op2)); + break; +#endif + case VM_SUB: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Result=GET_UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:(Result>Value1)|(Result&VM_FS); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_SUBB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)-GET_VALUE(true,Op2)); + break; + case VM_SUBD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)-GET_VALUE(false,Op2)); + break; +#endif + case VM_JZ: + if ((Flags & VM_FZ)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JNZ: + if ((Flags & VM_FZ)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_INC: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)+1); + if (Cmd->ByteMode) + Result&=0xff; + SET_VALUE(Cmd->ByteMode,Op1,Result); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; +#ifdef VM_OPTIMIZE + case VM_INCB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)+1); + break; + case VM_INCD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)+1); + break; +#endif + case VM_DEC: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)-1); + SET_VALUE(Cmd->ByteMode,Op1,Result); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; +#ifdef VM_OPTIMIZE + case VM_DECB: + SET_VALUE(true,Op1,GET_VALUE(true,Op1)-1); + break; + case VM_DECD: + SET_VALUE(false,Op1,GET_VALUE(false,Op1)-1); + break; +#endif + case VM_JMP: + SET_IP(GET_VALUE(false,Op1)); + continue; + case VM_XOR: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)^GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_AND: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_OR: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)|GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_TEST: + { + uint Result=GET_UINT32(GET_VALUE(Cmd->ByteMode,Op1)&GET_VALUE(Cmd->ByteMode,Op2)); + Flags=Result==0 ? VM_FZ:Result&VM_FS; + } + break; + case VM_JS: + if ((Flags & VM_FS)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JNS: + if ((Flags & VM_FS)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JB: + if ((Flags & VM_FC)!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JBE: + if ((Flags & (VM_FC|VM_FZ))!=0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JA: + if ((Flags & (VM_FC|VM_FZ))==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_JAE: + if ((Flags & VM_FC)==0) + { + SET_IP(GET_VALUE(false,Op1)); + continue; + } + break; + case VM_PUSH: + R[7]-=4; + SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],GET_VALUE(false,Op1)); + break; + case VM_POP: + SET_VALUE(false,Op1,GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); + R[7]+=4; + break; + case VM_CALL: + R[7]-=4; + SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Cmd-PreparedCode+1); + SET_IP(GET_VALUE(false,Op1)); + continue; + case VM_NOT: + SET_VALUE(Cmd->ByteMode,Op1,~GET_VALUE(Cmd->ByteMode,Op1)); + break; + case VM_SHL: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=GET_UINT32(Value1<<Value2); + Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1<<(Value2-1))&0x80000000 ? VM_FC:0); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_SHR: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=GET_UINT32(Value1>>Value2); + Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_SAR: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint Value2=GET_VALUE(Cmd->ByteMode,Op2); + uint Result=GET_UINT32(((int)Value1)>>Value2); + Flags=(Result==0 ? VM_FZ:(Result&VM_FS))|((Value1>>(Value2-1))&VM_FC); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_NEG: + { + // We use "0-value" expression to suppress "unary minus to unsigned" + // compiler warning. + uint Result=GET_UINT32(0-GET_VALUE(Cmd->ByteMode,Op1)); + Flags=Result==0 ? VM_FZ:VM_FC|(Result&VM_FS); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#ifdef VM_OPTIMIZE + case VM_NEGB: + SET_VALUE(true,Op1,0-GET_VALUE(true,Op1)); + break; + case VM_NEGD: + SET_VALUE(false,Op1,0-GET_VALUE(false,Op1)); + break; +#endif + case VM_PUSHA: + { + const int RegCount=sizeof(R)/sizeof(R[0]); + for (int I=0,SP=R[7]-4;I<RegCount;I++,SP-=4) + SET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK],R[I]); + R[7]-=RegCount*4; + } + break; + case VM_POPA: + { + const int RegCount=sizeof(R)/sizeof(R[0]); + for (uint I=0,SP=R[7];I<RegCount;I++,SP+=4) + R[7-I]=GET_VALUE(false,(uint *)&Mem[SP & VM_MEMMASK]); + } + break; + case VM_PUSHF: + R[7]-=4; + SET_VALUE(false,(uint *)&Mem[R[7]&VM_MEMMASK],Flags); + break; + case VM_POPF: + Flags=GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK]); + R[7]+=4; + break; + case VM_MOVZX: + SET_VALUE(false,Op1,GET_VALUE(true,Op2)); + break; + case VM_MOVSX: + SET_VALUE(false,Op1,(signed char)GET_VALUE(true,Op2)); + break; + case VM_XCHG: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + SET_VALUE(Cmd->ByteMode,Op1,GET_VALUE(Cmd->ByteMode,Op2)); + SET_VALUE(Cmd->ByteMode,Op2,Value1); + } + break; + case VM_MUL: + { + uint Result=GET_VALUE(Cmd->ByteMode,Op1)*GET_VALUE(Cmd->ByteMode,Op2); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_DIV: + { + uint Divider=GET_VALUE(Cmd->ByteMode,Op2); + if (Divider!=0) + { + uint Result=GET_VALUE(Cmd->ByteMode,Op1)/Divider; + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + } + break; + case VM_ADC: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint FC=(Flags&VM_FC); + uint Result=GET_UINT32(Value1+GET_VALUE(Cmd->ByteMode,Op2)+FC); + if (Cmd->ByteMode) + Result&=0xff; + Flags=(Result<Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; + case VM_SBB: + { + uint Value1=GET_VALUE(Cmd->ByteMode,Op1); + uint FC=(Flags&VM_FC); + uint Result=GET_UINT32(Value1-GET_VALUE(Cmd->ByteMode,Op2)-FC); + if (Cmd->ByteMode) + Result&=0xff; + Flags=(Result>Value1 || Result==Value1 && FC)|(Result==0 ? VM_FZ:(Result&VM_FS)); + SET_VALUE(Cmd->ByteMode,Op1,Result); + } + break; +#endif // for #ifndef NORARVM + case VM_RET: + if (R[7]>=VM_MEMSIZE) + return(true); + SET_IP(GET_VALUE(false,(uint *)&Mem[R[7] & VM_MEMMASK])); + R[7]+=4; + continue; +#ifdef VM_STANDARDFILTERS + case VM_STANDARD: + ExecuteStandardFilter((VM_StandardFilters)Cmd->Op1.Data); + break; +#endif + case VM_PRINT: + break; + } + Cmd++; + --MaxOpCount; + } +} + + + + +void RarVM::Prepare(byte *Code,uint CodeSize,VM_PreparedProgram *Prg) +{ + InitBitInput(); + memcpy(InBuf,Code,Min(CodeSize,BitInput::MAX_SIZE)); + + // Calculate the single byte XOR checksum to check validity of VM code. + byte XorSum=0; + for (uint I=1;I<CodeSize;I++) + XorSum^=Code[I]; + + faddbits(8); + + Prg->CmdCount=0; + if (XorSum==Code[0]) // VM code is valid if equal. + { +#ifdef VM_STANDARDFILTERS + VM_StandardFilters FilterType=IsStandardFilter(Code,CodeSize); + if (FilterType!=VMSF_NONE) + { + // VM code is found among standard filters. + Prg->Cmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; + CurCmd->OpCode=VM_STANDARD; + CurCmd->Op1.Data=FilterType; + CurCmd->Op1.Addr=&CurCmd->Op1.Data; + CurCmd->Op2.Addr=&CurCmd->Op2.Data; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + CodeSize=0; + } +#endif + uint DataFlag=fgetbits(); + faddbits(1); + + // Read static data contained in DB operators. This data cannot be + // changed, it is a part of VM code, not a filter parameter. + + if (DataFlag&0x8000) + { + uint DataSize=ReadData(*this)+1; + for (uint I=0;(uint)InAddr<CodeSize && I<DataSize;I++) + { + Prg->StaticData.Add(1); + Prg->StaticData[I]=fgetbits()>>8; + faddbits(8); + } + } + + while ((uint)InAddr<CodeSize) + { + Prg->Cmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount]; + uint Data=fgetbits(); + if ((Data&0x8000)==0) + { + CurCmd->OpCode=(VM_Commands)(Data>>12); + faddbits(4); + } + else + { + CurCmd->OpCode=(VM_Commands)((Data>>10)-24); + faddbits(6); + } + if (VM_CmdFlags[CurCmd->OpCode] & VMCF_BYTEMODE) + { + CurCmd->ByteMode=(fgetbits()>>15)!=0; + faddbits(1); + } + else + CurCmd->ByteMode=0; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + int OpNum=(VM_CmdFlags[CurCmd->OpCode] & VMCF_OPMASK); + CurCmd->Op1.Addr=CurCmd->Op2.Addr=NULL; + if (OpNum>0) + { + DecodeArg(CurCmd->Op1,CurCmd->ByteMode); // reading the first operand + if (OpNum==2) + DecodeArg(CurCmd->Op2,CurCmd->ByteMode); // reading the second operand + else + { + if (CurCmd->Op1.Type==VM_OPINT && (VM_CmdFlags[CurCmd->OpCode]&(VMCF_JUMP|VMCF_PROC))) + { + // Calculating jump distance. + 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->CmdCount; + } + CurCmd->Op1.Data=Distance; + } + } + } + Prg->CmdCount++; + } + } + + // Adding RET command at the end of program. + Prg->Cmd.Add(1); + VM_PreparedCommand *CurCmd=&Prg->Cmd[Prg->CmdCount++]; + CurCmd->OpCode=VM_RET; + CurCmd->Op1.Addr=&CurCmd->Op1.Data; + CurCmd->Op2.Addr=&CurCmd->Op2.Data; + CurCmd->Op1.Type=CurCmd->Op2.Type=VM_OPNONE; + + // If operand 'Addr' field has not been set by DecodeArg calls above, + // let's set it to point to operand 'Data' field. It is necessary for + // VM_OPINT type operands (usual integers) or maybe if something was + // not set properly for other operands. 'Addr' field is required + // for quicker addressing of operand data. + 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; + } + +#ifdef VM_OPTIMIZE + if (CodeSize!=0) + Optimize(Prg); +#endif +} + + +void RarVM::DecodeArg(VM_PreparedOperand &Op,bool ByteMode) +{ + uint Data=fgetbits(); + if (Data & 0x8000) + { + Op.Type=VM_OPREG; // Operand is register (R[0]..R[7]) + Op.Data=(Data>>12)&7; // Register number + Op.Addr=&R[Op.Data]; // Register address + faddbits(4); // 1 flag bit and 3 register number bits + } + else + if ((Data & 0xc000)==0) + { + Op.Type=VM_OPINT; // Operand is integer + if (ByteMode) + { + Op.Data=(Data>>6) & 0xff; // Byte integer. + faddbits(10); + } + else + { + faddbits(2); + Op.Data=ReadData(*this); // 32 bit integer. + } + } + else + { + // Operand is data addressed by register data, base address or both. + Op.Type=VM_OPREGMEM; + if ((Data & 0x2000)==0) + { + // Base address is zero, just use the address from register. + Op.Data=(Data>>10)&7; + Op.Addr=&R[Op.Data]; + Op.Base=0; + faddbits(6); + } + else + { + if ((Data & 0x1000)==0) + { + // Use both register and base address. + Op.Data=(Data>>9)&7; + Op.Addr=&R[Op.Data]; + faddbits(7); + } + else + { + // Use base address only. Access memory by fixed address. + Op.Data=0; + faddbits(4); + } + Op.Base=ReadData(*this); // Read base address. + } + } +} + + +uint RarVM::ReadData(BitInput &Inp) +{ + uint Data=Inp.fgetbits(); + switch(Data&0xc000) + { + case 0: + Inp.faddbits(6); + return((Data>>10)&0xf); + case 0x4000: + if ((Data&0x3c00)==0) + { + Data=0xffffff00|((Data>>2)&0xff); + Inp.faddbits(14); + } + else + { + Data=(Data>>6)&0xff; + Inp.faddbits(10); + } + return(Data); + case 0x8000: + Inp.faddbits(2); + Data=Inp.fgetbits(); + Inp.faddbits(16); + return(Data); + default: + Inp.faddbits(2); + Data=(Inp.fgetbits()<<16); + Inp.faddbits(16); + Data|=Inp.fgetbits(); + Inp.faddbits(16); + return(Data); + } +} + + +void RarVM::SetMemory(uint Pos,byte *Data,uint DataSize) +{ + if (Pos<VM_MEMSIZE && Data!=Mem+Pos) + memmove(Mem+Pos,Data,Min(DataSize,VM_MEMSIZE-Pos)); +} + + +#ifdef VM_OPTIMIZE +void RarVM::Optimize(VM_PreparedProgram *Prg) +{ + VM_PreparedCommand *Code=&Prg->Cmd[0]; + uint CodeSize=Prg->CmdCount; + + for (uint I=0;I<CodeSize;I++) + { + VM_PreparedCommand *Cmd=Code+I; + + // Replace universal opcodes with faster byte or word only processing + // opcodes. + switch(Cmd->OpCode) + { + case VM_MOV: + Cmd->OpCode=Cmd->ByteMode ? VM_MOVB:VM_MOVD; + continue; + case VM_CMP: + Cmd->OpCode=Cmd->ByteMode ? VM_CMPB:VM_CMPD; + continue; + } + if ((VM_CmdFlags[Cmd->OpCode] & VMCF_CHFLAGS)==0) + continue; + + // If we do not have jump commands between the current operation + // and next command which will modify processor flags, we can replace + // the current command with faster version which does not need to + // modify flags. + bool FlagsRequired=false; + for (uint J=I+1;J<CodeSize;J++) + { + int Flags=VM_CmdFlags[Code[J].OpCode]; + if (Flags & (VMCF_JUMP|VMCF_PROC|VMCF_USEFLAGS)) + { + FlagsRequired=true; + break; + } + if (Flags & VMCF_CHFLAGS) + break; + } + + // Below we'll replace universal opcodes with faster byte or word only + // processing opcodes, which also do not modify processor flags to + // provide better performance. + if (FlagsRequired) + continue; + switch(Cmd->OpCode) + { + case VM_ADD: + Cmd->OpCode=Cmd->ByteMode ? VM_ADDB:VM_ADDD; + continue; + case VM_SUB: + Cmd->OpCode=Cmd->ByteMode ? VM_SUBB:VM_SUBD; + continue; + case VM_INC: + Cmd->OpCode=Cmd->ByteMode ? VM_INCB:VM_INCD; + continue; + case VM_DEC: + Cmd->OpCode=Cmd->ByteMode ? VM_DECB:VM_DECD; + continue; + case VM_NEG: + Cmd->OpCode=Cmd->ByteMode ? VM_NEGB:VM_NEGD; + continue; + } + } +} +#endif + + +#ifdef VM_STANDARDFILTERS +VM_StandardFilters RarVM::IsStandardFilter(byte *Code,uint CodeSize) +{ + struct StandardFilterSignature + { + int Length; + uint CRC; + VM_StandardFilters Type; + } static StdList[]={ + 53, 0xad576887, VMSF_E8, + 57, 0x3cd7e57e, VMSF_E8E9, + 120, 0x3769893f, VMSF_ITANIUM, + 29, 0x0e06077d, VMSF_DELTA, + 149, 0x1c2c5dc8, VMSF_RGB, + 216, 0xbc85e701, VMSF_AUDIO, + 40, 0x46b9c560, VMSF_UPCASE + }; + uint CodeCRC=CRC(0xffffffff,Code,CodeSize)^0xffffffff; + for (uint I=0;I<ASIZE(StdList);I++) + if (StdList[I].CRC==CodeCRC && StdList[I].Length==CodeSize) + return(StdList[I].Type); + return(VMSF_NONE); +} + + +void RarVM::ExecuteStandardFilter(VM_StandardFilters FilterType) +{ + switch(FilterType) + { + case VMSF_E8: + case VMSF_E8E9: + { + byte *Data=Mem; + int DataSize=R[4]; + uint FileOffset=R[6]; + + if ((uint)DataSize>=VM_GLOBALMEMADDR || DataSize<4) + break; + + const int FileSize=0x1000000; + byte CmpByte2=FilterType==VMSF_E8E9 ? 0xe9:0xe8; + for (int CurPos=0;CurPos<DataSize-4;) + { + byte CurByte=*(Data++); + CurPos++; + if (CurByte==0xe8 || CurByte==CmpByte2) + { +#ifdef PRESENT_INT32 + int32 Offset=CurPos+FileOffset; + int32 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=GET_VALUE(false,Data); + if ((Addr & 0x80000000)!=0) + { + if (((Addr+Offset) & 0x80000000)==0) + SET_VALUE(false,Data,Addr+FileSize); + } + else + if (((Addr-FileSize) & 0x80000000)!=0) + SET_VALUE(false,Data,Addr-Offset); +#endif + Data+=4; + CurPos+=4; + } + } + } + break; + case VMSF_ITANIUM: + { + byte *Data=Mem; + int DataSize=R[4]; + uint FileOffset=R[6]; + + if ((uint)DataSize>=VM_GLOBALMEMADDR || DataSize<21) + break; + + int CurPos=0; + + FileOffset>>=4; + + while (CurPos<DataSize-21) + { + int Byte=(Data[0]&0x1f)-0x10; + if (Byte>=0) + { + static byte Masks[16]={4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0}; + byte CmdMask=Masks[Byte]; + if (CmdMask!=0) + for (int I=0;I<=2;I++) + if (CmdMask & (1<<I)) + { + int StartPos=I*41+5; + int OpType=FilterItanium_GetBits(Data,StartPos+37,4); + if (OpType==5) + { + int Offset=FilterItanium_GetBits(Data,StartPos+13,20); + FilterItanium_SetBits(Data,(Offset-FileOffset)&0xfffff,StartPos+13,20); + } + } + } + Data+=16; + CurPos+=16; + FileOffset++; + } + } + break; + case VMSF_DELTA: + { + int DataSize=R[4],Channels=R[0],SrcPos=0,Border=DataSize*2; + SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + if ((uint)DataSize>=VM_GLOBALMEMADDR/2) + break; + + // Bytes from same channels are grouped to continual data blocks, + // so we need to place them back to their interleaving positions. + for (int CurChannel=0;CurChannel<Channels;CurChannel++) + { + byte PrevByte=0; + for (int DestPos=DataSize+CurChannel;DestPos<Border;DestPos+=Channels) + Mem[DestPos]=(PrevByte-=Mem[SrcPos++]); + } + } + break; + case VMSF_RGB: + { + int DataSize=R[4],Width=R[0]-3,PosR=R[1]; + byte *SrcData=Mem,*DestData=SrcData+DataSize; + const int Channels=3; + SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + if ((uint)DataSize>=VM_GLOBALMEMADDR/2 || PosR<0) + break; + for (int CurChannel=0;CurChannel<Channels;CurChannel++) + { + uint PrevByte=0; + + for (int I=CurChannel;I<DataSize;I+=Channels) + { + uint Predicted; + int UpperPos=I-Width; + if (UpperPos>=3) + { + byte *UpperData=DestData+UpperPos; + uint UpperByte=*UpperData; + uint UpperLeftByte=*(UpperData-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; + } + else + Predicted=PrevByte; + DestData[I]=PrevByte=(byte)(Predicted-*(SrcData++)); + } + } + for (int I=PosR,Border=DataSize-2;I<Border;I+=3) + { + byte G=DestData[I+1]; + DestData[I]+=G; + DestData[I+2]+=G; + } + } + break; + case VMSF_AUDIO: + { + int DataSize=R[4],Channels=R[0]; + byte *SrcData=Mem,*DestData=SrcData+DataSize; + SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + if ((uint)DataSize>=VM_GLOBALMEMADDR/2) + break; + for (int CurChannel=0;CurChannel<Channels;CurChannel++) + { + uint PrevByte=0,PrevDelta=0,Dif[7]; + int D1=0,D2=0,D3; + int K1=0,K2=0,K3=0; + memset(Dif,0,sizeof(Dif)); + + for (int I=CurChannel,ByteCount=0;I<DataSize;I+=Channels,ByteCount++) + { + D3=D2; + D2=PrevDelta-D1; + D1=PrevDelta; + + uint Predicted=8*PrevByte+K1*D1+K2*D2+K3*D3; + Predicted=(Predicted>>3) & 0xff; + + uint CurByte=*(SrcData++); + + Predicted-=CurByte; + DestData[I]=Predicted; + PrevDelta=(signed char)(Predicted-PrevByte); + PrevByte=Predicted; + + int D=((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) + { + uint 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; + } + } + } + } + } + break; + case VMSF_UPCASE: + { + int DataSize=R[4],SrcPos=0,DestPos=DataSize; + if ((uint)DataSize>=VM_GLOBALMEMADDR/2) + break; + while (SrcPos<DataSize) + { + byte CurByte=Mem[SrcPos++]; + if (CurByte==2 && (CurByte=Mem[SrcPos++])!=2) + CurByte-=32; + Mem[DestPos++]=CurByte; + } + SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x1c],DestPos-DataSize); + SET_VALUE(false,&Mem[VM_GLOBALMEMADDR+0x20],DataSize); + } + break; + } +} + + +uint RarVM::FilterItanium_GetBits(byte *Data,int BitPos,int BitCount) +{ + int InAddr=BitPos/8; + int InBit=BitPos&7; + uint BitField=(uint)Data[InAddr++]; + BitField|=(uint)Data[InAddr++] << 8; + BitField|=(uint)Data[InAddr++] << 16; + BitField|=(uint)Data[InAddr] << 24; + BitField >>= InBit; + return(BitField & (0xffffffff>>(32-BitCount))); +} + + +void RarVM::FilterItanium_SetBits(byte *Data,uint BitField,int BitPos,int BitCount) +{ + int InAddr=BitPos/8; + int InBit=BitPos&7; + uint AndMask=0xffffffff>>(32-BitCount); + AndMask=~(AndMask<<InBit); + + BitField<<=InBit; + + for (uint I=0;I<4;I++) + { + Data[InAddr+I]&=AndMask; + Data[InAddr+I]|=BitField; + AndMask=(AndMask>>8)|0xff000000; + BitField>>=8; + } +} +#endif |