/// <summary> /// Computes the number of lines of output required to hold the formatted output. /// </summary> /// <param name="formatter">Format definition.</param> /// <param name="dfd">Data format descriptor.</param> /// <returns>Line count.</returns> public static int ComputeRequiredLineCount(Formatter formatter, PseudoOpNames opNames, FormatDescriptor dfd, byte[] data, int offset) { if (dfd.IsString) { Debug.Assert(false); // shouldn't be calling here anymore List <string> lines = FormatStringOp(formatter, opNames, dfd, data, offset, out string popcode); return(lines.Count); } switch (dfd.FormatType) { case FormatDescriptor.Type.Default: case FormatDescriptor.Type.NumericLE: case FormatDescriptor.Type.NumericBE: case FormatDescriptor.Type.Fill: case FormatDescriptor.Type.Junk: return(1); case FormatDescriptor.Type.Dense: { // no delimiter, two output bytes per input byte int maxLen = MAX_OPERAND_LEN; int textLen = dfd.Length * 2; return((textLen + maxLen - 1) / maxLen); } default: Debug.Assert(false); return(1); } }
private static Dictionary <string, string> PropsToDict(PseudoOpNames pon) { Dictionary <string, string> dict = new Dictionary <string, string>(); foreach (PropertyInfo prop in pon.GetType().GetProperties()) { string value = (string)prop.GetValue(pon); if (!string.IsNullOrEmpty(value)) { dict[prop.Name] = value; } } return(dict); }
/// <summary> /// Merges the non-null, non-empty strings in "other" into this instance. /// </summary> public void Merge(PseudoOpNames other) { // Lots of fields, we don't do this often... use reflection. Type type = GetType(); PropertyInfo[] props = type.GetProperties(); foreach (PropertyInfo pi in props) { string str = (string)pi.GetValue(other); if (string.IsNullOrEmpty(str)) { continue; } pi.SetValue(this, str); } }
/// <summary> /// Merges the non-null, non-empty strings. /// </summary> public static PseudoOpNames Merge(PseudoOpNames basePon, PseudoOpNames newPon) { Dictionary <string, string> baseDict = PropsToDict(basePon); Dictionary <string, string> newDict = PropsToDict(newPon); foreach (KeyValuePair <string, string> kvp in newDict) { if (string.IsNullOrEmpty(kvp.Value)) { continue; } baseDict[kvp.Key] = kvp.Value; } return(new PseudoOpNames(baseDict)); }
/// <summary> /// Converts a collection of bytes that represent a string into an array of characters, /// stripping the high bit. Framing data, such as leading lengths and trailing nulls, /// are not shown. /// </summary> /// <param name="formatter">Formatter object.</param> /// <param name="subType">String sub-type.</param> /// <param name="data">File data.</param> /// <param name="offset">Offset, within data, of start of string.</param> /// <param name="length">Number of bytes to convert.</param> /// <param name="popcode">Pseudo-opcode string.</param> /// <param name="showHexZeroes">If nonzero, show 1+ zeroes (representing a leading /// length or null-termination) instead of an empty string.</param> /// <returns>Array of characters with string data.</returns> private static char[] BytesToChars(Formatter formatter, PseudoOpNames opNames, FormatDescriptor.SubType subType, byte[] data, int offset, int length, out string popcode, out int showHexZeroes) { Debug.Assert(length > 0); // See also GenMerlin32.OutputString(). int strOffset = offset; int strLen = length; bool highAscii = false; bool reverse = false; showHexZeroes = 0; switch (subType) { case FormatDescriptor.SubType.None: // High or low ASCII, full width specified by formatter. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrGenericHi : opNames.StrGeneric; break; case FormatDescriptor.SubType.Dci: // High or low ASCII, full width specified by formatter. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrDciHi : opNames.StrDci; break; case FormatDescriptor.SubType.Reverse: // High or low ASCII, full width specified by formatter. Show characters // in reverse order. highAscii = (data[offset + strLen - 1] & 0x80) != 0; popcode = highAscii ? opNames.StrReverseHi : opNames.StrReverse; reverse = true; break; case FormatDescriptor.SubType.DciReverse: // High or low ASCII, full width specified by formatter. Show characters // in reverse order. highAscii = (data[offset + strLen - 1] & 0x80) != 0; popcode = highAscii ? opNames.StrDciReverseHi : opNames.StrDciReverse; reverse = true; break; case FormatDescriptor.SubType.CString: // High or low ASCII, with a terminating null. Don't show the null. If // it's an empty string, just show the null byte as hex. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrNullTermHi : opNames.StrNullTerm; strLen--; if (strLen == 0) { showHexZeroes = 1; } break; case FormatDescriptor.SubType.L8String: // High or low ASCII, with a leading length byte. Don't show the null. // If it's an empty string, just show the length byte as hex. strOffset++; strLen--; if (strLen == 0) { showHexZeroes = 1; } else { highAscii = (data[strOffset] & 0x80) != 0; } popcode = highAscii ? opNames.StrLen8Hi : opNames.StrLen8; break; case FormatDescriptor.SubType.L16String: // High or low ASCII, with a leading length word. Don't show the null. // If it's an empty string, just show the length word as hex. Debug.Assert(strLen > 1); strOffset += 2; strLen -= 2; if (strLen == 0) { showHexZeroes = 2; } else { highAscii = (data[strOffset] & 0x80) != 0; } popcode = highAscii ? opNames.StrLen16Hi : opNames.StrLen16; break; default: Debug.Assert(false); popcode = ".!!!"; break; } char[] text = new char[strLen]; if (!reverse) { for (int i = 0; i < strLen; i++) { text[i] = (char)(data[i + strOffset] & 0x7f); } } else { for (int i = 0; i < strLen; i++) { text[i] = (char)(data[strOffset + (strLen - i - 1)] & 0x7f); } } return(text); }
/// <summary> /// Generates a pseudo-op statement for the specified data operation. /// /// For most operations, only one output line will be generated. For larger items, /// like long comments, the value may be split into multiple lines. The sub-index /// indicates which line should be formatted. /// </summary> /// <param name="formatter">Format definition.</param> /// <param name="opNames">Table of pseudo-op names.</param> /// <param name="symbolTable">Project symbol table.</param> /// <param name="labelMap">Symbol label map. May be null.</param> /// <param name="dfd">Data format descriptor.</param> /// <param name="data">File data array.</param> /// <param name="offset">Start offset.</param> /// <param name="subIndex">For multi-line items, which line.</param> public static PseudoOut FormatDataOp(Formatter formatter, PseudoOpNames opNames, SymbolTable symbolTable, Dictionary <string, string> labelMap, FormatDescriptor dfd, byte[] data, int offset, int subIndex) { if (dfd == null) { // should never happen //Debug.Assert(false, "Null dfd at offset+" + offset.ToString("x6")); PseudoOut failed = new PseudoOut(); failed.Opcode = failed.Operand = "!FAILED!+" + offset.ToString("x6"); return(failed); } int length = dfd.Length; Debug.Assert(length > 0); // All outputs for a given offset show the same offset and length, even for // multi-line items. PseudoOut po = new PseudoOut(); switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { // This shouldn't happen. Debug.Assert(false); length = 1; } po.Opcode = opNames.GetDefineData(length); int operand = RawData.GetWord(data, offset, length, false); po.Operand = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: po.Opcode = opNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, operand, length, FormatNumericOpFlags.None); break; case FormatDescriptor.Type.NumericBE: po.Opcode = opNames.GetDefineBigData(length); operand = RawData.GetWord(data, offset, length, true); po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, operand, length, FormatNumericOpFlags.None); break; case FormatDescriptor.Type.Fill: po.Opcode = opNames.Fill; po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2); break; case FormatDescriptor.Type.Dense: { int maxPerLine = MAX_OPERAND_LEN / 2; offset += subIndex * maxPerLine; length -= subIndex * maxPerLine; if (length > maxPerLine) { length = maxPerLine; } po.Opcode = opNames.Dense; po.Operand = formatter.FormatDenseHex(data, offset, length); //List<PseudoOut> outList = new List<PseudoOut>(); //GenerateTextLines(text, "", "", po, outList); //po = outList[subIndex]; } break; case FormatDescriptor.Type.String: // It's hard to do strings in single-line pieces because of prefix lengths, // terminating nulls, DCI polarity, and reverse-order strings. We // really just want to convert the whole thing to a run of chars // and then pull out a chunk. As an optimization we can handle // generic strings (subtype=None) more efficiently, which should solve // the problem of massive strings created by auto-analysis. if (dfd.FormatSubType == FormatDescriptor.SubType.None) { int maxPerLine = MAX_OPERAND_LEN - 2; offset += subIndex * maxPerLine; length -= subIndex * maxPerLine; if (length > maxPerLine) { length = maxPerLine; } char[] ltext = BytesToChars(formatter, opNames, dfd.FormatSubType, data, offset, length, out string lpopcode, out int unused); po.Opcode = lpopcode; po.Operand = "\u201c" + new string(ltext) + "\u201d"; } else { char[] text = BytesToChars(formatter, opNames, dfd.FormatSubType, data, offset, length, out string popcode, out int showHexZeroes); if (showHexZeroes == 1) { po.Opcode = opNames.DefineData1; po.Operand = formatter.FormatHexValue(0, 2); } else if (showHexZeroes == 2) { po.Opcode = opNames.DefineData2; po.Operand = formatter.FormatHexValue(0, 4); } else { Debug.Assert(showHexZeroes == 0); po.Opcode = popcode; List <PseudoOut> outList = new List <PseudoOut>(); GenerateTextLines(text, "\u201c", "\u201d", po, outList); po = outList[subIndex]; } } break; default: Debug.Assert(false); po.Opcode = ".???"; po.Operand = "$" + data[offset].ToString("x2"); break; } return(po); }
/// <summary> /// Converts a collection of bytes that represent a string into an array of formatted /// string operands. /// </summary> /// <param name="formatter">Formatter object.</param> /// <param name="opNames">Pseudo-opcode name table.</param> /// <param name="dfd">Format descriptor.</param> /// <param name="data">File data.</param> /// <param name="offset">Offset, within data, of start of string.</param> /// <param name="popcode">Pseudo-opcode string.</param> /// <returns>Array of operand strings.</returns> public static List <string> FormatStringOp(Formatter formatter, PseudoOpNames opNames, FormatDescriptor dfd, byte[] data, int offset, out string popcode) { int hiddenLeadingBytes = 0; int trailingBytes = 0; StringOpFormatter.ReverseMode revMode = StringOpFormatter.ReverseMode.Forward; Formatter.DelimiterSet delSet = formatter.Config.mStringDelimiters; Formatter.DelimiterDef delDef; CharEncoding.Convert charConv; switch (dfd.FormatSubType) { case FormatDescriptor.SubType.Ascii: if (dfd.FormatType == FormatDescriptor.Type.StringDci) { charConv = CharEncoding.ConvertLowAndHighAscii; } else { charConv = CharEncoding.ConvertAscii; } delDef = delSet.Get(CharEncoding.Encoding.Ascii); break; case FormatDescriptor.SubType.HighAscii: if (dfd.FormatType == FormatDescriptor.Type.StringDci) { charConv = CharEncoding.ConvertLowAndHighAscii; } else { charConv = CharEncoding.ConvertHighAscii; } delDef = delSet.Get(CharEncoding.Encoding.HighAscii); break; case FormatDescriptor.SubType.C64Petscii: if (dfd.FormatType == FormatDescriptor.Type.StringDci) { charConv = CharEncoding.ConvertLowAndHighC64Petscii; } else { charConv = CharEncoding.ConvertC64Petscii; } delDef = delSet.Get(CharEncoding.Encoding.C64Petscii); break; case FormatDescriptor.SubType.C64Screen: if (dfd.FormatType == FormatDescriptor.Type.StringDci) { charConv = CharEncoding.ConvertLowAndHighC64ScreenCode; } else { charConv = CharEncoding.ConvertC64ScreenCode; } delDef = delSet.Get(CharEncoding.Encoding.C64ScreenCode); break; default: Debug.Assert(false); charConv = CharEncoding.ConvertAscii; delDef = delSet.Get(CharEncoding.Encoding.Ascii); break; } if (delDef == null) { delDef = Formatter.DOUBLE_QUOTE_DELIM; } switch (dfd.FormatType) { case FormatDescriptor.Type.StringGeneric: // Generic character data. popcode = opNames.StrGeneric; break; case FormatDescriptor.Type.StringReverse: // Character data, full width specified by formatter. Show characters // in reverse order. popcode = opNames.StrReverse; revMode = StringOpFormatter.ReverseMode.FullReverse; break; case FormatDescriptor.Type.StringNullTerm: // Character data with a terminating null. Don't show the null byte. popcode = opNames.StrNullTerm; trailingBytes = 1; //if (strLen == 0) { // showHexZeroes = 1; //} break; case FormatDescriptor.Type.StringL8: // Character data with a leading length byte. Don't show the length. hiddenLeadingBytes = 1; //if (strLen == 0) { // showHexZeroes = 1; //} popcode = opNames.StrLen8; break; case FormatDescriptor.Type.StringL16: // Character data with a leading length word. Don't show the length. Debug.Assert(dfd.Length > 1); hiddenLeadingBytes = 2; //if (strLen == 0) { // showHexZeroes = 2; //} popcode = opNames.StrLen16; break; case FormatDescriptor.Type.StringDci: // High bit on last byte is flipped. popcode = opNames.StrDci; break; default: Debug.Assert(false); popcode = ".!!!"; break; } StringOpFormatter stropf = new StringOpFormatter(formatter, delDef, StringOpFormatter.RawOutputStyle.CommaSep, MAX_OPERAND_LEN, charConv); stropf.FeedBytes(data, offset + hiddenLeadingBytes, dfd.Length - hiddenLeadingBytes - trailingBytes, 0, revMode); return(stropf.Lines); }
/// <summary> /// Generates a pseudo-op statement for the specified data operation. /// /// For most operations, only one output line will be generated. For larger items, /// like dense hex, the value may be split into multiple lines. The sub-index /// indicates which line should be formatted. /// </summary> /// <param name="formatter">Format definition.</param> /// <param name="opNames">Table of pseudo-op names.</param> /// <param name="symbolTable">Project symbol table.</param> /// <param name="labelMap">Symbol label map. May be null.</param> /// <param name="dfd">Data format descriptor.</param> /// <param name="data">File data array.</param> /// <param name="offset">Start offset.</param> /// <param name="subIndex">For multi-line items, which line.</param> public static PseudoOut FormatDataOp(Formatter formatter, PseudoOpNames opNames, SymbolTable symbolTable, Dictionary <string, string> labelMap, FormatDescriptor dfd, byte[] data, int offset, int subIndex) { if (dfd == null) { // should never happen //Debug.Assert(false, "Null dfd at offset+" + offset.ToString("x6")); PseudoOut failed = new PseudoOut(); failed.Opcode = failed.Operand = "!FAILED!+" + offset.ToString("x6"); return(failed); } int length = dfd.Length; Debug.Assert(length > 0); // All outputs for a given offset show the same offset and length, even for // multi-line items. PseudoOut po = new PseudoOut(); if (dfd.IsString) { Debug.Assert(false); // shouldn't be calling here anymore List <string> lines = FormatStringOp(formatter, opNames, dfd, data, offset, out string popcode); po.Opcode = popcode; po.Operand = lines[subIndex]; } else { switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { // This shouldn't happen. Debug.Assert(false); length = 1; } po.Opcode = opNames.GetDefineData(length); int operand = RawData.GetWord(data, offset, length, false); po.Operand = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: po.Opcode = opNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, operand, length, FormatNumericOpFlags.None); break; case FormatDescriptor.Type.NumericBE: po.Opcode = opNames.GetDefineBigData(length); operand = RawData.GetWord(data, offset, length, true); po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, operand, length, FormatNumericOpFlags.None); break; case FormatDescriptor.Type.Fill: po.Opcode = opNames.Fill; po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2); break; case FormatDescriptor.Type.Junk: if (dfd.FormatSubType != FormatDescriptor.SubType.None) { po.Opcode = opNames.Align; int alignPow = FormatDescriptor.AlignmentToPower(dfd.FormatSubType); po.Operand = formatter.FormatHexValue(1 << alignPow, 2) + " (" + length.ToString() + " bytes)"; } else { po.Opcode = opNames.Junk; po.Operand = length.ToString(); } break; case FormatDescriptor.Type.Dense: { int maxPerLine = MAX_OPERAND_LEN / 2; offset += subIndex * maxPerLine; length -= subIndex * maxPerLine; if (length > maxPerLine) { length = maxPerLine; } po.Opcode = opNames.Dense; po.Operand = formatter.FormatDenseHex(data, offset, length); //List<PseudoOut> outList = new List<PseudoOut>(); //GenerateTextLines(text, "", "", po, outList); //po = outList[subIndex]; } break; default: Debug.Assert(false); po.Opcode = ".???"; po.Operand = "$" + data[offset].ToString("x2"); break; } } return(po); }