Author Topic: [FF7] Chocobo Betting  (Read 810 times)

ff7man

  • *
  • Posts: 10
    • View Profile
[FF7] Chocobo Betting
« on: 2022-01-20 22:27:42 »
Does anyone know how the winning chocobos in the gold saucer chocobo betting are calculated or where to look?

Fuzzfingergaming made a video describing his process: https://www.youtube.com/watch?v=jXLHLeT12Ts

I made an html tool to calculate the chocobos using his formula, but it's not perfect.

https://www.youtube.com/watch?v=0EPUW8ckGwg

I know if you use save states on the chocobo betting win pre game screen you can return after the race and re-input the values to get the same result. So the results should be pre calculated based on stats and jockey color.

Would be nice to know the official equation that calculates the winners. I'm not sure where to go looking to find this however.

Anyone able to help?



ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #1 on: 2022-01-22 17:06:38 »
Alright so this isn't my first reverse engineering rodeo, so I took a stab at trying to answer this myself, but I still need help.

I setup ff7 steam.

Using black_chocobo I created a save at the chocobo square.

Loading the betting screen there are visual indicators we can see: top speed, stamina, name, chocobo color, jockey color, sprinting, prizes, and gil

I'm guessing the real solution to solving this mystery involves these variables or chocobo stats we can't see.

Using Cheat Engine I searched the game for the value of my gil.

Right click the address -> and the money updates. Neat.

Looking for 1 byte and 2 byte variables of the speed and stamina leads me no where. Not sure why. Those values could be obfuscated somehow...

According to https://finalfantasy.fandom.com/wiki/Chocobo_(Final_Fantasy_VII) someone has reverse engineered the equation for how they calculate speed and stamina.

The displayed value for the speed is derived from Dash/34 and the value for stamina is actual stamina/10.

Searching for stamina*10 didn't retrieve any results in cheat engine and neither did searching for speed*34

I'm hoping if I can find those values in memory and then maybe I can see other data around it for the chocobos stats or a win order.

Taking a step back, and borrowing on the shoulders of giants I found the ff7 gears pdf.

I discovered the data for the minigames are not in the ff7_en.exe, but are rather loaded into memory when you start a minigame.

The data that gets loaded is stored in C:\ff7\data\minigame\chocobo.lgp . Using the lgp tool I can extra the archive and see a bunch of graphical assets and a large 1mb chocobo.wat file.

AFAIK no one has reverse engineered the wat files for minigames... well I at least haven't seen any tools or documentation.

Except maybe @ergonomy_joe who managed to reverse engineer the coaster game. Crazy. That's some next level skill. Kudos.

I found them on this forum and they claim to have done the same for all the minigames except condor - https://forums.qhimm.com/index.php?topic=16507.0

Anyone have a way to reach him? I can't send personal messages yet. Can someone give me privs to do so?

He made an unreleased tool afaik to extract some of the files in the wat back in 2016 https://magnetiktank.blogspot.com/2016/01/ff7s-chocobowat-having-fun-with-3d.html -> the link on geocities is long dead but archive.org has a copy.
https://magnetiktank.blogspot.com/2016/01/ff7-chocobowat.html

I know there are concerns of copyright with his code.

From what I can tell the wat file has a bunch of strings that read a bit like a binary and reference various elements of source code.

If I could just load the code in memory and pause execution and then find where I'm at, I wouldn't need to re the wat file to see the assembly it generates.

Instead of looking for stamina or speed values maybe I can look for the chocobo names. I searched for them in cheat engine, but didn't have any hits.

Then I recalled gears mentioned ff7 doesn't use standard ascii tables for text, but rather uses their own bastardized table.

The text for the chocobo name MIKE is actually 2D 29 2B 25. However searching for an int with those yields nothing... Weird. Prior intuition lead me to believe it might be endianness.

So, I searched for 4 bytes of 25 2B 29 2D and low and behold I found it :) Modifying the characters updates the text on the screen.

Viewing the memory region I can see each chocobo has about 164 bytes between their names.

Most of those bytes change radically once the game starts, so I doubt they are values for the static stamina or top speed. You can zero out most and it doesn't seem to make an impact on the game.

With the address I decided to set a breakpoint on memory write to the chocobo name. However, on loading the betting screen the debugger crashes.

This might be an issue with how minigames are loaded or anticheat, but I found a workaround by using a debugger option in cheat engine to use VEH breakpoints.

This pauses the game the exact instruction a write is called to the chocobo name, landing me exe+373306A.

Dumping the process memory with task manager and searching for the bytes in hxd brings me to the loaded code :) hazzah progress.

Even though we are attached with cheat engine. I can re-attach using IDA. With IDA attached I see the code starts at 401000 from the functions window. Adding the base address plus the position from cheat engine 373306A and we get 772AA3. Jumping to that address and enabling viewing opcodes I can see we are looking at the same piece of memory as cheat engine but now we have more tools. We are in the middle of a long code block in sub_772357, generating psuedocode for the function, and we can see numerous 0->6 for loops showing something is happening for each chocobo. This looks really promising.

I'm not the most familiar with C/asm, but a piece that sticks out to me are a set of ifs.
If a value is <40. Subtract from 40 and divide by 10
If a value is >40 but <60. Subtract it from 60 and divide by 5
If a value is >60 but <80 . Subtract it from 80 and divide it by 5.
If a value is >80. Subtract it from 110 and divide it by 5.

Then divide that value by 8 and save it

Being less than 110 looks oddly similar to the speed ranges.

Using the PSX version of the game and epsxe I can use save states and reload the betting screen quickly.
 
From that I noticed the stats in class C are:
73-87 -> 14 window
251-278 -> 17 window

and in class B are:
89-105 -> 16 window
300-329 -> 29 window

Using cheat engine I searched 110-speed/5 and 110-speed/5/8, but I didn't get any values that updated the screen...

There are a few /10's in the code perhaps they are operating on the dash value?

I'm not sure really where to go from here. I could use some tips from someone who knows how to RE better than I do. 

Code: [Select]
__int16 sub_772357()
{
  int v0; // eax
  int v1; // ST88_4
  int v2; // ST84_4
  __int16 v3; // ST80_2
  int v4; // eax
  __int16 v5; // si
  __int16 v6; // si
  int v7; // eax
  int v8; // esi
  int v9; // eax
  int v10; // eax
  int v11; // ST8C_4
  int v12; // ST78_4
  int v13; // ST7C_4
  int v14; // ST70_4
  int v15; // ST74_4
  int v16; // ST94_4
  int v17; // ST90_4
  char *v19; // [esp+40h] [ebp-134h]
  signed int ii; // [esp+50h] [ebp-124h]
  signed int jj; // [esp+50h] [ebp-124h]
  char *v22; // [esp+5Ch] [ebp-118h]
  __int16 i; // [esp+8Ch] [ebp-E8h]
  __int16 j; // [esp+8Ch] [ebp-E8h]
  __int16 k; // [esp+8Ch] [ebp-E8h]
  __int16 n; // [esp+8Ch] [ebp-E8h]
  __int16 kk; // [esp+8Ch] [ebp-E8h]
  int l; // [esp+90h] [ebp-E4h]
  signed int m; // [esp+90h] [ebp-E4h]
  __int16 *v30; // [esp+98h] [ebp-DCh]
  int v31; // [esp+9Ch] [ebp-D8h]
  int v32; // [esp+A0h] [ebp-D4h]
  int v33; // [esp+A4h] [ebp-D0h]
  int v34; // [esp+ACh] [ebp-C8h]
  int v35[45]; // [esp+B0h] [ebp-C4h]
  float v36; // [esp+164h] [ebp-10h]
  float v37; // [esp+168h] [ebp-Ch]
  float v38; // [esp+16Ch] [ebp-8h]
  int v39; // [esp+170h] [ebp-4h]

  v0 = sub_676578();
  v34 = v0;
  for ( i = 0; i < 6; ++i )
  {
    word_E71158[82 * i + 62] = 224 * i / 6 + 16;
    LOWORD(v0) = i + 1;
  }
  for ( j = 0; j < 40; ++j )
  {
    v1 = sub_7AE9C0() % 6;
    v2 = sub_7AE9C0() % 6;
    v3 = word_E711D4[82 * v1];
    word_E711D4[82 * v1] = word_E711D4[82 * v2];
    word_E711D4[82 * v2] = v3;
    LOWORD(v0) = j + 1;
  }
  for ( k = 0; k < 6; ++k )
  {
    v30 = &word_E71158[82 * k];
    if ( !dword_E71128 || k )
    {
      v5 = word_97A3E0[4 * (unsigned __int8)byte_DC0AF3];
      v30[44] = sub_7AE9C0() % word_97A3E2[4 * (unsigned __int8)byte_DC0AF3] + v5;
      v6 = word_97A3E4[4 * (unsigned __int8)byte_DC0AF3];
      v30[43] = sub_7AE9C0() % word_97A3E6[4 * (unsigned __int8)byte_DC0AF3] + v6;
      v30[47] = 50;
      v30[49] = 100;
      v7 = sub_7AE9C0();
      v30[3] = ((v7 >> 31) ^ abs((_BYTE)v7) & 3) - (v7 >> 31);
      v8 = word_97A3E0[4 * (unsigned __int8)byte_DC0AF3];
      *((_DWORD *)v30 + 27) = sub_7AE9C0() % 300 + v8;
      *((_DWORD *)v30 + 26) = *((_DWORD *)v30 + 27);
      v30[48] = 1;
      v9 = -((sub_7AE9C0() & 7) != 0);
      LOBYTE(v9) = v9 & 0xFE;
      v30[38] = v9 + 2;
      v30[46] = v30[50] / 10;
      v30[67] = 3;
      v30[50] = 50 * (sub_7AE9C0() & 1) + 50;
    }
    else
    {
      v30[44] = ((unsigned __int8)byte_DC0AE7 << 8) + (unsigned __int8)byte_DC0AE6;
      v30[43] = ((unsigned __int8)byte_DC0AE9 << 8) + (unsigned __int8)byte_DC0AE8;
      v30[47] = (unsigned __int8)byte_DC0AEA;
      v30[49] = (unsigned __int8)byte_DC0AEB;
      v4 = sub_7AE9C0();
      v30[3] = ((v4 >> 31) ^ abs((_BYTE)v4) & 3) - (v4 >> 31);
      *((_DWORD *)v30 + 27) = ((unsigned __int8)byte_DC0AF7 << 8) + (unsigned __int8)byte_DC0AF6;
      *((_DWORD *)v30 + 26) = *((_DWORD *)v30 + 27);
      v30[48] = 1;
      v30[38] = (unsigned __int8)byte_DC0AEE;
      v30[46] = v30[50] / 10;
      switch ( byte_DC0AED )
      {
        case 1:
          v30[67] = 1;
          break;
        case 2:
          v30[67] = 2;
          break;
        case 3:
        case 4:
          v30[67] = 0;
          break;
        default:
          v30[67] = 3;
          break;
      }
      v30[50] = (unsigned __int8)byte_DC0AEC;
    }
    if ( v30[50] >= 40 )
    {
      if ( v30[50] >= 60 )
      {
        if ( v30[50] >= 80 )
        {
          v30[41] = 0;
          v10 = (110 - v30[50]) / 5;
        }
        else
        {
          v30[41] = 1;
          v10 = (80 - v30[50]) / 5;
        }
        v30[51] = v10;
      }
      else
      {
        v30[41] = 2;
        v30[51] = (60 - v30[50]) / 5;
      }
    }
    else
    {
      v30[41] = 3;
      v30[51] = (40 - v30[50]) / 10;
    }
    v30[46] = v30[50] / 8;
    v30[76] = *((_WORD *)dword_E7112C + 2);
    v30[65] = k;
    v30[2] = v30[43] / 2;
    v30[39] = v30[43];
    *v30 = 10;
    v30[1] = 11;
    v30[66] = 0;
    v30[77] = -1;
    v30[80] = 0;
    v30[79] = 0;
    v30[78] = 0;
    v30[81] = 0;
    v11 = 256 - v30[62];
    v30[75] = v30[62];
    v30[8] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * *v30 + 8)
            + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * *v30))
           / 256;
    v30[9] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * *v30 + 10)
            + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * *v30 + 2))
           / 256;
    v30[10] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * *v30 + 12)
             + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * *v30 + 4))
            / 256;
    v30[32] = v30[8];
    v30[33] = v30[9];
    v30[34] = v30[10];
    v30[20] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * v30[1] + 8)
             + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * v30[1]))
            / 256;
    v30[21] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * v30[1] + 10)
             + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * v30[1] + 2))
            / 256;
    v30[22] = (v11 * *(signed __int16 *)(dword_E715FC + 24 * v30[1] + 12)
             + v30[62] * *(signed __int16 *)(dword_E715FC + 24 * v30[1] + 4))
            / 256;
    v30[24] = 0;
    v30[25] = 0;
    v30[26] = 0;
    v30[28] = 0;
    v30[29] = 0;
    v30[30] = 0;
    v30[25] = sub_77CFBF(dword_E715FC + 49152, dword_E715FC + 49176);
    v30[56] = 0;
    v30[57] = 0;
    v30[59] = v30[2] / dword_E7101C;
    v30[63] = 0;
    v30[64] = 0;
    v31 = v30[20] - v30[8];
    v32 = v30[21] - v30[9];
    v33 = v30[22] - v30[10];
    sub_6638B0(&v31, &v31);
    v30[29] = sub_77CF40(v31, v33);
    v30[25] = v30[29];
    LOWORD(v0) = k + 1;
  }
  if ( byte_DC0AF2 )
  {
    v12 = dword_E7120C;
    v13 = dword_E71210;
    v14 = dword_E71224;
    v15 = dword_E71228;
    qmemcpy(&unk_E711FC, word_E71158, 0xA4u);
    dword_E7120C = v12;
    dword_E71210 = v13;
    dword_E7123C = v12;
    dword_E71240 = v13;
    dword_E71224 = v14;
    dword_E71228 = v15;
    dword_E71268 += dword_E71268 / 4;
    dword_E71264 += dword_E71264 / 4;
    word_E71252 += word_E71252 / 10;
    LOWORD(v0) = word_E71254 / 10;
    word_E71254 += word_E71254 / 10;
    word_E7125A = 100;
    word_E71260 = 100;
    word_E71202 = 3;
    word_E71282 = 0;
    word_E7128C = 9;
    word_E7128E = 1;
  }
  for ( l = 0; l < 45; ++l )
  {
    v35[l] = l;
    LOWORD(v0) = l + 1;
  }
  for ( m = 0; m < 200; ++m )
  {
    v16 = sub_7AE9C0() % 45;
    v0 = sub_7AE9C0();
    v17 = v35[v16];
    v35[v16] = v35[v0 % 45];
    v35[v0 % 45] = v17;
    LOWORD(v0) = m + 1;
  }
  for ( n = 0; n < 6; ++n )
  {
    if ( byte_DC0AF2 && n == 1 )
    {
      for ( ii = 0; ii < 8; ++ii )
        *((_BYTE *)&dword_E711E0[41 * n] + ii) = byte_97C8A8[ii];
    }
    else
    {
      v22 = (char *)&unk_97CC58 + 7 * v35[n];
      for ( jj = 0; jj < 6; ++jj )
        *((_BYTE *)&dword_E711E0[41 * n] + jj) = *v22++;
      *((_BYTE *)&dword_E711E0[41 * n] + jj) = -1;
      if ( dword_E71128 && n )
        sub_773487(n, 0, 0);
    }
    LOWORD(v0) = n + 1;
  }
  if ( dword_E71128 )
  {
    dword_E711E0[0] = dword_DC0ADC;
    word_E711E4 = word_DC0AE0;
    byte_E711E6 = -1;
  }
  for ( kk = 0; kk < 6; ++kk )
  {
    v19 = (char *)dword_E719E8 + 60 * word_E71158[82 * kk + 72];
    if ( (!byte_DC0AF2 || kk != 1) && (!dword_E71128 || kk) )
    {
      switch ( word_E71158[82 * kk + 3] )
      {
        case 0:
          v19[8] = 0;
          v19[7] = 0;
          v19[6] = 0;
          v38 = (double)(unsigned __int8)v19[6] / 256.0;
          v37 = (double)(unsigned __int8)v19[7] / 256.0;
          v36 = (double)(unsigned __int8)v19[8] / 256.0;
          v39 = 1065353216;
          sub_684E20((int)&v36, *(_DWORD *)(*((_DWORD *)v19 + 13) + 180));
          break;
        case 1:
          v19[6] = 64;
          v19[7] = 64;
          v19[8] = 0;
          v38 = (double)(unsigned __int8)v19[6] / 256.0;
          v37 = (double)(unsigned __int8)v19[7] / 256.0;
          v36 = (double)(unsigned __int8)v19[8] / 256.0;
          v39 = 1065353216;
          sub_684E20((int)&v36, *(_DWORD *)(*((_DWORD *)v19 + 13) + 180));
          break;
        case 2:
          v19[6] = 64;
          v19[7] = 16;
          v19[8] = 0;
          v38 = (double)(unsigned __int8)v19[6] / 256.0;
          v37 = (double)(unsigned __int8)v19[7] / 256.0;
          v36 = (double)(unsigned __int8)v19[8] / 256.0;
          v39 = 1065353216;
          sub_684E20((int)&v36, *(_DWORD *)(*((_DWORD *)v19 + 13) + 180));
          break;
        case 3:
          v19[6] = 0;
          v19[7] = 16;
          v19[8] = 64;
          v38 = (double)(unsigned __int8)v19[6] / 256.0;
          v37 = (double)(unsigned __int8)v19[7] / 256.0;
          v36 = (double)(unsigned __int8)v19[8] / 256.0;
          v39 = 1065353216;
          sub_684E20((int)&v36, *(_DWORD *)(*((_DWORD *)v19 + 13) + 180));
          break;
        default:
          break;
      }
    }
    LOWORD(v0) = kk + 1;
  }
  return v0;
}

Using black chocobo I created a chocobo with set values and that helped a bit.

Taking a look deeper look at the name in memory. The name is actually the trailing piece of data.

Each chocobo has 164 bytes of memory.

Code: [Select]
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 0A 00 0B 00 BC 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A
00 23 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 40 FA 0A 00 24 DD 00 00 00 00 00 04 00 00 00 00 00 00 00
04 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 78
07 00 00 00 00 00 00 78 07 40 08 00 00 0A 00 51 00 01 00 52
00 53 00 05 00 3D 10 00 00 3D 10 00 00 00 00 00 00 00 00 9F
00 00 00 00 00 A5 00 00 00 00 00 00 00 00 00 03 00 23 48 4F
43 4F 12 FF

