Assembly Language: Things learned

Any information you would like to share or ask in regards to the hacking of Legend of Legaia
Post Reply
User avatar
meth962
Level 23
Posts: 266
Joined: Tue Apr 03, 2012 8:37 am
Location: USA
Contact:

Assembly Language: Things learned

Post by meth962 » Tue Feb 24, 2015 5:26 pm

Decided to make a new topic describing some of the things I discovered while debugging the game, in hopes that it will help others and maybe someone will pick it up and be able to take over should I disappear :) The practices and behaviors stated here could help other people understand what the assembly code is doing in other games or the like.

This is also to show you why such a simple thing may take a long time to figure out haha. I'll start the most recent example since it's fresh.

Experience Table
So when you level up, you might think the game just looks up a table of how much experience you need for the next one; but you would be wrong in Legaia's case.

It uses a base experience table, but adds up each value until the count of values added is your level. For instance, if you're level 4, it adds the first four numbers together. Though it will do a bunch of calculating afterwords, let's start there. Here is the table.

1, 2, 3, 5, 7, 10, 14, 17,
21, 26, 31, 37, 43, 50, 57, 65,
73,82,91,101,111,122,133,145,
157,170,183,197,211,226,241,257,
273,290,307,325,343,362,381,401,
421,442,463,485,507,530,553,577,
601,626,651,677,703,730,757,785,
813,842,871,901,931,962,993,1025,
1057,1090,1123,1157,1191,1226,1261,1297,
1333,1370,1407,1445,1483,1522,1561,1601,
1641,1682,1723,1765,1807,1850,1893,1937,
1981,2026,2071,2117,2163,2210,2257,2305,
2353,2402

So already you can see it's not done so nicely. Let's do the example I used when discovering this. I had a save at level 31 leveling up to 32. So adding the first 31 numbers gives me the total of:

exp = 2631

Now time for a bunch of fun math equations (;_;) Lucky for you, I took all the assembly code which looks like this...

sll r2, r6, 0x04
addu r3, r2, r3
subu r3, r3, r6
sll r2, r3, 0x03
addu r3, r2, r3
addu r3, r3, r6

into fun math! yaaaay (not)

exp = (((exp << 4) - exp) << 3) + exp

O_o okay so I admit, that doesn't look any better. Now the << or >> mean to shift the binary numbers. Without getting into computer science, you can mathamatically think of it as multiplying or dividing by 2 to the power of said number. So 3 << 2 would mean 3 * 2^2 aka 3 * 4. So I break this down even further for you!

exp = exp * 121

Now our 2631 * 121 = 318,351 experience. This is what Vahn needs to level up again.

Now before you try to do any levels less than 16, guess what? There is code to check if the current level is less than 17 (aka 1-16) and it changes this calculation further! See ugly translated math below, and trust me, the assembly code for it is much much MUCH worse.

r3 = (exp << 5) - exp;
r2 = (((r3 << 6) - r3) << 3) + exp;
r3 = r2 << 2;
r2 += r3;
r2 = (r2 << 7) - exp;
r3 = 3,425,353,235; // this is just a hard coded number in the code
r7 = r2 * r3 >> 32;
exp = r7 >> 16;

All this can further be broken down to this: exp * 9,999,999 * 3,425,353,235 / 281474976710656
Look crazy right? In computer world, it's makes a little more sense.
exp * 9,999,999 * 0xcc2abe13 (hex). You'll get a large number, and it will overflow, so you just take the overflow value / 65536.

So if you plug 1 in, you get 121.
Level 2, plug 3 in and you get 365.
Annoying isn't it? I guess they did this to fix their exp calculations without changing the tables.

Okay if you're still reading at this point, you are clearly more crazy than I am. And I have a treat just for people like you....

Guess what??

That's only for VAHN. Now for Noa and Gala, the game further calculates a stunt growth to this!! Arrrgh.

Essentially, like every other stat in the game, Vahn is neutral or the base, and Noa/Gala are plus or minus an amount from him. Same with experience. The general rule is, Noa requires up to a percent less experience than Vahn to level up, and Gala requires up to a percent more. So their experience curves make it so Noa will level up ever so slightly faster and Gala ever so slightly slower :)

Here's that stunt table, at least what I call it...maybe a "modifier" table would be a better name for it:

125, 251, 376, 501, 625, 749, 872, 995,
1116,1237,1356,1474,1590,1705,1819,1930,
2040,2148,2254,2358,2460,2559,2656,2750,
2842,2931,3018,3101,3182,3259,3334,3405,
3473,3538,3600,3658,3713,3764,3812,3856,
3897,3933,3967,3996,4022,4043,4062,4076,
4086,4093,4095,4094,4089,4080,4068,4051,
4031,4007,3979,3947,3912,3873,3830,3784,
3734,3680,3624,3563,3500,3433,3363,3289,
3213,3134,3051,2966,2878,2787,2694,2598,
2500,2399,2296,2191,2084,1975,1864,1751,
1636,1520,1403,1284,1164,1043, 921, 799,
675,551

