Do the same for font groups

This commit is contained in:
Matt Nadareski
2026-02-12 15:46:50 -05:00
parent ad3df18b24
commit f0a0fc3137
2 changed files with 90 additions and 83 deletions

View File

@@ -173,75 +173,6 @@ namespace SabreTools.Data.Extensions
}
}
/// <summary>
/// Read resource data as a font group
/// </summary>
/// <param name="entry">Resource data entry to parse into a font group</param>
/// <returns>A filled font group on success, null on error</returns>
public static FontGroupHeader? AsFontGroup(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data is null)
return null;
// Initialize the iterator
int offset = 0;
// Create the output object
var fontGroupHeader = new FontGroupHeader();
fontGroupHeader.NumberOfFonts = entry.Data.ReadUInt16LittleEndian(ref offset);
if (fontGroupHeader.NumberOfFonts > 0)
{
fontGroupHeader.DE = new DirEntry[fontGroupHeader.NumberOfFonts];
for (int i = 0; i < fontGroupHeader.NumberOfFonts; i++)
{
var dirEntry = new DirEntry();
dirEntry.FontOrdinal = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry = new FontDirEntry();
dirEntry.Entry.Version = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.Size = entry.Data.ReadUInt32LittleEndian(ref offset);
dirEntry.Entry.Copyright = entry.Data.ReadBytes(ref offset, 60);
dirEntry.Entry.Type = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.Points = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.VertRes = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.HorizRes = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.Ascent = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.InternalLeading = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.ExternalLeading = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.Italic = entry.Data.ReadByte(ref offset);
dirEntry.Entry.Underline = entry.Data.ReadByte(ref offset);
dirEntry.Entry.StrikeOut = entry.Data.ReadByte(ref offset);
dirEntry.Entry.Weight = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.CharSet = entry.Data.ReadByte(ref offset);
dirEntry.Entry.PixWidth = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.PixHeight = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.PitchAndFamily = entry.Data.ReadByte(ref offset);
dirEntry.Entry.AvgWidth = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.MaxWidth = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.FirstChar = entry.Data.ReadByte(ref offset);
dirEntry.Entry.LastChar = entry.Data.ReadByte(ref offset);
dirEntry.Entry.DefaultChar = entry.Data.ReadByte(ref offset);
dirEntry.Entry.BreakChar = entry.Data.ReadByte(ref offset);
dirEntry.Entry.WidthBytes = entry.Data.ReadUInt16LittleEndian(ref offset);
dirEntry.Entry.Device = entry.Data.ReadUInt32LittleEndian(ref offset);
dirEntry.Entry.Face = entry.Data.ReadUInt32LittleEndian(ref offset);
dirEntry.Entry.Reserved = entry.Data.ReadUInt32LittleEndian(ref offset);
// TODO: Determine how to read these two? Immediately after?
dirEntry.Entry.DeviceName = entry.Data.ReadNullTerminatedAnsiString(ref offset) ?? string.Empty;
dirEntry.Entry.FaceName = entry.Data.ReadNullTerminatedAnsiString(ref offset) ?? string.Empty;
fontGroupHeader.DE[i] = dirEntry;
}
}
// TODO: Implement entry parsing
return fontGroupHeader;
}
/// <summary>
/// Read resource data as a string table resource
/// </summary>
@@ -274,20 +205,6 @@ namespace SabreTools.Data.Extensions
return stringTable;
}
/// <summary>
/// Read resource data as a version info resource
/// </summary>
/// <param name="entry">Resource data entry to parse into a version info resource</param>
/// <returns>A filled version info resource on success, null on error</returns>
public static VersionInfo? AsVersionInfo(this Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data is null)
return null;
return Serialization.Readers.PortableExecutable.ParseVersionInfo(entry.Data);
}
/// <summary>
/// Parse a byte array into a ResourceHeader
/// </summary>

View File