Name = Choco2
Top Speed = 62 (-48 bytes /34)
Stamina = 415 (-28 bytes and -32 bytes /10)
Acceleration = 81 (-42 bytes)
Cooperation = 82 (-38 bytes)
Stamina = 4157 (-28 bytes and -32 bytes)
Intelligence = 83 (-36 bytes)
Run speed = x778  = 1912/3323 -> (-50 bytes)
Sprint Speed = x840 = 2112 /3523 (-48 bytes)

With this I am able to modify the speed/stamina values.

The second chocobo:
Code: [Select]
00 06 00 00 00 1E 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00
00 0A 00 0B 00 61 03 02 00 00 00 00 00 00 00 00 00 E7 F9 0A
00 10 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 41 FA 0A 00 11 DF 00 00 00 00 00 04 00 00 00 00 00 00 00
04 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 C3
06 00 00 02 00 00 00 C3 06 E5 09 00 00 06 00 32 00 01 00 64
00 32 00 02 00 0A 0A 00 00 0A 0A 00 00 00 00 00 00 00 00 90
00 00 00 00 00 35 00 00 00 00 00 01 00 00 00 03 00 39 2F 35
2E 27 FF FF
74,257,YOUNG,bronze

-28 Bytes = Stamina = x0a0a/10=257
-36 Bytes = Intelligence = 32
-38 Bytes = Cooperation = 64
-42 Bytes = Acceleration = 32
-48 Bytes = TS x9e5/34 = 74
-50 Bytes = Run speed = x6c3 = 1731

-40 Bytes = 1 = UNK
-44 Bytes = 6 = UNK

According to the psuedo code and validated with the values intel/coop/acceleration are either 32, or 64 for ai.

