/// <summary> /// Determines whether the data in the buffer can be represented as ASCII values. /// Using ".DD1 'A'" for 0x41 is obvious, but we also allow ".DD2 'A'" for /// 0x41 0x00. 16-bit character constants are more likely as intermediate /// operands, but could be found in data areas. /// /// High and low ASCII are allowed, and may be freely mixed. /// /// Testing explicitly is probably excessive, and possibly counter-productive if /// the user is trying to flag an area that is a mix of ASCII and non-ASCII and /// just wants hex for the rest, but we'll give it a try. /// </summary> /// <param name="wordWidth">Number of bytes per character.</param> /// <param name="isBigEndian">Word endian-ness.</param> /// <returns>True if data in all regions can be represented as high or low ASCII.</returns> private bool IsRawAsciiCompatible(int wordWidth, bool isBigEndian) { IEnumerator <TypedRangeSet.TypedRange> iter = Selection.RangeListIterator; while (iter.MoveNext()) { TypedRangeSet.TypedRange rng = iter.Current; Debug.Assert(((rng.High - rng.Low + 1) / wordWidth) * wordWidth == rng.High - rng.Low + 1); for (int i = rng.Low; i <= rng.High; i += wordWidth) { int val = RawData.GetWord(mFileData, rng.Low, wordWidth, isBigEndian); if (val < 0x20 || (val >= 0x7f && val < 0xa0) || val >= 0xff) { // bad value, fail return(false); } } } return(true); }
/// <summary> /// Creates a list of FormatDescriptors, based on the current control configuration. /// /// The entries in the list are guaranteed to be sorted by start address and not /// overlap. /// /// We assume that whatever the control gives us is correct, e.g. it's not going /// to tell us to put a buffer full of zeroes into a DCI string. /// </summary> /// <returns>Result list.</returns> private void CreateDescriptorListFromControls() { FormatDescriptor.Type type = FormatDescriptor.Type.Default; FormatDescriptor.SubType subType = FormatDescriptor.SubType.None; WeakSymbolRef symbolRef = null; int chunkLength = -1; // Decode the "display as" panel, if it's relevant. if (radioSimpleDataHex.Enabled) { if (radioSimpleDataHex.Checked) { subType = FormatDescriptor.SubType.Hex; } else if (radioSimpleDataDecimal.Checked) { subType = FormatDescriptor.SubType.Decimal; } else if (radioSimpleDataBinary.Checked) { subType = FormatDescriptor.SubType.Binary; } else if (radioSimpleDataAscii.Checked) { subType = FormatDescriptor.SubType.Ascii; } else if (radioSimpleDataAddress.Checked) { subType = FormatDescriptor.SubType.Address; } else if (radioSimpleDataSymbolic.Checked) { WeakSymbolRef.Part part; if (radioSymbolPartLow.Checked) { part = WeakSymbolRef.Part.Low; } else if (radioSymbolPartHigh.Checked) { part = WeakSymbolRef.Part.High; } else if (radioSymbolPartBank.Checked) { part = WeakSymbolRef.Part.Bank; } else { Debug.Assert(false); part = WeakSymbolRef.Part.Low; } subType = FormatDescriptor.SubType.Symbol; symbolRef = new WeakSymbolRef(symbolEntryTextBox.Text, part); } else { Debug.Assert(false); } } else { subType = 0; // set later, or doesn't matter } // Decode the main format. if (radioDefaultFormat.Checked) { // Default/None; note this would create a multi-byte Default format, which isn't // really allowed. What we actually want to do is remove the explicit formatting // from all spanned offsets, so we use a dedicated type for that. type = FormatDescriptor.Type.REMOVE; } else if (radioSingleBytes.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 1; } else if (radio16BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 2; } else if (radio16BitBig.Checked) { type = FormatDescriptor.Type.NumericBE; chunkLength = 2; } else if (radio24BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 3; } else if (radio32BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 4; } else if (radioDenseHex.Checked) { type = FormatDescriptor.Type.Dense; } else if (radioFill.Checked) { type = FormatDescriptor.Type.Fill; } else if (radioStringMixed.Checked) { type = FormatDescriptor.Type.String; } else if (radioStringMixedReverse.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.Reverse; } else if (radioStringNullTerm.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.CString; } else if (radioStringLen8.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.L8String; } else if (radioStringLen16.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.L16String; } else if (radioStringDci.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.Dci; //} else if (radioStringDciReverse.Checked) { // type = FormatDescriptor.Type.String; // subType = FormatDescriptor.SubType.DciReverse; } else { Debug.Assert(false); // default/none } Results = new SortedList <int, FormatDescriptor>(); IEnumerator <TypedRangeSet.TypedRange> iter = Selection.RangeListIterator; while (iter.MoveNext()) { TypedRangeSet.TypedRange rng = iter.Current; if (type == FormatDescriptor.Type.String) { // We want to create one FormatDescriptor object per string. That way // each string gets its own line. if ((subType == FormatDescriptor.SubType.None || subType == FormatDescriptor.SubType.Reverse)) { CreateMixedStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.CString) { CreateCStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.L8String || subType == FormatDescriptor.SubType.L16String) { CreateLengthStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.Dci || subType == FormatDescriptor.SubType.DciReverse) { CreateDciStringEntries(rng.Low, rng.High, subType); } else { Debug.Assert(false); CreateMixedStringEntries(rng.Low, rng.High, subType); // shrug } } else { CreateSimpleEntries(type, subType, chunkLength, symbolRef, rng.Low, rng.High); } } }
/// <summary> /// Analyzes the selection to see which data formatting options are suitable. /// Disables radio buttons and updates labels. /// /// Call this once, when the dialog is first loaded. /// </summary> private void AnalyzeRanges() { Debug.Assert(Selection.Count != 0); string fmt = (Selection.RangeCount == 1) ? Properties.Resources.FMT_FORMAT_SINGLE_GROUP : Properties.Resources.FMT_FORMAT_MULTIPLE_GROUPS; selectFormatLabel.Text = string.Format(fmt, Selection.Count, Selection.RangeCount); IEnumerator <TypedRangeSet.TypedRange> iter = Selection.RangeListIterator; int mixedAsciiOkCount = 0; int mixedAsciiNotCount = 0; int nullTermStringCount = 0; int len8StringCount = 0; int len16StringCount = 0; int dciStringCount = 0; //int revDciStringCount = 0; // For each range, check to see if the data within qualifies for the various // options. If any of them fail to meet the criteria, the option is disabled // for all ranges. while (iter.MoveNext()) { TypedRangeSet.TypedRange rng = iter.Current; Debug.WriteLine("Testing [" + rng.Low + ", " + rng.High + "]"); // Start with the easy ones. Single-byte and dense are always enabled. int count = rng.High - rng.Low + 1; Debug.Assert(count > 0); if ((count & 0x01) != 0) { // not divisible by 2, disallow 16-bit entries radio16BitLittle.Enabled = false; radio16BitBig.Enabled = false; } if ((count & 0x03) != 0) { // not divisible by 4, disallow 32-bit entries radio32BitLittle.Enabled = false; } if ((count / 3) * 3 != count) { // not divisible by 3, disallow 24-bit entries radio24BitLittle.Enabled = false; } // Check for run of bytes (2 or more of the same thing). Remember that // we check this one region at a time, and each region could have different // bytes, but so long as the bytes are all the same within a region we're good. if (radioFill.Enabled && count > 1 && DataAnalysis.RecognizeRun(mFileData, rng.Low, rng.High) == count) { // LGTM } else { radioFill.Enabled = false; } // See if there's enough string data to make it worthwhile. We use an // arbitrary threshold of 2+ ASCII characters, and require twice as many // ASCII as non-ASCII. We arbitrarily require the strings to be either // high or low ASCII, and treat the other as non-ASCII. (We could relax // this -- we generate separate items for each string and non-ASCII chunk -- // but I'm trying to hide the option when the buffer doesn't really seem // to be holding strings. Could replace with some sort of minimum string // length requirement?) if (radioStringMixed.Enabled) { int asciiCount; DataAnalysis.CountAsciiBytes(mFileData, rng.Low, rng.High, out int lowAscii, out int highAscii, out int nonAscii); if (highAscii > lowAscii) { asciiCount = highAscii; nonAscii += lowAscii; } else { asciiCount = lowAscii; nonAscii += highAscii; } if (asciiCount >= 2 && asciiCount >= nonAscii * 2) { // Looks good mixedAsciiOkCount += asciiCount; mixedAsciiNotCount += nonAscii; } else { // Fail radioStringMixed.Enabled = false; radioStringMixedReverse.Enabled = false; mixedAsciiOkCount = mixedAsciiNotCount = -1; } } // Check for null-terminated strings. Zero-length strings are allowed, but // not counted -- we want to have some actual character data. Individual // strings need to be entirely high-ASCII or low-ASCII, but not all strings // in a region have to be the same. if (radioStringNullTerm.Enabled) { int strCount = DataAnalysis.RecognizeNullTerminatedStrings(mFileData, rng.Low, rng.High); if (strCount > 0) { nullTermStringCount += strCount; } else { radioStringNullTerm.Enabled = false; nullTermStringCount = -1; } } // Check for strings prefixed with an 8-bit length. if (radioStringLen8.Enabled) { int strCount = DataAnalysis.RecognizeLen8Strings(mFileData, rng.Low, rng.High); if (strCount > 0) { len8StringCount += strCount; } else { radioStringLen8.Enabled = false; len8StringCount = -1; } } // Check for strings prefixed with a 16-bit length. if (radioStringLen16.Enabled) { int strCount = DataAnalysis.RecognizeLen16Strings(mFileData, rng.Low, rng.High); if (strCount > 0) { len16StringCount += strCount; } else { radioStringLen16.Enabled = false; len16StringCount = -1; } } // Check for DCI strings. All strings within a single range must have the // same "polarity", e.g. low ASCII terminated by high ASCII. if (radioStringDci.Enabled) { int strCount = DataAnalysis.RecognizeDciStrings(mFileData, rng.Low, rng.High); if (strCount > 0) { dciStringCount += strCount; } else { radioStringDci.Enabled = false; dciStringCount = -1; } } //// Check for reverse DCI strings. All strings within a single range must have the //// same "polarity", e.g. low ASCII terminated by high ASCII. //if (radioStringDciReverse.Enabled) { // int strCount = DataAnalysis.RecognizeReverseDciStrings(mFileData, // rng.Low, rng.High); // if (strCount > 0) { // revDciStringCount += strCount; // } else { // radioStringDciReverse.Enabled = false; // revDciStringCount = -1; // } //} } // Update the dialog with string and character counts, summed across all regions. if (mixedAsciiOkCount > 0) { Debug.Assert(radioStringMixed.Enabled); radioStringMixed.Text = string.Format(radioStringMixed.Text, mixedAsciiOkCount, mixedAsciiNotCount); radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text, mixedAsciiOkCount, mixedAsciiNotCount); } else { Debug.Assert(!radioStringMixed.Enabled); radioStringMixed.Text = string.Format(radioStringMixed.Text, "xx", "xx"); radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text, "xx", "xx"); } if (nullTermStringCount > 0) { Debug.Assert(radioStringNullTerm.Enabled); radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, nullTermStringCount); } else { Debug.Assert(!radioStringNullTerm.Enabled); radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, "xx"); } if (len8StringCount > 0) { Debug.Assert(radioStringLen8.Enabled); radioStringLen8.Text = string.Format(radioStringLen8.Text, len8StringCount); } else { Debug.Assert(!radioStringLen8.Enabled); radioStringLen8.Text = string.Format(radioStringLen8.Text, "xx"); } if (len16StringCount > 0) { Debug.Assert(radioStringLen16.Enabled); radioStringLen16.Text = string.Format(radioStringLen16.Text, len16StringCount); } else { Debug.Assert(!radioStringLen16.Enabled); radioStringLen16.Text = string.Format(radioStringLen16.Text, "xx"); } if (dciStringCount > 0) { Debug.Assert(radioStringDci.Enabled); radioStringDci.Text = string.Format(radioStringDci.Text, dciStringCount); } else { Debug.Assert(!radioStringDci.Enabled); radioStringDci.Text = string.Format(radioStringDci.Text, "xx"); } //if (revDciStringCount > 0) { // Debug.Assert(radioStringDciReverse.Enabled); // radioStringDciReverse.Text = // string.Format(radioStringDciReverse.Text, revDciStringCount); //} else { // Debug.Assert(!radioStringDciReverse.Enabled); // radioStringDciReverse.Text = string.Format(radioStringDciReverse.Text, "xx"); //} }