194 lines
6.4 KiB
C#
194 lines
6.4 KiB
C#
// See https://aka.ms/new-console-template for more information
|
|
|
|
using CHUNITHM_Patch_Finder.Converters;
|
|
using CHUNITHM_Patch_Finder.Models;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Serialization;
|
|
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> [PATH TO PATTERNS]");
|
|
Environment.Exit(22); // EINVAL
|
|
}
|
|
|
|
var deserializer = new DeserializerBuilder()
|
|
.WithNamingConvention(LowerCaseNamingConvention.Instance)
|
|
.Build();
|
|
var exeFile = args[0];
|
|
var exeFileName = Path.GetFileName(exeFile);
|
|
var patternsFile = args.Length >= 2
|
|
? args[1]
|
|
: Path.Join("Patterns", Path.GetFileNameWithoutExtension(exeFile) + ".yaml");
|
|
|
|
if (!Path.Exists(patternsFile))
|
|
{
|
|
Console.WriteLine($"[ERROR] Could not find patterns file {patternsFile}. Nothing to search for.");
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
var patches =
|
|
deserializer.Deserialize<Pattern[]>(
|
|
File.ReadAllText(patternsFile));
|
|
var binary = File.ReadAllBytes(exeFile);
|
|
var scanner = new Scanner(binary);
|
|
var exportedPatches = new List<BemaniPatcherPatch>();
|
|
|
|
// 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();
|
|
|
|
if (nonMatches.Count > 0)
|
|
{
|
|
Console.WriteLine($"No offset found for patch {patch.Name}, signatures {string.Join(",", nonMatches.Select(p => p.Key))}");
|
|
continue;
|
|
}
|
|
|
|
var patchObject = new BemaniPatcherStandardPatch
|
|
{
|
|
Name = patch.Name,
|
|
Tooltip = patch.Tooltip,
|
|
Danger = patch.Danger,
|
|
Patches = patch.Patches.Select(p =>
|
|
{
|
|
var signature = (p.Signature ?? patch.Signature)!;
|
|
|
|
return new BemaniPatcherStandardPatchEntry
|
|
{
|
|
Offset = matches[signature].Offset + p.Offset,
|
|
Off = p.Off,
|
|
On = p.On,
|
|
};
|
|
}).ToList()
|
|
};
|
|
|
|
exportedPatches.Add(patchObject);
|
|
}
|
|
|
|
// max track count patch
|
|
if (exeFileName == "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}");
|
|
|
|
var patch = new BemaniPatcherNumberPatch
|
|
{
|
|
Name = "Maximum tracks",
|
|
Offset = trackCountAddress + 1,
|
|
Size = 4,
|
|
Min = 3,
|
|
Max = 12,
|
|
};
|
|
|
|
exportedPatches.Add(patch);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("Track count function not found");
|
|
}
|
|
} else if (exeFileName == "amdaemon.exe")
|
|
{
|
|
var results = scanner.FindPatterns([
|
|
"E8 ?? ?? ?? ?? 3B C5 7C 32",
|
|
"E8 ?? ?? ?? ?? A8 10 74 C1",
|
|
]);
|
|
|
|
if (results.All(r => r.Found))
|
|
{
|
|
var logLevel1RelativeAddress = BitConverter.ToInt32(binary, results[0].Offset + 1);
|
|
var logLevel1Address = results[0].Offset + 5 + logLevel1RelativeAddress;
|
|
|
|
var logLevel2RelativeAddress = BitConverter.ToInt32(binary, results[1].Offset + 1);
|
|
var logLevel2Address = results[1].Offset + 5 + logLevel2RelativeAddress;
|
|
|
|
Console.WriteLine($"Found log level functions at {logLevel1Address:X} and {logLevel2Address:X}");
|
|
|
|
exportedPatches.Add(new BemaniPatcherStandardPatch
|
|
{
|
|
Name = "Verbose logging",
|
|
Tooltip = "Force sets 2 flags that enables verbose logging, may be useful for developers",
|
|
Patches = [
|
|
new BemaniPatcherStandardPatchEntry
|
|
{
|
|
Offset = logLevel1Address + 6,
|
|
Off = [0xC3, 0xCC, 0xCC, 0xCC],
|
|
On = [0x83, 0xC0, 0x3F, 0xC3],
|
|
},
|
|
new BemaniPatcherStandardPatchEntry
|
|
{
|
|
Offset = logLevel2Address + 6,
|
|
Off = [0xC3, 0xCC, 0xCC, 0xCC],
|
|
On = [0x83, 0xC8, 0xFF, 0xC3],
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
results = scanner.FindPatterns([
|
|
"E8 ?? ?? ?? ?? 83 F8 01 0F 84 ?? ?? ?? ?? 85 C0 79 2E", // http3ConnectServerSocketNb
|
|
"E8 ?? ?? ?? ?? 83 F8 01 74 29 85 C0", // http3ReadResponseSocketNb
|
|
"E8 ?? ?? ?? ?? 83 F8 01 74 4F", // http3SendRequestSocketNb
|
|
]);
|
|
|
|
if (results.All(r => r.Found))
|
|
{
|
|
Console.WriteLine($"Found Auth2.0 HTTP routines at {string.Join(", ", results.Select(r => r.Offset.ToString("X")))}");
|
|
|
|
var patchEntries = (
|
|
from result in results
|
|
let relativeAddress = BitConverter.ToInt32(binary, result.Offset + 1)
|
|
select result.Offset + 5 + relativeAddress into address
|
|
select new BemaniPatcherStandardPatchEntry { Offset = address + 7, Off = [0x0F, 0x84], On = [0x90, 0xE9] }
|
|
).ToList();
|
|
|
|
exportedPatches.Add(new BemaniPatcherStandardPatch
|
|
{
|
|
Name = "Disable SSL for Auth2.0",
|
|
Tooltip = "Force using non-SSL routines for Auth2.0 online certification, may be useful for developers",
|
|
Patches = patchEntries,
|
|
});
|
|
}
|
|
}
|
|
|
|
var settings = new JsonSerializerSettings
|
|
{
|
|
ContractResolver = new DefaultContractResolver
|
|
{
|
|
NamingStrategy = new CamelCaseNamingStrategy(),
|
|
},
|
|
Converters = [new HexNumberJsonConverter()],
|
|
Formatting = Formatting.Indented,
|
|
};
|
|
|
|
File.WriteAllText(
|
|
"patches.json",
|
|
JsonConvert.SerializeObject(exportedPatches, settings));
|
|
|
|
Console.WriteLine("Wrote patches to patches.json"); |