Here's a quick script to parse a blob of memory to a csv
Code: [Select]
def decodeMe(myin):
 a =""" !"#$%&'()*+,-./0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
 res = ""
 smy = myin.split(" ")
 for val in smy:
  dec = int(val, 16)
  if dec >= len(a):
   if dec != 255:
    print("replacing char: "+val+ " with space")
   dec = 0
  res += a[dec]
 return(res)

def parse(thebytes):
 thebytes = thebytes.split()
 nameVal = decodeMe(" ".join(thebytes[157:]))
 nameVal = nameVal.rstrip()
 stamina = thebytes[130]+thebytes[129]
 dec = int(stamina, 16)
 staminaVal = "0x"+stamina+"/10 = "+str(dec/10)
 stamina2 = thebytes[126]+thebytes[125]
 dec = int(stamina2, 16)
 stamina2Val = "0x"+stamina2+"/10 = "+str(dec/10)
 unk1Val = thebytes[123]
 intelVal = thebytes[121]
 coopVal = thebytes[119]
 unk2Val = thebytes[117]
 accVal = thebytes[115]
 unk3Val = thebytes[113]
 speed = thebytes[110]+thebytes[109]
 dec = int(speed, 16)
 speedVal = "0x"+speed+"/34 = "+str(dec/34)
 rs = thebytes[108]+thebytes[107]
 dec = int(rs, 16)
 runSpeedVal = "0x"+rs+" = "+str(dec)
 unk4Val = thebytes[103]
 rs = thebytes[100]+thebytes[99]
 dec = int(rs, 16)
 runSpeed2Val = "0x"+rs+" = "+str(dec)
 first = " ".join(thebytes[0:91])
 first = first.replace("00 00","")
 first = first.replace("  "," ")
 otherVal = first
 return(",,"+nameVal+","+staminaVal+","+stamina2Val+","+intelVal+","+coopVal+","+accVal+","+speedVal+","+runSpeedVal+","+runSpeed2Val+","+unk1Val+","+unk2Val+","+unk3Val+","+unk4Val+","+otherVal+",")
def main():
 allbytes = input("Please paste the space separated bytes, \nstarting with FF FF FF FF 00 00 00 00 FF FF FF FF and ending with FF FF FF FF: ")
 print("Parsing: "+str(len(allbytes)))
 if len(allbytes) != 3623:
  print("Wrong size, expecting 3623 bytes")
  input("Press any key to continue")
 sbytes = allbytes[105:]
 
 add = "data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other,\n"
 for i in [1, 2, 3, 4, 5, 6]:
  tbytes = sbytes[0:492]
  print("\n"+tbytes)
  add += parse(tbytes) +"\n"
  sbytes = sbytes[492:]
 with open("out.csv","a+") as f:
  f.write(add)
 print("done -> results appended to out.csv")
main()

I generated some csv collecting data from 10 runs.
Code: [Select]
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
364512,,Choco2,0x103D/10 = 415.7,0x103D/10 = 415.7,53,52,51,0x0840/34 = 62.11764705882353,0x0778 = 1912,0x0778 = 1912,5,1,0A,0,00 0A 00 0B 00 BC 03 03   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,bronze,YOUNG,0x0A0A/10 = 257.0,0x0A0A/10 = 257.0,32,64,32,0x09E5/34 = 74.5,0x06C3 = 1731,0x06C3 = 1731,2,1,6,2,00 06 00 1E 00 A5 00 20 03 FF FF   0A 00 0B 00 61 03 02   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,plat,SANDY,0x0ABF/10 = 275.1,0x0ABF/10 = 275.1,64,64,32,0x0B00/34 = 82.82352941176471,0x0726 = 1830,0x0726 = 1830,2,1,0C,0,00 0B 00 01 00 35 00 20 03 FF FF   0A 00 0B 00 93 03   E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,plat,JOEL,0x09EC/10 = 254.0,0x09EC/10 = 254.0,32,64,32,0x0A80/34 = 79.05882352941177,0x0736 = 1846,0x0736 = 1846,2,1,6,2,00 0C 00 02 00 10 00 20 03 FF FF   0A 00 0B 00 9B 03   E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,gold,JU,0x0A97/10 = 271.1,0x0A97/10 = 271.1,32,64,32,0x0A62/34 = 78.17647058823529,0x073A = 1850,0x073A = 1850,2,1,6,2,00 0D 00 03 00 CA 00 20 03 FF FF   0A 00 0B 00 9D 03 01   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
sprinting,silver,SARA,0x0ACF/10 = 276.7,0x0ACF/10 = 276.7,64,64,32,0x0A1B/34 = 76.08823529411765,0x075F = 1887,0x075F = 1887,2,1,0C,0,00 0E 00 04 00 80 00 20 03 FF FF   0A 00 0B 00 AF 03   E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
352164,silver,FOX,0x0AA7/10 = 272.7,0x0AA7/10 = 272.7,32,64,32,0x0A5E/34 = 78.05882352941177,0x073D = 1853,0x073D = 1853,2,1,6,2,00 0A 00 0B 00 9E 03 03   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,plat,JOHN,0x0AC9/10 = 276.1,0x0AC9/10 = 276.1,32,64,32,0x0B34/34 = 84.3529411764706,0x072A = 1834,0x072A = 1834,2,1,6,2,00 0A 00 1E 00 CA 00 20 03 FF FF   0A 00 0B 00 95 03   E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,gold,SEAN,0x0A00/10 = 256.0,0x0A00/10 = 256.0,64,64,32,0x0AA9/34 = 80.26470588235294,0x06E7 = 1767,0x06E7 = 1767,2,1,0C,0,00 0B 00 01 00 5A 00 20 03 FF FF   0A 00 0B 00 73 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,bronze,TOM,0x09F9/10 = 255.3,0x09F9/10 = 255.3,32,64,32,0x0A2D/34 = 76.61764705882354,0x0715 = 1813,0x0715 = 1813,2,1,6,2,00 0C 00 02 00 A5 00 20 03 FF FF   0A 00 0B 00 8A 03 02   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
sprinting,gold,PAULA,0x0A21/10 = 259.3,0x0A21/10 = 259.3,32,64,32,0x0B38/34 = 84.47058823529412,0x0752 = 1874,0x0752 = 1874,2,1,6,2,00 0D 00 03 00 10 00 20 03 FF FF   0A 00 0B 00 A9 03 01   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,bronze,JAMES,0x09ED/10 = 254.1,0x09ED/10 = 254.1,64,64,32,0x0B0B/34 = 83.1470588235294,0x06D3 = 1747,0x06D3 = 1747,2,1,0C,0,00 0E 00 04 00 80 00 20 03 FF FF   0A 00 0B 00 69 03 02   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
145326,silver,HARVEY,0x0A69/10 = 266.5,0x0A69/10 = 266.5,64,64,32,0x0B17/34 = 83.5,0x0731 = 1841,0x0731 = 1841,2,1,0C,0,00 0A 00 0B 00 98 03 03   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,silver,DARIO,0x09DA/10 = 252.2,0x09DA/10 = 252.2,64,64,32,0x0A8A/34 = 79.3529411764706,0x06B5 = 1717,0x06B5 = 1717,2,1,0C,0,00 0A 00 1E 00 35 00 20 03 FF FF   0A 00 0B 00 5A 03 03   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,JENNY,0x0ADD/10 = 278.1,0x0ADD/10 = 278.1,32,64,32,0x0B2E/34 = 84.17647058823529,0x06BD = 1725,0x06BD = 1725,2,1,6,2,00 0B 00 01 00 A5 00 20 03 FF FF   0A 00 0B 00 5E 03 03   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,plat,SAM,0x0A9C/10 = 271.6,0x0A9C/10 = 271.6,32,64,32,0x0BA4/34 = 87.6470588235294,0x06D2 = 1746,0x06D2 = 1746,2,1,6,2,00 0C 00 02 00 5A 00 20 03 FF FF   0A 00 0B 00 69 03   E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,silver,LIA,0x09F5/10 = 254.9,0x09F5/10 = 254.9,64,64,32,0x0A10/34 = 75.76470588235294,0x074E = 1870,0x074E = 1870,2,1,0C,0,00 0D 00 03 00 CA 00 20 03 FF FF   0A 00 0B 00 A7 03 03   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,plat,JOHN,0x09DB/10 = 252.3,0x09DB/10 = 252.3,64,64,32,0x09D9/34 = 74.1470588235294,0x06CF = 1743,0x06CF = 1743,2,1,0C,0,00 0E 00 04 00 80 00 20 03 FF FF   0A 00 0B 00 67 03   E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
452613,silver,RICA,0x14A8/10 = 528.8,0x0B3A/10 = 287.4,32,64,32,0x0A11/34 = 75.79411764705883,0x0754 = 1876,0x0754 = 1876,2,1,6,2,00 FE 00 14 01 04 01 F2 00 E1 00 A8 03 A9 03 03 00 09  00 BF F3 09 00 B4 DD 19 07  1D 03  E6 F9 0A 00 C6 DD  28 04  3D 04  E6 F9 0A 00 C6 DD
,plat,AIMEE,0x1552/10 = 545.8,0x0BD4/10 = 302.8,64,64,32,0x0ACE/34 = 81.3529411764706,0x06F8 = 1784,0x06F8 = 1784,2,1,0C,0,00 0A 00 C0 FD 80 00 20 03 FF FF   A9 03 AA 03  09  00 22 F4 09 00 46 DD 88 0A 00 80  51 07 00 80  3F F4 0A 00 1F DF  64 06  20 07  E6 F9 0A 00 23 DD
,bronze,JENNY,0x1438/10 = 517.6,0x05C4/10 = 147.6,64,64,32,0x0B0F/34 = 83.26470588235294,0x072B = 1835,0x057F = 1407,2,1,0C,0,00 0B 00 01 00 A5 00 20 03 FF FF   A8 03 A9 03 0A 00 02 00 09  00 99 F3 09 00 0E DD 7C 07  07 08  E7 F9 0A 00 B2 DF  6C 04  6F 04  E7 F9 0A 00 B2 DF
,silver,GREY,0x158E/10 = 551.8,0x0B4A/10 = 289.0,64,64,32,0x0B02/34 = 82.88235294117646,0x0744 = 1860,0x0744 = 1860,2,1,0C,0,00 0C 00 02 00 10 00 20 03 FF FF   AC 03 AD 03 03 00 09  00 1B F5 09 00 8B DC 01 09  5A 0D  4D F5 0A 00 3D DC  A4 02  B0 02  E6 F9 0A 00 81 DC
,silver,SANDY,0x15A2/10 = 553.8,0x0B94/10 = 296.4,32,64,32,0x0B58/34 = 85.41176470588235,0x0738 = 1848,0x0738 = 1848,2,1,6,2,00 0D 00 03 00 CA 00 20 03 FF FF   AB 03 AC 03 03 00 09  00 DE F4 09 00 22 DD 74 01 00 80  DD 09  F3 F4 0A 00 3D DC  53 01  4F 01  E7 F9 0A 00 6D DE
sprinting,gold,DAN,0x14FE/10 = 537.4,0x0013/10 = 1.9,32,64,32,0x09CE/34 = 73.82352941176471,0x070C = 1804,0x070C = 1804,2,1,6,2,00 0E 00 04 00 5A 00 20 03 FF FF   AA 03 AB 03 01 00 09  00 50 F4 09 00 DE DD C6 03  7E 06  99 F4 0A 00 3D DC  30 01  F6  00 E7 F9 0A 00 10 DF
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
213564,silver,FOX,0x0A15/10 = 258.1,0x0A15/10 = 258.1,32,64,32,0x0A47/34 = 77.38235294117646,0x0729 = 1833,0x0729 = 1833,2,1,6,2,00 0A 00 0B 00 94 03 03   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,silver,MARIE,0x0AC8/10 = 276.0,0x0AC8/10 = 276.0,32,64,32,0x0B45/34 = 84.8529411764706,0x0762 = 1890,0x0762 = 1890,2,1,6,2,00 0A 00 1E 00 35 00 20 03 FF FF   0A 00 0B 00 B1 03 03   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,plat -s,ANDY,0x0A9B/10 = 271.5,0x0A9B/10 = 271.5,64,64,32,0x0B11/34 = 83.32352941176471,0x0723 = 1827,0x0723 = 1827,2,1,0C,0,00 0B 00 01 00 CA 00 20 03 FF FF   0A 00 0B 00 91 03   E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
sad face,bronz -s,DAN,0x0A30/10 = 260.8,0x0A30/10 = 260.8,32,64,32,0x0B80/34 = 86.58823529411765,0x0725 = 1829,0x0725 = 1829,2,1,6,2,00 0C 00 02 00 80 00 20 03 FF FF   0A 00 0B 00 92 03 02   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,plat,MIKE,0x0AE0/10 = 278.4,0x0AE0/10 = 278.4,32,64,32,0x0B33/34 = 84.32352941176471,0x06DE = 1758,0x06DE = 1758,2,1,6,2,00 0D 00 03 00 10 00 20 03 FF FF   0A 00 0B 00 6F 03   E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,gold,PAULA,0x0ACF/10 = 276.7,0x0ACF/10 = 276.7,64,64,32,0x0A14/34 = 75.88235294117646,0x0708 = 1800,0x0708 = 1800,2,1,0C,0,00 0E 00 04 00 A5 00 20 03 FF FF   0A 00 0B 00 84 03 01   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
142653,gold,JU,0x09F9/10 = 255.3,0x09F9/10 = 255.3,64,64,32,0x0B30/34 = 84.23529411764706,0x06D7 = 1751,0x06D7 = 1751,2,1,0C,0,00 0A 00 0B 00 6B 03 01   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,plat,SARA,0x0A0E/10 = 257.4,0x0A0E/10 = 257.4,64,64,32,0x0B78/34 = 86.3529411764706,0x06DE = 1758,0x06DE = 1758,2,1,0C,0,00 0A 00 1E 00 5A 00 20 03 FF FF   0A 00 0B 00 6F 03   E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,bronze,SAM,0x0A86/10 = 269.4,0x0A86/10 = 269.4,64,64,32,0x0AE2/34 = 81.94117647058823,0x06D1 = 1745,0x06D1 = 1745,2,1,0C,0,00 0B 00 01 00 10 00 20 03 FF FF   0A 00 0B 00 68 03 02   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,plat,SEAN,0x0A17/10 = 258.3,0x0A17/10 = 258.3,64,64,32,0x0B65/34 = 85.79411764705883,0x0712 = 1810,0x0712 = 1810,2,1,0C,0,00 0C 00 02 00 35 00 20 03 FF FF   0A 00 0B 00 89 03   E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,silver,ELEN,0x09CB/10 = 250.7,0x09CB/10 = 250.7,64,64,32,0x0A3B/34 = 77.02941176470588,0x06BB = 1723,0x06BB = 1723,2,1,0C,0,00 0D 00 03 00 80 00 20 03 FF FF   0A 00 0B 00 5D 03 03   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,KNIGHT,0x0A02/10 = 256.2,0x0A02/10 = 256.2,32,64,32,0x0A18/34 = 76.0,0x0709 = 1801,0x0709 = 1801,2,1,6,2,00 0E 00 04 00 A5 00 20 03 FF FF   0A 00 0B 00 84 03 03   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
425136,bronze,JENNY,0x0A9D/10 = 271.7,0x0A9D/10 = 271.7,64,64,32,0x0B11/34 = 83.32352941176471,0x06F0 = 1776,0x06F0 = 1776,2,1,0C,0,00 0A 00 0B 00 78 03 02   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,gold,YOUNG,0x0AEF/10 = 279.9,0x0AEF/10 = 279.9,32,64,32,0x0A4C/34 = 77.52941176470588,0x06E1 = 1761,0x06E1 = 1761,2,1,6,2,00 0A 00 1E 00 5A 00 20 03 FF FF   0A 00 0B 00 70 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
sprinting,bronze,JAMES,0x0ADA/10 = 277.8,0x0ADA/10 = 277.8,32,64,32,0x0A3C/34 = 77.05882352941177,0x0769 = 1897,0x0769 = 1897,2,1,6,2,00 0B 00 01 00 A5 00 20 03 FF FF   0A 00 0B 00 B4 03 02   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,gold,KNIGHT,0x0A5B/10 = 265.1,0x0A5B/10 = 265.1,32,64,32,0x0AC4/34 = 81.05882352941177,0x06EF = 1775,0x06EF = 1775,2,1,6,2,00 0C 00 02 00 10 00 20 03 FF FF   0A 00 0B 00 77 03 01   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,bronze,ARL,0x0A2C/10 = 260.4,0x0A2C/10 = 260.4,32,64,32,0x0B0A/34 = 83.11764705882354,0x0769 = 1897,0x0769 = 1897,2,1,6,2,00 0D 00 03 00 CA 00 20 03 FF FF   0A 00 0B 00 B4 03 02   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,bronze,JOEL,0x0A9E/10 = 271.8,0x0A9E/10 = 271.8,32,64,32,0x0AC4/34 = 81.05882352941177,0x06BF = 1727,0x06BF = 1727,2,1,6,2,00 0E 00 04 00 80 00 20 03 FF FF   0A 00 0B 00 5F 03 02   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
352164,gold,LIA,0x09FD/10 = 255.7,0x09FD/10 = 255.7,64,64,32,0x0B11/34 = 83.32352941176471,0x06F9 = 1785,0x06F9 = 1785,2,1,0C,0,00 0A 00 0B 00 7C 03 01   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,silver,ROBER,0x0AD0/10 = 276.8,0x0AD0/10 = 276.8,64,64,32,0x0B05/34 = 82.97058823529412,0x06F9 = 1785,0x06F9 = 1785,2,1,0C,0,00 0A 00 1E 00 35 00 20 03 FF FF   0A 00 0B 00 7C 03 03   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,gold,PAULA,0x0A56/10 = 264.6,0x0A56/10 = 264.6,64,64,32,0x0B31/34 = 84.26470588235294,0x073A = 1850,0x073A = 1850,2,1,0C,0,00 0B 00 01 00 10 00 20 03 FF FF   0A 00 0B 00 9D 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,NANCY,0x0A91/10 = 270.5,0x0A91/10 = 270.5,64,64,32,0x0A21/34 = 76.26470588235294,0x0758 = 1880,0x0758 = 1880,2,1,0C,0,00 0C 00 02 00 A5 00 20 03 FF FF   0A 00 0B 00 AC 03 03   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,plat,JOEL,0x0A49/10 = 263.3,0x0A49/10 = 263.3,64,64,32,0x0B7F/34 = 86.55882352941177,0x0706 = 1798,0x0706 = 1798,2,1,0C,0,00 0D 00 03 00 5A 00 20 03 FF FF   0A 00 0B 00 83 03   E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,gold,YOUNG,0x0A9A/10 = 271.4,0x0A9A/10 = 271.4,64,64,32,0x0A2A/34 = 76.52941176470588,0x06C7 = 1735,0x06C7 = 1735,2,1,0C,0,00 0E 00 04 00 CA 00 20 03 FF FF   0A 00 0B 00 63 03 01   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
614235,gold,YOUNG,0x0AD2/10 = 277.0,0x0AD2/10 = 277.0,32,64,32,0x0AC5/34 = 81.08823529411765,0x0734 = 1844,0x0734 = 1844,2,1,6,2,00 0A 00 0B 00 9A 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,JENNY,0x0A5D/10 = 265.3,0x0A5D/10 = 265.3,32,64,32,0x0A2B/34 = 76.55882352941177,0x0742 = 1858,0x0742 = 1858,2,1,6,2,00 0A 00 1E 00 A5 00 20 03 FF FF   0A 00 0B 00 A1 03 03   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,bronze,ELEN,0x0A9D/10 = 271.7,0x0A9D/10 = 271.7,64,64,32,0x0A56/34 = 77.82352941176471,0x06C2 = 1730,0x06C2 = 1730,2,1,0C,0,00 0B 00 01 00 CA 00 20 03 FF FF   0A 00 0B 00 61 03 02   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,gold,DARIO,0x09DF/10 = 252.7,0x09DF/10 = 252.7,32,64,32,0x09E8/34 = 74.58823529411765,0x0711 = 1809,0x0711 = 1809,2,1,6,2,00 0C 00 02 00 35 00 20 03 FF FF   0A 00 0B 00 88 03 01   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,bronze,JOEL,0x09F2/10 = 254.6,0x09F2/10 = 254.6,64,64,32,0x0AF8/34 = 82.58823529411765,0x074D = 1869,0x074D = 1869,2,1,0C,0,00 0D 00 03 00 80 00 20 03 FF FF   0A 00 0B 00 A6 03 02   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,plat,TERRY,0x09DA/10 = 252.2,0x09DA/10 = 252.2,64,64,32,0x0AF5/34 = 82.5,0x0748 = 1864,0x0748 = 1864,2,1,0C,0,00 0E 00 04 00 5A 00 20 03 FF FF   0A 00 0B 00 A4 03   E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
625143,plat,JOEL,0x0AA2/10 = 272.2,0x0AA2/10 = 272.2,64,64,32,0x0A39/34 = 76.97058823529412,0x0712 = 1810,0x0712 = 1810,2,1,0C,0,00 0A 00 0B 00 89 03   E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,silver,JU,0x0A57/10 = 264.7,0x0A57/10 = 264.7,64,64,32,0x0AE0/34 = 81.88235294117646,0x0759 = 1881,0x0759 = 1881,2,1,0C,0,00 0A 00 1E 00 5A 00 20 03 FF FF   0A 00 0B 00 AC 03 03   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
,bronze,SAMMY,0x0A8E/10 = 270.2,0x0A8E/10 = 270.2,64,64,32,0x0BAA/34 = 87.82352941176471,0x0712 = 1810,0x0712 = 1810,2,1,0C,0,00 0B 00 01 00 35 00 20 03 FF FF   0A 00 0B 00 89 03 02   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
,plat,ROBIN,0x0AA5/10 = 272.5,0x0AA5/10 = 272.5,32,64,32,0x0BB0/34 = 88.0,0x06F4 = 1780,0x06F4 = 1780,2,1,6,2,00 0C 00 02 00 CA 00 20 03 FF FF   0A 00 0B 00 7A 03   E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
,gold,ARL,0x0A02/10 = 256.2,0x0A02/10 = 256.2,32,64,32,0x0A59/34 = 77.91176470588235,0x06CF = 1743,0x06CF = 1743,2,1,6,2,00 0D 00 03 00 10 00 20 03 FF FF   0A 00 0B 00 67 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,gold,SAM,0x0AD2/10 = 277.0,0x0AD2/10 = 277.0,64,64,32,0x0B62/34 = 85.70588235294117,0x06B2 = 1714,0x06B2 = 1714,2,1,0C,0,00 0E 00 04 00 A5 00 20 03 FF FF   0A 00 0B 00 59 03 01   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
Chocobo colors are always in the same order. I'm not seeing any patterns...

Acceleration and Cooperation are always the same.

Jockey color definitely has an impact if bronze.

To test if Intelligence matters I set all the values I know to be to the same, using this quick script. The unknown values don't seem change so I'm replacing the last half of the chocobo values. The contents of the first half is still a mystery.
Code: [Select]
allbytes = ""
print(allbytes[0:105],end='')
sbytes = allbytes[105:]
p2bytes = ""
for i in [1, 2, 3, 4, 5, 6]:
 tbytes = sbytes[0:492]
 p1bytes = tbytes[0:297]
 if not p2bytes:
  p2bytes = tbytes[297:-22]
 p3bytes = tbytes[470:]
 print(p1bytes+p2bytes+p3bytes,end='')
 sbytes = sbytes[492:]
print(sbytes,end='')
To be honest I'm kinda surprised this doesn't crash the game, there's a weird artifact where the chocobos group together, but don't overlap at race start.
Code: [Select]
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
,gold,RICA,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0A 00 0B 00 8F 03 01   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
sprinting,silver,GEORGE,0x0A41/10 = 262.5,0x0A41/10 = 262.5,32,64,32,0x0ACD/34 = 81.32352941176471,0x06FF = 1791,0x06FF = 1791,2,1,6,2,00 0A 00 1E 00 10 00 20 03 FF FF   0A 00 0B 00 7F 03 03   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,gold,JAMES,0x0AED/10 = 279.7,0x0AED/10 = 279.7,32,64,32,0x0B75/34 = 86.26470588235294,0x0739 = 1849,0x0739 = 1849,2,1,6,2,00 0B 00 01 00 80 00 20 03 FF FF   0A 00 0B 00 9C 03 01   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
sprinting,gold,AIMEE,0x0A8A/10 = 269.8,0x0A8A/10 = 269.8,64,64,32,0x0B67/34 = 85.8529411764706,0x06C7 = 1735,0x06C7 = 1735,2,1,0C,0,00 0C 00 02 00 CA 00 20 03 FF FF   0A 00 0B 00 63 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,ROBIN,0x0A98/10 = 271.2,0x0A98/10 = 271.2,64,64,32,0x09E2/34 = 74.41176470588235,0x06FB = 1787,0x06FB = 1787,2,1,0C,0,00 0D 00 03 00 A5 00 20 03 FF FF   0A 00 0B 00 7D 03 03   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,gold,JENNY,0x0A2D/10 = 260.5,0x0A2D/10 = 260.5,32,64,32,0x0ADC/34 = 81.76470588235294,0x0714 = 1812,0x0714 = 1812,2,1,6,2,00 0E 00 04 00 5A 00 20 03 FF FF   0A 00 0B 00 8A 03 01   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
341526,,,,,,,,,,,,,,,
data,color,name,stamina1,stamina2,intelligence,cooperation,acceleration,top speed,run speed,runspeed2,unk1,unk2,unk3,unk4,other
,gold,RICA,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,     00 0A 00 0B 00 8F 03 01   00 E7 F9 0A 00 B2 DF     41 FA 0A 00 B3 DF  00 04  00 04  E7 F9 0A 00 B2 DF
sprinting,silver,GEORGE,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0A 00 1E 00 10 00 20 03 FF FF   0A 00 0B 00 7F 03 03   00 E6 F9 0A 00 C6 DD     40 FA 0A 00 C7 DD  00 04  00 04  E6 F9 0A 00 C6 DD
,gold,JAMES,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0B 00 01 00 80 00 20 03 FF FF   0A 00 0B 00 9C 03 01   00 E6 F9 0A 00 81 DC     40 FA 0A 00 82 DC  00 04  00 04  E6 F9 0A 00 81 DC
sprinting,gold,AIMEE,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0C 00 02 00 CA 00 20 03 FF FF   0A 00 0B 00 63 03 01   00 E6 F9 0A 00 23 DD     40 FA 0A 00 24 DD  00 04  00 04  E6 F9 0A 00 23 DD
,silver,ROBIN,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0D 00 03 00 A5 00 20 03 FF FF   0A 00 0B 00 7D 03 03   00 E7 F9 0A 00 6D DE     41 FA 0A 00 6E DE  00 04  00 04  E7 F9 0A 00 6D DE
,gold,JENNY,0x09C4/10 = 250.0,0x09C4/10 = 250.0,64,64,32,0x0A91/34 = 79.55882352941177,0x071E = 1822,0x071E = 1822,2,1,0C,0,00 0E 00 04 00 5A 00 20 03 FF FF   0A 00 0B 00 8A 03 01   00 E7 F9 0A 00 10 DF     41 FA 0A 00 11 DF  00 04  00 04  E7 F9 0A 00 10 DF
461253,,,,,,,,,,,,,,,
I then gave C1 a lower intel of 32 and got the same ordering.

So at least in C class those three values do nothing.

I increased the top speed by 34 (1/kmh) on c1 and it became 164253. So speed matters, a higher speed will give a higher ranking.

Sprinting doesn't appear to matter 4 beat 6 and 1, but 2 lost to 5.

Increasing and decreasing stamina a little and a lot does nothing to the order, it appears stamina does not matter.

Changing the name didn't make a difference.

Altering the run speed did, higher run speeds give higher rankings.

So I need to figure out how run speed is calculated.

Let's see if we can figure out the rest of the 164 chocobo bytes.

I didn't see the max run value I put in black chocobo in memory indicating it's not used.

Byte -60 = 0=normal, 1=normal, 2=sprinting, 3=normal, 4=normal
2 on c2 = 461253
1 on c2 = 564312
0 on c2 = 643125
3 on c2 = 643125
4 on c2 = 643125
So sprinting does change the outcome. I haven't seen this byte set to 1 yet.

-67, -68 = no change
-70,-71,-72 = no change
-77 = Is a weird byte
Setting 4 to 0 on chocobo 2 changes the results from 461253 to 632145
Setting 4 to 3 on chocobo 2 changes the results from 461253 to 123564
Setting 4 to 2 on chocobo 2 changes the results from 461253 to 134526
Setting 4 to 1 on chocobo 2 changes the results from 461253 to 123564
Setting 4 to 5 on chocobo 2 changes the results from 461253 to 413625
Setting 4 to 80 on chocobo 2 changes the result from 461253 to 136254

-85 = Is another weird byte, changing 4 gives different results than changing 4 from -77
-91,-92 = no change
-93,94,54 = no change
-115,116 = messes with camera angle, same order
-118,119,120 = different weird camera angle, same order
-130,134 = camera angle, same order
-145 = when FF/00 -> 461253
-145,146 = random when set to 00 00?, 461253 when 01 01
-146 = when FF/00 -> 461253
-147,148 = when set to 00 00 on c6, that chocobo sprints until exhausted, but no one passes until the very end 432156
-150 = 35-> 0, 613254
-154 = 5-> 4 changes chocobo to color of No.5, but crashes when loading race
-156 = Changes jockey color, but can also change the jockey to be any object in the minigame
15 = gold, 14 = silver, 13 = gold, 12 = gold, 11 = silver, 10 =gold, 9 = hard crash, 1= no rider, 2=no rider, 3= no rider, 4=no rider, 5=graphic crash,6=hard crash, 7 = hard crash, 8=hard crash
16=mog in rainbow pipe animation overlayed
17=clam animation
The numbers change based on the generated value chocobo6's rider is 15 chocobo1's rider is 9. chocobo1 is 0 chocobo 6 is 5. The race crashes when the chocobo gets to the object on the path.

So to summarize I know these variables modify the outcome.
Top Speed, Sprinting, Jockey Color, Run Speed (hidden value), and Sprint Likelihood (Hidden value, not tied to jockey color, same across a class).

Name and stamina do not seem to matter. (stamina only matters when they run out, the chance they have a high top speed and run out of stamina is really low, and they don't just barely run out so the difference in range really doesn't help too much)

-77,85,145,146,147,148,150 can modify results too

77,85 = are always set to 4 though in class c
145-148 = are always 20 03 FF FF in class c
150 is the only byte that differs.  However giving two chocobos the same value doesn't put them next to each other with the same other stats.

A higher ranking jockey doesn't mean it has a higher run speed. Perhaps it makes a difference in how run speed is calculated...
« Last Edit: 2022-02-03 08:27:36 by ff7man »

nfitc1

  • *
  • Posts: 2989
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FF7] Chocobo Betting
« Reply #2 on: 2022-01-26 15:14:02 »
I'm going to provide some preliminary speculation based on my own observations on this minigame.

Stats are important, but in the end it's all random. Whether the chocobos will sprint or if they will veer left or right and at what time. The chocobo's rank is based on a holistic calculation of all its stats. I can't tell you HOW that calculation is performed. The higher the intelligence, the less they will swerve. The higher the stamina, the longer they can sprint. yadda, yadda.

It also seems like there's some external data at work here. As the race begins, the game just "boosts" some of the positions. It sort of chooses which two it wants to win and gives them some stat-independent advantages at random intervals. These two are more likely to win and it's just completely random whether they do or not. The highest ranks don't always get boosted either.

What I would do: Since you have identified where these are in the PSX memory, what you can do is make a savestate before the chocobos begin running, check the winners vs losers, reload the save, swap the winners' stats with the losers' stats and see how that affects the outcome.

Addendum: I haven't seen ergonomy_joe for a while. Not saying he's not reading the forums. I still do, but I rarely post anymore.
I have looked at the chocobo.wat file, but it doesn't seem independent enough to parse into individual files. This is a "big" hassle in the lgp files too. They don't contain enough metadata on their own to be used separately. It's all contained in the exe file where different fragments start and their size.
However, looking at the bytes inside it looks like it contains several parts that consist of default values for some things. Possibly even proprietary AI scripts which I can't immediately find handlers for. I say that because they LOOK like scripts, but not asm code. It's more like the WM scripts or Field scripts, but they're quite short. It also likely contains the modeling info for the different tracks though the model data and textures for the objects are saved independently in the chocobo.lgp file.
« Last Edit: 2022-01-26 16:49:34 by nfitc1 »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #3 on: 2022-01-26 20:36:32 »
I think how often they sprint is just set by the bytes[10-13] class C is 20 03 FF FF.

Hmmm. Why do you think there's a boosting stat for the two it wants to win?

I ran a similar test by replacing all the chocobos with the same stats. I'll take a stab at doing that test though.

I've continued looking at things these last couple days.

There are actually 7.5 chocobos in memory and not 6. Something I didn't notice until I made a better script to rip out data. It's a little confusing. The first chocobo doesn't get fully initialized and has some null data in regards to "willingness to sprint" values. The 7th chocobo is a copy of the chocobo currently in first. At the prerace screen this is the first chocobo fully initialized. The half chocobo is data following the 7th chocobo. It appears to just be a copy of the class/"willingness to sprint" values for the 7th chocobo.

Bytes 152 -> show the current order the chocobos are in

I had an idea that I might be able to bruteforce the run speed with enough data. So I wrote out a script to play the game in super speed in an emulator. It opens the chocobo betting game. Dumps the memory into a nice csv. Patches the memory to make the chocobos invisible (modifies all chocobo pointers to be the next jocker pointer). Takes a screenshot. Uses opencv to identify the jockey color (I apply a filter, crop, get the average color, and report found when a close enough match happens. It's surprisingly hard to do when the jockey is constantly rotating). Patches memory again to make the chocobos visible. Runs the race. Dumps the data again after the race into a nice csv. Resets the game state, waits a random time, and repeats.

I ran it for a few hours and generated data for 683 runs of class c. This data dump has the 7th chocobo and the set includes winning order and jockey color.

I can't attach files, so hopefully this gets mod approval. https://anonfiles.com/XbM0QeD9x3/ff7classc_7z

From Chocobo Stance Modifier -> https://gamehacking.org/game/88853

Byte 150 is the jockeys/chocobo state during a race.
It does not mess with the order, just the animation

From Chocobos Have Infinite Stamina When Racing -> https://gamehacking.org/game/88853
Byte 126/-32 aka Stamina2 is the stamina remaining in a race for that chocobo

Looking closer at the data. Something interesting does happen at the end game screen.
As the data in memory is not in the final order. The last two places swap. I don't account for this in my csv.

Memory would put the order as Terry(3),Rudy(1),Tom(4),Gary(5),Andy(2),Harvey(6)
          but on screen it displays Terry(3),Rudy(1),Tom(4),Gary(5),Harvey(6),Andy(2)

I thought maybe it stops updating when a chocobo crosses the finish line, but I recorded a video and I don't see the memory order occurring from the time the fiirst chocobo to the last chocobo cross the finish line.

The 5/6 swapping doesn't happen all the time either...
Sometimes the memory order is just wrong from what is displayed.




I ran your test but I think the jockeys have something to do with it:

Order: 352614
Young(silver), Tim(silver) = Winners
Fox (gold), Terry(silver) = Losers
Swapped stats on Young <->Fox, Tim<->Terry, Fox was in choco0 so I copied Terry to choco7 as well.
Stats swapped: runspeed2,runspeed1,topspeed,acc,coop,intel,stamina2,stamina1
New Order: 134265

Code: [Select]
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,0,FOX,81,2761,252,2515,2515,0,1701,32,64,32,0,0,0,0,0A000B005203,E7F90A00B2,41FA0A00B3DF,4,4,E7F90A,B2DF,2,6,1,2,0,10,3,00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0B 00 52 03 01 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 B3 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 A5 06 00 00 02 00 00 00 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 D3 09 00 00 D3 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
s,1,NANCY,83,2834,271,2713,2713,0,1779,32,64,32,2003,0A,0,10,0A000B007903,E6F90A0081,40FA0A0082DC,4,4,E6F90A,81DC,2,6,1,2,0,CA,3,00 0A 00 00 00 1E 00 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 79 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 82 DC 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 F3 06 00 00 02 00 00 00 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 99 0A 00 00 99 0A 00 00 00 00 00 00 00 00 94 00 00 00 00 00 CA 00 00 00 00 00 01 00 00 00 03 00 2E 21 2E 23 39 FF FF
s,2,YOUNG,85,2888,251,2506,2506,0,1821,64,64,32,2003,0B,1,CA,0A000B008E03,E7F90A006D,41FA0A006EDE,4,4,E7F90A,6DDE,0,0C,1,2,0,5A,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 8E 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 6E DE 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 1D 07 00 00 00 00 00 00 1D 07 48 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 CA 09 00 00 CA 09 00 00 00 00 00 00 00 00 97 00 00 00 00 00 5A 00 00 00 00 00 02 00 00 00 03 00 39 2F 35 2E 27 FF FF
s,3,TERRY,75,2566,253,2533,2533,0,1806,32,64,32,2003,0C,2,5A,0A000B008703,E7F90A0010,41FA0A0011DF,4,4,E7F90A,10DF,2,6,1,2,0,35,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 87 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 11 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 0E 07 00 00 02 00 00 00 0E 07 06 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 E5 09 00 00 E5 09 00 00 00 00 00 00 00 00 96 00 00 00 00 00 35 00 00 00 00 00 03 00 00 00 03 00 34 25 32 32 39 FF FF
s,4,TIM,87,2970,270,2701,2701,0,1834,64,64,32,2003,0D,3,35,0A000B009503,E6F90A00C6,40FA0A00C7DD,4,4,E6F90A,C6DD,0,0C,1,2,0,80,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 95 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 C7 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 2A 07 00 00 00 00 00 00 2A 07 9A 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 8D 0A 00 00 8D 0A 00 00 00 00 00 00 00 00 98 00 00 00 00 00 80 00 00 00 00 00 04 00 00 00 03 00 34 29 2D FF FF FF FF
g,5,JENNY,77,2617,255,2554,2554,0,1756,64,64,32,2003,0E,4,80,0A000B006E03,E6F90A0023,40FA0A0024DD,4,4,E6F90A,23DD,0,0C,1,2,0,A5,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 6E 03 01 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 24 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 DC 06 00 00 00 00 00 00 DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 FA 09 00 00 FA 09 00 00 00 00 00 00 00 00 92 00 00 00 00 00 A5 00 00 00 00 00 05 00 00 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,FOX,81,2761,252,2515,2515,0,1701,32,64,32,2003,0F,5,A5,1000000,62F60A00C6,BCF60A00C6DD,4,4,E7F90A,B2DF,2,6,1,2,0,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 62 F6 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BC F6 0A 00 C6 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 A5 06 00 00 02 00 00 00 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 D3 09 00 00 D3 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 7F 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,2,FOX,81,2761,503,5030,1238,0,1701,32,64,32,AF00,D7,0,0,AA03AB030000,8AF409005D,99F40A0020DF,6,6,E7F90A,B2DF,2,6,1,2,1,30,3,00 D7 00 00 00 04 01 00 00 AF 00 00 00 ED 00 00 00 C7 00 00 00 AA 03 AB 03 00 00 01 00 00 00 09 00 00 00 00 00 8A F4 09 00 5D DE 00 00 F8 0D 00 80 00 00 00 00 25 0F 00 00 00 00 00 00 99 F4 0A 00 20 DF 00 00 00 00 62 06 00 00 00 00 00 00 8F 06 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 50 00 00 00 A5 06 00 00 02 00 AE 07 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 D6 04 00 00 A6 13 00 00 05 00 00 10 B4 A2 00 01 03 00 00 00 30 00 FF FF 03 00 02 00 04 00 03 00 26 2F 38 FF FF FF FF
s,3,NANCY,83,2834,543,5426,2942,0,1779,32,64,32,2003,0A,0,10,AA03AB030000,62F4090066,99F40A003DDC,1,1,E6F90A,81DC,2,6,1,2,1,D8,3,00 0A 00 00 00 CF FE 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 AA 03 AB 03 00 00 03 00 00 00 09 00 00 00 00 00 62 F4 09 00 66 DD 00 00 46 0B 00 80 00 00 00 00 2C 0F 00 00 00 00 00 00 99 F4 0A 00 3D DC 00 00 00 00 A2 01 00 00 00 00 00 00 21 01 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 AB 00 00 00 F3 06 00 00 02 00 FB 07 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 7E 0B 00 00 32 15 00 00 03 00 23 00 BF 05 00 01 03 00 00 00 D8 00 FF FF 03 00 03 00 00 00 03 00 2E 21 2E 23 39 FF FF
s,0,YOUNG,85,2888,501,5012,2724,0,1821,64,64,32,2003,0B,1,CA,AD03AE030000,75F5090020,A7F50A0075DE,4,5,E7F90A,6DDE,0,0C,1,2,1,57,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 AD 03 AE 03 00 00 03 00 00 00 09 00 00 00 00 00 75 F5 09 00 20 DE 00 00 4C 00 00 00 00 00 00 00 DB 02 00 00 00 00 00 00 A7 F5 0A 00 75 DE 00 00 00 00 B2 04 00 00 00 00 00 00 09 05 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 6C 00 00 00 1D 07 00 00 00 00 48 0B 1D 07 48 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 A4 0A 00 00 94 13 00 00 01 00 BE 11 2B DA 00 01 02 00 00 00 57 00 FF FF 02 00 00 00 07 00 03 00 39 2F 35 2E 27 FF FF
s,5,TERRY,75,2566,507,5066,2746,0,1806,32,64,32,2003,0C,2,5A,A803A9030000,B4F3090057,E7F90A0010DF,3,3,E7F90A,10DF,2,6,1,2,1,6B,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 A8 03 A9 03 00 00 03 00 00 00 09 00 00 00 00 00 B4 F3 09 00 57 DE 00 00 6E 01 00 00 00 00 00 00 39 03 00 00 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 4C 03 00 00 00 00 00 00 0E 03 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 67 00 00 00 0E 07 00 00 02 00 06 0A 0E 07 06 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 BA 0A 00 00 CA 13 00 00 06 00 00 08 2E FD 00 01 04 00 00 00 6B 00 FF FF 04 00 05 00 02 00 03 00 34 25 32 32 39 FF FF
s,1,TIM,87,2970,540,5402,3044,0,1834,64,64,32,2003,0D,3,35,AB03AC030000,EDF40900CF,F3F40A00AEDD,3,3,E6F90A,C6DD,0,0C,1,2,1,84,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 AB 03 AC 03 00 00 03 00 00 00 09 00 00 00 00 00 ED F4 09 00 CF DD 00 00 53 0D 00 00 00 00 00 00 1C 08 00 80 00 00 00 00 F3 F4 0A 00 AE DD 00 00 00 00 52 03 00 00 00 00 00 00 41 03 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 6F 00 00 00 2A 07 00 00 00 00 46 08 2A 07 9A 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 E4 0B 00 00 1A 15 00 00 02 00 86 11 91 5C 00 01 03 00 00 00 84 00 FF FF 03 00 01 00 09 00 03 00 34 29 2D FF FF FF FF
g,4,JENNY,77,2617,511,5108,1436,0,1756,64,64,32,2003,0E,4,80,A903AA030A00,FDF3090008,3FF40A00ECDC,3,3,E6F90A,23DD,0,0C,1,2,1,B0,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 A9 03 AA 03 0A 00 01 00 00 00 09 00 00 00 00 00 FD F3 09 00 08 DD 00 00 FE 03 00 80 00 00 00 00 70 0B 00 00 00 00 00 00 3F F4 0A 00 EC DC 00 00 00 00 E6 03 00 00 00 00 00 00 83 03 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 A2 00 00 00 DC 06 00 00 00 00 8B 08 DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 9C 05 00 00 F4 13 00 00 04 00 00 0D A5 26 00 01 03 00 00 00 B0 00 FF FF 03 00 04 00 01 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,YOUNG,85,2888,501,5012,4852,0,1821,64,64,32,2003,0F,5,A5,3A003B002406,F108090084,E4080A0082E3,6,7,E7F90A,6DDE,0,0C,1,2,1,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 3A 00 3B 00 24 06 03 00 00 00 09 00 00 00 00 00 F1 08 09 00 84 E3 00 00 65 0B 00 80 00 00 00 00 8E 02 00 80 00 00 00 00 E4 08 0A 00 82 E3 00 00 00 00 E2 06 00 00 00 00 00 00 01 07 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 30 00 00 00 1D 07 00 00 00 00 1E 07 1D 07 48 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 F4 12 00 00 94 13 00 00 00 00 B2 06 B6 6D 06 01 00 00 00 00 7F 00 00 00 00 00 00 00 00 00 03 00 39 2F 35 2E 27 FF FF
Swapping bytes Young <=> Fox Tim <=> Terry,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,0,FOX,85,2888,251,2506,2506,0,1821,64,64,32,0,0,0,0,0A000B005203,E7F90A00B2,41FA0A00B3DF,4,4,E7F90A,B2DF,2,6,1,2,0,10,3,00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0B 00 52 03 01 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 B3 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 1D 07 00 00 02 00 00 00 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 CA 09 00 00 CA 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
s,1,NANCY,83,2834,271,2713,2713,0,1779,32,64,32,2003,0A,0,10,0A000B007903,E6F90A0081,40FA0A0082DC,4,4,E6F90A,81DC,2,6,1,2,0,CA,3,00 0A 00 00 00 1E 00 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 79 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 82 DC 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 F3 06 00 00 02 00 00 00 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 99 0A 00 00 99 0A 00 00 00 00 00 00 00 00 94 00 00 00 00 00 CA 00 00 00 00 00 01 00 00 00 03 00 2E 21 2E 23 39 FF FF
s,2,YOUNG,81,2761,252,2515,2515,0,1701,32,64,32,2003,0B,1,CA,0A000B008E03,E7F90A006D,41FA0A006EDE,4,4,E7F90A,6DDE,0,0C,1,2,0,5A,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 8E 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 6E DE 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 A5 06 00 00 00 00 00 00 A5 06 C9 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 D3 09 00 00 D3 09 00 00 00 00 00 00 00 00 97 00 00 00 00 00 5A 00 00 00 00 00 02 00 00 00 03 00 39 2F 35 2E 27 FF FF
s,3,TERRY,87,2970,270,2701,2701,0,1834,64,64,32,2003,0C,2,5A,0A000B008703,E7F90A0010,41FA0A0011DF,4,4,E7F90A,10DF,2,6,1,2,0,35,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 87 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 11 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 2A 07 00 00 02 00 00 00 2A 07 9A 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 8D 0A 00 00 8D 0A 00 00 00 00 00 00 00 00 96 00 00 00 00 00 35 00 00 00 00 00 03 00 00 00 03 00 34 25 32 32 39 FF FF
s,4,TIM,75,2566,253,2533,2533,0,1806,32,64,32,2003,0D,3,35,0A000B009503,E6F90A00C6,40FA0A00C7DD,4,4,E6F90A,C6DD,0,0C,1,2,0,80,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 95 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 C7 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 0E 07 00 00 00 00 00 00 0E 07 06 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 E5 09 00 00 E5 09 00 00 00 00 00 00 00 00 98 00 00 00 00 00 80 00 00 00 00 00 04 00 00 00 03 00 34 29 2D FF FF FF FF
g,5,JENNY,77,2617,255,2554,2554,0,1756,64,64,32,2003,0E,4,80,0A000B006E03,E6F90A0023,40FA0A0024DD,4,4,E6F90A,23DD,0,0C,1,2,0,A5,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 6E 03 01 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 24 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 DC 06 00 00 00 00 00 00 DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 FA 09 00 00 FA 09 00 00 00 00 00 00 00 00 92 00 00 00 00 00 A5 00 00 00 00 00 05 00 00 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,FOX,85,2888,251,2506,2506,0,1821,64,64,32,2003,0F,5,A5,1000000,62F60A00C6,BCF60A00C6DD,4,4,E7F90A,B2DF,2,6,1,2,0,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 62 F6 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BC F6 0A 00 C6 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 1D 07 00 00 02 00 00 00 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 CA 09 00 00 CA 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 7F 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,0,FOX,85,2888,501,5012,2324,0,1821,64,64,32,3101,3C,0,0,AD03AE030000,4DF5090099,A7F50A0007DE,1,2,E7F90A,B2DF,2,6,1,2,1,70,3,00 3C 01 00 00 1F 01 00 00 31 01 00 00 65 01 00 00 50 01 00 00 AD 03 AE 03 00 00 01 00 00 00 09 00 00 00 00 00 4D F5 09 00 99 DE 00 00 31 0E 00 80 00 00 00 00 E6 0F 00 00 00 00 00 00 A7 F5 0A 00 07 DE 00 00 00 00 45 01 00 00 00 00 00 00 44 02 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 7E 00 00 00 1D 07 00 00 02 00 60 07 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 14 09 00 00 94 13 00 00 01 00 3F 0A 75 0C 00 01 02 00 00 00 70 00 FF FF 02 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
s,2,NANCY,83,2834,543,5426,2870,0,1779,32,64,32,2003,0A,0,10,AA03AB030000,73F40900CF,99F40A0019DD,2,1,E6F90A,81DC,2,6,1,2,1,A6,3,00 0A 00 00 00 7F FE 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 AA 03 AB 03 00 00 03 00 00 00 09 00 00 00 00 00 73 F4 09 00 CF DD 00 00 28 0D 00 00 00 00 00 00 82 0E 00 00 00 00 00 00 99 F4 0A 00 19 DD 00 00 00 00 D1 02 00 00 00 00 00 00 FF 01 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 6D 00 00 00 F3 06 00 00 02 00 FB 07 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 36 0B 00 00 32 15 00 00 04 00 00 10 82 9E 00 01 03 00 00 00 A6 00 FF FF 03 00 02 00 04 00 03 00 2E 21 2E 23 39 FF FF
s,1,YOUNG,81,2761,503,5030,2790,0,1701,32,64,32,2003,0B,1,CA,AB03AC030A00,F0F409002C,F3F40A003BDE,4,4,E7F90A,6DDE,0,0C,1,2,1,64,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 AB 03 AC 03 0A 00 03 00 00 00 09 00 00 00 00 00 F0 F4 09 00 2C DE 00 00 8C 0A 00 00 00 00 00 00 96 01 00 00 00 00 00 00 F3 F4 0A 00 3B DE 00 00 00 00 90 04 00 00 00 00 00 00 81 04 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 5B 00 00 00 A5 06 00 00 00 00 2A 07 A5 06 C9 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 E6 0A 00 00 A6 13 00 00 02 00 00 0F 0F CE 00 01 03 00 00 00 64 00 FF FF 03 00 01 00 07 00 03 00 39 2F 35 2E 27 FF FF
s,5,TERRY,87,2970,540,5402,2864,0,1834,64,64,32,2003,0C,2,5A,A903AA030000,28F4090086,3FF40A00B7DC,2,0,E7F90A,10DF,2,6,1,2,1,BC,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 A9 03 AA 03 00 00 03 00 00 00 09 00 00 00 00 00 28 F4 09 00 86 DE 00 00 87 0F 00 00 00 00 00 00 CD 0E 00 00 00 00 00 00 3F F4 0A 00 B7 DC 00 00 00 00 19 02 00 00 00 00 00 00 D7 00 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 4B 00 00 00 2A 07 00 00 02 00 9A 0B 2A 07 9A 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 30 0B 00 00 1A 15 00 00 03 00 00 09 95 CE 00 01 03 00 00 00 BC 00 FF FF 03 00 05 00 02 00 03 00 34 25 32 32 39 FF FF
s,4,TIM,75,2566,507,5066,2938,0,1806,32,64,32,2003,0D,3,35,A903AA030A00,DFF3090026,E6F90A00C6DD,4,4,E6F90A,C6DD,0,0C,1,2,0,A3,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 A9 03 AA 03 0A 00 03 00 00 00 09 00 00 00 00 00 DF F3 09 00 26 DD 00 00 1B 05 00 00 00 00 00 00 CD 04 00 80 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 A4 00 00 00 0E 07 00 00 00 00 0E 07 0E 07 06 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 7A 0B 00 00 CA 13 00 00 06 00 3E 11 F0 2F 00 00 01 00 00 00 A3 00 FF FF 00 00 04 00 89 01 03 00 34 29 2D FF FF FF FF
g,3,JENNY,77,2617,511,5108,1419,0,1756,64,64,32,2003,0E,4,80,AA03AB036400,62F409003C,99F40A0057DD,5,4,E6F90A,23DD,0,0C,1,2,1,98,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 AA 03 AB 03 64 00 01 00 00 00 09 00 00 00 00 00 62 F4 09 00 3C DD 00 00 28 08 00 80 00 00 00 00 53 01 00 00 00 00 00 00 99 F4 0A 00 57 DD 00 00 00 00 00 05 00 00 00 00 00 00 85 04 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 8C 00 00 00 DC 06 00 00 00 00 39 0A DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 8B 05 00 00 F4 13 00 00 05 00 00 06 2E 33 00 01 03 00 00 00 98 00 FF FF 03 00 03 00 02 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,FOX,85,2888,501,5012,2324,0,1821,64,64,32,2003,0F,5,A5,B00301000000,95F6090066,BCF60A00C6DD,1,1,E7F90A,B2DF,2,6,1,2,1,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 B0 03 01 00 00 00 01 00 00 00 09 00 00 00 00 00 95 F6 09 00 66 DF 00 00 18 01 00 80 00 00 00 00 F1 01 00 80 00 00 00 00 BC F6 0A 00 C6 DD 00 00 00 00 24 01 00 00 00 00 00 00 2B 01 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 3B 00 00 00 1D 07 00 00 02 00 A3 07 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 14 09 00 00 94 13 00 00 01 00 DF 0F BB 6D 00 01 01 00 00 00 7F 00 FF FF 02 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
352614 -> 134265,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

Swapping just terry and young who are both silver:
352614 ->456231
3 -> 1st -> 5th
4 -> 6th -> 1st


Code: [Select]
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,0,FOX,81,2761,252,2515,2515,0,1701,32,64,32,0,0,0,0,0A000B005203,E7F90A00B2,41FA0A00B3DF,4,4,E7F90A,B2DF,2,6,1,2,0,10,3,00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A 00 0B 00 52 03 01 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 B3 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 A5 06 00 00 02 00 00 00 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 D3 09 00 00 D3 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
s,1,NANCY,83,2834,271,2713,2713,0,1779,32,64,32,2003,0A,0,10,0A000B007903,E6F90A0081,40FA0A0082DC,4,4,E6F90A,81DC,2,6,1,2,0,CA,3,00 0A 00 00 00 1E 00 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 79 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 82 DC 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 00 00 00 00 F3 06 00 00 02 00 00 00 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 99 0A 00 00 99 0A 00 00 00 00 00 00 00 00 94 00 00 00 00 00 CA 00 00 00 00 00 01 00 00 00 03 00 2E 21 2E 23 39 FF FF
s,2,YOUNG,75,2566,253,2533,2533,0,1806,32,64,32,2003,0B,1,CA,0A000B008E03,E7F90A006D,41FA0A006EDE,4,4,E7F90A,6DDE,0,0C,1,2,0,5A,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 8E 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 6E DE 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 00 00 00 00 0E 07 00 00 00 00 00 00 0E 07 06 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 E5 09 00 00 E5 09 00 00 00 00 00 00 00 00 97 00 00 00 00 00 5A 00 00 00 00 00 02 00 00 00 03 00 39 2F 35 2E 27 FF FF
s,3,TERRY,85,2888,251,2506,2506,0,1821,64,64,32,2003,0C,2,5A,0A000B008703,E7F90A0010,41FA0A0011DF,4,4,E7F90A,10DF,2,6,1,2,0,35,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 87 03 03 00 00 00 00 00 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 FA 0A 00 11 DF 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 00 00 00 00 1D 07 00 00 02 00 00 00 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 CA 09 00 00 CA 09 00 00 00 00 00 00 00 00 96 00 00 00 00 00 35 00 00 00 00 00 03 00 00 00 03 00 34 25 32 32 39 FF FF
s,4,TIM,87,2970,270,2701,2701,0,1834,64,64,32,2003,0D,3,35,0A000B009503,E6F90A00C6,40FA0A00C7DD,4,4,E6F90A,C6DD,0,0C,1,2,0,80,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 95 03 03 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 C7 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 00 00 00 00 2A 07 00 00 00 00 00 00 2A 07 9A 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 8D 0A 00 00 8D 0A 00 00 00 00 00 00 00 00 98 00 00 00 00 00 80 00 00 00 00 00 04 00 00 00 03 00 34 29 2D FF FF FF FF
g,5,JENNY,77,2617,255,2554,2554,0,1756,64,64,32,2003,0E,4,80,0A000B006E03,E6F90A0023,40FA0A0024DD,4,4,E6F90A,23DD,0,0C,1,2,0,A5,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 0A 00 0B 00 6E 03 01 00 00 00 00 00 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 FA 0A 00 24 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 00 00 00 00 DC 06 00 00 00 00 00 00 DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 FA 09 00 00 FA 09 00 00 00 00 00 00 00 00 92 00 00 00 00 00 A5 00 00 00 00 00 05 00 00 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,FOX,81,2761,252,2515,2515,0,1701,32,64,32,2003,0F,5,A5,1000000,62F60A00C6,BCF60A00C6DD,4,4,E7F90A,B2DF,2,6,1,2,0,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 62 F6 0A 00 C6 DD 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 BC F6 0A 00 C6 DD 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 00 00 00 00 A5 06 00 00 02 00 00 00 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 D3 09 00 00 D3 09 00 00 00 00 00 00 00 00 8D 00 00 00 00 00 7F 00 00 00 00 00 00 00 00 00 03 00 26 2F 38 FF FF FF FF
color,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,b156,all
g,5,FOX,81,2761,503,5030,0,0,1701,32,64,32,401,4,0,0,A803A9030000,B1F309001B,E7F90A00B2DF,3,3,E7F90A,B2DF,2,6,1,2,1,70,3,00 04 01 00 00 F3 00 00 00 04 01 00 00 D8 00 00 00 27 01 00 00 A8 03 A9 03 00 00 01 00 00 00 09 00 00 00 00 00 B1 F3 09 00 1B DE 00 00 79 01 00 00 00 00 00 00 3C 09 00 80 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 B2 03 00 00 00 00 00 00 AF 03 00 00 00 00 E7 F9 0A 00 B2 DF 00 00 00 00 6C 00 00 00 A5 06 00 00 02 00 A6 06 A5 06 C9 0A 00 00 06 00 32 00 01 00 64 00 32 00 02 00 00 00 00 00 A6 13 00 00 06 00 00 01 7F B0 00 01 04 00 00 00 70 00 FF FF 04 00 05 00 03 00 03 00 26 2F 38 FF FF FF FF
s,3,NANCY,83,2834,543,5426,2852,0,1779,32,64,32,2003,0A,0,10,AA03AB030000,5BF409002A,99F40A00E4DC,2,2,E6F90A,81DC,2,6,1,2,1,B2,3,00 0A 00 00 00 C8 FD 10 00 20 03 FF FF 00 00 00 00 00 00 00 00 AA 03 AB 03 00 00 03 00 00 00 09 00 00 00 00 00 5B F4 09 00 2A DD 00 00 19 0F 00 80 00 00 00 00 7A 0A 00 80 00 00 00 00 99 F4 0A 00 E4 DC 00 00 00 00 9F 02 00 00 00 00 00 00 A9 02 00 00 00 00 E6 F9 0A 00 81 DC 00 00 00 00 AD 00 00 00 F3 06 00 00 02 00 FB 07 F3 06 12 0B 00 00 06 00 32 00 01 00 64 00 32 00 02 00 24 0B 00 00 32 15 00 00 04 00 00 10 59 12 00 01 03 00 00 00 B2 00 FF FF 03 00 03 00 03 00 03 00 2E 21 2E 23 39 FF FF
s,4,YOUNG,75,2566,507,5066,2810,0,1806,32,64,32,2003,0B,1,CA,A803A9030000,CBF309006D,E7F90A006DDE,3,3,E7F90A,6DDE,0,0C,1,2,1,98,3,00 0B 00 01 00 00 00 CA 00 20 03 FF FF 00 00 00 00 00 00 00 00 A8 03 A9 03 00 00 03 00 00 00 09 00 00 00 00 00 CB F3 09 00 6D DD 00 00 87 0B 00 00 00 00 00 00 7C 0D 00 80 00 00 00 00 E7 F9 0A 00 6D DE 00 00 00 00 B1 03 00 00 00 00 00 00 A2 03 00 00 00 00 E7 F9 0A 00 6D DE 00 00 18 00 93 00 00 00 0E 07 00 00 00 00 06 0A 0E 07 06 0A 00 00 0C 00 32 00 01 00 64 00 32 00 02 00 FA 0A 00 00 CA 13 00 00 05 00 00 10 25 6C 00 01 03 00 00 00 98 00 FF FF 03 00 04 00 01 00 03 00 39 2F 35 2E 27 FF FF
s,0,TERRY,85,2888,501,5012,2820,0,1821,64,64,32,2003,0C,2,5A,AC03AD030000,FFF409004D,4DF50A003DDC,1,1,E7F90A,10DF,2,6,1,2,1,D8,3,00 0C 00 02 00 00 00 5A 00 20 03 FF FF 00 00 00 00 00 00 00 00 AC 03 AD 03 00 00 03 00 00 00 09 00 00 00 00 00 FF F4 09 00 4D DD 00 00 28 00 00 00 00 00 00 00 81 05 00 00 00 00 00 00 4D F5 0A 00 3D DC 00 00 00 00 14 01 00 00 00 00 00 00 6A 01 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 B3 00 00 00 1D 07 00 00 02 00 28 08 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 04 0B 00 00 94 13 00 00 01 00 CC 0B 62 1C 00 01 02 00 00 00 D8 00 FF FF 02 00 00 00 00 00 03 00 34 25 32 32 39 FF FF
s,2,TIM,87,2970,540,5402,2936,0,1834,64,64,32,2003,0D,3,35,AB03AC030000,A8F409002B,F3F40A003DDC,1,0,E6F90A,C6DD,0,0C,1,2,1,D8,3,00 0D 00 03 00 00 00 35 00 20 03 FF FF 00 00 00 00 00 00 00 00 AB 03 AC 03 00 00 03 00 00 00 09 00 00 00 00 00 A8 F4 09 00 2B DE 00 00 AF 06 00 80 00 00 00 00 F8 0A 00 80 00 00 00 00 F3 F4 0A 00 3D DC 00 00 00 00 01 01 00 00 00 00 00 00 D7 00 00 00 00 00 E6 F9 0A 00 C6 DD 00 00 00 00 A4 00 00 00 2A 07 00 00 00 00 62 09 2A 07 9A 0B 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 78 0B 00 00 1A 15 00 00 02 00 00 0B 89 E2 00 01 03 00 00 00 D8 00 FF FF 03 00 02 00 02 00 03 00 34 29 2D FF FF FF FF
g,1,JENNY,77,2617,511,5108,1844,0,1756,64,64,32,2003,0E,4,80,AC03AD030000,0DF5090093,4DF50A005FDD,4,6,E6F90A,23DD,0,0C,1,2,1,96,3,00 0E 00 04 00 00 00 80 00 20 03 FF FF 00 00 00 00 00 00 00 00 AC 03 AD 03 00 00 01 00 00 00 09 00 00 00 00 00 0D F5 09 00 93 DC 00 00 AA 06 00 00 00 00 00 00 62 0D 00 00 00 00 00 00 4D F5 0A 00 5F DD 00 00 00 00 72 04 00 00 00 00 00 00 34 06 00 00 00 00 E6 F9 0A 00 23 DD 00 00 00 00 C8 00 00 00 DC 06 00 00 00 00 8B 08 DC 06 39 0A 00 00 0C 00 32 00 01 00 64 00 64 00 02 00 34 07 00 00 F4 13 00 00 03 00 00 13 69 1C 00 01 03 00 00 00 96 00 FF FF 03 00 01 00 02 00 03 00 2A 25 2E 2E 39 FF FF
see above,0,TERRY,85,2888,501,5012,4852,0,1821,64,64,32,2003,0F,5,A5,59005A008705,EC0B0900E6,DE0B0A00E8ED,8,8,E7F90A,10DF,2,6,1,2,0,7F,3,00 0F 00 05 00 00 00 A5 00 20 03 FF FF 00 00 00 00 00 00 00 00 59 00 5A 00 87 05 03 00 00 00 09 00 00 00 00 00 EC 0B 09 00 E6 ED 00 00 39 05 00 80 00 00 00 00 56 01 00 80 00 00 00 00 DE 0B 0A 00 E8 ED 00 00 00 00 08 08 00 00 00 00 00 00 24 08 00 00 00 00 E7 F9 0A 00 10 DF 00 00 00 00 3A 00 00 00 1D 07 00 00 02 00 1E 07 1D 07 48 0B 00 00 06 00 32 00 01 00 64 00 64 00 02 00 F4 12 00 00 94 13 00 00 00 00 3A 17 C1 CA EB 00 00 00 00 00 7F 00 00 00 00 00 00 00 00 00 03 00 34 25 32 32 39 FF FF
352614 ->456231,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Code: [Select]
from ReadWriteMemory import ReadWriteMemory

def swap(f,s):
 b = f.split(" ")
 c = s.split(" ")
 # runspeed2
 b[99] = c[99]
 b[100] = c[100]
 # runspeed1
 b[107] = c[107]
 b[108] = c[108]
 # top speed
 b[109] = c[109]
 b[110] = c[110]
 # acc
 b[115] = c[115]
 # coop
 b[119] = c[119]
 # intel
 b[121] = c[121]
 # stamina2
 b[125] = c[125]
 b[126] = c[126]
 # stamina
 b[129] = c[129]
 b[130] = c[130]
 return(" ".join(b))
 
print("Copying bytes from ePSXe.exe")
rwm = ReadWriteMemory()
process = rwm.get_process_by_name('ePSXe.exe')
process.open()
byteBuffer = process.readByte(0xbf95b4,1183)
process.close()
allbytes = ""
for x in byteBuffer:
 x = "{:02x} ".format(int(x,16))
 allbytes += x
allbytes = allbytes.rstrip()
allbytes = allbytes.upper()
front = allbytes[0:104]
pbytes = allbytes[105:]

allsplit = []
n = 492
for i in range(0,len(pbytes),n):
 allsplit.append(pbytes[i:i+n])

#Young(silver), Tim(silver) = Winners
#Fox (gold), Terry(silver) = Losers

n0 = allsplit[0]
n1 = allsplit[1]
# c2 to c0 Young -> Terry
n2 = swap(allsplit[2],allsplit[3])
# c3 to c4 Terry -> Young
n3 = swap(allsplit[3],allsplit[2])
n4 = allsplit[4]
n5 = allsplit[5]
n6 = allsplit[6]
patched = front+" "+n0+n1+n2+n3+n4+n5+n6
print(patched)
Chart of bytes:


Kind of weird it's not 6th on that swap. Leads me to believe some other piece of data matters... Or something to do with that 5/6 place swapping bug. I guess I can test that by swapping 3 and 2.

352614 -> swap 3 and 2 -> 352164

Idk what's going on there. I triple checked it and used a script to generate it. Swapping two chocobos is ugly.

FWIW in the all the same stats test I replaced bytes 100-157

I'm not sure how many answers we'll get looking at just memory.
« Last Edit: 2022-02-19 19:45:56 by ff7man »

nfitc1

  • *
  • Posts: 2989
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: [FF7] Chocobo Betting
« Reply #4 on: 2022-01-27 20:36:58 »
Hmmm. Why do you think there's a boosting stat for the two it wants to win?

Observation. I have no hard evidence, but I've seen the lower speed chocos win over higher speed with better sprint. There might be more going on, but it looked like they were "favored" because they were making better use of their speed and sprint. Otherwise you could just bet on the two with the highest speed stat and win every time.

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #5 on: 2022-01-29 06:59:02 »
I was playing with https://github.com/ergonomy-joe/ff7-coaster to try and get an understanding of how it works.

There appears to be remnants of code to run the chocobo game as well.

https://github.com/ergonomy-joe/ff7-coaster/blob/4c9a90abe7073406c0eae0f05e84ea3b80c4cc84/NEWFF7/initpath.cpp#L272
https://github.com/ergonomy-joe/ff7-coaster/blob/4c9a90abe7073406c0eae0f05e84ea3b80c4cc84/CoasterMain.cpp#L14

AFAIK he still hasn't released the full source for coaster. Ff7lib.lib is a binary with parts he reverse engineered. I'm not quite sure why there's drama around this file. Super Mario 64 and Zelda OOT both have code on github. The code in ff7lib.lib is likely available from the 1.02 patch eidos released and you will still need assets from coaster.lgp to be able to play the game.

Anyways I tried compiling his code with vs2022 but after adding legacy_stdio_definitions.lib as a linker input, I quickly got an error saying ff7lib.lib has symbols that need to be updated. Which I would need source to do. So I downloaded a copy of vs2008 trial and the directx5sdk. After updating the project configuration to point to my extracted dx5 folder for both inc and lib I got it to compile. Running this compiled binary has been a different beast. When run it opens ff7config.exe, on close it changes the resolution and crashes silently. I added a bunch of print statements and it gets to calling a function in ff7lib.lib that initializes directx, so I really have no idea why it crashed. Pretty sure it has the reg keys its looking for. I added debug messages to the initpath file and it has updated paths to where my install is. I tried ffnx, aali's driver, and even used qemu to emulate a windows 98 computer. Windows 98 doesn't support visual studio 2008. Perhaps with 2005? When the project is compiled with 2005, music plays, but it pagefaults and shows a garbled screen. If anyone has been able to get his project to run I would be interested to know what magic I'm missing.

I found this project https://github.com/FFT-Hackers/FF7-Disassembly where they have a json blob that can be imported into ida with this plugin https://github.com/FFT-Hackers/IDA-Tools to give a bunch of functions names. It adds some names to sub772357 (the sub we broke on when we were writing chocobo names). You don't see this in the pseudecode, but at the top of the function we are taking 0xA4 bytes (164 bytes = chocobo size in memory) and looping through them. The output from ghidra is actually a lot more readable giving you an idea where you are in the 164 bytes.

Code: [Select]
void FUN_00772357(void)

{
  short sVar1;
  ushort uVar2;
  undefined2 uVar3;
  int iVar4;
  int iVar5;
  undefined4 uVar6;
  uint uVar7;
  int iVar8;
  int iVar9;
  ushort extraout_var;
  ushort extraout_var_00;
  undefined4 *puVar10;
  undefined4 *puVar11;
  int local_128;
  undefined *local_11c;
  short local_ec;
  int local_e8;
  uint local_dc;
  int local_d8;
  uint local_d4;
  undefined4 local_cc;
  int aiStack200 [45];
  float local_14;
  float local_10;
  float local_c;
  undefined4 local_8;
 
  local_cc = FUN_00676578();
  for (local_ec = 0; local_ec < 6; local_ec = local_ec + 1) {
    *(short *)(&DAT_00e711d4 + local_ec * 0xa4) = (short)((local_ec * 0xe0) / 6) + 0x10;
  }
  for (local_ec = 0; local_ec < 0x28; local_ec = local_ec + 1) {
    iVar4 = rand();
    iVar5 = rand();
    uVar3 = *(undefined2 *)(&DAT_00e711d4 + (iVar4 % 6) * 0xa4);
    *(undefined2 *)(&DAT_00e711d4 + (iVar4 % 6) * 0xa4) =
         *(undefined2 *)(&DAT_00e711d4 + (iVar5 % 6) * 0xa4);
    *(undefined2 *)(&DAT_00e711d4 + (iVar5 % 6) * 0xa4) = uVar3;
  }
  for (local_ec = 0; local_ec < 6; local_ec = local_ec + 1) {
    iVar4 = (int)local_ec;
    iVar5 = iVar4 * 0xa4;
    puVar10 = &DAT_00e71158 + iVar4 * 0x29;
    if ((DAT_00e71128 == 0) || (local_ec != 0)) {
      sVar1 = *(short *)(&DAT_0097a3e0 + (uint)DAT_00dc0af3 * 8);
      iVar8 = rand();
      (&DAT_00e711b0)[iVar4 * 0x52] =
           sVar1 + (short)(iVar8 % (int)*(short *)(&DAT_0097a3e2 + (uint)DAT_00dc0af3 * 8));
      sVar1 = *(short *)(&DAT_0097a3e4 + (uint)DAT_00dc0af3 * 8);
      iVar8 = rand();
      *(short *)(iVar5 + 0xe711ae) =
           sVar1 + (short)(iVar8 % (int)*(short *)(&DAT_0097a3e6 + (uint)DAT_00dc0af3 * 8));
      *(undefined2 *)(iVar5 + 0xe711b6) = 0x32;
      *(undefined2 *)(iVar5 + 0xe711ba) = 100;
      uVar6 = rand();
      uVar2 = (ushort)((int)uVar6 >> 0x1f);
      *(ushort *)(iVar5 + 0xe7115e) = (((ushort)uVar6 ^ uVar2) - uVar2 & 3 ^ uVar2) - uVar2;
      sVar1 = *(short *)(&DAT_0097a3e0 + (uint)DAT_00dc0af3 * 8);
      iVar8 = rand();
      (&DAT_00e711c4)[iVar4 * 0x29] = (int)sVar1 + iVar8 % 300;
      (&DAT_00e711c0)[iVar4 * 0x29] = (&DAT_00e711c4)[iVar4 * 0x29];
      (&DAT_00e711b8)[iVar4 * 0x52] = 1;
      uVar7 = rand();
      *(ushort *)(iVar5 + 0xe711a4) = (-(ushort)((uVar7 & 7) != 0) & 0xfffe) + 2;
      *(short *)(iVar5 + 0xe711b4) = *(short *)(iVar5 + 0xe711bc) / 10;
      *(undefined2 *)(iVar5 + 0xe711de) = 3;
      uVar2 = rand();
      *(ushort *)(iVar5 + 0xe711bc) = (uVar2 & 1) * 0x32 + 0x32;
    }
    else {
      (&DAT_00e711b0)[iVar4 * 0x52] = (ushort)DAT_00dc0ae6 + (ushort)DAT_00dc0ae7 * 0x100;
      *(ushort *)(iVar5 + 0xe711ae) = (ushort)DAT_00dc0ae8 + (ushort)DAT_00dc0ae9 * 0x100;
      *(ushort *)(iVar5 + 0xe711b6) = (ushort)DAT_00dc0aea;
      *(ushort *)(iVar5 + 0xe711ba) = (ushort)DAT_00dc0aeb;
      uVar6 = rand();
      uVar2 = (ushort)((int)uVar6 >> 0x1f);
      *(ushort *)(iVar5 + 0xe7115e) = (((ushort)uVar6 ^ uVar2) - uVar2 & 3 ^ uVar2) - uVar2;
      (&DAT_00e711c4)[iVar4 * 0x29] = (uint)DAT_00dc0af6 + (uint)DAT_00dc0af7 * 0x100;
      (&DAT_00e711c0)[iVar4 * 0x29] = (&DAT_00e711c4)[iVar4 * 0x29];
      (&DAT_00e711b8)[iVar4 * 0x52] = 1;
      *(ushort *)(iVar5 + 0xe711a4) = (ushort)DAT_00dc0aee;
      *(short *)(iVar5 + 0xe711b4) = *(short *)(iVar5 + 0xe711bc) / 10;
      switch(DAT_00dc0aed) {
      case 1:
        *(undefined2 *)(iVar5 + 0xe711de) = 1;
        break;
      case 2:
        *(undefined2 *)(iVar5 + 0xe711de) = 2;
        break;
      case 3:
      case 4:
        *(undefined2 *)(iVar5 + 0xe711de) = 0;
        break;
      default:
        *(undefined2 *)(iVar5 + 0xe711de) = 3;
      }
      *(ushort *)(iVar5 + 0xe711bc) = (ushort)DAT_00dc0aec;
    }
    if (*(short *)(iVar5 + 0xe711bc) < 0x28) {
      *(undefined2 *)(iVar5 + 0xe711aa) = 3;
      *(short *)(iVar5 + 0xe711be) = (short)((0x28 - *(short *)(iVar5 + 0xe711bc)) / 10);
    }
    else if (*(short *)(iVar5 + 0xe711bc) < 0x3c) {
      *(undefined2 *)(iVar5 + 0xe711aa) = 2;
      *(short *)(iVar5 + 0xe711be) = (short)((0x3c - *(short *)(iVar5 + 0xe711bc)) / 5);
    }
    else if (*(short *)(iVar5 + 0xe711bc) < 0x50) {
      *(undefined2 *)(iVar5 + 0xe711aa) = 1;
      *(short *)(iVar5 + 0xe711be) = (short)((0x50 - *(short *)(iVar5 + 0xe711bc)) / 5);
    }
    else {
      *(undefined2 *)(iVar5 + 0xe711aa) = 0;
      *(short *)(iVar5 + 0xe711be) = (short)((0x6e - *(short *)(iVar5 + 0xe711bc)) / 5);
    }
    *(short *)(iVar5 + 0xe711b4) =
         (short)((int)((int)*(short *)(iVar5 + 0xe711bc) +
                      ((int)*(short *)(iVar5 + 0xe711bc) >> 0x1f & 7U)) >> 3);
    *(undefined2 *)(iVar5 + 0xe711f0) = *(undefined2 *)(DAT_00e7112c + 4);
    *(short *)(&DAT_00e711da + iVar5) = local_ec;
    *(short *)(&DAT_00e7115c + iVar5) = *(short *)(iVar5 + 0xe711ae) / 2;
    *(undefined2 *)(iVar5 + 0xe711a6) = *(undefined2 *)(iVar5 + 0xe711ae);
    *(undefined2 *)puVar10 = 10;
    *(undefined2 *)((int)&DAT_00e71158 + iVar5 + 2) = 0xb;
    *(undefined2 *)(iVar5 + 0xe711dc) = 0;
    *(undefined2 *)(iVar5 + 0xe711f2) = 0xffff;
    *(undefined2 *)(&DAT_00e711f8 + iVar5) = 0;
    *(undefined2 *)(iVar5 + 0xe711f6) = 0;
    *(undefined2 *)(iVar5 + 0xe711f4) = 0;
    *(undefined2 *)(iVar5 + 0xe711fa) = 0;
    iVar9 = 0x100 - *(short *)(&DAT_00e711d4 + iVar5);
    *(undefined2 *)(iVar5 + 0xe711ee) = *(undefined2 *)(&DAT_00e711d4 + iVar5);
    iVar8 = (int)*(short *)(DAT_00e715fc + *(short *)puVar10 * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 8 + *(short *)puVar10 * 0x18) * iVar9;
    *(short *)(&DAT_00e71168 + iVar5) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    iVar8 = (int)*(short *)(DAT_00e715fc + 2 + *(short *)puVar10 * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 10 + *(short *)puVar10 * 0x18) * iVar9;
    *(short *)(&DAT_00e7116a + iVar5) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    iVar8 = (int)*(short *)(DAT_00e715fc + 4 + *(short *)puVar10 * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 0xc + *(short *)puVar10 * 0x18) * iVar9;
    *(short *)(&DAT_00e7116c + iVar5) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    *(undefined2 *)(iVar5 + 0xe71198) = *(undefined2 *)(&DAT_00e71168 + iVar5);
    *(undefined2 *)(iVar5 + 0xe7119a) = *(undefined2 *)(&DAT_00e7116a + iVar5);
    *(undefined2 *)(iVar5 + 0xe7119c) = *(undefined2 *)(&DAT_00e7116c + iVar5);
    iVar8 = (int)*(short *)(DAT_00e715fc + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 8 + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) * iVar9
    ;
    *(short *)(iVar5 + 0xe71180) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    iVar8 = (int)*(short *)(DAT_00e715fc + 2 + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 10 + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) *
            iVar9;
    *(short *)(iVar5 + 0xe71182) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    iVar8 = (int)*(short *)(DAT_00e715fc + 4 + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) *
            (int)*(short *)(&DAT_00e711d4 + iVar5) +
            *(short *)(DAT_00e715fc + 0xc + *(short *)((int)&DAT_00e71158 + iVar5 + 2) * 0x18) *
            iVar9;
    *(short *)(iVar5 + 0xe71184) = (short)(iVar8 + (iVar8 >> 0x1f & 0xffU) >> 8);
    *(undefined2 *)(iVar5 + 0xe71188) = 0;
    *(undefined2 *)(iVar5 + 0xe7118a) = 0;
    *(undefined2 *)(iVar5 + 0xe7118c) = 0;
    *(undefined2 *)(iVar5 + 0xe71190) = 0;
    *(undefined2 *)(iVar5 + 0xe71192) = 0;
    *(undefined2 *)(iVar5 + 0xe71194) = 0;
    uVar3 = FUN_0077cfbf(DAT_00e715fc + 0xc000,DAT_00e715fc + 0xc018);
    *(undefined2 *)(iVar5 + 0xe7118a) = uVar3;
    (&DAT_00e711c8)[iVar4 * 0x52] = 0;
    *(undefined2 *)(iVar5 + 0xe711ca) = 0;
    *(short *)(iVar5 + 0xe711ce) = (short)((int)*(short *)(&DAT_00e7115c + iVar5) / DAT_00e7101c);
    (&DAT_00e711d6)[iVar4 * 0x52] = 0;
    *(undefined2 *)(iVar5 + 0xe711d8) = 0;
    local_dc = (int)*(short *)(iVar5 + 0xe71180) - (int)*(short *)(&DAT_00e71168 + iVar5);
    local_d8 = (int)*(short *)(iVar5 + 0xe71182) - (int)*(short *)(&DAT_00e7116a + iVar5);
    local_d4 = (int)*(short *)(iVar5 + 0xe71184) - (int)*(short *)(&DAT_00e7116c + iVar5);
    FUN_006638b0(&local_dc,&local_dc);
    uVar3 = FUN_0077cf40(local_dc & 0xffff | (uint)extraout_var_00 << 0x10,
                         local_d4 & 0xffff | (uint)extraout_var << 0x10);
    *(undefined2 *)(iVar5 + 0xe71192) = uVar3;
    *(undefined2 *)(iVar5 + 0xe7118a) = *(undefined2 *)(iVar5 + 0xe71192);
  }
  if (DAT_00dc0af2 != '\0') {
    puVar10 = &DAT_00e71158;
    puVar11 = &DAT_00e711fc;
    for (iVar4 = 0x29; iVar4 != 0; iVar4 = iVar4 + -1) {
      *puVar11 = *puVar10;
      puVar10 = puVar10 + 1;
      puVar11 = puVar11 + 1;
    }
    _DAT_00e7123c = DAT_00e7120c;
    _DAT_00e71240 = DAT_00e71210;
    DAT_00e71268 = DAT_00e71268 + ((int)(DAT_00e71268 + (DAT_00e71268 >> 0x1f & 3U)) >> 2);
    DAT_00e71264 = DAT_00e71264 + ((int)(DAT_00e71264 + (DAT_00e71264 >> 0x1f & 3U)) >> 2);
    DAT_00e71252 = DAT_00e71252 + DAT_00e71252 / 10;
    DAT_00e71254 = DAT_00e71254 + DAT_00e71254 / 10;
    _DAT_00e7125a = 100;
    _DAT_00e71260 = 100;
    _DAT_00e71202 = 3;
    _DAT_00e71282 = 0;
    _DAT_00e7128c = 9;
    _DAT_00e7128e = 1;
  }
  for (local_e8 = 0; local_e8 < 0x2d; local_e8 = local_e8 + 1) {
    aiStack200[local_e8] = local_e8;
  }
  for (local_e8 = 0; local_e8 < 200; local_e8 = local_e8 + 1) {
    iVar5 = rand();
    iVar8 = rand();
    iVar4 = aiStack200[iVar5 % 0x2d];
    aiStack200[iVar5 % 0x2d] = aiStack200[iVar8 % 0x2d];
    aiStack200[iVar8 % 0x2d] = iVar4;
  }
  for (local_ec = 0; local_ec < 6; local_ec = local_ec + 1) {
    if ((DAT_00dc0af2 == '\0') || (local_ec != 1)) {
      local_11c = &DAT_0097cc58 + aiStack200[local_ec] * 7;
      for (local_128 = 0; local_128 < 6; local_128 = local_128 + 1) {
        (&DAT_00e711e0)[local_128 + local_ec * 0xa4] = *local_11c;
        local_11c = local_11c + 1;
      }
      (&DAT_00e711e0)[local_128 + local_ec * 0xa4] = 0xff;
      if ((DAT_00e71128 != 0) && (local_ec != 0)) {
        FUN_00773487((int)local_ec,0,0);
      }
    }
    else {
      for (local_128 = 0; local_128 < 8; local_128 = local_128 + 1) {
        *(undefined *)(local_128 + 0xe71284) = (&DAT_0097c8a8)[local_128];
      }
    }
  }
  if (DAT_00e71128 != 0) {
    _DAT_00e711e0 = DAT_00dc0adc;
    _DAT_00e711e4 = DAT_00dc0ae0;
    DAT_00e711e6 = 0xff;
  }
  for (local_ec = 0; local_ec < 6; local_ec = local_ec + 1) {
    iVar4 = DAT_00e719e8 + *(short *)(&DAT_00e711e8 + local_ec * 0xa4) * 0x3c;
    if (((DAT_00dc0af2 == '\0') || (local_ec != 1)) && ((DAT_00e71128 == 0 || (local_ec != 0)))) {
      switch(*(undefined2 *)(local_ec * 0xa4 + 0xe7115e)) {
      case 0:
        *(undefined *)(iVar4 + 8) = 0;
        *(undefined *)(iVar4 + 7) = 0;
        *(undefined *)(iVar4 + 6) = 0;
        local_c = (float)(uint)*(byte *)(iVar4 + 6) / _DAT_007b7f28;
        local_10 = (float)(uint)*(byte *)(iVar4 + 7) / _DAT_007b7f28;
        local_14 = (float)(uint)*(byte *)(iVar4 + 8) / _DAT_007b7f28;
        local_8 = 0x3f800000;
        FUN_00684e20(&local_14,*(undefined4 *)(*(int *)(iVar4 + 0x34) + 0xb4),local_cc);
        break;
      case 1:
        *(undefined *)(iVar4 + 6) = 0x40;
        *(undefined *)(iVar4 + 7) = 0x40;
        *(undefined *)(iVar4 + 8) = 0;
        local_c = (float)(uint)*(byte *)(iVar4 + 6) / _DAT_007b7f28;
        local_10 = (float)(uint)*(byte *)(iVar4 + 7) / _DAT_007b7f28;
        local_14 = (float)(uint)*(byte *)(iVar4 + 8) / _DAT_007b7f28;
        local_8 = 0x3f800000;
        FUN_00684e20(&local_14,*(undefined4 *)(*(int *)(iVar4 + 0x34) + 0xb4),local_cc);
        break;
      case 2:
        *(undefined *)(iVar4 + 6) = 0x40;
        *(undefined *)(iVar4 + 7) = 0x10;
        *(undefined *)(iVar4 + 8) = 0;
        local_c = (float)(uint)*(byte *)(iVar4 + 6) / _DAT_007b7f28;
        local_10 = (float)(uint)*(byte *)(iVar4 + 7) / _DAT_007b7f28;
        local_14 = (float)(uint)*(byte *)(iVar4 + 8) / _DAT_007b7f28;
        local_8 = 0x3f800000;
        FUN_00684e20(&local_14,*(undefined4 *)(*(int *)(iVar4 + 0x34) + 0xb4),local_cc);
        break;
      case 3:
        *(undefined *)(iVar4 + 6) = 0;
        *(undefined *)(iVar4 + 7) = 0x10;
        *(undefined *)(iVar4 + 8) = 0x40;
        local_c = (float)(uint)*(byte *)(iVar4 + 6) / _DAT_007b7f28;
        local_10 = (float)(uint)*(byte *)(iVar4 + 7) / _DAT_007b7f28;
        local_14 = (float)(uint)*(byte *)(iVar4 + 8) / _DAT_007b7f28;
        local_8 = 0x3f800000;
        FUN_00684e20(&local_14,*(undefined4 *)(*(int *)(iVar4 + 0x34) + 0xb4),local_cc);
      }
    }
  }
  return;
}

I discovered the 20 03 bytes are actually if the chocobo thinks its a long or short course. 20 -> DE when on a long course.
« Last Edit: 2022-01-31 18:02:11 by ff7man »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #6 on: 2022-02-01 01:28:38 »
Ok, I found how run speed is generated.

On pc there are the static memory regions:
Pre: e710fc-e71142
Chocobo 1: e71143-e711e6 -> JENNY
Chocobo 2: e711e7-e7128a -> LE
Chocobo 3: e7128b-e7132e -> TIM
Chocobo 4: e7132f-e713d2 -> TOM
Chocobo 5: e713d3-e71476 -> T
Chocobo 6: e71477-e7151a -> SARA
Chocobo 7: e7151b-e715be -> JENNY
Chocobo 8: e715bf-e71662 -> NULL

c0 starts with: [NULLx20]
c8 is is the first part of c2 not c1: 00 0A 00 00 00 1E 00 35 00 20 03 FF FF [NULL-48] 9A 20 E2 0E [NULL-100]

The FFT-Hackers json blog indicated sub_7AE9C0 actually returns a random value. So renamed that function in ghidra.

Top speed is at e711b0, looking at the ghidra code you'll see:
Code: [Select]
sVar1 = *(short *)(&DAT_0097a3e0 + (uint)DAT_00dc0af3 * 8);
iVar8 = rand();
(&DAT_00e711b0)[iVar4 * 0x52] = sVar1 + (short)(iVar8 % (int)*(short *)(&DAT_0097a3e2 + (uint)DAT_00dc0af3 * 8));
At first glance the [iVar4*0x52] was confusing. The value for Top Speed is at e711b0 repeating every 164 bytes.
I believe the psuedocode index is just incorrect as this line would be updating the next chocobos bytes, which doesn't seem right.
Assuming the code index should read [iVar4*0xA4]
The values 97a3e0, 97a3e2, and dc0af3 don't change between chocobos.
And the top speed is randomly generated between a set range.

The run speed value is at e711ae.
Code: [Select]
sVar1 = *(short *)(&DAT_0097a3e4 + (uint)DAT_00dc0af3 * 8);
iVar8 = rand();
*(short *)(iVar5 + 0xe711ae) =  sVar1 + (short)(iVar8 % (int)*(short *)(&DAT_0097a3e6 + (uint)DAT_00dc0af3 * 8));
The values of 97a3e4, 97a3e6, and dc0af3 never change. So run speed is randomly generated between a range.
Using different values from Top Speed.

The raw value for stamina is at e711c4.
Code: [Select]
sVar1 = *(short *)(&DAT_0097a3e0 + (uint)DAT_00dc0af3 * 8);
iVar8 = rand();
(&DAT_00e711c4)[iVar4 * 0x29] = (int)sVar1 + iVar8 % 300;
(&DAT_00e711c0)[iVar4 * 0x29] = (&DAT_00e711c4)[iVar4 * 0x29];
Which uses 97a3e0 and dc0af3. So stamina is randomly generated within a range.

None of these static values are shared anywhere.

Sprinting is at e711a4
Code: [Select]
uVar7 = rand();
*(ushort *)(iVar5 + 0xe711a4) = (-(ushort)((uVar7 & 7) != 0) & 0xfffe) + 2;
Which is randomly set as well.

We already know cooperation and acceleration are statics, and intel is either 32 or 64, but it doesn't impact race outcome. Unless I did something wrong copying bytes.

Shoutout to ghidra for making this output so easy to read over ida.

The jockey pointer is at e711e8.
Code: [Select]
iVar4 = DAT_00e719e8 + *(short *)(&DAT_00e711e8 + local_ec * 0xa4) * 0x3c;
...
switch(*(undefined2 *)(local_ec * 0xa4 + 0xe7115e)) {
e719e8 is after the chocobos with the values 28 E0 4E 0F 23
e7115e is byte 28 -> 0,2,2,3,2,1,1,0

C1: Platinum C2: Bronze C3: Bronze C4: Silver C5: Bronze C6: Gold
Which is the jockey color order. That would have been nice to know earlier.

e7115e is randomly generated
Code: [Select]
uVar6 = rand();
uVar2 = (ushort)((int)uVar6 >> 0x1f);
*(ushort *)(iVar5 + 0xe7115e) = (((ushort)uVar6 ^ uVar2) - uVar2 & 3 ^ uVar2) - uVar2;

There are 4 jockeys:
Case 0 would give 0,0,0; platinum
Case 1 would give 64,64,0; gold
Case 2 would give 64,16,0; bronze... weird
Case 3 would give 0,16,64; silver

I don't know what these values do.

After starting the race, I put a breakpoint when it read a chocobo name and it landed in 6f5b03.
The order on screen is different from the order in memory.
What I believe is happening is depending on the jockey colors the ordering gets altered.

6f5b03 calls 6f564e which has some variables that look similar to the jockey assigned values.

Here's an example

Code: [Select]
mystery_typeb156 intel_adjb124 intel_typeb104 jockeyb28 order-b156 name Top Speed Raw Top Speed Stamina Raw Stamina Raw Stamina2 Sprinting Run Speed Intelligence Cooperation Acceleration

3 2 2 3 0 ANDY 81 2739 559 5588 3014 0 1899 32 64 32
3 2 2 1 3 GRAHAM 79 2702 523 5232 999 0 1770 32 64 32
3 2 2 0 5 FOX 84 2871 500 5000 2920 0 1762 32 64 32
3 2 0 1 1 JAMES 76 2575 505 5046 1446 0 1797 64 64 32
3 2 2 2 4 JU 76 2588 516 5156 1409 0 1886 32 64 32
3 2 0 3 2 RICA 79 2701 554 5538 2964 0 1779 64 64 32
3 2 0 1 0 JAMES 76 2575 505 5046 3606 0 1797 64 64 32
Andy,James,Rica,Graham,Ju,Fox
James,Andy,Graham,Rica,Fox,Ju
146253 became 412635

I patched the game to only give gold jockeys and the order still got messed up.

Code: [Select]
E8 E6 C2 03 00 99 33 C2 2B C2 83 E0 03 33 C2 2B C2 ->
E8 E6 C2 03 00 99 33 C2 2B C2 B8 01 00 00 00 90 90
Same with platinum jockeys. 01 -> 00

Here's my script to rip out values. I'm not sure how much more I'll work on this. I'm not quite sure where to go.
Code: [Select]
from ReadWriteMemory import ReadWriteMemory

def decodeMe(myin):
 a =""" !"#$%&'()*+,-./0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
 res = ""
 smy = myin.split(" ")
 for val in smy:
  dec = int(val, 16)
  if dec >= len(a):
   if dec != 255:
    print("replacing char: "+val+ " with space")
   dec = 0
  res += a[dec]
 return(res)
 
