Files
wiicompiled/translator/tests/Translator.Tests/ContinuationPlannerTests.cs
T
patchzyy ec226e8348 init
2026-08-23 17:10:50 +02:00

162 lines
5.6 KiB
C#

using System.Buffers.Binary;
using Translator.Core.Mods;
using Translator.Core.Mods.Mkwii;
using Translator.Core.Parsing.Kamek;
using Xunit;
namespace Translator.Tests;
public class ContinuationPlannerTests
{
[Fact]
public void AddModuleTailJumpContinuationsAddsInternalBaseTarget()
{
const uint moduleBase = 0x81200000u;
var moduleImage = new byte[0x20];
WriteU32(moduleImage, 0x00, 0x3D80807Eu); // lis r12,0x807E
WriteU32(moduleImage, 0x04, 0x618C3064u); // ori r12,r12,0x3064
WriteU32(moduleImage, 0x08, 0x7D8903A6u); // mtctr r12
WriteU32(moduleImage, 0x0C, 0x4E800420u); // bctr
var plan = ContinuationPlanner.Build(EmptyChunk(), TestManifest(), moduleBase);
var result = ContinuationPlanner.AddModuleTailJumpContinuations(
plan,
TestManifest(),
moduleBase,
moduleImage);
var entry = Assert.Single(result.Entries);
Assert.Equal(0x807E3064u, entry.Address);
Assert.Equal(0x807E2D18u, entry.ContainingFunctionStart);
Assert.Equal(0x807E30ACu, entry.ContainingFunctionEnd);
Assert.Equal(0x8120000Cu, entry.SourceCommandAddress);
Assert.Contains("tail jump", entry.Reason);
}
[Fact]
public void AddModuleTailJumpContinuationsSupportsSignedAddiLowImmediate()
{
const uint moduleBase = 0x81200000u;
var moduleImage = new byte[0x20];
WriteU32(moduleImage, 0x00, 0x3D80807Fu); // lis r12,0x807F
WriteU32(moduleImage, 0x04, 0x398CFFF0u); // addi r12,r12,-0x10
WriteU32(moduleImage, 0x08, 0x7D8903A6u); // mtctr r12
WriteU32(moduleImage, 0x0C, 0x4E800420u); // bctr
var manifest = new BaseManifest(
"test",
1,
"RMCP01",
"P",
"",
0,
[
new BaseSectionMetadata(".text", "StaticR.rel", 0x807EFFF0u, 0x807F0100u, true, false, "base_text.bin", 0)
],
[
new BaseFunctionRangeMetadata(0x807EFFE0u, 0x807F0100u, "func_807EFFE0", ".text", 0, "test", ["Executable"])
],
"ranges.json");
var plan = ContinuationPlanner.Build(EmptyChunk(), manifest, moduleBase);
var result = ContinuationPlanner.AddModuleTailJumpContinuations(plan, manifest, moduleBase, moduleImage);
var entry = Assert.Single(result.Entries);
Assert.Equal(0x807EFFF0u, entry.Address);
Assert.Equal(0x807EFFE0u, entry.ContainingFunctionStart);
}
[Fact]
public void AddModuleTailJumpContinuationsIgnoresFunctionEntries()
{
const uint moduleBase = 0x81200000u;
var moduleImage = new byte[0x20];
WriteU32(moduleImage, 0x00, 0x3D80807Eu); // lis r12,0x807E
WriteU32(moduleImage, 0x04, 0x618C2D18u); // ori r12,r12,0x2D18
WriteU32(moduleImage, 0x08, 0x7D8903A6u); // mtctr r12
WriteU32(moduleImage, 0x0C, 0x4E800420u); // bctr
var manifest = TestManifest();
var plan = ContinuationPlanner.Build(EmptyChunk(), manifest, moduleBase);
var result = ContinuationPlanner.AddModuleTailJumpContinuations(plan, manifest, moduleBase, moduleImage);
Assert.Empty(result.Entries);
}
[Fact]
public void AddRetroWfcExecutableHookContinuationsAddsInternalBaseTarget()
{
var manifest = new BaseManifest(
"test",
1,
"RMCP01",
"P",
"",
0,
[
new BaseSectionMetadata(".text", "main.dol", 0x800F164Cu, 0x800F1BC8u, true, false, "base_text.bin", 0)
],
[
new BaseFunctionRangeMetadata(0x800F164Cu, 0x800F1BC8u, "func_800F164C", ".text", 0, "test", ["Executable"])
],
"ranges.json");
var hook = new RetroWfcExecutableHookPlan(
5,
"executableHookWithContinuation",
"branchCtr",
0x800F164Cu,
4,
0x800F164Cu,
0x800F1BC8u,
".text",
["support.hostnameRewrite.gethostbyname"],
"overlayHookPatch",
0x8179E1A4u,
"support.hostnameRewrite.gethostbyname",
"moduleFunction",
null,
0x800F165Cu);
var plan = ContinuationPlanner.Build(EmptyChunk(), manifest, 0x81200000u);
var result = ContinuationPlanner.AddRetroWfcExecutableHookContinuations(plan, manifest, [hook]);
var entry = Assert.Single(result.Entries);
Assert.Equal(0x800F165Cu, entry.Address);
Assert.Equal(0x800F164Cu, entry.ContainingFunctionStart);
Assert.Equal(0x800F1BC8u, entry.ContainingFunctionEnd);
Assert.Equal(0x800F164Cu, entry.SourceCommandAddress);
Assert.Contains("Retro WFC executable hook continuation", entry.Reason);
}
private static KamekChunk EmptyChunk() =>
new(
0,
0,
0,
0,
0,
0,
KamekChunk.HeaderSize,
[],
[]);
private static BaseManifest TestManifest() =>
new(
"test",
1,
"RMCP01",
"P",
"",
0,
[
new BaseSectionMetadata(".text", "StaticR.rel", 0x807E2D18u, 0x807E30ACu, true, false, "base_text.bin", 0)
],
[
new BaseFunctionRangeMetadata(0x807E2D18u, 0x807E30ACu, "func_807E2D18", ".text", 0, "test", ["Executable"])
],
"ranges.json");
private static void WriteU32(byte[] data, int offset, uint value) =>
BinaryPrimitives.WriteUInt32BigEndian(data.AsSpan(offset, 4), value);
}