diff --git a/BurnOutSharp.Builder/Extensions.cs b/BurnOutSharp.Builder/Extensions.cs index 0dc2dc41..f22ebae4 100644 --- a/BurnOutSharp.Builder/Extensions.cs +++ b/BurnOutSharp.Builder/Extensions.cs @@ -484,11 +484,130 @@ namespace BurnOutSharp.Builder int possibleSignature = entry.Data.ReadUInt16(ref signatureOffset); if (possibleSignature == 0xFFFF) { - Console.WriteLine("Extended dialog box template found, but not implemented"); - #region Extended dialog template - // TODO: Implement extended dialog template + var dialogTemplateExtended = new Models.PortableExecutable.DialogTemplateExtended(); + + dialogTemplateExtended.Version = entry.Data.ReadUInt16(ref offset); + dialogTemplateExtended.Signature = entry.Data.ReadUInt16(ref offset); + dialogTemplateExtended.HelpID = entry.Data.ReadUInt32(ref offset); + dialogTemplateExtended.ExtendedStyle = (Models.PortableExecutable.ExtendedWindowStyles)entry.Data.ReadUInt32(ref offset); + dialogTemplateExtended.Style = (Models.PortableExecutable.WindowStyles)entry.Data.ReadUInt32(ref offset); + dialogTemplateExtended.DialogItems = entry.Data.ReadUInt16(ref offset); + dialogTemplateExtended.PositionX = entry.Data.ReadInt16(ref offset); + dialogTemplateExtended.PositionY = entry.Data.ReadInt16(ref offset); + dialogTemplateExtended.WidthX = entry.Data.ReadInt16(ref offset); + dialogTemplateExtended.HeightY = entry.Data.ReadInt16(ref offset); + + #region Menu resource + + int currentOffset = offset; + ushort menuResourceIdentifier = entry.Data.ReadUInt16(ref offset); + offset = currentOffset; + + // 0x0000 means no elements + if (menuResourceIdentifier == 0x0000) + { + // Increment the pointer if it was empty + offset += sizeof(ushort); + } + else + { + // Flag if there's an ordinal at the end + bool menuResourceHasOrdinal = menuResourceIdentifier == 0xFFFF; + if (menuResourceHasOrdinal) + offset += sizeof(ushort); + + // Read the menu resource as a string + dialogTemplateExtended.MenuResource = entry.Data.ReadString(ref offset, Encoding.Unicode); + + // Align to the WORD boundary + while ((offset % 2) != 0) + _ = entry.Data.ReadByte(ref offset); + + // Read the ordinal if we have the flag set + if (menuResourceHasOrdinal) + dialogTemplateExtended.MenuResourceOrdinal = entry.Data.ReadUInt16(ref offset); + } + + #endregion + + #region Class resource + + currentOffset = offset; + ushort classResourceIdentifier = entry.Data.ReadUInt16(ref offset); + offset = currentOffset; + + // 0x0000 means no elements + if (classResourceIdentifier == 0x0000) + { + // Increment the pointer if it was empty + offset += sizeof(ushort); + } + else + { + // Flag if there's an ordinal at the end + bool classResourcehasOrdinal = classResourceIdentifier == 0xFFFF; + if (classResourcehasOrdinal) + offset += sizeof(ushort); + + // Read the class resource as a string + dialogTemplateExtended.ClassResource = entry.Data.ReadString(ref offset, Encoding.Unicode); + + // Align to the WORD boundary + while ((offset % 2) != 0) + _ = entry.Data.ReadByte(ref offset); + + // Read the ordinal if we have the flag set + if (classResourcehasOrdinal) + dialogTemplateExtended.ClassResourceOrdinal = entry.Data.ReadUInt16(ref offset); + } + + #endregion + + #region Title resource + + currentOffset = offset; + ushort titleResourceIdentifier = entry.Data.ReadUInt16(ref offset); + offset = currentOffset; + + // 0x0000 means no elements + if (titleResourceIdentifier == 0x0000) + { + // Increment the pointer if it was empty + offset += sizeof(ushort); + } + else + { + // Read the title resource as a string + dialogTemplateExtended.TitleResource = entry.Data.ReadString(ref offset, Encoding.Unicode); + + // Align to the WORD boundary + while ((offset % 2) != 0) + _ = entry.Data.ReadByte(ref offset); + } + + #endregion + + #region Point size and typeface + + // Only if DS_SETFONT is set are the values here used + if (dialogTemplateExtended.Style.HasFlag(Models.PortableExecutable.WindowStyles.DS_SETFONT)) + { + dialogTemplateExtended.PointSize = entry.Data.ReadUInt16(ref offset); + dialogTemplateExtended.Weight = entry.Data.ReadUInt16(ref offset); + dialogTemplateExtended.Italic = entry.Data.ReadByte(ref offset); + dialogTemplateExtended.CharSet = entry.Data.ReadByte(ref offset); + dialogTemplateExtended.Typeface = entry.Data.ReadString(ref offset, Encoding.Unicode); + } + + // Align to the DWORD boundary + while ((offset % 4) != 0) + _ = entry.Data.ReadByte(ref offset); + + #endregion + + dialogBoxResource.DialogTemplateExtended = dialogTemplateExtended; #endregion diff --git a/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs b/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs index 1bed5211..1326d82c 100644 --- a/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs +++ b/BurnOutSharp.Models/PortableExecutable/DialogBoxResource.cs @@ -9,11 +9,22 @@ /// public class DialogBoxResource { + #region Dialog template + /// /// Dialog box header structure /// public DialogTemplate DialogTemplate; + /// + /// Dialog box extended header structure + /// + public DialogTemplateExtended DialogTemplateExtended; + + #endregion + + #region Dialog item templates + /// /// Following the DLGTEMPLATE header in a standard dialog box template are one or more /// DLGITEMTEMPLATE structures that define the dimensions and style of the controls in the dialog @@ -21,5 +32,7 @@ /// These DLGITEMTEMPLATE structures must be aligned on DWORD boundaries. /// public DialogItemTemplate[] DialogItemTemplates; + + #endregion } } diff --git a/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs b/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs new file mode 100644 index 00000000..aac642c6 --- /dev/null +++ b/BurnOutSharp.Models/PortableExecutable/DialogTemplateExtended.cs @@ -0,0 +1,172 @@ +using System.Runtime.InteropServices; + +namespace BurnOutSharp.Models.PortableExecutable +{ + /// + /// An extended dialog box template begins with a DLGTEMPLATEEX header that describes + /// the dialog box and specifies the number of controls in the dialog box. For each + /// control in a dialog box, an extended dialog box template has a block of data that + /// uses the DLGITEMTEMPLATEEX format to describe the control. + /// + /// The DLGTEMPLATEEX structure is not defined in any standard header file. The + /// structure definition is provided here to explain the format of an extended template + /// for a dialog box. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public class DialogTemplateExtended + { + /// + /// The version number of the extended dialog box template. This member must be + /// set to 1. + /// + public ushort Version; + + /// + /// Indicates whether a template is an extended dialog box template. If signature + /// is 0xFFFF, this is an extended dialog box template. In this case, the dlgVer + /// member specifies the template version number. If signature is any value other + /// than 0xFFFF, this is a standard dialog box template that uses the DLGTEMPLATE + /// and DLGITEMTEMPLATE structures. + /// + public ushort Signature; + + /// + /// The help context identifier for the dialog box window. When the system sends a + /// WM_HELP message, it passes this value in the wContextId member of the HELPINFO + /// structure. + /// + public uint HelpID; + + /// + /// The extended windows styles. This member is not used when creating 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 dialog box. + /// + /// If style includes the DS_SETFONT or DS_SHELLFONT dialog box style, the DLGTEMPLATEEX + /// header of the extended dialog box template contains four additional members (pointsize, + /// weight, italic, and typeface) that describe the font to use for the text in the client + /// area and controls of the dialog box. If possible, the system creates a font according + /// to the values specified in these members. Then the system sends a WM_SETFONT message + /// to the dialog box and to each control to provide a handle to the font. + /// + public WindowStyles Style; + + /// + /// The number of controls in the dialog box. + /// + public ushort DialogItems; + + /// + /// The x-coordinate, in dialog box units, of the upper-left corner of the dialog box. + /// + /// + /// 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 dialog box. + /// + /// + /// 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 dialog box. + /// + /// + /// 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 dialog box. + /// + /// + /// 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; + + /// + /// A variable-length array of 16-bit elements that identifies a menu resource for the dialog box. + /// If the first element of this array is 0x0000, the dialog box has no menu and the array has no + /// other elements. If the first element is 0xFFFF, the array has one additional element that + /// specifies the ordinal value of a menu resource in an executable file. 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 menu resource in an executable file. + /// + public string MenuResource; + + /// + /// The ordinal value of a menu resource in an executable file. + /// + public ushort MenuResourceOrdinal; + + /// A variable-length array of 16-bit elements that identifies the window class of the + /// dialog box. If the first element of the array is 0x0000, the system uses the predefined dialog + /// box class for the dialog box and the array has no other elements. If the first element is 0xFFFF, + /// the array has one additional element that specifies the ordinal value of a predefined system + /// 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. + /// + public string ClassResource; + + /// + /// The ordinal value of a predefined system window class. + /// + public ushort ClassResourceOrdinal; + + /// + /// 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. + /// + public string TitleResource; + + /// + /// The point size of the font to use for the text in the dialog box and its controls. + /// + /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. + /// + public ushort PointSize; + + /// + /// The weight of the font. Note that, although this can be any of the values listed for the lfWeight + /// member of the LOGFONT structure, any value that is used will be automatically changed to FW_NORMAL. + /// + /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. + /// + public ushort Weight; + + /// + /// Indicates whether the font is italic. If this value is TRUE, the font is italic. + /// + /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. + /// + public byte Italic; + + /// + /// The character set to be used. For more information, see the lfcharset member of LOGFONT. + /// + /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. + /// + public byte CharSet; + + /// + /// The name of the typeface for the font. + /// + /// This member is present only if the style member specifies DS_SETFONT or DS_SHELLFONT. + /// + public string Typeface; + } +} diff --git a/ExecutableTest/Program.cs b/ExecutableTest/Program.cs index f1aaddff..4ccbce8a 100644 --- a/ExecutableTest/Program.cs +++ b/ExecutableTest/Program.cs @@ -1248,6 +1248,63 @@ namespace ExecutableTest } } } + else if (dialogBox.DialogTemplateExtended != 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(); + //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 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]"); + // } + //} + } else { Console.WriteLine($"{padding}Dialog box resource found, but malformed");