def parse(thebytes):
 #print(thebytes)
 startbytes = thebytes
 thebytes = thebytes.split()
 nameVal = decodeMe(" ".join(thebytes[157:]))
 nameVal = nameVal.rstrip()
 stamina = thebytes[130]+thebytes[129]
 dec = int(stamina, 16)
 staminaValRaw = dec
 staminaVal = str(round(dec/10))
 stamina2 = thebytes[126]+thebytes[125]
 dec = int(stamina2, 16)
 stamina2ValRaw = dec
 stamina2Val = str(round(dec/10))
 dec = int(stamina2, 16)
 stamina2Val = "0x"+stamina2+"/10 = "+str(dec/10)
 unk1Val = thebytes[123]
 intelVal = thebytes[121]
 coopVal = thebytes[119]
 unk2Val = thebytes[117]
 accVal = thebytes[115]
 unk3Val = thebytes[113]
 speed = thebytes[110]+thebytes[109]
 dec = int(speed, 16)
 speedValRaw = dec
 speedVal = str(round(dec/34))
 rs = thebytes[108]+thebytes[107]
 dec = int(rs, 16)
 runSpeedVal = str(dec)
 unk4Val = thebytes[103]
 rs = thebytes[100]+thebytes[99]
 dec = int(rs, 16)
 runSpeed2Val = "0x"+rs+" = "+str(dec)
 sprintingVal = int(thebytes[97],16)
 first = " ".join(thebytes[0:91])
 otherVal = first
 classVal = "".join(thebytes[9:11])
 b152=str(int(thebytes[151],16))
 b27=str(thebytes[27])
 b104=str(thebytes[103]) # intel val
 b124=str(thebytes[123]) # intel type
 #print(b124+","+b104+","+b27+","+b152+","+str(nameVal)+","+str(speedVal)+","+str(speedValRaw)+","+str(staminaVal)+","+str(staminaValRaw)+","+str(stamina2ValRaw)+","+str(sprintingVal)+","+str(runSpeedVal)+","+str(intelVal)+","+str(coopVal)+","+str(accVal)+","+str(classVal))
 b2jockey = str(thebytes[1])
 b4choco = str(thebytes[3])
 b8 = str(thebytes[7])
 b22cam1=str("".join(thebytes[21:27]))
 b38cam2=str("".join(thebytes[37:42]))
 b62 = str("".join(thebytes[61:67]))
 # 4
 b73 = str(thebytes[72])
 # 4
 b81=str(thebytes[80])
 b85=str("".join(thebytes[85:88]))
 b90=str("".join(thebytes[89:91]))
 b104=str(thebytes[103])
 b114=str(thebytes[113])
 b118=str(thebytes[117])
 b124=str(thebytes[123])
 b141=str(thebytes[140])
 b146=str(thebytes[145])
 b152=str(thebytes[151])
 b156=str(thebytes[155])
 
 return(b156+","+b124+","+b104+","+b27+","+b152+","+str(nameVal)+","+str(speedVal)+","+str(speedValRaw)+","+str(staminaVal)+","+str(staminaValRaw)+","+str(stamina2ValRaw)+","+str(sprintingVal)+","+str(runSpeedVal)+","+str(intelVal)+","+str(coopVal)+","+str(accVal)+","+str(classVal)+","+b2jockey+","+b4choco+","+b8+","+b22cam1+","+b38cam2+","+b62+","+b73+","+b81+","+b85+","+b90+","+b104+","+b114+","+b118+","+b124+","+b141+","+b146+","+startbytes+",")
 
