I noticed when poking around the memory at the actual values that some of the values in Ifrit were 1 off but it's probably a rounding issue, I could take a look in the code and see where it pushes the value from.
EDIT:
Function that gets the values in FF8_EN.exe (Steam version) is at 0x48C3F0 and takes 3 parameters:
param 1: level
param 2: pointer to section 7 of a dat file
param 3: integer (0 = str, 1 = mag (think this actually vit), 2 = vit (think this is actually mag), 3 = spr, 4 = spd, 5 = eva)
Is the information on
http://wiki.qhimm.com/view/FF8/FileFormat_DAT#Section_7:_Informations_.26_stats in the correct order?
Since I'd expect the formulas for str and mag to be the same rather than str and vit - I'm guessing mag and vit should be swapped since the order they usually appear in is str, vit, mag, spr, spd, luck, eva (although monsters have no luck).
For the following sections:
ESI = pointer to the 4 bytes needed, so ESI = 1st byte, ESI+1 = 2nd byte, ESI+2 = 3rd byte, ESI+3 = 4th byte
arg.1 = level
result ends up in EAX
ok, here is the ASM for the mag, spr, spd and eva cases (it gets capped to 255 after this but I've omitted that part) - I think this is actually for vit, spr, spd and eva, see note above:
Code: [Select]
Code:
0048C447 |. 8B7C24 10 MOV EDI,DWORD PTR SS:[ARG.1] ; default case of switch FF8_EN.48C43D0048C44B |. 33C9 XOR ECX,ECX0048C44D |. 8A4E 03 MOV CL,BYTE PTR DS:[ESI+3]0048C450 |. 8BC7 MOV EAX,EDI0048C452 |. 99 CDQ0048C453 |. F7F9 IDIV ECX0048C455 |. 33DB XOR EBX,EBX0048C457 |. 8A5E 01 MOV BL,BYTE PTR DS:[ESI+1]0048C45A |. 8BC8 MOV ECX,EAX0048C45C |. 8BC7 MOV EAX,EDI0048C45E |. 99 CDQ0048C45F |. F7FB IDIV EBX0048C461 |. 33D2 XOR EDX,EDX0048C463 |. 8A16 MOV DL,BYTE PTR DS:[ESI]0048C465 |. 0FAFD7 IMUL EDX,EDI0048C468 |. 2BC1 SUB EAX,ECX0048C46A |. 33C9 XOR ECX,ECX0048C46C |. 8A4E 02 MOV CL,BYTE PTR DS:[ESI+2]0048C46F |. 03C2 ADD EAX,EDX0048C471 |. 03C1 ADD EAX,ECX
if you're not using integer arithmetic, this is the same as: floor(lvl/b) - floor(lvl/d) + a*lvl + c - which is the same as JBedford128's formula above
ASM for str and vit (it gets capped to 255 after this but I've omitted that part): - I think this is actually for str and mag, see note above
Code: [Select]
Code:
0048C480 |. 8B7C24 10 MOV EDI,DWORD PTR SS:[ARG.1] ; cases 0, 2 of switch FF8_EN.48C43D0048C484 |. 33C9 XOR ECX,ECX //ecx = 00048C486 |. 8BC7 MOV EAX,EDI //eax = lvl0048C488 |. 8A4E 03 MOV CL,BYTE PTR DS:[ESI+3] //cl = d0048C48B |. 0FAFC7 IMUL EAX,EDI EAX //edx:eax = lvl^20048C48E |. 99 CDQ //sign extend eax into edx0048C48F |. F7F9 IDIV ECX //eax = floor(lvl^2/d)0048C491 |. 33DB XOR EBX,EBX //ebx = 00048C493 |. 8A5E 01 MOV BL,BYTE PTR DS:[ESI+1] bl = b0048C496 |. 99 CDQ //sign extend eax into edx0048C497 |. 2BC2 SUB EAX,EDX //not sure about this one edx will either be 0xFFFFFFFF or 0x00000000 eax ends up as eax or eax + 10048C499 |. 8BC8 MOV ECX,EAX //ecx = floor(lvl^2/d)0048C49B |. 8BC7 MOV EAX,EDI //eax = lvl0048C49D |. 99 CDQ //sign extend eax into edx0048C49E |. F7FB IDIV EBX //eax = floor(lvl/b)0048C4A0 |. D1F9 SAR ECX,1 //ecx = floor(floor(lvl^2/d)/2)0048C4A2 |. 8BD8 MOV EBX,EAX //ebx = floor(lvl/b)0048C4A4 |. B8 67666666 MOV EAX,0x66666667 //eax = 0x666666670048C4A9 |. 2BD9 SUB EBX,ECX //ebx = floor(lvl/b) - floor(floor(lvl^2/d)/2)0048C4AB |. 33C9 XOR ECX,ECX //ecx = 00048C4AD |. 8A0E MOV CL,BYTE PTR DS:[ESI] cl = a0048C4AF |. 0FAFCF IMUL ECX,EDI //edx:ecx = lvl*a0048C4B2 |. F7E9 IMUL ECX //edx:eax = lvl*a*0x666666670048C4B4 |. C1FA 02 SAR EDX,2 //edx = floor(lvl*a*0x66666667/2^34)0048C4B7 |. 8BC2 MOV EAX,EDX //eax = floor(lvl*a*0x66666667/2^34)0048C4B9 |. 03D3 ADD EDX,EBX //edx = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2)0048C4BB |. C1E8 1F SHR EAX,1F //eax = sign bit of eax0048C4BE |. 03C2 ADD EAX,EDX //not sure what effect this has0048C4C0 |. 33D2 XOR EDX,EDX //edx = 00048C4C2 |. 8A56 02 MOV DL,BYTE PTR DS:[ESI+2] dl = c0048C4C5 |. 03C2 ADD EAX,EDX //eax = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c0048C4C7 |. 99 CDQ //sign extend eax into edx0048C4C8 |. 83E2 03 AND EDX,0x00000003 //edx = either 0x00000000%4 or 0xFFFFFFFF%4, so 0 or 3 depending on sign of eax0048C4CB |. 03C2 ADD EAX,EDX //eax = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c 0048C4CD |. C1F8 02 SAR EAX,2//eax = floor((floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4)
So something like:
floor((floor((lvl*a*0x66666667)/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4)
as a rough guess.
There are a couple of things I'm not sure about above though and probably some mistakes in the annotations above. Someone that's better at signed arithmetic would probably help

.
The formula actually looks fairly close to what JBedford128 had (note that 0x66666667/2^34 is ~1/10 - it's so close that you could probably just use divide by 10 and it probably was in the original code - I suspect it's a compiler optimization for division by 10) - the only main difference is the flooring.
I'd say that:
floor((floor(lvl*a/10) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c)/4) probably works.
or if you're using integer arithmetic:
((lvl*a)/10 + lvl/b - (lvl^2/d)/2 + c)/4
I do wonder if this differs from the PSX code though.