Hello, here's my weekly update. Lots of nerd talk so brace yourselves lol. Skip to the end if you just want the too long didn't read.
Thank You to Testers
First off, thank you to @Fern, @Apodemus, Jay, and @finnethen for helping me test the API this past week. With their feedback, I was able to rewrite some of the code to make improvements and narrow down what operating systems were having issues. In the end, I was able to bring down the PC requirements for it to run from Windows 10/11 down to Windows XP and above.
Second, welcome to @finnethen to the project. Turns out he was working on a similar project so we decided to trade notes and resources. It's been awesome bouncing ideas off of someone who is familiar with reverse engineering and assembly instructions.
Compatibility with ZTCC
A few things happened last week, namely that I did not know I was only running on vanilla Zoo Tycoon, and was pointed out by all testers when it failed on their computers the first time. I reinstalled with ZTCC (which is the whole point of this project!) but that meant extra time was needed to get it working with the expansion packs. As of today, all testers confirmed it works with ZTCC.
addToBudget()
Now that addToBudget seems to work, I've started branching off to other functions I might try to reverse engineer, or at least find out what they do. When code gets compiled and you open it up in a hex editor, you might note that you see a bunch of gibberish numbers and if you're lucky, maybe some hieroglyphics on the right-hand side of your viewer. It turns out that there is method to the madness, and each function compiled from something like C++ will have a distinguishable pattern in the hex code to identify it. For example, what looks like broken text is actually just bits and pieces of a character array at work and it's just a tiny puzzle that needs figured out.
The above is an example of our addToBudget function in x86 assembly instructions. Each instruction, namely each function call, lives at a certain address - the ones in orange on the left. It turns out that the game calls the addToBudget address (fcn.0040f018 above) 15 times in different places.
(formatting might look awful on mobile)
01 | function: 00613e4a at: 00613f61
02 | function: 0060d8e3 at: 0060d9d3
03 | function: 0060ccc2 at: 0060cccd
04a | function: 004f94be at: 0050A245 (Conclusion: Selling animal, vending machine) (has a label: 0050a242) (Todo for a and b: test all sellable animals, objects)
04b | function: 0050a0b0 at: 0050a245 (Conclusion: Selling animal, vending machine) (has a label: 0050a242)
05 | function: 006089e8 at: 006089fc
06 | function: 00607174 at: 00607299
07 | function: 005b0f17 at: 005b0f2d
08 | function: 005a13c5 at: 005a1679
09 | function: 004f6e3c at: 004f7031 (Conclusion: Admission entrance fee)
10 | function: 004d9088 at: 004d920f (Conclusion: readgustment to budget after destroying something) (This one is interesting. It triggers when you undo after destroying something, so it subtracts money you just earned. However, none of these trigger when you earn money by destroying)
11 | function: 004a2c98 at: 004a2f97
12 | function: 00483f11 at: 00484047
13 | function: 0042ec49 at: 0042ec81 (Unknown: Could be donations or benefactors?)(Triggers every few seconds, not sure what is being added or subtracted from budget) Values observed: 0.018, 0.013, 0.457, 0.406
14 | function: 0042d858 at: 0042d93f (Conclusion: guest purchases) Values observed: 2.00, 2.00, 2.00, 2.50 Buildings observed: Stands, vending machines, arcades (Todo: test other buildings) (interesting observation: A single payment is actually double the price you set. Ex: If you set to 1.25, you actually get paid 2.50.)
15 | function: 0042ce0a at: 005a981c
The above shows 15 different functions each with a different purpose. On the left is the address where these functions live, and on the right is the address where addToBudget is called. I don't know what the purpose for manipulating the budget is at each one, but I was convinced with four of them, and one I'm not too sure about. The one I'm unclear about looks like the game gives you money every few seconds--not much, about 0.018, 0.013, 0.457, 0.406 were observed. I think these might be donations or benefactors, but it's hard to say for sure until I find a way to see where those are stored in memory. Anyway, I found this all interesting and will be fun looking at those parent functions into more detail.
I want to point out that @finnethen was able to confirm that addToBudget is part of a bigger class. What this means is that we might see about what lives inside this class. I'm thinking it's a general class that stores everything about the zoo in that moment, which might be useful for trying to decode the .zoo files later on.
Main loop/Entry point
I fell a bit behind because I needed to rewrite a lot of the code to have it work on older computers and ZTCC, but I think I managed to find the main loop and entry point functions. The main loop is what runs in perpetuity as the game is going, and the value of finding it means that we would be able to take control over events that are happening while the game runs. There's way more too it and I need to figure things out, but if done right then this could let us detect key presses, drawing of assets to the window, and event listening. I mentioned this before, but if I can hook onto the main loop somehow then a modder might do something like this:
// tired not allowed!
if (zookeeper.isTired)
{
zookeeper.fire();
}
// christmas!
if (player.LeftMouseClick(animal.at("Polar Bear 3"))
{
game.playCokaColaJingle();
}
// weather?
random = sys.rand(time);
if (random == 5)
{
game.weather = "fall";
game.weather.time = 20; // seconds
}
Once the loop is figured out and at least a few more functions interfaced or reversed, then it would finally make sense to release a first version of this API and some mods to go with it.
API at Work
If you want to see what I had my testers check for me, here is what they did. They loaded a zoo, and every time they sold an animal or a building, the game would give them $5000. Here is a clip of the API at work. Sorry for the awful aspect ratio. Might want to full screen to see the changes to the budget lol:
Of course, in the final release of the API, this $5000 hack wouldn't be a part of it. It's only meant to highlight that my code is capable of interfacing with the game's assembly instructions using a higher level language. It would be up to a modder to get creative!
Too Long, Didn't Read
So, API is now working on computers Windows XP and above and is now compatible with Zoo Tycoon Complete Collection.
Me and @finnethen have started trading resources, ideas, and mappings of the code to figure this all out. He is working on a similar project.
Stretch Goals
Short term in order of priority:
Translate the first two functions I found into C++ (almost done!)
Create a .dll wrapper that will allow my API to hook onto the zoo.exe file when it runs
Hook onto main loop
Interface with at least two more functions
Add lua scripting support
Main thread vs new thread
Test, test, test. Any volunteers? Highly recommend a virtual machine setup. I can show you how.
Start pushing code to Github and share repository with community
Long term:
Figure out why there are ID limitations for some objects/buildings. Is there a way to expand capacity?
Create functionality in my dll that looks for a separate mod folder from the usual one in Zoo Tycoon. That way mods that use the API are separate.
Those are my new stretch goals. My next priority is hooking onto the main loop to finally control the functions I've been discovering using scripting. Ideally, my API would load into the game, and conversely load scripts made by modders. I'll see about adding Lua support to make this all more accessible.
How much longer until release?
For the first version, probably in a couple of months or less. For a full-featured API that gives modding access to most of the game, probably a year or two, likely longer.
This is a long term project. I will at least be committed to working on it until I graduate from university which isn't until Fall 2025, maybe Spring 2026. A lot of what I'm learning has and will apply to the project. For example, I know a common request is to fix the pathfinding. Usually games use something called A* pathfinding, and it's similar to something I'm using right now in one of my classes called Dijkstra's Algorithm. It's an algorithm that calculates the shortest path and is used in things like GPS, self-driving cars, and many other games out there. Likely the game is using a similar algorithm, I think it just needs a few conditionals to stop them from walking off-the-path.
As always though, no promises! Just excited rambling lol