#print("Copying bytes from ff7_en.exe")
rwm = ReadWriteMemory()
process = rwm.get_process_by_name('ff7_en.exe')
process.open()
byteBuffer = process.readByte(0xE710FC,1600)
process.close()

allbytes =""
for x in byteBuffer:
 x = "{:02x} ".format(int(x,16))
 allbytes += x
allbytes = allbytes.rstrip()
allbytes = allbytes.upper()

sbytes = allbytes
sbytes = sbytes[213:]
#print(sbytes)
add = "mystery_typeb156,intel_adjb124,intel_typeb104,jockeyb28,order-b156,name,Top Speed,Raw Top Speed,Stamina,Raw Stamina,Raw Stamina2,Sprinting,Run Speed,Intelligence,Cooperation,Acceleration,Willingness to Sprint,b2-jockeyp,b4-chocop,b8,b22-camera1,b38-camera2,b62,b73,b81,b85,b90,b104,b114,b118,b124,b141,b146,all\n"
print(add)
for i in [1, 2, 3, 4, 5, 6, 7, 8]:
  tbytes = sbytes[0:492]
  a = parse(tbytes)
  print(a)
  sbytes = sbytes[492:]
Simple script to encode a string
Code: [Select]
a =""" !"#$%&'()*+,-./0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
#myin = "MIKE!"
myin = input("Please enter a string: ")
res = ""
for b in myin:
 pos = a.find(b)
 hexN = '{0:x}'.format(int(pos)).upper()
 if len(hexN) == 1:
  hexN = "0"+hexN
 res +=hexN
print(res)
res2 ="".join(reversed([res[i:i+2] for i in range(0, len(res), 2)]))
print("reverse: "+res2)
Simple script to decode a string
Code: [Select]
a =""" !"#$%&'()*+,-./0123456789:;<=>[email protected][\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""
res = ""
myin = input("Please enter a space separated string: ")
smy = myin.split(" ")
for val in smy:
 dec = int(val, 16)
 
 if dec >= len(a):
  if dec != 255:
   print("replacing char: "+val+ " with space")
  dec = 0
 res += a[dec]
