5bf2c7ddc1
The Frostbite .cas chunks holding APT ActionScript are greppable, so screens can
be identified and their whole symbol table recovered without driving the GUI.
Control: KitAssignmentPopup (a string from an already-exported BIG) hits 43 times
across the 52 cas files, so a miss would have been meaningful.
FUT_GET_MATCH_KITS_DP hits 10 times. The binding screen is
external.ion_fut.screens.futSelectTeam
(fifa_installpackage_01/cas_01.cas @ 0x3707ecd7), which no exported BIG contained
after 33 attempts at guessing names.
It binds FUT_GET_MATCH_KITS_DP, KitSelectDP and TeamSetupDP, and carries exactly
the vocabulary the native side implies:
panels/locks mcKitHome mcKitAway mcLockHome mcLockAway m_arrKitPanels
sides HOME_SIDE AWAY_SIDE NEUTRAL_SIDE SIDE_HOME SIDE_AWAY
DP fields KITS_AVAILABLE KIT_ HOME_KIT_ID AWAY_KIT_ID
flow InitializeKitConfig GetKitArrayForFUT InitializeKitsFromArray
EnterKitSelect IsKitSelectCreated ExitKitSelect SaveKitsForMatch
lock CheckIsKitLocked RemoveKitLocks SetKitReady SetKitUnReady
uniform SetUniform ION_Uniform GetNonConflictingUniformID
CheckIsKitLocked is the lock predicate the native side does not own — recall
sub_180033430 only ever writes LOCKED = 0, so the "kit is currently locked" dialog
is raised here.
Adds find_apt_in_cas.py (control-guarded) and the recovered 934-symbol table.
87 lines
2.5 KiB
Python
Executable File
87 lines
2.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Find an APT/ActionScript symbol inside the FIFA 17 Frostbite .cas archives.
|
|
|
|
Frosty is a GUI-only tool and its Legacy Explorer is the documented way to reach
|
|
these assets, but the chunks holding APT ActionScript are stored plainly enough to
|
|
grep — so a screen can be identified, and its whole symbol table recovered,
|
|
without driving the GUI at all.
|
|
|
|
ALWAYS passes a control first: `KitAssignmentPopup` is a string from an
|
|
already-exported BIG, so if it misses, the archives are packed differently than
|
|
assumed and no negative from this tool may be quoted.
|
|
|
|
python3 find_apt_in_cas.py FUT_GET_MATCH_KITS_DP
|
|
python3 find_apt_in_cas.py --dump 0x3707ecd7 fifa_installpackage_01/cas_01.cas
|
|
"""
|
|
import argparse
|
|
import glob
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
ROOT = "/mnt/games/FIFA 17"
|
|
CONTROL = b"KitAssignmentPopup"
|
|
|
|
|
|
def cas_files():
|
|
return sorted(glob.glob(os.path.join(ROOT, "**", "*.cas"), recursive=True))
|
|
|
|
|
|
def find(needle: bytes):
|
|
control_total = 0
|
|
hits = []
|
|
for p in cas_files():
|
|
d = open(p, "rb").read()
|
|
control_total += d.count(CONTROL)
|
|
start = 0
|
|
while True:
|
|
i = d.find(needle, start)
|
|
if i < 0:
|
|
break
|
|
hits.append((p, i))
|
|
start = i + 1
|
|
return control_total, hits
|
|
|
|
|
|
def dump(path, off, span=90000):
|
|
with open(path, "rb") as f:
|
|
f.seek(max(0, off - span // 2))
|
|
d = f.read(span)
|
|
seen = []
|
|
for m in re.finditer(rb"[ -~]{4,}", d):
|
|
t = m.group().decode("latin1")
|
|
if t not in seen:
|
|
seen.append(t)
|
|
return seen
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("needle", nargs="?")
|
|
ap.add_argument("--dump", metavar="OFFSET")
|
|
ap.add_argument("--file")
|
|
args = ap.parse_args()
|
|
|
|
if args.dump:
|
|
path = args.file if os.path.isabs(args.file or "") else os.path.join(
|
|
ROOT, "Data/Win32/superbundlelayout", args.file or "")
|
|
for s in dump(path, int(args.dump, 0)):
|
|
print(s)
|
|
return 0
|
|
|
|
if not args.needle:
|
|
ap.error("needle required")
|
|
ctl, hits = find(args.needle.encode())
|
|
print(f"control {CONTROL.decode()}: {ctl} hit(s)")
|
|
if ctl == 0:
|
|
print("CONTROL FAILED — archives not greppable this way; no negative is valid.")
|
|
return 1
|
|
print(f"{args.needle}: {len(hits)} hit(s)")
|
|
for p, i in hits[:20]:
|
|
print(f" {os.path.relpath(p, ROOT)} @ {i:#x}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|