|
| 1 | +using Microsoft.Xna.Framework; |
| 2 | +using Microsoft.Xna.Framework.Content; |
| 3 | +using Microsoft.Xna.Framework.Graphics; |
| 4 | +using Mono.Cecil; |
| 5 | +using MonoMod; |
| 6 | +using MonoMod.Detour; |
| 7 | +using MonoMod.InlineRT; |
| 8 | +using System; |
| 9 | +using System.Collections.Generic; |
| 10 | +using System.Diagnostics; |
| 11 | +using System.IO; |
| 12 | +using System.IO.Compression; |
| 13 | +using System.Linq; |
| 14 | +using System.Reflection; |
| 15 | +using System.Runtime.CompilerServices; |
| 16 | +using System.Text; |
| 17 | +using System.Threading; |
| 18 | +using System.Threading.Tasks; |
| 19 | +using XnaToFna.ContentTransformers; |
| 20 | + |
| 21 | +namespace XnaToFna { |
| 22 | + public static partial class ContentHelper { |
| 23 | + // Yo dawg, I heard you like patching... |
| 24 | + public static partial class FNAHooks { |
| 25 | + public static class Offline { |
| 26 | + |
| 27 | + private static bool IsHooked = false; |
| 28 | + public static bool Enabled = true; |
| 29 | + |
| 30 | + public static void Hook() { |
| 31 | + if (!IsHooked) { |
| 32 | + Dictionary<string, Func<ContentTypeReader>> typeCreators = (Dictionary<string, Func<ContentTypeReader>>) |
| 33 | + typeof(ContentTypeReaderManager).GetField("typeCreators", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); |
| 34 | + typeCreators["Microsoft.Xna.Framework.Content.EffectReader, Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553"] |
| 35 | + = () => new EffectTransformer(); |
| 36 | + typeCreators["Microsoft.Xna.Framework.Content.SoundEffectReader"] // Games somehow don't use the full name for this one... |
| 37 | + = () => new SoundEffectTransformer(); |
| 38 | + } |
| 39 | + |
| 40 | + Hook(IsHooked, typeof(ContentManager), "GetContentReaderFromXnb", ref orig_GetContentReaderFromXnb); |
| 41 | + |
| 42 | + // Hooking constructors? Seems to work just fine on my machine(tm). |
| 43 | + Hook(IsHooked, typeof(ContentReader), ".ctor[0]", ref orig_ctor_ContentReader); |
| 44 | + |
| 45 | + object gl = typeof(GraphicsDevice).GetField("GLDevice", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(Game.GraphicsDevice); |
| 46 | + Type t_gl = gl.GetType(); |
| 47 | + orig_SupportsDxt1 = (bool) t_gl.GetProperty("SupportsDxt1").GetValue(gl); |
| 48 | + Hook(IsHooked, t_gl, "get_SupportsDxt1", ref orig_get_SupportsDxt1); |
| 49 | + orig_SupportsS3tc = (bool) t_gl.GetProperty("SupportsS3tc").GetValue(gl); |
| 50 | + Hook(IsHooked, t_gl, "get_SupportsS3tc", ref orig_get_SupportsS3tc); |
| 51 | + |
| 52 | + IsHooked = true; |
| 53 | + } |
| 54 | + |
| 55 | + internal static MethodBase Find(Type type, string name) { |
| 56 | + MethodBase found = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); |
| 57 | + if (found != null) |
| 58 | + return found; |
| 59 | + |
| 60 | + if (name.StartsWith("get_") || name.StartsWith("set_")) { |
| 61 | + PropertyInfo prop = type.GetProperty(name.Substring(4), BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); |
| 62 | + if (name[0] == 'g') |
| 63 | + found = prop.GetGetMethod(true); |
| 64 | + else |
| 65 | + found = prop.GetSetMethod(true); |
| 66 | + } |
| 67 | + |
| 68 | + return found; |
| 69 | + } |
| 70 | + internal static void Hook<T>(bool isHooked, Type type, string name, ref T trampoline) { |
| 71 | + MethodBase from; |
| 72 | + MethodBase to; |
| 73 | + if (name.StartsWith(".ctor[")) { |
| 74 | + ConstructorInfo[] ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); |
| 75 | + int index; |
| 76 | + if (int.TryParse(name.Substring(6, name.Length - 6 - 1), out index)) |
| 77 | + from = ctors[index]; |
| 78 | + else |
| 79 | + from = ctors[0]; |
| 80 | + to = Find(typeof(T).DeclaringType, "ctor_" + type.Name); |
| 81 | + } else { |
| 82 | + from = Find(type, name); |
| 83 | + to = Find(typeof(T).DeclaringType, name); |
| 84 | + } |
| 85 | + |
| 86 | + // Keeps the detour intact. The JIT likes to revert our changes... |
| 87 | + if (isHooked) { |
| 88 | + RuntimeDetour.Refresh(from); |
| 89 | + return; |
| 90 | + } |
| 91 | + |
| 92 | + T tmp = |
| 93 | + from |
| 94 | + .Detour<T>( |
| 95 | + to |
| 96 | + ); |
| 97 | + if (trampoline == null) |
| 98 | + trampoline = tmp; |
| 99 | + } |
| 100 | + |
| 101 | + private delegate ContentReader d_GetContentReaderFromXnb(ContentManager self, string originalAssetName, ref Stream stream, BinaryReader xnbReader, char platform, Action<IDisposable> recordDisposableObject); |
| 102 | + private static d_GetContentReaderFromXnb orig_GetContentReaderFromXnb; |
| 103 | + private static ContentReader GetContentReaderFromXnb(ContentManager self, string originalAssetName, ref Stream stream, BinaryReader xnbReader, char platform, Action<IDisposable> recordDisposableObject) { |
| 104 | + if (!Enabled) |
| 105 | + return orig_GetContentReaderFromXnb(self, originalAssetName, ref stream, xnbReader, platform, recordDisposableObject); |
| 106 | + |
| 107 | + // output will be disposed with the ContentReader. |
| 108 | + Stream output = File.OpenWrite(originalAssetName + ".tmp"); |
| 109 | + |
| 110 | + // We need to read the first 6 bytes ourselves (4 header bytes + version + flags). |
| 111 | + long xnbPos = xnbReader.BaseStream.Position; |
| 112 | + xnbReader.BaseStream.Seek(0, SeekOrigin.Begin); |
| 113 | + |
| 114 | + using (BinaryWriter writer = new BinaryWriter(output, Encoding.ASCII, true)) { |
| 115 | + writer.Write(xnbReader.ReadBytes(5)); |
| 116 | + |
| 117 | + byte flags = xnbReader.ReadByte(); |
| 118 | + flags = (byte) (flags & ~0x80); // Remove the compression flag. |
| 119 | + writer.Write(flags); |
| 120 | + |
| 121 | + writer.Write(0); // Write size = 0 for now, will be updated later. |
| 122 | + } |
| 123 | + |
| 124 | + xnbReader.BaseStream.Seek(xnbPos, SeekOrigin.Begin); |
| 125 | + |
| 126 | + ContentReader reader = orig_GetContentReaderFromXnb(self, originalAssetName, ref stream, xnbReader, platform, recordDisposableObject); |
| 127 | + ((CopyingStream) reader.BaseStream).Output = output; |
| 128 | + return reader; |
| 129 | + } |
| 130 | + |
| 131 | + private delegate void d_ctor_ContentReader(ContentReader self, ContentManager manager, Stream stream, GraphicsDevice graphicsDevice, string assetName, int version, char platform, Action<IDisposable> recordDisposableObject); |
| 132 | + private static d_ctor_ContentReader orig_ctor_ContentReader; |
| 133 | + private static void ctor_ContentReader(ContentReader self, ContentManager manager, Stream stream, GraphicsDevice graphicsDevice, string assetName, int version, char platform, Action<IDisposable> recordDisposableObject) { |
| 134 | + if (!Enabled) { |
| 135 | + orig_ctor_ContentReader(self, manager, stream, graphicsDevice, assetName, version, platform, recordDisposableObject); |
| 136 | + return; |
| 137 | + } |
| 138 | + |
| 139 | + // What... what is this? Where am I? *Who* am I?! |
| 140 | + stream = new CopyingStream(stream, null); |
| 141 | + orig_ctor_ContentReader(self, manager, stream, graphicsDevice, assetName, version, platform, recordDisposableObject); |
| 142 | + } |
| 143 | + |
| 144 | + public static bool? SupportsDxt1; |
| 145 | + private static bool orig_SupportsDxt1; |
| 146 | + private delegate bool d_get_SupportsDxt1(object self); |
| 147 | + private static d_get_SupportsDxt1 orig_get_SupportsDxt1; |
| 148 | + private static bool get_SupportsDxt1(object self) { |
| 149 | + return SupportsDxt1 ?? orig_SupportsDxt1; |
| 150 | + } |
| 151 | + |
| 152 | + public static bool? SupportsS3tc; |
| 153 | + private static bool orig_SupportsS3tc; |
| 154 | + private delegate bool d_get_SupportsS3tc(object self); |
| 155 | + private static d_get_SupportsS3tc orig_get_SupportsS3tc; |
| 156 | + private static bool get_SupportsS3tc(object self) { |
| 157 | + return SupportsS3tc ?? orig_SupportsS3tc; |
| 158 | + } |
| 159 | + |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | +} |
0 commit comments