Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 44 additions & 29 deletions Source/Commands/BranchCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,64 @@ namespace Kamek.Commands
class BranchCommand : Command
{
public readonly Word Target;
public readonly Word? Original;

public BranchCommand(Word source, Word target, bool isLink)
: base(isLink ? Ids.BranchLink : Ids.Branch, source)
private static Ids IdFromFlags(bool isLink, bool isConditional)
{
if (isConditional)
return isLink ? Ids.CondBranchLink : Ids.CondBranch;
else
return isLink ? Ids.BranchLink : Ids.Branch;

throw new NotImplementedException();
}

public BranchCommand(Word source, Word target, Word? original, bool isLink)
: base(IdFromFlags(isLink, original.HasValue), source)
{
Target = target;
Original = original;
}

public override void WriteArguments(BinaryWriter bw)
{
Target.AssertNotAmbiguous();
bw.WriteBE(Target.Value);

if (Original.HasValue)
{
Original.Value.AssertNotRelative();
bw.WriteBE(Original.Value.Value);
}
}

public override IEnumerable<string> PackForRiivolution()
{
Address.Value.AssertAbsolute();
Target.AssertAbsolute();

return new string[] { string.Format("<memory offset=\"0x{0:X8}\" value=\"{1:X8}\" />", Address.Value.Value, GenerateInstruction()) };
return GenerateWriteCommand().PackForRiivolution();
}

public override IEnumerable<string> PackForDolphin()
{
Address.Value.AssertAbsolute();
Target.AssertAbsolute();

return new string[] { string.Format("0x{0:X8}:dword:0x{1:X8}", Address.Value.Value, GenerateInstruction()) };
return GenerateWriteCommand().PackForDolphin();
}

public override IEnumerable<ulong> PackGeckoCodes()
{
Address.Value.AssertAbsolute();
Target.AssertAbsolute();

ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | GenerateInstruction();
code |= 0x4000000UL << 32;

return new ulong[1] { code };
return GenerateWriteCommand().PackGeckoCodes();
}

public override IEnumerable<ulong> PackActionReplayCodes()
{
Address.Value.AssertAbsolute();
Target.AssertAbsolute();

ulong code = ((ulong)(Address.Value.Value & 0x1FFFFFF) << 32) | GenerateInstruction();
code |= 0x4000000UL << 32;

return new ulong[1] { code };
return GenerateWriteCommand().PackActionReplayCodes();
}

public override bool Apply(KamekFile file)
{
if (file.Contains(Address.Value) && Address.Value.Type == Target.Type)
{
if (Original.HasValue && file.ReadUInt32(Address.Value) != Original.Value.Value)
return true;

file.WriteUInt32(Address.Value, GenerateInstruction());
return true;
}
Expand All @@ -74,19 +77,31 @@ public override bool Apply(KamekFile file)

public override void ApplyToCodeFile(CodeFiles.CodeFile file)
{
Address.Value.AssertAbsolute();
Target.AssertAbsolute();

file.WriteUInt32(Address.Value.Value, GenerateInstruction());
GenerateWriteCommand().ApplyToCodeFile(file);
}


private WriteCommand GenerateWriteCommand()
{
Target.AssertAbsolute();
return new WriteCommand(
Address.Value,
new Word(WordType.Value, GenerateInstruction()),
WriteCommand.Type.Value32,
Original);
}

private uint GenerateInstruction()
{
long delta = Target - Address.Value;
uint insn = (Id == Ids.BranchLink) ? 0x48000001U : 0x48000000U;
uint insn = IsLink() ? 0x48000001U : 0x48000000U;
insn |= ((uint)delta & 0x3FFFFFC);
return insn;
}

private bool IsLink()
{
return Id == Ids.BranchLink || Id == Ids.CondBranchLink;
}
}
}
2 changes: 2 additions & 0 deletions Source/Commands/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public enum Ids : byte

Branch = 64,
BranchLink = 65,
CondBranch = 66,
CondBranchLink = 67,
}