print(res)
here's a script to patch bytes so they are the same
Code: [Select]
from ReadWriteMemory import ReadWriteMemory
def patchBytes(a,b):
 rwm = ReadWriteMemory()
 process = rwm.get_process_by_name('ff7_en.exe')
 process.open()
 process.writeByte(a,b) #c1
 process.writeByte(a+0xa4,b) #c2
 process.writeByte(a+0xa4+0xa4,b) #c3
 process.writeByte(a+0xa4+0xa4+0xa4,b) #c4
 process.writeByte(a+0xa4+0xa4+0xa4+0xa4,b) #c5
 process.writeByte(a+0xa4+0xa4+0xa4+0xa4+0xa4,b) #c6
 process.writeByte(a+0xa4+0xa4+0xa4+0xa4+0xa4+0xa4,b) #c7
 process.close()
def main():
 patchBytes(0xe711b0,[0x1D]) #TopSpeed
 patchBytes(0xe711b1,[0x0A]) #TopSpeed
 patchBytes(0xe711c0,[0xd3]) #Stamina
 patchBytes(0xe711c1,[0x09]) #Stamina
 patchBytes(0xe711c4,[0xd3]) #Stamina
 patchBytes(0xe711c5,[0x09]) #Stamina
 patchBytes(0xe711ae,[0xA5]) #RunSpeed
 patchBytes(0xe711af,[0x06]) #RunSpeed
 patchBytes(0xe711a6,[0xA5]) #RunSpeed
 patchBytes(0xe711a7,[0x06]) #RunSpeed
 patchBytes(0xe711a4,[0x00]) #Sprinting
 patchBytes(0xe711bc,[0x64]) #Intel
 patchBytes(0xe711aa,[0x02]) #Inteltype
 patchBytes(0xe711b4,[0x06]) #b114
 patchBytes(0xe7114a,[0xA5]) #b8
 patchBytes(0xe711d4,[0xD8]) #b146
