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!
Assembly Language: Things learned
Assembly Language: Things learned
ã¾ã•ã‹ã¤ã‚ãŒã¤
-
- Level 60
- Posts: 1797
- Joined: Tue Jul 26, 2011 9:07 pm
- Location: the the the the
Re: Assembly Language: Things learned
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.
Re: Assembly Language: Things learned
I'm assuming "sll" stands for shift left logical. Might want to explain the difference in logical and arithmetic shifting too --- 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
Re: Assembly Language: Things learned
I'm not sure what you're trying to display there. All the tables are up above or in the Game Mechanics thread though.Nightshade wrote:So, if I understand your explanations correctly, then I should get something like:
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!K73SK wrote:I'm assuming "sll" stands for shift left logical. Might want to explain the difference in logical and arithmetic shifting too --- Assuming the assembler picks up on shift left/right arithmetic (sla/sra?)
-- 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.
ã¾ã•ã‹ã¤ã‚ãŒã¤
-
- Level 60
- Posts: 1797
- Joined: Tue Jul 26, 2011 9:07 pm
- Location: the the the the
Re: Assembly Language: Things learned
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.
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.
Re: Assembly Language: Things learned
Nah, no worries, I know you. So I don't think that.
ã¾ã•ã‹ã¤ã‚ãŒã¤
Re: Assembly Language: Things learned
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!
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!
ã¾ã•ã‹ã¤ã‚ãŒã¤