public readonly Ids Id;
Expand Down
12 changes: 7 additions & 5 deletions Source/Hooks/BranchHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ namespace Kamek.Hooks
{
class BranchHook : Hook
{
public BranchHook(bool isLink, Word[] args, AddressMapper mapper)
public BranchHook(bool isLink, bool isConditional, Word[] args, AddressMapper mapper)
{
if (args.Length != 2)
if (args.Length != (isConditional ? 3 : 2))
throw new InvalidDataException("wrong arg count for BranchCommand");

// expected args:
// source : pointer to game code
// dest : pointer to game code or to Kamek code
// source : pointer to game code
// dest : pointer to game code or to Kamek code
// original : value (an encoded PPC instruction, probably)
var source = GetAbsoluteArg(args[0], mapper);
var dest = GetAnyPointerArg(args[1], mapper);
Word? original = isConditional ? GetValueArg(args[2]) : null;

Commands.Add(new Commands.BranchCommand(source, dest, isLink));
Commands.Add(new Commands.BranchCommand(source, dest, original, isLink));
}
}
}
12 changes: 9 additions & 3 deletions Source/Hooks/Hook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public enum HookType : uint {
kctConditionalWrite = 2,
kctInjectBranch = 3,
kctInjectCall = 4,
kctPatchExit = 5
kctPatchExit = 5,
kctConditionalInjectBranch = 6,
kctConditionalInjectCall = 7
}

