Skip to content

Commit

Permalink
Finished automatic swinfo generation
Browse files Browse the repository at this point in the history
  • Loading branch information
cheese3660 committed Aug 26, 2023
1 parent cf7bd58 commit 0b661fd
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 93 deletions.
92 changes: 5 additions & 87 deletions SpaceWarpPatcher/Patcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,107 +119,25 @@ private static string[] AllSourceFiles(DirectoryInfo directoryInfo) =>
.Select(fileInfo => fileInfo.FullName)
.ToArray();

private static bool IsDisabled(FileInfo jsonFile, string[] allDisabled)
{
var obj = JObject.Parse(File.ReadAllText(jsonFile.FullName));
if (!obj.ContainsKey("spec")) return false;
if (obj["spec"].Value<string>() is "1.2" or "1.0") return false;
return !allDisabled.Contains(obj["mod_id"].Value<string>());
}

private static (string name, string path) GetNameAndPath(FileInfo jsonFile)
{
var path = '"' + jsonFile.Directory.FullName.Replace("\"","\\\"").Replace("\\","\\\\") + '"';
var obj = JObject.Parse(File.ReadAllText(jsonFile.FullName));
var id = obj["mod_id"].Value<string>();
var replaced = id.Replace(".", "_").Replace(" ", "_");
return (replaced, path);
}

[HarmonyPrefix]
[HarmonyPatch(typeof(Chainloader), nameof(Chainloader.Start))]
private static void PreStartActions()
{
var trueLogger = Logger.CreateLogSource("Roslyn Compiler");
var changed = CompileRoslynMods(trueLogger);
GenerateSpaceWarpPathsDLL(changed, trueLogger);
}

private static void GenerateSpaceWarpPathsDLL(bool changed, ManualLogSource trueLogger)
{
var cacheLocation = Path.Combine(Paths.BepInExRootPath, "AssemblyCache");

PathsGenerator.GenerateSpaceWarpPathsDLL(changed, trueLogger);
try
{
var addressablePaths = Path.Combine(cacheLocation, "SpaceWarpPaths.dll");
if (changed || !File.Exists(addressablePaths))
{
// Preload newtonsoft.json
try
{
Assembly.LoadFile(Path.Combine(Paths.ManagedPath, "Newtonsoft.Json.dll"));
}
catch (Exception e)
{
trueLogger.LogError(e);
}

var disabledPluginsFilepath = Path.Combine(Paths.BepInExRootPath, "disabled_plugins.cfg");
var allDisabled = File.ReadAllText(disabledPluginsFilepath)
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
var allSwinfos =
new DirectoryInfo(Path.Combine(Paths.BepInExRootPath, "plugins"))
.EnumerateFiles("swinfo.json", SearchOption.AllDirectories).Where(x => IsDisabled(x, allDisabled))
.Select(x => GetNameAndPath(x));
// Now we build the dll
var dll = "public static class SpaceWarpPaths {\n";
foreach (var swinfo in allSwinfos)
{
dll += $"public static string {swinfo.name} = {swinfo.path};\n";
}

dll += "}";
trueLogger.LogInfo($"Compiling:\n{dll}");
var tree = CSharpSyntaxTree.ParseText(dll);
var references = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && x.Location.Length > 0)
.Select(x => MetadataReference.CreateFromFile(x.Location)).ToList();
var compilation = CSharpCompilation.Create("SpaceWarpPaths.dll", new[] { tree },references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var result = compilation.Emit(addressablePaths);
foreach (var diagnostic in result.Diagnostics)
{
if (diagnostic.WarningLevel == 0)
{
trueLogger.LogError(diagnostic.Location + ": " + diagnostic);
}
else
{
trueLogger.LogInfo(diagnostic.Location + ": " + diagnostic);
}
}

if (!result.Success)
{
try
{
File.Delete(addressablePaths);
}
catch
{
//Ignored
}

}
}

Assembly.LoadFile(addressablePaths);
SwinfoTransformer.TransformModSwinfos();
}
catch (Exception e)
{
trueLogger.LogError(e);
//ignored
trueLogger.LogError(e.ToString());
}
}



