Re: "Void Knowledge Archive, by the members of HallofOrigin"
Posted by: Stackout
Date: 2015-09-26 18:09:41
Has anyone bothered doing major reversing of gen4 yet, both static analysis and debugging (setting in-memory breakpoints etc)?
Glitch City Laboratories closed on 1 September 2020 (announcement). This is an archived copy of a thread from Glitch City Laboratories Forums.
You can join Glitch City Research Institute to ask questions or discuss current developments.
You may also download the archive of this forum in .tar.gz, .sql.gz, or .sqlite.gz formats.
I guess that gen4 has a rudimentary ASLR, like gen3 does.
Has anyone bothered doing major reversing of gen4 yet, both static analysis and debugging (setting in-memory breakpoints etc)?
;we start out with offset=0, obviously
94000130 FCFF0000 ;if we are holding L+R:
B21C4D28 00000000 ;offset = read(offset + 0x21C4D28)
B0000004 00000000 ;offset = read(offset + 0x4)
100014A4 000001FE ;write(offset+0x14A4,0x1FE) (0x1FE = dec 510 = HoO map id)
10002866 0000012F ;write(offset+0x2866,0x12F) (x coordinate)
1000286A 000002F5 ;write(offset+0x286A,0x2F5) (y coordinate)
1000286E 00000002 ;write(offset+0x286E,0x2) (z coordinate)
D2000000 00000000 ;terminate code (reset offset to 0)local function whereami()
local aslr_data_ptr = memory.readdword(0x021C4D28)
local mapdata_ptr = memory.readdword(aslr_data_ptr + 0x4)
local m = memory.readword(mapdata_ptr + 0x14A4)
local x = memory.readword(mapdata_ptr + 0x2866)
local y = memory.readword(mapdata_ptr + 0x286A)
local z = memory.readword(mapdata_ptr + 0x286E)
gui.text(1,184,string.format("Matrix: %d, X: %d, Y: %d, Z: %d",m,x,y,z))
end
gui.register(whereami)
This means that Arceus might still be possible to find in the Void! If we happen to stumble upon an invalid map ID that the game considers to be HoO (just as many invalid OW map IDs are considered Jubilifes) in the void, and if this map contains at least one script (e.g. an NPC), then we can trigger a battle with Arceus!
EDIT: well, that was easy… here's a lua script showing your current map id and x,y,z coordinates (PEARL ONLY, probably also works on Diamond? probably won't work on PT/HG/SS):local function whereami()
local aslr_data_ptr = memory.readdword(0x021C4D28)
local mapdata_ptr = memory.readdword(aslr_data_ptr + 0x4)
local m = memory.readword(mapdata_ptr + 0x14A4)
local x = memory.readword(mapdata_ptr + 0x2866)
local y = memory.readword(mapdata_ptr + 0x286A)
local z = memory.readword(mapdata_ptr + 0x286E)
gui.text(1,184,string.format("Matrix: %d, X: %d, Y: %d, Z: %d",m,x,y,z))
end
gui.register(whereami)
it would be really really really awesome if someone could extend this to show the id of the map to your left, right, top, and bottom, and how many steps away those are… );
This doesn't really add up to me. To have the Arceus script loaded, you need to be in the real HoO. To run the script, you need an event (npc or etc) set to run the script.
The effects you're getting sound like they're from being "In" the real HoO (at least partially loading it) but having the rest of the data loaded from another map.
I'm not sure if that can be done through the void. The only scripts I've seen run are the ones that start immediately on entering the map (Hall of Fame, Cynthia battle, getting kicked out of the restaurant at night, receiving the Pal Pad) because while in the void there aren't any npcs/signs/etc to trigger other scripts. Once you save, backtrack and enter the map you're actually in that map with npc data and all, so there's no different person to talk to to trigger the script.
That said, I'd love to be proven wrong. Perhaps if a Hall of Origin void can be found directly next to a real place, a tweak could be used to displace it, so you can be "In" it and talk to an npc from that real place. I don't like the odds of that showing up though, and I'm not sure if any tweaks really work like that.This is an interesting thought. I guess in terms of realism it's no less hopeful than my ideas ;D
On the other hand the coordinates only change when I save, so I don't think they're reading the right thing?Indeed! I've fixed x and y, but I wasn't sure where to find z. I've searched around and found six possible candidate RAM locations, and I picked the one closest by, but if my z reading appears off, it probably is ;)
local function whereami()
local aslr_data_ptr = memory.readdword(0x021C4D28)
local mapdata_ptr = memory.readdword(aslr_data_ptr + 0x4)
local m = memory.readword(mapdata_ptr + 0x14A4)
local x = memory.readword(mapdata_ptr + 0x14A4 + 8)
local y = memory.readword(mapdata_ptr + 0x14A4 + 12)
local z = memory.readword(mapdata_ptr + 0x14A4 + 0x6C99A)
gui.text(1,184,string.format("Matrix: %d, X: %d, Y: %d, Z: %d",m,x,y,z))
end
gui.register(whereami)
That said, I'd love to be proven wrong. Perhaps if a Hall of Origin void can be found directly next to a real place, a tweak could be used to displace it, so you can be "In" it and talk to an npc from that real place. I don't like the odds of that showing up though, and I'm not sure if any tweaks really work like that.
Nice going, you two ;)
That said, I'd love to be proven wrong. Perhaps if a Hall of Origin void can be found directly next to a real place, a tweak could be used to displace it, so you can be "In" it and talk to an npc from that real place. I don't like the odds of that showing up though, and I'm not sure if any tweaks really work like that.
When you mean "real place", are you referring to an actual location or a void area with the same map ID as said location?
EDIT: Figured something out. Walk into the Jubilife pokemon center, save your game, then use pokesav to change your map coordinates to 510,6,9,0. When you reload, you'll be in the HoO but with the Jubilife pokemon center's NPCs. Talk to the little girl in front of you to trigger a battle with Arceus. Now change your coordinates to 510,13,5,0. You're in front of a different person now, yet talking to him will also trigger Arceus.
This means that more than just one single script can trigger Arceus (but not all of them, e.g. Nurse Joy doesn't say 'Dodogyuuun' now :P ). Given that I just demonstrated that you keep the NPCs from the last map you save in, we will for sure trigger a battle with Arceus if, starting from a void area X, we manage to - without saving and resetting - find a HoO void when our x and y positions are at values that put us next to NPCs on our original map X.
Script #1
CheckFlag 0x8E
CompareLastResultJump 0x1 Function_#1
End
Script #2
LockAll
SetVar 0x4118 0x0
Call Function_#2
Call Function_#3
PlayCry 0x1ED 0x0
WaitCry
Call Function_#8
PlayCry 0x1ED 0x0
Message 0x0
CloseMessageOnKeyPress
ClearFlag 0x8E
WildBattle2 0x1ED 0x50
SetFlag 0x8E
CheckLost 0x800C
If 0x800C 0x0
CompareLastResultJump 0x1 Function_#9
CheckWildBattle2 0x800C
If 0x800C 0x1
CompareLastResultJump 0x1 Function_#10
ClearFlag 0x11E
ReleaseAll
End
Script #3
End
Script #4
UseScript_#2Script #1
EndLevel Scripts File
If only we knew where the 'next map id' bits were stored in memory, we might be able to write a lua script that determines whether the HoO is reachable from any given position…
When you mean "real place", are you referring to an actual location or a void area with the same map ID as said location?
Here's yet another real Hall of Origin void which may or may not be the same one I found earlier. I only went west and south of 319W this time around.
[Mystery Z][Veilstone][Veilstone][Floaroma ]
[Veilstone][Floaroma ][Floaroma ][Poketch ]
[Floaroma ][Poketch ][Poketch ][Mystery Z]
[Floaroma ][Poketch ][POKETCH ][Mystery Z]
[Poketch ][Mystery Z][Mystery Z][Mystery Z]
. . . . . . . . . . . . . . . . . . . . . . . . . . . . [Iron Island][SolaceonRuin][SolaceonRuin][Iron Island]
. . . . . . . . . . . . . . . . . . . . . . . . . . . . [Iron Island][HallOfOrigin][HallOfOrigin][HallOfOrigin]
[Mystery Zone][Mystery Zone][SolaceonRuin][Iron Island][HallOfOrigin][HallOfOrigin][HALLOFORIGIN][HALLOFORIGIN]
[SolaceonRuin][Iron Island][HallOfOrigin][HallOfOrigin][HallOfOrigin][HallOfOrigin][HALLOFORIGIN][HALLOFORIGIN]
[Iron Island][HallOfOrigin][HallOfOrigin][HallOfOrigin]
[HallOfOrigin][HallOfOrigin][Mystery Zone]
As you can see script 2 involves playing a cry and starting a battle, this would be the Arceus fight. All script 4 does is call script 2, which is nice since it doubles our chances of making this work.Well, that explains that then! So we do need to find the correct NPC, which is either #2 or #4.
In the event viewer, the HoO has 2 events. One overworld (Arceus) who is set to script 0 and one trigger set to script 2. The trigger has a flag associated - this probably means it only appears when that flag is set, that flag being the event? It's 16664 if anyone knows.From my very small experience trying to hack Pokemon Ruby a decade ago, yes, you're probably right that 16664 is the flag it checks for to see whether you have the Azure Flute etc.
Alternately we could brute force it with a script that changes our X and/or Y by 32 at a time and records the results. (Maybe 31 at a time, then taking one step to make sure it's loading things correctly?).You're a genius, and your observation on the settability of x and y is correct. You inspired me to change my lua script to the following - excuse the messiness, and note that I've only looked at the x id, though the y id should be 8 bytes after the x id's location in all cases:
(One note, I don't think the x and y that the new script fetches are settable? I'm not too experienced with RAM so I might be doing something wrong. The simplest way to do it manually seemed to be set a cheat then disable it, but the value reverts. Is this just a RAM mirror?)
function whereami()
aslr_data_ptr = memory.readdword(0x021C4D28)
mapdata_ptr = memory.readdword(aslr_data_ptr + 0x4)
m = memory.readword(mapdata_ptr + 0x14A4)
x = memory.readword(mapdata_ptr + 0x14A4 + 8)
y = memory.readword(mapdata_ptr + 0x14A4 + 12)
z = memory.readword(mapdata_ptr + 0x14A4 + 0x6C99A)
all_x = {0x226E7AC, 0x2291DE4, 0x2291DF0, 0x2291DFE,0x2333286,0x2333292,0x23332BA,0x2333E0A4}
for i = 1,8 do
-- The values above were taken from my own save state of Pearl. As such, we need to subtract my own map_data_ptr from them, and add the current session's:
all_x[i] = all_x[i] - 0x226D300 + mapdata_ptr
end
gui.text(1,104,string.format("Matrix: %d, X: %d, Y: %d, Z: %d",m,x,y,z))
gui.text(0,114,string.format("ASLR data: 0x%X / map data: 0x%X",aslr_data_ptr,mapdata_ptr))
gui.text(0,124,string.format("X: %d %d %d %d %d %d %d %d",memory.readword(all_x[1]),memory.readword(all_x[2]),memory.readword(all_x[3]),memory.readword(all_x[4]),memory.readword(all_x[5]),memory.readword(all_x[6]),memory.readword(all_x[7]),memory.readword(all_x[8])))
--force_x = 561+0
--memory.writeword(all_x[1],force_x) -- if this one is different from the others, the save file will be corrupted; if it is forced during save loading, the game will immediately crash when the save is loaded. This seems to actually move you collision-wise, but not visually.
--memory.writeword(all_x[2],force_x) -- the same; also if this one is forced, the save load screen will display a glitched white block. Does nothing live - perhaps this is the saved x position only?
--memory.writeword(all_x[3],force_x) -- changes map ID!!! but only live, not when loading save. Can be used to save inside a MZ (open start menu, change value to warp you into MZ), with bizarre results.
--memory.writeword(all_x[4],force_x) -- immediately snaps you into place, but without changing map id. Is saved when you save the game only if forced DURING the save, otherwise your new position is NOT saved. Interestingly, THIS IS ONLY GRAPHICAL
--memory.writeword(all_x[5],force_x) -- seems related to graphics loading
--memory.writeword(all_x[6],force_x) -- this one...
--memory.writeword(all_x[7],force_x) -- ...and this one are related; if one of them is flipped, the screen goes black and the other one starts counting like a madman. Counting resets when reloading the graphics. When forcing these to be set and taking a step, the perspective twists, showing this is contorlling the camera.
--memory.writeword(all_x[8],force_x) -- unknown?
end
gui.register(whereami)In other words, we can read out what the next map ID is going to be by changing our x to x+32, reading the matrix that gives us, and going back. I don't really have time to code this, but it seems a good PoC would be if we could 'virtually' travel to Darkrai by manipulating this value in increments of 32 and to see if this indeed replicates the path to Darkrai we actually observe ourselves taking. Just a thought.
Well, I asked for clarification because I have already reached a real Hall of Origin void (I posted about it earlier). But the voids surrounding it are the same ones bordering the Hall of Origin itself.
EDIT: Here's yet another real Hall of Origin void which may or may not be the same one I found earlier. I only went west and south of 319W this time around.
[img width=545]https://i.imgur.com/qlHHS4u.png[/img]
To clarify, when I said real/fake HoO earlier I meant 510/511. That was a silly choice of words when real/fake Sinnoh are a thing, sorry :P
The Hall of Origin loads script file 232, which is:Script #1
CheckFlag 0x8E
CompareLastResultJump 0x1 Function_#1
End
Script #2
LockAll
SetVar 0x4118 0x0
Call Function_#2
Call Function_#3
PlayCry 0x1ED 0x0
WaitCry
Call Function_#8
PlayCry 0x1ED 0x0
Message 0x0
CloseMessageOnKeyPress
ClearFlag 0x8E
WildBattle2 0x1ED 0x50
SetFlag 0x8E
CheckLost 0x800C
If 0x800C 0x0
CompareLastResultJump 0x1 Function_#9
CheckWildBattle2 0x800C
If 0x800C 0x1
CompareLastResultJump 0x1 Function_#10
ClearFlag 0x11E
ReleaseAll
End
Script #3
End
Script #4
UseScript_#2
There are also functions and movements as part of this script file, but they're less telling so I'll leave them out.
As you can see script 2 involves playing a cry and starting a battle, this would be the Arceus fight. All script 4 does is call script 2, which is nice since it doubles our chances of making this work.
In the event viewer, the HoO has 2 events. One overworld (Arceus) who is set to script 0 and one trigger set to script 2. The trigger has a flag associated - this probably means it only appears when that flag is set, that flag being the event? It's 16664 if anyone knows.
A HoO void next to (or 1 matrix space/32 tiles away from, or other distances useful in tweaking?) the loaded building that is the entry point. Thinking further on this, I don't think tweaking affects the map like that though. It moves/clears the terrain and movement permissions which are part of the map file itself, rather than scripts and events which are part of the map header. The only useful thing that stands out to me there is map re-entry without a walk anywhere code.
[…]
The areas in the void for repeat to the North-East and South-West, or directly West where X underflows, North where Y underflows. The size of the matrix… might affect this? I kind of thought it did but now I'm not sure. The Poketch matrix is 1x1, and it's void looks like this in my save (allcaps is the Poketch that I entered from, with graphics and etc.):
If you're on the Hall of Origin void, the areas are just repeating diagonally. It's explained on the doc.
Going 32 steps north from an area and then going east will result in you getting to the same area. The amount of steps required varies on the void. Sinnoh requires 960 steps east before you reach the same area while indoor areas require 32.