CVR Manager 1.0.0

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Exterrata
2026-10-25 02:46:35 -04:00
commit 9bb4f85802
59 changed files with 7913 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
using System.Text.Json;
using System.Text.Json.Nodes;
namespace CVRManager.Services;
public static class CacheGuardService
{
const string AssemblyName = "CVRCacheGuard";
const string AssemblyFile = AssemblyName + ".dll";
const int UserAssemblyType = 16;
static string Source => Path.Combine(AppContext.BaseDirectory, "CacheGuard");
static string Managed(string gameDir) => Path.Combine(gameDir, "ChilloutVR_Data", "Managed");
public static void Deploy(string gameDir, Action<string> log, string? source = null)
{
var managed = Managed(gameDir);
var scripting = Path.Combine(gameDir, "ChilloutVR_Data", "ScriptingAssemblies.json");
var init = Path.Combine(gameDir, "ChilloutVR_Data", "RuntimeInitializeOnLoads.json");
source ??= Source;
if (!Directory.Exists(source) || !Directory.Exists(managed) || !File.Exists(scripting) || !File.Exists(init)) return;
var changed = false;
var deps = Path.Combine(managed, AssemblyName);
Directory.CreateDirectory(deps);
foreach (var file in Directory.EnumerateFiles(source, "*.dll"))
{
var name = Path.GetFileName(file);
var target = name == AssemblyFile ? Path.Combine(managed, name) : Path.Combine(deps, name);
changed |= CopyIfDifferent(file, target);
}
var scriptingJson = JsonNode.Parse(File.ReadAllText(scripting))!.AsObject();
var names = scriptingJson["names"]!.AsArray();
var types = scriptingJson["types"]!.AsArray();
if (!names.Any(n => n?.GetValue<string>() == AssemblyFile))
{
names.Add(AssemblyFile);
types.Add(UserAssemblyType);
WriteJson(scripting, scriptingJson);
changed = true;
}
var initJson = JsonNode.Parse(File.ReadAllText(init))!.AsObject();
var root = initJson["root"]!.AsArray();
if (!root.Any(e => e?["assemblyName"]?.GetValue<string>() == AssemblyName))
{
root.Add(new JsonObject
{
["assemblyName"] = AssemblyName,
["nameSpace"] = AssemblyName,
["className"] = "Entry",
["methodName"] = "Init",
["loadTypes"] = 2,
["isUnityClass"] = false,
});
WriteJson(init, initJson);
changed = true;
}
if (changed) log("Multi-instance cache guard added to this install.");
}
public static void Remove(string gameDir, Action<string> log)
{
var data = Path.Combine(gameDir, "ChilloutVR_Data");
var scripting = Path.Combine(data, "ScriptingAssemblies.json");
var init = Path.Combine(data, "RuntimeInitializeOnLoads.json");
var changed = false;
if (File.Exists(scripting))
{
var json = JsonNode.Parse(File.ReadAllText(scripting))!.AsObject();
var names = json["names"]!.AsArray();
var types = json["types"]!.AsArray();
for (var i = names.Count - 1; i >= 0; i--)
if (names[i]?.GetValue<string>() == AssemblyFile)
{
names.RemoveAt(i);
if (i < types.Count) types.RemoveAt(i);
changed = true;
}
if (changed) WriteJson(scripting, json);
}
if (File.Exists(init))
{
var json = JsonNode.Parse(File.ReadAllText(init))!.AsObject();
var root = json["root"]!.AsArray();
var before = root.Count;
for (var i = root.Count - 1; i >= 0; i--)
if (root[i]?["assemblyName"]?.GetValue<string>() == AssemblyName) root.RemoveAt(i);
if (root.Count != before)
{
WriteJson(init, json);
changed = true;
}
}
var managed = Managed(gameDir);
try
{
File.Delete(Path.Combine(managed, AssemblyFile));
var deps = Path.Combine(managed, AssemblyName);
if (Directory.Exists(deps)) Directory.Delete(deps, true);
}
catch (Exception e) when (e is IOException or UnauthorizedAccessException) { }
if (changed) log("Multi-instance cache guard removed from this install.");
}
static bool CopyIfDifferent(string source, string target)
{
var s = new FileInfo(source);
var t = new FileInfo(target);
if (t.Exists && t.Length == s.Length && t.LastWriteTimeUtc == s.LastWriteTimeUtc) return false;
try
{
File.Copy(source, target, true);
File.SetLastWriteTimeUtc(target, s.LastWriteTimeUtc);
return true;
}
catch (IOException) when (t.Exists)
{
return false;
}
}
static void WriteJson(string path, JsonNode node) =>
File.WriteAllText(path, node.ToJsonString(new JsonSerializerOptions { WriteIndented = false }), new System.Text.UTF8Encoding(false));
}