private static bool CompileRoslynMods(ManualLogSource trueLogger)
{
try
Expand Down
106 changes: 106 additions & 0 deletions SpaceWarpPatcher/PathsGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using BepInEx;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Newtonsoft.Json.Linq;

namespace SpaceWarpPatcher;

internal static class PathsGenerator
{



private static (string name, string path) GetNameAndPath(FileInfo jsonFile)
{
var path = '"' + jsonFile.Directory.FullName.Replace("\"","\\\"").Replace("\\","\\\\") + '"';
var obj = JObject.Parse(File.ReadAllText(jsonFile.FullName));
var id = obj["mod_id"].Value<string>();
var replaced = id.Replace(".", "_").Replace(" ", "_");
return (replaced, path);
}
private static bool IsDisabled(FileInfo jsonFile, string[] allDisabled)
{
var obj = JObject.Parse(File.ReadAllText(jsonFile.FullName));
if (!obj.ContainsKey("spec")) return false;
if (obj["spec"].Value<string>() is "1.2" or "1.0") return false;
return !allDisabled.Contains(obj["mod_id"].Value<string>());
}
internal static void GenerateSpaceWarpPathsDLL(bool changed, ManualLogSource trueLogger)
{
var cacheLocation = Path.Combine(Paths.BepInExRootPath, "AssemblyCache");

try
{
var addressablePaths = Path.Combine(cacheLocation, "SpaceWarpPaths.dll");
if (changed || !File.Exists(addressablePaths))
{
// Preload newtonsoft.json
try
{
Assembly.LoadFile(Path.Combine(Paths.ManagedPath, "Newtonsoft.Json.dll"));
}
catch (Exception e)
{
trueLogger.LogError(e);
}

var disabledPluginsFilepath = Path.Combine(Paths.BepInExRootPath, "disabled_plugins.cfg");
var allDisabled = File.ReadAllText(disabledPluginsFilepath)
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
var allSwinfos =
new DirectoryInfo(Path.Combine(Paths.BepInExRootPath, "plugins"))
.EnumerateFiles("swinfo.json", SearchOption.AllDirectories).Where(x => IsDisabled(x, allDisabled)).Concat(new DirectoryInfo(Path.Combine(Paths.GameRootPath,"GameData","Mods")).EnumerateFiles("swinfo.json",SearchOption.AllDirectories)).Select(GetNameAndPath);
// Now we build the dll
var dll = "public static class SpaceWarpPaths {\n";
foreach (var swinfo in allSwinfos)
{
dll += $"public static string {swinfo.name} = {swinfo.path};\n";
}

dll += "}";
trueLogger.LogInfo($"Compiling:\n{dll}");
var tree = CSharpSyntaxTree.ParseText(dll);
var references = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && x.Location.Length > 0)
.Select(x => MetadataReference.CreateFromFile(x.Location)).ToList();
var compilation = CSharpCompilation.Create("SpaceWarpPaths.dll", new[] { tree },references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
var result = compilation.Emit(addressablePaths);
foreach (var diagnostic in result.Diagnostics)
{
if (diagnostic.WarningLevel == 0)
{
trueLogger.LogError(diagnostic.Location + ": " + diagnostic);
}
else
{
trueLogger.LogInfo(diagnostic.Location + ": " + diagnostic);
}
}

if (!result.Success)
{
try
{
File.Delete(addressablePaths);
}
catch
{
//Ignored
}

}
}

Assembly.LoadFile(addressablePaths);
}
catch (Exception e)
{
trueLogger.LogError(e);
//ignored
}
}
}
12 changes: 6 additions & 6 deletions SpaceWarpPatcher/SpaceWarpPatcher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BepInEx.BaseLib" Version="5.4.21"/>
<PackageReference Include="HarmonyX" Version="2.10.1"/>
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.5.0"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0"/>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1"/>
<PackageReference Include="BepInEx.BaseLib" Version="5.4.21" />
<PackageReference Include="HarmonyX" Version="2.10.1" />
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.5.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
</Project>
130 changes: 130 additions & 0 deletions SpaceWarpPatcher/SwinfoTransformer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using Newtonsoft.Json.Linq;

namespace SpaceWarpPatcher;

internal static class SwinfoTransformer
{

internal static JObject TransformSwinfo(JObject swinfo, string hash, DirectoryInfo directoryInfo)
{
var addressables = Path.Combine(directoryInfo.FullName, "addressables");
string catalog = null;
if (Directory.Exists(addressables))
{
var toBecomeCatalog = Path.Combine(addressables, "catalog.json");
if (File.Exists(toBecomeCatalog))
{
catalog = toBecomeCatalog;
}
}

string entryPoint = swinfo.TryGetValue("EntryPoint", out var value) ? value.Value<string>() : null;
if (entryPoint != null) {
var dll = directoryInfo.EnumerateFiles("*.dll", SearchOption.TopDirectoryOnly).FirstOrDefault();
if (dll != null)
{
entryPoint = dll.FullName;
}
}

JObject target = new JObject();
if (catalog == null)
{
if (entryPoint == null)
{
target["APIVersion"] = "0.0.1.1";
target["ModVersion"] = swinfo["version"];
target["ModName"] = swinfo["name"];
target["ModAuthor"] = swinfo["author"];
target["ModDescription"] = swinfo["description"];
target["Hash"] = hash;
}
else
{
target["APIVersion"] = "0.0.1.1";
target["ModVersion"] = swinfo["version"];
target["ModName"] = swinfo["name"];
target["ModAuthor"] = swinfo["author"];
target["ModDescription"] = swinfo["description"];
target["EntryPoint"] = entryPoint;
target["Hash"] = hash;
}
}
else
{
if (entryPoint == null)
{
target["APIVersion"] = "0.0.1.1";
target["ModVersion"] = swinfo["version"];
target["ModName"] = swinfo["name"];
target["ModAuthor"] = swinfo["author"];
target["ModDescription"] = swinfo["description"];
target["Catalog"] = catalog;
target["Hash"] = hash;
}
else
{
target["APIVersion"] = "0.0.1.1";
target["ModVersion"] = swinfo["version"];
target["ModName"] = swinfo["name"];
target["ModAuthor"] = swinfo["author"];
target["ModDescription"] = swinfo["description"];
target["EntryPoint"] = entryPoint;
target["Catalog"] = catalog;
target["Hash"] = hash;
}
}

return target;
}

private static byte[] GetHash(string inputString)
{
using (HashAlgorithm algorithm = SHA256.Create())
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
}

private static string GetHashString(string inputString)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in GetHash(inputString))
sb.Append(b.ToString("X2"));

return sb.ToString();
}

internal static void TransformModSwinfos()
{
var allSwinfos =
new DirectoryInfo(Path.Combine(Paths.GameRootPath, "GameData", "Mods")).EnumerateFiles("swinfo.json",
SearchOption.AllDirectories);
foreach (var swinfo in allSwinfos)
{
var directory = swinfo.Directory!;
var target = Path.Combine(directory.FullName, "modinfo.json");
var swinfoText = File.ReadAllText(swinfo.FullName);
var swinfoData = JObject.Parse(swinfoText);
var hash = GetHashString(swinfoText);
var toCompareHash = "";
if (File.Exists(target))
{
var targetModInfo = JObject.Parse(File.ReadAllText(target));
if (targetModInfo.ContainsKey("Hash"))
{
toCompareHash = (string)(targetModInfo.GetValue("Hash") as JValue)?.Value;
}
else
{
toCompareHash = hash;
}
}
if (hash == toCompareHash) continue;
File.WriteAllText(target,TransformSwinfo(swinfoData,hash, directory).ToString());
}
}
}

0 comments on commit 0b661fd

Please sign in to comment.