@@ -1287,6 +1287,22 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Parse a byte array into a DirEntry
/// </summary>
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled DirEntry on success, null on error</returns>
public static Data.Models.PortableExecutable.Resource.Entries.DirEntry ParseDirEntry(byte[] data, ref int offset)
{
var obj = new Data.Models.PortableExecutable.Resource.Entries.DirEntry();
obj.FontOrdinal = data.ReadUInt16LittleEndian(ref offset);
obj.Entry = ParseFontDirEntry(data, ref offset);
return obj;
}
/// <summary>
/// Parse a Stream into a ExportAddressTable
/// </summary>
@@ -1477,6 +1493,80 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Parse a byte array into a FontDirEntry
/// </summary>
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled FontDirEntry on success, null on error</returns>
public static Data.Models.PortableExecutable.Resource.Entries.FontDirEntry ParseFontDirEntry(byte[] data, ref int offset)
{
var obj = new Data.Models.PortableExecutable.Resource.Entries.FontDirEntry();
obj.Version = data.ReadUInt16LittleEndian(ref offset);
obj.Size = data.ReadUInt32LittleEndian(ref offset);
obj.Copyright = data.ReadBytes(ref offset, 60);
obj.Type = data.ReadUInt16LittleEndian(ref offset);
obj.Points = data.ReadUInt16LittleEndian(ref offset);
obj.VertRes = data.ReadUInt16LittleEndian(ref offset);
obj.HorizRes = data.ReadUInt16LittleEndian(ref offset);
obj.Ascent = data.ReadUInt16LittleEndian(ref offset);
obj.InternalLeading = data.ReadUInt16LittleEndian(ref offset);
obj.ExternalLeading = data.ReadUInt16LittleEndian(ref offset);
obj.Italic = data.ReadByte(ref offset);
obj.Underline = data.ReadByte(ref offset);
obj.StrikeOut = data.ReadByte(ref offset);
obj.Weight = data.ReadUInt16LittleEndian(ref offset);
obj.CharSet = data.ReadByte(ref offset);
obj.PixWidth = data.ReadUInt16LittleEndian(ref offset);
obj.PixHeight = data.ReadUInt16LittleEndian(ref offset);
obj.PitchAndFamily = data.ReadByte(ref offset);
obj.AvgWidth = data.ReadUInt16LittleEndian(ref offset);
obj.MaxWidth = data.ReadUInt16LittleEndian(ref offset);
obj.FirstChar = data.ReadByte(ref offset);
obj.LastChar = data.ReadByte(ref offset);
obj.DefaultChar = data.ReadByte(ref offset);
obj.BreakChar = data.ReadByte(ref offset);
obj.WidthBytes = data.ReadUInt16LittleEndian(ref offset);
obj.Device = data.ReadUInt32LittleEndian(ref offset);
obj.Face = data.ReadUInt32LittleEndian(ref offset);
obj.Reserved = data.ReadUInt32LittleEndian(ref offset);
// TODO: Determine how to read these two? Immediately after?
obj.DeviceName = data.ReadNullTerminatedAnsiString(ref offset) ?? string.Empty;
obj.FaceName = data.ReadNullTerminatedAnsiString(ref offset) ?? string.Empty;
return obj;
}
/// <summary>
/// Parse a byte array into a FontGroupHeader
/// </summary>
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled FontGroupHeader on success, null on error</returns>
public static Data.Models.PortableExecutable.Resource.Entries.FontGroupHeader ParseFontGroupHeader(byte[] data)
{
// Initialize the iterator
int offset = 0;
// Create the output object
var fontGroupHeader = new Data.Models.PortableExecutable.Resource.Entries.FontGroupHeader();
fontGroupHeader.NumberOfFonts = data.ReadUInt16LittleEndian(ref offset);
if (fontGroupHeader.NumberOfFonts > 0)
{
fontGroupHeader.DE = new Data.Models.PortableExecutable.Resource.Entries.DirEntry[fontGroupHeader.NumberOfFonts];
for (int i = 0; i < fontGroupHeader.NumberOfFonts; i++)
{
fontGroupHeader.DE[i] = ParseDirEntry(data, ref offset);
}
}
// TODO: Implement entry parsing
return fontGroupHeader;
}
/// <summary>
/// Parse a Stream into a FunctionDefinition
/// </summary>