ILRepack/Steps/Win32Resources/Win32ResourceStep.cs (110 lines of code) (raw):

// // Copyright (c) 2018 Alexander Vostres // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using System; using System.Collections.Generic; using System.IO; using System.Linq; using ILRepacking.Steps.Win32Resources.PE; using Mono.Cecil; namespace ILRepacking.Steps.Win32Resources { class Win32ResourceStep : IRepackStep { private readonly ILogger _logger; private readonly IRepackContext _repackContext; private readonly Dictionary<AssemblyDefinition, int> _aspOffsets; private ResourceDirectory _merged; public Win32ResourceStep(ILogger logger, IRepackContext repackContext, Dictionary<AssemblyDefinition, int> aspOffsets) { _logger = logger; _repackContext = repackContext; _aspOffsets = aspOffsets; } private ResourceDirectory LoadResources(ModuleDefinition src) { return RsrcReader.ReadResourceDirectory(src.FileName); } private ResourceDirectory MergeWin32Resources(ResourceDirectory primary) { if (primary == null) return null; foreach (var ass in _repackContext.OtherAssemblies) { MergeDirectory(new List<ResourceEntry>(), primary, ass, LoadResources(ass.MainModule)); } return primary; } private void MergeDirectory(List<ResourceEntry> parents, ResourceDirectory ret, AssemblyDefinition ass, ResourceDirectory directory) { foreach (var entry in directory.Entries) { var exist = ret.Entries.FirstOrDefault(x => entry.Name == null ? entry.Id == x.Id : entry.Name == x.Name); if (exist == null) ret.Entries.Add(entry); else MergeEntry(parents, exist, ass, entry); } } private void MergeEntry(List<ResourceEntry> parents, ResourceEntry exist, AssemblyDefinition ass, ResourceEntry entry) { if (exist.Data != null && entry.Data != null) { if (IsAspResourceEntry(parents, exist)) { _aspOffsets[ass] = exist.Data.Length; byte[] newData = new byte[exist.Data.Length + entry.Data.Length]; Array.Copy(exist.Data, 0, newData, 0, exist.Data.Length); Array.Copy(entry.Data, 0, newData, exist.Data.Length, entry.Data.Length); exist.Data = newData; } else if (!IsVersionInfoResource(parents, exist)) { _logger.Warn(string.Format("Duplicate Win32 resource with id={0}, parents=[{1}], name={2} in assembly {3}, ignoring", entry.Id, string.Join(",", parents.Select(p => p.Name ?? p.Id.ToString()).ToArray()), entry.Name, ass.Name)); } return; } if (exist.Data != null || entry.Data != null) { _logger.Warn("Inconsistent Win32 resources, ignoring"); return; } parents.Add(exist); MergeDirectory(parents, exist.Directory, ass, entry.Directory); parents.RemoveAt(parents.Count - 1); } private static bool IsAspResourceEntry(List<ResourceEntry> parents, ResourceEntry exist) { return exist.Id == 101 && parents.Count == 1 && parents[0].Id == 3771; } private static bool IsVersionInfoResource(List<ResourceEntry> parents, ResourceEntry exist) { return exist.Id == 0 && parents.Count == 2 && parents[0].Id == 16 && parents[1].Id == 1; } public void Perform() { _logger.Info("Merging Win32 resources"); var resources = LoadResources(_repackContext.PrimaryAssemblyMainModule); _merged = MergeWin32Resources(resources); } public void Patch(string outFile) { if (_merged.Entries.Count == 0) { return; } _logger.Info("Patching Win32 resources"); var ms = new MemoryStream(); using (var f = File.Open(outFile, FileMode.Open, FileAccess.Read, FileShare.Read)) { f.CopyTo(ms); } ms.Position = 0; var reader = new ImageReader(ms); using (var file = File.Open(outFile, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { var writer = new ImageWriter(reader, file); writer.Prepare(_merged); writer.Write(); } } } }