CHUNITHM-Patch-Finder/Program.cs

120 lines
3.5 KiB
C#
Raw Normal View History

2024-04-11 06:30:43 +00:00
// See https://aka.ms/new-console-template for more information
2024-04-11 08:44:23 +00:00
using CHUNITHM_Patch_Finder.Converters;
using CHUNITHM_Patch_Finder.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
2024-04-11 06:30:43 +00:00
using Reloaded.Memory.Sigscan;
using Reloaded.Memory.Sigscan.Definitions.Structs;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
if (args.Length < 1)
{
Console.WriteLine($"Usage: {Environment.ProcessPath} <PATH TO EXE>");
Environment.Exit(22); // EINVAL
}
var deserializer = new DeserializerBuilder()
.WithNamingConvention(LowerCaseNamingConvention.Instance)
.Build();
var patches =
deserializer.Deserialize<Pattern[]>(
File.ReadAllText("patterns.yaml"));
var binary = File.ReadAllBytes(args[0]);
var scanner = new Scanner(binary);
2024-04-11 08:44:23 +00:00
var exportedPatches = new List<BemaniPatcherPatch>();
2024-04-11 06:30:43 +00:00
// Other patches
foreach (var patch in patches)
{
var signatures = patch.Patches.Select(p => p.Signature ?? patch.Signature).ToList();
if (signatures.Any(s => s == null))
{
Console.WriteLine($"[ERROR] No signature provided for patch {patch.Name}");
continue;
}
var matches = signatures
.Zip(scanner.FindPatternsCached(signatures)!)
.GroupBy(p => p.First)
.ToDictionary(g => g.Key, g => g.First().Second);
var nonMatches = matches.Where(p => !p.Value.Found).ToList();
2024-04-11 06:30:43 +00:00
if (nonMatches.Count > 0)
2024-04-11 06:30:43 +00:00
{
Console.WriteLine($"No offset found for patch {patch.Name}, signatures {string.Join(",", nonMatches.Select(p => p.Key))}");
2024-04-11 06:30:43 +00:00
continue;
}
2024-04-11 08:44:23 +00:00
var patchObject = new BemaniPatcherStandardPatch
2024-04-11 06:30:43 +00:00
{
2024-04-11 08:44:23 +00:00
Name = patch.Name,
Tooltip = patch.Tooltip,
Danger = patch.Danger,
Patches = patch.Patches.Select(p =>
2024-04-11 06:30:43 +00:00
{
2024-04-11 08:44:23 +00:00
var signature = (p.Signature ?? patch.Signature)!;
return new BemaniPatcherStandardPatchEntry
{
Offset = matches[signature].Offset + p.Offset,
Off = p.Off,
On = p.On,
};
}).ToList()
};
2024-04-11 06:30:43 +00:00
exportedPatches.Add(patchObject);
}
// max track count patch
if (Path.GetFileName(args[0]) == "chusanApp.exe")
{
var offset = scanner
.FindPatterns([
"E8 ?? ?? ?? ?? 8D ?? 78 8B F0", // SDHD 2.00-2.20
"E8 ?? ?? ?? ?? 8B 4C 24 24 8B F0", // SDGS 1.30
])
.FirstOrDefault(o => o.Found, new PatternScanResult(-1));
if (offset.Found)
{
var trackCountRelativeAddress = BitConverter.ToInt32(binary, offset.Offset + 1);
var trackCountAddress = offset.Offset + 5 + trackCountRelativeAddress;
Console.WriteLine($"Found track count function at {trackCountAddress:X}");
2024-04-11 08:44:23 +00:00
var patch = new BemaniPatcherNumberPatch
2024-04-11 06:30:43 +00:00
{
2024-04-11 08:44:23 +00:00
Name = "Maximum tracks",
Offset = trackCountAddress + 1,
Size = 4,
Min = 3,
Max = 12,
2024-04-11 06:30:43 +00:00
};
exportedPatches.Add(patch);
}
else
{
Console.WriteLine("Track count function not found");
}
}
2024-04-11 08:44:23 +00:00
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(),
},
Converters = [new HexNumberJsonConverter()],
Formatting = Formatting.Indented,
};
2024-04-11 06:30:43 +00:00
File.WriteAllText(
"patches.json",
2024-04-11 08:44:23 +00:00
JsonConvert.SerializeObject(exportedPatches, settings));
2024-04-11 06:30:43 +00:00
Console.WriteLine("Wrote patches to patches.json");