public DiagService(BinaryReader reader, CTFLanguage language, long baseAddress, int poolIndex, ECU parentEcu) { ParentECU = parentEcu; PoolIndex = poolIndex; BaseAddress = baseAddress; reader.BaseStream.Seek(baseAddress, SeekOrigin.Begin); ulong bitflags = reader.ReadUInt32(); ulong bitflagExtended = reader.ReadUInt32(); Qualifier = CaesarReader.ReadBitflagStringWithReader(ref bitflags, reader, baseAddress); Name_CTF = CaesarReader.ReadBitflagInt32(ref bitflags, reader, -1); Description_CTF = CaesarReader.ReadBitflagInt32(ref bitflags, reader, -1); DataClass_ServiceType = CaesarReader.ReadBitflagUInt16(ref bitflags, reader); DataClass_ServiceTypeShifted = 1 << (DataClass_ServiceType - 1); IsExecutable = CaesarReader.ReadBitflagUInt16(ref bitflags, reader);; ClientAccessLevel = CaesarReader.ReadBitflagUInt16(ref bitflags, reader);; SecurityAccessLevel = CaesarReader.ReadBitflagUInt16(ref bitflags, reader);; T_ComParam_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); T_ComParam_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Q_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Q_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); R_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); R_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); InputRefNameMaybe = CaesarReader.ReadBitflagStringWithReader(ref bitflags, reader, baseAddress); U_prep_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); U_prep_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); // array of DWORDs, probably reference to elsewhere V_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); V_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); RequestBytes_Count = CaesarReader.ReadBitflagInt16(ref bitflags, reader); RequestBytes_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); W_OutPres_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); W_OutPres_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); field50 = CaesarReader.ReadBitflagUInt16(ref bitflags, reader); NegativeResponseName = CaesarReader.ReadBitflagStringWithReader(ref bitflags, reader, baseAddress); // negative response name UnkStr3 = CaesarReader.ReadBitflagStringWithReader(ref bitflags, reader, baseAddress); UnkStr4 = CaesarReader.ReadBitflagStringWithReader(ref bitflags, reader, baseAddress); P_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); P_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); DiagServiceCodeCount = CaesarReader.ReadBitflagInt32(ref bitflags, reader); DiagServiceCodeOffset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); S_Count = CaesarReader.ReadBitflagInt16(ref bitflags, reader); S_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); bitflags = bitflagExtended; X_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); X_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Y_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Y_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Z_Count = CaesarReader.ReadBitflagInt32(ref bitflags, reader); Z_Offset = CaesarReader.ReadBitflagInt32(ref bitflags, reader); if (RequestBytes_Count > 0) { reader.BaseStream.Seek(baseAddress + RequestBytes_Offset, SeekOrigin.Begin); RequestBytes = reader.ReadBytes(RequestBytes_Count); } else { RequestBytes = new byte[] { }; } // u_table to u_entries InputPreparations = new List <DiagPreparation>(); for (int prepIndex = 0; prepIndex < U_prep_Count; prepIndex++) { long presentationTableOffset = baseAddress + U_prep_Offset; reader.BaseStream.Seek(presentationTableOffset + (prepIndex * 10), SeekOrigin.Begin); // DIOpenDiagService (reads 4, 4, 2 then calls DiagServiceReadPresentation) to build a presentation int prepEntryOffset = reader.ReadInt32(); // file: 0 (DW) int prepEntryBitPos = reader.ReadInt32(); // file: 4 (DW) ushort prepEntryMode = reader.ReadUInt16(); // file: 8 (W) DiagPreparation preparation = new DiagPreparation(reader, language, presentationTableOffset + prepEntryOffset, prepEntryBitPos, prepEntryMode, parentEcu, this); //preparation.PrintDebug(); InputPreparations.Add(preparation); } OutputPreparations = new List <List <DiagPreparation> >(); long outPresBaseAddress = BaseAddress + W_OutPres_Offset; for (int presIndex = 0; presIndex < W_OutPres_Count; presIndex++) { reader.BaseStream.Seek(outPresBaseAddress + (presIndex * 8), SeekOrigin.Begin); int resultPresentationCount = reader.ReadInt32(); int resultPresentationOffset = reader.ReadInt32(); List <DiagPreparation> ResultPresentationSet = new List <DiagPreparation>(); for (int presInnerIndex = 0; presInnerIndex < resultPresentationCount; presInnerIndex++) { long presentationTableOffset = outPresBaseAddress + resultPresentationOffset; reader.BaseStream.Seek(presentationTableOffset + (presIndex * 10), SeekOrigin.Begin); int prepEntryOffset = reader.ReadInt32(); // file: 0 (DW) int prepEntryBitPos = reader.ReadInt32(); // file: 4 (DW) ushort prepEntryMode = reader.ReadUInt16(); // file: 8 (W) DiagPreparation preparation = new DiagPreparation(reader, language, presentationTableOffset + prepEntryOffset, prepEntryBitPos, prepEntryMode, parentEcu, this); ResultPresentationSet.Add(preparation); } OutputPreparations.Add(ResultPresentationSet); } DiagComParameters = new List <ComParameter>(); long comParamTableBaseAddress = BaseAddress + T_ComParam_Offset; for (int cpIndex = 0; cpIndex < T_ComParam_Count; cpIndex++) { reader.BaseStream.Seek(comParamTableBaseAddress + (cpIndex * 4), SeekOrigin.Begin); int resultCpOffset = reader.ReadInt32(); long cpEntryBaseAddress = comParamTableBaseAddress + resultCpOffset; ComParameter cp = new ComParameter(reader, cpEntryBaseAddress, parentEcu.ECUInterfaces); DiagComParameters.Add(cp); } // DJ_Zugriffsberechtigung_Abgleich // DJ_Zugriffsberechtigung // DT_Abgasklappe_kontinuierlich // FN_HardReset // WVC_Implizite_Variantenkodierung_Write // NR_Disable_Resp_required noexec // DT_Laufzeiten_Resetzaehler_nicht_implementiert exec /* * if (false && qualifierName.Contains("RVC_SCN_Variantencodierung_VGS_73_Lesen")) * { * * Console.WriteLine($"{nameof(field50)} : {field50}"); * Console.WriteLine($"{nameof(IsExecutable)} : {IsExecutable} {IsExecutable != 0}"); * Console.WriteLine($"{nameof(AccessLevel)} : {AccessLevel}"); * Console.WriteLine($"{nameof(SecurityAccessLevel)} : {SecurityAccessLevel}"); * Console.WriteLine($"{nameof(DataClass)} : {DataClass}"); * * * * Console.WriteLine($"{qualifierName} - ReqBytes: {RequestBytes_Count}, P: {P_Count}, Q: {Q_Count}, R: {R_Count}, S: {S_Count}, T: {T_Count}, Preparation: {U_prep_Count}, V: {V_Count}, W: {W_Count}, X: {X_Count}, Y: {Y_Count}, Z: {Z_Count}, DSC {DiagServiceCodeCount}"); * Console.WriteLine($"at 0x{baseAddress:X}, W @ 0x{W_Offset:X}, DSC @ 0x{DiagServiceCodeOffset:X}"); * Console.WriteLine($"ReqBytes: {BitUtility.BytesToHex(RequestBytes)}"); * } */ //Console.WriteLine($"{qualifierName} - O: {RequestBytes_Count}, P: {P_Count}, Q: {Q_Count}, R: {R_Count}, S: {S_Count}, T: {T_Count}, U: {U_Count}, V: {V_Count}, W: {W_Count}, X: {X_Count}, Y: {Y_Count}, Z: {Z_Count}, DSC {DiagServiceCodeCount}"); byte[] dscPool = parentEcu.ParentContainer.CaesarCFFHeader.DSCPool; long dscTableBaseAddress = BaseAddress + DiagServiceCodeOffset; using (BinaryReader dscPoolReader = new BinaryReader(new MemoryStream(dscPool))) { for (int dscIndex = 0; dscIndex < DiagServiceCodeCount; dscIndex++) { reader.BaseStream.Seek(dscTableBaseAddress + (4 * dscIndex), SeekOrigin.Begin); long dscEntryBaseAddress = reader.ReadInt32() + dscTableBaseAddress; reader.BaseStream.Seek(dscEntryBaseAddress, SeekOrigin.Begin); ulong dscEntryBitflags = reader.ReadUInt16(); uint idk1 = CaesarReader.ReadBitflagUInt8(ref dscEntryBitflags, reader); uint idk2 = CaesarReader.ReadBitflagUInt8(ref dscEntryBitflags, reader); int dscPoolOffset = CaesarReader.ReadBitflagInt32(ref dscEntryBitflags, reader); string dscQualifier = CaesarReader.ReadBitflagStringWithReader(ref dscEntryBitflags, reader, dscEntryBaseAddress); dscPoolReader.BaseStream.Seek(dscPoolOffset * 8, SeekOrigin.Begin); long dscRecordOffset = dscPoolReader.ReadInt32() + parentEcu.ParentContainer.CaesarCFFHeader.DscBlockOffset; int dscRecordSize = dscPoolReader.ReadInt32(); reader.BaseStream.Seek(dscRecordOffset, SeekOrigin.Begin); // Console.WriteLine($"DSC {qualifierName} @ 0x{dscTableBaseAddress:X8} {idk1}/{idk2} pool @ 0x{dscPoolOffset:X}, name: {dscQualifier}"); byte[] dscBytes = reader.ReadBytes(dscRecordSize); #if DEBUG //string dscName = $"{parentEcu.Qualifier}_{Qualifier}_{dscIndex}.pal"; //Console.WriteLine($"Exporting DSC: {dscName}"); //File.WriteAllBytes(dscName, dscBytes); #endif // at this point, the DSC binary is available in dscBytes, intended for use in DSCContext (but is currently unimplemented) // Console.WriteLine($"DSC actual at 0x{dscRecordOffset:X}, size=0x{dscRecordSize:X}\n"); } } }
public string InterpretData(byte[] inBytes, DiagPreparation inPreparation) { // might be relevant: DMPrepareSingleDatum, DMPresentSingleDatum byte[] workingBytes = inBytes.Skip(inPreparation.BitPosition / 8).Take(TypeLength_1a).ToArray(); if (workingBytes.Length != TypeLength_1a) { return($"InBytes [{BitUtility.BytesToHex(workingBytes)}] length mismatch (expecting {TypeLength_1a})"); } // handle booleans first since they're the edge case where they can cross byte boundaries if (inPreparation.SizeInBits == 1) { int bytesToSkip = (int)(inPreparation.BitPosition / 8); int bitsToSkip = inPreparation.BitPosition % 8; byte selectedByte = inBytes[bytesToSkip]; int selectedBit = (selectedByte >> bitsToSkip) & 1; return($"{DescriptionString}: {selectedBit} {DisplayedUnitString} (BitType)"); } // everything else should be aligned to byte boundaries if (inPreparation.BitPosition % 8 != 0) { return("BitOffset was outside byte boundary (skipped)"); } int dataType = GetDataType(); string humanReadableType = $"UnhandledType:{dataType}"; string parsedValue = BitUtility.BytesToHex(workingBytes, true); if ((dataType == 6 || (dataType == 20))) { // parse as a regular int (BE) int result = 0; for (int i = 0; i < workingBytes.Length; i++) { result <<= 8; result |= workingBytes[i]; } humanReadableType = "IntegerType"; parsedValue = result.ToString(); if (dataType == 20) { humanReadableType = "ScaledType"; double valueToScale = result; foreach (Scale scale in Scales) { valueToScale *= scale.MultiplyFactor; valueToScale += scale.AddConstOffset; } parsedValue = valueToScale.ToString("0.000000"); } } else if (dataType == 18) { humanReadableType = "HexdumpType"; } else if (dataType == 17) { humanReadableType = "StringType"; parsedValue = Encoding.UTF8.GetString(workingBytes); } return($"{DescriptionString}: {parsedValue} {DisplayedUnitString} ({humanReadableType})"); }
public string InterpretData(byte[] inBytes, DiagPreparation inPreparation, bool describe = true) { // might be relevant: DMPrepareSingleDatum, DMPresentSingleDatum bool isDebugBuild = false; #if DEBUG isDebugBuild = true; #endif string descriptionPrefix = describe ? $"{DescriptionString}: " : ""; byte[] workingBytes = inBytes.Skip(inPreparation.BitPosition / 8).Take(TypeLength_1A).ToArray(); bool isEnumType = (EnumType_1E == 0) && ((Type_1C == 1) || (ScaleCountMaybe > 1)); // hack: sometimes hybrid types (regularly parsed as an scaled value if within bounds) are misinterpreted as pure enums // this is a temporary fix for kilometerstand until there's a better way to ascertain its type // this also won't work on other similar cases without a unit string e.g. error instance counter (Häufigkeitszähler) if (DisplayedUnitString == "km") { isEnumType = false; } if (workingBytes.Length != TypeLength_1A) { return($"InBytes [{BitUtility.BytesToHex(workingBytes)}] length mismatch (expecting {TypeLength_1A})"); } // handle booleans first since they're the edge case where they can cross byte boundaries if (inPreparation.SizeInBits == 1) { int bytesToSkip = (int)(inPreparation.BitPosition / 8); int bitsToSkip = inPreparation.BitPosition % 8; byte selectedByte = inBytes[bytesToSkip]; int selectedBit = (selectedByte >> bitsToSkip) & 1; if (isEnumType && (Scales.Count > selectedBit)) { return($"{descriptionPrefix}{Language.GetString(Scales[selectedBit].EnumDescription)} {DisplayedUnitString}"); } else { return($"{descriptionPrefix}{selectedBit} {DisplayedUnitString}"); } } // everything else should be aligned to byte boundaries if (inPreparation.BitPosition % 8 != 0) { return("BitOffset was outside byte boundary (skipped)"); } int dataType = GetDataType(); int rawIntInterpretation = 0; string humanReadableType = $"UnhandledType:{dataType}"; string parsedValue = BitUtility.BytesToHex(workingBytes, true); if ((dataType == 6 || (dataType == 20))) { // parse as a regular int (BE) for (int i = 0; i < workingBytes.Length; i++) { rawIntInterpretation <<= 8; rawIntInterpretation |= workingBytes[i]; } humanReadableType = "IntegerType"; parsedValue = rawIntInterpretation.ToString(); if (dataType == 20) { humanReadableType = "ScaledType"; double valueToScale = rawIntInterpretation; // if there's only one scale, use it as-is // if there's more than one, use the first scale as an interim solution; // the results of stacking scales does not make sense // there might be a better, non-hardcoded (0) solution to this, and perhaps with a sig-fig specifier valueToScale *= Scales[0].MultiplyFactor; valueToScale += Scales[0].AddConstOffset; parsedValue = valueToScale.ToString("0.000000"); } } else if (dataType == 18) { humanReadableType = "HexdumpType"; } else if (dataType == 17) { humanReadableType = "StringType"; parsedValue = Encoding.UTF8.GetString(workingBytes); } if (isEnumType) { // discovered by @VladLupashevskyi in https://github.com/jglim/CaesarSuite/issues/27 // if an enum is specified, the inclusive upper bound and lower bound will be defined in the scale object bool useNewInterpretation = false; foreach (Scale scale in Scales) { if ((scale.EnumUpBound > 0) || (scale.EnumLowBound > 0)) { useNewInterpretation = true; break; } } if (useNewInterpretation) { foreach (Scale scale in Scales) { if ((rawIntInterpretation >= scale.EnumLowBound) && (rawIntInterpretation <= scale.EnumUpBound)) { return($"{descriptionPrefix}{Language.GetString(scale.EnumDescription)} {DisplayedUnitString}"); } } } else { // original implementation, probably incorrect if (rawIntInterpretation < Scales.Count) { return($"{descriptionPrefix}{Language.GetString(Scales[rawIntInterpretation].EnumDescription)} {DisplayedUnitString}"); } } return($"{descriptionPrefix}(Enum not found) {DisplayedUnitString}"); // this bit below for troubleshooting problematic presentations /* * if (rawIntInterpretation < Scales.Count) * { * return $"{descriptionPrefix}{Language.GetString(Scales[rawIntInterpretation].EnumDescription)} {DisplayedUnitString}"; * } * else * { * // seems like an enum-like value broke * return $"{descriptionPrefix}{Language.GetString(Scales[0].EnumDescription)} {DisplayedUnitString} [!]"; * } */ } else { if (isDebugBuild) { return($"{descriptionPrefix}{parsedValue} {DisplayedUnitString} ({humanReadableType})"); } else { return($"{descriptionPrefix}{parsedValue} {DisplayedUnitString}"); } } }
public string InterpretData(byte[] inBytes, DiagPreparation inPreparation, bool describe = true) { // might be relevant: DMPrepareSingleDatum, DMPresentSingleDatum bool isDebugBuild = false; #if DEBUG isDebugBuild = true; #endif string descriptionPrefix = describe ? $"{DescriptionString}: " : ""; byte[] workingBytes = inBytes.Skip(inPreparation.BitPosition / 8).Take(TypeLength_1A).ToArray(); bool isEnumType = (EnumType_1E == 0) && ((Type_1C == 1) || (ScaleCountMaybe > 1)); if (workingBytes.Length != TypeLength_1A) { return($"InBytes [{BitUtility.BytesToHex(workingBytes)}] length mismatch (expecting {TypeLength_1A})"); } // handle booleans first since they're the edge case where they can cross byte boundaries if (inPreparation.SizeInBits == 1) { int bytesToSkip = (int)(inPreparation.BitPosition / 8); int bitsToSkip = inPreparation.BitPosition % 8; byte selectedByte = inBytes[bytesToSkip]; int selectedBit = (selectedByte >> bitsToSkip) & 1; if (isEnumType && (Scales.Count > selectedBit)) { return($"{descriptionPrefix}{Language.GetString(Scales[selectedBit].EnumDescription)} {DisplayedUnitString}"); } else { return($"{descriptionPrefix}{selectedBit} {DisplayedUnitString}"); } } // everything else should be aligned to byte boundaries if (inPreparation.BitPosition % 8 != 0) { return("BitOffset was outside byte boundary (skipped)"); } int dataType = GetDataType(); int rawIntInterpretation = 0; string humanReadableType = $"UnhandledType:{dataType}"; string parsedValue = BitUtility.BytesToHex(workingBytes, true); if ((dataType == 6 || (dataType == 20))) { // parse as a regular int (BE) for (int i = 0; i < workingBytes.Length; i++) { rawIntInterpretation <<= 8; rawIntInterpretation |= workingBytes[i]; } humanReadableType = "IntegerType"; parsedValue = rawIntInterpretation.ToString(); if (dataType == 20) { humanReadableType = "ScaledType"; double valueToScale = rawIntInterpretation; // if there's only one scale, use it as-is // if there's more than one, use the first scale as an interim solution; // the results of stacking scales does not make sense // there might be a better, non-hardcoded (0) solution to this, and perhaps with a sig-fig specifier valueToScale *= Scales[0].MultiplyFactor; valueToScale += Scales[0].AddConstOffset; parsedValue = valueToScale.ToString("0.000000"); } } else if (dataType == 18) { humanReadableType = "HexdumpType"; } else if (dataType == 17) { humanReadableType = "StringType"; parsedValue = Encoding.UTF8.GetString(workingBytes); } if (isEnumType && (rawIntInterpretation < Scales.Count)) { return($"{descriptionPrefix}{Language.GetString(Scales[rawIntInterpretation].EnumDescription)} {DisplayedUnitString}"); // this bit below for troubleshooting problematic presentations /* * if (rawIntInterpretation < Scales.Count) * { * return $"{descriptionPrefix}{Language.GetString(Scales[rawIntInterpretation].EnumDescription)} {DisplayedUnitString}"; * } * else * { * // seems like an enum-like value broke * return $"{descriptionPrefix}{Language.GetString(Scales[0].EnumDescription)} {DisplayedUnitString} [!]"; * } */ } else { if (isDebugBuild) { return($"{descriptionPrefix}{parsedValue} {DisplayedUnitString} ({humanReadableType})"); } else { return($"{descriptionPrefix}{parsedValue} {DisplayedUnitString}"); } } }