From d0635813e5088efd9cc799002001ba190c0cac14 Mon Sep 17 00:00:00 2001
From: flibber-hk <76987839+flibber-hk@users.noreply.github.com>
Date: Tue, 27 Jan 2026 21:08:43 +0000
Subject: [PATCH] initial implementation
---
AssetHelper/AssetHelper.csproj | 2 +-
AssetHelper/Core/BundleMetadata.cs | 5 ++--
.../Plugin/LoadingPage/LoadingScreen.cs | 23 +++++++++++++++
.../Plugin/LoadingPage/MemoryWatcher.cs | 29 +++++++++++++++++++
AssetHelper/Plugin/StartupOverrideManager.cs | 9 ++++++
AssetHelper/packages.lock.json | 6 ++--
6 files changed, 68 insertions(+), 6 deletions(-)
create mode 100644 AssetHelper/Plugin/LoadingPage/MemoryWatcher.cs
diff --git a/AssetHelper/AssetHelper.csproj b/AssetHelper/AssetHelper.csproj
index c5052cf..649c4a7 100644
--- a/AssetHelper/AssetHelper.csproj
+++ b/AssetHelper/AssetHelper.csproj
@@ -71,7 +71,7 @@
-
+
diff --git a/AssetHelper/Core/BundleMetadata.cs b/AssetHelper/Core/BundleMetadata.cs
index ed6a0e6..2a4fb2d 100644
--- a/AssetHelper/Core/BundleMetadata.cs
+++ b/AssetHelper/Core/BundleMetadata.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
+using AssetHelperLib.IO;
using AssetsTools.NET;
using AssetsTools.NET.Extra;
using Silksong.AssetHelper.Internal;
@@ -113,8 +114,8 @@ internal static List DetermineDirectDepsInternal(string bundleName, out
AssetsManager mgr = new();
string sceneBundlePath = Path.Combine(AssetPaths.BundleFolder, bundleFile);
- using MemoryStream ms = new(File.ReadAllBytes(sceneBundlePath));
- BundleFileInstance bun = mgr.LoadBundleFile(ms, sceneBundlePath);
+ using RentedFileArray rfa = new(sceneBundlePath);
+ BundleFileInstance bun = mgr.LoadBundleFile(rfa.Stream, sceneBundlePath);
AssetsFileInstance afileInst = mgr.LoadAssetsFileFromBundle(bun, 0, false);
AssetsFile afile = afileInst.file;
diff --git a/AssetHelper/Plugin/LoadingPage/LoadingScreen.cs b/AssetHelper/Plugin/LoadingPage/LoadingScreen.cs
index 485df11..d3c3772 100644
--- a/AssetHelper/Plugin/LoadingPage/LoadingScreen.cs
+++ b/AssetHelper/Plugin/LoadingPage/LoadingScreen.cs
@@ -108,6 +108,29 @@ void Awake()
subtextRect.anchoredPosition = new Vector2(0, -45);
}
+#if DEBUG
+ {
+ GameObject memTextObj = new("MemText");
+ memTextObj.transform.SetParent(_canvasObject.transform);
+ Text _memText = memTextObj.AddComponent();
+
+ _memText.font = Resources.GetBuiltinResource("Arial.ttf");
+ _memText.text = string.Empty;
+ _memText.fontSize = 20;
+ _memText.alignment = TextAnchor.MiddleCenter;
+ _memText.color = Color.white;
+
+ RectTransform memtextRect = _memText.rectTransform;
+ memtextRect.anchorMin = new Vector2(0.5f, 0.5f);
+ memtextRect.anchorMax = new Vector2(0.5f, 0.5f);
+ memtextRect.pivot = new Vector2(0.5f, 1f);
+ memtextRect.sizeDelta = new Vector2(800, 100);
+ memtextRect.anchoredPosition = new Vector2(0, -90);
+
+ memTextObj.AddComponent();
+ }
+#endif
+
// For testing
_statusText.text = "Loading...";
}
diff --git a/AssetHelper/Plugin/LoadingPage/MemoryWatcher.cs b/AssetHelper/Plugin/LoadingPage/MemoryWatcher.cs
new file mode 100644
index 0000000..121c2bb
--- /dev/null
+++ b/AssetHelper/Plugin/LoadingPage/MemoryWatcher.cs
@@ -0,0 +1,29 @@
+using System;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Silksong.AssetHelper.Plugin.LoadingPage;
+
+
+///
+/// Unscientific class to watch memory usage.
+///
+[RequireComponent(typeof(Text))]
+internal class MemoryWatcher : MonoBehaviour
+{
+ private long maxSoFar = 0;
+
+ private Text _text;
+
+ void Awake()
+ {
+ _text = GetComponent();
+ }
+
+ void Update()
+ {
+ long mem = GC.GetTotalMemory(forceFullCollection: false);
+ maxSoFar = mem > maxSoFar ? mem : maxSoFar;
+ _text.text = $"Memory: {mem:E}\nMax {maxSoFar:E}";
+ }
+}
diff --git a/AssetHelper/Plugin/StartupOverrideManager.cs b/AssetHelper/Plugin/StartupOverrideManager.cs
index ce61a8d..def8d8e 100644
--- a/AssetHelper/Plugin/StartupOverrideManager.cs
+++ b/AssetHelper/Plugin/StartupOverrideManager.cs
@@ -1,6 +1,8 @@
using System;
+using System.Buffers;
using System.Collections;
using System.Collections.Generic;
+using AssetHelperLib.IO;
using MonoDetour.HookGen;
using Silksong.AssetHelper.Core;
using Silksong.AssetHelper.Plugin.LoadingPage;
@@ -43,6 +45,9 @@ private static IEnumerator WrapStartManagerStart(StartManager self, IEnumerator
// This should already be the case, but we should check just in case it matters.
yield return new WaitUntil(() => AddressablesData.IsAddressablesLoaded);
+ // Assign the shared array pool for IO at the start of the procedure
+ RentedFileArray.Pool = ArrayPool.Create(250 * 1024 * 1024, 5);
+
LoadingScreen screen = LoadingScreenExtensions.Create();
bool failed = false;
@@ -98,6 +103,10 @@ private static IEnumerator WrapStartManagerStart(StartManager self, IEnumerator
// Even if there was an error, still let them into the game normally
UObject.Destroy(screen);
+
+ // Clear the pool now that it's no longer in use; in theory the GC can reclaim it/when if it wants.
+ RentedFileArray.Pool = null;
+
yield return null;
yield return original;
diff --git a/AssetHelper/packages.lock.json b/AssetHelper/packages.lock.json
index e1d4f48..7901cb8 100644
--- a/AssetHelper/packages.lock.json
+++ b/AssetHelper/packages.lock.json
@@ -4,9 +4,9 @@
".NETStandard,Version=v2.1": {
"AssetHelperLib": {
"type": "Direct",
- "requested": "[0.10.1, )",
- "resolved": "0.10.1",
- "contentHash": "qRX+I2d6T0WVcJrxPV7bBlGD37B1Eg5GAo36+72lXOJqgHjhnJrgOcnqtwm9vRaOaBfnzMV3f8+myTioLeT9cw=="
+ "requested": "[0.11.0, )",
+ "resolved": "0.11.0",
+ "contentHash": "1YIHnp3S+gBLa1NzFTtSmMFA+zT7D0mCgCPqPhVa4pv5CKpJQgSiZPeiRxzMDvDMK0sdGa6O6/Uuh1pPIrBYlw=="
},
"AssetsTools.NET": {
"type": "Direct",