/// <summary> /// Constructor. /// </summary> /// <param name="formatter">Reference to text formatter.</param> /// <param name="delimiterDef">String delimiter values.</param> /// <param name="byteStyle">How to format raw byte data.</param> /// <param name="charConv">Character conversion delegate.</param> public StringOpFormatter(Formatter formatter, Formatter.DelimiterDef delimiterDef, RawOutputStyle byteStyle, CharEncoding.Convert charConv) { mRawStyle = byteStyle; mMaxOperandLen = formatter.OperandWrapLen; CharConv = charConv; mDelimiterDef = delimiterDef; mBuffer = new char[mMaxOperandLen]; mHexChars = formatter.HexDigits; Lines = new List <string>(); // suffix not used, so we don't expect it to be set to something Debug.Assert(string.IsNullOrEmpty(mDelimiterDef.Suffix)); Reset(); }
/// <summary> /// Constructor. Initializes various fields based on the configuration. We want to /// do as much work as possible here. /// </summary> public Formatter(FormatConfig config) { mFormatConfig = config; // copy struct if (mFormatConfig.mEndOfLineCommentDelimiter == null) { mFormatConfig.mEndOfLineCommentDelimiter = string.Empty; } if (mFormatConfig.mFullLineCommentDelimiterBase == null) { mFormatConfig.mFullLineCommentDelimiterBase = string.Empty; } if (mFormatConfig.mBoxLineCommentDelimiter == null) { mFormatConfig.mBoxLineCommentDelimiter = string.Empty; } if (string.IsNullOrEmpty(mFormatConfig.mNonUniqueLabelPrefix)) { mFormatConfig.mNonUniqueLabelPrefix = "@"; } if (mFormatConfig.mAddSpaceLongComment) { mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase + " "; } else { mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase; } // Prep the static parts of the hex dump buffer. mHexDumpBuffer = new char[73]; for (int i = 0; i < mHexDumpBuffer.Length; i++) { mHexDumpBuffer[i] = ' '; } mHexDumpBuffer[6] = ':'; // Resolve boolean flags to character or string values. if (mFormatConfig.mUpperHexDigits) { mHexFmtChar = 'X'; } else { mHexFmtChar = 'x'; } if (mFormatConfig.mSuppressHexNotation) { mHexPrefix = ""; } else { mHexPrefix = "$"; } if (mFormatConfig.mSuppressImpliedAcc) { mAccChar = ""; } else if (mFormatConfig.mUpperOperandA) { mAccChar = "A"; } else { mAccChar = "a"; } if (mFormatConfig.mUpperOperandXY) { mXregChar = 'X'; mYregChar = 'Y'; } else { mXregChar = 'x'; mYregChar = 'y'; } if (mFormatConfig.mUpperOperandS) { mSregChar = 'S'; } else { mSregChar = 's'; } // process the delimiter patterns DelimiterSet chrDelim = mFormatConfig.mCharDelimiters; if (chrDelim == null) { Debug.WriteLine("NOTE: char delimiters not set"); chrDelim = DelimiterSet.GetDefaultCharDelimiters(); } switch (mFormatConfig.mHexDumpCharConvMode) { case FormatConfig.CharConvMode.Ascii: mHexDumpCharConv = CharEncoding.ConvertAscii; break; case FormatConfig.CharConvMode.LowHighAscii: mHexDumpCharConv = CharEncoding.ConvertLowAndHighAscii; break; case FormatConfig.CharConvMode.C64Petscii: mHexDumpCharConv = CharEncoding.ConvertC64Petscii; break; case FormatConfig.CharConvMode.C64ScreenCode: mHexDumpCharConv = CharEncoding.ConvertC64ScreenCode; break; default: // most some things don't configure the hex dump; this is fine mHexDumpCharConv = CharEncoding.ConvertLowAndHighAscii; break; } }
private void OutputString(int offset, string labelStr, string commentStr) { // Generic strings whose encoding matches the configured text encoding are output // with a simple .text directive. // // CString and L8String have directives (.null, .ptext), but we can only use // them if the string fits on one line and doesn't include delimiters. // // We might be able to define a macro for Reverse. // // We don't currently switch character encodings in the middle of a file. We could // do so to flip between PETSCII, screen codes, low ASCII, and high ASCII, but it // adds a lot of noise and it's unclear that this is generally useful. Anattrib attr = Project.GetAnattrib(offset); FormatDescriptor dfd = attr.DataDescriptor; Debug.Assert(dfd != null); Debug.Assert(dfd.IsString); Debug.Assert(dfd.Length > 0); CharEncoding.Convert charConv = null; CharEncoding.Convert dciConv = null; switch (dfd.FormatSubType) { case FormatDescriptor.SubType.Ascii: charConv = CharEncoding.ConvertAscii; dciConv = CharEncoding.ConvertLowAndHighAscii; break; case FormatDescriptor.SubType.HighAscii: charConv = CharEncoding.ConvertHighAscii; dciConv = CharEncoding.ConvertLowAndHighAscii; break; case FormatDescriptor.SubType.C64Petscii: charConv = CharEncoding.ConvertC64Petscii; dciConv = CharEncoding.ConvertLowAndHighC64Petscii; break; case FormatDescriptor.SubType.C64Screen: charConv = CharEncoding.ConvertC64ScreenCode; dciConv = CharEncoding.ConvertLowAndHighC64ScreenCode; break; default: break; } if (charConv == null) { OutputNoJoy(offset, dfd.Length, labelStr, commentStr); return; } // Issue a .enc, if needed. UpdateCharacterEncoding(dfd); Formatter formatter = SourceFormatter; byte[] data = Project.FileData; int hiddenLeadingBytes = 0; int shownLeadingBytes = 0; int trailingBytes = 0; string opcodeStr; switch (dfd.FormatType) { case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringReverse: opcodeStr = sDataOpNames.StrGeneric; break; case FormatDescriptor.Type.StringNullTerm: opcodeStr = sDataOpNames.StrNullTerm; trailingBytes = 1; break; case FormatDescriptor.Type.StringL8: opcodeStr = sDataOpNames.StrLen8; hiddenLeadingBytes = 1; break; case FormatDescriptor.Type.StringL16: opcodeStr = sDataOpNames.StrGeneric; shownLeadingBytes = 2; break; case FormatDescriptor.Type.StringDci: opcodeStr = sDataOpNames.StrDci; if ((Project.FileData[offset] & 0x80) != 0) { // ".shift" directive only works for strings where the low bit starts // clear and ends high. OutputNoJoy(offset, dfd.Length, labelStr, commentStr); return; } break; default: Debug.Assert(false); return; } StringOpFormatter stropf = new StringOpFormatter(SourceFormatter, Formatter.DOUBLE_QUOTE_DELIM, StringOpFormatter.RawOutputStyle.CommaSep, MAX_OPERAND_LEN, charConv); if (dfd.FormatType == FormatDescriptor.Type.StringDci) { // DCI is awkward because the character encoding flips on the last byte. Rather // than clutter up StringOpFormatter for this rare item, we just accept low/high // throughout. stropf.CharConv = dciConv; } // Feed bytes in, skipping over hidden bytes (leading L8, trailing null). stropf.FeedBytes(data, offset + hiddenLeadingBytes, dfd.Length - hiddenLeadingBytes - trailingBytes, shownLeadingBytes, StringOpFormatter.ReverseMode.Forward); Debug.Assert(stropf.Lines.Count > 0); // See if we need to do this over. bool redo = false; switch (dfd.FormatType) { case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringL16: // All good the first time. break; case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringL8: case FormatDescriptor.Type.StringDci: if (stropf.Lines.Count != 1) { // Must be single-line. opcodeStr = sDataOpNames.StrGeneric; stropf.CharConv = charConv; // undo DCI hack redo = true; } break; default: Debug.Assert(false); return; } if (redo) { //Debug.WriteLine("REDO off=+" + offset.ToString("x6") + ": " + dfd.FormatType); // This time, instead of skipping over leading length bytes, we include them // explicitly. stropf.Reset(); stropf.FeedBytes(data, offset, dfd.Length, hiddenLeadingBytes, StringOpFormatter.ReverseMode.Forward); } opcodeStr = formatter.FormatPseudoOp(opcodeStr); foreach (string str in stropf.Lines) { OutputLine(labelStr, opcodeStr, str, commentStr); labelStr = commentStr = string.Empty; // only show on first } }