diff --git a/SabreTools.Serialization/Deserializers/PortableExecutable.cs b/SabreTools.Serialization/Deserializers/PortableExecutable.cs index 1bc36ce2..4f37bfde 100644 --- a/SabreTools.Serialization/Deserializers/PortableExecutable.cs +++ b/SabreTools.Serialization/Deserializers/PortableExecutable.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if NET35_OR_GREATER || NETCOREAPP using System.Linq; +#endif using System.Text; using SabreTools.IO.Extensions; using SabreTools.Models.PortableExecutable; @@ -1052,28 +1054,62 @@ namespace SabreTools.Serialization.Deserializers // If we have import lookup tables if (importTable.ImportLookupTables != null && importLookupTables.Count > 0) { +#if NET20 + var addresses = new List(); + foreach (var kvp in importTable.ImportLookupTables) + { + if (kvp.Value == null) + continue; + + var vaddrs = Array.ConvertAll(kvp.Value, + ilte => ilte == null ? 0 : (int)ilte.HintNameTableRVA.ConvertVirtualAddress(sections)); + addresses.AddRange(vaddrs); + } +#else var addresses = importTable.ImportLookupTables .SelectMany(kvp => kvp.Value ?? []) .Where(ilte => ilte != null) .Select(ilte => (int)ilte!.HintNameTableRVA.ConvertVirtualAddress(sections)); +#endif hintNameTableEntryAddresses.AddRange(addresses); } // If we have import address tables if (importTable.ImportAddressTables != null && importTable.ImportAddressTables.Count > 0) { +#if NET20 + var addresses = new List(); + foreach (var kvp in importTable.ImportAddressTables) + { + if (kvp.Value == null) + continue; + + var vaddrs = Array.ConvertAll(kvp.Value, + iate => iate == null ? 0 : (int)iate.HintNameTableRVA.ConvertVirtualAddress(sections)); + addresses.AddRange(vaddrs); + } +#else var addresses = importTable.ImportAddressTables .SelectMany(kvp => kvp.Value ?? []) .Where(iate => iate != null) .Select(iate => (int)iate!.HintNameTableRVA.ConvertVirtualAddress(sections)); +#endif hintNameTableEntryAddresses.AddRange(addresses); } // Sanitize the addresses - hintNameTableEntryAddresses = hintNameTableEntryAddresses.Where(addr => addr != 0) - .Distinct() - .OrderBy(a => a) - .ToList(); + hintNameTableEntryAddresses = hintNameTableEntryAddresses.FindAll(addr => addr != 0); +#if NET20 + var temp = new List(); + foreach (int value in hintNameTableEntryAddresses) + { + if (!temp.Contains(value)) + temp.Add(value); + } +#else + hintNameTableEntryAddresses = hintNameTableEntryAddresses.Distinct().ToList(); +#endif + hintNameTableEntryAddresses.Sort(); // If we have any addresses, add them to the table if (hintNameTableEntryAddresses.Count > 0) @@ -1214,11 +1250,12 @@ namespace SabreTools.Serialization.Deserializers return resourceDirectoryTable; // If we're not aligned to a section - if (!sections.Any(s => s != null && s.PointerToRawData == initialOffset)) + var firstSection = Array.Find(sections, s => s != null && s.PointerToRawData == initialOffset); + if (firstSection == null) return resourceDirectoryTable; // Get the section size - int size = (int)sections.First(s => s != null && s.PointerToRawData == initialOffset)!.SizeOfRawData; + int size = (int)firstSection.SizeOfRawData; // Align to the 512-byte boundary, we find the start of an MS-DOS header, or the end of the file while (data.Position - initialOffset < size && data.Position % 0x200 != 0 && data.Position < data.Length - 1) diff --git a/SabreTools.Serialization/ExtensionAttribute.cs b/SabreTools.Serialization/ExtensionAttribute.cs new file mode 100644 index 00000000..f7d035b2 --- /dev/null +++ b/SabreTools.Serialization/ExtensionAttribute.cs @@ -0,0 +1,9 @@ +#if NET20 + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] + internal sealed class ExtensionAttribute : Attribute {} +} + +#endif \ No newline at end of file diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj index e3a2f4a1..a7053303 100644 --- a/SabreTools.Serialization/SabreTools.Serialization.csproj +++ b/SabreTools.Serialization/SabreTools.Serialization.csproj @@ -28,16 +28,11 @@ - - - - - - + - + diff --git a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs index db1e1f14..cc85e728 100644 --- a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs +++ b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs @@ -1,5 +1,5 @@ +using System; using System.IO; -using System.Linq; using SabreTools.Models.InstallShieldCabinet; namespace SabreTools.Serialization.Wrappers @@ -289,7 +289,7 @@ namespace SabreTools.Serialization.Wrappers if (Model.FileGroups == null) return null; - return Model.FileGroups.FirstOrDefault(fg => fg != null && string.Equals(fg.Name, name)); + return Array.Find(Model.FileGroups, fg => fg != null && string.Equals(fg.Name, name)); } /// diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs index 309274b3..bccd7dbc 100644 --- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs +++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.IO; +#if NET35_OR_GREATER || NETCOREAPP using System.Linq; +#endif using System.Text; using SabreTools.IO.Extensions; @@ -41,10 +43,17 @@ namespace SabreTools.Serialization.Wrappers // Populate the raw header padding data based on the source uint headerStartAddress = Model.Stub.Header.NewExeHeaderAddr; - uint firstSectionAddress = Model.SectionTable - .Select(s => s?.PointerToRawData ?? 0) - .Where(s => s != 0 && s >= headerStartAddress) - .Min(); + uint firstSectionAddress = uint.MaxValue; + foreach (var s in Model.SectionTable) + { + if (s == null || s.PointerToRawData == 0) + continue; + if (s.PointerToRawData < headerStartAddress) + continue; + + if (s.PointerToRawData < firstSectionAddress) + firstSectionAddress = s.PointerToRawData; + } // Check if the header length is more than 0 before reading data int headerLength = (int)(firstSectionAddress - headerStartAddress); @@ -82,10 +91,17 @@ namespace SabreTools.Serialization.Wrappers // Populate the header padding strings based on the source uint headerStartAddress = Model.Stub.Header.NewExeHeaderAddr; - uint firstSectionAddress = Model.SectionTable - .Select(s => s?.PointerToRawData ?? 0) - .Where(s => s != 0 && s >= headerStartAddress) - .Min(); + uint firstSectionAddress = uint.MaxValue; + foreach (var s in Model.SectionTable) + { + if (s == null || s.PointerToRawData == 0) + continue; + if (s.PointerToRawData < headerStartAddress) + continue; + + if (s.PointerToRawData < firstSectionAddress) + firstSectionAddress = s.PointerToRawData; + } // Check if the header length is more than 0 before reading strings int headerLength = (int)(firstSectionAddress - headerStartAddress); @@ -643,10 +659,9 @@ namespace SabreTools.Serialization.Wrappers get { var manifest = GetAssemblyManifest(); - return manifest? - .AssemblyIdentities? - .FirstOrDefault(ai => !string.IsNullOrEmpty(ai?.Version))? - .Version; + var identities = manifest?.AssemblyIdentities ?? []; + var versionIdentity = Array.Find(identities, ai => !string.IsNullOrEmpty(ai?.Version)); + return versionIdentity?.Version; } } @@ -837,9 +852,22 @@ namespace SabreTools.Serialization.Wrappers return null; // Try to find a key that matches +#if NET20 + Models.PortableExecutable.StringData? match = null; + foreach (var st in stringTable) + { + if (st?.Children == null) + continue; + + match = Array.Find(st.Children, sd => sd != null && key.Equals(sd.Key, StringComparison.OrdinalIgnoreCase)); + if (match != null) + break; + } +#else var match = stringTable .SelectMany(st => st?.Children ?? []) .FirstOrDefault(sd => sd != null && key.Equals(sd.Key, StringComparison.OrdinalIgnoreCase)); +#endif // Return either the match or null return match?.Value?.TrimEnd('\0'); @@ -878,19 +906,29 @@ namespace SabreTools.Serialization.Wrappers if (DebugData == null) return []; - var nb10Found = DebugData.Select(r => r.Value) - .Select(r => r as SabreTools.Models.PortableExecutable.NB10ProgramDatabase) - .Where(n => n != null) - .Where(n => n?.PdbFileName?.Contains(path) == true) - .Select(n => n as object); + var debugFound = new List(); + foreach (var data in DebugData.Values) + { + if (data == null) + continue; - var rsdsFound = DebugData.Select(r => r.Value) - .Select(r => r as SabreTools.Models.PortableExecutable.RSDSProgramDatabase) - .Where(r => r != null) - .Where(r => r?.PathAndFileName?.Contains(path) == true) - .Select(r => r as object); + if (data is Models.PortableExecutable.NB10ProgramDatabase n) + { + if (n.PdbFileName == null || !n.PdbFileName.Contains(path)) + continue; - return nb10Found.Concat(rsdsFound); + debugFound.Add(n); + } + else if (data is Models.PortableExecutable.RSDSProgramDatabase r) + { + if (r.PathAndFileName == null || !r.PathAndFileName.Contains(path)) + continue; + + debugFound.Add(r); + } + } + + return debugFound; } /// @@ -904,37 +942,49 @@ namespace SabreTools.Serialization.Wrappers if (DebugData == null) return []; - return DebugData.Select(r => r.Value) - .Select(b => b as byte[]) - .Where(b => b != null) - .Where(b => + var table = new List(); + foreach (var data in DebugData.Values) + { + if (data == null) + continue; + if (data is not byte[] b || b == null) + continue; + + try { - try + string? arrayAsASCII = Encoding.ASCII.GetString(b); + if (arrayAsASCII.Contains(value)) { - string? arrayAsASCII = Encoding.ASCII.GetString(b!); - if (arrayAsASCII.Contains(value)) - return true; + table.Add(b); + continue; } - catch { } + } + catch { } - try + try + { + string? arrayAsUTF8 = Encoding.UTF8.GetString(b); + if (arrayAsUTF8.Contains(value)) { - string? arrayAsUTF8 = Encoding.UTF8.GetString(b!); - if (arrayAsUTF8.Contains(value)) - return true; + table.Add(b); + continue; } - catch { } + } + catch { } - try + try + { + string? arrayAsUnicode = Encoding.Unicode.GetString(b); + if (arrayAsUnicode.Contains(value)) { - string? arrayAsUnicode = Encoding.Unicode.GetString(b!); - if (arrayAsUnicode.Contains(value)) - return true; + table.Add(b); + continue; } - catch { } + } + catch { } + } - return false; - }); + return table; } #endregion @@ -1027,14 +1077,21 @@ namespace SabreTools.Serialization.Wrappers if (ResourceData == null) return []; - return ResourceData.Select(r => r.Value) - .Select(r => r as SabreTools.Models.PortableExecutable.DialogBoxResource) - .Where(d => d != null) - .Where(d => - { - return (d?.DialogTemplate?.TitleResource?.Contains(title) ?? false) - || (d?.ExtendedDialogTemplate?.TitleResource?.Contains(title) ?? false); - }); + var resources = new List(); + foreach (var resource in ResourceData.Values) + { + if (resource == null) + continue; + if (resource is not Models.PortableExecutable.DialogBoxResource dbr || dbr == null) + continue; + + if (dbr.DialogTemplate?.TitleResource?.Contains(title) ?? false) + resources.Add(dbr); + else if (dbr.ExtendedDialogTemplate?.TitleResource?.Contains(title) ?? false) + resources.Add(dbr); + } + + return resources; } /// @@ -1048,26 +1105,29 @@ namespace SabreTools.Serialization.Wrappers if (ResourceData == null) return []; - return ResourceData.Select(r => r.Value) - .Select(r => r as SabreTools.Models.PortableExecutable.DialogBoxResource) - .Where(d => d != null) - .Where(d => - { - if (d?.DialogItemTemplates != null) - { - return d.DialogItemTemplates - .Where(dit => dit?.TitleResource != null) - .Any(dit => dit?.TitleResource?.Contains(title) == true); - } - else if (d?.ExtendedDialogItemTemplates != null) - { - return d.ExtendedDialogItemTemplates - .Where(edit => edit?.TitleResource != null) - .Any(edit => edit?.TitleResource?.Contains(title) == true); - } + var resources = new List(); + foreach (var resource in ResourceData.Values) + { + if (resource == null) + continue; + if (resource is not Models.PortableExecutable.DialogBoxResource dbr || dbr == null) + continue; - return false; - }); + if (dbr.DialogItemTemplates != null) + { + var templates = Array.FindAll(dbr.DialogItemTemplates, dit => dit?.TitleResource != null); + if (Array.FindIndex(templates, dit => dit?.TitleResource?.Contains(title) == true) > -1) + resources.Add(dbr); + } + else if (dbr.ExtendedDialogItemTemplates != null) + { + var templates = Array.FindAll(dbr.ExtendedDialogItemTemplates, edit => edit?.TitleResource != null); + if (Array.FindIndex(templates, edit => edit?.TitleResource?.Contains(title) == true) > -1) + resources.Add(dbr); + } + } + + return resources; } /// @@ -1081,11 +1141,26 @@ namespace SabreTools.Serialization.Wrappers if (ResourceData == null) return []; - return ResourceData.Select(r => r.Value) +#if NET20 + var stringTables = new List?>(); + foreach (var resource in ResourceData.Values) + { + if (resource == null) + continue; + if (resource is not Dictionary st || st == null) + continue; + + + } + + return stringTables; +#else + return ResourceData.Values .Select(r => r as Dictionary) .Where(st => st != null) .Where(st => st?.Select(kvp => kvp.Value)? .Any(s => s != null && s.Contains(entry)) == true); +#endif } /// @@ -1099,9 +1174,24 @@ namespace SabreTools.Serialization.Wrappers if (ResourceData == null) return []; +#if NET20 + var resources = new List(); + foreach (var kvp in ResourceData) + { + if (!kvp.Key.Contains(typeName)) + continue; + if (kvp.Value == null || kvp.Value is not byte[] b || b == null) + continue; + + resources.Add(b); + } + + return resources; +#else return ResourceData.Where(kvp => kvp.Key.Contains(typeName)) .Select(kvp => kvp.Value as byte[]) .Where(b => b != null); +#endif } /// @@ -1115,37 +1205,49 @@ namespace SabreTools.Serialization.Wrappers if (ResourceData == null) return []; - return ResourceData.Select(r => r.Value) - .Select(r => r as byte[]) - .Where(b => b != null) - .Where(b => + var resources = new List(); + foreach (var resource in ResourceData.Values) + { + if (resource == null) + continue; + if (resource is not byte[] b || b == null) + continue; + + try { - try + string? arrayAsASCII = Encoding.ASCII.GetString(b!); + if (arrayAsASCII.Contains(value)) { - string? arrayAsASCII = Encoding.ASCII.GetString(b!); - if (arrayAsASCII.Contains(value)) - return true; + resources.Add(b); + continue; } - catch { } + } + catch { } - try + try + { + string? arrayAsUTF8 = Encoding.UTF8.GetString(b!); + if (arrayAsUTF8.Contains(value)) { - string? arrayAsUTF8 = Encoding.UTF8.GetString(b!); - if (arrayAsUTF8.Contains(value)) - return true; + resources.Add(b); + continue; } - catch { } + } + catch { } - try + try + { + string? arrayAsUnicode = Encoding.Unicode.GetString(b!); + if (arrayAsUnicode.Contains(value)) { - string? arrayAsUnicode = Encoding.Unicode.GetString(b!); - if (arrayAsUnicode.Contains(value)) - return true; + resources.Add(b); + continue; } - catch { } + } + catch { } + } - return false; - }); + return resources; } #endregion @@ -1321,11 +1423,11 @@ namespace SabreTools.Serialization.Wrappers // If we're checking exactly, return only exact matches if (exact) - return SectionNames.Any(n => n.Equals(sectionName)); + return Array.FindIndex(SectionNames, n => n.Equals(sectionName)) > -1; // Otherwise, check if section name starts with the value else - return SectionNames.Any(n => n.StartsWith(sectionName)); + return Array.FindIndex(SectionNames, n => n.StartsWith(sectionName)) > -1; } /// diff --git a/SabreTools.Serialization/Wrappers/WrapperBaseT.cs b/SabreTools.Serialization/Wrappers/WrapperBaseT.cs index f9ff019e..bd9bc06d 100644 --- a/SabreTools.Serialization/Wrappers/WrapperBaseT.cs +++ b/SabreTools.Serialization/Wrappers/WrapperBaseT.cs @@ -259,14 +259,22 @@ namespace SabreTools.Serialization.Wrappers /// Character encoding to use for checking /// String list containing the requested data, empty on error /// TODO: Move to IO? +#if NET20 + private List ReadStringsWithEncoding(byte[] sourceData, int charLimit, Encoding encoding) +#else private HashSet ReadStringsWithEncoding(byte[] sourceData, int charLimit, Encoding encoding) +#endif { // If we have an invalid character limit, default to 5 if (charLimit <= 0) charLimit = 5; // Create the string hash set to return +#if NET20 + var sourceStrings = new List(); +#else var sourceStrings = new HashSet(); +#endif // Setup cached data int sourceDataIndex = 0;