Reverse-engineering notes and tools for the PAL PS2 release of
Let's Make a Soccer Team! (SLES_541.51), and the mod built with
them, the LMASTER Mod: fixes for what the release
cut or broke, real names, a hidden third tier and a harder economy.
The repo has two halves:
DOC/: one spec per file format, recording what each field means and how it was worked out.SRC/: standalone Python tools that parse, check and convert those formats.
Every layout claim in the docs is labelled either confirmed, with the game-code address of the routine that proves it, or empirical, meaning it was inferred from the data and checked against every file on the disc.
No game data is included. You need your own copy of the disc.
Where to start:
| You want to | Read |
|---|---|
| play a mod someone made | INSTALL.md: checking your disc, applying a patch, keeping your saves safe |
| make a mod, or edit a career | USAGE.md: the mod editor and the save editor, tab by tab |
| know what a file or system does | the documentation index below |
| help | CONTRIBUTING.md, and "Next up" in TODO.md |
Sister project:
VPF-Reverse
reverse-engineers Virtua Pro Football (SLES_541.53), which runs on the
same engine and whose edit players this game imports
(PMPLAYER_FORMAT.md). Formats the two games
share are documented here first, and VPF-Reverse builds on them.
The long-term aim is GUI tools for editing the game's data to build
mods, and most of that is here now: every file on the disc is
documented, the data a starting-season mod changes has writers and
editor tabs, and edited files go back on a disc that boots. See
GOALS.md for the plan and where each stage stands,
TODO.md for the next tasks and DONE.md for
what's finished.
100% of the reverse engineering has been done using Claude Opus 5.5. I'm neither anti-AI nor pro-AI, and I am pro user choice. If you hate genAI with the passion of a thousand suns, this project is not for you. If you're a far-right techbro chud, this project is ALSO not for you.
- Python 3, standard library only for most commands (
editor.pyusestkinter, which ships with Python) pip install pillowfor the PNG commands (svr,csp,zbf) and the textures inninja.py's exportspip install capstonefor disassembly (sles_disasm,snr2 dis)
The tools expect this layout. Everything built from the disc is git-ignored, and none of it may be committed.
ISO/ the disc filesystem (SLES_541.51, DLL/, AUDIO/, DATA.CVM, ...)
+ the decrypted DATA.ISO (ignored)
DAT/ the contents of DATA.ISO, which is what most tools read (ignored)
ISO_JP/, DAT_JP/ the same for the Japanese release, optional (ignored)
mod/ the editor's default mod folder (ignored)
.regress/ regress.py's baselines; .cache/ save.py's field list (ignored)
SRC/ tools
DOC/ format and system documentation
PNACH/ PCSX2 patches (widescreen)
Put a dump of the disc that matches Redump (disc 12334, SLES-54151 v1.01) in the repo root, then run:
python SRC/extract_disc.py all "Let's Make a Soccer Team! (Europe, Australia) (En,Fr,De,Es,It).iso"extract_disc.py checks the image's size, CRC-32, MD5
and SHA-1 against Redump, extracts the disc filesystem into ISO/, decrypts
ISO/DATA.CVM into ISO/DATA.ISO, and extracts that into DAT/. Nothing is
mounted. Re-running skips files that are already there. *.iso files in the
repo root are git-ignored.
To do the same by hand:
-
Copy the disc's files, including
DATA.CVM, intoISO/. LeaveDATA.CVMunmodified. -
Decrypt the archive. Only its ISO9660 table of contents is encrypted, and the key recovered from the running game is the default:
python SRC/rofs_decrypt.py ISO/DATA.CVM ISO/DATA.ISO
-
Extract it into
DAT/(or mount it and copy its contents):python SRC/extract_disc.py extract ISO/DATA.ISO DAT
The decryption and key recovery are covered in
DATA_CVM_EXTRACTION.md and
LMAST_DATA_CVM_INFO.md.
The tools also read the Japanese release, Pro Soccer Club o Tsukurou!
Europe Championship (disc 30266,
SLPM-66316 v1.05). extract_disc.py recognises its dump and extracts it
to ISO_JP/ and DAT_JP/ (git-ignored):
python SRC/extract_disc.py all "Pro Soccer Club o Tsukurou! Europe Championship (Japan).iso"Pass those folders to any tool. The PAL files in ISO/ must be there
too: tools give PAL addresses, and gamever.py finds
them in the Japanese executable and overlays. patch_disc.py patches a
Japanese disc too, with every --mod switch but third-tier. See
JAPANESE_RELEASE.md.
All tools run from the repo root as python SRC/<tool>.py <command> ....
Running a tool with no arguments prints its full usage. Most commands accept
either files or directories, and info parses every matching file and marks
anything that doesn't fit the documented layout with !!. Search for them
with, for example, python SRC/tbb.py info DAT | grep '!!'.
| Tool | Reads | Does |
|---|---|---|
extract_disc.py |
a disc image | checks it against Redump (PAL or Japanese), extracts its files, decrypts DATA.CVM and extracts DATA.ISO, without mounting anything (all; verify, list and extract do one step each) |
rofs_decrypt.py |
ISO/DATA.CVM |
decrypts the table of contents into a plain DATA.ISO, with the key derived from the CVMH header |
| Tool | Reads | Does |
|---|---|---|
sles_disasm.py |
ISO/SLES_541.51 |
recovers about 12,600 symbol names from the .sndata export table and disassembles with labels, naming the 323 sites that call or reference overlay code (relocs), and lists the developers' field table: the source names, ranges and offsets of about 170 structures' members (fields) |
snr2.py |
ISO/DLL/*.REL |
parses the SN Systems overlays: header, symbols, relocations, xrefs, annotated disassembly; C++ classes from their type information (classes) and which class makes each call to a function (callers) |
gamever.py |
ISO/, ISO_JP/ |
tells the builds apart and finds a PAL address or constant in another build's executable or overlay; check lists every address the tools use |
python SRC/sles_disasm.py ISO/SLES_541.51 dis TblData
python SRC/sles_disasm.py ISO/SLES_541.51 relocs Talk_
python SRC/snr2.py dis ISO/DLL/SIMPRG.REL 12e000 40 --sles ISO/SLES_541.51--sles resolves an overlay's imports to their addresses in the main
executable.
Capstone has no R5900 mode, so a few EE-only opcodes (lq/sq, MMI)
disassemble as unrelated MIPS instructions.
| Tool | Formats | Doc |
|---|---|---|
pac.py |
BINPAC .PAC/.MRG/.HED, KC@P headers, PRSH (Sega PRS); rebuilds BINPACs and KC@P packs byte for byte (roundtrip, replace), and compresses PRS (prs, prscheck, replace --prs) |
PAC_FORMAT.md |
tbb.py |
TBB1/TBL1 parameter tables; reads and writes (all 70 files round-trip) |
TBB_FORMAT.md |
packdata.py |
etc::PackData inside KC@P entries (face packs, licensed kits, edit face, cut-ins); names lists each head's model and texture name |
PLAYER_DIR.md |
pbdata.py |
player database PBDATA_*.PAC: 27,950 players, 3,000 managers, 1,000 scouts (bit-packed records); list, show, CSV; writes edits (set, CSV import; every record round-trips; --sles re-sorts the ranking and patches the executable for rank, position and nationality edits) |
PBDATA_FORMAT.md |
initteam.py |
starting divisions, last season's order and computer-team squads (PLRRSRC_INITTEAMDATA.TBB, OTEAMMEMBER.TBB), with club names; club records, nations and stadiums; edits squad slots (set), club records (setteam), nations (setnation) and stadiums (setstadium), and swaps two clubs' places in the starting divisions and last season's results (swap); all five files round-trip. Also --mod real-grounds' writer, from SRC/grounds.csv (each club's real 2005-06 ground and capacity) |
INITTEAM_FORMAT.md |
teaminit.py |
the player's new club (TEAM_INIT_DATA.TBB) by league and team style: squad, youth team, staff, staff lists and the rival club, named from the player database; edits records (set), round-trips; decodes the game's salary, transfer-fee and loan-fee limits from the executable (limits) |
TEAMINIT_FORMAT.md |
clubresult.py |
how a final place is reported (CLUBRESULT.TBB), one row per schedule: league, cup or not ranked, the third-place match, and the clubs a cup's round names depend on ("Champion" to "Defeated 1st Rnd"); edits fields (set), round-trips |
CLUBRESULT_FORMAT.md |
training.py |
the training tables (TRAINING_LIST.TBB): what each of the 139 practice menus trains and how tiring it is, the facility and camp bonuses, match growth by position, ability groups, the coaching staff ability per ability, and decline with age; edits values (set), round-trips |
TRAINING_FORMAT.md |
camp.py |
the training camps (CAMP_LIST.TBB, CAMP_EXPLANE.TBB): each camp's city, nation and type, the clubs of its area and its description; edits fields (set), and fixes the camp types (fixtypes) and PAL's seven misplaced English camps (fixcities), round-trips |
CAMP_FORMAT.md |
formset.py |
the tactics screen's eight national presets (TACTICS_FORMATION_SET.TBB): seven team tactics and a formation each; edits them (set), round-trips |
PARAM_DIR.md |
tour.py |
tour opponents by region (TOUR_LIST.TBB); edits pairs (set), round-trips |
PARAM_DIR.md |
clubrank.py |
the club ranking's tables (CLUB_RANK_SYSTEM.TBB): the nation groups, the points each competition gives for each final place, the world rank points by club rank, the club rank by position in each nation or division (with the positions each rank gets) and the year-end leagues; edits the points, world rank points and positions (set), adds rows for divisions of a new size (addrows), round-trips |
PARAM_DIR.md |
clubevent.py |
the 46 PR events (club events) in the executable, named from MES.PAC: when each is offered, its cost, its chance in each business cycle and what success brings; edits every field into a copy of the executable (set), round-trips, on both releases |
CLUBEVENT_FORMAT.md |
natcal.py |
the national-team calendar (SCHEDULE_LIST.TBB): for each year of the 4-year cycle, the turns national teams play, call their squads up and release them, and which are for a finals tournament; checks the calendar against the national cups' schedule, edits the call-up, release and finals flags (set), round-trips |
PARAM_DIR.md |
regulation.py |
match regulations (REGULATION.TBB), one row per schedule: clubs, match grade, win bonus, prizes by position, yellow-card ban, matchday squad, foreign-player limit, substitutions, and the places up and down; edits fields (set), round-trips |
REGULATION_FORMAT.md |
gamedata.py |
GAME/GAMEDATA.BIN: checks the 4,131 records (body part and surface of each ball contact) and prints them with the fields the match engine is known to read |
GAMEDATA_FORMAT.md |
bpb.py |
the play books GAME/PLAYBOOK.BPB and COMBINATION.BPB: checks every play and prints one's starting spots and paths (players by role, and ball, in metres); checks and prints the COMBINATION2.CBB records (blocks, slots, areas) |
BPB_FORMAT.md |
plrcommon.py |
the common pack PLRESOURCECOMMON.PAC: checks all 25 tables (facilities, formations, nations, competitions) and prints any one, with the facility records' named fields; set edits a facility's build cost, second cost, upkeep, build time or stadium capacity, and roundtrip checks every field's edit |
PLRESOURCECOMMON_FORMAT.md |
plrsim.py |
the season-mode pack PLRESOURCESIM.PAC: checks all 16 entries (cities and weather, nations, affiliations, scouts' exclusives, combinations, free agents, colours, ...) and prints any one, named from the player database; replaces free agents (setfree) and overseas bases' costs (setbranch), edits the affiliations, scouts' exclusives and combinations (setgroup), and checks that the pack rebuilds byte for byte (roundtrip) |
PLRESOURCESIM_FORMAT.md |
leaguesize.py |
a nation's two divisions: checks that the first season plays out with each club once (nations), and changes the first division's size, moving clubs to or from the second (plan, which also gives the sizes each nation allows; build, for one or more leagues: the slots, last season's records, the games, the game days, the club counts in REGULATION.TBB and CLUBRESULT.TBB, and the club ranking's rows for a new size) |
SCHEDULE_FORMAT.md |
schedule.py |
season schedule packs SCHEDULE_{SYSTEM,COMPETITION,TEAM_ENTRY}.PAC: turns, games, pairings, team sources; re-encodes all three packs and their .HED byte for byte (roundtrip); builds and checks league schedules of 2–32 clubs (league) and cup brackets of 2–128 (knockout) and finds free turns for their game days (turns); moves schedules' game days (shift); rebuilds the badly balanced leagues at their own size (balance) |
SCHEDULE_FORMAT.md |
sqb.py |
SQB1 sequencer scripts: the root flow scripts in SEQ/ and the PwkScript formulas in PSC*.PAC; checks every command and label, disassembles with command, module and field names, prints and edits a PwkScript's data tables (tables, settable, one script or several in a pack), re-encodes every script byte for byte (roundtrip) and patches a command (setcmd); SQBFILENAME, GLOBALMEMORY |
SQB_FORMAT.md |
uniform.py |
club kits UNIFORM_LIST.TBB (designs and colours of every club's kits), the colour clash table COLOR_TBL.TBB, the 116 licensed kits' descriptors (PLPACK and the executable's copy), and the keeper kit table UNIFORM_GK.TBB; edits kits (set, setlicence, setexe, setgk; every row round-trips) |
UNIFORM_FORMAT.md |
stadium.py |
DAT/STADIUM: .PRI draw priorities, BUILD_STADIUM.TBB (which model each of the 119 stadiums uses), packs per model; setlook builds a stadium like another and setboards sets its 12 advert boards, and roundtrip checks each row's edit |
STADIUM_DIR.md |
system.py |
DAT/0SYSTEM: the 77 UI colours, DETAILFLAG, the 8 crest, badge and sponsor texture packs (which entry a team id picks), icons |
0SYSTEM_DIR.md |
emblem.py |
DAT/EMBLEM: the club editor's 96-colour palette and its 32/16-colour maps, crest masks, patterns and accessories, the sample, starting and rival crests field by field (crests), flag parts, and how the tables and packs line up. Writer: set edits the crests, presets, layer records and flag records, roundtrip re-encodes them |
EMBLEM_DIR.md |
news.py |
DAT/NEWS: the newspaper's picture packs (mastheads by league, article pictures, ads) and NEWSMONTHFLAG (which months print the best-player rankings) |
NEWS_DIR.md |
acrobata.py |
DAT/ACROBATA: the 875 Acroarts event scenes (ABDA/ABRS, POF0 pointers, the Ninja and texture resources inside), and the executable's index copy and scene-id table |
ACROBATA_DIR.md |
humanid.py |
BG/HUMANID.BIN, the human database: for each human id, the search function and the person (object) shown on a warm, mild or cold day; the executable's object table |
BG_DIR.md |
cvs.py |
the developers' CVS/ folders: each file's original name, revision and date, and which files the build added |
CVS_DIR.md |
python SRC/pac.py info DAT
python SRC/pac.py extract DAT/BG/BG_OF_01.MRG out/ --prs
python SRC/tbb.py dump DAT/0SYSTEM/SCHEDULE.TBB 0 --rows 10
python SRC/tbb.py replace DAT/PARAM/REGULATION.TBB 0 edited.bin out/REGULATION.TBB
python SRC/packdata.py list DAT/PLAYER/PLPACK_HOME.HED 0
python SRC/schedule.py compe DAT/PARAM 6
python SRC/sqb.py dis DAT/SEQ/ROOTMAINSEQ.SQB
python SRC/initteam.py leagues DAT/PARAM
python SRC/pbdata.py list DAT/PARAM/PBDATA_EU.PAC --find terry
python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out/PBDATA_EU.PAC 101 age=30 ability.13=99| Tool | Formats | Doc |
|---|---|---|
svr.py |
Ninja textures .SVR/.SVM/.SVP, including GS unswizzling |
SVR_FORMAT.md |
csp.py |
.CSP/.CSE 2D screen layouts (sprites, node tree, animation) |
CSE_FORMAT.md |
zbf.py |
.zbf depth buffers for pre-rendered rooms |
ZBF_FORMAT.md |
ninja.py |
Ninja models and motions .SNJ/.SNO/.SNM/.SNP/.SNA, loose and inside archives; OBJ export, animated glTF export |
NINJA_FORMAT.md |
python SRC/svr.py png DAT out/textures
python SRC/csp.py png DAT/CSE out/cse --crops
python SRC/zbf.py png DAT/BG out/depth
python SRC/ninja.py obj DAT/TEST3D/CAMERON.SNO out/cameron.obj
python SRC/ninja.py obj "DAT/PLAYER/FC_EURO_FACEPACK_00.HED#0.0" out/face.obj
python SRC/ninja.py gltf out/player.gltf DAT/PLAYER/M_PLAYER.SNO "DAT/GAME/PLAYERMOTION.PAC#68:snm"| Tool | Formats | Doc |
|---|---|---|
sounddat.py |
SOUNDDAT.PAC, commentary .TBL, ps2_DTPK banks (samples, instruments, and songs as MIDI files or rendered WAVs), FNAME* clip names; the executable's sound-bank and music-id tables (music) |
GAME_DIR.md, SOUND_DIR.md |
afs.py |
ISO/AUDIO/*.AFS: music, commentary, crowd chants and ambience (CRI AFS + ADX); checks every entry, decodes to WAV with loop points |
AUDIO_DIR.md |
python SRC/sounddat.py info DAT/GAME/SOUNDDAT.PAC
python SRC/sounddat.py extract DAT/GAME/SOUNDDAT.PAC out/sound --wav
python SRC/sounddat.py songs DAT/SOUND
python SRC/sounddat.py midi DAT/SOUND/MAP01.DAT out/midi
python SRC/sounddat.py music ISO/SLES_541.51 DAT/SOUND
python SRC/afs.py list ISO/AUDIO/BGM.AFS
python SRC/afs.py wav ISO/AUDIO/BGM.AFS out/bgmThe MIDI files carry the note data only, with loopStart/loopEnd
markers. wav renders the songs with the banks' own instruments instead.
| Tool | Formats | Doc |
|---|---|---|
mbb.py |
MESSAGE/MES.PAC and its MBB1 message files, all 7 language slots; vars reads what fills each variable from SLES_541.51 and SIMPRG.REL; names writes the club, national team and competition names with their copies as a table to fill in with real names |
MBB_FORMAT.md |
realnames.py |
SRC/realnames.csv, the real club, stadium, national team and competition names for --mod real-names, and SRC/realnames_phrases.csv, the hand edits of the other languages' mentions: import from the mbb.py names table, info checks them against MES.PAC, apply writes them |
LMASTER_MOD.md |
python SRC/mbb.py info DAT/MESSAGE/MES.PAC
python SRC/mbb.py dump DAT/MESSAGE/MES.PAC --cat 35002 --lang 1
python SRC/mbb.py csv DAT/MESSAGE/MES.PAC messages.csv
python SRC/mbb.py import DAT/MESSAGE/MES.PAC messages.csv out/MES.PAC
python SRC/mbb.py set DAT/MESSAGE/MES.PAC out/MES.PAC 3 2007 1 "Chelsea FC" 3 2007 2 "Chelsea"The CSV has one row per message and one column per language. Control codes
appear as tags such as {var:1:7} and {color:4}. import writes an
edited CSV back (and set one or more messages). A file that gets longer
grows into the spare room after it in MES.PAC (a median of 1,544
bytes per category and language), and the archive keeps its size
(details).
| Tool | Formats | Doc |
|---|---|---|
evsdatabin.py |
EvsDataBin_{EVENT,NEWS,MAIL}.bin (the shipping event tables) |
EVSDATABIN_FORMAT.md |
eventdata_turn.py |
EVENTDATA_TURN.TBB (unused prototype) |
EVENTDATA_TURN.md |
Both write CSV to a file, or to stdout if no output path is given:
python SRC/evsdatabin.py DAT/EVENT/EVSDATABIN_EVENT.BIN event.csv
python SRC/evsdatabin.py DAT/EVENT/EVSDATABIN_MAIL.BIN mail.csv --text DAT/MESSAGE/MES.PACWith --text, each message reference (event dialogue, news headline and
body, mail sender/subject/body) gets a column with its text from MES.PAC.
The tables aren't the whole event system. Scouting, transfers and loans run
as code-only "procedures" that send MAIL records; see
EVS_PROCEDURES.md. What an EVENT does after its
dialogue (open a screen, start a talk, chain another event) is its scene
type; see EVSDATABIN_FORMAT.md.
| Tool | Reads | Does |
|---|---|---|
patch_disc.py |
the disc image, ISO/DATA.CVM or ISO/DATA.ISO |
writes edited DAT/ files, archive entries and files outside DATA.CVM (disc:SLES_541.51) back onto the disc, with their copies (below), and applies named switches (--mod; --mod lmaster-lite, --mod lmaster-full and --mod lmaster-max for the LMASTER Mod); locate, copies and verify find where a file lives, where its copies are, and check an image holds given bytes; switches prints the editions, groups and switches |
preload.py |
DAT/PRELOAD, ISO/SLES_541.51, ISO/DLL/*.REL |
checks every PRELOAD pack entry against the file it copies, gives each pack's free room for a rebuild, prints the game's load lists, and says which packs hold a file and which screen loads each (who), i.e. where the game reads that file from |
python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out/PBDATA_EU.PAC 101 age=30
python SRC/patch_disc.py patch disc.iso modded.iso PARAM/PBDATA_EU.PAC=out/PBDATA_EU.PAC --copies
python SRC/preload.py who DAT 3_1.mbb # the English club names: read from STATIONMES1.PACWhat patch_disc.py patch handles (REBUILD.md):
- Size changes. Only the table of contents of
DATA.CVMis encrypted, so a file that keeps its size is written over the original without re-encrypting anything. A file can grow or shrink inside its last sector. One that needs more sectors is moved to the end ofDATA.ISO, which re-keys the table of contents and grows the disc image if it has to. - Archives. An entry that changes size rebuilds its archive: a
PRELOADpack, any other BINPAC with its.HEDcopies, and forMES.PACand the Acrobata pack the executable's index of it. KC@P packs are rebuilt bypac.py replace(REBUILD.md). - Copies. Many files have a copy in a
PRELOADpack, and the game reads that copy whenever the pack is loaded, so an edit must reach both.--copiesupdates every copy and rebuilds the packs around entries that change size (PRELOAD_DIR.md). - Switches (
--mod <name>,..., or each as a flag of its own). The LMASTER Mod (LMASTER_MOD.md) comes in three editions,--mod lmaster-lite(the fixes),--mod lmaster-full(the fixes, the third tier, the restored injury lines, real names and grounds, with the modded disc's own saves in-F) and--mod lmaster-max(full and the difficulty groups, saves in-M; both keep VS data in-V), made of groups of switches;--modtakes a name of any level (the switch levels), andpython SRC/patch_disc.py switchesprints the tree with a line on each switch;LMASTER_MOD.mdhas the details of each. The test tools, in no edition, areskip-tutorial(skips the opening playoffs;--skip-nationpicks the league) andlauncher(boots into the developers' debug menu). --renamerenames a file outsideDATA.CVM. Every patch of a whole image prints the executable's PCSX2 CRC.
| Tool | Reads | Does |
|---|---|---|
vcdiff.py |
two disc images, or an image and a patch | makes and applies xdelta patches (VCDIFF, RFC 3284) in plain Python, for sharing a mod against the Redump image; applies patches xdelta3 made, with its LZMA or DJW secondary compression; images may be CSO or ZSO |
cso.py |
a CSO or ZSO image | reads compressed disc images (CSO v0–v2, ZSO; deflate and LZ4) for vcdiff.py and patch_disc.py, and hands CHDs to chd.py; info checks the index, check gives the ISO's SHA-1 and whether it is the Redump dump, extract writes the ISO |
chd.py |
a CHD image | reads MAME's CHD v5 (DVD, raw and one-track CD CHDs; zlib, LZMA, Huffman, FLAC and the CD codecs, zstd with Python 3.14) for the same tools; info shows the header, codecs and metadata, check decodes every hunk against its CRC and the header's data SHA-1 and gives the ISO's SHA-1, extract writes the ISO |
python SRC/vcdiff.py make disc.iso modded.iso mymod.xdelta| Tool | Reads | Does |
|---|---|---|
save.py |
a memory-card save folder, ISO/SLES_541.51, ISO/DLL/SAVEPRG.REL |
decrypts and decodes a saved game by running the game's own serializers and re-encodes it byte for byte. Shows the club, squad, youth team, staff, pair combinations (combi), candidate lists (candidates), accounts and season plan (finances), the other clubs (clubs) and the imported Virtua Pro Football player (editplayer). set edits money, club status, players' abilities, condition, salary and contract, the combinations, staff and dissatisfaction, the season plan, other clubs' friendship and rank, and the edit player (edit:<field>). rename copies a save to a modded disc's name, copy to the card's next free slot, convert re-encodes one for a disc with --mod third-tier. --iso ISO_JP reads and writes the Japanese release's saves |
ps2mc.py |
a PCSX2 memory card image (.ps2 file) |
lists and checks the card (every page's ECC, every chain), copies saves off it (get), and writes a new image with a file replaced or added (put) or a folder added (mkdir) or copied (copy), with the ECC and FAT kept right; roundtrip writes every file back to an identical image |
pmplayer.py |
Virtua Pro Football's pmplayer.bin, ISO/DLL/SAVEPRG.REL |
the edit players a career can import: decrypts and checks the file, shows each field and what the import makes of it, edits names, height, weight, abilities, skills, styles and the rest (set), round-trips byte for byte, and compares a career's imported player with its record (check) |
python SRC/save.py show <card>/BESLES-54151-G003
python SRC/save.py player <card>/BESLES-54151-G003 3
python SRC/save.py set <card>/BESLES-54151-G003 edited.bin money=2000000000 0:all=99
python SRC/save.py rename <card>/BESLES-54151-G003 <card> F # to BESLES-54151-F003, for LMASTER full (M for max)
python SRC/pmplayer.py show <card>/BESLES-54153FASYS00/pmplayer.binSee SAVE_FORMAT.md and
PMPLAYER_FORMAT.md.
| Tool | Reads | Does |
|---|---|---|
editor.py |
DAT/, ISO/, a mod folder |
the mod editor: one window with a tab per kind of data (USAGE.md), saving to a mod folder (mod/ by default) and writing the matching python SRC/... command to mod/editor.log; File > Build disc makes the modded image or patch |
saveedit.py |
a career save on a PCSX2 folder memory card or card file (.ps2) |
the save editor: a separate window for one save, through save.py (USAGE.md, SAVE_EDITOR_PLAN.md) |
python SRC/editor.py open # the mod folder mod/
python SRC/editor.py open mods/terry # another mod folder
python SRC/saveedit.py open <card>/BESLES-54151-G000 # a save on a folder memory card
python SRC/saveedit.py open <card>.ps2 # a card file: pick a save from itUSAGE.md is the guide to both windows: the mod editor's
tabs, Build disc and the save editor's pages. Each tab edits through a
writer's own functions and takes the field names and ranges from it
(pbdata.edit_spec), so a file saved by the editor is byte for byte
what the logged command gives. The mod folder keeps each file under its
DAT/ path, and files from outside DATA.CVM under disc/
(mod/disc/SLES_541.51), which matches patch_disc.py's targets.
Tools reuse each other by importing the sibling script. The ones not
listed (afs.py, bpb.py, chd.py, cvs.py, gamedata.py,
pac.py, ps2mc.py, rofs_decrypt.py, svr.py, tbb.py) stand alone, and
regress.py runs the others as commands.
| Tool | Builds on |
|---|---|
sles_disasm.py, snr2.py |
each other: the executable's symbols name an overlay's imports |
gamever.py |
sles_disasm.py, snr2.py; and every tool that reads the executable or an overlay uses it to find PAL addresses in another build |
extract_disc.py |
rofs_decrypt.py |
csp.py |
svr.py for the textures inside CSP packs |
zbf.py |
pac.py, to read depth buffers straight from BG_*.MRG |
humanid.py |
pac.py for the PRELOAD copies; sles_disasm.py and gamever.py for the object table |
eventdata_turn.py |
tbb.py |
evsdatabin.py |
mbb.py for --text |
packdata.py, ninja.py |
pac.py (KC@P entries, PRSH) and each other; ninja.py also svr.py for its exports |
stadium.py, system.py, schedule.py |
pac.py, tbb.py |
emblem.py, news.py |
pac.py, tbb.py, svr.py |
mbb.py |
pac.py for MES.PAC; sles_disasm.py, snr2.py, gamever.py for vars |
sqb.py |
pac.py, packdata.py for COMBINATION2.CSB, pbdata.py for field and ability names |
pbdata.py |
pac.py, tbb.py, initteam.py (club names), mbb.py (Japanese names), sles_disasm.py and gamever.py (--sles) |
initteam.py |
pac.py, tbb.py, pbdata.py and mbb.py for names |
teaminit.py, plrcommon.py, plrsim.py |
tbb.py and pbdata.py for names; the last two pac.py; teaminit.py limits sles_disasm.py and gamever.py |
regulation.py |
tbb.py, schedule.py for the team-entry check and leaguesize.py for the schedules' names |
clubresult.py |
tbb.py, and regulation.py and schedule.py for the club-count checks |
training.py |
tbb.py, pbdata.py for the ability names and mbb.py for the practice names |
camp.py |
pac.py, tbb.py, and mbb.py for city and club names |
formset.py, tour.py |
tbb.py, and pbdata.py (formations) or camp.py and initteam.py (club names) |
clubrank.py |
tbb.py, and camp.py for the competitions' names |
leaguesize.py |
schedule.py, initteam.py, regulation.py, clubresult.py, clubrank.py |
uniform.py |
pac.py, tbb.py, packdata.py and svr.py (licensed kits), sles_disasm.py (the executable's copy), initteam.py (club names) |
clubevent.py |
sles_disasm.py and gamever.py for the tables, camp.py for the names |
natcal.py |
tbb.py, and schedule.py for the national cups' turns |
acrobata.py |
pac.py, ninja.py, svr.py, snr2.py, sles_disasm.py |
sounddat.py |
sles_disasm.py for music |
preload.py |
pac.py, and sles_disasm.py for the load lists |
save.py |
sles_disasm.py and snr2.py to run the game's serializers; pbdata.py, initteam.py, tbb.py for field layouts, names and tables; teaminit.py for the salary limits |
patch_disc.py |
extract_disc.py and rofs_decrypt.py (finding files, rewriting directory records), cso.py (a CSO, ZSO or CHD input), pac.py (rebuilding archives), tbb.py, sles_disasm.py and gamever.py (the executable, the Japanese build), sqb.py (--skip-tutorial), schedule.py (national-cups, league-balance, third-tier), regulation.py, clubresult.py, initteam.py and pbdata.py (third-tier), camp.py (the camp switches), training.py and mbb.py (drill-names, injury-lines), plrcommon.py (facility-tiers), plrsim.py (overseas-bases) |
editor.py |
the writers it edits through: pbdata.py (People), initteam.py (Clubs, Season), leaguesize.py (Season), natcal.py (National calendar), teaminit.py (New club), plrsim.py (Free agents, Player links, Overseas bases), initteam.py (Nations & stadiums), stadium.py (Stadiums), regulation.py and clubresult.py (Regulations), clubrank.py (Club ranking), training.py (Training), camp.py (Camps), tour.py (Tours), plrcommon.py (Facilities), clubevent.py (PR events), formset.py (Tactics), uniform.py (Kits), emblem.py (Crests), mbb.py (Text), sqb.py (Comments); extract_disc.py and patch_disc.py for Build disc, sles_disasm.py and pac.py for the executable and pack headers |
saveedit.py |
save.py, editor.py (widgets), ps2mc.py (card files), pbdata.py and initteam.py for names |
pmplayer.py |
save.py (cipher, CRC, and career saves for check), pbdata.py for field names, gamever.py |
cso.py, vcdiff.py |
chd.py for CHD images; vcdiff.py reads images through cso.py |
The disassemblers connect to the format tools through the docs: a
format is found in the code, documented in DOC/, implemented in
SRC/ and checked over all of DAT/ (the full workflow is in
AGENTS.md). Every tool's check
then goes into regress.py, which compares the output
with a saved baseline:
python SRC/regress.py bless # once, from a known-good state
python SRC/regress.py run # after every change; exit 1 on any differenceBaselines are kept in .regress/, which is git-ignored because they list
file names and counts from your disc. When an output change is intended,
bless <name> accepts it.
Every format and folder doc labels its claims confirmed or
empirical and has a "Still unknown" section. For players,
INSTALL.md covers checking the disc, applying a patch and
saves.
| Doc | Covers |
|---|---|
DATA_CVM_EXTRACTION.md |
the DATA.CVM container, how the ROFS key is derived from its header, regenerating DATA.ISO |
REBUILD.md |
putting edited files back on the disc (in-place patching, size changes, moved files, a grown disc, copies and rebuilt archives), sharing mods as xdelta patches, compressed images (CSO, ZSO, CHD) |
PRELOAD_DIR.md |
DAT/PRELOAD: the bulk-load packs, the load lists and folder ids, and which copy of a file the game reads, and rebuilding a pack |
JAPANESE_RELEASE.md |
the Japanese release (SLPM-66316): setting it up, how gamever.py finds addresses in it, and how its code and data differ from PAL |
LMAST_DATA_CVM_INFO.md |
the original ROFS key recovery in PCSX2 and cvm_tool workflow (historical) |
| Doc | Covers |
|---|---|
SNR2_FORMAT.md |
DLL/*.REL overlay format, the SN DLL loader, SLES_541.51's imports, which overlay each sequencer module lives in, the wild-card module, C++ classes from their type information |
SQB_FORMAT.md |
SEQ/*.SQB and PSC*.PAC sequencer scripts: command encoding, argument types, labels, the root and PwkScript command sets, what the PwkScripts compute (only the detail-screen comments run), global memory, the tutorial skip and the developer launcher |
GAME_FLOW.md |
the root scripts as a flow chart: title routes, new game, loading, the season loop, year start, main menu, match day, game over, the event timings |
EVS_PROCEDURES.md |
the scouting, transfer and loan procedures (code-only events), scout-list search criteria, squad and loan limits, the 13 regions |
INJURIES.md |
how injuries are rolled (fatigue and age pick the kind), the value table, recovery, and the condition-line messages, two of which were cut |
ECONOMY.md |
the club's economy: what a build costs (record cost × business cycle × sponsor discount), the plan screen's limits, attendance, tickets and season tickets, popularity, sponsors and TV, merchandise, transfer fees, PR events, camps and tours, and the switches that change them |
CLUBEVENT_FORMAT.md |
the PR events in the executable: the offer, chance and success records field by field, the names in MES.PAC, when each event is offered, the cut Renew Mobile Site, and the Japanese differences |
CLUB_LIMITS.md |
where the code fixes the clubs at 441 (resident clubs 1–441, the non-resident 442–459): every site of 440–442, sorted into club loops, resident checks, buffers and the rest, and how the third tier makes 442–459 full clubs |
UID_LIMITS.md |
where the game fixes the schedules at 164: UID 164 as the default rules, the files that set the count, the code that names it, the save pools the schedules share, and the third tier's leagues and seasons |
SAVE_FORMAT.md |
memory-card saves: Blowfish key, header and layout CRC, the ten Pwork blocks, money, date and squad fields, separate saves and the PCSX2 CRC for a modded disc |
PS2MC_FORMAT.md |
PCSX2's memory card image files: pages and their ECC, the superblock, the FAT, directory entries, and how ps2mc.py writes a card |
PMPLAYER_FORMAT.md |
Virtua Pro Football's edit players (pmplayer.bin): file layout, CRC, each record field and what the career import makes of it, and its play-style bug |
SAVE_EDITOR_PLAN.md |
the save editor's design on top of save.py: its pages, the steps it was built in, the decisions made, and what is tested in the game |
| Doc | Covers |
|---|---|
LMASTER_MOD.md |
the LMASTER Mod: its editions, groups and switches, each fix and improvement with its evidence and test status, the third tier, the real names, the real grounds, and the candidates still to build |
SPONSOR_NEGOTIATION.md |
the Sponsor screen's main sponsor negotiation, switched off in PAL by a stub check, and the patch_disc.py --sponsor-negotiation patch that restores it |
WIDESCREEN.md |
the PCSX2 16:9 patch (PNACH/): Hor+ 3D, and the UI fix that draws the HUD, menus and season-mode backdrops at 4:3 |
| Doc | Covers |
|---|---|
PAC_FORMAT.md |
BINPAC, KC@P, PRSH, and how the packer laid BINPACs out (the writer) |
TBB_FORMAT.md |
TBB1/TBL1 tables, symbol recovery from SLES_541.51 |
| Doc | Covers |
|---|---|
0SYSTEM_DIR.md |
DAT/0SYSTEM: UI colours, crest/badge/sponsor texture packs and how an id picks an entry, fonts by language, icons, leftovers |
ACROBATA_DIR.md |
DAT/ACROBATA: the Acroarts event scenes, their chunk layout and resources, the executable's copy of the pack index, scene ids by language |
BG_DIR.md |
DAT/BG: the rooms' archives and room codes, the 103 people and their heads, hair and palettes, hands and props, trophies, HUMANID.BIN |
CVS_DIR.md |
the CVS/ folders left on the disc: original file names, commit dates, which files the build made |
EMBLEM_DIR.md |
DAT/EMBLEM: the club editor's crest and flag parts, its colour palette, the edit tables, and the player-editor tables |
GAME_DIR.md |
DAT/GAME: commentary, sound banks, models, motions, the match AI's files and its leftovers |
NEWS_DIR.md |
DAT/NEWS: newspaper mastheads, article pictures, ads and cartoons, and the monthly ranking flags |
PARAM_DIR.md |
DAT/PARAM: which code loads each file, record sizes, and the tables without a doc of their own |
PLAYER_DIR.md |
DAT/PLAYER: face packs, licensed kits, etc::PackData |
SEQ_DIR.md |
DAT/SEQ: the root sequencer scripts, which are loaded, their CVS history |
SOUND_DIR.md |
DAT/SOUND: which bank is which, the executable's bank table, and the music table (music id → song or stream) |
STADIUM_DIR.md |
DAT/STADIUM: the 10 stadium models, .PRI draw priorities, crowds, adverts, BUILD_STADIUM and its writer |
TEST3D_DIR.md |
DAT/TEST3D: test models, debug shapes, test kits, the launcher viewers' data |
AUDIO_DIR.md |
ISO/AUDIO: the AFS archives (music, commentary, chants, ambience), AFS and ADX layouts, 0FLIST.DIR |
| Doc | Covers |
|---|---|
PBDATA_FORMAT.md |
the player database: header, bit-packed player/manager/scout records, ability and money tables, the match's player parameters and which AI classes read each |
INITTEAM_FORMAT.md |
starting leagues and divisions, last season's order, computer-team squads, where club names come from |
TEAMINIT_FORMAT.md |
the player's new club: what the league and team style choose (squad, youth team, staff, rival club) |
SCHEDULE_FORMAT.md |
the season calendar: schedule UIDs, turns, games, pairings, where entrants come from |
REGULATION_FORMAT.md |
match regulations per schedule: prizes and win bonus, bans, squad, foreigners and substitution limits, promotion and relegation places |
CLUBRESULT_FORMAT.md |
how each schedule reports a final place: a number, a cup round's name or nothing |
TRAINING_FORMAT.md |
the training tables: practices, facilities, camps, match growth and decline with age |
CAMP_FORMAT.md |
the training camps: cities, types, clubs and descriptions, and two bugs in the data |
PLRESOURCECOMMON_FORMAT.md |
the common resource pack: facility records (build time, upkeep, stadium capacity), formation and nation tables, and which code reads each |
PLRESOURCESIM_FORMAT.md |
the season-mode resource pack: what each of its 16 entries holds and which code reads it |
| Doc | Covers |
|---|---|
MBB_FORMAT.md |
message text, encodings and escape codes, talk-scene body reactions ({react:N}) |
EVSDATABIN_FORMAT.md |
club-management event tables, event ID types, scene types (what happens after a scene), talk types |
EVENTDATA_TURN.md |
the unused turn-event prototype |
| Doc | Covers |
|---|---|
SVR_FORMAT.md |
textures and GS swizzling |
CSE_FORMAT.md |
DAT/CSE 2D layouts |
ZBF_FORMAT.md |
pre-rendered Z buffers |
NINJA_FORMAT.md |
Ninja (NN) models, skeletons, motions, VU/PX Plus vertex streams |
UNIFORM_FORMAT.md |
club kits: UNIFORM_LIST bit layout, kit designs and colours, the 96 colours, the COLOR_TBL clash table, UNIFORM_GK keeper kits |
BPB_FORMAT.md |
the match AI's play books: records of player and ball paths, point coordinates, play ids; the COMBINATION2.CBB records and the 29 combination commands |
GAMEDATA_FORMAT.md |
GAME/GAMEDATA.BIN's 0x30-byte ball-contact records (partial) and the unread AI_PARAM.BIN |
CONTRIBUTING.md explains how to report a bug, test
something in the game, or send a change, and what a change needs before
it is merged. Everyone taking part follows the
CODE_OF_CONDUCT.md.
AGENTS.md has the conventions for docs and tools, the
workflow for a new format or writer, and where each kind of record goes:
facts and open questions in DOC/, tasks by stage in
TODO.md, finished items in DONE.md. It is
written for AI assistants but applies to anyone.
The project is run by Nexxus (Pyra Drake, Nexxus Drako Multimedia), who directs the research and makes the in-game tests; the docs record those results as "Nexxus's report".
The code in SRC/ and the documentation in DOC/ and this repository are
licensed under the GNU General Public License, version 3 or (at your option)
any later version. See LICENSE. Each file carries an
SPDX-License-Identifier: GPL-3.0-or-later header.
This covers only the work in this repository. The game, its disc image and its data files are not part of it and are not covered by this license.
See CONTRIBUTORS.md for everyone who has worked on
the project, the community it is made for (the
LMAST/SFCC English Fan Discord), the
first decryption of DATA.CVM by @tw09627, and the software it
builds on.
Neither the maintainer nor the community are equipped to deal with mental health or other crises. Please use findahelpline.com to find a local helpline, or if the crisis is life-threatening, contact your local emergency services.
Nexxus Drako Multimedia was created on the stolen lands of the Jagera and Turrbal peoples, in what is now called Brisbane. Sovereignty was never ceded. This always was, and always will be, Aboriginal land. I pay my respects to Jagera and Turrbal Elders past and present, and to all Aboriginal and Torres Strait Islander peoples.
Colonisation didn't end; it's ongoing, and I back First Nations-led fights for land back, treaty and truth-telling.
Nexxus Drako Multimedia is queer-run and queer-made. Trans, nonbinary, intersex, asexual, aromantic, bi, pan, lesbian, gay and every other queer voice is welcome here, and actively backed: not tolerated, backed.
Transphobia, intersexphobia, homophobia, biphobia, acephobia and any other bigotry get you removed, no warnings and no debate.
Trans rights are human rights.