Miscellaneous Forums > Scripting and Reverse Engineering

[FF7] Chocobo Betting

<< < (5/5)

nfitc1:
Wonder Catcher is the lowest hanging fruit in GS. I didn’t even remember it was a thing.
The Chocobo race reversing is extremely interesting! I’ve often wondered about it. I wish I could help, but my laptop is half busted.

ff7man:
Sorry to hear about your laptop, hopefully you get something new soon.

Good news, I figured out how to exploit the PC version :)

I discovered today chocobo betting on PC 100% does get it's initial random value from the system clock. And how do I know this?
Well although I couldn't hook all the time functions in windows. There is a program for linux that is well developed called libfaketime.
But ff7 isn't on linux, how'd you get around that?

I compiled a 32bit libfaketime and installed ff7 1998 in 32bit wine on ubuntu 22.04.
I was able to set a clock time at load and the races were always one of three sets of chocobos.
I'm guessing this is because there are slight differences to loading time.


--- Code: ---ubuntu@ubuntu:~/.wine/drive_c/Program Files/Square Soft, Inc/Final Fantasy VII$ WINEARCH=win32 LDFLAGS=-m32 LD_PRELOAD=/home/ubuntu/libfaketime32/src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" FAKETIME_DONT_RESET=1 /home/ubuntu/wine-7.0-4-proton-x86/bin/wine ff7.exe

--- End code ---

I got the idea from here: https://www.youtube.com/watch?v=-pkf-AaFXLo

https://github.com/wolfcw/libfaketime -> To compile as 32bit you need to add -m32 to CFLAG and LDFLAGS in src/Makefile

The version of Wine I used was prebuilt and I got it's libraries from `apt install wine winetricks`.
https://github.com/Kron4ek/Wine-Builds/releases/download/7.0-4-proton/wine-7.0-4-proton-x86.tar.xz

Installing ff7 was not easy. Here's some guides I needed.
https://appdb.winehq.org/objectManager.php?sClass=version&iId=10317
https://forums.qhimm.com/index.php?topic=11994.0
https://www.codeweavers.com/support/wiki/linux/faq/videomemorysize
One thing to note, winetricks didn't like some of the download links for the dependencies, so some of them had to be manually added to the winetricks cache folder.

With this new knowledge you might be excited to learn, libfaketime is actually not even needed, setting the date and then immediately starting the game has the same effect.


--- Code: ---date -s "9 NOV 2022 18:00:00" && WINEARCH=win32 /home/ubuntu/wine-7.0-4-proton-x86/bin/wine ff7.exe

--- End code ---
Even running the commands back to back with a split second in between works.

On windows you can disable automatic time updates, set a time from command prompt, and pretty quickly press the play button.


--- Code: ---time 23:00:00.00

--- End code ---

On an xbox you can go offline, reboot the xbox.
Now from settings you can set a time, start the game, kill the game, set a time, start the game, and you will get the same result.

I'm really surprised they didn't use a higher precision of time or use uptime.

If someone was really inclined you could probably set up a dns server, to redirect the ntp client of whatever device you are using to a server you control and do the same trick.
A container running https://github.com/btfak/sntp and dnsmasq could easily do the job.

ff7man:
I wanted to take another stab to see if using machine learning instead of me guessing could render better results.

Initial results showed that with only 4000 races machine learning and deep learning models aren't very effective ~50% at guessing winners.

I needed a faster way to generate data so I took a look into lua scripts with bizhawk.

With bizhawk address 0x0B7654 has the first chocobo name and I could map out the rest of the stats from there.

Thanks to https://gamehacking.org/game/88853 I found the correct race position is actually in the chocobo bytes at -24 from the name.

If I read all the positions when position 6 is set I will know the correct order and that the race has finished.

Piecing it all together that script looks like this: https://raw.githubusercontent.com/ff7man/chocobo-betting/main/ml/race.lua

Disabling audio and and video rendering with the nymashock core and I'm getting about 300fps receiving data from a race every ~29s (class c) ~25s (class b) ~19s (class a) ~17s (class s)

AceZephyr1 modified duckstation to run races and can do the same in ~2-3s, I wish that code and data set was public somewhere.

Bizhawk is slower because it is single threaded, but thankfully as a workaround you can run multiple copies of the emulator at the same time without impacting the speed of the others too much.
Running 4 emulators at the same time I was getting ~250fps on each on a ryzen 5 3600 with only a 36% cpu utilization.

I let this run for a week and a half or so and collected 50mb of data. https://github.com/ff7man/chocobo-betting/tree/main/ml has data for every race for the first 7+ min from psx power on.

Following https://www.kaggle.com/code/cullensun/deep-learning-model-for-hong-kong-horse-racing
I used that model to test and evaluate the data set.

https://raw.githubusercontent.com/ff7man/chocobo-betting/main/ml/deeplearn.py

Even with the large dataset and full access to the race data it is only able to guess the correct winning tickets ~76% of the time.

I guess statistical analysis beats out deeplearning/machine learning today. At least with my abilities.




ff7man:
I just realized I made a big mistake when calculating winners I wasn't checking if first and second were both in the final set. The odds of winning are actually much lower.


--- Code: ---    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

--- End code ---
should really be

--- Code: ---    guesses = []
    c0 = chocobos2[0].place
    c1 = chocobos2[1].place
    c2 = 7
    if len(chocobos2) >2:
        c2 = chocobos2[2].place
    guesses.append([c0,c1])
    guesses.append([c0,c2])
    if len(chocobos2) >2:
        guesses.append([c1,c2])
       
    win = False
    for guess in guesses:
        if 0 in guess and 1 in guess:
            win = True
    if win:
        wins +=1
    else:
        losses += 1

--- End code ---

The fuzzfinger method is only 63.38% accurate

--- Code: ---Class   Wins    Losses  Avg
C       649     351     64.9
B       657     343     65.7
A       606     394     60.6
S       623     377     62.3
total   2535    1465    63.38

--- End code ---
My modification is 73.70%

--- Code: ---Class   Wins    Losses  Avg
C       712     288     71.2
B       775     225     77.5
A       741     259     74.1
S       720     280     72.0
total   2948    1052    73.7

--- End code ---
Deeplearning is around ~76% knowing all data and at ~63% for visible data.

ff7man:
AceZephyr was nice and shared some things with me, so I added them to my repo https://github.com/ff7man/chocobo-betting/tree/main/acezephyr

1. A reverse engineered script that can generate items for frames when racing. I modified it to be able to generate betting data as well. You can use it to tell you what item you will win when manipulating RNG on psx. Quick modifications could also tell you what frames have items you care about too.
2. Modifications to Duckstation to race and extract data at a faster pace
3. Data generated from the modified Duckstation (The data is from racing not betting so it's not too useful for me)

It's possible the script could work on PC as well. You would need to change some things as PC uses 45 names instead of 43 and has different prize tables for ranks. Current time will also play a role.

ergonomy_joes source is super helpful, he lists them here: https://github.com/ergonomy-joe/ff7-chocobo/blob/main/NEWFF7/D_0097C8A8.cpp
The chocobo generation logic is here: https://github.com/ergonomy-joe/ff7-chocobo/blob/main/NEWFF7/C_00772340.cpp
The random seed is set via time() in https://github.com/ergonomy-joe/ff7-chocobo/blob/main/NEWFF7/C_00779C90.cpp

Navigation

[0] Message Index

[*] Previous page

Go to full version