abstract class Hook
Expand All @@ -26,9 +28,13 @@ public static Hook Create(Linker.HookData data, AddressMapper mapper)
case (uint)HookType.kctConditionalWrite:
return new WriteHook(true, data.args, mapper);
case (uint)HookType.kctInjectBranch:
return new BranchHook(false, data.args, mapper);
return new BranchHook(false, false, data.args, mapper);
case (uint)HookType.kctInjectCall:
return new BranchHook(true, data.args, mapper);
return new BranchHook(true, false, data.args, mapper);
case (uint)HookType.kctConditionalInjectBranch:
return new BranchHook(false, true, data.args, mapper);
case (uint)HookType.kctConditionalInjectCall:
return new BranchHook(true, true, data.args, mapper);
case (uint)HookType.kctPatchExit:
return new PatchExitHook(data.args, mapper);
default:
Expand Down
4 changes: 2 additions & 2 deletions Source/KamekFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ public byte[] Pack()
{
using (var bw = new BinaryWriter(ms))
{
bw.WriteBE((uint)0x4B616D65); // 'Kamek\0\0\2'
bw.WriteBE((uint)0x6B000002);
bw.WriteBE((uint)0x4B616D65); // 'Kamek\0\0\3'
bw.WriteBE((uint)0x6B000003);
bw.WriteBE((uint)_bssSize);
bw.WriteBE((uint)_codeBlob.Length);
bw.WriteBE((uint)_ctorStart);
Expand Down
7 changes: 7 additions & 0 deletions k_stdlib/kamek.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#define kctInjectBranch 3
#define kctInjectCall 4
#define kctPatchExit 5
#define kctInjectConditionalBranch 6
#define kctInjectConditionalCall 7


#define kmIdentifier(key, counter) \
Expand Down Expand Up @@ -80,6 +82,11 @@ struct _kmHook_4ui_2f_t { unsigned int a; unsigned int b; unsigned int c; unsign
#define kmBranch(addr, ptr) kmHook2(kctInjectBranch, (addr), (ptr))
#define kmCall(addr, ptr) kmHook2(kctInjectCall, (addr), (ptr))

// kmCondBranch, kmCondCall
// Set up a branch from a specific instruction to a specific address, conditionally
#define kmCondBranch(addr, original, ptr) kmHook3(kctInjectConditionalBranch, (addr), (ptr), (original))
#define kmCondCall(addr, original, ptr) kmHook3(kctInjectConditionalCall, (addr), (ptr), (original))

// kmBranchDefCpp, kmBranchDefAsm
// Set up a branch (b) from a specific instruction to a function defined
// directly underneath. If exitPoint is not NULL, the function will
Expand Down
11 changes: 11 additions & 0 deletions k_stdlib/kamek_asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ kctConditionalWrite .equ 2
kctInjectBranch .equ 3
kctInjectCall .equ 4
kctPatchExit .equ 5
kctInjectConditionalBranch .equ 6
kctInjectConditionalCall .equ 7

// general hook definition macros
kmHook0: .macro type
Expand Down Expand Up @@ -116,6 +118,15 @@ kmCall: .macro addr, ptr
kmHook2 kctInjectCall, addr, ptr
.endm

// kmCondBranch, kmCondCall
// Set up a branch from a specific instruction to a specific address, conditionally
kmCondBranch: .macro addr, original, ptr
kmHook3 kctInjectConditionalBranch, addr, ptr, original
.endm
kmCondCall: .macro addr, original, ptr
kmHook3 kctInjectConditionalCall, addr, ptr, original
.endm

// kmBranchDef
// Set up a branch (b) from a specific instruction to a function defined
// directly underneath.
Expand Down
18 changes: 18 additions & 0 deletions kamekfile.bt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ if (first8 == 0x4b616d656b000001) { // "Kamek", version 1
version = 1;
} else if (first8 == 0x4b616d656b000002) { // "Kamek", version 2
version = 2;
} else if (first8 == 0x4b616d656b000003) { // "Kamek", version 3
version = 3;
} else {
Warning("Not a Kamekfile.");
return;
Expand All @@ -59,6 +61,8 @@ enum <uint8> CommandType {
kCondWrite8 = 38,
kBranch = 64,
kBranchLink = 65,
kCondBranch = 66, // added in version >= 3
kCondBranchLink = 67, // added in version >= 3
};


Expand Down Expand Up @@ -217,6 +221,20 @@ typedef struct {
valueStr = Str("kmCall(%s, %s)",
readDestAddress(dest), readSrcAddress(src, kAddr32));
break;
case kCondBranch:
SrcAddress src <name="Src. Address", read=readSrcAddress(this, kRel24)>;
uint32 original <name="Original", read=formatHex>;
// intentionally using kAddr32 instead of kRel24 below
valueStr = Str("kmCondBranch(%s, %s, %s)",
readDestAddress(dest), formatHex(original), readSrcAddress(src, kAddr32));
break;
case kCondBranchLink:
SrcAddress src <name="Src. Address", read=readSrcAddress(this, kRel24)>;
uint32 original <name="Original", read=formatHex>;
// intentionally using kAddr32 instead of kRel24 below
valueStr = Str("kmCondCall(%s, %s, %s)",
readDestAddress(dest), formatHex(original), readSrcAddress(src, kAddr32));
break;
default:
break;
}
Expand Down
18 changes: 17 additions & 1 deletion loader/kamekLoader.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "kamekLoader.h"

#define KM_FILE_VERSION 2
#define KM_FILE_VERSION 3
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)

Expand Down Expand Up @@ -39,6 +39,8 @@ struct DVDHandle
#define kCondWrite8 38
#define kBranch 64
#define kBranchLink 65
#define kCondBranch 66
#define kCondBranchLink 67


void kamekError(const loaderFunctions *funcs, const char *str)
Expand Down Expand Up @@ -141,6 +143,18 @@ kCommandHandler(BranchLink) {
*(u32 *)address = 0x48000001;
return kHandleRel24(input, text, address);
}
kCommandHandler(CondBranch) {
u32 original = ((const u32 *)input)[1];
if (*(u32 *)address == original)
kHandleBranch(input, text, address);
return input + 8;
}
kCommandHandler(CondBranchLink) {
u32 original = ((const u32 *)input)[1];
if (*(u32 *)address == original)
kHandleBranchLink(input, text, address);
return input + 8;
}


inline void cacheInvalidateAddress(u32 address) {
Expand Down Expand Up @@ -220,6 +234,8 @@ void loadKamekBinary(const loaderFunctions *funcs, const void *binary, u32 binar
kDispatchCommand(CondWrite8);
kDispatchCommand(Branch);
kDispatchCommand(BranchLink);
kDispatchCommand(CondBranch);
kDispatchCommand(CondBranchLink);
default:
funcs->OSReport("Unknown command: %d\n", cmd);
}
Expand Down