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;
}
}
}