Decompiled source of FixPluginTypesSerialization v1.1.1
BepInEx/patchers/FixPluginTypesSerialization/FixPluginTypesSerialization.dll
Decompiled 10 months 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 10 months 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 10 months 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); } }