Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalFuzz v29.3.0
BepInEx/patchers/FixPluginTypesSerialization/FixPluginTypesSerialization.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FixPluginTypesSerialization.Patchers; using FixPluginTypesSerialization.UnityPlayer; using FixPluginTypesSerialization.UnityPlayer.Structs.Default; using FixPluginTypesSerialization.Util; using Microsoft.CodeAnalysis; using Microsoft.Deployment.Compression; using Microsoft.Deployment.Compression.Cab; using Mono.Cecil; using MonoMod.RuntimeDetour; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FixPluginTypesSerialization")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+18666a053a1d3d5c39ebafa9bb4683203fadb3a7")] [assembly: AssemblyProduct("FixPluginTypesSerialization")] [assembly: AssemblyTitle("FixPluginTypesSerialization")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FixPluginTypesSerialization { internal static class Config { private static readonly ConfigFile _config = new ConfigFile(Path.Combine(Paths.ConfigPath, "FixPluginTypesSerialization.cfg"), true); internal static ConfigEntry<string> UnityVersionOverride = _config.Bind<string>("Cache", "UnityVersionOverride", "", "Unity version is Major.Minor.Patch format i.e. 2017.2.1. If specified, this version will be used instead of auto-detection from executable info. Specify only if the patcher doesn't work otherwise."); internal static ConfigEntry<string> LastDownloadedGUID = _config.Bind<string>("Cache", "LastDownloadedGUID", "000000000000000000000000000000000", "The GUID of the last downloaded UnityPlayer pdb file." + Environment.NewLine + "If this GUID matches with the current one," + Environment.NewLine + "the offsets for the functions below will be used" + Environment.NewLine + "instead of generating them at runtime."); internal static ConfigEntry<string> MonoManagerAwakeFromLoadOffset = _config.Bind<string>("Cache", "MonoManagerAwakeFromLoadOffset", "00", "The in-memory offset of the MonoManager::AwakeFromLoad function."); internal static ConfigEntry<string> MonoManagerIsAssemblyCreatedOffset = _config.Bind<string>("Cache", "MonoManagerIsAssemblyCreatedOffset", "00", "The in-memory offset of the MonoManager::IsAssemblyCreated function."); internal static ConfigEntry<string> IsFileCreatedOffset = _config.Bind<string>("Cache", "IsFileCreatedOffset", "00", "The in-memory offset of the IsFileCreated function."); internal static ConfigEntry<string> ScriptingManagerDeconstructorOffset = _config.Bind<string>("Cache", "ScriptingManagerDeconstructorOffset", "00", "The in-memory offset of the ScriptingManagerDeconstructor function."); internal static ConfigEntry<string> ConvertSeparatorsToPlatformOffset = _config.Bind<string>("Cache", "ConvertSeparatorsToPlatformOffset", "00", "The in-memory offset of the ConvertSeparatorsToPlatform function."); internal static ConfigEntry<string> FreeAllocInternalOffset = _config.Bind<string>("Cache", "FreeAllocInternalOffset", "00", "The in-memory offset of the free_alloc_internal function."); internal static ConfigEntry<string> MallocInternalOffset = _config.Bind<string>("Cache", "MallocInternalOffset", "00", "The in-memory offset of the malloc_internal function."); internal static ConfigEntry<string> ScriptingAssembliesOffset = _config.Bind<string>("Cache", "ScriptingAssembliesOffset", "00", "The in-memory offset of the m_ScriptingAssemblies global field."); } internal static class FixPluginTypesSerializationPatcher { public static List<string> PluginPaths = (from f in Directory.GetFiles(Paths.PluginPath, "*.dll", SearchOption.AllDirectories) where IsNetAssembly(f) select f).ToList(); public static List<string> PluginNames = PluginPaths.Select((string p) => Path.GetFileName(p)).ToList(); public static IEnumerable<string> TargetDLLs { get; } = new string[0]; public static bool IsNetAssembly(string fileName) { try { AssemblyName.GetAssemblyName(fileName); } catch (BadImageFormatException) { return false; } return true; } public static void Patch(AssemblyDefinition ass) { } public static void Initialize() { Log.Init(); try { InitializeInternal(); } catch (Exception ex) { Log.Error($"Failed to initialize plugin types serialization fix: ({ex.GetType()}) {ex.Message}. Some plugins may not work properly."); Log.Error(ex); } } private static void InitializeInternal() { DetourUnityPlayer(); } private static void DetourUnityPlayer() { string text = Path.Combine(Paths.GameRootPath, "UnityPlayer.dll"); if (!File.Exists(text)) { text = Paths.ExecutablePath; } MiniPdbReader pdbReader = new MiniPdbReader(text); ProcessModule processModule = Process.GetCurrentProcess().Modules.Cast<ProcessModule>().FirstOrDefault(IsUnityPlayer) ?? Process.GetCurrentProcess().MainModule; CommonUnityFunctions.Init(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader); AwakeFromLoad awakeFromLoad = new AwakeFromLoad(); IsAssemblyCreated isAssemblyCreated = new IsAssemblyCreated(); IsFileCreated isFileCreated = new IsFileCreated(); ScriptingManagerDeconstructor scriptingManagerDeconstructor = new ScriptingManagerDeconstructor(); ConvertSeparatorsToPlatform convertSeparatorsToPlatform = new ConvertSeparatorsToPlatform(); awakeFromLoad.Patch(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader, Config.MonoManagerAwakeFromLoadOffset); isAssemblyCreated.Patch(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader, Config.MonoManagerIsAssemblyCreatedOffset); if (!IsAssemblyCreated.IsApplied) { isFileCreated.Patch(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader, Config.IsFileCreatedOffset); } convertSeparatorsToPlatform.Patch(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader, Config.ConvertSeparatorsToPlatformOffset); scriptingManagerDeconstructor.Patch(processModule.BaseAddress, processModule.ModuleMemorySize, pdbReader, Config.ScriptingManagerDeconstructorOffset); static bool IsUnityPlayer(ProcessModule p) { return p.ModuleName.ToLowerInvariant().Contains("unityplayer"); } } } internal static class Log { internal static ManualLogSource _logSource; internal static void Init() { _logSource = Logger.CreateLogSource("FixPluginTypesSerialization"); } internal static void Debug(object data) { _logSource.LogDebug(data); } internal static void Error(object data) { _logSource.LogError(data); } internal static void Fatal(object data) { _logSource.LogFatal(data); } internal static void Info(object data) { _logSource.LogInfo(data); } internal static void Message(object data) { _logSource.LogMessage(data); } internal static void Warning(object data) { _logSource.LogWarning(data); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "FixPluginTypesSerialization"; public const string PLUGIN_NAME = "FixPluginTypesSerialization"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace FixPluginTypesSerialization.Util { internal class BytePattern { private readonly byte?[] pattern; private int[] jumpTable; public int Length => pattern.Length; public bool IsE8 => pattern[0] == 232; public BytePattern(string bytes) { pattern = bytes.ParseHexBytes(); CreateJumpTable(); } public BytePattern(byte[] bytes) { pattern = bytes.Cast<byte?>().ToArray(); CreateJumpTable(); } public static implicit operator BytePattern(string pattern) { return new BytePattern(pattern); } public static implicit operator BytePattern(byte[] pattern) { return new BytePattern(pattern); } private void CreateJumpTable() { jumpTable = new int[pattern.Length]; int num = 0; jumpTable[0] = -1; int num2 = 1; while (num2 < pattern.Length) { if (pattern[num2] == pattern[num]) { jumpTable[num2] = jumpTable[num]; } else { jumpTable[num2] = num; while (num >= 0 && pattern[num2] != pattern[num]) { num = jumpTable[num]; } } num2++; num++; } } public unsafe long Match(IntPtr start, long maxSize) { byte* ptr = (byte*)start.ToPointer(); long num = 0L; long num2 = 0L; while (num < maxSize) { if (!pattern[num2].HasValue || ptr[num] == pattern[num2]) { num++; num2++; if (num2 == pattern.Length) { return num - num2; } } else { num2 = jumpTable[num2]; if (num2 < 0) { num++; num2++; } } } return 0L; } } internal class CommonUnityFunctions { public enum AllocateOptions { None, NullIfOutOfMemory } private delegate IntPtr MallocInternalFunc(ulong size, ulong allign, int label, AllocateOptions allocateOptions, IntPtr file, int line); private delegate void FreeAllocInternalV1Func(IntPtr ptr, int label); private delegate void FreeAllocInternalV2Func(IntPtr ptr, int label, IntPtr file, int line); private static MallocInternalFunc mallocInternal; private static FreeAllocInternalV1Func freeAllocInternalV1; private static FreeAllocInternalV2Func freeAllocInternalV2; public static IntPtr ScriptingAssemblies { get; private set; } public static void Init(IntPtr unityModule, int moduleSize, MiniPdbReader pdbReader) { IntPtr intPtr = PatternDiscover.Discover(unityModule, moduleSize, pdbReader, Config.MallocInternalOffset, new BytePattern[1] { Encoding.ASCII.GetBytes("malloc_internal") }, Array.Empty<BytePattern>()); if (intPtr != IntPtr.Zero) { mallocInternal = (MallocInternalFunc)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(MallocInternalFunc)); } IntPtr intPtr2 = PatternDiscover.Discover(unityModule, moduleSize, pdbReader, Config.FreeAllocInternalOffset, new BytePattern[1] { Encoding.ASCII.GetBytes("free_alloc_internal") }, Array.Empty<BytePattern>()); if (intPtr2 != IntPtr.Zero) { if (UseRightStructs.UnityVersion >= new Version(2019, 3)) { freeAllocInternalV2 = (FreeAllocInternalV2Func)Marshal.GetDelegateForFunctionPointer(intPtr2, typeof(FreeAllocInternalV2Func)); } else { freeAllocInternalV1 = (FreeAllocInternalV1Func)Marshal.GetDelegateForFunctionPointer(intPtr2, typeof(FreeAllocInternalV1Func)); } } IntPtr intPtr3 = PatternDiscover.Discover(unityModule, moduleSize, pdbReader, Config.ScriptingAssembliesOffset, new BytePattern[1] { Encoding.ASCII.GetBytes("m_ScriptingAssemblies@") }, Array.Empty<BytePattern>()); if (intPtr3 != IntPtr.Zero) { ScriptingAssemblies = intPtr3; } } public unsafe static IntPtr MallocString(string str, int label, out ulong length) { IntPtr intPtr = Marshal.StringToHGlobalAnsi(str); length = (ulong)str.Length; byte* ptr = (byte*)(void*)intPtr + length; while (*ptr != 0) { ptr++; length++; } IntPtr intPtr2 = mallocInternal(length + 1, 16uL, label, AllocateOptions.NullIfOutOfMemory, IntPtr.Zero, 0); for (ulong num = 0uL; num <= length; num++) { ((byte*)(void*)intPtr2)[num] = ((byte*)(void*)intPtr)[num]; } Marshal.FreeHGlobal(intPtr); return intPtr2; } public static IntPtr MallocInternal(ulong size, ulong allign, int label) { return mallocInternal(size, allign, label, AllocateOptions.NullIfOutOfMemory, IntPtr.Zero, 0); } public static void FreeAllocInternal(IntPtr ptr, int label) { if (UseRightStructs.UnityVersion >= new Version(2019, 3)) { freeAllocInternalV2(ptr, label, IntPtr.Zero, 0); } else { freeAllocInternalV1(ptr, label); } } } internal static class DictionaryExtensions { public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value) { key = tuple.Key; value = tuple.Value; } public static void Deconstruct(this VersionedHandler versionedHandler, out Version version, out object handler) { version = versionedHandler.version; handler = versionedHandler.handler; } } internal class MiniPdbReader { private static readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(5.0) }; private readonly PeReader _peReader; private byte[] _pdbFile; internal bool IsPdbAvailable; internal bool UseCache; private static byte[] DownloadFromWeb(string url) { Log.Info("Downloading : " + url + "\nThis pdb file is needed for the plugin to work properly. This may take a while, relax, modding is coming."); try { HttpResponseMessage result = _httpClient.GetAsync(url).GetAwaiter().GetResult(); Log.Info("Status Code : " + result.StatusCode); if (result.StatusCode != HttpStatusCode.OK) { return null; } return result.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); } catch (TaskCanceledException) { Log.Info("Could not download pdb. Plugin may not work correctly."); return null; } } internal MiniPdbReader(string targetFilePath) { _peReader = new PeReader(targetFilePath); if (_peReader.RsdsPdbFileName == null) { Log.Info("No pdb path found in the pe file. Falling back to sig matching"); return; } UseCache = Config.LastDownloadedGUID.Value == _peReader.PdbGuid; Log.Message((UseCache ? "U" : "Not u") + "sing the config cache"); if (!UseCache) { if (DownloadUnityPdb(_peReader)) { Config.LastDownloadedGUID.Value = _peReader.PdbGuid; IsPdbAvailable = true; } else { Log.Info("Failed to find the linked pdb in the unity symbol server. Falling back to sig matching"); } } else { IsPdbAvailable = true; } } private bool DownloadUnityPdb(PeReader peReader) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) string path = peReader.RsdsPdbFileName.TrimEnd('b') + "_"; byte[] array = DownloadFromWeb(Path.Combine("http://symbolserver.unity3d.com/", peReader.RsdsPdbFileName, peReader.PdbGuid, path)); if (array != null) { string tempPath = Path.GetTempPath(); string text = Path.Combine(tempPath, "pdb.cab"); try { File.Delete(text); } catch (Exception) { } Log.Info("Writing the compressed pdb to " + text); File.WriteAllBytes(text, array); CabInfo val = new CabInfo(text); Log.Info("Unpacking the compressed pdb"); ((ArchiveInfo)val).Unpack(tempPath); string path2 = Path.Combine(tempPath, peReader.RsdsPdbFileName); _pdbFile = File.ReadAllBytes(path2); File.Delete(text); File.Delete(path2); } return _pdbFile != null; } internal unsafe IntPtr FindFunctionOffset(BytePattern[] bytePatterns) { fixed (byte* ptr = &_pdbFile[0]) { IntPtr pdbStartAddress = (IntPtr)ptr; long sizeOfPdb = _pdbFile.Length; var anon = bytePatterns.Select((BytePattern p) => new { p = p, res = p.Match(pdbStartAddress, sizeOfPdb) }).FirstOrDefault(m => m.res > 0); if (anon == null) { Log.Error("No function offset found, cannot hook ! Please report it to the r2api devs !"); return IntPtr.Zero; } Log.Info($"Found at {anon.res:X} ({pdbStartAddress.ToInt64() + anon.res:X})"); uint* ptr2 = (uint*)(pdbStartAddress.ToInt64() + anon.res - 7); uint num = *ptr2; ushort* ptr3 = (ushort*)(pdbStartAddress.ToInt64() + anon.res - 3); int num2 = *ptr3 - 1; num += _peReader.ImageSectionHeaders[num2].VirtualAddress; Log.Info("Function offset : " + num.ToString("X") + " | PE section : " + num2); return new IntPtr(num); } } } internal static class MonoManagerCommon { public unsafe static void CopyNativeAssemblyListToManagedV1(List<StringStorageDefaultV1> managedAssemblyList, Vector<StringStorageDefaultV1> assemblyNames) { managedAssemblyList.Clear(); for (StringStorageDefaultV1* ptr = assemblyNames.first; ptr != assemblyNames.last; ptr++) { managedAssemblyList.Add(*ptr); } } public unsafe static void AddAssembliesToManagedListV1(List<StringStorageDefaultV1> managedAssemblyList, List<string> pluginAssemblyPaths) { foreach (string pluginAssemblyPath in pluginAssemblyPaths) { string? fileName = Path.GetFileName(pluginAssemblyPath); ulong num = (ulong)fileName.Length; IntPtr intPtr = Marshal.StringToHGlobalAnsi(fileName); byte* ptr = (byte*)(void*)intPtr + num; while (*ptr != 0) { ptr++; num++; } StringStorageDefaultV1 stringStorageDefaultV = default(StringStorageDefaultV1); stringStorageDefaultV.label = UseRightStructs.LabelMemStringId; stringStorageDefaultV.data = intPtr; stringStorageDefaultV.capacity = num; stringStorageDefaultV.size = num; StringStorageDefaultV1 item = stringStorageDefaultV; managedAssemblyList.Add(item); } } public unsafe static void AllocNativeAssemblyListFromManagedV1(List<StringStorageDefaultV1> managedAssemblyList, Vector<StringStorageDefaultV1>* assemblyNames) { StringStorageDefaultV1* ptr = (StringStorageDefaultV1*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf(typeof(StringStorageDefaultV1)) * managedAssemblyList.Count); int i = 0; StringStorageDefaultV1* ptr2 = ptr; for (; i < managedAssemblyList.Count; i++) { *ptr2 = managedAssemblyList[i]; ptr2++; } assemblyNames->first = ptr; assemblyNames->last = ptr + managedAssemblyList.Count; assemblyNames->end = assemblyNames->last; } public unsafe static void PrintAssembliesV1(Vector<StringStorageDefaultV1> assemblyNames) { for (StringStorageDefaultV1* ptr = assemblyNames.first; ptr != assemblyNames.last; ptr++) { nint ptr2 = ptr->data; if (ptr->data == 0) { ptr2 = (nint)((byte*)ptr + 8); } Log.Warning($"Ass: {Marshal.PtrToStringAnsi(ptr2, (int)ptr->size)} | label : {ptr->label:X}"); } } public unsafe static void CopyNativeAssemblyListToManagedV2(List<StringStorageDefaultV1> managedAssemblyList, DynamicArrayData assemblyNames) { managedAssemblyList.Clear(); ulong num = 0uL; StringStorageDefaultV1* ptr = (StringStorageDefaultV1*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { managedAssemblyList.Add(*ptr); ptr++; } } public unsafe static void AllocNativeAssemblyListFromManagedV2(List<StringStorageDefaultV1> managedAssemblyList, DynamicArrayData* assemblyNames) { StringStorageDefaultV1* ptr = (StringStorageDefaultV1*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf(typeof(StringStorageDefaultV1)) * managedAssemblyList.Count); int i = 0; StringStorageDefaultV1* ptr2 = ptr; for (; i < managedAssemblyList.Count; i++) { *ptr2 = managedAssemblyList[i]; ptr2++; } assemblyNames->ptr = (nint)ptr; assemblyNames->size = (ulong)managedAssemblyList.Count; assemblyNames->capacity = assemblyNames->size; } public unsafe static void PrintAssembliesV2(DynamicArrayData assemblyNames) { ulong num = 0uL; StringStorageDefaultV1* ptr = (StringStorageDefaultV1*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { nint ptr2 = ptr->data; if (ptr->data == 0) { ptr2 = (nint)((byte*)ptr + 8); } Log.Warning($"Ass: {Marshal.PtrToStringAnsi(ptr2, (int)ptr->size)} | label : {ptr->label:X}"); ptr++; } } public unsafe static void CopyNativeAssemblyListToManagedV3(List<StringStorageDefaultV2> managedAssemblyList, DynamicArrayData assemblyNames) { managedAssemblyList.Clear(); ulong num = 0uL; StringStorageDefaultV2* ptr = (StringStorageDefaultV2*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { managedAssemblyList.Add(*ptr); ptr++; } } public unsafe static void AddAssembliesToManagedListV3(List<StringStorageDefaultV2> managedAssemblyList, List<string> pluginAssemblyPaths) { foreach (string pluginAssemblyPath in pluginAssemblyPaths) { string? fileName = Path.GetFileName(pluginAssemblyPath); ulong num = (ulong)fileName.Length; IntPtr intPtr = Marshal.StringToHGlobalAnsi(fileName); byte* ptr = (byte*)(void*)intPtr + num; while (*ptr != 0) { ptr++; num++; } StringStorageDefaultV2 stringStorageDefaultV = default(StringStorageDefaultV2); stringStorageDefaultV.union = new StringStorageDefaultV2Union { heap = new HeapAllocatedRepresentationV2 { data = intPtr, capacity = num, size = num } }; stringStorageDefaultV.data_repr = StringRepresentation.Heap; stringStorageDefaultV.label = UseRightStructs.LabelMemStringId; StringStorageDefaultV2 item = stringStorageDefaultV; managedAssemblyList.Add(item); } } public unsafe static void AllocNativeAssemblyListFromManagedV3(List<StringStorageDefaultV2> managedAssemblyList, DynamicArrayData* assemblyNames) { StringStorageDefaultV2* ptr = (StringStorageDefaultV2*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf(typeof(StringStorageDefaultV2)) * managedAssemblyList.Count); int i = 0; StringStorageDefaultV2* ptr2 = ptr; for (; i < managedAssemblyList.Count; i++) { *ptr2 = managedAssemblyList[i]; ptr2++; } assemblyNames->ptr = (nint)ptr; assemblyNames->size = (ulong)managedAssemblyList.Count; assemblyNames->capacity = assemblyNames->size; } public unsafe static void PrintAssembliesV3(DynamicArrayData assemblyNames) { ulong num = 0uL; StringStorageDefaultV2* ptr = (StringStorageDefaultV2*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { if (ptr->data_repr == StringRepresentation.Embedded) { Log.Warning($"Ass: {Marshal.PtrToStringAnsi((IntPtr)ptr->union.embedded.data)} | label : {ptr->label:X}"); } else { Log.Warning($"Ass: {Marshal.PtrToStringAnsi(ptr->union.heap.data, (int)ptr->union.heap.size)} | label : {ptr->label:X}"); } ptr++; } } public unsafe static void CopyNativeAssemblyListToManagedV4(List<StringStorageDefaultV3> managedAssemblyList, DynamicArrayData assemblyNames) { managedAssemblyList.Clear(); ulong num = 0uL; StringStorageDefaultV3* ptr = (StringStorageDefaultV3*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { managedAssemblyList.Add(*ptr); ptr++; } } public unsafe static void AddAssembliesToManagedListV4(List<StringStorageDefaultV3> managedAssemblyList, List<string> pluginAssemblyPaths) { foreach (string pluginAssemblyPath in pluginAssemblyPaths) { string? fileName = Path.GetFileName(pluginAssemblyPath); ulong num = (ulong)fileName.Length; IntPtr intPtr = Marshal.StringToHGlobalAnsi(fileName); byte* ptr = (byte*)(void*)intPtr + num; while (*ptr != 0) { ptr++; num++; } StringStorageDefaultV3 stringStorageDefaultV = default(StringStorageDefaultV3); stringStorageDefaultV.union = new StringStorageDefaultV3Union { heap = new HeapAllocatedRepresentationV3 { data = intPtr, capacity = num, size = num, flags = new StringStorageDefaultV3Flags { IsHeap = true } } }; stringStorageDefaultV.label = UseRightStructs.LabelMemStringId; StringStorageDefaultV3 item = stringStorageDefaultV; managedAssemblyList.Add(item); } } public unsafe static void AllocNativeAssemblyListFromManagedV4(List<StringStorageDefaultV3> managedAssemblyList, DynamicArrayData* assemblyNames) { StringStorageDefaultV3* ptr = (StringStorageDefaultV3*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf(typeof(StringStorageDefaultV3)) * managedAssemblyList.Count); int i = 0; StringStorageDefaultV3* ptr2 = ptr; for (; i < managedAssemblyList.Count; i++) { *ptr2 = managedAssemblyList[i]; ptr2++; } assemblyNames->ptr = (nint)ptr; assemblyNames->size = (ulong)managedAssemblyList.Count; assemblyNames->capacity = assemblyNames->size; } public unsafe static void PrintAssembliesV4(DynamicArrayData assemblyNames) { ulong num = 0uL; StringStorageDefaultV3* ptr = (StringStorageDefaultV3*)assemblyNames.ptr; for (; num < assemblyNames.size; num++) { if (ptr->union.embedded.flags.IsEmbedded) { Log.Warning($"Ass: {Marshal.PtrToStringAnsi((IntPtr)ptr->union.embedded.data)} | label : {ptr->label:X}"); } else { Log.Warning($"Ass: {Marshal.PtrToStringAnsi(ptr->union.heap.data, (int)ptr->union.heap.size)} | label : {ptr->label:X}"); } ptr++; } } } internal static class NativeLibraryHelper { [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public class DynamicDllImportAttribute : Attribute { public string DLL; public string[] EntryPoints; public DynamicDllImportAttribute(string dll, params string[] entryPoints) { DLL = dll; EntryPoints = entryPoints; } } private static readonly IntPtr NULL = IntPtr.Zero; public static Dictionary<string, string> DllMap = new Dictionary<string, string>(); private static IntPtr _EntryPoint = NULL; public static IntPtr EntryPoint { get { if (_EntryPoint != NULL) { return _EntryPoint; } return _EntryPoint = OpenLibrary(null); } } public static ulong CurrentThreadId { get { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { return GetCurrentThreadId(); } return 0uL; } } [DllImport("kernel32")] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32")] private static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); public static IntPtr OpenLibrary(string name) { if (DllMap.TryGetValue(name, out var value)) { name = value; } if (Environment.OSVersion.Platform == PlatformID.Win32NT) { IntPtr intPtr = GetModuleHandle(name); if (intPtr == NULL) { intPtr = LoadLibrary(name); } return intPtr; } return NULL; } public static IntPtr GetFunction(this IntPtr lib, string name) { if (lib == NULL) { return NULL; } if (Environment.OSVersion.Platform == PlatformID.Win32NT) { return GetProcAddress(lib, name); } return NULL; } public static T GetDelegate<T>(this IntPtr lib, string name) where T : class { if (lib == NULL) { return null; } IntPtr function = lib.GetFunction(name); if (function == NULL) { return null; } return function.AsDelegate<T>(); } public static T GetDelegateAtRVA<T>(this IntPtr basea, long rva) where T : class { return new IntPtr(basea.ToInt64() + rva).AsDelegate<T>(); } public static T AsDelegate<T>(this IntPtr s) where T : class { return Marshal.GetDelegateForFunctionPointer(s, typeof(T)) as T; } [DllImport("kernel32")] private static extern uint GetCurrentThreadId(); public static void ResolveDynamicDllImports(this Type type) { FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { bool flag = true; object[] customAttributes = fieldInfo.GetCustomAttributes(typeof(DynamicDllImportAttribute), inherit: true); for (int j = 0; j < customAttributes.Length; j++) { DynamicDllImportAttribute dynamicDllImportAttribute = (DynamicDllImportAttribute)customAttributes[j]; flag = false; IntPtr intPtr = OpenLibrary(dynamicDllImportAttribute.DLL); if (intPtr == NULL) { continue; } string[] entryPoints = dynamicDllImportAttribute.EntryPoints; foreach (string name in entryPoints) { IntPtr function = intPtr.GetFunction(name); if (!(function == NULL)) { fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(function, fieldInfo.FieldType)); flag = true; break; } } if (flag) { break; } } if (!flag) { throw new EntryPointNotFoundException("No matching entry point found for " + fieldInfo.Name + " in " + fieldInfo.DeclaringType.FullName); } } } } public struct OriginalPathData { public IntPtr thisRef; public IntPtr thisDataRef; public ulong size; public OriginalPathData(IntPtr thisRef, IntPtr thisDataRef, ulong size) { this.thisRef = thisRef; this.thisDataRef = thisDataRef; this.size = size; } } internal class PatternDiscover { public static IntPtr Discover(IntPtr unityModule, int moduleSize, MiniPdbReader pdbReader, ConfigEntry<string> functionOffsetCache, BytePattern[] pdbPatterns, BytePattern[] sigPatterns) { if (pdbReader.IsPdbAvailable) { return DiscoverWithPdb(unityModule, pdbReader, functionOffsetCache, pdbPatterns); } return DiscoverWithSig(unityModule, moduleSize, sigPatterns); } public static IntPtr DiscoverWithPdb(IntPtr unityModule, MiniPdbReader pdbReader, ConfigEntry<string> functionOffsetCache, BytePattern[] pdbPatterns) { IntPtr intPtr; if (pdbReader.UseCache) { intPtr = new IntPtr(Convert.ToInt64(functionOffsetCache.Value, 16)); if (intPtr == IntPtr.Zero) { return intPtr; } } else { intPtr = pdbReader.FindFunctionOffset(pdbPatterns); if (intPtr == IntPtr.Zero) { functionOffsetCache.Value = "00"; return intPtr; } functionOffsetCache.Value = intPtr.ToString("X"); } return (IntPtr)(unityModule.ToInt64() + intPtr.ToInt64()); } public unsafe static IntPtr DiscoverWithSig(IntPtr unityModule, int moduleSize, BytePattern[] sigPatterns) { var anon = sigPatterns.Select((BytePattern p) => new { p = p, res = p.Match(unityModule, moduleSize) }).FirstOrDefault(m => m.res > 0); if (anon == null) { return IntPtr.Zero; } byte* ptr = (byte*)unityModule.ToPointer(); Log.Info($"Found at {anon.res:X} ({unityModule.ToInt64() + anon.res:X})"); long num = unityModule.ToInt64() + anon.res; if (anon.p.IsE8) { int num2 = *(int*)(unityModule.ToInt64() + anon.res + 1); Log.Info($"Parsed e8_offset: {num2:X}"); num = num + 5 + num2; } Log.Info($"memory address: {num:X} (image base: {unityModule.ToInt64():X})"); return new IntPtr(num); } } internal class PeReader { public struct IMAGE_DOS_HEADER { public ushort e_magic; public ushort e_cblp; public ushort e_cp; public ushort e_crlc; public ushort e_cparhdr; public ushort e_minalloc; public ushort e_maxalloc; public ushort e_ss; public ushort e_sp; public ushort e_csum; public ushort e_ip; public ushort e_cs; public ushort e_lfarlc; public ushort e_ovno; public ushort e_res_0; public ushort e_res_1; public ushort e_res_2; public ushort e_res_3; public ushort e_oemid; public ushort e_oeminfo; public ushort e_res2_0; public ushort e_res2_1; public ushort e_res2_2; public ushort e_res2_3; public ushort e_res2_4; public ushort e_res2_5; public ushort e_res2_6; public ushort e_res2_7; public ushort e_res2_8; public ushort e_res2_9; public uint e_lfanew; } public struct IMAGE_DATA_DIRECTORY { public uint VirtualAddress; public uint Size; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IMAGE_OPTIONAL_HEADER32 { public ushort Magic; public byte MajorLinkerVersion; public byte MinorLinkerVersion; public uint SizeOfCode; public uint SizeOfInitializedData; public uint SizeOfUninitializedData; public uint AddressOfEntryPoint; public uint BaseOfCode; public uint BaseOfData; public uint ImageBase; public uint SectionAlignment; public uint FileAlignment; public ushort MajorOperatingSystemVersion; public ushort MinorOperatingSystemVersion; public ushort MajorImageVersion; public ushort MinorImageVersion; public ushort MajorSubsystemVersion; public ushort MinorSubsystemVersion; public uint Win32VersionValue; public uint SizeOfImage; public uint SizeOfHeaders; public uint CheckSum; public ushort Subsystem; public ushort DllCharacteristics; public uint SizeOfStackReserve; public uint SizeOfStackCommit; public uint SizeOfHeapReserve; public uint SizeOfHeapCommit; public uint LoaderFlags; public uint NumberOfRvaAndSizes; public IMAGE_DATA_DIRECTORY ExportTable; public IMAGE_DATA_DIRECTORY ImportTable; public IMAGE_DATA_DIRECTORY ResourceTable; public IMAGE_DATA_DIRECTORY ExceptionTable; public IMAGE_DATA_DIRECTORY CertificateTable; public IMAGE_DATA_DIRECTORY BaseRelocationTable; public IMAGE_DATA_DIRECTORY Debug; public IMAGE_DATA_DIRECTORY Architecture; public IMAGE_DATA_DIRECTORY GlobalPtr; public IMAGE_DATA_DIRECTORY TLSTable; public IMAGE_DATA_DIRECTORY LoadConfigTable; public IMAGE_DATA_DIRECTORY BoundImport; public IMAGE_DATA_DIRECTORY IAT; public IMAGE_DATA_DIRECTORY DelayImportDescriptor; public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; public IMAGE_DATA_DIRECTORY Reserved; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IMAGE_OPTIONAL_HEADER64 { public ushort Magic; public byte MajorLinkerVersion; public byte MinorLinkerVersion; public uint SizeOfCode; public uint SizeOfInitializedData; public uint SizeOfUninitializedData; public uint AddressOfEntryPoint; public uint BaseOfCode; public ulong ImageBase; public uint SectionAlignment; public uint FileAlignment; public ushort MajorOperatingSystemVersion; public ushort MinorOperatingSystemVersion; public ushort MajorImageVersion; public ushort MinorImageVersion; public ushort MajorSubsystemVersion; public ushort MinorSubsystemVersion; public uint Win32VersionValue; public uint SizeOfImage; public uint SizeOfHeaders; public uint CheckSum; public ushort Subsystem; public ushort DllCharacteristics; public ulong SizeOfStackReserve; public ulong SizeOfStackCommit; public ulong SizeOfHeapReserve; public ulong SizeOfHeapCommit; public uint LoaderFlags; public uint NumberOfRvaAndSizes; public IMAGE_DATA_DIRECTORY ExportTable; public IMAGE_DATA_DIRECTORY ImportTable; public IMAGE_DATA_DIRECTORY ResourceTable; public IMAGE_DATA_DIRECTORY ExceptionTable; public IMAGE_DATA_DIRECTORY CertificateTable; public IMAGE_DATA_DIRECTORY BaseRelocationTable; public IMAGE_DATA_DIRECTORY Debug; public IMAGE_DATA_DIRECTORY Architecture; public IMAGE_DATA_DIRECTORY GlobalPtr; public IMAGE_DATA_DIRECTORY TLSTable; public IMAGE_DATA_DIRECTORY LoadConfigTable; public IMAGE_DATA_DIRECTORY BoundImport; public IMAGE_DATA_DIRECTORY IAT; public IMAGE_DATA_DIRECTORY DelayImportDescriptor; public IMAGE_DATA_DIRECTORY CLRRuntimeHeader; public IMAGE_DATA_DIRECTORY Reserved; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct IMAGE_FILE_HEADER { public ushort Machine; public ushort NumberOfSections; public uint TimeDateStamp; public uint PointerToSymbolTable; public uint NumberOfSymbols; public ushort SizeOfOptionalHeader; public ushort Characteristics; } [StructLayout(LayoutKind.Explicit)] public struct IMAGE_SECTION_HEADER { [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] public string Name; [FieldOffset(8)] public uint VirtualSize; [FieldOffset(12)] public uint VirtualAddress; [FieldOffset(16)] public uint SizeOfRawData; [FieldOffset(20)] public uint PointerToRawData; [FieldOffset(24)] public uint PointerToRelocations; [FieldOffset(28)] public uint PointerToLinenumbers; [FieldOffset(32)] public ushort NumberOfRelocations; [FieldOffset(34)] public ushort NumberOfLinenumbers; [FieldOffset(36)] public DataSectionFlags Characteristics; } [Flags] public enum DataSectionFlags : uint { TypeReg = 0u, TypeDsect = 1u, TypeNoLoad = 2u, TypeGroup = 4u, TypeNoPadded = 8u, TypeCopy = 0x10u, ContentCode = 0x20u, ContentInitializedData = 0x40u, ContentUninitializedData = 0x80u, LinkOther = 0x100u, LinkInfo = 0x200u, TypeOver = 0x400u, LinkRemove = 0x800u, LinkComDat = 0x1000u, NoDeferSpecExceptions = 0x4000u, RelativeGP = 0x8000u, MemPurgeable = 0x20000u, Memory16Bit = 0x20000u, MemoryLocked = 0x40000u, MemoryPreload = 0x80000u, Align1Bytes = 0x100000u, Align2Bytes = 0x200000u, Align4Bytes = 0x300000u, Align8Bytes = 0x400000u, Align16Bytes = 0x500000u, Align32Bytes = 0x600000u, Align64Bytes = 0x700000u, Align128Bytes = 0x800000u, Align256Bytes = 0x900000u, Align512Bytes = 0xA00000u, Align1024Bytes = 0xB00000u, Align2048Bytes = 0xC00000u, Align4096Bytes = 0xD00000u, Align8192Bytes = 0xE00000u, LinkExtendedRelocationOverflow = 0x1000000u, MemoryDiscardable = 0x2000000u, MemoryNotCached = 0x4000000u, MemoryNotPaged = 0x8000000u, MemoryShared = 0x10000000u, MemoryExecute = 0x20000000u, MemoryRead = 0x40000000u, MemoryWrite = 0x80000000u } public struct IMAGE_DEBUG_DIRECTORY { public enum _Type : uint { IMAGE_DEBUG_TYPE_UNKNOWN, IMAGE_DEBUG_TYPE_COFF, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DEBUG_TYPE_FPO, IMAGE_DEBUG_TYPE_MISC, IMAGE_DEBUG_TYPE_EXCEPTION, IMAGE_DEBUG_TYPE_FIXUP, IMAGE_DEBUG_TYPE_OMAP_TO_SRC, IMAGE_DEBUG_TYPE_OMAP_FROM_SRC, IMAGE_DEBUG_TYPE_BORLAND, IMAGE_DEBUG_TYPE_RESERVED10, IMAGE_DEBUG_TYPE_CLSID, IMAGE_DEBUG_TYPE_VC_FEATURE, IMAGE_DEBUG_TYPE_POGO, IMAGE_DEBUG_TYPE_ILTCG, IMAGE_DEBUG_TYPE_MPX, IMAGE_DEBUG_TYPE_REPRO } public uint Characteristics; public uint TimeDateStamp; public ushort MajorVersion; public ushort MinorVersion; public _Type Type; public uint SizeOfData; public uint AddressOfRawData; public uint PointerToRawData; } public struct RSDS { internal const int Magic = 1396986706; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] internal byte[] Guid; internal uint Age; } private IMAGE_DOS_HEADER dosHeader; private IMAGE_FILE_HEADER fileHeader; private IMAGE_OPTIONAL_HEADER32 optionalHeader32; private IMAGE_OPTIONAL_HEADER64 optionalHeader64; private IMAGE_DEBUG_DIRECTORY? imageDebugDirectory; private string rsdsPdbFileName; private string pdbGuid; private readonly IMAGE_SECTION_HEADER[] imageSectionHeaders; public bool Is32BitHeader { get { ushort num = 256; return (num & FileHeader.Characteristics) == num; } } public IMAGE_FILE_HEADER FileHeader => fileHeader; public IMAGE_OPTIONAL_HEADER32 OptionalHeader32 => optionalHeader32; public IMAGE_OPTIONAL_HEADER64 OptionalHeader64 => optionalHeader64; public IMAGE_DEBUG_DIRECTORY? ImageDebugDirectory => imageDebugDirectory; public IMAGE_SECTION_HEADER[] ImageSectionHeaders => imageSectionHeaders; public string RsdsPdbFileName => rsdsPdbFileName; public string PdbGuid => pdbGuid; public DateTime TimeStamp { get { DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(fileHeader.TimeDateStamp); return dateTime + TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); } } public PeReader(string filePath) { using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); BinaryReader binaryReader = new BinaryReader(fileStream); dosHeader = FromBinaryReader<IMAGE_DOS_HEADER>(binaryReader); fileStream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); binaryReader.ReadUInt32(); fileHeader = FromBinaryReader<IMAGE_FILE_HEADER>(binaryReader); uint virtualAddress; if (Is32BitHeader) { optionalHeader32 = FromBinaryReader<IMAGE_OPTIONAL_HEADER32>(binaryReader); virtualAddress = optionalHeader32.Debug.VirtualAddress; } else { optionalHeader64 = FromBinaryReader<IMAGE_OPTIONAL_HEADER64>(binaryReader); virtualAddress = optionalHeader64.Debug.VirtualAddress; } imageSectionHeaders = new IMAGE_SECTION_HEADER[fileHeader.NumberOfSections]; for (int i = 0; i < imageSectionHeaders.Length; i++) { IMAGE_SECTION_HEADER imageSectionHeader = FromBinaryReader<IMAGE_SECTION_HEADER>(binaryReader); imageSectionHeaders[i] = imageSectionHeader; if (!imageDebugDirectory.HasValue) { imageDebugDirectory = TryGetDebugDirectory(binaryReader, virtualAddress, ref imageSectionHeader); } } if (imageDebugDirectory.HasValue) { TryGetRSDS(binaryReader); } } private IMAGE_DEBUG_DIRECTORY? TryGetDebugDirectory(BinaryReader reader, uint debugRva, ref IMAGE_SECTION_HEADER imageSectionHeader) { uint num = ((imageSectionHeader.VirtualSize != 0) ? imageSectionHeader.VirtualSize : imageSectionHeader.SizeOfRawData); if (debugRva >= imageSectionHeader.VirtualAddress && debugRva < imageSectionHeader.VirtualAddress + num) { long position = reader.BaseStream.Position; uint num2 = debugRva - imageSectionHeader.VirtualAddress + imageSectionHeader.PointerToRawData; reader.BaseStream.Position = num2; IMAGE_DEBUG_DIRECTORY value = FromBinaryReader<IMAGE_DEBUG_DIRECTORY>(reader); reader.BaseStream.Position = position; return value; } return null; } private void TryGetRSDS(BinaryReader reader) { var anon = new { RSDSFileOffset = ImageDebugDirectory.Value.PointerToRawData, Size = ImageDebugDirectory.Value.SizeOfData }; reader.BaseStream.Position = anon.RSDSFileOffset; long num = anon.RSDSFileOffset + anon.Size; while (reader.BaseStream.Position != num) { if (reader.ReadInt32() != 1396986706) { continue; } GCHandle gCHandle = GCHandle.Alloc(reader.ReadBytes(Marshal.SizeOf<RSDS>()), GCHandleType.Pinned); try { RSDS rSDS = Marshal.PtrToStructure<RSDS>(gCHandle.AddrOfPinnedObject()); pdbGuid = new Guid(rSDS.Guid).ToString("N").ToUpperInvariant() + rSDS.Age; List<byte> list = new List<byte>(); bool flag = true; while (flag) { byte b = reader.ReadByte(); if (b != 0) { list.Add(b); } else { flag = false; } } rsdsPdbFileName = Path.GetFileName(Encoding.Default.GetString(list.ToArray())); break; } finally { gCHandle.Free(); } } } public static PeReader GetCallingAssemblyHeader() { return new PeReader(Assembly.GetCallingAssembly().Location); } public static PeReader GetAssemblyHeader() { return new PeReader(Assembly.GetAssembly(typeof(PeReader)).Location); } public static T FromBinaryReader<T>(BinaryReader reader) { GCHandle gCHandle = GCHandle.Alloc(reader.ReadBytes(Marshal.SizeOf(typeof(T))), GCHandleType.Pinned); T result = (T)Marshal.PtrToStructure(gCHandle.AddrOfPinnedObject(), typeof(T)); gCHandle.Free(); return result; } } internal static class Polyfills { public static bool StringIsNullOrWhiteSpace(string value) { return string.IsNullOrWhiteSpace(value); } public static Version VersionParse(string input) { return Version.Parse(input); } } internal static class StringExtensions { public static string Format(this string fmt, Dictionary<string, Func<string>> vars) { return vars.Aggregate(fmt, (string str, KeyValuePair<string, Func<string>> kv) => str.Replace("{" + kv.Key + "}", kv.Value())); } public static byte?[] ParseHexBytes(this string str) { List<byte?> list = new List<byte?>(); StringReader stringReader = new StringReader(str); while (stringReader.Peek() > 0) { char c = char.ToLower((char)stringReader.Read()); if (char.IsWhiteSpace(c)) { continue; } switch (c) { case ';': stringReader.ReadLine(); continue; case '?': list.Add(null); stringReader.Read(); continue; } if (IsHexChar(c) && stringReader.Peek() > 0) { char c2 = char.ToLower((char)stringReader.Peek()); if (IsHexChar(c2)) { stringReader.Read(); list.Add(byte.Parse($"{c}{c2}", NumberStyles.HexNumber)); } } } return list.ToArray(); static bool IsHexChar(char lowerC) { if ('0' > lowerC || lowerC > '9') { if ('a' <= lowerC) { return lowerC <= 'f'; } return false; } return true; } } } public struct VersionedHandler { public Version version; public object handler; public VersionedHandler(Version version, object handler) { this.version = version; this.handler = handler; } } } namespace FixPluginTypesSerialization.UnityPlayer { [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] internal class ApplicableToUnityVersionsSinceAttribute : Attribute { public string StartVersion { get; } public ApplicableToUnityVersionsSinceAttribute(string startVersion) { StartVersion = startVersion; } } public static class UseRightStructs { private static readonly Type[] InterfacesOfInterest; private static readonly Dictionary<Type, List<VersionedHandler>> VersionedHandlers; private static readonly Dictionary<Type, object> CurrentHandlers; private static Version _unityVersion; public static Version UnityVersion { get { if (_unityVersion == null) { InitializeUnityVersion(); } return _unityVersion; } } public static int LabelMemStringId { get; private set; } private static void InitializeUnityVersion() { if (!Polyfills.StringIsNullOrWhiteSpace(Config.UnityVersionOverride.Value)) { if (TryInitializeUnityVersion(Config.UnityVersionOverride.Value)) { Log.Debug("Unity version obtained from config file"); return; } Log.Error("Unity version " + Config.UnityVersionOverride.Value + " has incorrect format."); } if (TryInitializeUnityVersion(Process.GetCurrentProcess().MainModule.FileVersionInfo.FileVersion)) { Log.Debug("Unity version obtained from main application module."); } else { Log.Error("Running under default Unity version. UnityVersionHandler is not initialized."); } } private static bool TryInitializeUnityVersion(string version) { try { if (Polyfills.StringIsNullOrWhiteSpace(version)) { return false; } string[] array = version.Split('.'); int result = 0; int result2 = 0; int result3 = 0; bool flag = int.TryParse(array[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out result); if (flag && array.Length > 1) { flag = int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out result2); } if (flag && array.Length > 2) { flag = int.TryParse(array[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out result3); } if (!flag) { Log.Error("Failed to parse Unity version: " + version); return false; } _unityVersion = new Version(result, result2, result3); Log.Info($"Running under Unity v{UnityVersion}"); return true; } catch (Exception arg) { Log.Error($"Failed to parse Unity version: {arg}"); return false; } } static UseRightStructs() { VersionedHandlers = new Dictionary<Type, List<VersionedHandler>>(); CurrentHandlers = new Dictionary<Type, object>(); Type[] allTypesSafe = GetAllTypesSafe(); InterfacesOfInterest = allTypesSafe.Where((Type t) => t.IsInterface && typeof(INativeStruct).IsAssignableFrom(t) && t != typeof(INativeStruct)).ToArray(); Type[] interfacesOfInterest = InterfacesOfInterest; foreach (Type key in interfacesOfInterest) { VersionedHandlers[key] = new List<VersionedHandler>(); } foreach (Type item in allTypesSafe.Where((Type t) => !t.IsAbstract && InterfacesOfInterest.Any((Type i) => i.IsAssignableFrom(t)))) { foreach (ApplicableToUnityVersionsSinceAttribute customAttribute in item.GetCustomAttributes<ApplicableToUnityVersionsSinceAttribute>()) { object handler = Activator.CreateInstance(item); interfacesOfInterest = item.GetInterfaces(); foreach (Type type in interfacesOfInterest) { if (InterfacesOfInterest.Contains(type)) { VersionedHandlers[type].Add(new VersionedHandler(Polyfills.VersionParse(customAttribute.StartVersion), handler)); } } } } foreach (List<VersionedHandler> value in VersionedHandlers.Values) { value.Sort((VersionedHandler a, VersionedHandler b) => -a.version.CompareTo(b.version)); } GatherUnityVersionSpecificHandlers(); SetUnityVersionSpecificMemStringId(); } private static void GatherUnityVersionSpecificHandlers() { CurrentHandlers.Clear(); Type[] interfacesOfInterest = InterfacesOfInterest; foreach (Type key in interfacesOfInterest) { foreach (var (version2, value) in VersionedHandlers[key]) { if (!(version2 > UnityVersion)) { CurrentHandlers[key] = value; break; } } } } private static T GetHandler<T>() { if (CurrentHandlers.TryGetValue(typeof(T), out var value)) { return (T)value; } Log.Error($"No direct for {typeof(T).FullName} found for Unity {UnityVersion}; this likely indicates a severe error somewhere"); throw new ApplicationException("No handler"); } private static Type[] GetAllTypesSafe() { try { return typeof(UseRightStructs).Assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null).ToArray(); } } internal static T GetStruct<T>(IntPtr ptr) where T : INativeStruct { if (ptr == IntPtr.Zero) { throw new ArgumentNullException("ptr"); } T handler = GetHandler<T>(); handler.Pointer = ptr; return handler; } private static void SetUnityVersionSpecificMemStringId() { if (UnityVersion >= new Version(2023, 1)) { LabelMemStringId = 9; } else if (UnityVersion >= new Version(2021, 1)) { LabelMemStringId = 73; } else if (UnityVersion >= new Version(2020, 2)) { LabelMemStringId = 43; } else if (UnityVersion >= new Version(2020, 1)) { LabelMemStringId = 42; } else if (UnityVersion >= new Version(2019, 4)) { LabelMemStringId = 43; } else if (UnityVersion >= new Version(2019, 3)) { LabelMemStringId = 42; } else if (UnityVersion >= new Version(2019, 1)) { LabelMemStringId = 43; } else if (UnityVersion >= new Version(2018, 3)) { LabelMemStringId = 69; } else if (UnityVersion >= new Version(2017, 2)) { LabelMemStringId = 68; } else { LabelMemStringId = 66; } } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2023.v1 { [ApplicableToUnityVersionsSince("2023.1.0")] public class MonoManager : IMonoManager, INativeStruct { private ScriptingAssemblies _originalScriptingAssemblies; public List<StringStorageDefaultV3> ManagedAssemblyList = new List<StringStorageDefaultV3>(); public IntPtr Pointer { get { return CommonUnityFunctions.ScriptingAssemblies; } set { } } private unsafe RuntimeStatic<ScriptingAssemblies>* _this => (RuntimeStatic<ScriptingAssemblies>*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV4(ManagedAssemblyList, _this->value->names); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV4(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { MonoManagerCommon.AllocNativeAssemblyListFromManagedV4(ManagedAssemblyList, &_this->value->names); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV4(_this->value->names); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { *_this->value = _originalScriptingAssemblies; } } [ApplicableToUnityVersionsSince("2023.1.0")] public class RelativePathString : IRelativePathString, INativeStruct { public IntPtr Pointer { get; set; } private unsafe StringStorageDefaultV3* _this => (StringStorageDefaultV3*)(void*)Pointer; public RelativePathString() { } public RelativePathString(IntPtr pointer) { Pointer = pointer; } public unsafe void FixAbsolutePath() { if (!_this->union.embedded.flags.IsEmbedded && (_this->union.heap.data == 0 || _this->union.heap.size == 0L)) { return; } string fileName = Path.GetFileName(_this->union.embedded.flags.IsEmbedded ? Marshal.PtrToStringAnsi((IntPtr)_this->union.embedded.data) : Marshal.PtrToStringAnsi(_this->union.heap.data, (int)_this->union.heap.size)); int num = FixPluginTypesSerializationPatcher.PluginNames.IndexOf(fileName); if (num != -1) { ulong length; IntPtr data = CommonUnityFunctions.MallocString(FixPluginTypesSerializationPatcher.PluginPaths[num], UseRightStructs.LabelMemStringId, out length); if (!_this->union.embedded.flags.IsEmbedded) { CommonUnityFunctions.FreeAllocInternal(_this->union.heap.data, _this->label); } StringStorageDefaultV3* @this = _this; @this->union = new StringStorageDefaultV3Union { heap = new HeapAllocatedRepresentationV3 { data = data, capacity = length, size = length, flags = new StringStorageDefaultV3Flags { IsHeap = true } } }; @this->label = UseRightStructs.LabelMemStringId; } } public unsafe string ToStringAnsi() { if (!_this->union.embedded.flags.IsEmbedded && (_this->union.heap.data == 0 || _this->union.heap.size == 0L)) { return null; } if (_this->union.embedded.flags.IsEmbedded) { return Marshal.PtrToStringAnsi((IntPtr)_this->union.embedded.data); } return Marshal.PtrToStringAnsi(_this->union.heap.data, (int)_this->union.heap.size); } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2021.v1 { [ApplicableToUnityVersionsSince("2021.1.0")] public class MonoManager : IMonoManager, INativeStruct { private ScriptingAssemblies _originalScriptingAssemblies; public List<StringStorageDefaultV2> ManagedAssemblyList = new List<StringStorageDefaultV2>(); public IntPtr Pointer { get { return CommonUnityFunctions.ScriptingAssemblies; } set { } } private unsafe RuntimeStatic<ScriptingAssemblies>* _this => (RuntimeStatic<ScriptingAssemblies>*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV3(ManagedAssemblyList, _this->value->names); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV3(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { MonoManagerCommon.AllocNativeAssemblyListFromManagedV3(ManagedAssemblyList, &_this->value->names); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV3(_this->value->names); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { *_this->value = _originalScriptingAssemblies; } } [ApplicableToUnityVersionsSince("2021.1.0")] public class RelativePathString : IRelativePathString, INativeStruct { public IntPtr Pointer { get; set; } private unsafe StringStorageDefaultV2* _this => (StringStorageDefaultV2*)(void*)Pointer; public RelativePathString() { } public RelativePathString(IntPtr pointer) { Pointer = pointer; } public unsafe void FixAbsolutePath() { if (_this->data_repr != StringRepresentation.Embedded && (_this->union.heap.data == 0 || _this->union.heap.size == 0L)) { return; } string path = ((_this->data_repr != StringRepresentation.Embedded) ? Marshal.PtrToStringAnsi(_this->union.heap.data, (int)_this->union.heap.size) : Marshal.PtrToStringAnsi((IntPtr)_this->union.embedded.data)); string fileName = Path.GetFileName(path); int num = FixPluginTypesSerializationPatcher.PluginNames.IndexOf(fileName); if (num != -1) { ulong length; IntPtr data = CommonUnityFunctions.MallocString(FixPluginTypesSerializationPatcher.PluginPaths[num], UseRightStructs.LabelMemStringId, out length); if (_this->data_repr != StringRepresentation.Embedded) { CommonUnityFunctions.FreeAllocInternal(_this->union.heap.data, _this->label); } StringStorageDefaultV2* @this = _this; @this->union = new StringStorageDefaultV2Union { heap = new HeapAllocatedRepresentationV2 { data = data, capacity = length, size = length } }; @this->data_repr = StringRepresentation.Heap; @this->label = UseRightStructs.LabelMemStringId; } } public unsafe string ToStringAnsi() { if (_this->data_repr != StringRepresentation.Embedded && (_this->union.heap.data == 0 || _this->union.heap.size == 0L)) { return null; } if (_this->data_repr == StringRepresentation.Embedded) { return Marshal.PtrToStringAnsi((IntPtr)_this->union.embedded.data); } return Marshal.PtrToStringAnsi(_this->union.heap.data, (int)_this->union.heap.size); } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2020.v2 { [ApplicableToUnityVersionsSince("2020.2.0")] public class MonoManager : IMonoManager, INativeStruct { private ScriptingAssemblies _originalScriptingAssemblies; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get { return CommonUnityFunctions.ScriptingAssemblies; } set { } } private unsafe RuntimeStatic<ScriptingAssemblies>* _this => (RuntimeStatic<ScriptingAssemblies>*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV2(ManagedAssemblyList, _this->value->names); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { MonoManagerCommon.AllocNativeAssemblyListFromManagedV2(ManagedAssemblyList, &_this->value->names); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV2(_this->value->names); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { *_this->value = _originalScriptingAssemblies; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2020.v1 { [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(448)] public DynamicArrayData m_AssemblyNames; } [ApplicableToUnityVersionsSince("2020.1.0")] public class MonoManager : IMonoManager, INativeStruct { private DynamicArrayData _originalAssemblyNames; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV2(ManagedAssemblyList, _this->m_AssemblyNames); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { _originalAssemblyNames = _this->m_AssemblyNames; MonoManagerCommon.AllocNativeAssemblyListFromManagedV2(ManagedAssemblyList, &_this->m_AssemblyNames); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV2(_this->m_AssemblyNames); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2019 { public struct AssemblyList { public nint ptr; public int label; public ulong size; public ulong capacity; } [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct AssemblyStringStruct { public const int ValidStringLabel = 42; public nint data; public ulong capacity; public ulong extra; public ulong size; public int label; public bool IsValid() { if (data != 0) { return label == 42; } return false; } } [ApplicableToUnityVersionsSince("2019.3.0")] public class AssemblyString : IAssemblyString, INativeStruct { public IntPtr Pointer { get; set; } private unsafe AssemblyStringStruct* _this => (AssemblyStringStruct*)(void*)Pointer; public AssemblyString() { } public AssemblyString(IntPtr pointer) { Pointer = pointer; } public unsafe void FixAbsolutePath() { if (_this->data != 0) { string pathNameStr = Marshal.PtrToStringAnsi(_this->data, (int)_this->size); string text = FixPluginTypesSerializationPatcher.PluginPaths.FirstOrDefault((string p) => Path.GetFileName(p) == Path.GetFileName(pathNameStr)); if (!string.IsNullOrEmpty(text)) { ulong num = (ulong)text.Length; IntPtr intPtr = Marshal.StringToHGlobalAnsi(text); (IntPtr, IntPtr, ulong) value = ((IntPtr)_this, _this->data, _this->size); ReadStringFromFile.ModifiedPathsToOriginalPaths.Add(intPtr, value); _this->data = intPtr; _this->capacity = num; _this->size = num; } } } public unsafe void RestoreOriginalString(IntPtr constCharPtr) { if (ReadStringFromFile.ModifiedPathsToOriginalPaths.TryGetValue(constCharPtr, out var value)) { AssemblyStringStruct* ptr = (AssemblyStringStruct*)(void*)value.Item1; ptr->data = value.Item2; ptr->size = value.Item3; ptr->capacity = value.Item3; } } } [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(432)] public AssemblyList m_AssemblyNames; } [ApplicableToUnityVersionsSince("2019.3.0")] public class MonoManager : IMonoManager, INativeStruct { private AssemblyList _originalAssemblyNames; public List<AssemblyStringStruct> ManagedAssemblyList = new List<AssemblyStringStruct>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { ManagedAssemblyList.Clear(); ulong num = 0uL; AssemblyStringStruct* ptr = (AssemblyStringStruct*)_this->m_AssemblyNames.ptr; for (; num < _this->m_AssemblyNames.size; num++) { AssemblyStringStruct assemblyStringStruct = default(AssemblyStringStruct); assemblyStringStruct.capacity = ptr->capacity; assemblyStringStruct.extra = ptr->extra; assemblyStringStruct.label = ptr->label; assemblyStringStruct.size = ptr->size; assemblyStringStruct.data = ptr->data; AssemblyStringStruct item = assemblyStringStruct; ManagedAssemblyList.Add(item); ptr++; } } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { foreach (string pluginAssemblyPath in pluginAssemblyPaths) { string fileName = Path.GetFileName(pluginAssemblyPath); ulong num = (ulong)fileName.Length; AssemblyStringStruct assemblyStringStruct = default(AssemblyStringStruct); assemblyStringStruct.label = 42; assemblyStringStruct.data = Marshal.StringToHGlobalAnsi(fileName); assemblyStringStruct.capacity = num; assemblyStringStruct.size = num; AssemblyStringStruct item = assemblyStringStruct; ManagedAssemblyList.Add(item); } } public unsafe void AllocNativeAssemblyListFromManaged() { AssemblyStringStruct* ptr = (AssemblyStringStruct*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf<AssemblyStringStruct>() * ManagedAssemblyList.Count); int i = 0; AssemblyStringStruct* ptr2 = ptr; for (; i < ManagedAssemblyList.Count; i++) { ptr2->label = ManagedAssemblyList[i].label; ptr2->size = ManagedAssemblyList[i].size; ptr2->capacity = ManagedAssemblyList[i].capacity; ptr2->extra = ManagedAssemblyList[i].extra; ptr2->data = ManagedAssemblyList[i].data; ptr2++; } _originalAssemblyNames = _this->m_AssemblyNames; _this->m_AssemblyNames.ptr = (nint)ptr; _this->m_AssemblyNames.size = (ulong)ManagedAssemblyList.Count; _this->m_AssemblyNames.capacity = _this->m_AssemblyNames.size; } public unsafe void PrintAssemblies() { ulong num = 0uL; AssemblyStringStruct* ptr = (AssemblyStringStruct*)_this->m_AssemblyNames.ptr; for (; num < _this->m_AssemblyNames.size; num++) { if (ptr->IsValid()) { Log.Warning($"Ass: {Marshal.PtrToStringAnsi(ptr->data, (int)ptr->size)} | label : {ptr->label:X}"); } ptr++; } } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2019.v1 { [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(432)] public DynamicArrayData m_AssemblyNames; } [ApplicableToUnityVersionsSince("2019.1.0")] public class MonoManager : IMonoManager, INativeStruct { private DynamicArrayData _originalAssemblyNames; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV2(ManagedAssemblyList, _this->m_AssemblyNames); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { _originalAssemblyNames = _this->m_AssemblyNames; MonoManagerCommon.AllocNativeAssemblyListFromManagedV2(ManagedAssemblyList, &_this->m_AssemblyNames); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV2(_this->m_AssemblyNames); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2018 { public struct AssemblyList { public unsafe AssemblyStringStruct* first; public unsafe AssemblyStringStruct* last; public unsafe AssemblyStringStruct* end; } [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct AssemblyStringStruct { public const int ValidStringLabel = 69; public nint data; public ulong capacity; public ulong extra; public ulong size; public int label; public bool IsValid() { if (data != 0) { return label == 69; } return false; } } [ApplicableToUnityVersionsSince("5.3.0")] public class AssemblyString : IAssemblyString, INativeStruct { public IntPtr Pointer { get; set; } private unsafe AssemblyStringStruct* _this => (AssemblyStringStruct*)(void*)Pointer; public AssemblyString() { } public AssemblyString(IntPtr pointer) { Pointer = pointer; } public unsafe void FixAbsolutePath() { if (_this->data != 0) { string pathNameStr = Marshal.PtrToStringAnsi(_this->data, (int)_this->size); string text = FixPluginTypesSerializationPatcher.PluginPaths.FirstOrDefault((string p) => Path.GetFileName(p) == Path.GetFileName(pathNameStr)); if (!string.IsNullOrEmpty(text)) { ulong num = (ulong)text.Length; IntPtr intPtr = Marshal.StringToHGlobalAnsi(text); (IntPtr, IntPtr, ulong) value = ((IntPtr)_this, _this->data, _this->size); ReadStringFromFile.ModifiedPathsToOriginalPaths.Add(intPtr, value); _this->data = intPtr; _this->capacity = num; _this->size = num; } } } public unsafe void RestoreOriginalString(IntPtr constCharPtr) { if (ReadStringFromFile.ModifiedPathsToOriginalPaths.TryGetValue(constCharPtr, out var value)) { AssemblyStringStruct* ptr = (AssemblyStringStruct*)(void*)value.Item1; ptr->data = value.Item2; ptr->size = value.Item3; ptr->capacity = value.Item3; } } } [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(408)] public AssemblyList m_AssemblyNames; } [ApplicableToUnityVersionsSince("5.0.0")] public class MonoManager : IMonoManager, INativeStruct { private AssemblyList _originalAssemblyNames; public List<AssemblyStringStruct> ManagedAssemblyList = new List<AssemblyStringStruct>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { ManagedAssemblyList.Clear(); for (AssemblyStringStruct* ptr = _this->m_AssemblyNames.first; ptr != _this->m_AssemblyNames.last; ptr++) { AssemblyStringStruct assemblyStringStruct = default(AssemblyStringStruct); assemblyStringStruct.capacity = ptr->capacity; assemblyStringStruct.extra = ptr->extra; assemblyStringStruct.label = ptr->label; assemblyStringStruct.size = ptr->size; assemblyStringStruct.data = ptr->data; AssemblyStringStruct item = assemblyStringStruct; ManagedAssemblyList.Add(item); } } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { foreach (string pluginAssemblyPath in pluginAssemblyPaths) { string fileName = Path.GetFileName(pluginAssemblyPath); ulong num = (ulong)fileName.Length; AssemblyStringStruct assemblyStringStruct = default(AssemblyStringStruct); assemblyStringStruct.label = 69; assemblyStringStruct.data = Marshal.StringToHGlobalAnsi(fileName); assemblyStringStruct.capacity = num; assemblyStringStruct.size = num; AssemblyStringStruct item = assemblyStringStruct; ManagedAssemblyList.Add(item); } } public unsafe void AllocNativeAssemblyListFromManaged() { AssemblyStringStruct* ptr = (AssemblyStringStruct*)(void*)Marshal.AllocHGlobal(Marshal.SizeOf<AssemblyStringStruct>() * ManagedAssemblyList.Count); int i = 0; AssemblyStringStruct* ptr2 = ptr; for (; i < ManagedAssemblyList.Count; i++) { ptr2->label = ManagedAssemblyList[i].label; ptr2->size = ManagedAssemblyList[i].size; ptr2->capacity = ManagedAssemblyList[i].capacity; ptr2->extra = ManagedAssemblyList[i].extra; ptr2->data = ManagedAssemblyList[i].data; ptr2++; } _originalAssemblyNames = _this->m_AssemblyNames; _this->m_AssemblyNames.first = ptr; _this->m_AssemblyNames.last = ptr + ManagedAssemblyList.Count; _this->m_AssemblyNames.end = _this->m_AssemblyNames.last; } public unsafe void PrintAssemblies() { for (AssemblyStringStruct* ptr = _this->m_AssemblyNames.first; ptr != _this->m_AssemblyNames.last; ptr++) { if (ptr->IsValid()) { Log.Warning("Ass: " + Marshal.PtrToStringAnsi(ptr->data, (int)ptr->size)); } } } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2017.v3 { [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(408)] public Vector<StringStorageDefaultV1> m_AssemblyNames; } [ApplicableToUnityVersionsSince("2017.3.0")] public class MonoManager : IMonoManager, INativeStruct { private Vector<StringStorageDefaultV1> _originalAssemblyNames; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV1(ManagedAssemblyList, _this->m_AssemblyNames); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { _originalAssemblyNames = _this->m_AssemblyNames; MonoManagerCommon.AllocNativeAssemblyListFromManagedV1(ManagedAssemblyList, &_this->m_AssemblyNames); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV1(_this->m_AssemblyNames); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2017.v2 { [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(448)] public Vector<StringStorageDefaultV1> m_AssemblyNames; } [ApplicableToUnityVersionsSince("2017.2.0")] public class MonoManager : IMonoManager, INativeStruct { private Vector<StringStorageDefaultV1> _originalAssemblyNames; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV1(ManagedAssemblyList, _this->m_AssemblyNames); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { _originalAssemblyNames = _this->m_AssemblyNames; MonoManagerCommon.AllocNativeAssemblyListFromManagedV1(ManagedAssemblyList, &_this->m_AssemblyNames); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV1(_this->m_AssemblyNames); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.v2017.v1 { [StructLayout(LayoutKind.Explicit)] public struct MonoManagerStruct { [FieldOffset(504)] public Vector<StringStorageDefaultV1> m_AssemblyNames; } [ApplicableToUnityVersionsSince("2017.1.0")] public class MonoManager : IMonoManager, INativeStruct { private Vector<StringStorageDefaultV1> _originalAssemblyNames; public List<StringStorageDefaultV1> ManagedAssemblyList = new List<StringStorageDefaultV1>(); public IntPtr Pointer { get; set; } private unsafe MonoManagerStruct* _this => (MonoManagerStruct*)(void*)Pointer; public int AssemblyCount => ManagedAssemblyList.Count; public MonoManager() { } public MonoManager(IntPtr pointer) { Pointer = pointer; } public unsafe void CopyNativeAssemblyListToManaged() { MonoManagerCommon.CopyNativeAssemblyListToManagedV1(ManagedAssemblyList, _this->m_AssemblyNames); } public void AddAssembliesToManagedList(List<string> pluginAssemblyPaths) { MonoManagerCommon.AddAssembliesToManagedListV1(ManagedAssemblyList, pluginAssemblyPaths); } public unsafe void AllocNativeAssemblyListFromManaged() { _originalAssemblyNames = _this->m_AssemblyNames; MonoManagerCommon.AllocNativeAssemblyListFromManagedV1(ManagedAssemblyList, &_this->m_AssemblyNames); } public unsafe void PrintAssemblies() { MonoManagerCommon.PrintAssembliesV1(_this->m_AssemblyNames); } public unsafe void RestoreOriginalAssemblyNamesArrayPtr() { _this->m_AssemblyNames = _originalAssemblyNames; } } [ApplicableToUnityVersionsSince("2017.1.0")] public class RelativePathString : IRelativePathString, INativeStruct { public IntPtr Pointer { get; set; } private unsafe StringStorageDefaultV1* _this => (StringStorageDefaultV1*)(void*)Pointer; public RelativePathString() { } public RelativePathString(IntPtr pointer) { Pointer = pointer; } public unsafe void FixAbsolutePath() { if (_this->size == 0L) { return; } nint num = _this->data; if (num == 0) { num = (IntPtr)(Pointer.ToInt64() + 8); } string fileName = Path.GetFileName(Marshal.PtrToStringAnsi(num, (int)_this->size)); int num2 = FixPluginTypesSerializationPatcher.PluginNames.IndexOf(fileName); if (num2 != -1) { ulong length; IntPtr data = CommonUnityFunctions.MallocString(FixPluginTypesSerializationPatcher.PluginPaths[num2], UseRightStructs.LabelMemStringId, out length); if (_this->data != 0) { CommonUnityFunctions.FreeAllocInternal(_this->data, _this->label); } StringStorageDefaultV1* @this = _this; @this->data = data; @this->capacity = length; @this->extra = 0uL; @this->size = length; @this->label = UseRightStructs.LabelMemStringId; } } public unsafe string ToStringAnsi() { if (_this->size == 0L) { return null; } nint num = _this->data; if (num == 0) { num = (nint)((byte*)_this + 8); } return Marshal.PtrToStringAnsi(num, (int)_this->size); } } } namespace FixPluginTypesSerialization.UnityPlayer.Structs.Default { [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct DynamicArrayData { public nint ptr; public int label; public ulong size; public ulong capacity; } public interface IAssemblyString : INativeStruct { void FixAbsolutePath(); void RestoreOriginalString(IntPtr constCharPtr); } public interface IMonoManager : INativeStruct { int AssemblyCount { get; } void CopyNativeAssemblyListToManaged(); void AddAssembliesToManagedList(List<string> pluginAssemblyPaths); void AllocNativeAssemblyListFromManaged(); void PrintAssemblies(); void RestoreOriginalAssemblyNamesArrayPtr(); } public interface IRelativePathString : INativeStruct { void FixAbsolutePath(); string ToStringAnsi(); } public struct RuntimeStatic<T> where T : struct { public unsafe T* value; public int label; public int unknown1; public ulong memAlign; public unsafe fixed byte memAreaName[32]; public unsafe fixed byte memObjectName[32]; public RegisterRuntimeInitializeAndCleanup registerCallbacks; public ulong unknown2; public ulong unknown3; public ulong unknown4; public ulong unknown5; public ulong unknown6; public ulong unknown7; public ReadWriteSpinLock @lock; } public struct RegisterRuntimeInitializeAndCleanup { public int order; public IntPtr userData; public IntPtr init; public IntPtr cleanup; public bool initCalled; public unsafe RegisterRuntimeInitializeAndCleanup* m_Next; public unsafe RegisterRuntimeInitializeAndCleanup* m_Prev; } public struct ReadWriteSpinLock { public long m_Counter; public ulong unknown1; public ulong unknown2; public ulong unknown3; public ulong unknown4; public ulong unknown5; public ulong unknown6; public ulong unknown7; } public struct ScriptingAssemblies { public DynamicArrayData names; public DynamicArrayData types; } [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct StringStorageDefaultV1 { public nint data; public ulong capacity; public ulong extra; public ulong size; public int label; } [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct StringStorageDefaultV2 { public StringStorageDefaultV2Union union; public StringRepresentation data_repr; public int label; } [StructLayout(LayoutKind.Explicit, Pack = 8)] public struct StringStorageDefaultV2Union { [FieldOffset(0)] public StackAllocatedRepresentationV2 embedded; [FieldOffset(0)] public HeapAllocatedRepresentationV2 heap; } public struct StackAllocatedRepresentationV2 { public unsafe fixed byte data[25]; } public struct HeapAllocatedRepresentationV2 { public nint data; public ulong capacity; public ulong size; } public enum StringRepresentation { Heap, Embedded, External } [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct StringStorageDefaultV3 { public StringStorageDefaultV3Union union; public int label; } [StructLayout(LayoutKind.Explicit, Pack = 8)] public struct StringStorageDefaultV3Union { [FieldOffset(0)] public StackAllocatedRepresentationV3 embedded; [FieldOffset(0)] public HeapAllocatedRepresentationV3 heap; } public struct StackAllocatedRepresentationV3 { public unsafe fixed byte data[31]; public StringStorageDefaultV3Flags flags; } public struct StringStorageDefaultV3Flags { public byte flags; public bool IsHeap { get { return (flags & 0x40) > 0; } set { if (value) { flags = 95; } else { flags = 0; } } } public bool IsExternal { get { return (flags & 0x80) > 0; } set { if (value) { flags = byte.MaxValue; } else { flags = 0; } } } public bool IsEmbedded { get { return flags < 64; } set { IsHeap = !value; } } public static implicit operator int(StringStorageDefaultV3Flags f) { return f.flags; } public static implicit operator byte(StringStorageDefaultV3Flags f) { return f.flags; } } [StructLayout(LayoutKind.Explicit)] public struct HeapAllocatedRepresentationV3 { [FieldOffset(0)] public nint data; [FieldOffset(8)] public ulong capacity; [FieldOffset(16)] public ulong size; [FieldOffset(31)] public StringStorageDefaultV3Flags flags; } public struct Vector<T> where T : struct { public unsafe T* first; public unsafe T* last; public unsafe T* end; } public interface INativeStruct { IntPtr Pointer { get; set; } } } namespace FixPluginTypesSerialization.Patchers { internal class AwakeFromLoad : Patcher { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void AwakeFromLoadDelegate(IntPtr _monoManager, int awakeMode); private static AwakeFromLoadDelegate original; private static NativeDetour _detour; internal static IMonoManager CurrentMonoManager; internal static bool IsApplied { get; private set; } protected override BytePattern[] PdbPatterns { get; } = new BytePattern[2] { Encoding.ASCII.GetBytes("MonoManager::AwakeFromLoad"), Encoding.ASCII.GetBytes("AwakeFromLoad@MonoManager") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[1] { "40 53 48 81 EC ? ? ? ? 33 C0 C7 44 24 ? ? ? ? ? 0F 57 C0" }; protected override void Apply(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<AwakeFromLoadDelegate>(OnAwakeFromLoad); _detour = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); original = _detour.GenerateTrampoline<AwakeFromLoadDelegate>(); _detour.Apply(); IsApplied = true; } internal static void Dispose() { NativeDetour detour = _detour; if (detour != null) { detour.Dispose(); } IsApplied = false; } private static void OnAwakeFromLoad(IntPtr _monoManager, int awakeMode) { CurrentMonoManager = UseRightStructs.GetStruct<IMonoManager>(_monoManager); CurrentMonoManager.CopyNativeAssemblyListToManaged(); IsAssemblyCreated.VanillaAssemblyCount = CurrentMonoManager.AssemblyCount; CurrentMonoManager.AddAssembliesToManagedList(FixPluginTypesSerializationPatcher.PluginPaths); CurrentMonoManager.AllocNativeAssemblyListFromManaged(); original(_monoManager, awakeMode); IsFileCreated.Dispose(); ConvertSeparatorsToPlatform.Dispose(); IsAssemblyCreated.Dispose(); } } internal class ConvertSeparatorsToPlatform : Patcher { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void ConvertSeparatorsToPlatformDelegate(IntPtr assemblyStringPathName); private static NativeDetour _detourConvertSeparatorsToPlatform; private static ConvertSeparatorsToPlatformDelegate originalConvertSeparatorsToPlatform; internal static bool IsApplied { get; private set; } protected override BytePattern[] PdbPatterns { get; } = new BytePattern[1] { Encoding.ASCII.GetBytes("ConvertSeparatorsToPlatform") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[0]; protected override void Apply(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<ConvertSeparatorsToPlatformDelegate>(OnConvertSeparatorsToPlatformV1); _detourConvertSeparatorsToPlatform = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); originalConvertSeparatorsToPlatform = _detourConvertSeparatorsToPlatform.GenerateTrampoline<ConvertSeparatorsToPlatformDelegate>(); _detourConvertSeparatorsToPlatform.Apply(); IsApplied = true; } internal static void Dispose() { if (_detourConvertSeparatorsToPlatform != null && _detourConvertSeparatorsToPlatform.IsApplied) { _detourConvertSeparatorsToPlatform.Dispose(); } IsApplied = false; } private static void OnConvertSeparatorsToPlatformV1(IntPtr assemblyStringPathName) { UseRightStructs.GetStruct<IRelativePathString>(assemblyStringPathName).FixAbsolutePath(); originalConvertSeparatorsToPlatform(assemblyStringPathName); } } internal class IsAssemblyCreated : Patcher { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate bool IsAssemblyCreatedDelegate(IntPtr _monoManager, int index); private static IsAssemblyCreatedDelegate original; private static NativeDetour _detour; internal static int VanillaAssemblyCount; internal static bool IsApplied { get; private set; } protected override BytePattern[] PdbPatterns { get; } = new BytePattern[2] { Encoding.ASCII.GetBytes("MonoManager::IsAssemblyCreated"), Encoding.ASCII.GetBytes("IsAssemblyCreated@MonoManager") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[2] { "E8 ? ? ? ? 84 C0 74 43 45 84 FF", "E8 ? ? ? ? 84 C0 74 41 45 84 FF" }; protected override void Apply(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<IsAssemblyCreatedDelegate>(OnIsAssemblyCreated); _detour = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); original = _detour.GenerateTrampoline<IsAssemblyCreatedDelegate>(); NativeDetour detour = _detour; if (detour != null) { detour.Apply(); } IsApplied = true; } internal static void Dispose() { NativeDetour detour = _detour; if (detour != null) { detour.Dispose(); } IsApplied = false; } private static bool OnIsAssemblyCreated(IntPtr _monoManager, int index) { if (index >= VanillaAssemblyCount) { return true; } return original(_monoManager, index); } } internal class IsFileCreated : Patcher { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate bool IsFileCreatedDelegate(IntPtr str); private static IsFileCreatedDelegate original; private static NativeDetour _detour; internal static bool IsApplied { get; private set; } protected override BytePattern[] PdbPatterns { get; } = new BytePattern[1] { Encoding.ASCII.GetBytes("IsFileCreated") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[0]; protected override void Apply(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<IsFileCreatedDelegate>(OnIsFileCreated); _detour = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); original = _detour.GenerateTrampoline<IsFileCreatedDelegate>(); _detour.Apply(); IsApplied = true; } internal static void Dispose() { NativeDetour detour = _detour; if (detour != null) { detour.Dispose(); } IsApplied = false; } private static bool OnIsFileCreated(IntPtr str) { string text = UseRightStructs.GetStruct<IRelativePathString>(str).ToStringAnsi(); if (text != null && FixPluginTypesSerializationPatcher.PluginNames.Any(text.EndsWith)) { return true; } return original(str); } } internal abstract class Patcher { protected abstract BytePattern[] PdbPatterns { get; } protected abstract BytePattern[] SigPatterns { get; } public void Patch(IntPtr unityModule, int moduleSize, MiniPdbReader pdbReader, ConfigEntry<string> functionOffsetCache) { IntPtr intPtr = PatternDiscover.Discover(unityModule, moduleSize, pdbReader, functionOffsetCache, PdbPatterns, SigPatterns); if (intPtr != IntPtr.Zero) { Apply(intPtr); } } protected abstract void Apply(IntPtr from); } internal class ReadStringFromFile : Patcher { [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate bool ReadStringFromFileDelegate(IntPtr outData, IntPtr assemblyStringPathName); private delegate IntPtr mono_assembly_load_from_full_delegate(IntPtr image, IntPtr constCharFName, IntPtr status, IntPtr refonly); private static NativeDetour _detourReadStringFromFile; private static ReadStringFromFileDelegate originalReadStringFromFile; private static readonly IntPtr Mono = NativeLibraryHelper.OpenLibrary("mono-2.0-bdwgc.dll"); private static readonly IntPtr mono_assembly_load_from_full_fn = Mono.GetFunction("mono_assembly_load_from_full"); private static NativeDetour _monoDetour; private static mono_assembly_load_from_full_delegate originalMonoAssemblyLoadFromFull; internal static readonly Dictionary<IntPtr, (IntPtr, IntPtr, ulong)> ModifiedPathsToOriginalPaths = new Dictionary<IntPtr, (IntPtr, IntPtr, ulong)>(); protected override BytePattern[] PdbPatterns { get; } = new BytePattern[1] { Encoding.ASCII.GetBytes("ReadStringFromFile") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[3] { "E8 ? ? ? ? 4C 8D 45 EF 48 8D 4D 17 84 C0", "E8 ? ? ? ? 48 8B 4D B7 0F B6 F0", "E8 ? ? ? ? 48 8D 4D 8F 0F B6 D8" }; protected override void Apply(IntPtr from) { ApplyReadStringFromFileDetour(from); ApplyMonoDetour(); } private void ApplyReadStringFromFileDetour(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<ReadStringFromFileDelegate>(OnReadStringFromFile); _detourReadStringFromFile = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); originalReadStringFromFile = _detourReadStringFromFile.GenerateTrampoline<ReadStringFromFileDelegate>(); _detourReadStringFromFile.Apply(); } private void ApplyMonoDetour() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<mono_assembly_load_from_full_delegate>(OnMonoAssemblyLoadFromFull); _monoDetour = new NativeDetour(mono_assembly_load_from_full_fn, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); originalMonoAssemblyLoadFromFull = _monoDetour.GenerateTrampoline<mono_assembly_load_from_full_delegate>(); _monoDetour.Apply(); } internal static void Dispose() { DisposeDetours(); } private static void DisposeDetours() { if (_detourReadStringFromFile != null && _detourReadStringFromFile.IsApplied) { _detourReadStringFromFile.Dispose(); } if (_monoDetour != null && _monoDetour.IsApplied) { _monoDetour.Dispose(); } foreach (KeyValuePair<IntPtr, (IntPtr, IntPtr, ulong)> modifiedPathsToOriginalPath in ModifiedPathsToOriginalPaths) { modifiedPathsToOriginalPath.Deconstruct(out var key, out var _); Marshal.FreeHGlobal(key); } } private static bool OnReadStringFromFile(IntPtr outData, IntPtr assemblyStringPathName) { UseRightStructs.GetStruct<IAssemblyString>(assemblyStringPathName).FixAbsolutePath(); return originalReadStringFromFile(outData, assemblyStringPathName); } private static IntPtr OnMonoAssemblyLoadFromFull(IntPtr image, IntPtr constCharFNamePtr, IntPtr status, IntPtr refonly) { IntPtr result = originalMonoAssemblyLoadFromFull(image, constCharFNamePtr, status, refonly); UseRightStructs.GetStruct<IAssemblyString>(constCharFNamePtr).RestoreOriginalString(constCharFNamePtr); return result; } } internal class ScriptingManagerDeconstructor : Patcher { [UnmanagedFunctionPointer(CallingConvention.FastCall)] private delegate void ScriptingManagerDeconstructorDelegate(IntPtr scriptingManagerPtr); private static NativeDetour _detour; private static ScriptingManagerDeconstructorDelegate orig; internal static bool IsApplied { get; private set; } protected override BytePattern[] PdbPatterns { get; } = new BytePattern[2] { Encoding.ASCII.GetBytes("ScriptingManager::~ScriptingManager"), Encoding.ASCII.GetBytes("?1ScriptingManager@") }; protected override BytePattern[] SigPatterns { get; } = new BytePattern[1] { "48 89 5C 24 ? 57 48 83 EC 20 48 8D 05 ? ? ? ? 48 8B D9 48 89 01 48 81 C1 ? ? ? ? E8 ? ? ? ? 48 8D 8B ? ? ? ? E8 ? ? ? ? 48 8D 8B" }; protected override void Apply(IntPtr from) { ApplyDetour(from); IsApplied = true; } private void ApplyDetour(IntPtr from) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate<ScriptingManagerDeconstructorDelegate>(OnDeconstructor); _detour = new NativeDetour(from, functionPointerForDelegate, new NativeDetourConfig { ManualApply = true }); orig = _detour.GenerateTrampoline<ScriptingManagerDeconstructorDelegate>(); _detour.Apply(); } internal static void Dispose() { DisposeDetours(); IsApplied = false; } private static void DisposeDetours() { if (_detour != null && _detour.IsApplied) { _detour.Dispose(); } } private static void OnDeconstructor(IntPtr scriptingManagerPtr) { AwakeFromLoad.CurrentMonoManager.RestoreOriginalAssemblyNamesArrayPtr(); Log.Info("Restored original AssemblyNames list"); orig(scriptingManagerPtr); } } }
BepInEx/patchers/FixPluginTypesSerialization/Microsoft.Deployment.Compression.Cab.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Text; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyDescription("Managed libraries for cabinet archive packing and unpacking")] [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] [assembly: AllowPartiallyTrustedCallers] [assembly: AssemblyFileVersion("3.10.1.2213")] [assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCopyright("Copyright (c) Outercurve Foundation. All rights reserved.")] [assembly: AssemblyProduct("Windows Installer XML Toolset")] [assembly: AssemblyConfiguration("")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, Assertion = true, UnmanagedCode = true)] [assembly: AssemblyVersion("3.0.0.0")] namespace Microsoft.Tools.WindowsInstallerXml { internal static class WixDistribution { public static string NewsUrl = "http://wixtoolset.org/news/"; public static string ShortProduct = "WiX Toolset"; public static string SupportUrl = "http://wixtoolset.org/"; public static string TelemetryUrlFormat = "http://wixtoolset.org/telemetry/v{0}/?r={1}"; public static string ReplacePlaceholders(string original, Assembly assembly) { if ((object)assembly != null) { FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(assembly.Location); original = original.Replace("[FileComments]", versionInfo.Comments); original = original.Replace("[FileCopyright]", versionInfo.LegalCopyright); original = original.Replace("[FileProductName]", versionInfo.ProductName); original = original.Replace("[FileVersion]", versionInfo.FileVersion); if (original.Contains("[FileVersionMajorMinor]")) { Version version = new Version(versionInfo.FileVersion); original = original.Replace("[FileVersionMajorMinor]", version.Major + "." + version.Minor); } if (TryGetAttribute<AssemblyCompanyAttribute>(assembly, out var attribute)) { original = original.Replace("[AssemblyCompany]", attribute.Company); } if (TryGetAttribute<AssemblyCopyrightAttribute>(assembly, out var attribute2)) { original = original.Replace("[AssemblyCopyright]", attribute2.Copyright); } if (TryGetAttribute<AssemblyDescriptionAttribute>(assembly, out var attribute3)) { original = original.Replace("[AssemblyDescription]", attribute3.Description); } if (TryGetAttribute<AssemblyProductAttribute>(assembly, out var attribute4)) { original = original.Replace("[AssemblyProduct]", attribute4.Product); } if (TryGetAttribute<AssemblyTitleAttribute>(assembly, out var attribute5)) { original = original.Replace("[AssemblyTitle]", attribute5.Title); } } original = original.Replace("[NewsUrl]", NewsUrl); original = original.Replace("[ShortProduct]", ShortProduct); original = original.Replace("[SupportUrl]", SupportUrl); return original; } private static bool TryGetAttribute<T>(Assembly assembly, out T attribute) where T : Attribute { attribute = null; object[] customAttributes = assembly.GetCustomAttributes(typeof(T), inherit: false); if (customAttributes != null && customAttributes.Length != 0) { attribute = customAttributes[0] as T; } return attribute != null; } } } namespace Microsoft.Deployment.Compression.Cab { internal class CabPacker : CabWorker { private const string TempStreamName = "%%TEMP%%"; private NativeMethods.FCI.Handle fciHandle; private NativeMethods.FCI.PFNALLOC fciAllocMemHandler; private NativeMethods.FCI.PFNFREE fciFreeMemHandler; private NativeMethods.FCI.PFNOPEN fciOpenStreamHandler; private NativeMethods.FCI.PFNREAD fciReadStreamHandler; private NativeMethods.FCI.PFNWRITE fciWriteStreamHandler; private NativeMethods.FCI.PFNCLOSE fciCloseStreamHandler; private NativeMethods.FCI.PFNSEEK fciSeekStreamHandler; private NativeMethods.FCI.PFNFILEPLACED fciFilePlacedHandler; private NativeMethods.FCI.PFNDELETE fciDeleteFileHandler; private NativeMethods.FCI.PFNGETTEMPFILE fciGetTempFileHandler; private NativeMethods.FCI.PFNGETNEXTCABINET fciGetNextCabinet; private NativeMethods.FCI.PFNSTATUS fciCreateStatus; private NativeMethods.FCI.PFNGETOPENINFO fciGetOpenInfo; private IPackStreamContext context; private FileAttributes fileAttributes; private DateTime fileLastWriteTime; private int maxCabBytes; private long totalFolderBytesProcessedInCurrentCab; private CompressionLevel compressionLevel; private bool dontUseTempFiles; private IList<Stream> tempStreams; public bool UseTempFiles { get { return !dontUseTempFiles; } set { dontUseTempFiles = !value; } } public CompressionLevel CompressionLevel { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return compressionLevel; } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) compressionLevel = value; } } public CabPacker(CabEngine cabEngine) : base(cabEngine) { //IL_0103: Unknown result type (might be due to invalid IL or missing references) fciAllocMemHandler = base.CabAllocMem; fciFreeMemHandler = base.CabFreeMem; fciOpenStreamHandler = CabOpenStreamEx; fciReadStreamHandler = CabReadStreamEx; fciWriteStreamHandler = CabWriteStreamEx; fciCloseStreamHandler = CabCloseStreamEx; fciSeekStreamHandler = CabSeekStreamEx; fciFilePlacedHandler = CabFilePlaced; fciDeleteFileHandler = CabDeleteFile; fciGetTempFileHandler = CabGetTempFile; fciGetNextCabinet = CabGetNextCabinet; fciCreateStatus = CabCreateStatus; fciGetOpenInfo = CabGetOpenInfo; tempStreams = new List<Stream>(); compressionLevel = (CompressionLevel)6; } private void CreateFci(long maxArchiveSize) { NativeMethods.FCI.CCAB cCAB = new NativeMethods.FCI.CCAB(); checked { if (maxArchiveSize > 0 && maxArchiveSize < cCAB.cb) { cCAB.cb = Math.Max(32768, (int)maxArchiveSize); } object option = context.GetOption("maxFolderSize", (object[])null); if (option != null) { long num = Convert.ToInt64(option, CultureInfo.InvariantCulture); if (num > 0 && num < cCAB.cbFolderThresh) { cCAB.cbFolderThresh = (int)num; } } maxCabBytes = cCAB.cb; cCAB.szCab = context.GetArchiveName(0); if (cCAB.szCab == null) { throw new FileNotFoundException("Cabinet name not provided by stream context."); } cCAB.setID = (short)new Random().Next(-32768, 32768); base.CabNumbers[cCAB.szCab] = 0; currentArchiveName = cCAB.szCab; totalArchives = 1; base.CabStream = null; base.Erf.Clear(); fciHandle = NativeMethods.FCI.Create(base.ErfHandle.AddrOfPinnedObject(), fciFilePlacedHandler, fciAllocMemHandler, fciFreeMemHandler, fciOpenStreamHandler, fciReadStreamHandler, fciWriteStreamHandler, fciCloseStreamHandler, fciSeekStreamHandler, fciDeleteFileHandler, fciGetTempFileHandler, cCAB, IntPtr.Zero); CheckError(extracting: false); } } [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] public void Pack(IPackStreamContext streamContext, IEnumerable<string> files, long maxArchiveSize) { //IL_01d0: Unknown result type (might be due to invalid IL or missing references) if (streamContext == null) { throw new ArgumentNullException("streamContext"); } if (files == null) { throw new ArgumentNullException("files"); } lock (this) { try { context = streamContext; ResetProgressData(); CreateFci(maxArchiveSize); checked { FileAttributes fileAttributes = default(FileAttributes); DateTime dateTime = default(DateTime); foreach (string file in files) { Stream stream = context.OpenFileReadStream(file, ref fileAttributes, ref dateTime); if (stream != null) { totalFileBytes += stream.Length; totalFiles++; context.CloseFileReadStream(file, stream); } } long num = 0L; currentFileNumber = -1; FileAttributes attributes = default(FileAttributes); DateTime lastWriteTime = default(DateTime); foreach (string file2 in files) { Stream stream2 = context.OpenFileReadStream(file2, ref attributes, ref lastWriteTime); if (stream2 == null) { continue; } if (stream2.Length >= 2147450880) { throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "File {0} exceeds maximum file size for cabinet format.", new object[1] { file2 })); } if (num > 0) { bool flag = num + stream2.Length >= 2147450880; if (!flag) { flag = Convert.ToBoolean(streamContext.GetOption("nextFolder", new object[2] { file2, currentFolderNumber }), CultureInfo.InvariantCulture); } if (flag) { FlushFolder(); num = 0L; } } if (currentFolderTotalBytes > 0) { currentFolderTotalBytes = 0L; currentFolderNumber++; num = 0L; } currentFileName = file2; currentFileNumber++; currentFileTotalBytes = stream2.Length; currentFileBytesProcessed = 0L; OnProgress((ArchiveProgressType)0); num += stream2.Length; AddFile(file2, stream2, attributes, lastWriteTime, execute: false, CompressionLevel); } FlushFolder(); FlushCabinet(); } } finally { if (base.CabStream != null) { context.CloseArchiveWriteStream((int)currentArchiveNumber, currentArchiveName, base.CabStream); base.CabStream = null; } if (base.FileStream != null) { context.CloseFileReadStream(currentFileName, base.FileStream); base.FileStream = null; } context = null; if (fciHandle != null) { fciHandle.Dispose(); fciHandle = null; } } } } internal override int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv) { //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown if (base.CabNumbers.ContainsKey(path)) { Stream stream = base.CabStream; if (stream == null) { short num = base.CabNumbers[path]; currentFolderTotalBytes = 0L; stream = context.OpenArchiveWriteStream((int)num, path, true, (CompressionEngine)(object)base.CabEngine); if (stream == null) { throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, "Cabinet {0} not provided.", new object[1] { num })); } currentArchiveName = path; currentArchiveTotalBytes = Math.Min(totalFolderBytesProcessedInCurrentCab, maxCabBytes); currentArchiveBytesProcessed = 0L; OnProgress((ArchiveProgressType)3); base.CabStream = stream; } path = "%%CAB%%"; } else { if (path == "%%TEMP%%") { Stream stream2 = new MemoryStream(); tempStreams.Add(stream2); int result = base.StreamHandles.AllocHandle(stream2); err = 0; return result; } if (path != "%%CAB%%") { path = Path.Combine(Path.GetTempPath(), path); Stream stream3 = new FileStream(path, FileMode.Open, FileAccess.ReadWrite); tempStreams.Add(stream3); stream3 = (Stream)new DuplicateStream(stream3); int result2 = base.StreamHandles.AllocHandle(stream3); err = 0; return result2; } } return base.CabOpenStreamEx(path, openFlags, shareMode, out err, pv); } internal override int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) { int num = base.CabWriteStreamEx(streamHandle, memory, cb, out err, pv); checked { if (num > 0 && err == 0 && DuplicateStream.OriginalStream(base.StreamHandles[streamHandle]) == DuplicateStream.OriginalStream(base.CabStream)) { currentArchiveBytesProcessed += cb; if (currentArchiveBytesProcessed > currentArchiveTotalBytes) { currentArchiveBytesProcessed = currentArchiveTotalBytes; } } return num; } } internal override int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv) { Stream stream = DuplicateStream.OriginalStream(base.StreamHandles[streamHandle]); checked { if (stream == DuplicateStream.OriginalStream(base.FileStream)) { context.CloseFileReadStream(currentFileName, stream); base.FileStream = null; long num = currentFileTotalBytes - currentFileBytesProcessed; currentFileBytesProcessed += num; fileBytesProcessed += num; OnProgress((ArchiveProgressType)2); currentFileTotalBytes = 0L; currentFileBytesProcessed = 0L; currentFileName = null; } else if (stream == DuplicateStream.OriginalStream(base.CabStream)) { if (stream.CanWrite) { stream.Flush(); } currentArchiveBytesProcessed = currentArchiveTotalBytes; OnProgress((ArchiveProgressType)5); currentArchiveNumber++; totalArchives++; context.CloseArchiveWriteStream(unchecked((int)currentArchiveNumber), currentArchiveName, stream); currentArchiveName = base.NextCabinetName; currentArchiveBytesProcessed = (currentArchiveTotalBytes = 0L); totalFolderBytesProcessedInCurrentCab = 0L; base.CabStream = null; } else { stream.Close(); tempStreams.Remove(stream); } return base.CabCloseStreamEx(streamHandle, out err, pv); } } protected override void Dispose(bool disposing) { try { if (disposing && fciHandle != null) { fciHandle.Dispose(); fciHandle = null; } } finally { base.Dispose(disposing); } } private static NativeMethods.FCI.TCOMP GetCompressionType(CompressionLevel compLevel) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown //IL_000d: Unknown result type (might be due to invalid IL or missing references) if ((int)compLevel < 1) { return NativeMethods.FCI.TCOMP.TYPE_NONE; } if ((int)compLevel > 10) { compLevel = (CompressionLevel)10; } int num = checked(6 * (compLevel - 1)) / 9; return (NativeMethods.FCI.TCOMP)checked((ushort)(3 | (3840 + (num << 8)))); } private void AddFile(string name, Stream stream, FileAttributes attributes, DateTime lastWriteTime, bool execute, CompressionLevel compLevel) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) base.FileStream = stream; fileAttributes = attributes & (FileAttributes.ReadOnly | FileAttributes.Hidden | FileAttributes.System | FileAttributes.Archive); fileLastWriteTime = lastWriteTime; currentFileName = name; NativeMethods.FCI.TCOMP compressionType = GetCompressionType(compLevel); IntPtr intPtr = IntPtr.Zero; try { Encoding encoding = Encoding.ASCII; if (Encoding.UTF8.GetByteCount(name) > name.Length) { encoding = Encoding.UTF8; fileAttributes |= FileAttributes.Normal; } byte[] bytes = encoding.GetBytes(name); intPtr = Marshal.AllocHGlobal(checked(bytes.Length + 1)); Marshal.Copy(bytes, 0, intPtr, bytes.Length); Marshal.WriteByte(intPtr, bytes.Length, 0); base.Erf.Clear(); NativeMethods.FCI.AddFile(fciHandle, string.Empty, intPtr, execute, fciGetNextCabinet, fciCreateStatus, fciGetOpenInfo, compressionType); } finally { if (intPtr != IntPtr.Zero) { Marshal.FreeHGlobal(intPtr); } } CheckError(extracting: false); base.FileStream = null; currentFileName = null; } private void FlushFolder() { base.Erf.Clear(); NativeMethods.FCI.FlushFolder(fciHandle, fciGetNextCabinet, fciCreateStatus); CheckError(extracting: false); } private void FlushCabinet() { base.Erf.Clear(); NativeMethods.FCI.FlushCabinet(fciHandle, fGetNextCab: false, fciGetNextCabinet, fciCreateStatus); CheckError(extracting: false); } private int CabGetOpenInfo(string path, out short date, out short time, out short attribs, out int err, IntPtr pv) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown CompressionEngine.DateTimeToDosDateAndTime(fileLastWriteTime, ref date, ref time); attribs = checked((short)fileAttributes); Stream stream = base.FileStream; base.FileStream = (Stream)new DuplicateStream(stream); int result = base.StreamHandles.AllocHandle(stream); err = 0; return result; } private int CabFilePlaced(IntPtr pccab, string filePath, long fileSize, int continuation, IntPtr pv) { return 0; } private int CabGetNextCabinet(IntPtr pccab, uint prevCabSize, IntPtr pv) { NativeMethods.FCI.CCAB cCAB = new NativeMethods.FCI.CCAB(); Marshal.PtrToStructure(pccab, (object)cCAB); cCAB.szDisk = string.Empty; cCAB.szCab = context.GetArchiveName(cCAB.iCab); base.CabNumbers[cCAB.szCab] = checked((short)cCAB.iCab); base.NextCabinetName = cCAB.szCab; Marshal.StructureToPtr((object)cCAB, pccab, fDeleteOld: false); return 1; } private int CabCreateStatus(NativeMethods.FCI.STATUS typeStatus, uint cb1, uint cb2, IntPtr pv) { checked { switch (typeStatus) { case NativeMethods.FCI.STATUS.FILE: if (cb2 != 0 && currentFileBytesProcessed < currentFileTotalBytes) { if (currentFileBytesProcessed + cb2 > currentFileTotalBytes) { cb2 = (uint)currentFileTotalBytes - (uint)currentFileBytesProcessed; } currentFileBytesProcessed += cb2; fileBytesProcessed += cb2; OnProgress((ArchiveProgressType)1); } break; case NativeMethods.FCI.STATUS.FOLDER: if (cb1 == 0) { currentFolderTotalBytes = cb2 - totalFolderBytesProcessedInCurrentCab; totalFolderBytesProcessedInCurrentCab = cb2; } else if (currentFolderTotalBytes == 0L) { OnProgress((ArchiveProgressType)4); } break; } return 0; } } private int CabGetTempFile(IntPtr tempNamePtr, int tempNameSize, IntPtr pv) { string s = ((!UseTempFiles) ? "%%TEMP%%" : Path.GetFileName(Path.GetTempFileName())); byte[] bytes = Encoding.ASCII.GetBytes(s); if (bytes.Length >= tempNameSize) { return -1; } Marshal.Copy(bytes, 0, tempNamePtr, bytes.Length); Marshal.WriteByte(tempNamePtr, bytes.Length, 0); return 1; } private int CabDeleteFile(string path, out int err, IntPtr pv) { try { if (path != "%%TEMP%%") { path = Path.Combine(Path.GetTempPath(), path); File.Delete(path); } } catch (IOException) { } err = 0; return 1; } } public class CabEngine : CompressionEngine { private CabPacker packer; private CabUnpacker unpacker; private CabPacker Packer { get { if (packer == null) { packer = new CabPacker(this); } return packer; } } private CabUnpacker Unpacker { get { if (unpacker == null) { unpacker = new CabUnpacker(this); } return unpacker; } } protected override void Dispose(bool disposing) { if (disposing) { if (packer != null) { packer.Dispose(); packer = null; } if (unpacker != null) { unpacker.Dispose(); unpacker = null; } } ((CompressionEngine)this).Dispose(disposing); } public override void Pack(IPackStreamContext streamContext, IEnumerable<string> files, long maxArchiveSize) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) Packer.CompressionLevel = ((CompressionEngine)this).CompressionLevel; Packer.UseTempFiles = ((CompressionEngine)this).UseTempFiles; Packer.Pack(streamContext, files, maxArchiveSize); } public override bool IsArchive(Stream stream) { return Unpacker.IsArchive(stream); } public override IList<ArchiveFileInfo> GetFileInfo(IUnpackStreamContext streamContext, Predicate<string> fileFilter) { return Unpacker.GetFileInfo(streamContext, fileFilter); } public override void Unpack(IUnpackStreamContext streamContext, Predicate<string> fileFilter) { Unpacker.Unpack(streamContext, fileFilter); } internal void ReportProgress(ArchiveProgressEventArgs e) { ((CompressionEngine)this).OnProgress(e); } } internal abstract class CabWorker : IDisposable { internal const string CabStreamName = "%%CAB%%"; private CabEngine cabEngine; private HandleManager<Stream> streamHandles; private Stream cabStream; private Stream fileStream; private NativeMethods.ERF erf; private GCHandle erfHandle; private IDictionary<string, short> cabNumbers; private string nextCabinetName; private bool suppressProgressEvents; private byte[] buf; protected string currentFileName; protected int currentFileNumber; protected int totalFiles; protected long currentFileBytesProcessed; protected long currentFileTotalBytes; protected short currentFolderNumber; protected long currentFolderTotalBytes; protected string currentArchiveName; protected short currentArchiveNumber; protected short totalArchives; protected long currentArchiveBytesProcessed; protected long currentArchiveTotalBytes; protected long fileBytesProcessed; protected long totalFileBytes; public CabEngine CabEngine => cabEngine; internal NativeMethods.ERF Erf => erf; internal GCHandle ErfHandle => erfHandle; internal HandleManager<Stream> StreamHandles => streamHandles; internal bool SuppressProgressEvents { get { return suppressProgressEvents; } set { suppressProgressEvents = value; } } internal IDictionary<string, short> CabNumbers => cabNumbers; internal string NextCabinetName { get { return nextCabinetName; } set { nextCabinetName = value; } } internal Stream CabStream { get { return cabStream; } set { cabStream = value; } } internal Stream FileStream { get { return fileStream; } set { fileStream = value; } } protected CabWorker(CabEngine cabEngine) { this.cabEngine = cabEngine; streamHandles = new HandleManager<Stream>(); erf = new NativeMethods.ERF(); erfHandle = GCHandle.Alloc(erf, GCHandleType.Pinned); cabNumbers = new Dictionary<string, short>(1); buf = new byte[32768]; } ~CabWorker() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected void ResetProgressData() { currentFileName = null; currentFileNumber = 0; totalFiles = 0; currentFileBytesProcessed = 0L; currentFileTotalBytes = 0L; currentFolderNumber = 0; currentFolderTotalBytes = 0L; currentArchiveName = null; currentArchiveNumber = 0; totalArchives = 0; currentArchiveBytesProcessed = 0L; currentArchiveTotalBytes = 0L; fileBytesProcessed = 0L; totalFileBytes = 0L; } protected void OnProgress(ArchiveProgressType progressType) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown if (!suppressProgressEvents) { ArchiveProgressEventArgs e = new ArchiveProgressEventArgs(progressType, currentFileName, (currentFileNumber >= 0) ? currentFileNumber : 0, totalFiles, currentFileBytesProcessed, currentFileTotalBytes, currentArchiveName, (int)currentArchiveNumber, (int)totalArchives, currentArchiveBytesProcessed, currentArchiveTotalBytes, fileBytesProcessed, totalFileBytes); CabEngine.ReportProgress(e); } } internal IntPtr CabAllocMem(int byteCount) { return Marshal.AllocHGlobal((IntPtr)byteCount); } internal void CabFreeMem(IntPtr memPointer) { Marshal.FreeHGlobal(memPointer); } internal int CabOpenStream(string path, int openFlags, int shareMode) { int err; return CabOpenStreamEx(path, openFlags, shareMode, out err, IntPtr.Zero); } internal virtual int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown path = path.Trim(); Stream stream = cabStream; cabStream = (Stream)new DuplicateStream(stream); int result = streamHandles.AllocHandle(stream); err = 0; return result; } internal int CabReadStream(int streamHandle, IntPtr memory, int cb) { int err; return CabReadStreamEx(streamHandle, memory, cb, out err, IntPtr.Zero); } internal virtual int CabReadStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) { Stream stream = streamHandles[streamHandle]; int num = cb; if (num > buf.Length) { buf = new byte[num]; } num = stream.Read(buf, 0, num); Marshal.Copy(buf, 0, memory, num); err = 0; return num; } internal int CabWriteStream(int streamHandle, IntPtr memory, int cb) { int err; return CabWriteStreamEx(streamHandle, memory, cb, out err, IntPtr.Zero); } internal virtual int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) { Stream stream = streamHandles[streamHandle]; if (cb > buf.Length) { buf = new byte[cb]; } Marshal.Copy(memory, buf, 0, cb); stream.Write(buf, 0, cb); err = 0; return cb; } internal int CabCloseStream(int streamHandle) { int err; return CabCloseStreamEx(streamHandle, out err, IntPtr.Zero); } internal virtual int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv) { streamHandles.FreeHandle(streamHandle); err = 0; return 0; } internal int CabSeekStream(int streamHandle, int offset, int seekOrigin) { int err; return CabSeekStreamEx(streamHandle, offset, seekOrigin, out err, IntPtr.Zero); } internal virtual int CabSeekStreamEx(int streamHandle, int offset, int seekOrigin, out int err, IntPtr pv) { checked { offset = (int)streamHandles[streamHandle].Seek(offset, unchecked((SeekOrigin)seekOrigin)); err = 0; return offset; } } protected virtual void Dispose(bool disposing) { if (disposing) { if (cabStream != null) { cabStream.Close(); cabStream = null; } if (fileStream != null) { fileStream.Close(); fileStream = null; } } if (erfHandle.IsAllocated) { erfHandle.Free(); } } protected void CheckError(bool extracting) { if (Erf.Error) { throw new CabException(Erf.Oper, Erf.Type, CabException.GetErrorMessage(Erf.Oper, Erf.Type, extracting)); } } } [Serializable] public class CabException : ArchiveException { private static ResourceManager errorResources; private int error; private int errorCode; public int Error => error; public int ErrorCode => errorCode; internal static ResourceManager ErrorResources { get { if (errorResources == null) { errorResources = new ResourceManager(typeof(CabException).Namespace + ".Errors", typeof(CabException).Assembly); } return errorResources; } } public CabException(string message, Exception innerException) : this(0, 0, message, innerException) { } public CabException(string message) : this(0, 0, message, null) { } public CabException() : this(0, 0, null, null) { } internal CabException(int error, int errorCode, string message, Exception innerException) : base(message, innerException) { this.error = error; this.errorCode = errorCode; } internal CabException(int error, int errorCode, string message) : this(error, errorCode, message, null) { } protected CabException(SerializationInfo info, StreamingContext context) : base(info, context) { if (info == null) { throw new ArgumentNullException("info"); } error = info.GetInt32("cabError"); errorCode = info.GetInt32("cabErrorCode"); } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) { throw new ArgumentNullException("info"); } info.AddValue("cabError", error); info.AddValue("cabErrorCode", errorCode); ((Exception)this).GetObjectData(info, context); } internal static string GetErrorMessage(int error, int errorCode, bool extracting) { int num = (extracting ? 2000 : 1000); string text = ErrorResources.GetString(checked(num + error).ToString(CultureInfo.InvariantCulture.NumberFormat), CultureInfo.CurrentCulture); if (text == null) { text = ErrorResources.GetString(num.ToString(CultureInfo.InvariantCulture.NumberFormat), CultureInfo.CurrentCulture); } if (errorCode != 0) { string @string = ErrorResources.GetString("1", CultureInfo.CurrentCulture); text = string.Format(CultureInfo.InvariantCulture, "{0} " + @string, new object[2] { text, errorCode }); } return text; } } internal class CabUnpacker : CabWorker { private NativeMethods.FDI.Handle fdiHandle; private NativeMethods.FDI.PFNALLOC fdiAllocMemHandler; private NativeMethods.FDI.PFNFREE fdiFreeMemHandler; private NativeMethods.FDI.PFNOPEN fdiOpenStreamHandler; private NativeMethods.FDI.PFNREAD fdiReadStreamHandler; private NativeMethods.FDI.PFNWRITE fdiWriteStreamHandler; private NativeMethods.FDI.PFNCLOSE fdiCloseStreamHandler; private NativeMethods.FDI.PFNSEEK fdiSeekStreamHandler; private IUnpackStreamContext context; private List<ArchiveFileInfo> fileList; private int folderId; private Predicate<string> filter; [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] public CabUnpacker(CabEngine cabEngine) : base(cabEngine) { fdiAllocMemHandler = base.CabAllocMem; fdiFreeMemHandler = base.CabFreeMem; fdiOpenStreamHandler = base.CabOpenStream; fdiReadStreamHandler = base.CabReadStream; fdiWriteStreamHandler = base.CabWriteStream; fdiCloseStreamHandler = base.CabCloseStream; fdiSeekStreamHandler = base.CabSeekStream; fdiHandle = NativeMethods.FDI.Create(fdiAllocMemHandler, fdiFreeMemHandler, fdiOpenStreamHandler, fdiReadStreamHandler, fdiWriteStreamHandler, fdiCloseStreamHandler, fdiSeekStreamHandler, 1, base.ErfHandle.AddrOfPinnedObject()); if (base.Erf.Error) { int oper = base.Erf.Oper; int type = base.Erf.Type; base.ErfHandle.Free(); throw new CabException(oper, type, CabException.GetErrorMessage(oper, type, extracting: true)); } } [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] public bool IsArchive(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } lock (this) { short id; int cabFolderCount; int fileCount; return IsCabinet(stream, out id, out cabFolderCount, out fileCount); } } [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] public IList<ArchiveFileInfo> GetFileInfo(IUnpackStreamContext streamContext, Predicate<string> fileFilter) { if (streamContext == null) { throw new ArgumentNullException("streamContext"); } lock (this) { context = streamContext; filter = fileFilter; base.NextCabinetName = string.Empty; fileList = new List<ArchiveFileInfo>(); bool flag = base.SuppressProgressEvents; base.SuppressProgressEvents = true; try { short num = 0; while (base.NextCabinetName != null) { base.Erf.Clear(); base.CabNumbers[base.NextCabinetName] = num; NativeMethods.FDI.Copy(fdiHandle, base.NextCabinetName, string.Empty, 0, CabListNotify, IntPtr.Zero, IntPtr.Zero); CheckError(extracting: true); num = checked((short)(num + 1)); } List<ArchiveFileInfo> list = fileList; fileList = null; return list.AsReadOnly(); } finally { base.SuppressProgressEvents = flag; if (base.CabStream != null) { context.CloseArchiveReadStream((int)currentArchiveNumber, currentArchiveName, base.CabStream); base.CabStream = null; } context = null; } } } [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] public void Unpack(IUnpackStreamContext streamContext, Predicate<string> fileFilter) { checked { lock (this) { IList<ArchiveFileInfo> fileInfo = GetFileInfo(streamContext, fileFilter); ResetProgressData(); if (fileInfo != null) { totalFiles = fileInfo.Count; for (int i = 0; i < fileInfo.Count; i++) { totalFileBytes += fileInfo[i].Length; if (fileInfo[i].ArchiveNumber >= totalArchives) { int num = fileInfo[i].ArchiveNumber + 1; totalArchives = (short)num; } } } context = streamContext; fileList = null; base.NextCabinetName = string.Empty; folderId = -1; currentFileNumber = -1; try { short num2 = 0; while (base.NextCabinetName != null) { base.Erf.Clear(); base.CabNumbers[base.NextCabinetName] = num2; NativeMethods.FDI.Copy(fdiHandle, base.NextCabinetName, string.Empty, 0, CabExtractNotify, IntPtr.Zero, IntPtr.Zero); CheckError(extracting: true); num2++; } } finally { if (base.CabStream != null) { context.CloseArchiveReadStream(unchecked((int)currentArchiveNumber), currentArchiveName, base.CabStream); base.CabStream = null; } if (base.FileStream != null) { context.CloseFileWriteStream(currentFileName, base.FileStream, FileAttributes.Normal, DateTime.Now); base.FileStream = null; } context = null; } } } } internal override int CabOpenStreamEx(string path, int openFlags, int shareMode, out int err, IntPtr pv) { if (base.CabNumbers.ContainsKey(path)) { Stream stream = base.CabStream; if (stream == null) { short num = base.CabNumbers[path]; stream = context.OpenArchiveReadStream((int)num, path, (CompressionEngine)(object)base.CabEngine); if (stream == null) { throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, "Cabinet {0} not provided.", new object[1] { num })); } currentArchiveName = path; currentArchiveNumber = num; checked { if (totalArchives <= currentArchiveNumber) { int num2 = currentArchiveNumber + 1; totalArchives = (short)num2; } currentArchiveTotalBytes = stream.Length; currentArchiveBytesProcessed = 0L; if (folderId != -3) { OnProgress((ArchiveProgressType)3); } base.CabStream = stream; } } path = "%%CAB%%"; } return base.CabOpenStreamEx(path, openFlags, shareMode, out err, pv); } internal override int CabReadStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) { int result = base.CabReadStreamEx(streamHandle, memory, cb, out err, pv); checked { if (err == 0 && base.CabStream != null && fileList == null && DuplicateStream.OriginalStream(base.StreamHandles[streamHandle]) == DuplicateStream.OriginalStream(base.CabStream)) { currentArchiveBytesProcessed += cb; if (currentArchiveBytesProcessed > currentArchiveTotalBytes) { currentArchiveBytesProcessed = currentArchiveTotalBytes; } } return result; } } internal override int CabWriteStreamEx(int streamHandle, IntPtr memory, int cb, out int err, IntPtr pv) { int num = base.CabWriteStreamEx(streamHandle, memory, cb, out err, pv); checked { if (num > 0 && err == 0) { currentFileBytesProcessed += cb; fileBytesProcessed += cb; OnProgress((ArchiveProgressType)1); } return num; } } internal override int CabCloseStreamEx(int streamHandle, out int err, IntPtr pv) { Stream stream = DuplicateStream.OriginalStream(base.StreamHandles[streamHandle]); if (stream == DuplicateStream.OriginalStream(base.CabStream)) { if (folderId != -3) { OnProgress((ArchiveProgressType)5); } context.CloseArchiveReadStream((int)currentArchiveNumber, currentArchiveName, stream); currentArchiveName = base.NextCabinetName; currentArchiveBytesProcessed = (currentArchiveTotalBytes = 0L); base.CabStream = null; } return base.CabCloseStreamEx(streamHandle, out err, pv); } protected override void Dispose(bool disposing) { try { if (disposing && fdiHandle != null) { fdiHandle.Dispose(); fdiHandle = null; } } finally { base.Dispose(disposing); } } private static string GetFileName(NativeMethods.FDI.NOTIFICATION notification) { Encoding encoding = ((((uint)notification.attribs & 0x80u) != 0) ? Encoding.UTF8 : Encoding.Default); int i; for (i = 0; Marshal.ReadByte(notification.psz1, i) != 0; i = checked(i + 1)) { } byte[] array = new byte[i]; Marshal.Copy(notification.psz1, array, 0, i); string text = encoding.GetString(array); if (Path.IsPathRooted(text)) { text = text.Replace(Path.VolumeSeparatorChar.ToString() ?? "", ""); } return text; } private bool IsCabinet(Stream cabStream, out short id, out int cabFolderCount, out int fileCount) { int num = base.StreamHandles.AllocHandle(cabStream); try { base.Erf.Clear(); NativeMethods.FDI.CABINFO pfdici; bool result = NativeMethods.FDI.IsCabinet(fdiHandle, num, out pfdici) != 0; if (base.Erf.Error) { if (base.Erf.Oper != 3) { throw new CabException(base.Erf.Oper, base.Erf.Type, CabException.GetErrorMessage(base.Erf.Oper, base.Erf.Type, extracting: true)); } result = false; } id = pfdici.setID; cabFolderCount = pfdici.cFolders; fileCount = pfdici.cFiles; return result; } finally { base.StreamHandles.FreeHandle(num); } } private int CabListNotify(NativeMethods.FDI.NOTIFICATIONTYPE notificationType, NativeMethods.FDI.NOTIFICATION notification) { checked { switch (notificationType) { case NativeMethods.FDI.NOTIFICATIONTYPE.CABINET_INFO: { string text = Marshal.PtrToStringAnsi(notification.psz1); base.NextCabinetName = ((text.Length != 0) ? text : null); return 0; } case NativeMethods.FDI.NOTIFICATIONTYPE.PARTIAL_FILE: return 0; case NativeMethods.FDI.NOTIFICATIONTYPE.COPY_FILE: { string fileName = GetFileName(notification); if ((filter == null || filter(fileName)) && fileList != null) { FileAttributes fileAttributes = unchecked((FileAttributes)(notification.attribs & 0x27)); if (fileAttributes == (FileAttributes)0) { fileAttributes = FileAttributes.Normal; } DateTime lastWriteTime = default(DateTime); CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, ref lastWriteTime); long length = notification.cb; CabFileInfo item = new CabFileInfo(fileName, notification.iFolder, notification.iCabinet, fileAttributes, lastWriteTime, length); fileList.Add((ArchiveFileInfo)(object)item); currentFileNumber = fileList.Count - 1; fileBytesProcessed += notification.cb; } totalFiles++; totalFileBytes += notification.cb; return 0; } default: return 0; } } } private int CabExtractNotify(NativeMethods.FDI.NOTIFICATIONTYPE notificationType, NativeMethods.FDI.NOTIFICATION notification) { switch (notificationType) { case NativeMethods.FDI.NOTIFICATIONTYPE.CABINET_INFO: if (base.NextCabinetName != null && base.NextCabinetName.StartsWith("?", StringComparison.Ordinal)) { base.NextCabinetName = base.NextCabinetName.Substring(1); } else { string text = Marshal.PtrToStringAnsi(notification.psz1); base.NextCabinetName = ((text.Length != 0) ? text : null); } return 0; case NativeMethods.FDI.NOTIFICATIONTYPE.NEXT_CABINET: { string key = Marshal.PtrToStringAnsi(notification.psz1); base.CabNumbers[key] = notification.iCabinet; base.NextCabinetName = "?" + base.NextCabinetName; return 0; } case NativeMethods.FDI.NOTIFICATIONTYPE.COPY_FILE: return CabExtractCopyFile(notification); case NativeMethods.FDI.NOTIFICATIONTYPE.CLOSE_FILE_INFO: return CabExtractCloseFile(notification); default: return 0; } } private int CabExtractCopyFile(NativeMethods.FDI.NOTIFICATION notification) { checked { if (notification.iFolder != folderId) { if (notification.iFolder != -3 && folderId != -1) { currentFolderNumber++; } folderId = notification.iFolder; } string fileName = GetFileName(notification); if (filter == null || filter(fileName)) { currentFileNumber++; currentFileName = fileName; currentFileBytesProcessed = 0L; currentFileTotalBytes = notification.cb; OnProgress((ArchiveProgressType)0); DateTime dateTime = default(DateTime); CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, ref dateTime); Stream stream = context.OpenFileWriteStream(fileName, unchecked((long)notification.cb), dateTime); if (stream != null) { base.FileStream = stream; return base.StreamHandles.AllocHandle(stream); } fileBytesProcessed += notification.cb; OnProgress((ArchiveProgressType)2); currentFileName = null; } return 0; } } private int CabExtractCloseFile(NativeMethods.FDI.NOTIFICATION notification) { Stream stream = base.StreamHandles[notification.hf]; base.StreamHandles.FreeHandle(notification.hf); string fileName = GetFileName(notification); FileAttributes fileAttributes = (FileAttributes)(notification.attribs & 0x27); if (fileAttributes == (FileAttributes)0) { fileAttributes = FileAttributes.Normal; } DateTime dateTime = default(DateTime); CompressionEngine.DosDateAndTimeToDateTime(notification.date, notification.time, ref dateTime); stream.Flush(); context.CloseFileWriteStream(fileName, stream, fileAttributes, dateTime); base.FileStream = null; checked { long num = currentFileTotalBytes - currentFileBytesProcessed; currentFileBytesProcessed += num; fileBytesProcessed += num; OnProgress((ArchiveProgressType)2); currentFileName = null; return 1; } } } [Serializable] public class CabFileInfo : ArchiveFileInfo { private int cabFolder; public CabInfo Cabinet => (CabInfo)(object)((ArchiveFileInfo)this).Archive; public string CabinetName => ((ArchiveFileInfo)this).ArchiveName; public int CabinetFolderNumber { get { if (cabFolder < 0) { ((ArchiveFileInfo)this).Refresh(); } return cabFolder; } } public CabFileInfo(CabInfo cabinetInfo, string filePath) : base((ArchiveInfo)(object)cabinetInfo, filePath) { if (cabinetInfo == null) { throw new ArgumentNullException("cabinetInfo"); } cabFolder = -1; } internal CabFileInfo(string filePath, int cabFolder, int cabNumber, FileAttributes attributes, DateTime lastWriteTime, long length) : base(filePath, cabNumber, attributes, lastWriteTime, length) { this.cabFolder = cabFolder; } protected CabFileInfo(SerializationInfo info, StreamingContext context) : base(info, context) { cabFolder = info.GetInt32("cabFolder"); } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { ((ArchiveFileInfo)this).GetObjectData(info, context); info.AddValue("cabFolder", cabFolder); } protected override void Refresh(ArchiveFileInfo newFileInfo) { ((ArchiveFileInfo)this).Refresh(newFileInfo); cabFolder = ((CabFileInfo)(object)newFileInfo).cabFolder; } } [Serializable] public class CabInfo : ArchiveInfo { public CabInfo(string path) : base(path) { } protected CabInfo(SerializationInfo info, StreamingContext context) : base(info, context) { } protected override CompressionEngine CreateCompressionEngine() { return (CompressionEngine)(object)new CabEngine(); } public IList<CabFileInfo> GetFiles() { IList<ArchiveFileInfo> files = ((ArchiveInfo)this).GetFiles(); List<CabFileInfo> list = new List<CabFileInfo>(files.Count); foreach (CabFileInfo item in files) { list.Add(item); } return list.AsReadOnly(); } public IList<CabFileInfo> GetFiles(string searchPattern) { IList<ArchiveFileInfo> files = ((ArchiveInfo)this).GetFiles(searchPattern); List<CabFileInfo> list = new List<CabFileInfo>(files.Count); foreach (CabFileInfo item in files) { list.Add(item); } return list.AsReadOnly(); } } internal sealed class HandleManager<T> where T : class { private List<T> handles; public T this[int handle] { get { if (handle > 0 && handle <= handles.Count) { return handles[checked(handle - 1)]; } return null; } } public HandleManager() { handles = new List<T>(); } public int AllocHandle(T obj) { handles.Add(obj); return handles.Count; } public void FreeHandle(int handle) { if (handle > 0 && handle <= handles.Count) { handles[checked(handle - 1)] = null; } } } internal static class NativeMethods { internal static class FCI { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr PFNALLOC(int cb); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void PFNFREE(IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNOPEN(string path, int oflag, int pmode, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNREAD(int fileHandle, IntPtr memory, int cb, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNWRITE(int fileHandle, IntPtr memory, int cb, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNCLOSE(int fileHandle, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSEEK(int fileHandle, int dist, int seekType, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNDELETE(string path, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETNEXTCABINET(IntPtr pccab, uint cbPrevCab, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNFILEPLACED(IntPtr pccab, string path, long fileSize, int continuation, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETOPENINFO(string path, out short date, out short time, out short pattribs, out int err, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSTATUS(STATUS typeStatus, uint cb1, uint cb2, IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNGETTEMPFILE(IntPtr tempNamePtr, int tempNameSize, IntPtr pv); internal enum ERROR { NONE, OPEN_SRC, READ_SRC, ALLOC_FAIL, TEMP_FILE, BAD_COMPR_TYPE, CAB_FILE, USER_ABORT, MCI_FAIL } internal enum TCOMP : ushort { MASK_TYPE = 15, TYPE_NONE = 0, TYPE_MSZIP = 1, TYPE_QUANTUM = 2, TYPE_LZX = 3, BAD = 15, MASK_LZX_WINDOW = 7936, LZX_WINDOW_LO = 3840, LZX_WINDOW_HI = 5376, SHIFT_LZX_WINDOW = 8, MASK_QUANTUM_LEVEL = 240, QUANTUM_LEVEL_LO = 16, QUANTUM_LEVEL_HI = 112, SHIFT_QUANTUM_LEVEL = 4, MASK_QUANTUM_MEM = 7936, QUANTUM_MEM_LO = 2560, QUANTUM_MEM_HI = 5376, SHIFT_QUANTUM_MEM = 8, MASK_RESERVED = 57344 } internal enum STATUS : uint { FILE, FOLDER, CABINET } [StructLayout(LayoutKind.Sequential)] internal class CCAB { internal int cb = int.MaxValue; internal int cbFolderThresh = 2147450880; internal int cbReserveCFHeader; internal int cbReserveCFFolder; internal int cbReserveCFData; internal int iCab; internal int iDisk; internal int fFailOnIncompressible; internal short setID; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] internal string szDisk = string.Empty; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] internal string szCab = string.Empty; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] internal string szCabPath = string.Empty; } internal class Handle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; internal Handle() : base(IntPtr.Zero, ownsHandle: true) { } [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)] protected override bool ReleaseHandle() { return Destroy(handle); } } internal const int MIN_DISK = 32768; internal const int MAX_DISK = int.MaxValue; internal const int MAX_FOLDER = 2147450880; internal const int MAX_FILENAME = 256; internal const int MAX_CABINET_NAME = 256; internal const int MAX_CAB_PATH = 256; internal const int MAX_DISK_NAME = 256; internal const int CPU_80386 = 1; [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FCICreate", ThrowOnUnmappableChar = true)] internal static extern Handle Create(IntPtr perf, PFNFILEPLACED pfnfcifp, PFNALLOC pfna, PFNFREE pfnf, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, PFNDELETE pfndelete, PFNGETTEMPFILE pfnfcigtf, [MarshalAs(UnmanagedType.LPStruct)] CCAB pccab, IntPtr pv); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FCIAddFile", ThrowOnUnmappableChar = true)] internal static extern int AddFile(Handle hfci, string pszSourceFile, IntPtr pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fExecute, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis, PFNGETOPENINFO pfnfcigoi, TCOMP typeCompress); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FCIFlushCabinet", ThrowOnUnmappableChar = true)] internal static extern int FlushCabinet(Handle hfci, [MarshalAs(UnmanagedType.Bool)] bool fGetNextCab, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FCIFlushFolder", ThrowOnUnmappableChar = true)] internal static extern int FlushFolder(Handle hfci, PFNGETNEXTCABINET pfnfcignc, PFNSTATUS pfnfcis); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FCIDestroy", ThrowOnUnmappableChar = true)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool Destroy(IntPtr hfci); } internal static class FDI { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate IntPtr PFNALLOC(int cb); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void PFNFREE(IntPtr pv); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNOPEN(string path, int oflag, int pmode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNREAD(int hf, IntPtr pv, int cb); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNWRITE(int hf, IntPtr pv, int cb); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNCLOSE(int hf); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNSEEK(int hf, int dist, int seektype); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate int PFNNOTIFY(NOTIFICATIONTYPE fdint, NOTIFICATION fdin); internal enum ERROR { NONE, CABINET_NOT_FOUND, NOT_A_CABINET, UNKNOWN_CABINET_VERSION, CORRUPT_CABINET, ALLOC_FAIL, BAD_COMPR_TYPE, MDI_FAIL, TARGET_FILE, RESERVE_MISMATCH, WRONG_CABINET, USER_ABORT } internal enum NOTIFICATIONTYPE { CABINET_INFO, PARTIAL_FILE, COPY_FILE, CLOSE_FILE_INFO, NEXT_CABINET, ENUMERATE } internal struct CABINFO { internal int cbCabinet; internal short cFolders; internal short cFiles; internal short setID; internal short iCabinet; internal int fReserve; internal int hasprev; internal int hasnext; } [StructLayout(LayoutKind.Sequential)] internal class NOTIFICATION { internal int cb; internal IntPtr psz1; internal IntPtr psz2; internal IntPtr psz3; internal IntPtr pv; internal IntPtr hf_ptr; internal short date; internal short time; internal short attribs; internal short setID; internal short iCabinet; internal short iFolder; internal int fdie; internal int hf => (int)hf_ptr; } internal class Handle : SafeHandle { public override bool IsInvalid => handle == IntPtr.Zero; internal Handle() : base(IntPtr.Zero, ownsHandle: true) { } protected override bool ReleaseHandle() { return Destroy(handle); } } internal const int MAX_DISK = int.MaxValue; internal const int MAX_FILENAME = 256; internal const int MAX_CABINET_NAME = 256; internal const int MAX_CAB_PATH = 256; internal const int MAX_DISK_NAME = 256; internal const int CPU_80386 = 1; [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FDICreate", ThrowOnUnmappableChar = true)] internal static extern Handle Create([MarshalAs(UnmanagedType.FunctionPtr)] PFNALLOC pfnalloc, [MarshalAs(UnmanagedType.FunctionPtr)] PFNFREE pfnfree, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, int cpuType, IntPtr perf); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FDICopy", ThrowOnUnmappableChar = true)] internal static extern int Copy(Handle hfdi, string pszCabinet, string pszCabPath, int flags, PFNNOTIFY pfnfdin, IntPtr pfnfdid, IntPtr pvUser); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FDIDestroy", ThrowOnUnmappableChar = true)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool Destroy(IntPtr hfdi); [DllImport("cabinet.dll", BestFitMapping = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "FDIIsCabinet", ThrowOnUnmappableChar = true)] internal static extern int IsCabinet(Handle hfdi, int hf, out CABINFO pfdici); } [StructLayout(LayoutKind.Sequential)] internal class ERF { private int erfOper; private int erfType; private int fError; internal int Oper { get { return erfOper; } set { erfOper = value; } } internal int Type { get { return erfType; } set { erfType = value; } } internal bool Error { get { return fError != 0; } set { fError = (value ? 1 : 0); } } internal void Clear() { Oper = 0; Type = 0; Error = false; } } } }
BepInEx/patchers/FixPluginTypesSerialization/Microsoft.Deployment.Compression.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyDescription("Abstract base libraries for archive packing and unpacking")] [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] [assembly: AllowPartiallyTrustedCallers] [assembly: AssemblyFileVersion("3.10.1.2213")] [assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCopyright("Copyright (c) Outercurve Foundation. All rights reserved.")] [assembly: AssemblyProduct("Windows Installer XML Toolset")] [assembly: AssemblyConfiguration("")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, Unrestricted = true)] [assembly: AssemblyVersion("3.0.0.0")] namespace Microsoft.Tools.WindowsInstallerXml { internal static class WixDistribution { public static string NewsUrl = "http://wixtoolset.org/news/"; public static string ShortProduct = "WiX Toolset"; public static string SupportUrl = "http://wixtoolset.org/"; public static string TelemetryUrlFormat = "http://wixtoolset.org/telemetry/v{0}/?r={1}"; public static string ReplacePlaceholders(string original, Assembly assembly) { if ((object)assembly != null) { FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(assembly.Location); original = original.Replace("[FileComments]", versionInfo.Comments); original = original.Replace("[FileCopyright]", versionInfo.LegalCopyright); original = original.Replace("[FileProductName]", versionInfo.ProductName); original = original.Replace("[FileVersion]", versionInfo.FileVersion); if (original.Contains("[FileVersionMajorMinor]")) { Version version = new Version(versionInfo.FileVersion); original = original.Replace("[FileVersionMajorMinor]", version.Major + "." + version.Minor); } if (TryGetAttribute<AssemblyCompanyAttribute>(assembly, out var attribute)) { original = original.Replace("[AssemblyCompany]", attribute.Company); } if (TryGetAttribute<AssemblyCopyrightAttribute>(assembly, out var attribute2)) { original = original.Replace("[AssemblyCopyright]", attribute2.Copyright); } if (TryGetAttribute<AssemblyDescriptionAttribute>(assembly, out var attribute3)) { original = original.Replace("[AssemblyDescription]", attribute3.Description); } if (TryGetAttribute<AssemblyProductAttribute>(assembly, out var attribute4)) { original = original.Replace("[AssemblyProduct]", attribute4.Product); } if (TryGetAttribute<AssemblyTitleAttribute>(assembly, out var attribute5)) { original = original.Replace("[AssemblyTitle]", attribute5.Title); } } original = original.Replace("[NewsUrl]", NewsUrl); original = original.Replace("[ShortProduct]", ShortProduct); original = original.Replace("[SupportUrl]", SupportUrl); return original; } private static bool TryGetAttribute<T>(Assembly assembly, out T attribute) where T : Attribute { attribute = null; object[] customAttributes = assembly.GetCustomAttributes(typeof(T), inherit: false); if (customAttributes != null && customAttributes.Length != 0) { attribute = customAttributes[0] as T; } return attribute != null; } } } namespace Microsoft.Deployment.Compression { [Serializable] public class ArchiveException : IOException { public ArchiveException(string message, Exception innerException) : base(message, innerException) { } public ArchiveException(string message) : this(message, null) { } public ArchiveException() : this(null, null) { } protected ArchiveException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [Serializable] public abstract class ArchiveFileInfo : FileSystemInfo { private ArchiveInfo archiveInfo; private string name; private string path; private bool initialized; private bool exists; private int archiveNumber; private FileAttributes attributes; private DateTime lastWriteTime; private long length; public override string Name => name; public string Path => path; public override string FullName { get { string text = System.IO.Path.Combine(Path, Name); if (Archive != null) { text = System.IO.Path.Combine(ArchiveName, text); } return text; } } public ArchiveInfo Archive { get { return archiveInfo; } internal set { archiveInfo = value; OriginalPath = value?.FullName; FullPath = OriginalPath; } } public string ArchiveName { get { if (Archive == null) { return null; } return Archive.FullName; } } public int ArchiveNumber => archiveNumber; public override bool Exists { get { if (!initialized) { Refresh(); } return exists; } } public long Length { get { if (!initialized) { Refresh(); } return length; } } public new FileAttributes Attributes { get { if (!initialized) { Refresh(); } return attributes; } } public new DateTime LastWriteTime { get { if (!initialized) { Refresh(); } return lastWriteTime; } } protected ArchiveFileInfo(ArchiveInfo archiveInfo, string filePath) { if (filePath == null) { throw new ArgumentNullException("filePath"); } Archive = archiveInfo; name = System.IO.Path.GetFileName(filePath); path = System.IO.Path.GetDirectoryName(filePath); attributes = FileAttributes.Normal; lastWriteTime = DateTime.MinValue; } protected ArchiveFileInfo(string filePath, int archiveNumber, FileAttributes attributes, DateTime lastWriteTime, long length) : this(null, filePath) { exists = true; this.archiveNumber = archiveNumber; this.attributes = attributes; this.lastWriteTime = lastWriteTime; this.length = length; initialized = true; } protected ArchiveFileInfo(SerializationInfo info, StreamingContext context) : base(info, context) { archiveInfo = (ArchiveInfo)info.GetValue("archiveInfo", typeof(ArchiveInfo)); name = info.GetString("name"); path = info.GetString("path"); initialized = info.GetBoolean("initialized"); exists = info.GetBoolean("exists"); archiveNumber = info.GetInt32("archiveNumber"); attributes = (FileAttributes)info.GetValue("attributes", typeof(FileAttributes)); lastWriteTime = info.GetDateTime("lastWriteTime"); length = info.GetInt64("length"); } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("archiveInfo", archiveInfo); info.AddValue("name", name); info.AddValue("path", path); info.AddValue("initialized", initialized); info.AddValue("exists", exists); info.AddValue("archiveNumber", archiveNumber); info.AddValue("attributes", attributes); info.AddValue("lastWriteTime", lastWriteTime); info.AddValue("length", length); } public override string ToString() { return FullName; } public override void Delete() { throw new NotSupportedException(); } public new void Refresh() { base.Refresh(); if (Archive != null) { string fileName = System.IO.Path.Combine(Path, Name); ArchiveFileInfo file = Archive.GetFile(fileName); if (file == null) { throw new FileNotFoundException("File not found in archive.", fileName); } Refresh(file); } } public void CopyTo(string destFileName) { CopyTo(destFileName, overwrite: false); } public void CopyTo(string destFileName, bool overwrite) { if (destFileName == null) { throw new ArgumentNullException("destFileName"); } if (!overwrite && File.Exists(destFileName)) { throw new IOException(); } if (Archive == null) { throw new InvalidOperationException(); } Archive.UnpackFile(System.IO.Path.Combine(Path, Name), destFileName); } public Stream OpenRead() { return Archive.OpenRead(System.IO.Path.Combine(Path, Name)); } public StreamReader OpenText() { return Archive.OpenText(System.IO.Path.Combine(Path, Name)); } protected virtual void Refresh(ArchiveFileInfo newFileInfo) { exists = newFileInfo.exists; length = newFileInfo.length; attributes = newFileInfo.attributes; lastWriteTime = newFileInfo.lastWriteTime; } } [Serializable] public abstract class ArchiveInfo : FileSystemInfo { public DirectoryInfo Directory => new DirectoryInfo(Path.GetDirectoryName(FullName)); public string DirectoryName => Path.GetDirectoryName(FullName); public long Length => new FileInfo(FullName).Length; public override string Name => Path.GetFileName(FullName); public override bool Exists => File.Exists(FullName); protected ArchiveInfo(string path) { if (path == null) { throw new ArgumentNullException("path"); } OriginalPath = path; FullPath = Path.GetFullPath(path); } protected ArchiveInfo(SerializationInfo info, StreamingContext context) : base(info, context) { } public override string ToString() { return FullName; } public override void Delete() { File.Delete(FullName); } public void CopyTo(string destFileName) { File.Copy(FullName, destFileName); } public void CopyTo(string destFileName, bool overwrite) { File.Copy(FullName, destFileName, overwrite); } public void MoveTo(string destFileName) { File.Move(FullName, destFileName); FullPath = Path.GetFullPath(destFileName); } public bool IsValid() { using Stream stream = File.OpenRead(FullName); using CompressionEngine compressionEngine = CreateCompressionEngine(); return compressionEngine.FindArchiveOffset(stream) >= 0; } public IList<ArchiveFileInfo> GetFiles() { return InternalGetFiles(null); } public IList<ArchiveFileInfo> GetFiles(string searchPattern) { if (searchPattern == null) { throw new ArgumentNullException("searchPattern"); } string pattern = string.Format(CultureInfo.InvariantCulture, "^{0}$", new object[1] { Regex.Escape(searchPattern).Replace("\\*", ".*").Replace("\\?", ".") }); Regex regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); return InternalGetFiles((string match) => regex.IsMatch(match)); } public void Unpack(string destDirectory) { Unpack(destDirectory, null); } public void Unpack(string destDirectory, EventHandler<ArchiveProgressEventArgs> progressHandler) { using CompressionEngine compressionEngine = CreateCompressionEngine(); compressionEngine.Progress += progressHandler; ArchiveFileStreamContext archiveFileStreamContext = new ArchiveFileStreamContext(FullName, destDirectory, null); archiveFileStreamContext.EnableOffsetOpen = true; compressionEngine.Unpack(archiveFileStreamContext, null); } public void UnpackFile(string fileName, string destFileName) { if (fileName == null) { throw new ArgumentNullException("fileName"); } if (destFileName == null) { throw new ArgumentNullException("destFileName"); } UnpackFiles(new string[1] { fileName }, null, new string[1] { destFileName }); } public void UnpackFiles(IList<string> fileNames, string destDirectory, IList<string> destFileNames) { UnpackFiles(fileNames, destDirectory, destFileNames, null); } public void UnpackFiles(IList<string> fileNames, string destDirectory, IList<string> destFileNames, EventHandler<ArchiveProgressEventArgs> progressHandler) { if (fileNames == null) { throw new ArgumentNullException("fileNames"); } if (destFileNames == null) { if (destDirectory == null) { throw new ArgumentNullException("destFileNames"); } destFileNames = fileNames; } if (destFileNames.Count != fileNames.Count) { throw new ArgumentOutOfRangeException("destFileNames"); } IDictionary<string, string> fileNames2 = CreateStringDictionary(fileNames, destFileNames); UnpackFileSet(fileNames2, destDirectory, progressHandler); } public void UnpackFileSet(IDictionary<string, string> fileNames, string destDirectory) { UnpackFileSet(fileNames, destDirectory, null); } public void UnpackFileSet(IDictionary<string, string> fileNames, string destDirectory, EventHandler<ArchiveProgressEventArgs> progressHandler) { if (fileNames == null) { throw new ArgumentNullException("fileNames"); } using CompressionEngine compressionEngine = CreateCompressionEngine(); compressionEngine.Progress += progressHandler; ArchiveFileStreamContext archiveFileStreamContext = new ArchiveFileStreamContext(FullName, destDirectory, fileNames); archiveFileStreamContext.EnableOffsetOpen = true; compressionEngine.Unpack(archiveFileStreamContext, (string match) => fileNames.ContainsKey(match)); } public Stream OpenRead(string fileName) { Stream stream = File.OpenRead(FullName); CompressionEngine compressionEngine = CreateCompressionEngine(); return new CargoStream(compressionEngine.Unpack(stream, fileName), stream, compressionEngine); } public StreamReader OpenText(string fileName) { return new StreamReader(OpenRead(fileName)); } public void Pack(string sourceDirectory) { Pack(sourceDirectory, includeSubdirectories: false, CompressionLevel.Max, null); } public void Pack(string sourceDirectory, bool includeSubdirectories, CompressionLevel compLevel, EventHandler<ArchiveProgressEventArgs> progressHandler) { IList<string> relativeFilePathsInDirectoryTree = GetRelativeFilePathsInDirectoryTree(sourceDirectory, includeSubdirectories); PackFiles(sourceDirectory, relativeFilePathsInDirectoryTree, relativeFilePathsInDirectoryTree, compLevel, progressHandler); } public void PackFiles(string sourceDirectory, IList<string> sourceFileNames, IList<string> fileNames) { PackFiles(sourceDirectory, sourceFileNames, fileNames, CompressionLevel.Max, null); } public void PackFiles(string sourceDirectory, IList<string> sourceFileNames, IList<string> fileNames, CompressionLevel compLevel, EventHandler<ArchiveProgressEventArgs> progressHandler) { if (sourceFileNames == null) { throw new ArgumentNullException("sourceFileNames"); } if (fileNames == null) { string[] array = new string[sourceFileNames.Count]; for (int i = 0; i < sourceFileNames.Count; i = checked(i + 1)) { array[i] = Path.GetFileName(sourceFileNames[i]); } fileNames = array; } else if (fileNames.Count != sourceFileNames.Count) { throw new ArgumentOutOfRangeException("fileNames"); } using CompressionEngine compressionEngine = CreateCompressionEngine(); compressionEngine.Progress += progressHandler; IDictionary<string, string> files = CreateStringDictionary(fileNames, sourceFileNames); ArchiveFileStreamContext archiveFileStreamContext = new ArchiveFileStreamContext(FullName, sourceDirectory, files); archiveFileStreamContext.EnableOffsetOpen = true; compressionEngine.CompressionLevel = compLevel; compressionEngine.Pack(archiveFileStreamContext, fileNames); } public void PackFileSet(string sourceDirectory, IDictionary<string, string> fileNames) { PackFileSet(sourceDirectory, fileNames, CompressionLevel.Max, null); } public void PackFileSet(string sourceDirectory, IDictionary<string, string> fileNames, CompressionLevel compLevel, EventHandler<ArchiveProgressEventArgs> progressHandler) { if (fileNames == null) { throw new ArgumentNullException("fileNames"); } string[] array = new string[fileNames.Count]; fileNames.Keys.CopyTo(array, 0); using CompressionEngine compressionEngine = CreateCompressionEngine(); compressionEngine.Progress += progressHandler; ArchiveFileStreamContext archiveFileStreamContext = new ArchiveFileStreamContext(FullName, sourceDirectory, fileNames); archiveFileStreamContext.EnableOffsetOpen = true; compressionEngine.CompressionLevel = compLevel; compressionEngine.Pack(archiveFileStreamContext, array); } internal IList<string> GetRelativeFilePathsInDirectoryTree(string dir, bool includeSubdirectories) { IList<string> list = new List<string>(); RecursiveGetRelativeFilePathsInDirectoryTree(dir, string.Empty, includeSubdirectories, list); return list; } internal ArchiveFileInfo GetFile(string path) { IList<ArchiveFileInfo> list = InternalGetFiles((string match) => string.Compare(match, path, ignoreCase: true, CultureInfo.InvariantCulture) == 0); if (list == null || list.Count <= 0) { return null; } return list[0]; } protected abstract CompressionEngine CreateCompressionEngine(); private static IDictionary<string, string> CreateStringDictionary(IList<string> keys, IList<string> values) { IDictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < keys.Count; i = checked(i + 1)) { dictionary.Add(keys[i], values[i]); } return dictionary; } private void RecursiveGetRelativeFilePathsInDirectoryTree(string dir, string relativeDir, bool includeSubdirectories, IList<string> fileList) { string[] files = System.IO.Directory.GetFiles(dir); for (int i = 0; i < files.Length; i++) { string fileName = Path.GetFileName(files[i]); fileList.Add(Path.Combine(relativeDir, fileName)); } if (includeSubdirectories) { files = System.IO.Directory.GetDirectories(dir); for (int i = 0; i < files.Length; i++) { string fileName2 = Path.GetFileName(files[i]); RecursiveGetRelativeFilePathsInDirectoryTree(Path.Combine(dir, fileName2), Path.Combine(relativeDir, fileName2), includeSubdirectories, fileList); } } } private IList<ArchiveFileInfo> InternalGetFiles(Predicate<string> fileFilter) { using CompressionEngine compressionEngine = CreateCompressionEngine(); ArchiveFileStreamContext archiveFileStreamContext = new ArchiveFileStreamContext(FullName, null, null); archiveFileStreamContext.EnableOffsetOpen = true; IList<ArchiveFileInfo> fileInfo = compressionEngine.GetFileInfo(archiveFileStreamContext, fileFilter); for (int i = 0; i < fileInfo.Count; i = checked(i + 1)) { fileInfo[i].Archive = this; } return fileInfo; } } public class ArchiveProgressEventArgs : EventArgs { private ArchiveProgressType progressType; private string currentFileName; private int currentFileNumber; private int totalFiles; private long currentFileBytesProcessed; private long currentFileTotalBytes; private string currentArchiveName; private short currentArchiveNumber; private short totalArchives; private long currentArchiveBytesProcessed; private long currentArchiveTotalBytes; private long fileBytesProcessed; private long totalFileBytes; public ArchiveProgressType ProgressType => progressType; public string CurrentFileName => currentFileName; public int CurrentFileNumber => currentFileNumber; public int TotalFiles => totalFiles; public long CurrentFileBytesProcessed => currentFileBytesProcessed; public long CurrentFileTotalBytes => currentFileTotalBytes; public string CurrentArchiveName => currentArchiveName; public int CurrentArchiveNumber => currentArchiveNumber; public int TotalArchives => totalArchives; public long CurrentArchiveBytesProcessed => currentArchiveBytesProcessed; public long CurrentArchiveTotalBytes => currentArchiveTotalBytes; public long FileBytesProcessed => fileBytesProcessed; public long TotalFileBytes => totalFileBytes; public ArchiveProgressEventArgs(ArchiveProgressType progressType, string currentFileName, int currentFileNumber, int totalFiles, long currentFileBytesProcessed, long currentFileTotalBytes, string currentArchiveName, int currentArchiveNumber, int totalArchives, long currentArchiveBytesProcessed, long currentArchiveTotalBytes, long fileBytesProcessed, long totalFileBytes) { this.progressType = progressType; this.currentFileName = currentFileName; this.currentFileNumber = currentFileNumber; this.totalFiles = totalFiles; this.currentFileBytesProcessed = currentFileBytesProcessed; this.currentFileTotalBytes = currentFileTotalBytes; this.currentArchiveName = currentArchiveName; checked { this.currentArchiveNumber = (short)currentArchiveNumber; this.totalArchives = (short)totalArchives; this.currentArchiveBytesProcessed = currentArchiveBytesProcessed; this.currentArchiveTotalBytes = currentArchiveTotalBytes; this.fileBytesProcessed = fileBytesProcessed; this.totalFileBytes = totalFileBytes; } } } public enum ArchiveProgressType { StartFile, PartialFile, FinishFile, StartArchive, PartialArchive, FinishArchive } public class ArchiveFileStreamContext : IPackStreamContext, IUnpackStreamContext { private IList<string> archiveFiles; private string directory; private IDictionary<string, string> files; private bool extractOnlyNewerFiles; private bool enableOffsetOpen; public IList<string> ArchiveFiles => archiveFiles; public string Directory => directory; public IDictionary<string, string> Files => files; public bool ExtractOnlyNewerFiles { get { return extractOnlyNewerFiles; } set { extractOnlyNewerFiles = value; } } public bool EnableOffsetOpen { get { return enableOffsetOpen; } set { enableOffsetOpen = value; } } public ArchiveFileStreamContext(string archiveFile) : this(archiveFile, null, null) { } public ArchiveFileStreamContext(string archiveFile, string directory, IDictionary<string, string> files) : this(new string[1] { archiveFile }, directory, files) { if (archiveFile == null) { throw new ArgumentNullException("archiveFile"); } } public ArchiveFileStreamContext(IList<string> archiveFiles, string directory, IDictionary<string, string> files) { if (archiveFiles == null || archiveFiles.Count == 0) { throw new ArgumentNullException("archiveFiles"); } this.archiveFiles = archiveFiles; this.directory = directory; this.files = files; } public virtual string GetArchiveName(int archiveNumber) { if (archiveNumber < archiveFiles.Count) { return Path.GetFileName(archiveFiles[archiveNumber]); } return string.Empty; } public virtual Stream OpenArchiveWriteStream(int archiveNumber, string archiveName, bool truncate, CompressionEngine compressionEngine) { if (archiveNumber >= archiveFiles.Count) { return null; } if (string.IsNullOrEmpty(archiveName)) { throw new ArgumentNullException("archiveName"); } Stream stream = File.Open(Path.Combine(Path.GetDirectoryName(archiveFiles[0]), archiveName), truncate ? FileMode.OpenOrCreate : FileMode.Open, FileAccess.ReadWrite); if (enableOffsetOpen) { long num = compressionEngine.FindArchiveOffset(new DuplicateStream(stream)); if (num < 0) { num = stream.Length; } if (num > 0) { stream = new OffsetStream(stream, num); } stream.Seek(0L, SeekOrigin.Begin); } if (truncate) { stream.SetLength(0L); } return stream; } public virtual void CloseArchiveWriteStream(int archiveNumber, string archiveName, Stream stream) { if (stream == null) { return; } stream.Close(); if (!(stream is FileStream fileStream)) { return; } string name = fileStream.Name; if (!string.IsNullOrEmpty(archiveName) && archiveName != Path.GetFileName(name)) { string text = Path.Combine(Path.GetDirectoryName(archiveFiles[0]), archiveName); if (File.Exists(text)) { File.Delete(text); } File.Move(name, text); } } public virtual Stream OpenFileReadStream(string path, out FileAttributes attributes, out DateTime lastWriteTime) { string text = TranslateFilePath(path); if (text == null) { attributes = FileAttributes.Normal; lastWriteTime = DateTime.Now; return null; } attributes = File.GetAttributes(text); lastWriteTime = File.GetLastWriteTime(text); return File.Open(text, FileMode.Open, FileAccess.Read, FileShare.Read); } public virtual void CloseFileReadStream(string path, Stream stream) { stream?.Close(); } public virtual object GetOption(string optionName, object[] parameters) { return null; } public virtual Stream OpenArchiveReadStream(int archiveNumber, string archiveName, CompressionEngine compressionEngine) { if (archiveNumber >= archiveFiles.Count) { return null; } Stream stream = File.Open(archiveFiles[archiveNumber], FileMode.Open, FileAccess.Read, FileShare.Read); if (enableOffsetOpen) { long num = compressionEngine.FindArchiveOffset(new DuplicateStream(stream)); if (num > 0) { stream = new OffsetStream(stream, num); } else { stream.Seek(0L, SeekOrigin.Begin); } } return stream; } public virtual void CloseArchiveReadStream(int archiveNumber, string archiveName, Stream stream) { stream?.Close(); } public virtual Stream OpenFileWriteStream(string path, long fileSize, DateTime lastWriteTime) { string text = TranslateFilePath(path); if (text == null) { return null; } FileInfo fileInfo = new FileInfo(text); if (fileInfo.Exists) { if (extractOnlyNewerFiles && lastWriteTime != DateTime.MinValue && fileInfo.LastWriteTime >= lastWriteTime) { return null; } FileAttributes fileAttributes = FileAttributes.ReadOnly | FileAttributes.Hidden | FileAttributes.System; if ((fileInfo.Attributes & fileAttributes) != 0) { fileInfo.Attributes &= ~fileAttributes; } } if (!fileInfo.Directory.Exists) { fileInfo.Directory.Create(); } return File.Open(text, FileMode.Create, FileAccess.Write, FileShare.None); } public virtual void CloseFileWriteStream(string path, Stream stream, FileAttributes attributes, DateTime lastWriteTime) { stream?.Close(); string text = TranslateFilePath(path); if (text == null) { return; } FileInfo fileInfo = new FileInfo(text); if (lastWriteTime != DateTime.MinValue) { try { fileInfo.LastWriteTime = lastWriteTime; } catch (ArgumentException) { } catch (IOException) { } } try { fileInfo.Attributes = attributes; } catch (IOException) { } } private string TranslateFilePath(string path) { string text = ((files == null) ? path : files[path]); if (text != null && directory != null) { text = Path.Combine(directory, text); } return text; } } public class BasicUnpackStreamContext : IUnpackStreamContext { private Stream archiveStream; private Stream fileStream; public Stream FileStream => fileStream; public BasicUnpackStreamContext(Stream archiveStream) { this.archiveStream = archiveStream; } public Stream OpenArchiveReadStream(int archiveNumber, string archiveName, CompressionEngine compressionEngine) { return new DuplicateStream(archiveStream); } public void CloseArchiveReadStream(int archiveNumber, string archiveName, Stream stream) { } public Stream OpenFileWriteStream(string path, long fileSize, DateTime lastWriteTime) { fileStream = new MemoryStream(new byte[fileSize], 0, checked((int)fileSize), writable: true, publiclyVisible: true); return fileStream; } public void CloseFileWriteStream(string path, Stream stream, FileAttributes attributes, DateTime lastWriteTime) { } } public abstract class CompressionEngine : IDisposable { private CompressionLevel compressionLevel; private bool dontUseTempFiles; public bool UseTempFiles { get { return !dontUseTempFiles; } set { dontUseTempFiles = !value; } } public CompressionLevel CompressionLevel { get { return compressionLevel; } set { compressionLevel = value; } } public event EventHandler<ArchiveProgressEventArgs> Progress; protected CompressionEngine() { compressionLevel = CompressionLevel.Normal; } ~CompressionEngine() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } public void Pack(IPackStreamContext streamContext, IEnumerable<string> files) { if (files == null) { throw new ArgumentNullException("files"); } Pack(streamContext, files, 0L); } public abstract void Pack(IPackStreamContext streamContext, IEnumerable<string> files, long maxArchiveSize); public abstract bool IsArchive(Stream stream); public virtual long FindArchiveOffset(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } long num = 4L; long length = stream.Length; checked { for (long num2 = 0L; num2 <= length - num; num2 += num) { stream.Seek(num2, SeekOrigin.Begin); if (IsArchive(stream)) { return num2; } } return -1L; } } public IList<ArchiveFileInfo> GetFileInfo(Stream stream) { return GetFileInfo(new BasicUnpackStreamContext(stream), null); } public abstract IList<ArchiveFileInfo> GetFileInfo(IUnpackStreamContext streamContext, Predicate<string> fileFilter); public IList<string> GetFiles(Stream stream) { return GetFiles(new BasicUnpackStreamContext(stream), null); } public IList<string> GetFiles(IUnpackStreamContext streamContext, Predicate<string> fileFilter) { if (streamContext == null) { throw new ArgumentNullException("streamContext"); } IList<ArchiveFileInfo> fileInfo = GetFileInfo(streamContext, fileFilter); IList<string> list = new List<string>(fileInfo.Count); for (int i = 0; i < fileInfo.Count; i = checked(i + 1)) { list.Add(fileInfo[i].Name); } return list; } public Stream Unpack(Stream stream, string path) { if (stream == null) { throw new ArgumentNullException("stream"); } if (path == null) { throw new ArgumentNullException("path"); } BasicUnpackStreamContext basicUnpackStreamContext = new BasicUnpackStreamContext(stream); Unpack(basicUnpackStreamContext, (string match) => string.Compare(match, path, ignoreCase: true, CultureInfo.InvariantCulture) == 0); Stream fileStream = basicUnpackStreamContext.FileStream; if (fileStream != null) { fileStream.Position = 0L; } return fileStream; } public abstract void Unpack(IUnpackStreamContext streamContext, Predicate<string> fileFilter); protected void OnProgress(ArchiveProgressEventArgs e) { if (this.Progress != null) { this.Progress(this, e); } } protected virtual void Dispose(bool disposing) { } public static void DosDateAndTimeToDateTime(short dosDate, short dosTime, out DateTime dateTime) { if (dosDate == 0 && dosTime == 0) { dateTime = DateTime.MinValue; return; } SafeNativeMethods.DosDateTimeToFileTime(dosDate, dosTime, out var fileTime); dateTime = DateTime.FromFileTimeUtc(fileTime); dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Local); } public static void DateTimeToDosDateAndTime(DateTime dateTime, out short dosDate, out short dosTime) { dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc); long fileTime = dateTime.ToFileTimeUtc(); SafeNativeMethods.FileTimeToDosDateTime(ref fileTime, out dosDate, out dosTime); } } public enum CompressionLevel { None = 0, Min = 1, Normal = 6, Max = 10 } public class CargoStream : Stream { private Stream source; private List<IDisposable> cargo; public Stream Source => source; public IList<IDisposable> Cargo => cargo; public override bool CanRead => source.CanRead; public override bool CanWrite => source.CanWrite; public override bool CanSeek => source.CanSeek; public override long Length => source.Length; public override long Position { get { return source.Position; } set { source.Position = value; } } public CargoStream(Stream source, params IDisposable[] cargo) { if (source == null) { throw new ArgumentNullException("source"); } this.source = source; this.cargo = new List<IDisposable>(cargo); } public override void Flush() { source.Flush(); } public override void SetLength(long value) { source.SetLength(value); } public override void Close() { source.Close(); foreach (IDisposable item in cargo) { item.Dispose(); } } public override int Read(byte[] buffer, int offset, int count) { return source.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { source.Write(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return source.Seek(offset, origin); } } public class DuplicateStream : Stream { private Stream source; private long position; public Stream Source => source; public override bool CanRead => source.CanRead; public override bool CanWrite => source.CanWrite; public override bool CanSeek => source.CanSeek; public override long Length => source.Length; public override long Position { get { return position; } set { position = value; } } public DuplicateStream(Stream source) { if (source == null) { throw new ArgumentNullException("source"); } this.source = OriginalStream(source); } public static Stream OriginalStream(Stream stream) { if (!(stream is DuplicateStream duplicateStream)) { return stream; } return duplicateStream.Source; } public override void Flush() { source.Flush(); } public override void SetLength(long value) { source.SetLength(value); } public override void Close() { source.Close(); } public override int Read(byte[] buffer, int offset, int count) { long num = source.Position; source.Position = position; int result = source.Read(buffer, offset, count); position = source.Position; source.Position = num; return result; } public override void Write(byte[] buffer, int offset, int count) { long num = source.Position; source.Position = position; source.Write(buffer, offset, count); position = source.Position; source.Position = num; } public override long Seek(long offset, SeekOrigin origin) { long num = 0L; switch (origin) { case SeekOrigin.Current: num = position; break; case SeekOrigin.End: num = Length; break; } position = checked(num + offset); return position; } } public interface IPackStreamContext { string GetArchiveName(int archiveNumber); Stream OpenArchiveWriteStream(int archiveNumber, string archiveName, bool truncate, CompressionEngine compressionEngine); void CloseArchiveWriteStream(int archiveNumber, string archiveName, Stream stream); Stream OpenFileReadStream(string path, out FileAttributes attributes, out DateTime lastWriteTime); void CloseFileReadStream(string path, Stream stream); object GetOption(string optionName, object[] parameters); } public interface IUnpackStreamContext { Stream OpenArchiveReadStream(int archiveNumber, string archiveName, CompressionEngine compressionEngine); void CloseArchiveReadStream(int archiveNumber, string archiveName, Stream stream); Stream OpenFileWriteStream(string path, long fileSize, DateTime lastWriteTime); void CloseFileWriteStream(string path, Stream stream, FileAttributes attributes, DateTime lastWriteTime); } public class OffsetStream : Stream { private Stream source; private long sourceOffset; public Stream Source => source; public long Offset => sourceOffset; public override bool CanRead => source.CanRead; public override bool CanWrite => source.CanWrite; public override bool CanSeek => source.CanSeek; public override long Length => checked(source.Length - sourceOffset); public override long Position { get { return checked(source.Position - sourceOffset); } set { source.Position = checked(value + sourceOffset); } } public OffsetStream(Stream source, long offset) { if (source == null) { throw new ArgumentNullException("source"); } this.source = source; sourceOffset = offset; this.source.Seek(sourceOffset, SeekOrigin.Current); } public override int Read(byte[] buffer, int offset, int count) { return source.Read(buffer, offset, count); } public override void Write(byte[] buffer, int offset, int count) { source.Write(buffer, offset, count); } public override int ReadByte() { return source.ReadByte(); } public override void WriteByte(byte value) { source.WriteByte(value); } public override void Flush() { source.Flush(); } public override long Seek(long offset, SeekOrigin origin) { return checked(source.Seek(offset + ((origin == SeekOrigin.Begin) ? sourceOffset : 0), origin) - sourceOffset); } public override void SetLength(long value) { source.SetLength(checked(value + sourceOffset)); } public override void Close() { source.Close(); } } [SuppressUnmanagedCodeSecurity] internal static class SafeNativeMethods { [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool DosDateTimeToFileTime(short wFatDate, short wFatTime, out long fileTime); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool FileTimeToDosDateTime(ref long fileTime, out short wFatDate, out short wFatTime); } }