// ********************************************************************************************************** // BytePackingXml.cs // 5/18/2022 // NGI - Next Generation Interceptor // // Contract No. HQ0856-21-C-0003/1022000209 // // THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S. // INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS. // // RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON // COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. // DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON // COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED // TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF // RAYTHEON COMPANY. // // UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY. // // DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M, // NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006, // INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3, // DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3, // SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4, // INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION. // // CONTROLLED BY: MISSILE DEFENSE AGENCY // CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE // CUI CATEGORY: CTI // DISTRIBUTION/DISSEMINATION CONTROL: F // POC: Alex Kravchenko (1118268) // ********************************************************************************************************** using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Text.RegularExpressions; using System.Xml; using System.Xml.XPath; namespace Raytheon.Common.Coe { // Takes an XML ICD and adds in nodes to represent the byte packing // The padding added will be displayed in the tool as arrays of characters internal class BytePackingXml : XmlDocument { private readonly int m_Packing = 0; private readonly Dictionary m_ConstMap; private readonly Dictionary m_SizeMap; private readonly Dictionary m_PadMap; public BytePackingXml(string icdStr) : base() { // Create the dictionaries m_ConstMap = new Dictionary(); m_PadMap = new Dictionary(); m_SizeMap = new Dictionary(); // Create an XML document from the string LoadXml(icdStr); XPathNavigator node = CreateNavigator(); //Get the type of packing XPathNodeIterator nodeset = node.Select("/interface/packing"); if (nodeset.MoveNext() == true) { if (!Parse.Try(nodeset.Current.Value.Trim(), out m_Packing)) { switch (nodeset.Current.Value.Trim()) { case "char": case "1": m_Packing = 1; break; case "short": case "2": m_Packing = 2; break; case "long": case "4": m_Packing = 4; break; case "double": case "8": default: m_Packing = 8; break; } } } // Handle all of the constants nodeset = node.Select("/interface/constant|/interface/namespace/constant"); while (nodeset.MoveNext()) { ProcessConstantNode(nodeset.Current); } nodeset = node.Select("/interface/typedef|/interface/namespace/typedef"); while (nodeset.MoveNext()) { ProcessTypedefNode(nodeset.Current); } nodeset = node.Select("/interface/enum|/interface/namespace/enum"); while (nodeset.MoveNext()) { ProcessEnumerationNode(nodeset.Current); } nodeset = node.Select("/interface/structure|/interface/message|/interface/namespace/structure|/interface/namespace/message"); while (nodeset.MoveNext()) { ProcessStructureNode(nodeset.Current); } NormalizeIcdLabels(); } // This function takes all of the messages in the ICD // and converts the labels into decimal, so when we do // a string lookup, they will all be in the same known format private void NormalizeIcdLabels() { XPathNavigator navigator = CreateNavigator(); XPathNodeIterator nodeset; nodeset = navigator.Select("/interface/message/label|/interface/namespace/message/label"); while (nodeset.MoveNext()) { try { double dLabel = GetConstFromString(nodeset.Current.Value); nodeset.Current.SetValue(((int)dLabel).ToString()); } catch { throw new Exception("Message Label, " + nodeset.Current.Value + ", can not be converted to an integer"); } } } private void ProcessConstantNode(XPathNavigator node) { string name = ""; string constStr = ""; double constNum = 0; if (node.MoveToChild("name", "")) { name = node.Value.Trim(); node.MoveToParent(); } if (node.MoveToChild("value", "")) { constStr = node.Value.Trim(); if ((constStr.Length != 0) && (constStr[0] != '\"')) { constNum = GetConstFromString(constStr); } else { constNum = 0; } node.MoveToParent(); } // Verify the correctnes of the tag if ((name != "") && (constStr != "")) { AddItemToMap(m_ConstMap, name, constNum); } else { throw new Exception( "ERROR: Constant Definition Incorrect - :" + name + " :" + constStr); } } private void ProcessTypedefNode(XPathNavigator node) { string name = ""; string type = ""; int typeSize = 0; // Size of the item int typePad = 0; //Size of the largest item to pad to if (node.MoveToChild("name", "")) { name = node.Value.Trim(); node.MoveToParent(); } if (node.MoveToChild("value", "")) { type = node.Value.Trim(); GetSizeFromType(type, out typeSize, out typePad); node.MoveToParent(); } // Verify the correctnes of the tag if ((name != "") && (type != "")) { AddItemToMap(m_PadMap, name, typePad); AddItemToMap(m_SizeMap, name, typeSize); } else { throw new Exception( "ERROR: Typedef Definition Incorrect - :" + name + " :" + type); } } private void ProcessEnumerationNode(XPathNavigator node) { string name; double constNum = 0; var constStr = string.Empty; if (node.MoveToChild("name", "")) { name = node.Value.Trim(); AddItemToMap(m_PadMap, name, 4); AddItemToMap(m_SizeMap, name, 4); node.MoveToParent(); } else { throw new Exception("ERROR: Enumeration Definition Incorrect. No tag present."); } XPathNodeIterator nodeSet = node.Select("item|enum_item"); while (nodeSet.MoveNext()) { name = string.Empty; if ((nodeSet.Current.MoveToChild("name", "")) || (nodeSet.Current.MoveToChild("item_name", ""))) { name = nodeSet.Current.Value.Trim(); nodeSet.Current.MoveToParent(); } if (nodeSet.Current.MoveToChild("value", "")) { constStr = nodeSet.Current.Value.Trim(); constNum = GetConstFromString(constStr); nodeSet.Current.MoveToParent(); } // Verify the correctnes of the tag if ((name != "") && (constStr != "")) { AddItemToMap(m_ConstMap, name, constNum); } else { throw new Exception($"ERROR: Enumeration Item Definition Incorrect - : {name} : {constStr}"); } } } private void ProcessStructureNode(XPathNavigator node) { string name; if (node.MoveToChild("name", "")) { name = node.Value.Trim(); node.MoveToParent(); } else { throw new Exception("ERROR: Stucture/Message Definition Incorrect. No tag present."); } int maxSize = 0; int padCount = 0; uint bitCount = 0; // Used to see how many bits have been processed. int lastItemSize = 0; var nodeSet = node.Select("item|struct_item|msg_item"); while (nodeSet.MoveNext()) { GetItemSize(nodeSet.Current, out int padSize, out int itemSize, out int reps, out uint bits); if ((lastItemSize != itemSize) || ((bitCount + bits) > (uint)(itemSize * 8))) { bitCount = 0; // Size changed or bit rollover } if (bitCount == 0) { padCount += AddPadding(node, nodeSet.Current, padSize, padCount); // Set maxSize if (padSize > maxSize) { maxSize = padSize; } // Keep up with the pad count padCount += (itemSize * reps); } lastItemSize = itemSize; bitCount += bits; } if (maxSize != 0) { // Add final padding padCount += AddPadding(node, null, maxSize, padCount); } AddItemToMap(m_PadMap, name, maxSize); AddItemToMap(m_SizeMap, name, padCount); } private void GetItemSize(XPathNavigator node, out int padSize, out int itemSize, out int reps, out uint bits) { string name = ""; if ((node.MoveToChild("name", "")) || (node.MoveToChild("item_name", ""))) { name = node.Value.Trim(); node.MoveToParent(); } itemSize = -1; padSize = -1; reps = 1; bits = 0; var nodeSet = node.Select("type"); while (nodeSet.MoveNext()) { GetSizeFromType(nodeSet.Current.Value.Trim(), out padSize, out itemSize); } nodeSet = node.Select("bits"); if (nodeSet.MoveNext()) { bits = (uint)GetConstFromString(nodeSet.Current.Value.Trim()); } nodeSet = node.Select("arrayLength|imageWidth|imageHeight"); while (nodeSet.MoveNext()) { try { reps *= (int)GetConstFromString(nodeSet.Current.Value.Trim()); } catch (Exception e) { throw new Exception (e.Message + " item name = \"" + name + "\", tag = <" + nodeSet.Current.Name + ">."); } } if ((itemSize == -1) || (padSize == -1)) { throw new Exception ("ERROR: Item named " + name + "does not contain a tag."); } if (bits == 0) bits = (uint)padSize * 8; } private double GetConstFromString(string constStr) { // remove namespace constStr = Regex.Replace(constStr, @"[^:]+::([^:]+)", "$1"); if ((constStr.Length > 0) && (constStr[0] == '\'')) { byte charData = (byte)constStr[1]; constStr = charData.ToString(); } if (Parse.Try(constStr, out double data) == false) { if (Parse.Try(constStr, out int iData) == false) { try { data = m_ConstMap[constStr]; } catch { throw new Exception("ERROR: ConstantValue - \"" + constStr + "\" does not resolve to a number."); } } else { data = (double)iData; } } return data; } private void AddItemToMap(Dictionary map, string name, int value) { if (map.ContainsKey(name)) { throw new Exception("ERROR: Element " + name + " is defined multiple times."); } else { map.Add(name, value); } } private void AddItemToMap(Dictionary map, string name, double value) { if (map.ContainsKey(name)) { throw new Exception("ERROR: Element " + name + " is defined multiple times."); } else { map.Add(name, value); } } private void GetSizeFromType(string type, out int typePad, out int typeSize) { // Remove all whitespace type = type.Replace(" ", ""); // remove namespace type = Regex.Replace(type, @"[^:]+::([^:]+)", "$1"); switch (type) { case "uint8_t": type = "char"; break; case "uint16_t": case "int16_t": type = "short"; break; case "uint32_t": case "int32_t": type = "int"; break; case "uint64_t": case "int64_t": type = "double"; break; } if ((type == "char")) { typePad = 1; typeSize = 1; } else if ((type == "short")) { typePad = 2; typeSize = 2; } else if ((Regex.IsMatch(type, @"unsigned", RegexOptions.IgnoreCase)) || (type == "int") || (type == "float") || (type == "boolean") || (type == "address")) { typePad = 4; typeSize = 4; } else if ((type == "double")) { typePad = 8; typeSize = 8; } else // The type is complex and has already been defined { try { typePad = m_PadMap[type]; typeSize = m_SizeMap[type]; } catch { throw new Exception("ERROR: - " + type + " used without being defined."); } } } private int AddPadding(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padSize, int padCount) { int padAdd = 0; int padTo = padSize; if (m_Packing < padSize) { padTo = m_Packing; } if (m_Packing > 0 && padTo != 0 && (padCount % padTo != 0)) { padAdd = padTo - (padCount % padTo); InsertPaddingNode(ParentElement, CurrentElement, padAdd); } return padAdd; } private void InsertPaddingNode(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padAdd) { string pad = "" + "" + Message.PADDING_ITEM_NAME + "" + "char" + "" + padAdd + "" + "S" + ""; if (CurrentElement != null) { CurrentElement.InsertBefore(pad); } else // End padding { ParentElement.AppendChild(pad); } } } }