diff --git a/CommonWPF/CommonWPF.csproj b/CommonWPF/CommonWPF.csproj
index 55ae103..f23cf2c 100644
--- a/CommonWPF/CommonWPF.csproj
+++ b/CommonWPF/CommonWPF.csproj
@@ -54,6 +54,7 @@
+
Code
diff --git a/CommonWPF/ListToStringConverter.cs b/CommonWPF/ListToStringConverter.cs
new file mode 100644
index 0000000..e92d256
--- /dev/null
+++ b/CommonWPF/ListToStringConverter.cs
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 faddenSoft
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace CommonWPF {
+ ///
+ /// Converts a List<string> to a multi-line string, suitable for presentation
+ /// in a TextBlock or other UI element.
+ ///
+ ///
+ /// https://stackoverflow.com/a/345515/294248
+ ///
+ /// In XAML, reference with:
+ /// xmlns:common="clr-namespace:CommonWPF;assembly=CommonWPF"
+ ///
+ [ValueConversion(typeof(List), typeof(string))]
+ public class ListToStringConverter : IValueConverter {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
+ if (targetType != typeof(string)) {
+ Debug.WriteLine("Invalid targetType for string conversion");
+ return DependencyProperty.UnsetValue;
+ }
+
+ return string.Join("\r\n", ((List)value).ToArray());
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter,
+ CultureInfo culture) {
+ return DependencyProperty.UnsetValue;
+ }
+ }
+}
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 0fbd915..4cb8fad 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -4177,7 +4177,7 @@ namespace SourceGen {
}
Tools.Omf.WpfGui.OmfViewer ov =
- new Tools.Omf.WpfGui.OmfViewer(this.mMainWin, pathName, fileData);
+ new Tools.Omf.WpfGui.OmfViewer(this.mMainWin, pathName, fileData, mFormatter);
ov.ShowDialog();
}
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index 445532f..a5bbf86 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -87,6 +87,9 @@
+
+ OmfSegmentViewer.xaml
+
OmfViewer.xaml
@@ -281,6 +284,10 @@
Designer
MSBuild:Compile
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/SourceGen/Tools/Omf/OmfFile.cs b/SourceGen/Tools/Omf/OmfFile.cs
index 0e158fc..deab4c3 100644
--- a/SourceGen/Tools/Omf/OmfFile.cs
+++ b/SourceGen/Tools/Omf/OmfFile.cs
@@ -38,8 +38,8 @@ namespace SourceGen.Tools.Omf {
/// Appendix F describes OMF v2.1, and Chapter 8 has some useful information about
/// how the loader works (e.g. page 205).
/// - "Undocumented Secrets of the Apple IIGS System Loader" by Neil Parker,
- /// http://nparker.llx.com/a2/loader.html . Among other things it documents ExpressLoad
- /// segments, something Apple apparently never did.
+ /// http://nparker.llx.com/a2/loader.html . Among other things it documents the
+ /// contents of ExpressLoad segments, which I haven't found in an official reference.
/// - Apple IIgs Tech Note #66, "ExpressLoad Philosophy".
///
/// Related:
@@ -76,19 +76,20 @@ namespace SourceGen.Tools.Omf {
Object, // output of assembler/compiler, before linking
Library, // static code library
RunTimeLibrary, // dynamic shared library
+ Indeterminate, // valid OMF, but type is indeterminate
Foreign // not OMF, or not IIgs OMF
}
public FileKind OmfFileKind { get; private set; }
private bool mIsDamaged;
- private string mDamageMsg = string.Empty;
-
private List mSegmentList = new List();
public List SegmentList {
get { return mSegmentList; }
}
+ public List MessageList { get; private set; } = new List();
+
///
/// Constructor.
@@ -105,7 +106,13 @@ namespace SourceGen.Tools.Omf {
OmfSegment.ParseResult result = DoAnalyze(false);
if (result == OmfSegment.ParseResult.IsLibrary ||
result == OmfSegment.ParseResult.Failure) {
- DoAnalyze(true);
+ // Failed; try again as a library.
+ List firstFail = new List(MessageList);
+ result = DoAnalyze(true);
+ if (result == OmfSegment.ParseResult.Failure) {
+ // Failed both ways. Report the failures from the non-library attempt.
+ MessageList = firstFail;
+ }
}
}
@@ -114,24 +121,32 @@ namespace SourceGen.Tools.Omf {
int offset = 0;
int len = mFileData.Length;
+ List msgs = new List();
+
while (len > 0) {
- OmfSegment.ParseResult result =
- OmfSegment.ParseSegment(mFileData, offset, parseAsLibrary, out OmfSegment seg);
- if (result == OmfSegment.ParseResult.Failure) {
- // parsing failed; reject file or stop early
- if (first) {
- OmfFileKind = FileKind.Foreign;
- } else {
- mIsDamaged = true;
- mDamageMsg = string.Format("File may be damaged; ignoring last {0} bytes",
- mFileData.Length - offset);
- }
- return result;
- } else if (result == OmfSegment.ParseResult.IsLibrary) {
+ OmfSegment.ParseResult result = OmfSegment.ParseSegment(mFileData, offset,
+ parseAsLibrary, msgs, out OmfSegment seg);
+
+ MessageList.Clear();
+ foreach (string str in msgs) {
+ MessageList.Add(str);
+ }
+
+ if (result == OmfSegment.ParseResult.IsLibrary) {
// Need to start over in library mode.
Debug.WriteLine("Restarting in library mode");
return result;
+ } else if (result == OmfSegment.ParseResult.Failure) {
+ // Could be a library we failed to parse, could be a totally bad file.
+ // If we were on the first segment, fail immediately so we can retry as
+ // library. If not, it's probably not a library (assuming the Library
+ // Dictionary segment appears first), but rather a partially-bad OMF.
+ if (first) {
+ return result;
+ }
+ break;
}
+ first = false;
Debug.Assert(seg.FileLength > 0);
mSegmentList.Add(seg);
diff --git a/SourceGen/Tools/Omf/OmfSegment.cs b/SourceGen/Tools/Omf/OmfSegment.cs
index 7c24821..147437a 100644
--- a/SourceGen/Tools/Omf/OmfSegment.cs
+++ b/SourceGen/Tools/Omf/OmfSegment.cs
@@ -76,15 +76,23 @@ namespace SourceGen.Tools.Omf {
private const int LOAD_NAME_LEN = 10;
public class NameValueNote {
- public string Name { get; set; }
- public object Value { get; set; }
- public string Note { get; set; }
+ public string Name { get; private set; }
+ public object Value { get; private set; }
+ public int Width { get; private set; }
+ public string Note { get; private set; }
+
+ public NameValueNote(string name, object value, int width, string note) {
+ Name = name;
+ Value = value;
+ Width = width;
+ Note = note;
+ }
}
///
/// Values pulled from file header. Useful for display.
///
- List RawValues = new List();
+ public List RawValues = new List();
public enum SegmentVersion { v0_0, v1_0, v2_0, v2_1 }
@@ -107,9 +115,9 @@ namespace SourceGen.Tools.Omf {
BankRel = 0x0100, // v2.1
Skip = 0x0200, // v2.1
Reloadable = 0x0400, // v2.0
- AbsoluteBank = 0x0800, // v2.0
+ AbsBank = 0x0800, // v2.0
NoSpecial = 0x1000, // v2.0
- PositionIndep = 0x2000, //
+ PosnIndep = 0x2000, //
Private = 0x4000, //
Dynamic = 0x8000 //
}
@@ -118,6 +126,7 @@ namespace SourceGen.Tools.Omf {
// Header fields.
//
+ public int FileOffset { get; private set; }
public int FileLength { get; private set; } // from BLKCNT or BYTECNT
public int ResSpc { get; private set; }
public int Length { get; private set; }
@@ -155,19 +164,31 @@ namespace SourceGen.Tools.Omf {
IsLibrary
}
+ ///
+ /// Parses an OMF segment header.
+ ///
+ /// File data.
+ /// Offset at which to start parsing.
+ /// Set to true to parse the header as if it were part
+ /// of a library file. Affects parsing of v1 headers.
+ /// Notes and errors generated by the parser.
+ /// Completed object, or null on failure.
+ /// Result code.
public static ParseResult ParseSegment(byte[] data, int offset, bool parseAsLibrary,
- out OmfSegment segResult) {
+ List msgs, out OmfSegment segResult) {
segResult = null;
+ //Debug.WriteLine("PARSE offset=" + offset);
+
Debug.Assert(offset < data.Length);
if (data.Length - offset < MIN_HEADER_V0) {
// Definitely too small.
+ AddErrorMsg(msgs, offset, "remaining file space too small to hold segment");
return ParseResult.Failure;
}
- //Debug.WriteLine("PARSE offset=" + offset);
-
OmfSegment newSeg = new OmfSegment();
+ newSeg.FileOffset = offset;
// Start with the version number. The meaning of everything else depends on this.
int minLen, expectedDispName;
@@ -189,10 +210,13 @@ namespace SourceGen.Tools.Omf {
break;
default:
// invalid version, this is probably not OMF
+ AddErrorMsg(msgs, offset, "invalid segment type " + data[offset + 0x0f]);
return ParseResult.Failure;
}
if (data.Length - offset < minLen) {
// Too small for this version of the header.
+ AddErrorMsg(msgs, offset, "remaining file space too small to hold " +
+ newSeg.Version + " segment");
return ParseResult.Failure;
}
@@ -227,6 +251,8 @@ namespace SourceGen.Tools.Omf {
expectedDispName += 4;
if (data.Length - offset < minLen + 4) {
+ AddErrorMsg(msgs, offset, "remaining file space too small to hold " +
+ newSeg.Version + " segment");
return ParseResult.Failure;
}
newSeg.TempOrg = RawData.GetWord(data, offset + 0x2c, 4, false);
@@ -238,14 +264,14 @@ namespace SourceGen.Tools.Omf {
kindByte = data[offset + 0x0c];
if (!Enum.IsDefined(typeof(SegmentKind), kindByte & 0x1f)) {
// Example: Moria GS has a kind of $1F for its GLOBALS segment.
- Debug.WriteLine("Invalid segment kind $" + kindByte.ToString("x2"));
+ AddErrorMsg(msgs, offset, "invalid segment kind $" + kindByte.ToString("x2"));
return ParseResult.Failure;
}
newSeg.Kind = (SegmentKind)(kindByte & 0x1f);
int kindAttrs = 0;
if ((kindByte & 0x20) != 0) {
- kindAttrs |= (int)SegmentAttribute.PositionIndep;
+ kindAttrs |= (int)SegmentAttribute.PosnIndep;
}
if ((kindByte & 0x40) != 0) {
kindAttrs |= (int)SegmentAttribute.Private;
@@ -258,7 +284,7 @@ namespace SourceGen.Tools.Omf {
// Yank all the attribute bits out at once. Don't worry about v2.0 vs. v2.1.
kindWord = RawData.GetWord(data, offset + 0x14, 2, false);
if (!Enum.IsDefined(typeof(SegmentKind), kindWord & 0x001f)) {
- Debug.WriteLine("Invalid segment kind $" + kindWord.ToString("x4"));
+ AddErrorMsg(msgs, offset, "invalid segment kind $" + kindWord.ToString("x4"));
return ParseResult.Failure;
}
newSeg.Kind = (SegmentKind)(kindWord & 0x001f);
@@ -268,6 +294,7 @@ namespace SourceGen.Tools.Omf {
// If we found a library dictionary segment, and we're not currently handling the
// file as a library, reject this and try again.
if (newSeg.Kind == SegmentKind.LibraryDict && !parseAsLibrary) {
+ AddInfoMsg(msgs, offset, "found Library Dictionary segment, retrying as library");
return ParseResult.IsLibrary;
}
@@ -296,47 +323,57 @@ namespace SourceGen.Tools.Omf {
}
newSeg.FileLength = segLen;
+ //
// Perform validity checks. If any of these fail, we're probably reading something
// that isn't OMF (or, if this isn't the first segment, we might have gone off the
// rails at some point).
- if (numLen != 4 || numSex != 0) {
- Debug.WriteLine("Invalid NUMLEN (" + numLen + ") or NUMSEX (" + numSex + ")");
+ //
+
+ if (numLen != 4) {
+ AddErrorMsg(msgs, offset, "NUMLEN must be 4, was " + numLen);
+ return ParseResult.Failure;
+ }
+ if (numSex != 0) {
+ AddErrorMsg(msgs, offset, "NUMSEX must be 0, was " + numSex);
return ParseResult.Failure;
}
if (offset + segLen > data.Length) {
- // Segment is longer than the file. (This can happen easily in a static lib.)
- Debug.WriteLine("Segment exceeds EOF: offset=" + offset + " len=" + data.Length +
- " segLen=" + segLen);
+ // Segment is longer than the file. (This can happen easily in a static lib if
+ // we're not parsing it as such.)
+ AddErrorMsg(msgs, offset, "segment file length exceeds EOF (segLen=" + segLen +
+ ", remaining=" + (data.Length - offset) + ")");
return ParseResult.Failure;
}
if (dispName < expectedDispName || dispName > (segLen - LOAD_NAME_LEN)) {
- Debug.WriteLine("Invalid DISPNAME " + dispName + " segLen=" + segLen);
+ AddErrorMsg(msgs, offset, "invalid DISPNAME " + dispName + " (expected " +
+ expectedDispName + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
if (dispData < expectedDispName + LOAD_NAME_LEN || dispData > (segLen - 1)) {
- Debug.WriteLine("Invalid DISPDATA " + dispData + " segLen=" + segLen);
+ AddErrorMsg(msgs, offset, "invalid DISPDATA " + dispData + " (expected " +
+ (expectedDispName + LOAD_NAME_LEN) + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
if (newSeg.BankSize > 0x00010000) {
- Debug.WriteLine("Invalid BANKSIZE $" + newSeg.BankSize.ToString("x"));
+ AddErrorMsg(msgs, offset, "invalid BANKSIZE $" + newSeg.BankSize.ToString("x"));
return ParseResult.Failure;
}
if (newSeg.Align > 0x00010000) {
- Debug.WriteLine("Invalid ALIGN $" + newSeg.Align.ToString("x"));
+ AddErrorMsg(msgs, offset, "invalid ALIGN $" + newSeg.Align.ToString("x"));
return ParseResult.Failure;
}
if (newSeg.BankSize != 0x00010000 && newSeg.BankSize != 0) {
// This is fine, just a little weird.
- Debug.WriteLine("Unusual BANKSIZE $" + newSeg.BankSize.ToString("x6"));
+ AddInfoMsg(msgs, offset, "unusual BANKSIZE $" + newSeg.BankSize.ToString("x6"));
}
if (newSeg.Align != 0 && newSeg.Align != 0x0100 && newSeg.Align != 0x00010000) {
// Unexpected; the loader will round up.
- Debug.WriteLine("Unusual ALIGN $" + newSeg.Align.ToString("x6"));
+ AddInfoMsg(msgs, offset, "unusual ALIGN $" + newSeg.Align.ToString("x6"));
}
if (newSeg.Entry != 0 && newSeg.Entry >= newSeg.Length) {
// This is invalid, but if we got this far we might as well keep going.
- Debug.WriteLine("Invalid ENTRY $" + newSeg.Entry.ToString("x6"));
+ AddInfoMsg(msgs, offset, "invalid ENTRY $" + newSeg.Entry.ToString("x6"));
}
// Extract LOADNAME. Fixed-width field, padded with spaces. Except for the
@@ -354,29 +391,110 @@ namespace SourceGen.Tools.Omf {
// string preceded by length byte
int segNameLen = data[offset + segNameStart];
if (segNameStart + 1 + segNameLen > segLen) {
- Debug.WriteLine("Var-width SEGNAME ran off end of segment (len=" +
- segNameLen + ")");
+ AddInfoMsg(msgs, offset, "var-width SEGNAME ran off end of segment (len=" +
+ segNameLen + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
segName = Encoding.ASCII.GetString(data, offset + segNameStart + 1, segNameLen);
} else {
// fixed-width string
if (segNameStart + newSeg.LabLen > segLen) {
- Debug.WriteLine("Fixed-width SEGNAME ran off end of segment (len=" +
- newSeg.LabLen + ")");
+ AddInfoMsg(msgs, offset, "fixed-width SEGNAME ran off end of segment (LABLEN=" +
+ newSeg.LabLen + ", segLen=" + segLen + ")");
return ParseResult.Failure;
}
segName = ExtractString(data, offset + segNameStart, newSeg.LabLen);
}
- Debug.WriteLine("LOADNAME='" + loadName + "' SEGNAME='" + segName + "'");
+ //AddInfoMsg(msgs, offset, "GOT LOADNAME='" + loadName + "' SEGNAME='" + segName + "'");
+
newSeg.LoadName = loadName;
newSeg.SegName = segName;
+ //
+ // Populate the "raw data" table. We add the fields shown in the specification in
+ // the order in which they appear.
+ //
+
+ if (newSeg.Version == SegmentVersion.v0_0 ||
+ (newSeg.Version == SegmentVersion.v1_0 && !parseAsLibrary)) {
+ newSeg.AddRaw("BLKCNT", blkByteCnt, 4, "blocks");
+ } else {
+ newSeg.AddRaw("BYTECNT", blkByteCnt, 4, "bytes");
+ }
+ newSeg.AddRaw("RESSPC", newSeg.ResSpc, 4, string.Empty);
+ newSeg.AddRaw("LENGTH", newSeg.Length, 4, string.Empty);
+ if (newSeg.Version <= SegmentVersion.v1_0) {
+ string attrStr = AttrsToString(newSeg.Attrs);
+ if (!string.IsNullOrEmpty(attrStr)) {
+ attrStr = " -" + attrStr;
+ }
+ newSeg.AddRaw("KIND", data[offset+0x0c], 1,
+ KindToString(newSeg.Kind) + attrStr);
+ } else {
+ newSeg.AddRaw("undefined", data[offset + 0x0c], 1, string.Empty);
+ }
+ newSeg.AddRaw("LABLEN", newSeg.LabLen, 1, string.Empty);
+ newSeg.AddRaw("NUMLEN", numLen, 1, "must be 4");
+ newSeg.AddRaw("VERSION", data[offset + 0x0f], 1, VersionToString(newSeg.Version));
+ newSeg.AddRaw("BANKSIZE", newSeg.BankSize, 4, string.Empty);
+ if (newSeg.Version >= SegmentVersion.v2_0) {
+ string attrStr = AttrsToString(newSeg.Attrs);
+ if (!string.IsNullOrEmpty(attrStr)) {
+ attrStr = " -" + attrStr;
+ }
+ newSeg.AddRaw("KIND", RawData.GetWord(data, offset + 0x14, 2, false), 2,
+ KindToString(newSeg.Kind) + attrStr);
+ newSeg.AddRaw("undefined", RawData.GetWord(data, offset + 0x16, 2, false), 2,
+ string.Empty);
+ } else {
+ newSeg.AddRaw("undefined", RawData.GetWord(data, offset + 0x14, 4, false), 4,
+ string.Empty);
+ }
+ newSeg.AddRaw("ORG", newSeg.Org, 4, string.Empty);
+ newSeg.AddRaw("ALIGN", newSeg.Align, 4, string.Empty);
+ newSeg.AddRaw("NUMSEX", numSex, 1, "must be 0");
+ if (newSeg.Version == SegmentVersion.v1_0) {
+ newSeg.AddRaw("LCBANK", newSeg.LcBank, 1, string.Empty);
+ } else {
+ newSeg.AddRaw("undefined", data[offset + 0x21], 1, string.Empty);
+ }
+ if (newSeg.Version >= SegmentVersion.v1_0) {
+ newSeg.AddRaw("SEGNUM", newSeg.SegNum, 2, string.Empty);
+ newSeg.AddRaw("ENTRY", newSeg.Entry, 4, string.Empty);
+ newSeg.AddRaw("DISPNAME", dispName, 2, string.Empty);
+ newSeg.AddRaw("DISPDATA", dispData, 2, string.Empty);
+ if (newSeg.Version >= SegmentVersion.v2_1) {
+ newSeg.AddRaw("tempORG", newSeg.TempOrg, 4, string.Empty);
+ }
+ newSeg.AddRaw("LOADNAME", loadName, 10, string.Empty);
+ }
+ newSeg.AddRaw("SEGNAME", segName, 0, string.Empty);
+
segResult = newSeg;
return ParseResult.Success;
}
+ //
+ // Helper functions.
+ //
+
+ private void AddRaw(string name, object value, int width, string note) {
+ if (value is byte) {
+ value = (int)(byte)value;
+ }
+ RawValues.Add(new NameValueNote(name, value, width, note));
+ }
+ private static void AddInfoMsg(List msgs, int offset, string msg) {
+ msgs.Add("Note (+" + offset.ToString("x6") + "): " + msg);
+ }
+ private static void AddErrorMsg(List msgs, int offset, string msg) {
+ msgs.Add("Error (+" + offset.ToString("x6") + "): " + msg);
+ }
+
+ ///
+ /// Extracts a fixed-length ASCII string, stopping early if a '\0' is encountered.
+ ///
private static string ExtractString(byte[] data, int offset, int len) {
StringBuilder sb = new StringBuilder();
for (int i = offset; i < offset + len; i++) {
@@ -388,5 +506,54 @@ namespace SourceGen.Tools.Omf {
}
return sb.ToString();
}
+
+ ///
+ /// Converts a segment version to a human-readable string.
+ ///
+ public static string VersionToString(SegmentVersion vers) {
+ switch (vers) {
+ case SegmentVersion.v0_0: return "v0.0";
+ case SegmentVersion.v1_0: return "v1.0";
+ case SegmentVersion.v2_0: return "v2.0";
+ case SegmentVersion.v2_1: return "v2.1";
+ default: return "v?.?";
+ }
+ }
+
+ ///
+ /// Converts a segment kind to a human-readable string.
+ ///
+ public static string KindToString(SegmentKind kind) {
+ switch (kind) {
+ case SegmentKind.Code: return "Code";
+ case SegmentKind.Data: return "Data";
+ case SegmentKind.JumpTable: return "Jump Table";
+ case SegmentKind.PathName: return "Pathname";
+ case SegmentKind.LibraryDict: return "Library Dict";
+ case SegmentKind.Init: return "Init";
+ case SegmentKind.AbsoluteBank: return "Abs Bank";
+ case SegmentKind.DpStack: return "DP/Stack";
+ default: return "???";
+ }
+ }
+
+ public static string AttrsToString(SegmentAttribute attrs) {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < 16; i++) {
+ int bit = 1 << i;
+ if (((int)attrs & bit) != 0) {
+ SegmentAttribute attr = (SegmentAttribute)bit;
+ sb.Append(' ');
+ sb.Append(attr.ToString());
+ }
+ }
+ return sb.ToString();
+ }
+
+
+ public override string ToString() {
+ return "[OmfSegment " + SegNum + " '" + LoadName + "' '" + SegName + "']";
+ }
}
}
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml
new file mode 100644
index 0000000..c9c59f0
--- /dev/null
+++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+ File offset {0}, length {1} ({2})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs
new file mode 100644
index 0000000..7bfe3a6
--- /dev/null
+++ b/SourceGen/Tools/Omf/WpfGui/OmfSegmentViewer.xaml.cs
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2020 faddenSoft
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+
+using Asm65;
+
+namespace SourceGen.Tools.Omf.WpfGui {
+ ///
+ /// Apple IIgs OMF segment viewer.
+ ///
+ public partial class OmfSegmentViewer : Window, INotifyPropertyChanged {
+ private OmfFile mOmfFile;
+ private OmfSegment mOmfSeg;
+ private Formatter mFormatter;
+
+ // INotifyPropertyChanged implementation
+ public event PropertyChangedEventHandler PropertyChanged;
+ private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ private string mFileOffsetLen;
+ public string FileOffsetLen {
+ get { return mFileOffsetLen; }
+ set { mFileOffsetLen = value; OnPropertyChanged(); }
+ }
+
+ public class HeaderItem {
+ public string Name { get; private set; }
+ public string Value { get; private set; }
+ public string Note { get; private set; }
+
+ public HeaderItem(string name, string value, string note) {
+ Name = name;
+ Value = value;
+ Note = note;
+ }
+ }
+ public List HeaderItems { get; private set; } = new List();
+
+ public class RecordItem {
+ public string Type { get; private set; }
+ public string Len { get; private set; }
+ public string Value { get; private set; }
+ }
+ public List RecordItems { get; private set; } = new List();
+
+ public class RelocItem {
+ public string Offset { get; private set; }
+ public string Reference { get; private set; }
+ public string Width { get; private set; }
+ public string Shift { get; private set; }
+ }
+ public List RelocItems { get; private set; } = new List();
+
+
+ ///
+ /// Constructor.
+ ///
+ /// Parent window.
+ /// OMF file object.
+ /// Segment to view. Must be part of omfFile.
+ /// Text formatter.
+ public OmfSegmentViewer(Window owner, OmfFile omfFile, OmfSegment omfSeg,
+ Formatter formatter) {
+ InitializeComponent();
+ Owner = owner;
+ DataContext = this;
+
+ mOmfFile = omfFile;
+ mOmfSeg = omfSeg;
+ mFormatter = formatter;
+
+ string fmt = (string)FindResource("str_FileOffsetLenFmt");
+ FileOffsetLen = string.Format(fmt,
+ mFormatter.FormatOffset24(omfSeg.FileOffset),
+ omfSeg.FileLength,
+ mFormatter.FormatHexValue(omfSeg.FileLength, 4));
+
+ GenerateHeaderItems();
+ }
+
+ private void GenerateHeaderItems() {
+ foreach (OmfSegment.NameValueNote nvn in mOmfSeg.RawValues) {
+ string value;
+ if (nvn.Value is int) {
+ int byteWidth = nvn.Width;
+ if (byteWidth > 3) {
+ byteWidth = 3;
+ }
+ value = mFormatter.FormatHexValue((int)nvn.Value, byteWidth * 2);
+ } else {
+ value = nvn.Value.ToString();
+ }
+ HeaderItems.Add(new HeaderItem(nvn.Name, value, nvn.Note));
+ }
+ }
+ }
+}
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
index b46154d..fe34d0b 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
+++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml
@@ -41,12 +41,12 @@ limitations under the License.
-
+
-
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
index b8a0206..ba6f167 100644
--- a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
+++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs
@@ -19,15 +19,19 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
+using System.Windows.Controls;
using System.Windows.Input;
+using Asm65;
+
namespace SourceGen.Tools.Omf.WpfGui {
///
/// Apple IIgs OMF file viewer.
///
public partial class OmfViewer : Window, INotifyPropertyChanged {
- private string mPathName;
private byte[] mFileData;
+ private OmfFile mOmfFile;
+ private Formatter mFormatter;
//private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
@@ -38,65 +42,81 @@ namespace SourceGen.Tools.Omf.WpfGui {
}
public class SegmentListItem {
- private OmfSegment mOmfSeg;
+ public OmfSegment OmfSeg { get; private set; }
public int SegNum {
- get {
- return mOmfSeg.SegNum;
- }
+ get { return OmfSeg.SegNum; }
+ }
+ public string Version {
+ get { return OmfSegment.VersionToString(OmfSeg.Version); }
}
public string Kind {
- get {
- return mOmfSeg.Kind.ToString();
- }
+ get { return OmfSegment.KindToString(OmfSeg.Kind); }
}
public string LoadName {
- get {
- return mOmfSeg.LoadName;
- }
+ get { return OmfSeg.LoadName; }
}
public string SegName {
- get {
- return mOmfSeg.SegName;
- }
+ get { return OmfSeg.SegName; }
}
- public int MemLength {
- get {
- return mOmfSeg.Length;
- }
+ public int Length {
+ get { return OmfSeg.Length; }
}
public int FileLength {
- get {
- return mOmfSeg.FileLength;
- }
+ get { return OmfSeg.FileLength; }
}
public SegmentListItem(OmfSegment omfSeg) {
- mOmfSeg = omfSeg;
+ OmfSeg = omfSeg;
+ }
+ public override string ToString() {
+ return "[SegmentListItem " + OmfSeg + "]";
}
}
public List SegmentListItems { get; private set; } = new List();
+ private string mPathName;
+ public string PathName {
+ get { return mPathName; }
+ set { mPathName = value; OnPropertyChanged(); }
+ }
- public OmfViewer(Window owner, string pathName, byte[] data) {
+ private string mMessageStrings;
+ public string MessageStrings {
+ get { return mMessageStrings; }
+ set { mMessageStrings = value; OnPropertyChanged(); }
+ }
+
+
+ ///
+ /// Constructor.
+ ///
+ /// Parent window.
+ /// Path to file on disk. Only used for display.
+ /// File contents.
+ public OmfViewer(Window owner, string pathName, byte[] data, Formatter formatter) {
InitializeComponent();
Owner = owner;
DataContext = this;
- mPathName = pathName;
+ PathName = pathName;
mFileData = data;
+ mFormatter = formatter;
- OmfFile omfFile = new OmfFile(data);
- omfFile.Analyze();
+ mOmfFile = new OmfFile(data);
+ mOmfFile.Analyze();
- foreach (OmfSegment omfSeg in omfFile.SegmentList) {
+ foreach (OmfSegment omfSeg in mOmfFile.SegmentList) {
SegmentListItems.Add(new SegmentListItem(omfSeg));
}
+ MessageStrings = string.Join("\r\n", mOmfFile.MessageList.ToArray());
}
private void SegmentList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
- Debug.WriteLine("DCLICK");
+ SegmentListItem item = (SegmentListItem)((DataGrid)sender).SelectedItem;
+ OmfSegmentViewer dlg = new OmfSegmentViewer(this, mOmfFile, item.OmfSeg, mFormatter);
+ dlg.ShowDialog();
}
}
}