using MultiTerm.Core.Types; using System.Text; namespace MultiTerm.Core.Model; /// /// A string that contains symbols of multiple formats such as a standard character as well as hexadecimal or binary parts. /// public class MultiFormatString { /// /// List of Symbols that are stored in this /// public List> FormatValuePairs { get; private set; } = new(); /// /// Adds a single character to . /// /// format of the /// value of the symbol, must be convertible to an Unicode character public void Add(FormatType format, string value) { // check if value is valid, if yes add to FormatValuePairs if (ValidateValue(format, value)) { this.FormatValuePairs.Add(Tuple.Create(format, value)); } } /// /// Removes a given amount of characters from the end of . /// /// amount of characters to remove from the list, starting at the last item /// if amount <= 0 public void Remove(int amount) { // guard amount <= 0 if (amount <= 0) throw new ArgumentException($"{nameof(amount)} cannot be <= 0"); // limit amount to maximum int listCount = this.FormatValuePairs.Count; if (amount > listCount) { amount = listCount; } // remove range this.FormatValuePairs.RemoveRange( (listCount - amount) , amount); } /// /// Converts the to a string that only contains Unicode (UTF-16) characters. /// /// converted string public override string ToString() // TODO Implement Unit Tests! { StringBuilder stringBuilder = new(); string hexConversionCharacters = String.Empty, binaryConversionCharacters = String.Empty; // Internal function to check and finalize a Hexadecimal Conversion // parameter == null will force ending void finalizeHexConversion(Tuple? formatValuePair) { // hex conversion ongoing? if (String.IsNullOrEmpty(hexConversionCharacters) == false) { // ending conversion or reached limit of characters? if (formatValuePair == null || formatValuePair.Item1 != FormatType.Hexadecimal || hexConversionCharacters.Count() == 4) { // finalize conversion stringBuilder.Append(FromHexString(hexConversionCharacters)); // reset ongoing conversion hexConversionCharacters = String.Empty; } } } // Internal function to check and finalize a Binary Conversion // parameter == null will force ending void finalizeBinaryConversion(Tuple? formatValuePair) { // binary conversion ongoing? if (String.IsNullOrEmpty(binaryConversionCharacters) == false) { // ending conversion or reached limit of characters? if (formatValuePair == null || formatValuePair.Item1 != FormatType.Binary || binaryConversionCharacters.Count() == 16) { // finalize conversion stringBuilder.Append(FromBinaryString(binaryConversionCharacters)); // reset ongoing conversion binaryConversionCharacters = String.Empty; } } } // go through every character foreach (var formatValuePair in this.FormatValuePairs) { // finalize ongoing conversions if there are any finalizeHexConversion(formatValuePair); finalizeBinaryConversion(formatValuePair); switch (formatValuePair.Item1) // switch by format { case FormatType.Character: stringBuilder.Append(formatValuePair.Item2); break; case FormatType.Hexadecimal: hexConversionCharacters += formatValuePair.Item2; break; case FormatType.Binary: binaryConversionCharacters += formatValuePair.Item2; break; default: throw new NotImplementedException($"'{nameof(ToString)}()' does not implement conversion for format {formatValuePair.Item1}"); } } // fully finalize after the loop, finish all unfinished conversions finalizeHexConversion(null); finalizeBinaryConversion(null); return stringBuilder.ToString(); } /// /// Validates wether a certain is valid for the given . /// /// true if valid public static bool ValidateValue(FormatType format, string value) { // invalid if more than one character if(value.Count() > 1) { return false; } // extract character var character = value.First(); switch (format) { case FormatType.Character: return true; case FormatType.Hexadecimal: if (character >= '0' && character <= '9' || character >= 'A' && character <= 'F' || character >= 'a' && character <= 'f') { return true; } else { return false; }; case FormatType.Binary: if (character == '0' || character == '1') { return true; } else { return false; }; default: throw new NotImplementedException($"'{nameof(ValidateValue)}()' does not implement validation for format {format}"); } } // from: https://stackoverflow.com/questions/724862/converting-from-hex-to-string // test with: returns: "Hello world" for "48656C6C6F20776F726C64" // see https://www.fileformat.info/info/charset/UTF-16/list.htm private static string FromHexString(string hexString) { // Added PadLeft so strings with one character do not get ignored string internalHexString = hexString.PadLeft(4, '0'); var bytes = new byte[internalHexString.Length / 2]; for (var i = 0; i < bytes.Length; i++) { bytes[i] = Convert.ToByte(internalHexString.Substring(i * 2, 2), 16); } return Encoding.BigEndianUnicode.GetString(bytes); } private static string FromBinaryString(string binaryString) { string internalBinaryString = binaryString.PadLeft(16, '0'); var bytes = new byte[internalBinaryString.Length / 8]; for (var i = 0; i < bytes.Length; i++) { bytes[i] = Convert.ToByte(internalBinaryString.Substring(i * 8, 8), 2); } return Encoding.BigEndianUnicode.GetString(bytes); } }