Jump to content

ZT EMU API


Goosifer

Recommended Posts

Thanks for the link! I agree with Jay that there doesn't seem to be any reference to mapx or mapy in the zoo.exe file. Maybe Jay might find this process dump I ran today interesting and see if he might make sense of it. It's basically a dump of all of the string references made inside of the binary file, and most of the .ini properties are in there, but not the ones mentioned and not all of them. Interestingly, Map exists in that list, and I'm assuming that it's the the one in the .ini file. I hope many of these references could lead to abstracting more interesting functions, like mapsize for example.

 

After searching the assembly code, looks like MapSize is actually referenced three times (separate from Map), and that's just a snippet from a larger data structure it's a part of.:

 

        0044675c 8d 4c 24 1c     LEA        param_1=>local_c,[ESP + 0x1c]
        00446760 e8 cb 06        CALL       FUN_00406e30                                     undefined FUN_00406e30(undefined
                 fc ff
        00446765 bf d8 5e        MOV        EDI,s_MapSize_00575ed8                           = "MapSize"
                 57 00
        0044676a 83 c9 ff        OR         param_1,0xffffffff
        0044676d 33 c0           XOR        EAX,EAX
        0044676f f2 ae           SCASB.RE   ES:EDI
        00446771 f7 d1           NOT        param_1
        00446773 49              DEC        param_1
        00446774 81 c1 d8        ADD        param_1,s_MapSize_00575ed8                       = "MapSize"
                 5e 57 00
        0044677a 51              PUSH       param_1
        0044677b 68 d8 5e        PUSH       s_MapSize_00575ed8                               = "MapSize"
                 57 00
        00446780 8d 4c 24 20     LEA        param_1=>local_c,[ESP + 0x20]
        00446784 e8 b7 0f        CALL       FUN_00407740                                     undefined FUN_00407740(void * th
                 fc ff

 

It might not be what we're all thinking, all of that needs figured out first, if it's possible at all. Sadly not my priority yet, but might be fun to test later if those 'param_#' numbers might be referencing coordinates?

zoo.txt

Link to comment
Share on other sites

36 minutes ago, Fern said:

And here is a possible task from part of a conversation with Jay:

But if an advanced ZT API did come into existence, one of the things I thought would be useful concerns recolors. I always thought it was a bad design by Blue Fang to put the pal file names inside the views. Instead, they should have put the pal file names inside the ani files, or in the ai/uca/ucb/ucs files if there are more than 1 animation for an object. Then you would only have to load the images for 1 color, then edit the ani files for the other colors to say what pal file to use. That makes the loading easier and it makes the ztd smaller. That would especially make animal recolors extremely tiny.

 

I just missed this reply. I think this is super interesting! I might give it a try some time down the road. I can't say I am too familiar with ani files or graphics programming but this could be an interesting entry point to learn. I will say, in the data dump I attached above, it seems to make direct reference to DirectDraw and IDirectSound, so it might be possible to find documentation of whatever graphics rendering version of DirectX(?) they used in the making of this game and eventually abstract that too. I'm not sure if I'm smart enough for that yet though lol.

 

I did look at the Zoot source and I'd be eager to see if I can tackle some of Jay's wishlist in the readme if he'd be willing, but that might be a project for the end of the year.

 

Edit: Found it, the game was programmed in DirectX 7.0:

 

Quote

New for DirectX 7.0. When returned by the IDirectDraw7::StartModeTest method, this value means that no test could be initiated because all the resolutions chosen for testing already have refresh rate information in the registry. When returned by IDirectDraw7::EvaluateMode, the value means that DirectDraw has completed a refresh rate test. An attempt was made to page-unlock a surface with no outstanding page locks.

 

That's quoting that dump I sent.

 

Edit 2: To be clear, I think they heavily-leveraged DirectX to program the graphics handling, but as far as I understand they used their own proprietary graphics engine that uses that API. Still, might be useful later on to someone.

 

Edit 3: I just ran the process dump on one of my school project executables, and can at least say that it dumped all or most of the function names and variables I used in my project, so I want to think that those could in fact help me find more functions inside of the assembly.

  • Like 3
Link to comment
Share on other sites

from Jay: Concerning MapSize, I do not know how it works, but it was something in the ZT Beta. The zoo.ini file in the ZT Beta had the line "MapSize=20" in its [AI] section. I also do not know if that has something to do with the zoo map or the zoo map preview in the bottom left of ZT's screen.

Link to comment
Share on other sites

Hello! I have a somewhat boring update today, but it's still a big deal! I recently discovered that the game automatically loads .dll files that start with 'lang' in the name (which is not new to anyone here, I’m just a dumb dumb). This solved my biggest problem.

 

I don't need to patch the exe file for this API to work. I just need to write my own DLL (hopefully, it stays that way!). Typically, games don't allow dynamically-linked libraries from strangers to start up, so it takes more effort to convince a game to let you in, but I guess Blue Fang is just friendly like that. In the future, if any mods use this API, the only requirement will be to drop this DLL file into the ZT1 folder, just like anyone ever has.

 

The DLL I created is currently named lang-TekToolkit, but that may change. I'm not sure how the game's limit on the number of 'lang' files it can load works, but I hope it's fine since mine is created from scratch and interfaces with the game differently. It doesn't load anything, only translates the low-level code stored inside of zoo.exe.

 

In any case, the API now works, and my first function is completely reverse-engineered: addToBudget(). Whenever a guest pays for admission, my API intercepts the request, diverts it to my DLL file, and executes the identical function I've written that the original game requires. It then returns the correct budget update back to the player. Now that the API is inside the game's process, I can begin reverse-engineering other functions.

 

So it goes from this (zoo.exe):

mov eax,dword ptr ss:[esp+4]            
add ecx,8                               
push eax                                
call zoo.4B5F30       
fld st(0),dword ptr ss:[esp+4]          
fadd st(0),dword ptr ds:[ecx]           
fstp dword ptr ds:[ecx],st(0)           
ret 4     

 

To this (lang-TekToolkit.dll):

 

// deposit = admission fee paid by guest 
DWORD __stdcall addToBudget(float deposit)
{
	Memory<DWORD> r; // read
	Memory<float> w; // write
	Zoo::Process p; // initiate process data
	DWORD ptr = r.readMemory((void*)(p.base + 0x00186628)) + 0x08; // grab address to budget

	float* budget = (float*)(ptr); // store current budget
	w.writeMemory((void*)ptr, *budget + deposit); // update budget
	return __addToBudget(deposit); // return to location in zoo.exe
}

 

So now a modder only needs to do this if they want to play with the budget in future mods:

 

deposit = <any number>;
addToBudget(deposit);

 

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
  • Discover more functions
  • 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.

 

I changed my short term goals a bit. Right now the API is kind of useless because only the budget function is accessible and that's already possible through the game files. My plan now is to find the main game thread or make my own. A thread is basically where all of the action is happening and if I find it, that's where modders can really take control of the game logic and do cool stuff; I think I gave an example of what that might look like a few comments up, but I hope I can give a real one in the coming weeks.

 

The next update will be about thread control, and hopefully I've reverse-engineered some more easy functions.

 

P.S.: Still looking for testers!

  • Like 2
  • Love 1
Link to comment
Share on other sites

Savannahjan

You're making great progress; this is really exciting.  :jump1:

  • Love 1
Link to comment
Share on other sites

Yes there is a limit to how many dll files are in a game which is why each site created its own combined dll file.

You really should read the topics in this area.

http://www.ztcdd.org/DG/index.php?board=246.0

and this one which also has a dll topic

http://www.ztcdd.org/DG/index.php?board=273.0

 

I will test it and also pass it on to Jay to test. Preferably it would be best to send testers via pm until we are sure it won't cause problems. Cricket would be another person who has enough design knowledge to know what to look for as well.

As for buildings etc. each requires 3 ids and in some cases a 4th for a guest thought. Not the numerical spacing in the ids. Casey was the one who discovered this. Guest thought ids are taken from sets that will not cause a problem. Often we would set aside a 3 piece sequence to use just for them.

2601, "Tent"   
32601, "Guests will appreciate having a place out of the sun to sit or to eat the food they bought elsewhere."
57601, "the Tent"
7682,    "Nothing like getting back to nature for a good rest."

 

Items are like this - name, tooltip, guest thought

2595, "Bag Of Pretzels."   
32595, "Bag Of Pretzels. Guests will love these pretzels so much that they will go after the crumbs remaining in the bag."
57595, "I'm glad I bought the bag of pretzels. I can nibble on them for a while."

 

The tooltip is automatically triggered by the id sequence ingame. You won't see it on the ai.

 

And I for one have seen what happens with incorrectly used ids.

Link to comment
Share on other sites

Thanks for the tips, they're going to help a lot.

 

Right now I've got it working just fine on my Windows 11 computer, but not on any of my VMs. It's most likely because none of them have any service packs or VS C++ runtimes installed to run the dependencies I use, but that's what I'd like to find out.

 

On 5/2/2023 at 5:00 PM, Fern said:

I will test it and also pass it on to Jay to test. Preferably it would be best to send testers via pm until we are sure it won't cause problems. Cricket would be another person who has enough design knowledge to know what to look for as well.

 

Absolutely agreed. I'll send you a DM. Thank you so much!

 

Link to comment
Share on other sites

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.

 

fcn_0040f018.png.5bc9674b7b6c72a70b52b13df4e7d717.png

 

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

  • Like 2
  • Love 1
Link to comment
Share on other sites

Savannahjan

Great work going on here.  So glad you and @finnethen are collaborating!  :BTFTHNG:

  • Like 2
  • Love 1
Link to comment
Share on other sites

  • 3 weeks later...
On 5/8/2023 at 11:36 AM, Savannahjan said:

Great work going on here.  So glad you and @finnethen are collaborating!  :BTFTHNG:

 

Thanks, and yeah I'm glad too. @finnethen is the best!

 

I've been on a break from coding ever since finals had taken up all of my available time. I am on vacation right now and will be driving back home for the coming week. Once I get back in June I'll have more frequent updates--I still think it would be great to have a version of the API that modders can start using by the end of summer. We'll see.

 

A few updates on the project.

 

First, I've decided on a final name for the project. It will be called EMU (Extensible Modding Utility). I just thought it would be a cool nod to the three-letter animal acronym APE made popular. I meant it when I said that I wanted to honor Zootek in some way, and I have a few ideas after the name change.

 

Second, I've got a command console working. If you don't know what a command console is: some games let you open a terminal with a shortcut. It's a black window where you type commands that manipulate the game as you play. It's a good tool for an API because it lets me and modders debug API functions, and it's a fun tool for people to use in general because why not. Jay had a great idea where a possible application of this command console could be letting users modify .ini values while you're in the game (without actually changing the .ini file, if that makes sense).

 

Here's a 50 second clip of it in action. In this example, I continue to use the addToBudget function as I have before. I type the command in the console and you'll see the budget update in the game.:

 

 

Right now the console opens up automatically when Zoo Tycoon launches. Eventually I want it to only open up when hitting a shortcut.

 

I've also got a super primitive game loop working. If you don't know what a game loop is, it's usually where all of the game events and graphics refresh on a neverending loop. It's like an engine that makes sure a flip book is always flipping. This is important because it's where mods using the API will run when loaded. This game loop is actually how the console is working above. One of the challenges is that I had to create a new thread to make it work until I find a way to hook onto the real loop, so there is some performance loss at the moment that I hope I can patch later on.

 

Finally, thanks to Jay and Fern's help, I got a lot of the budget-related function calls figure out. As a reminder, here's how to read this chart: Bold just means they are the last two I've found. Leftmost is the number of the function. The first hexadecimal address is actually a mystery function I haven't defined yet, and the rightmost hex address is the location within that mystery function that the addToBudget() function is called. On the right I have a small note describing the context behind when that function call is made. That context will help me figure out what the mystery functions are actually doing when I have the time to figure those out, but we get a decent idea here. For example, this is how I learned that the parent function to number 5 is the easter egg cheat function that controls how all of the easter eggs are programmed.

 

If there's no comment it's because I haven't figured out how to trigger that function call yet. Some ideas are when you win a scenario and get money, certain event-related cash prizes, etc. If anyone has ideas, I would love to hear them!

 

Quote

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 (Conclusion: Adds $20,000 when renaming a male Zookeeper Jonathan Gilmour with .cfg file)
06 | function: 00607174 at: 00607299
07 | function: 005b0f17 at: 005b0f2d (Conclusion: sets the budget for a new zoo from the main menu)
08 | function: 005a13c5 at: 005a1679 (Conclusion: adds $10,000 when cash is low)
09 | function: 004f6e3c at: 004f7031 (Conclusion: Admission entrance fee)
10 | function: 004d9088 at: 004d920f (Conclusion: undo after a destroy or purchase - restores budget to previous state)
11 | function: 004a2c98 at: 004a2f97
12 | function: 00483f11 at: 00484047 (Conclusion: called every month to pay staff salary)
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

 

See you guys again in about two weeks!

  • Like 3
  • Love 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...