Skip to content

Commit 4e18434

Browse files
committed
Move FNA content hooks to ContentHelper.FNAHooks.Offline / Online
1 parent 6433739 commit 4e18434

File tree

6 files changed

+310
-181
lines changed

6 files changed

+310
-181
lines changed

XnaToFna.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
<Compile Include="src\Content\ContentTransformers\GZIPContentReader.cs" />
5252
<Compile Include="src\Content\ContentTransformers\SoundEffectTransformer.cs" />
5353
<Compile Include="src\Content\CopyingStream.cs" />
54+
<Compile Include="src\Content\FNAHooks.cs" />
55+
<Compile Include="src\Content\FNAHooks.Online.cs" />
56+
<Compile Include="src\Content\FNAHooks.Offline.cs" />
5457
<Compile Include="src\Content\XNBContent.cs" />
5558
<Compile Include="src\Content\WindowsMedia.cs" />
5659
<Compile Include="src\Content\SoundBank.cs" />

src/Content/ContentTransformers/GZIPContentReader.cs

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,11 @@ namespace XnaToFna.ContentTransformers {
2222
public class GZipContentReader<ContentType> : ContentTypeReader<ContentType> {
2323

2424
protected override ContentType Read(ContentReader input, ContentType existing) {
25-
GZipContentReaderFNAHooks.Hook(); // We may need to refresh the hooks - who knows what the JIT's doing.
26-
GZipContentReaderFNAHooks.ForcedStream = new GZipStream(input.BaseStream, CompressionMode.Decompress, true);
25+
// We may need to refresh the hooks - who knows what the JIT's doing.
26+
ContentHelper.FNAHooks.Online.Hook();
27+
ContentHelper.FNAHooks.Online.ForcedStream = new GZipStream(input.BaseStream, CompressionMode.Decompress, true);
2728
return input.ContentManager.Load<ContentType>("///XNATOFNA/gzip/" + input.AssetName);
2829
}
2930

3031
}
31-
32-
// Yo dawg, I heard you like patching...
33-
public static class GZipContentReaderFNAHooks {
34-
35-
public static bool Enabled = true;
36-
private static bool IsHooked = false;
37-
38-
public static void Hook() {
39-
ContentHelper.FNAHooks.Hook(IsHooked, typeof(ContentManager), "OpenStream", ref orig_OpenStream);
40-
IsHooked = true;
41-
}
42-
43-
public static Stream ForcedStream;
44-
private delegate Stream d_OpenStream(ContentManager self, string name);
45-
private static d_OpenStream orig_OpenStream;
46-
private static Stream OpenStream(ContentManager self, string name) {
47-
if (!Enabled)
48-
return orig_OpenStream(self, name);
49-
50-
bool isXTF = name.StartsWith("///XNATOFNA/");
51-
if (isXTF)
52-
name = name.Substring(12);
53-
54-
Stream stream;
55-
if (isXTF && ForcedStream != null) {
56-
stream = ForcedStream;
57-
ForcedStream = stream;
58-
} else {
59-
stream = orig_OpenStream(self, name);
60-
}
61-
return stream;
62-
}
63-
64-
}
6532
}

src/Content/FNAHooks.Offline.cs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
}

src/Content/FNAHooks.Online.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 Online {
26+
27+
public static bool Enabled = true;
28+
private static bool IsHooked = false;
29+
30+
public static void Hook() {
31+
ContentHelper.FNAHooks.Hook(IsHooked, typeof(ContentManager), "OpenStream", ref orig_OpenStream);
32+
IsHooked = true;
33+
}
34+
35+
public static Stream ForcedStream;
36+
private delegate Stream d_OpenStream(ContentManager self, string name);
37+
private static d_OpenStream orig_OpenStream;
38+
private static Stream OpenStream(ContentManager self, string name) {
39+
if (!Enabled)
40+
return orig_OpenStream(self, name);
41+
42+
bool isXTF = name.StartsWith("///XNATOFNA/");
43+
if (isXTF)
44+
name = name.Substring(17); // Removes //XNATOFNA/abcd/
45+
46+
Stream stream;
47+
if (isXTF && ForcedStream != null) {
48+
stream = ForcedStream;
49+
ForcedStream = stream;
50+
} else {
51+
stream = orig_OpenStream(self, name);
52+
}
53+
return stream;
54+
}
55+
56+
}
57+
}
58+
}
59+
}

src/Content/FNAHooks.cs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
26+
internal static MethodBase Find(Type type, string name) {
27+
MethodBase found = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
28+
if (found != null)
29+
return found;
30+
31+
if (name.StartsWith("get_") || name.StartsWith("set_")) {
32+
PropertyInfo prop = type.GetProperty(name.Substring(4), BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
33+
if (name[0] == 'g')
34+
found = prop.GetGetMethod(true);
35+
else
36+
found = prop.GetSetMethod(true);
37+
}
38+
39+
return found;
40+
}
41+
42+
internal static void Hook<T>(bool isHooked, Type type, string name, ref T trampoline) {
43+
MethodBase from;
44+
MethodBase to;
45+
if (name.StartsWith(".ctor[")) {
46+
ConstructorInfo[] ctors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
47+
int index;
48+
if (int.TryParse(name.Substring(6, name.Length - 6 - 1), out index))
49+
from = ctors[index];
50+
else
51+
from = ctors[0];
52+
to = Find(typeof(T).DeclaringType, "ctor_" + type.Name);
53+
} else {
54+
from = Find(type, name);
55+
to = Find(typeof(T).DeclaringType, name);
56+
}
57+
58+
// Keeps the detour intact. The JIT likes to revert our changes...
59+
if (isHooked) {
60+
RuntimeDetour.Refresh(from);
61+
return;
62+
}
63+
64+
T tmp =
65+
from
66+
.Detour<T>(
67+
to
68+
);
69+
if (trampoline == null)
70+
trampoline = tmp;
71+
}
72+
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)