Decompiled source of GunSoundSwap v1.1.1

Mods/GunSoundSwap.dll

Decompiled 18 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using GunSoundSwap;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "GunSoundSwap", "1.1.1", "Isaac", null)]
[assembly: MelonGame(null, null)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("GunSoundSwap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("GunSoundSwap")]
[assembly: AssemblyTitle("GunSoundSwap")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace GunSoundSwap
{
	public class Mod : MelonMod
	{
		private static MelonPreferences_Entry<bool> Enabled;

		public override void OnInitializeMelon()
		{
			Enabled = MelonPreferences.CreateCategory("GunSoundSwap").CreateEntry<bool>("Enabled", true, "Set to false and launch once to restore the original Main.bnk", (string)null, false, false, (ValueValidator)null, (string)null);
			try
			{
				Run();
			}
			catch (Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error("GunSoundSwap error, bank left as is: " + ex.Message);
			}
		}

		private void Run()
		{
			string path = Path.Combine(Application.streamingAssetsPath, "Audio", "GeneratedSoundBanks", "Windows");
			string text = Path.Combine(path, "Main.bnk");
			string text2 = Path.Combine(path, "Main.bnk.gss_original");
			string text3 = text + ".gss_tmp";
			if (!File.Exists(text))
			{
				((MelonBase)this).LoggerInstance.Warning("Main.bnk not found");
				return;
			}
			try
			{
				if (File.Exists(text3))
				{
					File.Delete(text3);
				}
			}
			catch
			{
			}
			string text4 = Patcher.Sha(text);
			bool flag = (File.Exists(text2) ? Patcher.Sha(text2) : "") == "F75D62BC31D62B217A9B31585E7E8DEA18FFE1914EA96BFB2B097365D3E3F15";
			if (!Enabled.Value)
			{
				if (text4 != "F75D62BC31D62B217A9B31585E7E8DEA18FFE1914EA96BFB2B097365D3E3F15" && flag)
				{
					Install(text2, text, text3, "F75D62BC31D62B217A9B31585E7E8DEA18FFE1914EA96BFB2B097365D3E3F15");
					((MelonBase)this).LoggerInstance.Msg("Disabled: original Main.bnk restored.");
				}
				return;
			}
			if (text4 == "49D2E166E8BC2E6D2B1012DE64C2E6679402F402416827B6FB50D55B2B0EFC1A")
			{
				((MelonBase)this).LoggerInstance.Msg("Main.bnk already patched.");
				return;
			}
			string origPath;
			if (text4 == "F75D62BC31D62B217A9B31585E7E8DEA18FFE1914EA96BFB2B097365D3E3F15")
			{
				if (!flag)
				{
					File.Copy(text, text2, overwrite: true);
				}
				origPath = text;
			}
			else
			{
				if (!flag)
				{
					((MelonBase)this).LoggerInstance.Warning("Main.bnk is not the expected stock bank (game update or other mod?). Not touching it.");
					return;
				}
				origPath = text2;
			}
			string text5 = FindWems();
			if (text5 == null)
			{
				((MelonBase)this).LoggerInstance.Warning("wems folder not found next to the mod");
				return;
			}
			int num = Patcher.Patch(origPath, text5, text3, delegate(string s)
			{
				((MelonBase)this).LoggerInstance.Msg(s);
			});
			if (num < 0)
			{
				try
				{
					File.Delete(text3);
					return;
				}
				catch
				{
					return;
				}
			}
			Install(text3, text, text3, "49D2E166E8BC2E6D2B1012DE64C2E6679402F402416827B6FB50D55B2B0EFC1A");
			((MelonBase)this).LoggerInstance.Msg("Patched Main.bnk: " + num + " sounds replaced.");
		}

		private void Install(string src, string bank, string tmp, string expect)
		{
			string text = bank + ".gss_new";
			if (src != tmp)
			{
				File.Copy(src, text, overwrite: true);
			}
			else
			{
				File.Move(src, text);
			}
			if (Patcher.Sha(text) != expect)
			{
				File.Delete(text);
				((MelonBase)this).LoggerInstance.Warning("Output hash mismatch; bank left untouched.");
			}
			else
			{
				File.Copy(text, bank, overwrite: true);
				File.Delete(text);
			}
		}

		private string FindWems()
		{
			List<string> list = new List<string>();
			try
			{
				list.Add(Path.Combine(Path.GetDirectoryName(((MelonBase)this).MelonAssembly.Location), "wems"));
			}
			catch
			{
			}
			list.Add(Path.Combine(MelonEnvironment.ModsDirectory, "GunSoundSwap", "wems"));
			try
			{
				string[] directories = Directory.GetDirectories(MelonEnvironment.ModsDirectory, "wems", SearchOption.AllDirectories);
				foreach (string item in directories)
				{
					list.Add(item);
				}
			}
			catch
			{
			}
			foreach (string item2 in list)
			{
				if (Directory.Exists(item2) && File.Exists(Path.Combine(item2, "manifest.txt")))
				{
					return item2;
				}
			}
			return null;
		}
	}
	public static class Patcher
	{
		public const string OriginalHash = "F75D62BC31D62B217A9B31585E7E8DEA18FFE1914EA96BFB2B097365D3E3F15";

		public const string PatchedHash = "49D2E166E8BC2E6D2B1012DE64C2E6679402F402416827B6FB50D55B2B0EFC1A";

		public static string Sha(string path)
		{
			using FileStream inputStream = File.OpenRead(path);
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "");
		}

		public static int Patch(string origPath, string wemDir, string outPath, Action<string> log)
		{
			byte[] array = File.ReadAllBytes(origPath);
			List<string> list = new List<string>();
			List<byte[]> list2 = new List<byte[]>();
			int num;
			for (int i = 0; i + 8 <= array.Length; i += 8 + num)
			{
				string item = Encoding.ASCII.GetString(array, i, 4);
				num = BitConverter.ToInt32(array, i + 4);
				byte[] array2 = new byte[num];
				Buffer.BlockCopy(array, i + 8, array2, 0, num);
				list.Add(item);
				list2.Add(array2);
			}
			int num2 = list.IndexOf("DIDX");
			int num3 = list.IndexOf("DATA");
			int num4 = list.IndexOf("HIRC");
			if (num2 < 0 || num3 < 0 || num4 < 0)
			{
				log("unexpected bank layout");
				return -1;
			}
			byte[] array3 = list2[num2];
			byte[] array4 = list2[num3];
			Dictionary<uint, byte[]> dictionary = new Dictionary<uint, byte[]>();
			string[] files = Directory.GetFiles(wemDir, "*.wem");
			foreach (string path in files)
			{
				if (uint.TryParse(Path.GetFileNameWithoutExtension(path), out var result))
				{
					dictionary[result] = File.ReadAllBytes(path);
				}
			}
			HashSet<uint> hashSet = new HashSet<uint>();
			int num5 = array3.Length / 12;
			for (int k = 0; k < num5; k++)
			{
				hashSet.Add(BitConverter.ToUInt32(array3, k * 12));
			}
			foreach (uint key2 in dictionary.Keys)
			{
				if (!hashSet.Contains(key2))
				{
					log("media id " + key2 + " not in this Main.bnk");
					return -1;
				}
			}
			MemoryStream memoryStream = new MemoryStream(array4.Length + 12582912);
			byte[] array5 = new byte[array3.Length];
			Dictionary<uint, uint> dictionary2 = new Dictionary<uint, uint>();
			for (int l = 0; l < num5; l++)
			{
				uint num6 = BitConverter.ToUInt32(array3, l * 12);
				uint num7 = BitConverter.ToUInt32(array3, l * 12 + 4);
				uint num8 = BitConverter.ToUInt32(array3, l * 12 + 8);
				int offset;
				int num9;
				if (dictionary.TryGetValue(num6, out var value))
				{
					offset = 0;
					num9 = (int)(dictionary2[num6] = (uint)value.Length);
				}
				else
				{
					value = array4;
					offset = (int)num7;
					num9 = (int)num8;
				}
				while (memoryStream.Length % 16 != 0L)
				{
					memoryStream.WriteByte(0);
				}
				BitConverter.GetBytes(num6).CopyTo(array5, l * 12);
				BitConverter.GetBytes((uint)memoryStream.Length).CopyTo(array5, l * 12 + 4);
				BitConverter.GetBytes((uint)num9).CopyTo(array5, l * 12 + 8);
				memoryStream.Write(value, offset, num9);
			}
			list2[num2] = array5;
			list2[num3] = memoryStream.ToArray();
			List<uint[]> list3 = new List<uint[]>();
			string path2 = Path.Combine(wemDir, "patches.txt");
			if (File.Exists(path2))
			{
				files = File.ReadAllLines(path2);
				for (int m = 0; m < files.Length; m++)
				{
					string text = files[m].Trim();
					if (text.Length != 0 && text[0] != '#')
					{
						string[] array6 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
						list3.Add(new uint[3]
						{
							uint.Parse(array6[0]),
							uint.Parse(array6[1]),
							uint.Parse(array6[2])
						});
					}
				}
			}
			byte[] array7 = list2[num4];
			uint num11 = BitConverter.ToUInt32(array7, 0);
			int num12 = 4;
			int num13 = 0;
			int num14 = 0;
			for (uint num15 = 0u; num15 < num11; num15++)
			{
				byte num16 = array7[num12];
				int num17 = BitConverter.ToInt32(array7, num12 + 1);
				int num18 = num12 + 5;
				if (num16 == 2)
				{
					uint key = BitConverter.ToUInt32(array7, num18 + 9);
					if (dictionary2.TryGetValue(key, out var value2))
					{
						BitConverter.GetBytes(65537u).CopyTo(array7, num18 + 4);
						BitConverter.GetBytes(value2).CopyTo(array7, num18 + 17);
						num13++;
					}
				}
				uint num19 = BitConverter.ToUInt32(array7, num18);
				foreach (uint[] item2 in list3)
				{
					if (item2[0] == num19)
					{
						BitConverter.GetBytes(item2[2]).CopyTo(array7, num18 + (int)item2[1]);
						num14++;
					}
				}
				num12 += 5 + num17;
			}
			if (num14 != list3.Count)
			{
				log("hirc patch target missing");
				return -1;
			}
			using (FileStream fileStream = new FileStream(outPath, FileMode.Create, FileAccess.Write))
			{
				for (int n = 0; n < list.Count; n++)
				{
					fileStream.Write(Encoding.ASCII.GetBytes(list[n]), 0, 4);
					fileStream.Write(BitConverter.GetBytes(list2[n].Length), 0, 4);
					fileStream.Write(list2[n], 0, list2[n].Length);
				}
			}
			log("replaced " + dictionary.Count + " media, patched " + num13 + " sound objects");
			return dictionary.Count;
		}
	}
}