diff --git a/exeinfogui/LX/TabLxResources.xeto b/exeinfogui/LX/TabLxResources.xeto new file mode 100644 index 0000000..16ce3c0 --- /dev/null +++ b/exeinfogui/LX/TabLxResources.xeto @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/exeinfogui/LX/TabLxResources.xeto.cs b/exeinfogui/LX/TabLxResources.xeto.cs new file mode 100644 index 0000000..1ba6ef2 --- /dev/null +++ b/exeinfogui/LX/TabLxResources.xeto.cs @@ -0,0 +1,141 @@ +// +// TabLxResources.xeto.cs +// +// Author: +// Natalia Portillo +// +// Copyright (c) 2017-2018 Copyright © Claunia.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using exeinfogui.NE; +using exeinfogui.Os2; +using exeinfogui.Win16; +using exeinfogui.Windows; +using Eto.Forms; +using Eto.Serialization.Xaml; + +namespace exeinfogui.LX +{ + public class TabLxResources : TabPage + { + PanelHexDump panelHexDump; + PanelNeAccelerators panelNeAccelerators; + PanelNeStrings panelNeStrings; + PanelOs2Bitmap panelOs2Bitmap; + Panel pnlResource; + TreeGridItemCollection treeData; + TreeGridView treeResources; + PanelWindowsIcon panelWindowsIcon; + + public TabLxResources() + { + XamlReader.Load(this); + + treeResources.Columns.Add(new GridColumn {HeaderText = "Type", DataCell = new TextBoxCell(0)}); + treeResources.Columns.Add(new GridColumn {HeaderText = "Size", DataCell = new TextBoxCell(1)}); + + treeResources.AllowMultipleSelection = false; + treeResources.SelectionChanged += TreeResourcesOnSelectionChanged; + + panelNeStrings = new PanelNeStrings(); + panelNeAccelerators = new PanelNeAccelerators(); + panelHexDump = new PanelHexDump(); + panelOs2Bitmap = new PanelOs2Bitmap(); + panelWindowsIcon = new PanelWindowsIcon(); + } + + public void Update(IEnumerable resourceTypes) + { + treeData = new TreeGridItemCollection(); + + foreach(libexeinfo.NE.ResourceType resourceType in resourceTypes.OrderBy(r => r.name)) + { + TreeGridItem root = new TreeGridItem + { + Values = new object[] {$"{resourceType.name}", null, null, null} + }; + + foreach(libexeinfo.NE.Resource resource in resourceType.resources.OrderBy(r => r.name)) + root.Children.Add(new TreeGridItem + { + Values = new object[] + { + $"{resource.name}", $"{resource.data.Length}", $"{resourceType.name}", resource + } + }); + + treeData.Add(root); + } + + treeResources.DataStore = treeData; + } + + void TreeResourcesOnSelectionChanged(object sender, EventArgs eventArgs) + { + if(!(((TreeGridItem)treeResources.SelectedItem)?.Values[3] is libexeinfo.NE.Resource resource)) + { + pnlResource.Content = null; + return; + } + + byte[] data = ((libexeinfo.NE.Resource)((TreeGridItem)treeResources.SelectedItem).Values[3]).data; + + switch(((TreeGridItem)treeResources.SelectedItem).Values[2]) + { + case "RT_STRING": + pnlResource.Content = panelNeStrings; + panelNeStrings.Update(data, libexeinfo.NE.TargetOS.OS2); + break; + case "RT_ACCELTABLE": + pnlResource.Content = panelNeAccelerators; + panelNeAccelerators.Update(data, libexeinfo.NE.TargetOS.OS2); + break; + case "RT_BITMAP": + case "RT_POINTER": + // TODO: Some do not contain valid OS/2 bitmaps + try + { + pnlResource.Content = panelOs2Bitmap; + panelOs2Bitmap.Update(data); + } + catch { goto default; } + + break; + case "RT_MENU": + if(BitConverter.ToUInt32(data, 0) == 40) + { + // Some OS/2 executables contain Windows "RT_ICON" resources, in OS/2 NE format + pnlResource.Content = panelWindowsIcon; + panelWindowsIcon.Update(data); + break; + } + + goto default; + default: + pnlResource.Content = panelHexDump; + panelHexDump.Update(data); + break; + } + } + } +} \ No newline at end of file diff --git a/exeinfogui/MainForm.xeto.cs b/exeinfogui/MainForm.xeto.cs index 26c69ad..9bf4dda 100644 --- a/exeinfogui/MainForm.xeto.cs +++ b/exeinfogui/MainForm.xeto.cs @@ -28,6 +28,7 @@ using System; using System.Linq; using exeinfogui.GEM; using exeinfogui.LE; +using exeinfogui.LX; using exeinfogui.NE; using Eto.Forms; using Eto.Serialization.Xaml; @@ -50,6 +51,7 @@ namespace exeinfogui TextBox txtOs; TextBox txtSubsystem; TextBox txtType; + TabLxResources tabLxResources; public MainForm() { @@ -60,11 +62,13 @@ namespace exeinfogui tabGemResources = new TabGemResources {Visible = false}; tabNeResources = new TabNeResources {Visible = false}; tabLeVxdVersion = new TabLeVxdVersion {Visible = false}; + tabLxResources = new TabLxResources {Visible = false}; tabMain.Pages.Add(tabSegments); tabMain.Pages.Add(tabStrings); tabMain.Pages.Add(tabGemResources); tabMain.Pages.Add(tabNeResources); tabMain.Pages.Add(tabLeVxdVersion); + tabMain.Pages.Add(tabLxResources); } protected void OnBtnLoadClick(object sender, EventArgs e) @@ -80,6 +84,7 @@ namespace exeinfogui tabSegments.Visible = false; tabNeResources.Visible = false; tabLeVxdVersion.Visible = false; + tabLxResources.Visible = false; OpenFileDialog dlgOpen = new OpenFileDialog {Title = "Choose executable file", MultiSelect = false}; @@ -94,7 +99,7 @@ namespace exeinfogui IExecutable mzExe = new MZ(dlgOpen.FileName); IExecutable neExe = new libexeinfo.NE(dlgOpen.FileName); IExecutable stExe = new AtariST(dlgOpen.FileName); - IExecutable lxExe = new LX(dlgOpen.FileName); + IExecutable lxExe = new libexeinfo.LX(dlgOpen.FileName); IExecutable coffExe = new COFF(dlgOpen.FileName); IExecutable peExe = new PE(dlgOpen.FileName); IExecutable geosExe = new Geos(dlgOpen.FileName); @@ -123,10 +128,15 @@ namespace exeinfogui else if(lxExe.Recognized) { recognizedExe = lxExe; - if(((LX)lxExe).WinVersion != null) + if(((libexeinfo.LX)lxExe).WinVersion != null) { tabLeVxdVersion.Visible = true; - tabLeVxdVersion.Update(((LX)lxExe).WinVersion); + tabLeVxdVersion.Update(((libexeinfo.LX)lxExe).WinVersion); + } + if(((libexeinfo.LX)lxExe).neFormatResourceTable.types != null && ((libexeinfo.LX)lxExe).neFormatResourceTable.types.Any()) + { + tabLxResources.Update(((libexeinfo.LX)lxExe).neFormatResourceTable.types); + tabLxResources.Visible = true; } } else if(peExe.Recognized) recognizedExe = peExe; diff --git a/exeinfogui/exeinfogui.csproj b/exeinfogui/exeinfogui.csproj index c3206fc..7d0a932 100644 --- a/exeinfogui/exeinfogui.csproj +++ b/exeinfogui/exeinfogui.csproj @@ -26,5 +26,6 @@ + \ No newline at end of file diff --git a/libexeinfo/LX/LX.cs b/libexeinfo/LX/LX.cs index ea2a376..bdbb41d 100644 --- a/libexeinfo/LX/LX.cs +++ b/libexeinfo/LX/LX.cs @@ -30,6 +30,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; +using libexeinfo.Os2; namespace libexeinfo { @@ -47,6 +48,7 @@ namespace libexeinfo public NE.ResidentName[] ImportNames; string ModuleDescription; string ModuleName; + public NE.ResourceTable neFormatResourceTable; public NE.ResidentName[] NonResidentNames; ObjectPageTableEntry[] objectPageTableEntries; ObjectTableEntry[] objectTableEntries; @@ -329,6 +331,55 @@ namespace libexeinfo resources[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian(buffer); } + SortedDictionary> os2Resources = + new SortedDictionary>(); + + for(int i = 0; i < resources.Length; i++) + { + os2Resources.TryGetValue(resources[i].type, out List thisResourceType); + + if(thisResourceType == null) thisResourceType = new List(); + + NE.Resource thisResource = new NE.Resource + { + id = resources[i].id, + name = $"{resources[i].id}", + flags = 0, + dataOffset = (uint)(sections[resources[i].obj_no - 1].Offset + resources[i].offset), + length = resources[i].size + }; + + thisResource.data = new byte[thisResource.length]; + BaseStream.Position = thisResource.dataOffset; + BaseStream.Read(thisResource.data, 0, thisResource.data.Length); + + thisResourceType.Add(thisResource); + os2Resources.Remove(resources[i].type); + os2Resources.Add(resources[i].type, thisResourceType); + } + + if(os2Resources.Count > 0) + { + neFormatResourceTable = new NE.ResourceTable(); + int counter = 0; + neFormatResourceTable.types = new NE.ResourceType[os2Resources.Count]; + foreach(KeyValuePair> kvp in os2Resources) + { + neFormatResourceTable.types[counter].count = (ushort)kvp.Value.Count; + neFormatResourceTable.types[counter].id = (ushort)kvp.Key; + neFormatResourceTable.types[counter].name = NE.ResourceIdToNameOs2((ushort)kvp.Key); + neFormatResourceTable.types[counter].resources = kvp.Value.OrderBy(r => r.id).ToArray(); + counter++; + } + + foreach(NE.ResourceType rtype in neFormatResourceTable.types) + { + if(rtype.name != "RT_STRING") continue; + + strings.AddRange(NE.GetOs2Strings(rtype)); + } + } + Segments = sections; Strings = strings; }