main()

Byte 134 has the correct memory order sometimes.
« Last Edit: 2022-02-03 04:52:58 by ff7man »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #7 on: 2022-02-03 03:41:54 »
I ran 1000 races for each class and collected data from each. It should be really helpful in testing anyone's theories without waiting for races to finish.

Order was collected using opencv match template

https://anonfiles.com/X01dzdF9xa/ff7_choco_races_1000_7z

We can get close to the right chocobos, but I'm not sure this is going to be solvable.
 
The point being, with the playstation version I could save state, watch the race, restore state, watch the race again, and get the same end results.
I don't have state saves with PC so I changed the jockey to always be the same and copy all the chocobo bytes, and watch the race.
I then start a new race and overwrite the new chocobo bytes with the old ones and the result is different....
If it's not the jockeys and not the stats assigned to the chocobos then some other randomness must also significantly impact how fast each chocobos runs or what movements they make, and that randomness must get generated when the minigame loads.
« Last Edit: 2022-02-03 07:31:26 by ff7man »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #8 on: 2022-02-13 16:31:13 »
I wrote a script to go through the data set predict the winners by removing bronzes, sorting by top speed and jockey, and then printing win/loss stats per class.
Code: [Select]
from operator import attrgetter
def race(chocobos):
    global wins,losses
    #remove bronze/lowest jockey
    chocobos2 = []
    lowest = 0
    findLowestJockey = False
    if findLowestJockey:
        lowest = 5
        for c in chocobos:
            if c.jockey < lowest:
                lowest = c.jockey
    for c in chocobos:
        if c.jockey != lowest:
            chocobos2.append(c)

    # we need to select 2 winners so,
    # if < 2 chocobos don't delete any
    if len(chocobos2) < 2:
        chocobos2 = chocobos

    # Sort by top speed, then jockey
    chocobos2.sort(key=attrgetter('topspeed','jockey'),reverse=True)

    # check if we predicted the correct winners
    success = 0
    if chocobos2[0].place <3:
        success +=1
    if chocobos2[1].place <3:
        success +=1
    if len(chocobos2)> 2:
        if chocobos2[2].place <3:
            success += 1
    if success >1:
        wins +=1
    else:
        losses += 1

class Chocobo:
    def __init__(self,name,ts,stamina,sprinting,jockey,course,order,place):
        self.topspeed = ts
        self.stamina = stamina
        self.sprinting = sprinting
        self.jockey = jockey
        self.name = name
        self.course = course
        self.order = order
        self.place = place
    def __str__(self):
        return "name=%s speed=%s stamina=%s sprinting=%s jockey=%s course=%s order=%s place=%s\n" % (self.name, self.topspeed, self.stamina, self.sprinting, self.jockey, self.course, self.order, self.place)
    def __repr__(self):
        return str(self)

def parse_csv(theFile):
    with open(theFile) as f:
        data = f.read()
    sdata = data.split("win-order,")
    for x in sdata:
        chocobos = []
        if "order-b152" in x:
            lines = x.split("\n")
            chocobos = []
            for i in range(0,len(lines)):
                if i >0 and i <7:
                    sline = lines[i].split(",")
                    name = sline[6]
                    place = sline[0]
                    jockey = int(sline[5])
                    # bronze -> silver -> gold -> plat
                    if jockey == 0: #plat
                        jockey = 3
                    elif jockey == 1: #gold
                        jockey = 2
                    elif jockey == 2: #bronze
                        jockey = 0
                    elif jockey == 3: #silver
                        jockey = 1
                    else:
                        print("not a jockey")
                    speed = sline[7]
                    stamina = sline[9]
                    end_stamina = sline[11]
                    sprinting = sline[12]
                    runspeed = sline[13]
                    intel = sline[14]
                    course = sline[17]
                    order = i-1
                    c = Chocobo(name,int(speed),stamina,int(sprinting),jockey,course,int(order),int(place))
                    chocobos.append(c)
        if len(chocobos) == 6:
            race(chocobos)

def run_rank(rank,thefile):
    global wins,losses,totalwins,totallosses
    wins = 0
    losses = 0
    parse_csv(thefile)
    avg = round(((wins / (wins + losses) * 100)),2)
    print(rank+"\t"+str(wins)+"\t"+str(losses)+"\t"+str(avg))
    totalwins += wins
    totallosses += losses

if __name__ == '__main__':
    global wins,losses,totalwins,totallosses
    win,losses,totalwins,totallosses = 0,0,0,0
    print("Class\tWins\tLosses\tAvg")
    run_rank("C","c1000.csv")
    run_rank("B","b1000.csv")
    run_rank("A","a1000.csv")
    run_rank("S","s1000.csv")
    avg = round(((totalwins / (totalwins + totallosses)) * 100),2)
    print("total\t"+str(totalwins)+"\t"+str(totallosses)+"\t"+str(avg))

Code: [Select]
Class   Wins    Losses  Avg
C       908     92      90.8
B       921     79      92.1
A       919     81      91.9
S       914     86      91.4
total   3662    338     91.55

On cases where it's wrong a lot of them are when there are sprinters.
Digging through the data I haven't made any good correlations.
I'm guessing it has something to do with a combination of sprinting, intelligence, run speed, and jockey color.
« Last Edit: 2022-02-21 22:46:16 by ff7man »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #9 on: 2022-02-15 07:25:47 »
I learned to use python pandas and the groupby functions was able to quickly answer some questions.

Code: [Select]
import pandas as pd
adf = pd.read_csv('b1000.csv')
adf = adf.dropna()
adf = adf.drop(['order-b152','mystery_typeb156','intel_adjb124','intel_typeb104','all'],1)
adf = adf.drop(['b4-chocop','b73','b81','b104','b118','b124','b141','Cooperation','Acceleration'],1)
adf = adf.drop(['b8','b22-camera1','b38-camera2','b62','b85','b90','b114','b146','b2-jockeyp'],1)
# change rank bronze->plat->silver->gold
adf.loc[adf['jockeyb28'] == "0",'jockey'] = "3" # plat #1
adf.loc[adf['jockeyb28'] == "1",'jockey'] = "2" # gold #
adf.loc[adf['jockeyb28'] == "2",'jockey'] = "0" # bronze #0
adf.loc[adf['jockeyb28'] == "3",'jockey'] = "1" # silver #2
adf.loc[adf['Sprinting'] == "2",'Sprinting'] = "1" # Change sprinting to be 1 instead of 2
dfs = []
for i in range(0,len(adf),8):
 tempdf = adf.iloc[i:i+6].reset_index(drop=True)
 tempdf.iloc[0]['Willingness to Sprint'] = tempdf.iloc[1]['Willingness to Sprint']
 dfs.append(tempdf)
ndf = pd.concat(dfs)
cols = ndf.columns.drop(['Willingness to Sprint','name'])
ndf[cols] = ndf[cols].apply(pd.to_numeric,errors='coerce')
ndf['Willingness to Sprint'] = ndf['Willingness to Sprint'].astype(str)
ndf['name'] = ndf['name'].astype(str)

