From 5b974260cc6706b51598aa116f920f97227ad91d Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 11 Nov 2022 14:22:53 -0800 Subject: [PATCH] Add PE extended dialog templates --- BurnOutSharp.Builder/Extensions.cs | 98 ++++++++++++- .../PortableExecutable/DialogBoxResource.cs | 10 +- .../DialogItemTemplateExtended.cs | 131 ++++++++++++++++++ .../DialogTemplateExtended.cs | 16 +++ ExecutableTest/Program.cs | 107 +++++++------- 5 files changed, 305 insertions(+), 57 deletions(-) create mode 100644 BurnOutSharp.Models/PortableExecutable/DialogItemTemplateExtended.cs diff --git a/BurnOutSharp.Builder/Extensions.cs b/BurnOutSharp.Builder/Extensions.cs index f22ebae4..cc0a067a 100644 --- a/BurnOutSharp.Builder/Extensions.cs +++ b/BurnOutSharp.Builder/Extensions.cs @@ -607,13 +607,105 @@ namespace BurnOutSharp.Builder #endregion - dialogBoxResource.DialogTemplateExtended = dialogTemplateExtended; + dialogBoxResource.ExtendedDialogTemplate = dialogTemplateExtended; #endregion #region Extended dialog item templates - // TODO: Implement extended dialog item templates + var dialogItemExtendedTemplates = new List(); + + for (int i = 0; i < dialogTemplateExtended.DialogItems; i++) + { + var dialogItemTemplate = new Models.PortableExecutable.DialogItemTemplateExtended(); + + dialogItemTemplate.HelpID = entry.Data.ReadUInt32(ref offset); + dialogItemTemplate.ExtendedStyle = (Models.PortableExecutable.ExtendedWindowStyles)entry.Data.ReadUInt32(ref offset); + dialogItemTemplate.Style = (Models.PortableExecutable.WindowStyles)entry.Data.ReadUInt32(ref offset); + dialogItemTemplate.PositionX = entry.Data.ReadInt16(ref offset); + dialogItemTemplate.PositionY = entry.Data.ReadInt16(ref offset); + dialogItemTemplate.WidthX = entry.Data.ReadInt16(ref offset); + dialogItemTemplate.HeightY = entry.Data.ReadInt16(ref offset); + dialogItemTemplate.ID = entry.Data.ReadUInt16(ref offset); + + #region Class resource + + currentOffset = offset; + ushort itemClassResourceIdentifier = entry.Data.ReadUInt16(ref offset); + offset = currentOffset; + + // 0xFFFF means ordinal only + if (itemClassResourceIdentifier == 0xFFFF) + { + // Increment the pointer + _ = entry.Data.ReadUInt16(ref offset); + + // Read the ordinal + dialogItemTemplate.ClassResourceOrdinal = (Models.PortableExecutable.DialogItemTemplateOrdinal)entry.Data.ReadUInt16(ref offset); + } + else + { + // Flag if there's an ordinal at the end + bool classResourcehasOrdinal = itemClassResourceIdentifier == 0xFFFF; + if (classResourcehasOrdinal) + offset += sizeof(ushort); + + // Read the class resource as a string + dialogItemTemplate.ClassResource = entry.Data.ReadString(ref offset, Encoding.Unicode); + + // Align to the WORD boundary + while ((offset % 2) != 0) + _ = entry.Data.ReadByte(ref offset); + } + + #endregion + + #region Title resource + + currentOffset = offset; + ushort itemTitleResourceIdentifier = entry.Data.ReadUInt16(ref offset); + offset = currentOffset; + + // 0xFFFF means ordinal only + if (itemTitleResourceIdentifier == 0xFFFF) + { + // Increment the pointer + _ = entry.Data.ReadUInt16(ref offset); + + // Read the ordinal + dialogItemTemplate.TitleResourceOrdinal = entry.Data.ReadUInt16(ref offset); + } + else + { + // Read the title resource as a string + dialogItemTemplate.TitleResource = entry.Data.ReadString(ref offset, Encoding.Unicode); + + // Align to the WORD boundary + while ((offset % 2) != 0) + _ = entry.Data.ReadByte(ref offset); + } + + #endregion + + #region Creation data + + dialogItemTemplate.CreationDataSize = entry.Data.ReadUInt16(ref offset); + if (dialogItemTemplate.CreationDataSize != 0) + dialogItemTemplate.CreationData = entry.Data.ReadBytes(ref offset, dialogItemTemplate.CreationDataSize); + + #endregion + + // Align to the DWORD boundary if we're not at the end + if (offset != entry.Data.Length) + { + while ((offset % 4) != 0) + _ = entry.Data.ReadByte(ref offset); + } + + dialogItemExtendedTemplates.Add(dialogItemTemplate); + } + + dialogBoxResource.ExtendedDialogItemTemplates = dialogItemExtendedTemplates.ToArray(); #endregion } @@ -742,7 +834,7 @@ namespace BurnOutSharp.Builder #endregion - #region Dialog Item Templates + #region Dialog item templates var dialogItemTemplates = new List(); diff --git a/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs b/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs index 1326d82c..a1a3aa3f 100644 --- a/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs +++ b/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs @@ -19,7 +19,7 @@ /// /// Dialog box extended header structure /// - public DialogTemplateExtended DialogTemplateExtended; + public DialogTemplateExtended ExtendedDialogTemplate; #endregion @@ -33,6 +33,14 @@ /// public DialogItemTemplate[] DialogItemTemplates; + /// + /// Following the DLGTEMPLATEEX header in an extended dialog box template is one or more + /// DLGITEMTEMPLATEEX structures that describe the controls of the dialog box. The cDlgItems + /// member of the DLGITEMTEMPLATEEX structure specifies the number of DLGITEMTEMPLATEEX + /// structures that follow in the template. + /// + public DialogItemTemplateExtended[] ExtendedDialogItemTemplates; + #endregion } } diff --git a/BurnOutSharp.Models/PortableExecutable/DialogItemTemplateExtended.cs b/BurnOutSharp.Models/PortableExecutable/DialogItemTemplateExtended.cs new file mode 100644 index 00000000..058eeca4 --- /dev/null +++ b/BurnOutSharp.Models/PortableExecutable/DialogItemTemplateExtended.cs @@ -0,0 +1,131 @@ +using System.Runtime.InteropServices; + +namespace BurnOutSharp.Models.PortableExecutable +{ + /// + /// A block of text used by an extended dialog box template to describe the extended dialog box. + /// For a description of the format of an extended dialog box template, see DLGTEMPLATEEX. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public class DialogItemTemplateExtended + { + /// + /// The help context identifier for the control. When the system sends a WM_HELP message, + /// it passes the helpID value in the dwContextId member of the HELPINFO structure. + /// + public uint HelpID; + + /// + /// The extended styles for a window. This member is not used to create controls in dialog + /// boxes, but applications that use dialog box templates can use it to create other types + /// of windows. + /// + public ExtendedWindowStyles ExtendedStyle; + + /// + /// The style of the control. This member can be a combination of window style values + /// (such as WS_BORDER) and one or more of the control style values (such as + /// BS_PUSHBUTTON and ES_LEFT). + /// + public WindowStyles Style; + + /// + /// The x-coordinate, in dialog box units, of the upper-left corner of the control. + /// This coordinate is always relative to the upper-left corner of the dialog box's + /// client area. + /// + /// + /// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen + /// units (pixels) by using the MapDialogRect function. + /// + public short PositionX; + + /// + /// The y-coordinate, in dialog box units, of the upper-left corner of the control. + /// This coordinate is always relative to the upper-left corner of the dialog box's + /// client area. + /// + /// + /// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen + /// units (pixels) by using the MapDialogRect function. + /// + public short PositionY; + + /// + /// The width, in dialog box units, of the control. + /// + /// + /// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen + /// units (pixels) by using the MapDialogRect function. + /// + public short WidthX; + + /// + /// The height, in dialog box units, of the control. + /// + /// + /// The x, y, cx, and cy members specify values in dialog box units. You can convert these values to screen + /// units (pixels) by using the MapDialogRect function. + /// + public short HeightY; + + /// + /// The control identifier. + /// + public ushort ID; + + /// + /// A variable-length array of 16-bit elements that specifies the window class of the control. If + /// the first element of this array is any value other than 0xFFFF, the system treats the array as + /// a null-terminated Unicode string that specifies the name of a registered window class. + /// + /// If the first element is 0xFFFF, the array has one additional element that specifies the ordinal + /// value of a predefined system class. + /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// + public string ClassResource; + + /// + /// The ordinal value of a predefined system class. + /// + public DialogItemTemplateOrdinal ClassResourceOrdinal; + + /// + /// A variable-length array of 16-bit elements that contains the initial text or resource identifier of the + /// control. If the first element of this array is 0xFFFF, the array has one additional element that + /// specifies the ordinal value of a resource, such as an icon, in an executable file. You can use a + /// resource identifier for controls, such as static icon controls, that load and display an icon or other + /// resource rather than text. If the first element is any value other than 0xFFFF, the system treats the + /// array as a null-terminated Unicode string that specifies the initial text. + /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// + public string TitleResource; + + /// + /// An ordinal value of a resource, such as an icon, in an executable file + /// + public ushort TitleResourceOrdinal; + + /// + /// The creation data array begins at the next WORD boundary after the title array. This creation data + /// can be of any size and format. If the first word of the creation data array is nonzero, it indicates + /// the size, in bytes, of the creation data (including the size word). + /// + public ushort CreationDataSize; + + /// + /// The creation data array begins at the next WORD boundary after the title array. This creation data + /// can be of any size and format. The control's window procedure must be able to interpret the data. + /// When the system creates the control, it passes a pointer to this data in the lParam parameter of the + /// WM_CREATE message that it sends to the control. + /// + public byte[] CreationData; + } +} diff --git a/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs b/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs index aac642c6..0116b2fe 100644 --- a/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs +++ b/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs @@ -106,6 +106,10 @@ namespace BurnOutSharp.Models.PortableExecutable /// any other value, the system treats the array as a null-terminated Unicode string that specifies /// the name of a menu resource in an executable file. /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// public string MenuResource; /// @@ -120,6 +124,10 @@ namespace BurnOutSharp.Models.PortableExecutable /// window class. If the first element has any other value, the system treats the array as a /// null-terminated Unicode string that specifies the name of a registered window class. /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// public string ClassResource; /// @@ -131,6 +139,10 @@ namespace BurnOutSharp.Models.PortableExecutable /// The title of the dialog box. If the first element of this array is 0x0000, the dialog box has no /// title and the array has no other elements. /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// public string TitleResource; /// @@ -167,6 +179,10 @@ namespace BurnOutSharp.Models.PortableExecutable /// /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. /// + /// + /// If you specify character strings in the class and title arrays, you must use Unicode strings. Use the + /// MultiByteToWideChar function to generate Unicode strings from ANSI strings. + /// public string Typeface; } } diff --git a/ExecutableTest/Program.cs b/ExecutableTest/Program.cs index 4ccbce8a..df65c6b0 100644 --- a/ExecutableTest/Program.cs +++ b/ExecutableTest/Program.cs @@ -1248,62 +1248,63 @@ namespace ExecutableTest } } } - else if (dialogBox.DialogTemplateExtended != null) + else if (dialogBox.ExtendedDialogTemplate != null) { - Console.WriteLine($"{padding}Version: {dialogBox.DialogTemplateExtended.Version}"); - Console.WriteLine($"{padding}Signature: {dialogBox.DialogTemplateExtended.Signature}"); - Console.WriteLine($"{padding}Help ID: {dialogBox.DialogTemplateExtended.HelpID}"); - Console.WriteLine($"{padding}Extended style: {dialogBox.DialogTemplateExtended.ExtendedStyle}"); - Console.WriteLine($"{padding}Style: {dialogBox.DialogTemplateExtended.Style}"); - Console.WriteLine($"{padding}Item count: {dialogBox.DialogTemplateExtended.DialogItems}"); - Console.WriteLine($"{padding}X-coordinate of upper-left corner: {dialogBox.DialogTemplateExtended.PositionX}"); - Console.WriteLine($"{padding}Y-coordinate of upper-left corner: {dialogBox.DialogTemplateExtended.PositionY}"); - Console.WriteLine($"{padding}Width of the dialog box: {dialogBox.DialogTemplateExtended.WidthX}"); - Console.WriteLine($"{padding}Height of the dialog box: {dialogBox.DialogTemplateExtended.HeightY}"); - Console.WriteLine($"{padding}Menu resource: {dialogBox.DialogTemplateExtended.MenuResource ?? "[EMPTY]"}"); - Console.WriteLine($"{padding}Menu resource ordinal: {dialogBox.DialogTemplateExtended.MenuResourceOrdinal}"); - Console.WriteLine($"{padding}Class resource: {dialogBox.DialogTemplateExtended.ClassResource ?? "[EMPTY]"}"); - Console.WriteLine($"{padding}Class resource ordinal: {dialogBox.DialogTemplateExtended.ClassResourceOrdinal}"); - Console.WriteLine($"{padding}Title resource: {dialogBox.DialogTemplateExtended.TitleResource ?? "[EMPTY]"}"); - Console.WriteLine($"{padding}Point size: {dialogBox.DialogTemplateExtended.PointSize}"); - Console.WriteLine($"{padding}Weight: {dialogBox.DialogTemplateExtended.Weight}"); - Console.WriteLine($"{padding}Italic: {dialogBox.DialogTemplateExtended.Italic}"); - Console.WriteLine($"{padding}Character set: {dialogBox.DialogTemplateExtended.CharSet}"); - Console.WriteLine($"{padding}Typeface: {dialogBox.DialogTemplateExtended.Typeface ?? "[EMPTY]"}"); + Console.WriteLine($"{padding}Version: {dialogBox.ExtendedDialogTemplate.Version}"); + Console.WriteLine($"{padding}Signature: {dialogBox.ExtendedDialogTemplate.Signature}"); + Console.WriteLine($"{padding}Help ID: {dialogBox.ExtendedDialogTemplate.HelpID}"); + Console.WriteLine($"{padding}Extended style: {dialogBox.ExtendedDialogTemplate.ExtendedStyle}"); + Console.WriteLine($"{padding}Style: {dialogBox.ExtendedDialogTemplate.Style}"); + Console.WriteLine($"{padding}Item count: {dialogBox.ExtendedDialogTemplate.DialogItems}"); + Console.WriteLine($"{padding}X-coordinate of upper-left corner: {dialogBox.ExtendedDialogTemplate.PositionX}"); + Console.WriteLine($"{padding}Y-coordinate of upper-left corner: {dialogBox.ExtendedDialogTemplate.PositionY}"); + Console.WriteLine($"{padding}Width of the dialog box: {dialogBox.ExtendedDialogTemplate.WidthX}"); + Console.WriteLine($"{padding}Height of the dialog box: {dialogBox.ExtendedDialogTemplate.HeightY}"); + Console.WriteLine($"{padding}Menu resource: {dialogBox.ExtendedDialogTemplate.MenuResource ?? "[EMPTY]"}"); + Console.WriteLine($"{padding}Menu resource ordinal: {dialogBox.ExtendedDialogTemplate.MenuResourceOrdinal}"); + Console.WriteLine($"{padding}Class resource: {dialogBox.ExtendedDialogTemplate.ClassResource ?? "[EMPTY]"}"); + Console.WriteLine($"{padding}Class resource ordinal: {dialogBox.ExtendedDialogTemplate.ClassResourceOrdinal}"); + Console.WriteLine($"{padding}Title resource: {dialogBox.ExtendedDialogTemplate.TitleResource ?? "[EMPTY]"}"); + Console.WriteLine($"{padding}Point size: {dialogBox.ExtendedDialogTemplate.PointSize}"); + Console.WriteLine($"{padding}Weight: {dialogBox.ExtendedDialogTemplate.Weight}"); + Console.WriteLine($"{padding}Italic: {dialogBox.ExtendedDialogTemplate.Italic}"); + Console.WriteLine($"{padding}Character set: {dialogBox.ExtendedDialogTemplate.CharSet}"); + Console.WriteLine($"{padding}Typeface: {dialogBox.ExtendedDialogTemplate.Typeface ?? "[EMPTY]"}"); Console.WriteLine(); - //Console.WriteLine($"{padding}Dialog item templates"); - //Console.WriteLine($"{padding}-------------------------"); - //if (dialogBox.DialogTemplate.ItemCount == 0 - // || dialogBox.DialogItemTemplates == null - // || dialogBox.DialogItemTemplates.Length == 0) - //{ - // Console.WriteLine($"{padding}No dialog item templates"); - //} - //else - //{ - // for (int i = 0; i < dialogBox.DialogItemTemplates.Length; i++) - // { - // var dialogItemTemplate = dialogBox.DialogItemTemplates[i]; + Console.WriteLine($"{padding}Dialog item templates"); + Console.WriteLine($"{padding}-------------------------"); + if (dialogBox.ExtendedDialogTemplate.DialogItems == 0 + || dialogBox.ExtendedDialogItemTemplates == null + || dialogBox.ExtendedDialogItemTemplates.Length == 0) + { + Console.WriteLine($"{padding}No dialog item templates"); + } + else + { + for (int i = 0; i < dialogBox.ExtendedDialogItemTemplates.Length; i++) + { + var dialogItemTemplate = dialogBox.ExtendedDialogItemTemplates[i]; - // Console.WriteLine($"{padding}Dialog item template {i}"); - // Console.WriteLine($"{padding} Style: {dialogItemTemplate.Style}"); - // Console.WriteLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle}"); - // Console.WriteLine($"{padding} X-coordinate of upper-left corner: {dialogItemTemplate.PositionX}"); - // Console.WriteLine($"{padding} Y-coordinate of upper-left corner: {dialogItemTemplate.PositionY}"); - // Console.WriteLine($"{padding} Width of the control: {dialogItemTemplate.WidthX}"); - // Console.WriteLine($"{padding} Height of the control: {dialogItemTemplate.HeightY}"); - // Console.WriteLine($"{padding} ID: {dialogItemTemplate.ID}"); - // Console.WriteLine($"{padding} Class resource: {dialogItemTemplate.ClassResource ?? "[EMPTY]"}"); - // Console.WriteLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal}"); - // Console.WriteLine($"{padding} Title resource: {dialogItemTemplate.TitleResource ?? "[EMPTY]"}"); - // Console.WriteLine($"{padding} Title resource ordinal: {dialogItemTemplate.TitleResourceOrdinal}"); - // Console.WriteLine($"{padding} Creation data size: {dialogItemTemplate.CreationDataSize}"); - // if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) - // Console.WriteLine($"{padding} Creation data: {BitConverter.ToString(dialogItemTemplate.CreationData).Replace("-", string.Empty)}"); - // else - // Console.WriteLine($"{padding} Creation data: [EMPTY]"); - // } - //} + Console.WriteLine($"{padding}Dialog item template {i}"); + Console.WriteLine($"{padding} Help ID: {dialogItemTemplate.HelpID}"); + Console.WriteLine($"{padding} Extended style: {dialogItemTemplate.ExtendedStyle}"); + Console.WriteLine($"{padding} Style: {dialogItemTemplate.Style}"); + Console.WriteLine($"{padding} X-coordinate of upper-left corner: {dialogItemTemplate.PositionX}"); + Console.WriteLine($"{padding} Y-coordinate of upper-left corner: {dialogItemTemplate.PositionY}"); + Console.WriteLine($"{padding} Width of the control: {dialogItemTemplate.WidthX}"); + Console.WriteLine($"{padding} Height of the control: {dialogItemTemplate.HeightY}"); + Console.WriteLine($"{padding} ID: {dialogItemTemplate.ID}"); + Console.WriteLine($"{padding} Class resource: {dialogItemTemplate.ClassResource ?? "[EMPTY]"}"); + Console.WriteLine($"{padding} Class resource ordinal: {dialogItemTemplate.ClassResourceOrdinal}"); + Console.WriteLine($"{padding} Title resource: {dialogItemTemplate.TitleResource ?? "[EMPTY]"}"); + Console.WriteLine($"{padding} Title resource ordinal: {dialogItemTemplate.TitleResourceOrdinal}"); + Console.WriteLine($"{padding} Creation data size: {dialogItemTemplate.CreationDataSize}"); + if (dialogItemTemplate.CreationData != null && dialogItemTemplate.CreationData.Length != 0) + Console.WriteLine($"{padding} Creation data: {BitConverter.ToString(dialogItemTemplate.CreationData).Replace("-", string.Empty)}"); + else + Console.WriteLine($"{padding} Creation data: [EMPTY]"); + } + } } else {