Breaking Videogames

For those who can't play a video game without dissecting it


Project maintained by banyaszvonat Hosted on GitHub Pages — Theme by mattgraham

The blog has been quiet, and the at most 5 lost souls who are also looking at the GitHub may have noticed it didn’t update either. The reason is that I found a lot of interesting things, got caught up documenting them as I went, and have been writing a cursed tome:

That’s more than 4x the size of the usual blog post. But in the interest of actually posting something, I’m going to split off a few parts and release it in smaller installments while I finish up the draft for another long post on the subject that may or may not end up elsewhere.

Now, let’s get back to Disgaea.


After I’ve done all I think I could with read/write breakpoints, I decided to change approaches to the advanced technique “look up unknown offsets used as operands and also change some things in memory to see what happens”. Don’t remember how or when, but I also found a function at 0x08885b40 ^[1], which I knew was getting passed in a pointer to a character data structure. It does a couple of things that are interesting for our purposes.

It’s unclear why the function checks for multiple impossible conditions. Checking for non-persisting debuffs could be explained by the function being written for a hypothetical earlier design iteration, where other debuffs did persist. However, I can’t think of any circumstances where a character of a different “team” can be in the player’s party. Maybe it’s a multiplayer thing.


On that note I want to address Xkeeper’s comment at line 73. While writing this post, I think I realized why might this field have been added: the templates for skill advancement are in a contiguous array, whereas the class IDs have gaps in them. Storing the index of the template saves having to search the array for the particular class ID. Of course, it would have been possible to store the index corresponding to a class ID in a hash table or something.

As an incidental observation, doing it this way probably saves a few bytes of memory. The maximum size of the player’s party is 128. The game allocates space for 64 deployed units on a map (10 of which are expected to be taken up by the player’s units), giving us an approximate upper bound of 182 characters loaded somewhere in memory at once, with the possibility of a few more allocated elsewhere (there are character data entries for various NPCs in the main hub, and there is a wholly different array for characters sequestered in memory, but at the moment it seems that one is used instead of the party in the save game, when it does get used.) But let’s be generous, and double it to 384. That translates to 384 extra bytes allocated. I’m going to ignore bytes lost alignment, because either there is none, e.g., the party ends at 0x089B5F38 and there is an array immediately after it. Or if it is aligned to 8 bytes: the character data structure’s length is divisible by 8 as-is, so removing one byte would not translate to any gains. I could be wrong about this.

There are 362 entries in the template array in this version, and assuming we store the hash on 4 bytes, and the index on 2 bytes, that’s 2172 bytes. Even if we store the hash on 2 bytes, that’s 1448 bytes total. (You could go lower and permit hash collisions, but we’re trying to avoid having to check the class IDs of array entries.)

But regardless, it was probably just easier. I don’t think the developers were that constrained for memory that saving a couple hundred bytes mattered. Oh, and in a couple of places, the game linearly searches through the template array anyway, for example in a function to get the “base class” for a particular class ID.


The next post will either be more random findings, or will detail the chances of getting a consolation prize Mint Gum, in excruciating detail.


^[1] As before, these addresses are in RAM at runtime, in the European PSP release of Disgaea: Afternoon of Darkness

^[2] “Evility”, probably a funnier pun in Japanese. Passive abilities were introduced in a limited form in the second game, and I believe it was only in the third game that they got properly integrated with other game systems. But evidently even the first game has a few passive abilities that exist as hardcoded rules keyed to Class IDs. Another one that comes to mind is Etna and Maderas’ team attack chances being a fixed percentage. (IIRC, Maderas has a 100% chance of joining Etna in a team attack, while Etna has 0% chance of joining Maderas.) I actually didn’t know that until reverse engineering the game, though it’s written on the wiki too.

^[3] In practice, there are special classes that break the rule baseClassID / 10 == actualClassID / 10, for example certain story characters based on existing monster classes. And there are some classes that are clearly related to others, but have their own ID and base class ID. These are possibly used to display portraits for different emotions in dialogue scenes, but their IDs are unrelated to the playable character’s class ID. These differences might be why there’s also a field for storing the base class ID, but I don’t remember what actually checks that field at the moment of writing this. Coupled with the part about storing an array index, there are at least 4 slightly different ways to determine the base class from a class ID: read the base class ID field if it’s a character, index the class template array yourself using the template index field, zero the last base 10 digit of the class ID, or use the function that searches the class templates mentioned above, and read the base ID stored in the template. To misquote Archer Sterling: “Do you want parser differentials? Because that’s how you get parser differentials.”

Back to index