print("Sorting by Win-Order")
result = ndf.groupby(['win-order']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by Sprinting")
result = ndf.groupby(['Sprinting']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by Intelligence")
result = ndf.groupby(['Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by sprinting and intelligence")
result = ndf.groupby(['Sprinting','Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by sprinting and jockey")
result = ndf.groupby(['Sprinting','jockey']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by sprinting, jockey, and intelligence")
result = ndf.groupby(['Sprinting','jockey','Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by jockey, sprinting, and intelligence")
result = ndf.groupby(['jockey','Sprinting','Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by jockey and intelligence")
result = ndf.groupby(['jockey','Intelligence']).mean().sort_values('win-order',ascending=True)
result = ndf.groupby(['jockey','Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by jockey and course")
result = ndf.groupby(['jockey','Willingness to Sprint']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by jockey, course, and intel")
result = ndf.groupby(['jockey','Willingness to Sprint','Intelligence']).mean().sort_values('win-order',ascending=True)
print(result)

print("Sorting by jockey, course, and sprinting")
result = ndf.groupby(['jockey','Willingness to Sprint','Sprinting']).mean().sort_values('win-order',ascending=True)
print(result)

Code: [Select]
           jockeyb28  Top Speed  Raw Top Speed  Stamina  Raw Stamina  Raw Stamina2  Sprinting  Run Speed  Intelligence  jockey
win-order
0              1.294     99.497       3383.286  629.596     6295.898      2517.730      0.131   2391.816         85.75   2.010
1              1.335     98.224       3339.311  629.063     6290.610      2459.703      0.115   2359.365         75.00   1.987
2              1.407     96.652       3285.818  629.828     6298.272      2398.962      0.119   2342.110         73.05   1.881
3              1.507     95.789       3257.094  630.105     6301.026      2256.965      0.118   2338.647         71.60   1.595
4              1.663     95.841       3259.200  630.472     6304.766      2080.643      0.149   2343.339         72.85   1.057
5              1.831     96.326       3274.975  629.105     6291.018      1762.689      0.127   2324.116         70.95   0.459
The higher your top speed the more likely you are to win
On average stamina is about the same
There's a 1/8 chance you'll be sprinting
The higher your Run Speed the more likely you are to win
The higher your Intelligence the more likely you are to win
The higher your Jockey in the order bronze->silver->gold->plat the more likely you are to win

Code: [Select]
           win-order  jockeyb28  Top Speed  Raw Top Speed     Stamina  Raw Stamina  Raw Stamina2    Run Speed  Intelligence    jockey
Sprinting
0           2.492272   1.502576  97.046938    3299.790689  629.557146  6295.561152   2243.590727  2350.134135     74.775806  1.500859
1           2.553360   1.530962  97.109354    3301.028986  630.645586  6306.395257   2263.548090  2348.274045     75.494071  1.479578
There's a ~50:50 it'll be a smart or dumb sprinter. On average sprinting doesn't seem to affect: intel, top speed, run speed, or jockey values. Something we already knew from reversing but good to reinforce.
Code: [Select]
                        win-order  jockeyb28  Top Speed  Raw Top Speed     Stamina  Raw Stamina  Raw Stamina2    Run Speed    jockey
Sprinting Intelligence
0         100            2.218329   1.522526  97.063150    3300.390065  629.576819  6295.778206   2318.897959  2348.717366  1.486330
1         100            2.273902   1.550388  97.188630    3303.116279  630.591731  6305.808786   2327.726098  2350.826873  1.457364
0         50             2.761346   1.482980  97.031014    3299.201967  629.537821  6295.347958   2169.622163  2351.525719  1.515129
1         50             2.844086   1.510753  97.026882    3298.857527  630.701613  6307.005376   2196.782258  2345.618280  1.502688
If your chocobo is smart it will place on average ~0.5 ranks better

Code: [Select]
                  win-order  jockeyb28  Top Speed  Raw Top Speed     Stamina  Raw Stamina  Raw Stamina2    Run Speed  Intelligence
Sprinting jockey
0         3        1.861621        0.0  96.997706    3298.064985  629.915902  6299.232416   2990.270642  2351.064220     73.967890
          2        1.911518        1.0  96.919908    3295.415713  629.495042  6294.860412   1238.028986  2350.661327     74.980931
          1        1.921970        3.0  97.110606    3302.078788  629.346212  6293.474242   2985.032576  2347.711364     75.340909
1         3        1.928962        0.0  97.229508    3305.409836  630.103825  6300.961749   2928.617486  2337.163934     75.409836
          2        1.989362        1.0  97.239362    3305.585106  629.494681  6295.106383   1279.276596  2350.632979     74.202128
          1        2.000000        3.0  96.989899    3296.818182  631.439394  6314.393939   3066.070707  2349.363636     76.010101
0         0        4.288786        2.0  97.159754    3303.609831  629.473118  6294.694316   1754.290323  2351.125192     74.807988
1         0        4.289474        2.0  96.989474    3296.689474  631.478947  6314.463158   1760.578947  2355.505263     76.315789
Plats are most likely to win, then golds, then silvers

Code: [Select]
                                           win-order  jockeyb28  Top Speed  Raw Top Speed     Stamina  Raw Stamina  Raw Stamina2  Sprinting    Run Speed
jockey Willingness to Sprint Intelligence
2      2003                  100            1.396783        1.0  97.061662    3300.072386  629.705094  6297.083110   2090.383378   0.126005  2344.648794
3      DE03                  100            1.456044        0.0  97.156593    3303.412088  631.035714  6310.373626   2544.604396   0.148352  2353.222527
       2003                  100            1.620787        0.0  96.862360    3293.235955  629.308989  6293.202247   3441.238764   0.109551  2348.660112
1      DE03                  100            1.629921        3.0  97.223097    3305.482940  629.133858  6291.223097   2535.677165   0.133858  2344.900262
2      DE03                  100            1.670241        1.0  96.825737    3292.549598  628.994638  6289.865952    992.037534   0.117962  2349.037534
1      2003                  100            1.680307        3.0  96.913043    3295.460358  630.506394  6305.120205   3448.478261   0.132992  2347.148338
3      DE03                  50             2.061425        0.0  96.972973    3296.948403  630.078624  6300.855037   2568.179361   0.127764  2347.132678
2      2003                  50             2.118110        1.0  97.073491    3300.194226  629.325459  6293.228346   1276.524934   0.144357  2354.763780
1      DE03                  50             2.210938        3.0  97.164062    3303.692708  629.570312  6295.661458   2579.807292   0.098958  2352.401042
       2003                  50             2.226519        3.0  97.082873    3301.055249  629.223757  6292.386740   3431.577348   0.157459  2347.207182
3      2003                  50             2.313187        0.0  97.115385    3302.381868  629.302198  6293.043956   3435.837912   0.104396  2348.664835
2      DE03                  50             2.497312        1.0  96.876344    3293.865591  629.959677  6299.435484    611.454301   0.112903  2354.102151
0      2003                  100            4.078125        2.0  97.026042    3299.354167  629.473958  6294.583333   1782.341146   0.148438  2351.864583
       DE03                  100            4.270718        2.0  97.580110    3316.770718  629.497238  6295.077348   1721.218232   0.118785  2352.734807
                             50             4.347107        2.0  96.955923    3296.446281  629.187328  6291.724518   1721.327824   0.126722  2353.267218
       2003                  50             4.462141        2.0  97.005222    3298.793734  630.715405  6307.065274   1791.785901   0.114883  2349.005222
Golds burn more stamina
Golds are better at short courses, plats are better at long
Silvers are pretty close to plats on shorts and golds on longs
Bronzes are garbage

Sprinting makes them change ranking more in this case too.
Code: [Select]
jockey Willingness to Sprint Sprinting
2      2003                  1           1.637255        1.0  97.647059    3319.509804  628.745098  6287.666667   1705.245098  2351.235294     73.039216
3      DE03                  1           1.773585        0.0  97.745283    3322.113208  628.575472  6286.018868   2553.867925  2340.820755     75.471698
                             0           1.775940        0.0  96.950376    3296.475188  630.842105  6308.430075   2557.556391  2351.472180     73.308271
2      2003                  0           1.780675        1.0  96.976994    3297.102761  629.633436  6296.303681   1675.052147  2349.529141     75.000000
1      DE03                  0           1.918639        3.0  97.192308    3304.588757  629.443787  6294.325444   2557.689349  2349.183432     74.408284
       2003                  0           1.925466        3.0  97.024845    3299.444099  629.243789  6292.580745   3433.610248  2346.166149     76.319876
       DE03                  1           1.943820        3.0  97.202247    3304.550562  628.662921  6286.808989   2558.887640  2344.730337     78.651685
3      2003                  0           1.950233        0.0  97.046656    3299.709176  628.958009  6289.720062   3437.790047  2350.642302     74.650078
2      DE03                  0           2.040971        1.0  96.863429    3293.746586  629.358118  6293.432473    805.647951  2351.781487     74.962064
1      2003                  1           2.045872        3.0  96.816514    3290.504587  633.706422  6336.917431   3480.192661  2353.146789     73.853211
3      2003                  1           2.142857        0.0  96.519481    3282.415584  632.207792  6321.532468   3444.506494  2332.129870     75.324675
2      DE03                  1           2.406977        1.0  96.755814    3289.069767  630.383721  6303.930233    774.058140  2349.918605     75.581395
0      2003                  0           4.268769        2.0  97.064565    3300.714715  630.024024  6300.144144   1786.487988  2349.516517     74.549550
                             1           4.277228        2.0  96.693069    3288.257426  630.554455  6305.247525   1790.811881  2356.504950     78.217822
       DE03                  1           4.303371        2.0  97.325843    3306.258427  632.528090  6324.921348   1726.269663  2354.370787     74.157303
                             0           4.309748        2.0  97.259434    3306.641509  628.896226  6288.987421   1720.573899  2352.809748     75.078616

Taking into account golds are better than plats at short courses on speed ties we get:
Code: [Select]
Class   Wins    Losses  Avg
C       911     89      91.1
B       922     78      92.2
A       919     81      91.9
S       915     85      91.5
total   3667    333     91.67
Which is only very marginally better.

I'm not sure how to add intel or the effect of course on jockey and sprinting

Sorting by intel then run speed gives a pretty good estimate too
Code: [Select]
Class   Wins    Losses  Avg
C       898     102     89.8
B       893     107     89.3
A       903     97      90.3
S       899     101     89.9
total   3593    407     89.83
as does sorting by intel, top speed, then run speed
Code: [Select]
Class   Wins    Losses  Avg
C       918     82      91.8
B       910     90      91.0
A       914     86      91.4
S       909     91      90.9
total   3651    349     91.27
« Last Edit: 2022-02-21 23:01:20 by ff7man »

ff7man

  • *
  • Posts: 10
    • View Profile
Re: [FF7] Chocobo Betting
« Reply #10 on: 2022-02-19 19:42:38 »


https://anonfiles.com/z63aZ2Ifx0/chocotrainer_zip

I wrote a python trainer to quickly edit values. Because of the extreme similarity between the memory layouts between pc and psx, it works on both pc (ff7_en.exe) and on a playstation emulator (ePSXe.exe). You will need to manually update the process name and base address used by your computer. I recommend using cheat engine to search for the name of the first chocobo (ie: PAU = 0x352130, my string encoding script can generate this) and counting up 164 bytes in the memory viewer.

Updating the jockey does not update the color displayed.

Setting 2byte values to less than 256 does not work (how I wrote the trainer, easy to change if need be).
Setting run speed -> 256 has the chocobo run at a really slow speed
Setting top speed -> 256 has the chocobo sprint at a really slow speed
Bronze is the only jockey that has a variable run speed (run2) which will adjust well above or below run1.
If stamina is set really low, none of them will choose to run fully out of stamina
Whether or not a chocobo gets stuck behind another seems to be largely luck based. I think intel helps, but idk how much.
With save states, I've observed, sometimes increasing a chocobos run speed/top speed will cause it to rank lower.

Here's the best I've done so far at guessing the winners.
Code: [Select]
#evaluator.py
from operator import attrgetter
import copy
count = 0
def merge(c1,c2):
    for i in range(len(c1)):
        c1[i].val = i
    for i in range(len(c2)):
        c2[i].val = i
    for i in range(len(c1)):
        for y in c2:
            if c1[i].name == y.name:
                c1[i].val += y.val
    return c1

def race(chocobos):
    #remove bronze/lowest jockey
    chocobos2 = []
    lowest = 0

    course = ""
    for c in chocobos:
        if c.place != 0:
            course = c.course

    for c in chocobos:
        if c.jockey != lowest:
            chocobos2.append(c)

    # we need to select 2 winners so,
    # if < 2 chocobos don't delete any
    if len(chocobos2) < 2:
        chocobos2 = chocobos

    chocobos3 = copy.deepcopy(chocobos2)
    # Sort by top speed, then jockey
    chocobos2.sort(key=attrgetter('topspeed','jockey'),reverse=True)
    # Sort by intel and run speed
    chocobos3.sort(key=attrgetter('runspeed'), reverse=True)
    #chocobos4 = copy.deepcopy(chocobos2)
    #chocobos4.sort(key=attrgetter('intel'), reverse=True)
    # check if we predicted the correct winners

    #chocobos2 = merge(chocobos2,chocobos3).copy()
    #chocobos2 = merge(chocobos2,chocobos4).copy()

    notchocobos2 = copy.deepcopy(chocobos2)
    for i in range(len(notchocobos2)):
        notchocobos2[i].val = float(i)
    nothing = 0
    for i in range(len(chocobos2)):
        chocobos2[i].val = float(i)
        # if we are a top runner bump it up
        for j in range(len(chocobos3)):
            if chocobos2[i].name == chocobos3[j].name:
                if j == 0:
                    chocobos2[i].val -= 3
                if j == 1:
                    chocobos2[i].val -= 2
                if j == 2:
                    chocobos2[i].val -= 0.5
                if j == 3:
                    chocobos2[i].val += 0
                if j == 4:
                    chocobos2[i].val += 1
                if j == 5:
                    chocobos2[i].val += 2

        # if we are smart bump it up
        if chocobos2[i].intel == 100:
            chocobos2[i].val -= 2
        # adjust for jockey course advantage/disadvantage
        if "2003" in course:
            if chocobos2[i].jockey == 3:
                chocobos2[i].val += 0.5
                if chocobos2[i].sprinting == 2:
                    chocobos2[i].val += .5
            elif chocobos2[i].jockey == 2:
                chocobos2[i].val -= 0.5
                if chocobos2[i].sprinting == 2:
                    chocobos2[i].val -= .5
            else:
                nothing +=1
        else:
            if chocobos2[i].jockey == 3:
                chocobos2[i].val -= 0.5
                if chocobos2[i].sprinting == 2:
                    chocobos2[i].val -= .5
            elif chocobos2[i].jockey == 2:
                chocobos2[i].val += 0.5
                if chocobos2[i].sprinting == 2:
                    chocobos2[i].val += .5
            else:
                nothing +=1
    chocobos2.sort(key=lambda x: (x.val,-x.topspeed, -x.jockey))
    success = 0
    if chocobos2[0].place <3:
        success +=1
    if chocobos2[1].place <3:
        success +=1
    if len(chocobos2)> 2:
        if chocobos2[2].place <3:
            success += 1
    #print("Predicted: "+str(chocobos2))
    #input("Hello")
    if success >1:
        return 1
    else:
        #print("og: "+str(notchocobos2))
        #print("predicted: "+str(chocobos2))
        chocobos2.sort(key=attrgetter('place'))
        #print("actual: "+str(chocobos2))
        #input("here")
        return 2

class Chocobo:
    def __init__(self,name,ts,stamina,sprinting,jockey,jockey2,course,order,place,intel,runspeed):
        self.topspeed = ts
        self.stamina = stamina
        self.sprinting = sprinting
        self.jockey = jockey
        self.jockey2 = jockey2
        self.name = name
        self.course = course
        self.order = order
        self.place = place
        self.intel = intel
        self.runspeed = int(runspeed)
        self.rss = int(runspeed)+int(ts)
        self.val = float(0)
    def __str__(self):
        return "name=%s speed=%s stamina=%s sprinting=%s jockey=%s ogjockey=%s course=%s order=%s place=%s intel=%s rs=%s rss=%s val=%s\n" % (self.name, self.topspeed, self.stamina/10, self.sprinting, self.jockey, self.jockey2, self.course, self.order, self.place, self.intel, self.runspeed, self.rss, self.val)
    def __repr__(self):
        return str(self)

def parse_csv(theFile):
    wins = 0
    losses = 0
    with open(theFile) as f:
        data = f.read()
    sdata = data.split("win-order,")
    for x in sdata:
        chocobos = []
        if "order-b152" in x:
            lines = x.split("\n")
            chocobos = []
            for i in range(0,len(lines)):
                if i >0 and i <7:
                    sline = lines[i].split(",")
                    name = sline[6]
                    place = sline[0]
                    jockey = int(sline[5])
                    # bronze -> silver -> gold -> plat
                    if jockey == 0: #plat
                        jockey = 3
                    elif jockey == 1: #gold
                        jockey = 2
                    elif jockey == 2: #bronze
                        jockey = 0
                    elif jockey == 3: #silver
                        jockey = 1
                    else:
                        print("not a jockey")
                    speed = sline[7]
                    stamina = sline[10]
                    end_stamina = sline[11]
                    sprinting = sline[12]
                    runspeed = sline[13]
                    intel = sline[14]
                    course = sline[17]
                    order = i-1
                    c = Chocobo(name,int(speed),int(stamina)/2,int(sprinting),jockey,jockey,course,int(order),int(place),int(intel),int(runspeed))
                    chocobos.append(c)
        if len(chocobos) == 6:
            res = race(chocobos)
            if res == 1:
             wins +=1
            elif res == 2:
             losses +=1
            else:
             print("how")
    return wins,losses

def run_rank(rank,thefile):
    w,l = parse_csv(thefile)
    avg = round(((w / (w + l) * 100)),2)
    print(rank+"\t"+str(w)+"\t"+str(l)+"\t"+str(avg))
    return w,l

if __name__ == '__main__':
    tw,tl = 0,0
    print("Class\tWins\tLosses\tAvg")
    w,l = run_rank("C","c1000.csv")
    tw += w
    tl += l
    w,l = run_rank("B","b1000.csv")
    tw += w
    tl += l
    w,l = run_rank("A","a1000.csv")
    tw += w
    tl += l
    w,l = run_rank("S","s1000.csv")
    tw += w
    tl += l
    avg = round(((tw / (tw + tl)) * 100),2)
    print("total\t"+str(tw)+"\t"+str(tl)+"\t"+str(avg))


Code: [Select]
Class Wins Losses Avg
C 940 60 94.0
B 943 57 94.3
A 957 43 95.7
S 951 49 95.1
total 3791 209 94.77

« Last Edit: 2022-02-22 06:28:56 by ff7man »