public override ScriptOp LoadOperation(int offset) { Reader.BaseStream.Position = offset; var opCodeIndex = Reader.ReadByte(); // 0x7B is the literal highest for Bo2 if (opCodeIndex > 0x7B) { return(null); } ScriptOp operation = new ScriptOp() { Metadata = ScriptOpMetadata.OperationInfo[opCodeIndex], OpCodeOffset = (int)Reader.BaseStream.Position - 1, }; // Use a type rather than large switch for each operation // so we can easily fix bugs and adjust across multiple op codes switch (operation.Metadata.OperandType) { case ScriptOperandType.None: { break; } case ScriptOperandType.Int8: { operation.Operands.Add(new ScriptOpOperand(Reader.ReadSByte())); break; } case ScriptOperandType.UInt8: { if (operation.Metadata.OpCode == ScriptOpCode.GetNegByte) { operation.Operands.Add(new ScriptOpOperand(-Reader.ReadByte())); } else { operation.Operands.Add(new ScriptOpOperand(Reader.ReadByte())); } break; } case ScriptOperandType.Int16: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand(Reader.ReadInt16())); break; } case ScriptOperandType.UInt16: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); if (operation.Metadata.OpCode == ScriptOpCode.GetNegUnsignedShort) { operation.Operands.Add(new ScriptOpOperand(-Reader.ReadUInt16())); } else { operation.Operands.Add(new ScriptOpOperand(Reader.ReadUInt16())); } break; } case ScriptOperandType.Int32: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadInt32())); break; } case ScriptOperandType.UInt32: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadUInt32())); break; } case ScriptOperandType.Hash: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand("\"" + GetHashValue(Reader.ReadUInt32(), "hash_") + "\"")); break; } case ScriptOperandType.Float: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadSingle())); break; } case ScriptOperandType.Vector: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle())); break; } case ScriptOperandType.VectorFlags: { var flags = Reader.ReadByte(); // Set each flag, it's either 1.0, -1.0, or simply 0.0 operation.Operands.Add(new ScriptOpOperand( string.Format("({0}, {1}, {2})", (flags & 0x20) != 0 ? 1.0f : (flags & 0x10) != 0 ? -1.0f : 0.0f, (flags & 0x08) != 0 ? 1.0f : (flags & 0x04) != 0 ? -1.0f : 0.0f, (flags & 0x02) != 0 ? 1.0f : (flags & 0x01) != 0 ? -1.0f : 0.0f))); break; } case ScriptOperandType.String: { // If it's anim animation, etc. we can just read at the location, but for strings // we can just grab via pointer switch (operation.Metadata.OpCode) { case ScriptOpCode.GetString: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand("\"" + GetString((int)Reader.BaseStream.Position).Value + "\"")); Reader.BaseStream.Position += 2; break; case ScriptOpCode.GetIString: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand("&\"" + GetString((int)Reader.BaseStream.Position).Value + "\"")); Reader.BaseStream.Position += 2; break; default: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand("%" + Reader.PeekNullTerminatedString(Reader.ReadInt32()))); break; } break; } case ScriptOperandType.VariableName: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand(Reader.PeekNullTerminatedString(Reader.ReadUInt16()))); break; } case ScriptOperandType.FunctionPointer: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.PeekNullTerminatedString(Reader.ReadInt32()))); break; } case ScriptOperandType.Call: { // Skip param count, it isn't stored here until in memory Reader.BaseStream.Position += 1; try { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.PeekNullTerminatedString(Reader.ReadInt32()))); } catch { return(null); } break; } case ScriptOperandType.VariableList: { var varCount = Reader.ReadByte(); for (int i = 0; i < varCount; i++) { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand(Reader.PeekNullTerminatedString(Reader.ReadUInt16()))); } break; } case ScriptOperandType.SwitchEnd: { var switches = LoadEndSwitch(); foreach (var switchBlock in switches) { operation.Operands.Add(new ScriptOpOperand(switchBlock)); } break; } default: { throw new ArgumentException("Invalid Op Type", "OpType"); } } operation.OpCodeSize = (int)Reader.BaseStream.Position - offset; return(operation); }
public override ScriptOp LoadOperation(int offset) { Reader.BaseStream.Position = offset; var opCodeIndex = Reader.ReadUInt16(); ScriptOp operation; // This is literally close to how Black Ops 3 handles it // as in, the value is literally an index if ((opCodeIndex & 0x2000) == 0) { if (opCodeIndex > 0x4000) { return(null); } var opCode = OpCodeTable[opCodeIndex]; if (opCode == ScriptOpCode.Invalid) { return(null); } operation = new ScriptOp() { Metadata = ScriptOpMetadata.OperationInfo[(int)opCode], OpCodeOffset = (int)Reader.BaseStream.Position - 2, }; } else { throw new ArgumentException("Invalid Op Code"); } // Use a type rather than large switch for each operation // so we can easily fix bugs and adjust across multiple op codes // Like Black Ops 2 all are aligned to different values switch (operation.Metadata.OperandType) { case ScriptOperandType.None: { break; } case ScriptOperandType.Int8: { operation.Operands.Add(new ScriptOpOperand(Reader.ReadSByte())); break; } case ScriptOperandType.UInt8: { if (operation.Metadata.OpCode == ScriptOpCode.GetNegByte) { operation.Operands.Add(new ScriptOpOperand(-Reader.ReadByte())); } else { operation.Operands.Add(new ScriptOpOperand(Reader.ReadByte())); } break; } case ScriptOperandType.Int16: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.Operands.Add(new ScriptOpOperand(Reader.ReadInt16())); break; } case ScriptOperandType.UInt16: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); if (operation.Metadata.OpCode == ScriptOpCode.GetNegUnsignedShort) { operation.Operands.Add(new ScriptOpOperand(-Reader.ReadUInt16())); } else { operation.Operands.Add(new ScriptOpOperand(Reader.ReadUInt16())); } break; } case ScriptOperandType.Int32: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadInt32())); break; } case ScriptOperandType.UInt32: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadUInt32())); break; } case ScriptOperandType.Hash: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand("\"" + GetHashValue(Reader.ReadUInt32(), "hash_") + "\"")); break; } case ScriptOperandType.Float: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadSingle())); break; } case ScriptOperandType.Vector: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(Reader.ReadSingle())); break; } case ScriptOperandType.VectorFlags: { var flags = Reader.ReadByte(); // Set each flag, it's either 1.0, -1.0, or simply 0.0 operation.Operands.Add(new ScriptOpOperand( string.Format("({0}, {1}, {2})", (flags & 0x20) != 0 ? 1.0f : (flags & 0x10) != 0 ? -1.0f : 0.0f, (flags & 0x08) != 0 ? 1.0f : (flags & 0x04) != 0 ? -1.0f : 0.0f, (flags & 0x02) != 0 ? 1.0f : (flags & 0x01) != 0 ? -1.0f : 0.0f))); break; } case ScriptOperandType.String: { // If it's anim animation, etc. we can just read at the location, but for strings // we can just grab via pointer switch (operation.Metadata.OpCode) { case ScriptOpCode.GetString: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand("\"" + GetString((int)Reader.BaseStream.Position)?.Value + "\"")); Reader.BaseStream.Position += 4; break; case ScriptOpCode.GetIString: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand("&\"" + GetString((int)Reader.BaseStream.Position)?.Value + "\"")); Reader.BaseStream.Position += 4; break; case ScriptOpCode.GetAnimation: Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 8); operation.Operands.Add(new ScriptOpOperand("%" + Reader.PeekNullTerminatedString(Reader.ReadInt32()))); Reader.BaseStream.Position += 4; break; } break; } case ScriptOperandType.VariableName: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); var name = GetHashValue(Reader.ReadUInt32(), "var_"); operation.Operands.Add(new ScriptOpOperand(name)); break; } case ScriptOperandType.FunctionPointer: { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 8); operation.Operands.Add(new ScriptOpOperand("&" + GetHashValue(Reader.ReadUInt32(), "function_"))); Reader.BaseStream.Position += 4; break; } case ScriptOperandType.Call: { if (operation.Metadata.OpCode == ScriptOpCode.ClassFunctionCall) { var paramterCount = Reader.ReadByte(); Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(GetHashValue(Reader.ReadUInt32(), "function_"))); operation.Operands.Add(new ScriptOpOperand(paramterCount)); } else { // Skip param count, it isn't stored here until in memory Reader.BaseStream.Position += 1; Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 8); operation.Operands.Add(new ScriptOpOperand(GetHashValue(Reader.ReadUInt32(), "function_"))); Reader.BaseStream.Position += 4; } break; } case ScriptOperandType.VariableList: { var varCount = Reader.ReadByte(); for (int i = 0; i < varCount; i++) { Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 4); operation.Operands.Add(new ScriptOpOperand(GetHashValue(Reader.ReadUInt32(), "var_"))); Reader.BaseStream.Position += 1; } break; } case ScriptOperandType.SwitchEnd: { var switches = LoadEndSwitch(); foreach (var switchBlock in switches) { operation.Operands.Add(new ScriptOpOperand(switchBlock)); } break; } default: { throw new ArgumentException("Invalid Op Type", "OpType"); } } // Ensure we're at the next op, all operations are aligned to 2 bytes Reader.BaseStream.Position += Utility.ComputePadding((int)Reader.BaseStream.Position, 2); operation.OpCodeSize = (int)Reader.BaseStream.Position - offset; return(operation); }