Disney Dreamlight Valley [0100D39012C1A000]

  • Thread starter Thread starter morarin
  • Start date Start date
  • Views Views 33,594
  • Replies Replies 200
  • Likes Likes 33
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
Thank you, patjenova. Your code helped me understand Khuong’s old crafting recipe code more correctly.

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:

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.
 
  • Like
Reactions: weilai

Site & Scene News

Popular threads in this forum