From 5026fd656989b68957374ae28e4c5e131d427872 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 23 Jun 2020 17:21:18 -0700 Subject: [PATCH] First step toward Apple IIgs OMF file handling This lays a bit of groundwork for an OMF file analyzer / viewer. --- SourceGen/MainController.cs | 83 ++++++++++++----- SourceGen/Res/Strings.xaml | 1 + SourceGen/Res/Strings.xaml.cs | 2 + SourceGen/SourceGen.csproj | 8 ++ SourceGen/Tools/Omf/OmfFile.cs | 58 ++++++++++++ SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml | 89 +++++++++++++++++++ SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs | 68 ++++++++++++++ .../Tools/WpfGui/FileConcatenator.xaml.cs | 2 +- SourceGen/Tools/WpfGui/FileSlicer.xaml.cs | 6 +- SourceGen/WpfGui/MainWindow.xaml | 4 + SourceGen/WpfGui/MainWindow.xaml.cs | 4 + 11 files changed, 298 insertions(+), 27 deletions(-) create mode 100644 SourceGen/Tools/Omf/OmfFile.cs create mode 100644 SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml create mode 100644 SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml.cs diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index d6be468..0fbd915 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -797,7 +797,7 @@ namespace SourceGen { /// /// Full pathname. /// Data file contents. - private byte[] LoadDataFile(string dataFileName) { + private static byte[] LoadDataFile(string dataFileName) { byte[] fileData; using (FileStream fs = File.Open(dataFileName, FileMode.Open, FileAccess.Read)) { @@ -4093,15 +4093,10 @@ namespace SourceGen { } public void ShowFileHexDump() { - OpenFileDialog fileDlg = new OpenFileDialog() { - Filter = Res.Strings.FILE_FILTER_ALL, - FilterIndex = 1 - }; - if (fileDlg.ShowDialog() != true) { + if (!OpenAnyFile(out string pathName)) { return; } - string fileName = fileDlg.FileName; - FileInfo fi = new FileInfo(fileName); + FileInfo fi = new FileInfo(pathName); if (fi.Length > Tools.WpfGui.HexDumpViewer.MAX_LENGTH) { string msg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT, fi.Length / 1024, Tools.WpfGui.HexDumpViewer.MAX_LENGTH / 1024); @@ -4111,7 +4106,7 @@ namespace SourceGen { } byte[] data; try { - data = File.ReadAllBytes(fileName); + data = File.ReadAllBytes(pathName); } catch (Exception ex) { // not expecting this to happen MessageBox.Show(ex.Message); @@ -4121,7 +4116,7 @@ namespace SourceGen { // Create the dialog without an owner, and add it to the "unowned" list. Tools.WpfGui.HexDumpViewer dlg = new Tools.WpfGui.HexDumpViewer(null, data, mFormatter); - dlg.SetFileName(Path.GetFileName(fileName)); + dlg.SetFileName(Path.GetFileName(pathName)); dlg.Closing += (sender, e) => { Debug.WriteLine("Window " + dlg + " closed, removing from unowned list"); mUnownedWindows.Remove(dlg); @@ -4137,18 +4132,66 @@ namespace SourceGen { } public void SliceFiles() { + if (!OpenAnyFile(out string pathName)) { + return; + } + + Tools.WpfGui.FileSlicer slicer = new Tools.WpfGui.FileSlicer(this.mMainWin, pathName, + mFormatter); + slicer.ShowDialog(); + } + + public void ConvertOmf() { + if (!OpenAnyFile(out string pathName)) { + return; + } + + // Load the file here, so basic problems like empty / oversized files can be + // reported immediately. + + byte[] fileData = null; + using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) { + string errMsg = null; + + if (fs.Length == 0) { + errMsg = Res.Strings.OPEN_DATA_EMPTY; + } else if (fs.Length < Tools.Omf.OmfFile.MIN_FILE_SIZE) { + errMsg = string.Format(Res.Strings.OPEN_DATA_TOO_SMALL_FMT, fs.Length); + } else if (fs.Length > Tools.Omf.OmfFile.MAX_FILE_SIZE) { + errMsg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT, + fs.Length / 1024, Tools.Omf.OmfFile.MAX_FILE_SIZE / 1024); + } + if (errMsg == null) { + fileData = new byte[fs.Length]; + int actual = fs.Read(fileData, 0, (int)fs.Length); + if (actual != fs.Length) { + errMsg = Res.Strings.OPEN_DATA_PARTIAL_READ; + } + } + + if (errMsg != null) { + MessageBox.Show(errMsg, Res.Strings.ERR_FILE_GENERIC_CAPTION, + MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + } + + Tools.Omf.WpfGui.OmfViewer ov = + new Tools.Omf.WpfGui.OmfViewer(this.mMainWin, pathName, fileData); + ov.ShowDialog(); + } + + private bool OpenAnyFile(out string pathName) { OpenFileDialog fileDlg = new OpenFileDialog() { Filter = Res.Strings.FILE_FILTER_ALL, FilterIndex = 1 }; if (fileDlg.ShowDialog() != true) { - return; + pathName = null; + return false; } - string pathName = Path.GetFullPath(fileDlg.FileName); - - Tools.WpfGui.FileSlicer slicer = new Tools.WpfGui.FileSlicer(this.mMainWin, pathName, - mFormatter); - slicer.ShowDialog(); + pathName = Path.GetFullPath(fileDlg.FileName); + return true; } #endregion Tools @@ -4240,16 +4283,11 @@ namespace SourceGen { } public void Debug_ApplesoftToHtml() { - OpenFileDialog fileDlg = new OpenFileDialog() { - Filter = Res.Strings.FILE_FILTER_ALL, - FilterIndex = 1 - }; - if (fileDlg.ShowDialog() != true) { + if (!OpenAnyFile(out string basPathName)) { return; } byte[] data; - string basPathName = Path.GetFullPath(fileDlg.FileName); try { data = File.ReadAllBytes(basPathName); } catch (IOException ex) { @@ -4326,6 +4364,7 @@ namespace SourceGen { ApplyUndoableChanges(cs); } + // Disable "analyze uncategorized data" for best results. public void Debug_ApplyPlatformSymbols() { ChangeSet cs = new ChangeSet(1); diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index ebda1b4..c28d9ba 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -137,6 +137,7 @@ limitations under the License. The file could not be opened: {0}. Unable to read the entire file File is too large ({0:N0} KiB, max is {1:N0} KiB). + File is too small ({0} bytes). The file is {0:N0} bytes long, but the project expected {1:N0}. The file has CRC {0}, but the project expected {1}. Failed diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index a427e24..86fd659 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -255,6 +255,8 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_OpenDataLoadFailedFmt"); public static string OPEN_DATA_TOO_LARGE_FMT = (string)Application.Current.FindResource("str_OpenDataTooLargeFmt"); + public static string OPEN_DATA_TOO_SMALL_FMT = + (string)Application.Current.FindResource("str_OpenDataTooSmallFmt"); public static string OPEN_DATA_WRONG_CRC_FMT = (string)Application.Current.FindResource("str_OpenDataWrongCrcFmt"); public static string OPEN_DATA_WRONG_LENGTH_FMT = diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index 16d0f7d..7f7cf90 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -85,6 +85,10 @@ GenTestRunner.xaml + + + OmfViewer.xaml + FileConcatenator.xaml @@ -276,6 +280,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/SourceGen/Tools/Omf/OmfFile.cs b/SourceGen/Tools/Omf/OmfFile.cs new file mode 100644 index 0000000..85d8cc0 --- /dev/null +++ b/SourceGen/Tools/Omf/OmfFile.cs @@ -0,0 +1,58 @@ +/* + * 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; + +namespace SourceGen.Tools.Omf { + /// + /// Apple IIgs OMF file. + /// + /// + /// References: + /// - "Apple IIgs Programmer's Workshop Reference". Chapter 7, page 228, describes + /// OMF v1.0 and v2.0. + /// - "Apple IIgs GS/OS Reference, for GS/OS System Software Version 5.0 and later". + /// Appendix F describes OMF v2.1, and Chapter 8 has some useful information about + /// how the loader works. + /// - "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. + /// - Apple IIgs Tech Note #66, "ExpressLoad Philosophy". + /// + /// Related: + /// - https://www.brutaldeluxe.fr/products/crossdevtools/omfanalyzer/ + /// - https://github.com/fadden/ciderpress/blob/master/reformat/Disasm.cpp + /// + public class OmfFile { + public const int MIN_FILE_SIZE = 37; // can't be smaller than v0 segment hdr + public const int MAX_FILE_SIZE = (1 << 24) - 1; // cap it at 16MB + + // TODO: + // - has an overall file type (load, object, RTL) + // - determine with a prioritized series of "could this be ____" checks + // - holds list of OmfSegment + // - has a list of warnings and errors that arose during parsing + // - holds on to byte[] with data + // OmfSegment: + // - header (common data, plus name/value dict with version-specific fields for display) + // - ref back to OmfFile for byte[] access? + // - list of OmfRecord + // - file-type-specific stuff can be generated and cached in second pass, e.g. + // generate a full relocation dictionary for load files (can't do this until we + // know the overall file type, which we can't know until all segments have been + // processed a bit) + } +} diff --git a/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml new file mode 100644 index 0000000..c55b04a --- /dev/null +++ b/SourceGen/Tools/Omf/WpfGui/OmfViewer.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +