mirror of
https://github.com/claunia/libexeinfo.git
synced 2025-12-16 19:14:24 +00:00
Add support for NE OS/2 resource table format and resource types.
This commit is contained in:
@@ -41,6 +41,12 @@ namespace libexeinfo
|
||||
/// </summary>
|
||||
const string STRING_FILE_INFO = "StringFileInfo";
|
||||
|
||||
const ushort SEGMENT_TYPE_MASK = 0x07;
|
||||
const ushort SEGMENT_FLAGS_MASK = 0x3F8;
|
||||
const ushort SEGMENT_DISCARD_MASK = 0xF000;
|
||||
const ushort SEGMENT_IOPRVL_MASK = 0xC00;
|
||||
const ushort KNOWN_RSRC_FLAGS = 0x1070;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of a resource type according to its identifier
|
||||
/// </summary>
|
||||
@@ -80,10 +86,38 @@ namespace libexeinfo
|
||||
}
|
||||
}
|
||||
|
||||
const ushort SEGMENT_TYPE_MASK = 0x07;
|
||||
const ushort SEGMENT_FLAGS_MASK = 0x3F8;
|
||||
const ushort SEGMENT_DISCARD_MASK = 0xF000;
|
||||
const ushort SEGMENT_IOPRVL_MASK = 0xC00;
|
||||
const ushort KNOWN_RSRC_FLAGS = 0x1070;
|
||||
/// <summary>
|
||||
/// Gets the name of a resource type according to its identifier
|
||||
/// </summary>
|
||||
/// <returns>The resource type name.</returns>
|
||||
/// <param name="id">Resource type identifier.</param>
|
||||
public static string ResourceIdToNameOs2(ushort id)
|
||||
{
|
||||
switch(id)
|
||||
{
|
||||
case (int)Os2ResourceTypes.RT_POINTER: return "RT_POINTER";
|
||||
case (int)Os2ResourceTypes.RT_BITMAP: return "RT_BITMAP";
|
||||
case (int)Os2ResourceTypes.RT_MENU: return "RT_MENU";
|
||||
case (int)Os2ResourceTypes.RT_DIALOG: return "RT_DIALOG";
|
||||
case (int)Os2ResourceTypes.RT_STRING: return "RT_STRING";
|
||||
case (int)Os2ResourceTypes.RT_FONTDIR: return "RT_FONTDIR";
|
||||
case (int)Os2ResourceTypes.RT_FONT: return "RT_FONT";
|
||||
case (int)Os2ResourceTypes.RT_ACCELTABLE: return "RT_ACCELTABLE";
|
||||
case (int)Os2ResourceTypes.RT_RCDATA: return "RT_RCDATA";
|
||||
case (int)Os2ResourceTypes.RT_MESSAGE: return "RT_MESSAGE";
|
||||
case (int)Os2ResourceTypes.RT_DLGINCLUDE: return "RT_DLGINCLUDE";
|
||||
case (int)Os2ResourceTypes.RT_VKEYTBL: return "RT_VKEYTBL";
|
||||
case (int)Os2ResourceTypes.RT_KEYTBL: return "RT_KEYTBL";
|
||||
case (int)Os2ResourceTypes.RT_CHARTBL: return "RT_CHARTBL";
|
||||
case (int)Os2ResourceTypes.RT_DISPLAYINFO: return "RT_DISPLAYINFO";
|
||||
case (int)Os2ResourceTypes.RT_FKASHORT: return "RT_FKASHORT";
|
||||
case (int)Os2ResourceTypes.RT_FKALONG: return "RT_FKALONG";
|
||||
case (int)Os2ResourceTypes.RT_HELPTABLE: return "RT_HELPTABLE";
|
||||
case (int)Os2ResourceTypes.RT_HELPSUBTABLE: return "RT_HELPSUBTABLE";
|
||||
case (int)Os2ResourceTypes.RT_FDDIR: return "RT_FDDIR";
|
||||
case (int)Os2ResourceTypes.RT_FD: return "RT_FD";
|
||||
default: return $"{id}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,55 @@ namespace libexeinfo
|
||||
GangloadArea = 1 << 3
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource types.
|
||||
/// </summary>
|
||||
public enum Os2ResourceTypes : ushort
|
||||
{
|
||||
/// <summary>mouse pointer shape</summary>
|
||||
RT_POINTER = 1,
|
||||
/// <summary>bitmap</summary>
|
||||
RT_BITMAP = 2,
|
||||
/// <summary>menu template</summary>
|
||||
RT_MENU = 3,
|
||||
/// <summary>dialog template</summary>
|
||||
RT_DIALOG = 4,
|
||||
/// <summary>string tables</summary>
|
||||
RT_STRING = 5,
|
||||
/// <summary>font directory</summary>
|
||||
RT_FONTDIR = 6,
|
||||
/// <summary>font</summary>
|
||||
RT_FONT = 7,
|
||||
/// <summary>accelerator tables</summary>
|
||||
RT_ACCELTABLE = 8,
|
||||
/// <summary>binary data</summary>
|
||||
RT_RCDATA = 9,
|
||||
/// <summary>error msg tables</summary>
|
||||
RT_MESSAGE = 10,
|
||||
/// <summary>dialog include file name</summary>
|
||||
RT_DLGINCLUDE = 11,
|
||||
/// <summary>key to vkey tables</summary>
|
||||
RT_VKEYTBL = 12,
|
||||
/// <summary>key to UGL tables</summary>
|
||||
RT_KEYTBL = 13,
|
||||
/// <summary>glyph to character tables</summary>
|
||||
RT_CHARTBL = 14,
|
||||
/// <summary>screen display information</summary>
|
||||
RT_DISPLAYINFO = 15,
|
||||
/// <summary>function key area short form</summary>
|
||||
RT_FKASHORT = 16,
|
||||
/// <summary>function key area long form</summary>
|
||||
RT_FKALONG = 17,
|
||||
/// <summary>Help table for IPFC</summary>
|
||||
RT_HELPTABLE = 18,
|
||||
/// <summary>Help subtable for IPFC</summary>
|
||||
RT_HELPSUBTABLE = 19,
|
||||
/// <summary>DBCS uniq/font driver directory</summary>
|
||||
RT_FDDIR = 20,
|
||||
/// <summary>DBCS uniq/font driver</summary>
|
||||
RT_FD = 21
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Program flags.
|
||||
/// </summary>
|
||||
@@ -78,9 +127,9 @@ namespace libexeinfo
|
||||
[Flags]
|
||||
public enum ResourceFlags : ushort
|
||||
{
|
||||
Moveable = 0x10,
|
||||
Pure = 0x20,
|
||||
Preload = 0x40,
|
||||
Moveable = 0x10,
|
||||
Pure = 0x20,
|
||||
Preload = 0x40,
|
||||
Discardable = 0x1000
|
||||
}
|
||||
|
||||
@@ -118,6 +167,57 @@ namespace libexeinfo
|
||||
RT_NEW = 0x2000
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SegmentFlags : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Segment data is iterated
|
||||
/// </summary>
|
||||
Iterated = 0x08,
|
||||
/// <summary>
|
||||
/// Segment is not fixed
|
||||
/// </summary>
|
||||
Moveable = 0x10,
|
||||
/// <summary>
|
||||
/// Segment can be shared
|
||||
/// </summary>
|
||||
Shared = 0x20,
|
||||
/// <summary>
|
||||
/// Segment will be preloaded; read-only if this is a data segment
|
||||
/// </summary>
|
||||
Preload = 0x40,
|
||||
/// <summary>
|
||||
/// Code segment is execute only; data segment is read-only
|
||||
/// </summary>
|
||||
Eronly = 0x80,
|
||||
/// <summary>
|
||||
/// Segment has relocation records
|
||||
/// </summary>
|
||||
Relocinfo = 0x100,
|
||||
/// <summary>
|
||||
/// Segment is conforming
|
||||
/// </summary>
|
||||
Conform = 0x200,
|
||||
/// <summary>
|
||||
/// Discardable
|
||||
/// </summary>
|
||||
Discardable = 0x1000,
|
||||
/// <summary>
|
||||
/// 32-bit code segment
|
||||
/// </summary>
|
||||
Code32 = 0x2000,
|
||||
/// <summary>
|
||||
/// Length of segment and minimum allocation size are in units of segment sector size
|
||||
/// </summary>
|
||||
Huge = 0x4000
|
||||
}
|
||||
|
||||
public enum SegmentType : ushort
|
||||
{
|
||||
Code = 0,
|
||||
Data = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Target operating system.
|
||||
/// </summary>
|
||||
@@ -217,48 +317,5 @@ namespace libexeinfo
|
||||
VFT_UNKNOWN = 0x00000000,
|
||||
VFT_VXD = 0x00000005
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SegmentFlags : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Segment data is iterated
|
||||
/// </summary>
|
||||
Iterated = 0x08,
|
||||
/// <summary>
|
||||
/// Segment is not fixed
|
||||
/// </summary>
|
||||
Moveable = 0x10,
|
||||
/// <summary>
|
||||
/// Segment can be shared
|
||||
/// </summary>
|
||||
Shared = 0x20,
|
||||
/// <summary>
|
||||
/// Segment will be preloaded; read-only if this is a data segment
|
||||
/// </summary>
|
||||
Preload = 0x40,
|
||||
/// <summary>
|
||||
/// Code segment is execute only; data segment is read-only
|
||||
/// </summary>
|
||||
Eronly = 0x80,
|
||||
/// <summary>
|
||||
/// Segment has relocation records
|
||||
/// </summary>
|
||||
Relocinfo = 0x100,
|
||||
/// <summary>
|
||||
/// Segment is conforming
|
||||
/// </summary>
|
||||
Conform = 0x200,
|
||||
/// <summary>
|
||||
/// Length of segment and minimum allocation size are in units of segment sector size
|
||||
/// </summary>
|
||||
Huge = 0x4000
|
||||
}
|
||||
|
||||
public enum SegmentType : ushort
|
||||
{
|
||||
Code = 0,
|
||||
Data = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,19 +216,86 @@ namespace libexeinfo
|
||||
if(Header.imported_names_offset >= Header.resource_table_offset &&
|
||||
Header.imported_names_offset <= resourceUpperLimit) resourceUpperLimit = Header.imported_names_offset;
|
||||
|
||||
if(Header.resource_table_offset < resourceUpperLimit && Header.resource_table_offset != 0 &&
|
||||
(Header.target_os == TargetOS.Windows || Header.target_os == TargetOS.Win32))
|
||||
{
|
||||
Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset,
|
||||
resourceUpperLimit);
|
||||
if(Header.resource_table_offset < resourceUpperLimit && Header.resource_table_offset != 0)
|
||||
if(Header.target_os == TargetOS.Windows || Header.target_os == TargetOS.Win32)
|
||||
{
|
||||
Resources = GetResources(BaseStream, BaseExecutable.Header.new_offset, Header.resource_table_offset,
|
||||
resourceUpperLimit);
|
||||
|
||||
for(int t = 0; t < Resources.types.Length; t++)
|
||||
Resources.types[t].resources = Resources.types[t].resources.OrderBy(r => r.name).ToArray();
|
||||
for(int t = 0; t < Resources.types.Length; t++)
|
||||
Resources.types[t].resources = Resources.types[t].resources.OrderBy(r => r.name).ToArray();
|
||||
|
||||
Resources.types = Resources.types.OrderBy(t => t.name).ToArray();
|
||||
Resources.types = Resources.types.OrderBy(t => t.name).ToArray();
|
||||
|
||||
Versions = GetVersions().ToArray();
|
||||
}
|
||||
Versions = GetVersions().ToArray();
|
||||
}
|
||||
else if(Header.target_os == TargetOS.OS2 && segments != null && Header.resource_entries > 0)
|
||||
{
|
||||
BaseStream.Position = BaseExecutable.Header.new_offset + Header.resource_table_offset;
|
||||
buffer = new byte[Header.resource_entries * 4];
|
||||
BaseStream.Read(buffer, 0, buffer.Length);
|
||||
Os2ResourceTableEntry[] entries = new Os2ResourceTableEntry[Header.resource_entries];
|
||||
for(int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
entries[i].etype = BitConverter.ToUInt16(buffer, i * 4 + 0);
|
||||
entries[i].ename = BitConverter.ToUInt16(buffer, i * 4 + 2);
|
||||
}
|
||||
|
||||
SegmentEntry[] resourceSegments = new SegmentEntry[Header.resource_entries];
|
||||
Array.Copy(segments, Header.segment_count - Header.resource_entries, resourceSegments, 0,
|
||||
Header.resource_entries);
|
||||
SegmentEntry[] realSegments = new SegmentEntry[Header.segment_count - Header.resource_entries];
|
||||
Array.Copy(segments, 0, realSegments, 0, realSegments.Length);
|
||||
segments = realSegments;
|
||||
|
||||
SortedDictionary<ushort, List<Resource>> os2resources =
|
||||
new SortedDictionary<ushort, List<Resource>>();
|
||||
|
||||
for(int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
os2resources.TryGetValue(entries[i].etype, out List<Resource> thisResourceType);
|
||||
|
||||
if(thisResourceType == null) thisResourceType = new List<Resource>();
|
||||
|
||||
Resource thisResource = new Resource
|
||||
{
|
||||
id = entries[i].ename,
|
||||
name = $"{entries[i].ename}",
|
||||
flags = (ResourceFlags)resourceSegments[i].dwFlags,
|
||||
dataOffset = (uint)(resourceSegments[i].dwLogicalSectorOffset * 16),
|
||||
length = resourceSegments[i].dwSegmentLength
|
||||
};
|
||||
|
||||
if(thisResource.length == 0)
|
||||
thisResource.length = 65536;
|
||||
if(thisResource.dataOffset == 0)
|
||||
thisResource.dataOffset = 65536;
|
||||
if((resourceSegments[i].dwFlags & (ushort)SegmentFlags.Huge) == (ushort)SegmentFlags.Huge)
|
||||
thisResource.length *= 16;
|
||||
thisResource.data = new byte[thisResource.length];
|
||||
BaseStream.Position = thisResource.dataOffset;
|
||||
BaseStream.Read(thisResource.data, 0, thisResource.data.Length);
|
||||
|
||||
thisResourceType.Add(thisResource);
|
||||
os2resources.Remove(entries[i].etype);
|
||||
os2resources.Add(entries[i].etype, thisResourceType);
|
||||
}
|
||||
|
||||
if(os2resources.Count > 0)
|
||||
{
|
||||
Resources = new ResourceTable();
|
||||
int counter = 0;
|
||||
Resources.types = new ResourceType[os2resources.Count];
|
||||
foreach(KeyValuePair<ushort, List<Resource>> kvp in os2resources)
|
||||
{
|
||||
Resources.types[counter].count = (ushort)kvp.Value.Count;
|
||||
Resources.types[counter].id = kvp.Key;
|
||||
Resources.types[counter].name = ResourceIdToNameOs2(kvp.Key);
|
||||
Resources.types[counter].resources = kvp.Value.OrderBy(r => r.id).ToArray();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resourceUpperLimit = ushort.MaxValue;
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ namespace libexeinfo
|
||||
/// </summary>
|
||||
public struct Resource
|
||||
{
|
||||
public ushort dataOffset;
|
||||
public ushort length;
|
||||
public uint dataOffset;
|
||||
public uint length;
|
||||
public ResourceFlags flags;
|
||||
public ushort id;
|
||||
public uint reserved;
|
||||
@@ -176,5 +176,12 @@ namespace libexeinfo
|
||||
/// </summary>
|
||||
public ushort dwMinimumAllocation;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Os2ResourceTableEntry
|
||||
{
|
||||
public ushort etype;
|
||||
public ushort ename;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user