Decompiled source of Custom Map Loader v1.0.0

plugins/morsecodeguy-custommaps/MapLoader.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SceneMaterialEditor")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SceneMaterialEditor")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0e0ebe3a-06b3-4c4f-a970-c2431cfdcb4b")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MapPlugin;

[BepInPlugin("com.morsecodeguy.mapplugin", "Map Plugin", "1.0.0")]
public class MapPlugin : BaseUnityPlugin
{
	public static Dictionary<string, CustomMapData> customMapDataDict = new Dictionary<string, CustomMapData>();

	public static List<MapItem> allMapItems = new List<MapItem>();

	public static string selectedCustomMapName = null;

	public static List<SongSound> customSongs = new List<SongSound>();

	private string mapsFolderPath;

	private void Awake()
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		mapsFolderPath = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "Maps");
		((Object)Chainloader.ManagerObject).hideFlags = (HideFlags)61;
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Maps folder path: " + mapsFolderPath));
		Harmony val = new Harmony("com.morsecodeguy.mapplugin");
		val.PatchAll();
		SceneManager.sceneLoaded += OnSceneLoaded;
		((MonoBehaviour)this).StartCoroutine(LoadMapsFromAssetBundles());
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
	}

	private IEnumerator LoadMapsFromAssetBundles()
	{
		if (!Directory.Exists(mapsFolderPath))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Maps folder not found: " + mapsFolderPath));
			yield break;
		}
		string[] mapDirectories = Directory.GetDirectories(mapsFolderPath);
		string[] array = mapDirectories;
		AudioClip audioClip = default(AudioClip);
		foreach (string mapDir in array)
		{
			string jsonPath = Path.Combine(mapDir, "mapinfo.json");
			if (!File.Exists(jsonPath))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Map configuration file not found: " + jsonPath));
				continue;
			}
			string json = File.ReadAllText(jsonPath);
			MapInfo mapInfo;
			try
			{
				mapInfo = JsonConvert.DeserializeObject<MapInfo>(json);
			}
			catch (Exception ex2)
			{
				Exception ex = ex2;
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to parse JSON in " + jsonPath + ": " + ex.Message));
				continue;
			}
			if (mapInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("MapInfo is null after deserialization in " + jsonPath));
				continue;
			}
			string assetsBundlePath = Path.Combine(mapDir, mapInfo.assetsBundleFileName);
			if (!File.Exists(assetsBundlePath))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Assets bundle file not found: " + assetsBundlePath));
				continue;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading assets bundle from: " + assetsBundlePath));
			AssetBundle assetsBundle = AssetBundle.LoadFromFile(assetsBundlePath);
			if ((Object)(object)assetsBundle == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to load assets bundle: " + assetsBundlePath));
				continue;
			}
			Dictionary<string, Object> renamedAssets = LoadAndRenameAssets(assetsBundle, mapInfo.mapName);
			string rawBackgroundName = Path.GetFileName(mapInfo.mapBackgroundAssetName);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Raw background asset name from JSON: " + rawBackgroundName));
			string backgroundAssetKey = string.Concat(str2: Path.GetFileNameWithoutExtension(rawBackgroundName), str0: mapInfo.mapName, str1: "_");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Constructed background asset key: " + backgroundAssetKey));
			if (!renamedAssets.TryGetValue(backgroundAssetKey, out var backgroundObj))
			{
				string availableKeys = string.Join(", ", renamedAssets.Keys);
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to load MapBackground with key " + backgroundAssetKey + " from " + assetsBundlePath + ". Available keys: " + availableKeys));
				assetsBundle.Unload(false);
				continue;
			}
			Texture backgroundImage = (Texture)(object)((backgroundObj is Texture) ? backgroundObj : null);
			if ((Object)(object)backgroundImage == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("MapBackground asset with key " + backgroundAssetKey + " is not a Texture."));
				assetsBundle.Unload(false);
				continue;
			}
			string sceneBundlePath = Path.Combine(mapDir, mapInfo.sceneBundleFileName);
			if (!File.Exists(sceneBundlePath))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Scene bundle file not found: " + sceneBundlePath));
				assetsBundle.Unload(false);
				continue;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Loading scene bundle from: " + sceneBundlePath));
			AssetBundle sceneBundle = AssetBundle.LoadFromFile(sceneBundlePath);
			if ((Object)(object)sceneBundle == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failed to load scene bundle: " + sceneBundlePath));
				assetsBundle.Unload(false);
				continue;
			}
			string scenePath = mapInfo.sceneAssetPath;
			bool sceneFound = false;
			string[] scenePaths = sceneBundle.GetAllScenePaths();
			string[] array2 = scenePaths;
			foreach (string s in array2)
			{
				if (s.Equals(scenePath, StringComparison.OrdinalIgnoreCase))
				{
					sceneFound = true;
					break;
				}
			}
			if (!sceneFound)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Scene path " + scenePath + " not found in scene bundle: " + sceneBundlePath));
				assetsBundle.Unload(false);
				sceneBundle.Unload(false);
				continue;
			}
			MapItem mapItem = new MapItem
			{
				mapName = mapInfo.mapName,
				backgroundImage = backgroundImage
			};
			allMapItems.Add(mapItem);
			customMapDataDict[mapInfo.mapName] = new CustomMapData
			{
				scenePath = scenePath,
				assetsBundle = assetsBundle,
				sceneBundle = sceneBundle,
				renamedAssets = renamedAssets
			};
			string sceneName = Path.GetFileNameWithoutExtension(scenePath);
			string audioKey = mapInfo.mapName + "_" + sceneName;
			int num;
			if (renamedAssets.TryGetValue(audioKey, out var audioAsset))
			{
				audioClip = (AudioClip)(object)((audioAsset is AudioClip) ? audioAsset : null);
				num = ((audioClip != null) ? 1 : 0);
			}
			else
			{
				num = 0;
			}
			if (num != 0)
			{
				SongSound song = new SongSound
				{
					AudioClip = audioClip,
					sceneName = sceneName
				};
				if ((Object)(object)MusicManager.singleton != (Object)null)
				{
					MusicManager.singleton.musicList.Add(song);
				}
				else
				{
					customSongs.Add(song);
				}
			}
			backgroundObj = null;
			audioAsset = null;
			audioClip = null;
		}
	}

	private Dictionary<string, Object> LoadAndRenameAssets(AssetBundle bundle, string prefix)
	{
		Dictionary<string, Object> dictionary = new Dictionary<string, Object>();
		Object[] array = bundle.LoadAllAssets();
		Object[] array2 = array;
		foreach (Object val in array2)
		{
			string text = Path.GetFileName(val.name);
			if (!text.StartsWith(prefix + "_"))
			{
				text = prefix + "_" + text;
			}
			val.name = text;
			dictionary[text] = val;
		}
		foreach (string key in dictionary.Keys)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Renamed asset: " + key));
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("All assets in bundle have been renamed with prefix: " + prefix + "_"));
		return dictionary;
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		((MonoBehaviour)this).StartCoroutine(UpdateGameSettingsPanel());
		bool flag = false;
		foreach (CustomMapData value in customMapDataDict.Values)
		{
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(value.scenePath);
			if (((Scene)(ref scene)).name.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
			{
				flag = true;
				break;
			}
		}
		if (flag)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom map scene loaded; scheduling object modifications...");
			((MonoBehaviour)this).StartCoroutine(ModifyCustomMapObjects());
		}
	}

	private IEnumerator UpdateGameSettingsPanel()
	{
		while ((Object)(object)GameSettingsPanel.singleton == (Object)null)
		{
			yield return null;
		}
		GameSettingsPanel singleton = GameSettingsPanel.singleton;
		foreach (MapItem mapItem in allMapItems)
		{
			if (!singleton.mapList.Exists((MapItem x) => x.mapName == mapItem.mapName))
			{
				singleton.mapList.Add(mapItem);
			}
			if (!singleton.mapSelect.buttonOptions.Exists((ButtonOption x) => x.optionValue == mapItem.mapName))
			{
				singleton.mapSelect.buttonOptions.Add(new ButtonOption
				{
					optionText = mapItem.mapName,
					optionValue = mapItem.mapName
				});
			}
		}
		singleton.UpdateMapBackground();
	}

	private IEnumerator ModifyCustomMapObjects()
	{
		float timeout = 5f;
		float timer = 0f;
		while (timer < timeout && (Object)(object)Object.FindObjectOfType<Renderer>() == (Object)null && (Object)(object)Object.FindObjectOfType<Collider>() == (Object)null)
		{
			timer += Time.deltaTime;
			yield return null;
		}
		ReconnectShaders();
		Collider[] colliders = Object.FindObjectsOfType<Collider>();
		int mapPartLayer = LayerMask.NameToLayer("MapPart");
		Collider[] array = colliders;
		foreach (Collider col in array)
		{
			((Component)col).gameObject.layer = mapPartLayer;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Custom map scene object modifications complete.");
	}

	private void ReconnectShaders()
	{
		Renderer[] array = Object.FindObjectsOfType<Renderer>();
		for (int i = 0; i < array.Length; i++)
		{
			Material[] materials = array[i].materials;
			foreach (Material val in materials)
			{
				val.shader = Shader.Find(((Object)val.shader).name);
			}
		}
	}
}
[Serializable]
public class MapInfo
{
	public string mapName;