All the game does with this table is add or subtract a bit of experience depending if it's Noa or Gala. First the calculation to get the amount to modify.

exp * 20 / stunt

So if Noa, subtract the amount. If Gala, add the amount. They're usually around a percent or so difference.

And there you have it. The not so easy way of doing experience tables. So when I had to dump out these experience tables, there was no way to just pull it out of the game :( I had to pull out the tables listed above, and build something quick to do the same calculations that the game does. Pain in my butt! Especially considering when I am home to do this stuff, it's usually real late and my brain hurts, haha!
まさかつあがつ

User avatar
Nightshade
Level 60
Posts: 1797
Joined: Tue Jul 26, 2011 9:07 pm
Location: the the the the

Re: Assembly Language: Things learned

Post by Nightshade » Tue Feb 24, 2015 6:02 pm

So, if I understand your explanations correctly, then I should get something like:

Code: Select all

expv
 [1]    121    365    730   1338   2190   3407   5111   7179   9735  12899
[11]  16671  21174  26407  32492  39428  47338  56222  66200  77274  89565
[21] 103073 117920 134105 151751 170856 191544 213814 237787

expn
 [1]    102    335    691   1285   2120   3316   4993   7035   9560  12690
[11]  16426  20887  26075  32110  38994  46847  55670  65584  76589  88806
[21] 102235 116998 133095 150647 169654 190237 212397 236254

expg
 [1]    141    394    768   1392   2260   3498   5228   7324   9909  13108
[11]  16917  21461  26739  32873  39862  47829  56773  66817  77960  90325
[21] 103911 118842 135115 152854 172059 192851 215231 239321
Do not question yourself with the why or the how. I simply am, and that is all you need to know.

User avatar
K73SK
Level 89
Posts: 3953
Joined: Tue Feb 01, 2011 7:00 am
Location: USA
Contact:

Re: Assembly Language: Things learned

Post by K73SK » Wed Feb 25, 2015 9:12 am

I'm assuming "sll" stands for shift left logical. Might want to explain the difference in logical and arithmetic shifting too :P --- Assuming the assembler picks up on shift left/right arithmetic (sla/sra?)
Donate to legendoflegaia.net if you are one who wants to keep it alive! http://www.legendoflegaia.net/donate.html

User avatar
meth962
Level 23
Posts: 266
Joined: Tue Apr 03, 2012 8:37 am
Location: USA
Contact:

Re: Assembly Language: Things learned

Post by meth962 » Wed Feb 25, 2015 10:33 am

Nightshade wrote:So, if I understand your explanations correctly, then I should get something like:
I'm not sure what you're trying to display there. All the tables are up above or in the Game Mechanics thread though.
K73SK wrote:I'm assuming "sll" stands for shift left logical. Might want to explain the difference in logical and arithmetic shifting too :P --- Assuming the assembler picks up on shift left/right arithmetic (sla/sra?)
SLL is shift logical, and mostly what Legaia uses. I wasn't trying to teach how to read assembly though, so I only pasted it in for an example of the process being translated :) Figured someone like you might take interest in it haha!

-- Wed Feb 25, 2015 10:40 am --

Oh the the the the, if anyone is interested, there's a ton of crazy Meth assembly notes stored here that I can share.

https://onedrive.live.com/redir?resid=A ... lder%2ctxt

It's where I store all my debug notes so I can figure out stuff on the road.
まさかつあがつ

User avatar
Nightshade
Level 60
Posts: 1797
Joined: Tue Jul 26, 2011 9:07 pm
Location: the the the the

Re: Assembly Language: Things learned

Post by Nightshade » Wed Feb 25, 2015 2:49 pm

I was just trying to see if I was following your explanations correctly. And I saw this thread before I noticed that you had updated the Game Mechanics thread accordingly, and wanted to know if I had the correct table, because I didn't see it posted in the other thread at first.

I apologize if I came off as dismissive or rude; it was not my intention.
Do not question yourself with the why or the how. I simply am, and that is all you need to know.

User avatar
meth962
Level 23
Posts: 266
Joined: Tue Apr 03, 2012 8:37 am
Location: USA
Contact:

Re: Assembly Language: Things learned

Post by meth962 » Wed Feb 25, 2015 3:40 pm

Nah, no worries, I know you. So I don't think that. :)
まさかつあがつ

User avatar
meth962
Level 23
Posts: 266
Joined: Tue Apr 03, 2012 8:37 am
Location: USA
Contact:

Re: Assembly Language: Things learned

Post by meth962 » Fri Feb 27, 2015 3:09 pm

Tables Within Tables - Tableception!

So I totally forgot to mention this piece when describing the war path to find the Experience information. It's no surprise to anyone that in order to find answers in the game/memory that you will be needing to find a lot of data, which will be represented in a table. But in an ideal world, and a hopeful Meth's head, you think the tables would be sequential like so....

Vahn ATK per level (this is made up)

20, 22, 23, 27, 33, 37,
42, etc.

Then you get sad when you realize no such table exists, or it is not just a table with one element in it, but ALL elements. For instance, the growth tables for leveling up are per level PER STAT. So instead of strength increase being in line, you would see all the other stats in a row for level 1, followed by all the stats for level 2!

(hp) (mp) (agl) (atk) (udf) (ldf) (spd) (int) (agl)
(hp) (mp) (agl) (atk) (udf) (ldf) (spd) (int) (agl)

And this is what happened when I found the "stunt" or "modify" table as I mentioned above. No idea why I undersold the idea of how much effort that took, but following the assembly code, I noticed it was increasing the address by 40 for each level. So the table modifier for experience I was looking for was every 40 bytes. Which could mean there were 10 stats nested together in that table, but I only cared about every 10th one.

Since I had to build an app to calculate this crap (or use excel or something), I needed to copy down the tables. The first one was easy (the base experience table) because it was sequential and I just manually typed out the numbers while having the playstation's memory up in another window. But this table, having to skip 40 bytes for 99 times, reading out of a table that looks like this:

06 cb 11 cd 07 ce 20 cf 01 d0 13 cb 11 01 55 cc
5f aa 2f ab 33 c0 ff 01 dd 03 ee 08 cc 09 10 ff
etc etc.

No way. So today's tip is about data dumping!

In some cases it's easy, you just need to dump the memory if you see a table sitting in memory, fully loaded.
Dump the playstation memory. Write an app to grab every Nth number and save it somewhere else if it's one of those non-linear tables. Then delete the memory dump. It's much quicker than doing it by hand.
Before I was lazy and just made things read from the memory dump file. But then when someone on here wanted the table, I'd have to have it cleaned up of the values we don't care about.

Some examples of tables that I have seen fully loaded into memory and easily readable are:
Base Experience
Stealable/Droppable

And examples of tables that are non-sequential, but still fully loaded in memory:
Experience Growth
Stat growth
Attack strength by input
ATK/DEF attributes of equipment

However, there is only 2MB for the playstation to use in memory, so many things are read from the disc (not just images/audio).
Here is a very important lesson when trying to dump data from the disc. Data is read into memory and then staged. This means the data you see stored on the disc, will not be the same as what is translated into memory. To better explain this point, let's use monster stats.

Obviously the game doesn't need all monster stats loaded into memory, nor would it need ALL monsters in a single battle. So the game looks up the stats for the 3-4 monsters in the battle. However, the data on disc is not stored like you would think. You won't see hp, followed by mp, followed by agility, followed by strength. I was super confused when I ran across Cort's stats and it looked like this:

hp: 50000 mp: 2500 agl: 800 atk: 207 udf: 261 ldf: 1789787 int: -8979

What?! Through a long process of debugging what writes each memory part, I saw the values copied many times and changed along the way, eventually leading back to the CD. Then I started making sense of it.

The cd values are copied, and then transformed. It turned out that, some values were multiplied, and some values represented TWO stats. For instance, Cort's UDF and LDF were both read from the same number (and yes they are the same).

So a skill you will need to learn is to follow the loading trail back to the CD. The technique explained is pretty basic, but it will take some experience to get use to.

1. Start by breakpointing a "write" to the memory location that you're watching. AKA Cort's UDF stat.
2. When it pauses, you'll need to view the assembly lines executed just above. Pay particular attention to the register with the value in it and the register with the address. You want to see if the value was changed/calculated, and you'll need the address from where it was copied.
3. Jump to the address you found in Step 2 and compare the layout in the area. It might match the CD, or it might be closer to what is stored in memory at the end. However, what you might notice is it doesn't make any sense at all. The value does not look like it was copied from this location. This is where it gets hairy. When copying from the disc, the memory to load/copy/calculate disc data is very short lived. By the time the assembly code breaks at your Step 1 address, all the info you may have wanted might be gone at from this address. Whether the data is there or not, you will need to do Step 4. Now breakpoint the new memory location you found in Step 2. If your data was there or not, we want to see where it came from. And if it wasn't there, you'll get to see the data while it IS there.
5. At this point, you're in the main loop of the process. Keep repeating steps 2-4 until you reach the first time the data is copied into memory. You will eventually see that the data in memory, is byte for byte the same as how it is stored on the CD. Then you know you've reached the end. At that point you can follow how the values are changed/manipulated.

In the enemy stat example, they are stored in a multiple different ways. Mostly by flags and values. It makes it really annoying to try and make something to mod the Legaia ROM to change enemy stats. For example, here is an example of some flags:

type 2a
- Read two bytes for HP, one byte for MP

type 2b
- Read two bytes for HP, two bytes for MP

subtype 7f 01
- Read two bytes for ATK, one byte to populate UDF and LDF
subtype 7f 03
- Read two bytes for ATK, and divide that value by 8 for SPD

It's really bizarre and annoying. But many types, many sub types. And I've only made it through half of the enemy data. Good luck to you on never finding stuff like that!
まさかつあがつ

Post Reply