Files
UnrealEngine/Engine/Source/Programs/AutomationTool/Win/WinResources.cs
2025-05-18 13:04:45 +08:00

294 lines
8.2 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
public enum ResourceType
{
Icon = 3,
RawData = 10,
GroupIcon = 14,
Version = 16,
}
public class IconResource
{
public byte Width;
public byte Height;
public byte ColorCount;
public ushort Planes;
public ushort BitCount;
public byte[] Data;
public IconResource(byte InWidth, byte InHeight, byte InColorCount, ushort InPlanes, ushort InBitCount, byte[] InData)
{
Width = InWidth;
Height = InHeight;
ColorCount = InColorCount;
Planes = InPlanes;
BitCount = InBitCount;
Data = InData;
}
public override string ToString()
{
return String.Format("{0}x{1}@{2}bpp", Width, Height, BitCount);
}
}
public class GroupIconResource
{
public IconResource[] Icons;
public GroupIconResource(IconResource[] InIcons)
{
Icons = InIcons;
}
public static GroupIconResource FromIco(string FileName)
{
using(FileStream Stream = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
BinaryReader Reader = new BinaryReader(Stream);
Reader.ReadUInt16(); // Reserved
Reader.ReadUInt16(); // Type
ushort Count = Reader.ReadUInt16();
IconResource[] Icons = new IconResource[Count];
uint[] Offsets = new uint[Count];
for(int Idx = 0; Idx < Count; Idx++)
{
Icons[Idx] = ReadIconHeader(Reader);
Offsets[Idx] = Reader.ReadUInt32();
}
for(int Idx = 0; Idx < Icons.Length; Idx++)
{
Stream.Seek(Offsets[Idx], SeekOrigin.Begin);
Stream.Read(Icons[Idx].Data, 0, Icons[Idx].Data.Length);
}
return new GroupIconResource(Icons);
}
}
public static GroupIconResource FromExe(string FileName)
{
using(ModuleResourceLibary Module = new ModuleResourceLibary(FileName))
{
byte[] GroupData = Module.ReadFirstResource(ResourceType.GroupIcon);
return FromByteArray(GroupData, Module);
}
}
public static GroupIconResource FromByteArray(byte[] GroupData, ModuleResourceLibary Module)
{
if (GroupData != null)
{
using (MemoryStream Input = new MemoryStream(GroupData))
{
BinaryReader Reader = new BinaryReader(Input);
Reader.ReadUInt16(); // Reserved
Reader.ReadUInt16(); // Type
ushort Count = Reader.ReadUInt16();
IconResource[] Icons = new IconResource[Count];
for (int Idx = 0; Idx < Count; Idx++)
{
Icons[Idx] = ReadIconHeader(Reader);
int IconId = Reader.ReadUInt16();
Icons[Idx].Data = Module.ReadResource(IconId, ResourceType.Icon);
}
return new GroupIconResource(Icons);
}
}
else
{
return null;
}
}
public byte[] ToByteArray()
{
using(MemoryStream Output = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(Output);
Writer.Write((ushort)0); // Reserved
Writer.Write((ushort)1); // Type (1 = icons)
Writer.Write((ushort)Icons.Length);
for(int Idx = 0; Idx < Icons.Length; Idx++)
{
WriteIconHeader(Writer, Icons[Idx]);
Writer.Write((ushort)(Idx + 1));
}
return Output.ToArray();
}
}
public static IconResource ReadIconHeader(BinaryReader Reader)
{
byte Width = Reader.ReadByte();
byte Height = Reader.ReadByte();
byte ColorCount = Reader.ReadByte();
if(Reader.ReadByte() != 0) throw new InvalidDataException();
ushort Planes = Reader.ReadUInt16();
ushort BitCount = Reader.ReadUInt16();
byte[] Data = new byte[Reader.ReadUInt32()];
return new IconResource(Width, Height, ColorCount, Planes, BitCount, Data);
}
public void WriteIconHeader(BinaryWriter Writer, IconResource Icon)
{
Writer.Write(Icon.Width);
Writer.Write(Icon.Height);
Writer.Write(Icon.ColorCount);
Writer.Write((byte)0);
Writer.Write(Icon.Planes);
Writer.Write(Icon.BitCount);
Writer.Write(Icon.Data.Length);
}
}
public class ModuleResourceLibary : IDisposable
{
const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
delegate bool EnumResourceNamesDelegate(IntPtr ModuleHandle, IntPtr Type, IntPtr Name, IntPtr Param);
[DllImport("kernel32.dll", SetLastError=true)]
extern static IntPtr LoadLibraryEx(string FileName, IntPtr FileHandle, uint Flags);
[DllImport("kernel32.dll", SetLastError=true)]
extern static bool FreeLibrary(IntPtr Handle);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool EnumResourceNames(IntPtr ModuleHandle, IntPtr Type, EnumResourceNamesDelegate EnumDelegate, IntPtr Param);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr FindResource(IntPtr ModuleHandle, IntPtr Name, IntPtr Type);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LoadResource(IntPtr ModuleHandle, IntPtr ResourceHandle);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr LockResource(IntPtr ResourceDataHandle);
[DllImport("kernel32.dll", SetLastError=true)]
static extern uint SizeofResource(IntPtr ModuleHandle, IntPtr ResourceDataHandle);
IntPtr ModuleHandle;
public ModuleResourceLibary(string FileName)
{
ModuleHandle = LoadLibraryEx(FileName, new IntPtr(0), LOAD_LIBRARY_AS_DATAFILE);
}
public void Dispose()
{
FreeLibrary(ModuleHandle);
}
public byte[] ReadResource(int ResourceId, ResourceType Type)
{
return ReadResourceInternal(new IntPtr(ResourceId), new IntPtr((int)Type));
}
public byte[] ReadFirstResource(ResourceType Type)
{
byte[] Result = null;
EnumResourceNames(ModuleHandle, new IntPtr((int)Type), (Handle, FoundType, Name, Param) => { Result = ReadResourceInternal(Name, FoundType); return false; }, new IntPtr(0));
return Result;
}
byte[] ReadResourceInternal(IntPtr Name, IntPtr Type)
{
IntPtr ResourceHandle = FindResource(ModuleHandle, Name, Type);
IntPtr ResourceDataHandle = LoadResource(ModuleHandle, ResourceHandle);
IntPtr ResourceData = LockResource(ResourceDataHandle);
uint ResourceSize = SizeofResource(ModuleHandle, ResourceHandle);
byte[] ResourceDataArray = new byte[ResourceSize];
Marshal.Copy(ResourceData, ResourceDataArray, 0, (int)ResourceSize);
return ResourceDataArray;
}
}
public class ModuleResourceUpdate : IDisposable
{
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr BeginUpdateResource(string pFileName, [MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool UpdateResource(IntPtr hUpdate, IntPtr lpType, IntPtr lpName, ushort wLanguage, IntPtr lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
const ushort DefaultLanguage = 1033; // en-us
IntPtr UpdateHandle;
List<IntPtr> UnmanagedPointers = new List<IntPtr>();
public ModuleResourceUpdate(string OutputFile, bool bRemoveExisting)
{
UpdateHandle = BeginUpdateResource(OutputFile, bRemoveExisting);
EpicGames.Core.Log.WriteLine(EpicGames.Core.LogEventType.Console, "Begin update resource '{0}' ({1})", OutputFile, Marshal.GetLastWin32Error());
}
public void SetData(int ResourceId, ResourceType Type, byte[] Data)
{
IntPtr UnmanagedPointer = Marshal.AllocHGlobal(Data.Length);
UnmanagedPointers.Add(UnmanagedPointer);
Marshal.Copy(Data, 0, UnmanagedPointer, Data.Length);
bool UpdateReturn = UpdateResource(UpdateHandle, new IntPtr((int)Type), new IntPtr(ResourceId), DefaultLanguage, UnmanagedPointer, (uint)Data.Length);
EpicGames.Core.Log.WriteLine(EpicGames.Core.LogEventType.Console, "Add resource {0}, length {1} ({2})", ResourceId, Data.Length, UpdateReturn);
if (!UpdateReturn)
{
throw new Exception("Couldn't update resource");
}
}
public void SetIcons(int ResourceId, GroupIconResource GroupIcon)
{
// Write the icon group resource
SetData(ResourceId, ResourceType.GroupIcon, GroupIcon.ToByteArray());
// Write the individual icons
for(int Idx = 0; Idx < GroupIcon.Icons.Length; Idx++)
{
SetData(Idx + 1, ResourceType.Icon, GroupIcon.Icons[Idx].Data);
}
}
public void Dispose()
{
bool EndReturn = EndUpdateResource(UpdateHandle, false);
EpicGames.Core.Log.WriteLine(EpicGames.Core.LogEventType.Console, "End update resource ({0}/{1})", EndReturn, Marshal.GetLastWin32Error());
foreach(IntPtr UnmanagedPointer in UnmanagedPointers)
{
Marshal.FreeHGlobal(UnmanagedPointer);
}
UnmanagedPointers.Clear();
}
}