Thank you, patjenova. Your code helped me understand Khuong’s old crafting recipe code more correctly.Here it is for 1.23.0
[00# Mastercode Get Craft 0xB4BFF00 (Select First Item)]
040A0000 08CB4F04 F0014053
040A0000 08CB4F08 F9078262
040A0000 08CB4F0C AA0203F3
040A0000 08CB4F10 1706704E
040A0000 04E51044 14F98FB0
And this is an example code you could try.
[(ZL) Change Selected Crafting Recipe To DreamSnaps Decor Reward]
580F0000 0B4BFF00
580F1000 00000040
780F0000 00000018
640F1000 00000000 01DF2085
I need to correct my earlier understanding. I was mainly looking at the pointer-code part of Khuong’s old code, and I missed that the Master Code also had the part that saves the recipe pointer into a scratch slot. After reading your v1.23.0 code, I realized that Khuong’s old code was not simply a normal pointer chain. It was also using a hook-assisted saved pointer mechanism. So I think your code is best understood as a proper v1.23.0 update of Khuong’s old crafting recipe code.
Both versions use the same basic idea:
Code:
hook Meta.CraftWithRecipe$$GetRelevantInventory
save CraftingRecipeItemData* to a scratch slot
read that saved pointer with Atmosphère pointer code
edit recipe->result_->itemID_
The v1.23.0 master code is:
Each `040A0000` line writes one 32-bit ARM64 instruction to `main + offset`.
So these lines:
write this small code cave:
And this line:
patches the original function at:
The original instruction there is:
After the patch, it becomes:
So the game jumps to the code cave.
The hooked function is:
In v1.23.0, the relevant argument is `recipeData`, which is a `CraftingRecipeItemData*`.
For an IL2CPP instance method on ARM64, the native register layout is usually like this:
So in this case, `X2` appears to be:
This also matches the original function. The game does:
and later reads:
`CraftingRecipeItemData + 0x48` is `ingredients_`, so `X19` is being used as the recipe object.
Now the code cave:
`ADRP` loads a 4KB page address into a register. Here it loads:
ARM64 often builds an address as `page + offset`, because a full 64-bit absolute address usually cannot be used directly in a normal load/store instruction.
This stores the 64-bit value in `X2` to:
In main-offset form:
So this line saves the current `CraftingRecipeItemData*` pointer to:
Then:
restores the original instruction that was replaced by the hook.
Finally:
returns to the original function, immediately after the replaced instruction.
So the code cave is basically:
The segment table is important here:
The code cave is placed at:
This is inside the `.text` segment and before `.rodata` starts at:
Since `.text` has execute permission, unused padding near the end of `.text` can be used as an ASM code cave.
The saved pointer slot is:
This is after `.bss` ends:
and before Imports starts:
So it is in the padding area after `.bss` and before Imports. More importantly, it is still in the same 4KB page as the end of `.bss`:
Since `.bss` is writable, this page is likely writable at runtime. That makes `0x710B4BFF00` suitable as a small 8-byte scratch slot for saving a pointer.
So the address usage is:
After the master code saves the recipe pointer, the example code can use it:
Line by line:
This loads a 64-bit pointer from:
into Atmosphère Cheat VM register `R15`.
Because the master code saved `recipeData` there, this gives:
Next:
This dereferences:
`CraftingRecipeItemData + 0x40` is `result_`, so now:
Next:
This adds `0x18`:
`ResultInstance + 0x18` is `itemID_`, so now `R15` points to:
Finally:
writes the 32-bit value `0x01DF2085` to the address in `R15`.
So the pointer code is basically:
The object path is:
This is the same basic design as Khuong’s old v1.8.6 code.
Khuong’s old code saved the recipe pointer to:
and then read it from the crafting recipe code.
Your v1.23.0 code saves the recipe pointer to:
and then reads it from the new pointer code.
So the mechanism is:
One small note: the posted example title says `(ZL)`, but the shown pointer-code lines do not include the usual button wrapper. If it should only run while holding ZL, it would normally need:
Code:
[00# Mastercode Get Craft 0xB4BFF00 (Select First Item)]
040A0000 08CB4F04 F0014053
040A0000 08CB4F08 F9078262
040A0000 08CB4F0C AA0203F3
040A0000 08CB4F10 1706704E
040A0000 04E51044 14F98FB0
Each `040A0000` line writes one 32-bit ARM64 instruction to `main + offset`.
So these lines:
Code:
040A0000 08CB4F04 F0014053
040A0000 08CB4F08 F9078262
040A0000 08CB4F0C AA0203F3
040A0000 08CB4F10 1706704E
write this small code cave:
Code:
0x7108CB4F04: ADRP X19, #0x710B4BF000
0x7108CB4F08: STR X2, [X19,#0xF00]
0x7108CB4F0C: MOV X19, X2
0x7108CB4F10: B 0x7104E51048
And this line:
Code:
040A0000 04E51044 14F98FB0
patches the original function at:
Code:
0x7104E51044
The original instruction there is:
Code:
0x7104E51044: MOV X19, X2
After the patch, it becomes:
Code:
0x7104E51044: B 0x7108CB4F04
So the game jumps to the code cave.
The hooked function is:
Code:
Meta.CraftWithRecipe$$GetRelevantInventory
In v1.23.0, the relevant argument is `recipeData`, which is a `CraftingRecipeItemData*`.
For an IL2CPP instance method on ARM64, the native register layout is usually like this:
Code:
X0 = this
X1 = first normal argument
X2 = second normal argument
X3 = MethodInfo
So in this case, `X2` appears to be:
Code:
CraftingRecipeItemData* recipeData
This also matches the original function. The game does:
Code:
MOV X19, X2
and later reads:
Code:
LDR X8, [X19,#0x48]
`CraftingRecipeItemData + 0x48` is `ingredients_`, so `X19` is being used as the recipe object.
Now the code cave:
Code:
ADRP X19, #0x710B4BF000
`ADRP` loads a 4KB page address into a register. Here it loads:
Code:
X19 = 0x710B4BF000
ARM64 often builds an address as `page + offset`, because a full 64-bit absolute address usually cannot be used directly in a normal load/store instruction.
Code:
STR X2, [X19,#0xF00]
This stores the 64-bit value in `X2` to:
Code:
0x710B4BF000 + 0xF00 = 0x710B4BFF00
In main-offset form:
Code:
0x710B4BFF00 = main + 0x0B4BFF00
So this line saves the current `CraftingRecipeItemData*` pointer to:
Code:
main + 0x0B4BFF00
Then:
Code:
MOV X19, X2
restores the original instruction that was replaced by the hook.
Finally:
Code:
B 0x7104E51048
returns to the original function, immediately after the replaced instruction.
So the code cave is basically:
Code:
*(u64 *)(main + 0x0B4BFF00) = recipeData;
X19 = recipeData;
return to the original function;
The segment table is important here:
Code:
Name Start End Permission
.text 0x7100000000 0x7108CB5000 R-X
.rodata 0x7108CB5000 0x710966E958 R--
.data 0x710A4FC000 0x710B1014C0 RW-
.bss 0x710B1014C0 0x710B4BF138 RW-
Imports 0x710B4C0008 0x710B4C17A8 R--
The code cave is placed at:
Code:
0x7108CB4F04
This is inside the `.text` segment and before `.rodata` starts at:
Code:
0x7108CB5000
Since `.text` has execute permission, unused padding near the end of `.text` can be used as an ASM code cave.
The saved pointer slot is:
Code:
0x710B4BFF00
This is after `.bss` ends:
Code:
0x710B4BF138
and before Imports starts:
Code:
0x710B4C0008
So it is in the padding area after `.bss` and before Imports. More importantly, it is still in the same 4KB page as the end of `.bss`:
Code:
0x710B4BF000 - 0x710B4BFFFF
Since `.bss` is writable, this page is likely writable at runtime. That makes `0x710B4BFF00` suitable as a small 8-byte scratch slot for saving a pointer.
So the address usage is:
Code:
0x7108CB4F04 -> executable .text padding, used as ASM code cave
0x710B4BFF00 -> writable .bss-end padding, used as saved pointer storage
After the master code saves the recipe pointer, the example code can use it:
Code:
[(ZL) Change Selected Crafting Recipe To DreamSnaps Decor Reward]
580F0000 0B4BFF00
580F1000 00000040
780F0000 00000018
640F1000 00000000 01DF2085
Line by line:
Code:
580F0000 0B4BFF00
This loads a 64-bit pointer from:
Code:
main + 0x0B4BFF00
into Atmosphère Cheat VM register `R15`.
Because the master code saved `recipeData` there, this gives:
Code:
R15 = CraftingRecipeItemData*
Next:
Code:
580F1000 00000040
This dereferences:
Code:
R15 = *(u64 *)(R15 + 0x40)
`CraftingRecipeItemData + 0x40` is `result_`, so now:
Code:
R15 = ResultInstance*
Next:
Code:
780F0000 00000018
This adds `0x18`:
Code:
R15 = R15 + 0x18
`ResultInstance + 0x18` is `itemID_`, so now `R15` points to:
Code:
&result_->itemID_
Finally:
Code:
640F1000 00000000 01DF2085
writes the 32-bit value `0x01DF2085` to the address in `R15`.
So the pointer code is basically:
Code:
recipe = *(CraftingRecipeItemData **)(main + 0x0B4BFF00);
result = recipe->result_; // +0x40
result->itemID_ = 0x01DF2085; // +0x18
The object path is:
Code:
CraftingRecipeItemData + 0x40 -> result_
ResultInstance + 0x18 -> itemID_
This is the same basic design as Khuong’s old v1.8.6 code.
Khuong’s old code saved the recipe pointer to:
Code:
main + 0x0B9B0F08
and then read it from the crafting recipe code.
Your v1.23.0 code saves the recipe pointer to:
Code:
main + 0x0B4BFF00
and then reads it from the new pointer code.
So the mechanism is:
Code:
hook GetRelevantInventory
save CraftingRecipeItemData* to scratch slot
read saved pointer with Atmosphère pointer code
edit recipe->result_->itemID_
One small note: the posted example title says `(ZL)`, but the shown pointer-code lines do not include the usual button wrapper. If it should only run while holding ZL, it would normally need:
Code:
80000100
...
20000000
As far as I can tell, patjenova is one of the most active cheat-code contributors on GBAtemp. His codes often show techniques that seem to come from many years of studying, updating, and reverse engineering different cheat-code patterns. In that sense, his codes are probably much better learning material than my own codes, since I usually only make and share practical, game-specific codes for the games I personally play.




, too much tenacity.... I can sometimes get that stupid festive fish to inventory then the game crashes. I'm no good with pointer codes, but my result seems like it was probably way too many lines for what it needed to be. I'll be seeing what I can do with what you posted now. You're far better than I am, so I will probably have more success now using your code as a template. Awesome! Thanks for the assistance!



