CG8516 / PvZA11y

An accessibility mod for Plants VS Zombies
MIT License
31 stars 11 forks source link

Inquiry Regarding Reverse Engineering Approaches #55

Open dellbeat opened 3 months ago

dellbeat commented 3 months ago

Hello, I saw this module developed by you on a webpage, and I would like to try to extend its support for version 1.0.0.1051. I have been searching for some information in the past few days, which can help me implement the features needed in the module. However, I haven't found some scenarios yet, such as the basic information (like handle, width, height, etc.) of the "NEXT LEVEL" button within the scene after passing the level.

Since I don't have much experience in reverse engineering, I would like to inquire whether the offset information you previously summarized was obtained through your own reverse engineering. If so, I would like to know how to obtain the button information within the scene. I hope you can provide a general idea, and I will try to explore it myself. Thank you!

CG8516 commented 3 months ago

Hi! I'm happy to hear you're interested in this project :) Sorry the code's such a mess, I was planning on moving all of the pointerchains/offsets to the Pointer.cs file, but never got around to it.

I'm not sure how much you know about reverse engineering or programming in general, let me know if there's anything I haven't explained in enough detail.

I primarily used cheat engine to find memory addresses, pointerchains, etc... If you haven't spent much time reverse engineering, I would highly recommend completing cheat-engine's built-in tutorial.

I also use ghidra for reverse-engineering, but I think I spent most of my time in cheat engine for this project, as I find it easier to just mess around with values and see what happens within the game.

Both supported versions use "lawnApp,878,a8" for the pointerchain to the 'next level' button. Structs don't typically have drastic changes, so if that pointerchain doesn't work for you, it'd probably be something similar like "lawnApp,87c,b0"

You should mainly be looking for where the classes/structs are stored in the game's memory. There's some very large classes like lawnapp, which is sort of like a manager for the entire game. Then there's other big classes like the board, which contains the state of all the plants/zombies on the board, etc..

But you can trace almost everything back up to the lawnapp class, as it contains references to most of the game components, so finding a stable pointerchain to lawnapp should be your #1 priority.

Once you find a decent lawnapp pointerchain, you can access almost everything in the game with just offsets from that memory address. For example, the board state will typically be stored at "lawnapp,868", which is a pointer that is located 0x868 bytes into the lawnapp class.

If I recall correctly, I think I used the player's sun value to find the board pointer. You can scan for the sun's value in memory, place a plant, rescan, collect sun, rescan, etc.. until you find the exact address.

Once you find the sun's value in memory, you can use the "find out what accesses this address" function in cheat engine, and it will show you what offset the sun value is within the board pointer. eg, it might have an instruction that accesses 'eax+5578' (might look different, but should be similar).

From that, you can assume that 'eax' is a big struct that contains a lot of values (at least up to 0x5578). In this case, eax would be the address where the current board information is stored.

Then you can copy whatever address eax was set to, and found out what writes to that address. You might see an instruction that accesses something like 'ecx+868' In that case, ecx will be the lawnapp struct. And the value at offset 0x868 is a pointer to the board.

So your lawnapp class would look like this: 0x00 - 0x867 : Unknown Data 0x868 : BoardPointer

Then your board struct would be 0x00 - 0x5577 : Unknown Data 0x5578 : Sun value

Cheat engine has a "dissect data/structure" tool, which is really useful for exploring classes/structs. If you open the 'lawnapp' address in the structure dissect tool, you should see some version info / copyright text somewhere near the start of the class.

From the structure dissect tool, you can navigate through pointer chains, and see what each class/struct contains. Sometimes cheat engine will be wrong with its guesses, so you might have to explicitly set an address type to a float for example. But in general, it does a pretty decent job. Sometimes cheat engine will also be able to automatically detect class names within the structure dissect tool, which is very helpful when you're first exploring.