	public string assetsBundleFileName;

	public string sceneBundleFileName;

	public string sceneAssetPath;

	public string mapBackgroundAssetName;
}
public class CustomMapData
{
	public string scenePath;

	public AssetBundle assetsBundle;

	public AssetBundle sceneBundle;

	public Dictionary<string, Object> renamedAssets;
}
[HarmonyPatch(typeof(SceneManagerWithParameters), "DoSceneLoad")]
public class DoSceneLoadPatch
{
	private static bool Prefix(bool forceShowLoadingBar, bool manuallyHideLoadingBar)
	{
		if (!string.IsNullOrEmpty(MapPlugin.selectedCustomMapName) && MapPlugin.customMapDataDict.TryGetValue(MapPlugin.selectedCustomMapName, out var value))
		{
			Debug.Log((object)("Overriding scene load with custom map: " + MapPlugin.selectedCustomMapName));
			LoadMaterialsFromBundle(value.assetsBundle);
			SceneManager.LoadSceneAsync(value.scenePath);
			SceneManagerWithParameters.loadingGameScene = true;
			return false;
		}
		return true;
	}

	private static void LoadMaterialsFromBundle(AssetBundle bundle)
	{
		if ((Object)(object)bundle == (Object)null)
		{
			Debug.LogError((object)"Asset bundle is null. Materials will not load.");
			return;
		}
		Material[] array = bundle.LoadAllAssets<Material>();
		Material[] array2 = array;
		foreach (Material val in array2)
		{
			Debug.Log((object)("Loaded material: " + ((Object)val).name));
		}
	}
}
[HarmonyPatch(typeof(MusicManager), "Awake")]
public class MusicManagerAwakePatch
{
	private static void Postfix(MusicManager __instance)
	{
		foreach (SongSound song in MapPlugin.customSongs)
		{
			if (!__instance.musicList.Exists((SongSound x) => x.sceneName == song.sceneName))
			{
				__instance.musicList.Add(song);
			}
		}
		MapPlugin.customSongs.Clear();
	}
}