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:
0048C447 |. 8B7C24 10 MOV EDI,DWORD PTR SS:[ARG.1] ; default case of switch FF8_EN.48C43D
0048C44B |. 33C9 XOR ECX,ECX
0048C44D |. 8A4E 03 MOV CL,BYTE PTR DS:[ESI+3]
0048C450 |. 8BC7 MOV EAX,EDI
0048C452 |. 99 CDQ
0048C453 |. F7F9 IDIV ECX
0048C455 |. 33DB XOR EBX,EBX
0048C457 |. 8A5E 01 MOV BL,BYTE PTR DS:[ESI+1]
0048C45A |. 8BC8 MOV ECX,EAX
0048C45C |. 8BC7 MOV EAX,EDI
0048C45E |. 99 CDQ
0048C45F |. F7FB IDIV EBX
0048C461 |. 33D2 XOR EDX,EDX
0048C463 |. 8A16 MOV DL,BYTE PTR DS:[ESI]
0048C465 |. 0FAFD7 IMUL EDX,EDI
0048C468 |. 2BC1 SUB EAX,ECX
0048C46A |. 33C9 XOR ECX,ECX
0048C46C |. 8A4E 02 MOV CL,BYTE PTR DS:[ESI+2]
0048C46F |. 03C2 ADD EAX,EDX
0048C471 |. 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
0048C480 |. 8B7C24 10 MOV EDI,DWORD PTR SS:[ARG.1] ; cases 0, 2 of switch FF8_EN.48C43D
0048C484 |. 33C9 XOR ECX,ECX //ecx = 0
0048C486 |. 8BC7 MOV EAX,EDI //eax = lvl
0048C488 |. 8A4E 03 MOV CL,BYTE PTR DS:[ESI+3] //cl = d
0048C48B |. 0FAFC7 IMUL EAX,EDI EAX //edx:eax = lvl^2
0048C48E |. 99 CDQ //sign extend eax into edx
0048C48F |. F7F9 IDIV ECX //eax = floor(lvl^2/d)
0048C491 |. 33DB XOR EBX,EBX //ebx = 0
0048C493 |. 8A5E 01 MOV BL,BYTE PTR DS:[ESI+1] bl = b
0048C496 |. 99 CDQ //sign extend eax into edx
0048C497 |. 2BC2 SUB EAX,EDX //not sure about this one edx will either be 0xFFFFFFFF or 0x00000000 eax ends up as eax or eax + 1
0048C499 |. 8BC8 MOV ECX,EAX //ecx = floor(lvl^2/d)
0048C49B |. 8BC7 MOV EAX,EDI //eax = lvl
0048C49D |. 99 CDQ //sign extend eax into edx
0048C49E |. 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 = 0x66666667
0048C4A9 |. 2BD9 SUB EBX,ECX //ebx = floor(lvl/b) - floor(floor(lvl^2/d)/2)
0048C4AB |. 33C9 XOR ECX,ECX //ecx = 0
0048C4AD |. 8A0E MOV CL,BYTE PTR DS:[ESI] cl = a
0048C4AF |. 0FAFCF IMUL ECX,EDI //edx:ecx = lvl*a
0048C4B2 |. F7E9 IMUL ECX //edx:eax = lvl*a*0x66666667
0048C4B4 |. 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 eax
0048C4BE |. 03C2 ADD EAX,EDX //not sure what effect this has
0048C4C0 |. 33D2 XOR EDX,EDX //edx = 0
0048C4C2 |. 8A56 02 MOV DL,BYTE PTR DS:[ESI+2] dl = c
0048C4C5 |. 03C2 ADD EAX,EDX //eax = floor(lvl*a*0x66666667/2^34) + floor(lvl/b) - floor(floor(lvl^2/d)/2) + c
0048C4C7 |. 99 CDQ //sign extend eax into edx
0048C4C8 |. 83E2 03 AND EDX,0x00000003 //edx = either 0x00000000%4 or 0xFFFFFFFF%4, so 0 or 3 depending on sign of eax
0048C4CB |. 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.