To find things like the current widget, you just want to keep the structure dissector open on the lawnapp address, and look for changes as you open/close menus. Then you can expand those address which change when you open/close menus, and look for values that change when you drag the in-game menus around, they'll probably be for your menu position. Try changing those values from within cheat engine and see if it makes the menu move.

The whole process takes a lot of time and experimentation. It can be hard when you're first starting, but it gets a lot easier with time.

I hope that helps :) Let me know if you have any specific questions, and I'll try to answer them as best as I can.

dellbeat commented 3 months ago

Sure, I'm glad to see such a detailed response from you. I will use the suggestions you provided as references for my actions in the future. In fact, a few days ago, I managed to locate the values related to sunlight, and subsequently, I located the offset of the main program.

However, when it comes to buttons, because their text does not change (the only time it changes is when the button has been destroyed), it is not as easy to locate (or it may be because I lack sufficient knowledge and haven't learned enough).

Anyway, I am still very grateful for your detailed answer. I will try to gradually advance this project (but I cannot make any promises). I hope you have a pleasant life!

CG8516 commented 3 months ago

No problem! I'm glad to hear you were already doing quite well so far :)

In that case, I would recommend getting started with cheat engine's structure dissect tool if you haven't already tried it before. It makes it much easier to search for useful data from each memory address. From the lawnapp pointer, you'll be looking for the offset to the current dialogue/widget. That should contain a pointer for the button, which will contain the button's position and size data.

Best of luck!

dellbeat commented 3 months ago

Sure, I'll explore it myself. If I have any questions or progress, I'll bring them up in the issue. Thanks for your response :)

dellbeat commented 3 months ago

Hello, recently I've been experimenting with reverse engineering PVZ and have made some progress. However, I've encountered a new issue and would like to seek your advice on how to approach it. It would be great if you could help me out.

Currently, I need to determine if the main interface has specific buttons (like Garden, Almanac, and Shop). From the existing publicly available documentation (1051), I haven't found any information on such content. I've found an original function table where there's a function defined as follows: Address: 448CB0 Function: GameSelector::GameSelector(LawnApp theApp, GameSelector this) Return value: Affects register: eax = *this; ecx, edx Description: Initializes the main menu interface and synchronizes with saved data. Requires a memory region of size 0x130 to construct this.

Could I use this function to infer the data structure of type GameSelector? I've tried using IDA to examine the assembly code, but due to limited experience in reverse engineering, I'm uncertain about type assignments within assembly and if it's similar to .NET.

Additionally, as I was writing this request for help, I discovered another function that controls the visibility of the specific buttons mentioned earlier. I'll attempt to work on it myself for now, but I would appreciate some guidance on the approach. This way, if I encounter similar scenarios in the future, I can handle them independently.

Looking forward to your reply and thank you in advance!

CG8516 commented 3 months ago

Hello!
I don't believe I bothered trying to decompile that part of the code.
I'm not even sure if I fully reverse-engineered the GameSelector struct.
It's been a while, and I don't have my notes with me to check, but I mostly reverse-engineered the bare minimum required to get the mod working.

For those buttons, I just read the save data and did some basic checks to determine if they should be visible or not.
Here's the code for that

Maybe it's not the best solution, but it was good enough for what I needed to do.
Sorry if this doesn't help you much

dellbeat commented 3 months ago

Alright, these things aren't in the 1051 version's save file, but since I've found a new method, I'll give it another try. Anyway, thanks a lot for taking the time to answer. Thank you!

CG8516 commented 3 months ago

Are you sure? They might have moved, but I doubt they aren't in the save file.
The game has to save what level you're on, and it has to save whether or not you have the sunflower trophy.

I don't believe there was ever a value storing whether or not the store/garden/almanac are unlocked, you just calculate it by reading what level you've reached, and how many times adventure mode has been completed.

Not trying to discourage you from trying a different method, I just think this way would be a lot easier for you.
Good luck either way :)

dellbeat commented 3 months ago

Yes, you're right. It might just be that these details weren't listed in the public documentation. I'll try to look into the save file section for this. Thanks for the reminder XD