Skip to content

Latest commit

 

History

History
2056 lines (1806 loc) · 135 KB

PBDATA_FORMAT.md

File metadata and controls

2056 lines (1806 loc) · 135 KB

Player database (PARAM/PBDATA_EU.PAC, PBDATA_JP.PAC)

PBDATA_EU.PAC holds every real person the game knows: 27,950 players, 3,000 managers and coaches, and 1,000 scouts. That covers names, nationality, age, height, weight, positions, preferred shirt number, a required status, personality traits, skills, play styles and 64 ability ratings per player. The squads in OTEAMMEMBER.TBB (INITTEAM_FORMAT.md) refer to players by their index here.

SRC/pbdata.py reads it. python SRC/initteam.py squads uses it to put names on the squads.

Confirmed from the game code

Address Symbol What it shows
0x110d50, 0x11034c PwkCallbackCommand_BpDataReadFile and its caller loads the pack and calls plBp_Create(work, entry0, entry1, entry2, entry3) with the four BINPAC entries in order
0x20dd78, 0x20c5d8 plBp_Create, PlBpinfoTask::init entry 0 is the header, entry 1 the records, entry 2 and entry 3 are kept at 0x390670 / 0x390674. If the header's first byte is '0', it isn't parsed
0x20da58 PlBpinfoTask::initBpmaster the header layout below
0x20c700 getPbase(id) player id (0 to count−1) is at records + id × 98. It is decoded by plBits_DecPlPbaseEx. Ids from 0x7cce are the edit player imported from Virtua Pro Football, held in the save (SAVE_FORMAT.md)
0x20c808 getMbase(id) ids from 0x6d2e, at records + players×98 + (id − 0x6d2e) × 81, decoded by plBits_DecPlMbaseEx
0x20c9b8 getSbase(id) ids from 0x78e6, decoded by plBits_DecPlSbaseEx
0x20bd28 PlBitsClass::readBits(n) reads n bits, most significant first. Bits are taken from each byte starting at 0x80, and bytes in order
0x20bdc8 PlBitsClass::readBitsStr(buf, n) n 8-bit characters: the name
0x2e8568 plBits_DecPlPbaseEx the player fields below, in order. Afterwards it adds 16, 150 and 45 to +0x28, +0x29 and +0x2a, replaces +0x34 with STATUS[v & 0xf], and maps each of the 64 abilities through 0x2e8540
0x2e8a70 plBits_DecPlMbaseEx the manager fields. The two groups of 9-bit values are sign-extended from bit 8 into s32 at +0x4c and +0x54 (pointers set up at 0x2e8b64)
0x2e8ec8 plBits_DecPlSbaseEx the scout fields. The last 45 of its 49 seven-bit values go through 0x2e8540
0x2e8540, 0x55b950 ability mapping ABILITY[min(v, 31)] with ABILITY = 38, 40, … 96, 98, 99
0x55b970 status table (required status) 0, 200, 1000, 3000, 5000, 7500, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000
0x2175e0, 0x2176a8 plPinfo_IsForeigner, plPinfo_IsEU player +0x14 is the nationality (PlNati). Bit 1 of +0x63 is an EU passport on top of it
0x218748 plPinfo_IsSkill +0x64 is a bit mask, one bit per PlPlayerSkill
0x20d908 getPinfoRank for ids ≥ 0x63f7 the rank is +0x18. Below that it is 15 − the row of 16 {first, last} ranges at 0x5eac08 (filled at run time) that holds entry 3's value for the player
0x20d9a0 getPinfoApos0 the main position is +0x1c. For ids below 0x63f7 it is the column of the group table at 0x52fbf8 (row 15 − rank) whose range holds entry 3's value (Entries 2 and 3)
0x20d470 PlBpinfoTask::serchPinfo searches the ranking: entry 2 (0x390670) through the group table at 0x52fbf8
0x24d450, 0x24d480, 0x33c5d8 pwkOteam_InitNonresident modes 0 and 2, _InitCommon the fixed squads of the id blocks (Player id blocks)
0x2734c8 pwkTeam_SetUnumberOpinfo +0x2b is the player's preferred shirt number, 1–99
0x218728 plPinfo_IsSkill (PlPinfo overload) reads the skills at PlPinfo +0x1fc, so the game's copy of the record starts at PlPinfo +0x198
0x24d8a8 pwkPlayStyle_Init PlPinfo +0x1f6 (record +0x5e) holds 5 play styles: it copies the non-zero ones into the style list at +0x27c, then adds random styles 1–22 (slti 0x17) for the player's position. The first stored style becomes the current one (+0x278)
0x289e50, 0x52f708 CDetailManager::AddPlate_PLAYSTYLE, Msg::GetString's base ids the current style is drawn as GetString(5, style). Type 5's base id is 150, and types below 0x29 use global slot 0, category 1. So style n is message 1:150 + n
0x246884 match growth (in the function before pwkGUtl_AddExp's caller at 0x246930) abilities 45–52 (0x2d + k) gain experience only when k is the system of the club's current team style (my-team data +0x4204); abilities 33–41 only for the player's own position row
0x215a00 plMisc_Nati2NatiTeam nation → national team: PLRESOURCECOMMON.PAC entry 3, table 2, one u16 per nation. pbdata.py names nations through it, because the team names are known (message category 3)

Entry 0: header (46 bytes)

Offset Type Value Meaning
0x00 char[4] 1000 version text. Only checked for a leading '0'
0x04 u32 1 not read
0x08 u32 × 3 27,950 / 3,000 / 1,000 record counts: players, managers, scouts
0x14 u32 3,058 stored at +0x34 of the task. Use not traced
0x18 u16 (+2) × 3 98 / 81 / 71 record sizes in bytes. Each is followed by ff ff
0x24 u16 64 stored at +0x50. Use not traced
0x26 8 bytes 2e 02 26 a5 00 00 30 30 not read by initBpmaster

27,950 × 98 + 3,000 × 81 + 1,000 × 71 = 3,053,100 bytes, exactly the size of entry 1 in PBDATA_EU.PAC.

Entry 1: records

Each record starts with a char[19] name (cp850, zero-padded), followed by bit fields read MSB first. The widths, order and destination offsets are all confirmed from the decoders. The destination offset is where the game keeps the value in memory. Meanings marked empirical come from the data alone. Player field names from +0x34 on are the developers' own labels (below); confirmed on such a row means the game code that reads the field was also found. Every field also has the developers' source name (The developers' field names), which names the fields the editor labels leave out.

Players (98 bytes, 779 bits used)

Bits × Offset Name Meaning
8 1 +0x14 nation nationality, 1–144. confirmed
5 1 +0x18 rank 0–15. confirmed (read directly only for ids ≥ 0x63f7)
4 3 +0x1c position main and up to two more positions: a cell of the detail screen's pitch grid, 0–12, with 13 meaning none. See Positions. confirmed
7 1 +0x28 age stored − 16. Empirical meaning: 16–40, and it matches the real players in 2005. Computer-team players don't show this age: their squad slot's age from OTEAMMEMBER.TBB is used instead (INITTEAM_FORMAT.md). Tested in game: a database age of 16 didn't change Terry's shown age
8 1 +0x29 height stored − 150, in cm (158–205). Empirical. The decoder stores the sum in a byte (sb at 0x2e8a14), so heights above 255 wrap. Tested in game: 313 cm shows as 57 cm
7 1 +0x2a weight stored − 45, in kg (48–100). Empirical
7 1 +0x2b shirt preferred shirt number. confirmed
3 1 +0x2c leg 0–3. Bit 0 set means right-footed, clear means left. Empirical: every well-known left-footer tested (Robben, Ashley Cole, Giggs, Messi, Roberto Carlos, Duff, Cech) has it clear. Bit 1 is set for famously two-footed players (Maldini, Henry, Rooney, Duff), and VPF's Player Edit screen shows Rooney (leg 3) as "Both(R)", so bit 1 is two-footed. The developers' editor names the four values 左, 右, 両左, 両右 (left, right, both-left, both-right). The detail screen shows a message from 100–103 (LEFT, RIGHT, LEFT, RIGHT), which fits 100 + leg, but the copy into PlPinfo +0x1c4 hasn't been traced. Tested in PCSX2: Van der Sar (250) set to 2 shows LEFT, as 100 + leg predicts; the screen doesn't show two-footedness
16 1 +0x30 castno always 0. source name castno; no reader found
16 1 +0x32 face face number, 0–17,172. confirmed: CDetailManager passes it to CDetailFace::Request (0x2869b4)
16 1 +0x34 req_status required status: band 0–15, looked up in the status table (0x55b970). confirmed: pwkTeam_UpdatePlayerCandidates (0x260cf8) leaves a player out of a candidate list when it is above the club's pwkTeam_Status(). Managers (+0x24) and scouts (+0x22) have the same field. The BPINFO CHECK screen labels the 10,000 cutoff "1mil" (SQB_FORMAT.md)
3 1 +0x36 tone speech tone, 0–3. Read by the event code in SIMPRG.REL; what each value sounds like isn't traced
2 8 +0x37 dissatis sensitivity to 8 causes of dissatisfaction, 0–3. confirmed; see Personality
4 1 +0x3f professionalism picks the starting power range. confirmed
4 1 +0x40 pressure pressure resistance. Read by the match engine (GAMEPRG.REL), not traced
4 1 +0x41 loyalty club loyalty; sets a promoted youth player's contract years. confirmed
4 1 +0x42 star star quality; scales popularity changes. confirmed
4 4 +0x43 explodetype source name explodetype, 0–15 ×4: probably meant for the growth spurts (pwkExplo_*, see explodetype). No label in the developers' editor. Mostly 0 (15,718, 24,808, 27,428 and 27,917 players), more often set for low-ranked players. Kept in the save but read by nothing: only the save's serializers touch PlPinfo +0x1db–+0x1de (its game copy) in the executable or any overlay
3 1 +0x47 moti_type motivation type, 0–7. confirmed
3 1 +0x48 cond_type condition type, 0–7. No reader found outside the debug and save code
5 1 +0x49 potential 0–7; read by plPinfo_InitEditAbil (0x21b2b8)
4 3 +0x4a growth physical, skill and mental growth types, 0–15. confirmed (pwkGUtl_GetPhysical/Skill/MentalGrowCoe, 0x245ef8–0x245f58)
3 1 +0x4d travel travel tolerance, 0–7. confirmed
3 1 +0x4e injury_res injury resistance, 0–7. confirmed
3 1 +0x4f recovery recovery, 0–7. confirmed
4 1 +0x50 foul_avoid foul avoidance, 0–15. Read by the match engine, not traced
4 1 +0x51 weak_foot weak-foot accuracy, 0–15. Read by the match engine, not traced. 163 of the 499 two-footed players have 8 or more, against 324 of the other 27,451
5 1 +0x52 policy the player's policy type, 0–24. confirmed (pwkTeamType_PolicyInit 0x27033c, also plCombi_* and pwkPromise_*)
3 1 +0x53 adapt adaptability, 0–6. confirmed: pwkTeamType_GetFit (0x270624) looks it up in a table at 0x3996d8 (30, 33, 35, 38, 40, 45, 50)
4 1 +0x54 intelligence always 0
2 1 +0x55 ball_touch ball-touch type, 0–2. Becomes match parameter 139, which nothing in the match reads
2 1 +0x56 dribble_style 0–3: which of four sets of dribble motions the player uses. confirmed; see The tendencies
2 1 +0x57 sleeves 0 by season, 1 short, 2 long. confirmed; always 0. See Kit style
3 1 +0x58 wristband 0 none, 1–4 a colour. confirmed; 0 in 27,256 players
3 1 +0x59 gloves outfield gloves in cold weather: 0 none, 1–4 a glove texture. confirmed; 0 in 27,187 players
2 1 +0x5a gk_cap the goalkeeper's cap, 0–3: source name nicknack_gkcap. No reader was found, and value 3 showed no cap in PCSX2. 1 in 46 players
3 1 +0x5b gk_gloves goalkeeper's gloves, 0–5. confirmed
1 1 +0x5c gk_pants 1: a goalkeeper wears long pants. confirmed
4 1 +0x5d boots boot colours, 0–14. confirmed
5 5 +0x5e style play styles, 1–22, 0 for none. confirmed. See Play styles
3 1 +0x63 flags bit 1: EU passport. confirmed. Set in 19,333 players
16 1 +0x64 skills bit mask. confirmed. All 16 bits are used; see Skills
3 11 +0x66 tendency how readily the match AI picks each kind of play, 0–4 with 2 neutral: dribble, shoot, carry, feed pass, side change, back pass, killer pass, one-two, clear, press, goalkeeping. confirmed; see The tendencies. Source name ai; no label in the developers' editor. +0x67 is also the row of the affinity table (see Personality). Team combos shift the other ten (pwkPCombo_Add, see explodetype and team combos)
5 64 +0x74 ability 64 ratings (PlAbilNo 0–63), each mapped to 38–99. confirmed. See Abilities

The 5 bits after the last field are zero in every record.

Player id blocks

The last 2,359 player records (25,591–27,949) aren't in any club's starting squad (OTEAMMEMBER.TBB, teams 3–441). Confirmed: pwkOteam_InitNonresident (0x24d568) gives them to teams through _InitCommon(record, first team, last team, first player, last player, per team) (0x33c5d8). It hands out the ids in order and takes each player's shirt number from +0x2b (or his place in the block if that is 0):

Ids Called from Teams What
25,591–26,040 mode 0, 0x24d450, at new-game setup (0x110ae4) 442–459, 25 each made-up players for the 18 clubs without an OTEAMMEMBER squad, three per league nation: England, France, Germany, Italy, Spain, Netherlands (empirical: each block of 25 has one nationality). They are the third-tier clubs of the tutorial's playoff (England's 442–444 Gillingham, Nottingham and Rotherham, LMASTER_MOD.md). England's first 18 and 16 from 25,623 are the built-in default club (TEAMINIT_FORMAT.md)
26,041–27,949 mode 2, 0x24d480, from PwkCallbackCommand_VS_Start (0x111478) 460–542, 23 each the national teams' fixed squads, used in VS mode. Every block has one nationality

A national-team record repeats a club player under the same name, as a separate record: Gianluigi Buffon is 3,175 at Juventus and 26,110 (shirt

  1. in Italy's block, John Terry 101 at Chelsea and 26,046 (shirt 6) in England's. The two can differ (that Terry's height, abilities and age aren't the club Terry's). Nexxus identified these as national-team players.

Tested in PCSX2: with record 26,046 (England's Terry) renamed NT.Terry.VS, England's starters in a VS match listed DF 6 NT.Terry.VS, in a 4-4-2 with the rest of the block's names (Robinson, A. Cole, G. Neville, Ferdinand, Beckham, Gerrard, J. Cole, Wright-Phillips, Owen, Rooney).

In a career the fixed squads aren't loaded: mode 3 (0x24d4b0) only sets the 83 team numbers, and squads are called up from the club players (National team call-ups). Mode 1 (0x24d4d8, from PwkCallbackCommand_PromotionEnd) isn't traced.

Managers and coaches (81 bytes, 642 bits used)

char[19] name, then (bits × count at offset): 8 +0x14 (nationality, empirical: same range and position as the players'), 5 +0x18 (rank), 3 +0x1c (job, see below), 16 +0x20 (face), 6 +0x22 (age, see below), 16 +0x24 (required status, confirmed table lookup), 4 ×4 +0x26 (appeartype), 2 ×5 +0x2a (comptype), 3 ×4 +0x2f, 2 +0x33, 6 +0x34, 3 ×8 +0x35, 8 ×3 +0x3d, 3 ×7 +0x40, 5 ×5 +0x47, signed 9 ×2 +0x4c, signed 9 ×4 +0x54, 1 ×2 +0x64, and 48 abilities (5 bits, mapped to 38–99) at +0x66. The 6 bits left over are zero. The fields from +0x2f on are named in The developers' staff editors.

Scouts (71 bytes, 565 bits used)

char[19] name, then 8 +0x14 (nationality, empirical), 8 +0x18 (age, see below), 5 +0x1c (rank), 16 +0x20 (face), 16 +0x22 (required status), 4 ×4 +0x24 (appeartype), 1 +0x28, and 49 × 7 bits at +0x29. The first 4 are special searches and the last 45 are abilities, clamped to 31 and mapped to 38–99. The 3 bits left over are zero. See The developers' staff editors.

Staff age

Manager +0x22 and scout +0x18 are the age, stored as is (no offset, unlike the players' +0x28). Without TEAM_INIT_DATA.TBB, pwkTeam_Init2 copies manager byte 0x22 (0x25eff4) and scout byte 0x18 (0x25f038) into the slot where the table otherwise puts the age. Where the table gives it, it equals these fields in all 180 manager and 108 scout records. Tested in PCSX2: the game shows that value as the staff member's age, in the Coach and Scout Candidate Lists and for the club's own staff (TEAMINIT_FORMAT.md). Ages run 35–55 for managers and 35–58 for scouts.

The developers' player editor

DEBUGPRG.REL holds the developers' editors for players, managers and scouts (Param::PinfoEditorTask, MinfoEditorTask, SinfoEditorTask), with Japanese (cp932) labels. The retail game never loads this overlay (SNR2_FORMAT.md), but its code and labels name most of the record.

Abilities (confirmed). A table at DEBUGPRG.REL 0x11128 holds 64 pairs {label, ability number}, numbers 0–63 in order:

Ability Label Ability Label
0 ドリブルスピード dribble speed 32 集中力 concentration
1 ドリブル精度 dribble accuracy 33 GK適正 GK aptitude
2 シュート精度 shot accuracy 34 SB適正
3 シュートテクニック shot technique 35 CB適正
4 ショートパス精度 short pass accuracy 36 WB適正
5 ロングパス精度 long pass accuracy 37 DM適正
6 クロスボール精度 cross accuracy 38 SM適正
7 ヘディング精度 heading accuracy 39 OM適正
8 トラップ trap 40 WG適正 (wing)
9 キープ力 ball keeping 41 FW適正
10 タックル tackle 42 中央適正 centre
11 インターセプト intercept 43 左サイド適正 left side
12 ボール奪取 ball winning 44 右サイド適正 right side
13 マーキング marking 45–52 3-4-3 … 5-4-1 理解度 system understanding
14 プレイスキック精度 placekick accuracy 53 速攻理解度 counterattack
15 セービング saving 54 ポゼッション理解度 possession
16 キャッチング catching 55 サイド攻撃理解度 side attack
17 ハイボール high balls 56 中央攻撃理解度 centre attack
18 飛び出し rushing out 57 オフサイド理解 offside
19 スピード speed 58 プレス理解 pressing
20 ダッシュ dash 59–63 攻撃パターンA–E理解度 attack patterns A–E
21 ジャンプ力 jump – –
22 レスポンス response – –
23 スタミナ stamina – –
24 キック力 kick strength – –
25 当たりの強さ contact strength – –
26 統率力 leadership – –
27 度胸 nerve – –
28 攻撃意識 attack minded – –
29 守備意識 defence minded – –
30 サポート意識 supportiveness – –
31 視野の広さ vision – –

This agrees with every ability the game code names (the bars, the position grid and the system growth) and with all four VPF players (Ability names). The 34–41 labels name the grid cells: SB/CB, WB/DM, SM/OM and WG/FW are the side and centre of each row. 59–63 are the five attack patterns the tactics screen calls Left Flank, Right Flank, Centre Drive, Counter Attack and Possession (messages 1:620–649), but nothing in the data or the code tells the five apart, so which letter is which pattern makes no difference (Ability names). No call to the three functions that take an ability number (plPinfo_Abil2PSM, pwkGUtl_GetGrowCoe, pwkGUtl_AddExp) passes 59–63 as a constant; the match doesn't copy them (The match engine's player parameters); and every practice grows all five equally. Their two other readers are loops over all five: the hexagon window's player rating, and the travelling salesman's boost for flag 336 (0x21070c, raising each by 40% for the match), which no event sets in the retail game (EVSDATABIN_FORMAT.md). A scan for loads at PlPinfo +0x16c, +0x172, +0x178, +0x17e and +0x184 missed it because it reads them from the team data (+0x18c = squad slot 0's +0x16c). Besides that, only the hexagon table reads them, weighting 60 alone. Training's combinations A–E (practices 12–16) are a different set: they pick their row from the club's five pattern slots, and rows 12–16 train the left side, right side, centre, counterattack and possession, the tactics screen's order (TRAINING_FORMAT.md).

Record fields (confirmed). The player editor draws each field's label and then reads that field, a few instructions later, in record order (DEBUGPRG.REL 0x5ad0–0x60a8): 利き足 leg +0x2c, 身長 height, 体重 weight, 基本背番号 shirt, 得意ポジション position, 必要ステータス required status +0x34, 口調 speech tone +0x36, プロフェッショナル意識 professionalism +0x3f, プレッシャー耐性 pressure resistance +0x40, クラブ忠誠心 club loyalty +0x41, スター性 star quality +0x42, モチベーションタイプ motivation type +0x47, コンディションタイプ condition type +0x48, 将来性 potential +0x49, フィジカル/スキル/精神成長タイプ growth types +0x4a–+0x4c, 移動耐性 travel tolerance +0x4d, 怪我の少なさ injury resistance +0x4e, 回復力 recovery +0x4f, ファールしにくさ foul avoidance +0x50, 逆足精度 weak-foot accuracy +0x51, ポリシー policy +0x52, 環境適応度 adaptability +0x53, インテリジェンス intelligence +0x54, ボールタッチタイプ ball-touch type +0x55, ドリブルスタイル dribble style +0x56, 固有プレイスタイル native play styles +0x5e. The manager and scout editors read their +0x24 and +0x22 under 必要ステータス too.

The editor has no label for +0x30, +0x37 (shown on its dissatisfaction page), +0x43–+0x46, +0x57–+0x5d or +0x66–+0x70. The retail game's own labels name +0x57–+0x5d (Kit style).

The developers' staff editors

The manager editor (MinfoEditorTask, title 監督エディット) and the scout editor (SinfoEditorTask, スカウトエディット) work like the player editor: each page draws a label and then reads the field. Both take a PlMinfo or PlSinfo (+0x910 and +0x3ac of the task), which is 4 bytes and then the record (PlMbase / PlSbase). Offsets below are record offsets.

Manager abilities (confirmed). The table at DEBUGPRG.REL 0x103d8 holds 48 pairs {label, ability number}, numbers 0–47 in order. Nearly all labels end in 指導力 "coaching ability", left out below:

Ability Label Ability Label
0 モチベーションケア能力 motivation care 24 DF
1 フィジカルケア能力 physical care 25 DM
2 人望 respect 26 OM
3 不満ケア能力 dissatisfaction care 27 FW
4 選手目利き力 judging players 28 中央適正 centre aptitude
5 能力開発 ability development 29 左サイド適正 left side aptitude
6 ユース選手 youth players 30 右サイド適正 right side aptitude
7 若手選手 young players 31–38 3-4-3 … 5-4-1 (the 8 systems)
8 中堅選手 mid-career players 39 速攻 counter attack
9 ベテラン選手 veterans 40 ポゼッション possession
10 ドリブル dribble 41 サイド攻撃 side attack
11 シュート shot 42 中央攻撃 centre attack
12 パス pass 43 オフサイド offside
13 ヘディング heading 44 プレス pressing
14 インターセプト intercept 45 攻撃パターン attack patterns
15 マーキング marking 46 連携 teamwork
16 セービング saving 47 セットプレイ set plays
17 飛び出し rushing out – –
18 スピード speed – –
19 スタミナ stamina – –
20 フィジカル physical – –
21 メンタル mental – –
22 攻守意識 attack/defence awareness – –
23 GK – –

All 31 abilities behind the manager and coach bars (Manager, coach and scout bars) agree with these labels: MOTIV 0, PHYSC 1, POPUL 2, COMMU 3, ASSES 4, TRAIN 5, DRIBB 10 … MARK 15, SAVIN 16, HND 17 (rushing out), SPEED 18 … MENTA 21, ATKDF 22, CENTA 28, FLANK 29/30, FASTB 39 … CLOSD 44, ATTST 45, TEAMW 46, FK 47.

Coach type (confirmed). The editor prints the job (PlMinfo +0xa0, from record +0x1c) under コーチタイプ "coach type", through the list at 0x10940:

Job Label
0 アシスタントバランス balanced assistant
1 アシスタント攻撃 attacking assistant
2 アシスタント守備 defensive assistant
3 フィジカルコーチ physical coach
4 GKコーチ GK coach
5 監督 manager
6 ユース監督 youth manager

This matches what the game does with the jobs (5 and 6 are set at run time, 0–2 share the coaching bars) and the job averages below.

Manager fields (confirmed). The page drawn at DEBUGPRG.REL 0x1c98–0x1f2c and the two after it (0x1f30–0x2214) read these fields. The first page (0x1560–0x1c94) shows only run-time state (popularity, promises, age, contract, salary):

Offset Bits Label Values in the database
+0x24 16 必要ステータス required status (as before)
+0x2f 3 モチベーションタイプ motivation type 0–7
+0x30 3 選手起用方針 player selection policy 0–7
+0x31 3 試合進行方針 match policy 0–7
+0x32 3 育成方針 training policy 0–7
+0x33 2 休養方針 rest policy 0–3
+0x34 6 ポリシー policy 0–24
+0x35 3 ×4 許容範囲 accepted range 1–7
+0x39 3 ×4 得意範囲 best range 2–7
+0x3d 8 ×3 フォーメーション formations 0–34, list at 0x10868
+0x40 3 攻撃志向 attacking 1–5
+0x41 3 ポゼッション志向 possession 1–5
+0x42 3 攻撃展開中央サイド attack through centre or sides 1–5
+0x43 3 攻撃展開左右 attack left or right 1–5
+0x44 3 プレス開始位置 where pressing starts 1–5
+0x45 3 プレス強度 pressing strength 1–5
+0x46 3 オフサイド強度 offside trap strength 1–5
+0x47 5 ×5 監督攻撃パターンセット attack pattern set 0–30, list at 0x107e8
+0x4c 9 ×2 監督指導可能練習 drills he can teach as manager all −1 (none)
+0x54 9 ×4 コーチ指導可能練習 drills he can teach as coach 52–138 or −1
+0x64 1 実名? real name? (Yes/No) all 0
+0x65 1 モデルパターン model pattern 0 (1,526), 1 (1,474)

What the four policies (+0x30–+0x33) and the seven tactical values (+0x40–+0x46) do in the game is below, after the affinity reader.

The affinity reader confirms +0x2f as well: pwkDissatis_GetAffintyType takes a manager's column from PlMinfo +0x33.

Where the game keeps the manager (confirmed). PlMinfo is 4 bytes and then the record, so record offset n is PlMinfo +n+4. My club's manager is the PlMinfo at PlTeamData +0x4854 (pwkTeam_GetManager, 0x269d08, copies 0xbc bytes from there), so the four policies are at PlTeamData +0x4888–+0x488b and the seven tactical values at +0x4898–+0x489e. A computer club's PlTeamData is built by pwkOteam_GetTeamData with its manager from the database (pwkOteam_GetManager, 0x24be40), and the match takes a whole 0x49a0-byte copy (PlGiTask::GetTeamData, 0x211258), so the same offsets hold everywhere.

The four policies (confirmed). The developers call them plan_appoint, plan_game, plan_grow and prac_off (field table, python SRC/sles_disasm.py ISO/SLES_541.51 fields plan_ prac_off). The manager detail screen shows all four: CDetailManager::ConvertManager (0x2871a0) copies PlMinfo +0x34–+0x37, and SetupDetailManagerPage1 (0x28b9e0–0x28baf0) prints message 10300 + n, 10310 + n, 10320 + n and 10330 + n of category 201. The Japanese names, and what PAL's English shows where it differs:

Value Selection (+0x30) Match (+0x31) Training (+0x32) Rest (+0x33)
0 バランス重視 balanced 積極的に動く act boldly 中央攻撃重視 centre attack あまり休まない rarely rest
1 選手能力重視 player ability 戦術変更を多用 change tactics a lot サイド攻撃重視 side attack たまに休む rest now and then
2 チーム力重視 team strength ("Team player") 選手交代を多用 use subs a lot ラインディフェンス重視 line defence 適度に休む rest moderately
3 若手重視 young players ("Talented youth") 臨機応変に対応 adapt as needed マンマークディフェンス重視 man-marking defence よく休む rest often
4 経験重視 experience 戦術変更を好む like tactic changes フィジカル重視 physical –
5 コンディション重視 condition ("Tactically-Fit") 選手交代を多用 use subs a lot (the same as 2) フォーメーション重視 formation –
6 モチベーション重視 motivation 選手交代を好む like subs コンディション重視 condition –
7 チームフィット重視 team fit ("Suitablity") あまり動かない rarely change 実戦重視 match practice –

In PBDATA_EU.PAC every value occurs (selection 185–477 managers each, match 213–455, training 141–546, rest 496–1,027). pbdata.py and the editor show these names.

Selection. plTeam_RegSel (0x2320a0) picks the starting eleven, and its fifth argument is always PlTeamData +0x4888: at the start of the season (pwkTeam_Init2), in pwkTeam_UpdateEquip, for each computer club (pwkOteam_GetTeamData, SIMPRG.REL 0x130c64), before a match for the side that isn't my club (PlGiTask::SetTeamData, 0x210a8c), on the tactics screen's automatic line-up (CTacticsBase::setTeamTacticsAuto, 0x2a7738), and through plTeam_RegReset (plTeam_ChangeTeamStyleCom, 0x22f2a8). The policy reaches CalcuStamemPoint (0x22f910), which scores a player for a position. The policy picks a row of 8 floats at 0x53a4f8:

Policy Position fit Rank × 10 Physical Skill Mental + condition + motivation + fit
0 balanced 2 1 1 1 1 0 0 0
1 player ability 2 1.5 1 1 1 0 0 0
2 team strength 2 1 1 1 1 0.2 0.2 0.3
3 young players 2 1 1.5 1 1 0 0 0
4 experience 2 1 1 1 1.5 0 0 0
5 condition 2 1 1 1 1 0.5 0 0
6 motivation 2 1 1 1 1 0 0.5 0
7 team fit 2 1 1 1 1 0 0 0.5

The first five weights multiply plPinfo_GetPositionFitValue, the rank (PlPinfo +0x1b0, record +0x18) × 10, and the three growth stages: _GetPhysical/Skill/MentalGrowTypeValue (0x22f650–0x22f690) look up the player's age against his growth types (+0x4a–+0x4c), and the result goes through {0, 2, 4, 6, 8, 10, 13, 16, 19, 22, 25} (0x53a4e8). Their sum is then multiplied by three factors, each a curve value plus the row's last three weights: condition (PlPinfo +0x240) and motivation (+0x242), in 10 steps of 6,553, through 0.70–1.15 (0x53a488); T-FIT (+0x29a) in steps of 10 through 0.75–1.10 (0x53a4b0). Last comes GetBasePositionPower, and the score is halved when PlPinfo +0x20c has a bit of 0x300 set. So "young players" favours the physical growth stage, "experience" the mental one, and 5–7 lean on condition, motivation and T-FIT.

The match reads the same byte. GAMEPRG.REL 0xd96e8 (called from match::MatchManager) takes the period of the match from 0xaacc0 (limits 1,800, 2,700, 3,600, 4,200, 4,800, 5,400 and 7,200 seconds, the ends of the 30th to 120th minutes) and the goal difference from 0xaadd8 (−3 to +3). Policies 0–1, 2–4 and 5–7 pick one of three 8 × 7 tables (0x249500, 0x249538, 0x249570) for a mode 0–4 (2 when level or early; 0 when behind late, 4 when ahead late; the third table moves later and only to 1 or 3). The mode adds a step (0x2495ad, 5 × 5) to five tactics sliders of each of the side's three formations: when behind (mode 0), +2 to sliders 0, 1, 4 and 5; when ahead (mode 4), −2, −2, −1 and −1 there and −1 to the offside trap. GAMEPRG.REL 0xf3cb0 also switches on it, picking one of three 8 × 7 word tables by period and score (policies 0, 2 and 5 0x2495d8; 1, 3 and 6 0x2496a0; 4 and 7 0x249768) for what looks like the substitutions. Both groupings fit the match policy's names (act boldly and change tactics a lot together; "rarely change" in the mildest group) better than the selection's, yet both read +0x4888.

Match. Nothing reads +0x31 (PlMinfo +0x35, PlTeamData +0x4889) except the detail screen and the save code (SAVEPRG.REL). The match reads the selection byte where this one's names would fit (above), so it may be a slip in GAMEPRG.REL; not tested.

Training and rest. The Training screen's coach plan (SIMPRG.REL 0xf06a8, in WP::CTrainingTask; called from 0xecbe8, the branch that sets task +0xf48 to 1) fills the 8 slots at task +0x198–+0x1b4 from my club's manager: the training policy picks a row of 7 practices from SIMPRG.REL 0x228cc0 (6 rows; policies 6 and 7 use row 5), and a 0 there means the current formation's system. Slot 4 is always 27 (rest).

Row Practices (PlPracNo, TRAINING_FORMAT.md)
0 possession, centre attack, power, stamina, set plays, practice match, teamwork
1 direct play, side attack, offside trap, stamina, set plays, practice match, teamwork
2 pressing, pressing, covering, offside trap, teamwork, practice match, covering
3 speed, power, stamina, speed, power, stamina, practice match
4 system, system, pressing, offside trap, practice match, teamwork, system
5 practice match, teamwork, practice match, practice match, set plays, set plays, teamwork

The rows don't follow the names from 3 on: man-marking (3) gets the physical row, physical (4) the formation row, and formation, condition and match practice (5–7) the match row. The rest policy changes the last two slots: 0 keeps the row, 1 puts 26 (conditioning) in the last, 2 puts 27 (rest) in the last, and 3 puts 26 in the one before and 27 in the last. No other reader of +0x32 or +0x33 was found, so the policies don't change how a computer club trains.

The tactical values (confirmed). plTeam_SetStyleTeamParam (0x22ed28) sets the 7 tactics sliders of each formation (PlTeamData +0x4260 + 0x70 × formation, tactics ×7, 0–6) to the manager's value (+0x4898–+0x489e: attacking, possession, centre or sides, left or right, where pressing starts, pressing strength, offside trap) plus a step from the table at 0x534438. The step's column comes from the strength ratio, own plTeam_GetStrength (+5 for the home side) over the opponent's: up to 0.79 column 0, up to 0.89 column 1, up to 1.09 column 2, up to 1.19 column 3, and above 1.19 column 0 again, so column 4 is never used.

Slider Column 0 1 2 3 4
attacking +2 +1 0 −1 −2
possession −1 0 0 0 +1
centre or sides +2 +1 0 −1 −2
left or right 0 0 0 0 0
pressing starts −1 0 0 0 +1
pressing strength +1 0 0 0 −1
offside trap −1 0 0 0 +1

It runs with plTeam_RegSel at the same places (season start, pwkTeam_UpdateEquip, each computer club, and before a match for the side that isn't mine). Which end of a slider means more attacking isn't traced, so whether the clubs much stronger than their opponent are meant to get column 4 is open. pbdata.py allows 1–5; the sliders take 0–6.

The policies (confirmed). The 25 policies are the cells of a 5 × 5 grid with two axes: counter to possession, and individual to organisation (the player editor's カウンター~ポゼッション and タレント~チーム). A player's own point on the grid is in the save (PlPinfo +0x296, +0x298, SAVE_FORMAT.md). The table at 0x555340 (pwkTeamType_GetPolicyBseExp) gives each policy's centre: policy p is in row p ÷ 5 and column p mod 5, one cell is 16,383 on each axis.

Row (policy ÷ 5) Counter-to-possession point (+0x296) Column (policy mod 5) Individual-to-organisation point (+0x298)
0 (0–4) 65,535, counter 0 0, individual
1 (5–9) 49,151 1 16,383
2 (10–14) 32,767 2 32,767
3 (15–19) 16,383 3 49,151
4 (20–24) 0, possession 4 65,535, organisation

So policy 0 is counter and individual, 12 the middle, 24 possession and organisation. Policy 25 (an entry at the end of the table, at policy 10's point) makes pwkTeamType_GetFit return 100 for any manager; no record in the database uses it (players and managers use 0–24, each value 859–1,441 players and 92–184 managers). pwkTeamType_Exp2Policy (0x270260) turns a point back into a policy.

The manager's two sets of 4 range values (+0x35 accepted, +0x39 best) draw a diamond around his policy's centre. In pwkTeamType_CheckPolicyAllow (0x2708f0) each value v reaches v × 16,383 ÷ 2, half a cell per unit: the 1st toward counter, the 2nd toward possession, the 3rd toward individual and the 4th toward organisation. A point is inside when it is within those reaches on both axes and below the straight line joining the two reaches of its quadrant. The readers:

Address Symbol What it does with the ranges
0x2703d0 pwkTeamType_GetFit a player's fit with the manager (the T-FIT bar): 75 if his point is inside the best diamond, else 25 if inside the accepted diamond, else 0; plus (131,070 − the distance from the manager's centre, both axes added) ÷ 2,621, up to 50; at most 100
0x23a620 pwkDissatis_MPolicy the share of your squad whose points are inside the manager's accepted diamond, as a percentage, feeding the manager's dissatisfaction
0x270770 pwkTeamType_PolicyMov moves a player's point and recomputes his policy with Exp2Policy

Most managers' best values are smaller than their accepted ones (5,721 of the 12,000 pairs smaller, 3,630 equal, 2,649 larger; averages about 2.85 and 3.15), so the best diamond is usually the inner one.

The lists the indices go into (confirmed, the editor prints the entry):

  • Attack patterns, 0x107e8: 0 なし none, then 6 each of LS00–LS05, RS, CT, CA, PO (labels like LS00_00). The letters fit the tactics screen's five kinds: left flank, right flank, centre, counter attack and possession. In the database, slots 0–2 are always set and 3 and 4 are often 0 (601 and 2,153 managers).
  • Formations, 0x10868: 35 labels such as 4-4-2 dv 3 and 4-3-3 tv 1, grouped by system 3-4-3 … 5-4-1. Entry 34 repeats entry 31's label (5-3-2 tv 1), probably a slip for 5-4-1.
  • Drills, 0x105b8: 139 training menu items: 0–7 the systems, 8–28 team training, 29–31 growth policies (長所を伸ばす, 平均的に, 短所を補足), 32–51 forward, defender, goalkeeper, physical and mental training, 52–73 play styles, 74–138 individual drills (シュート, 1000本セーブ, 呼吸法 …). The database's coaches use only 52–138.

Scout abilities (confirmed). The table at 0x13240 pairs 45 labels with numbers 4–48, the index into the 49 seven-bit values at +0x29. So scout ability n is value n + 4:

Ability Label Ability Label
0 クラブ交渉能力 club negotiation 8–20 GK, LSB, RSB, CB, LWB, RWB, DM, LSM, RSM, OM, LWG, RWG, FW
1 選手交渉能力 player negotiation 21 MC (managers)
2 金銭交渉能力 money negotiation 22 AC (assistant coaches)
3 現役選手探索能力 search: professionals 23 PC (physical coaches)
4 ユース探索能力 search: youth 24 GC (GK coaches)
5 新人探索能力 search: newcomers 25 YM (youth managers)
6 中堅探索能力 search: mid-career 26–31 England, France, Germany, Italy, Spain, Netherlands
7 ベテラン探索能力 search: veterans 32–44 Western, Central, Eastern, Northern Europe, South America A, B, North, West, East and South Africa, North/Central America and Caribbean, East Asia, South Asia and Middle East, Oceania

The labels of 8–44 end in SAbil. The 11 scout bars agree (CLB 0 … VETER 7, MANAG 21 … GCOAC 24), and the 12th, unlabelled value the screen computes is 25, youth managers. Empirical check: for each of the 14 most common nationalities, the scouts' best region on average is their own (England 85 against 60 for the other regions, Brazil South America A, Argentina South America B, Japan East Asia, Scotland, Belgium and Portugal Western Europe).

Scout fields (confirmed). The editor (0x97a0–0x98b0) reads 実名? real name? at +0x28 (all 0 in the database) and 必要ステータス at +0x22. Under 特殊検索 "special search" it loops over +0x29–+0x2c and prints each value below 36 from the list at 0x13428, or なし. These 36 are play-style types: Centre Forward, Moving, Post Player, Attacker, Dynamo, Crusher, Covering, Central MF, Side Attacker, Cut-in, Defensive Side, Sweeper, Libero, Stopper, Centre Back, Orthodox, Libero GK, Playmaker, Second Striker, Operaio, Shadow Striker, Wing, Estremo, Dash Out, Last Fort, High Tower, Attacking GK, Ace Striker, Crosser, Speed Star, Line Conductor, All-rounder, Ace Killer, Wall, Super Dribbler, Regista. In the database the first search is always set (0–35), and 322, 579 and 816 scouts have none (36) in the other three.

Not labelled by either editor, but named by the developers' field table (below): manager +0x18 rank (0–15), +0x20 faceno, +0x26 appeartype (×4, always 0) and +0x2a–+0x2e comptype (×5, 0–3 each, like the players' eight dissatisfaction sensitivities comptype +0x37), and scout +0x1c rank (0–15), +0x20 faceno and +0x24 appeartype (×4, always 0). Empirical: the face numbers run 17,173–20,172 for the 3,000 managers in order and 20,173–21,172 for the 1,000 scouts, continuing the players' faces (0–17,172). Which code reads the staff ranks, faces, appear types and manager comptypes isn't traced. The players' +0x30 (castno) is 0 in every record.

The developers' field names

SLES_541.51 holds the field table of the developers' structure editor (PwkEditTask, reached from pwkEdit_CallPwkEdit 0x23c3f8): rows of 0x2c bytes naming the members of about 170 of the game's structures, with each member's offset, size, setter and getter (0x23c420–0x242bc8), range and count. Confirmed from the table: the player, manager and scout records start at 0x53dfe8, 0x53fdd8 and 0x53f900, and every offset and count matches the decoders. Two members group several of pbdata.py's fields: the manager's _policyset and the scout's abil ×49 (4 special searches, then 45 abilities). pbdata.py's names follow the editors' labels where there are any, and these names otherwise:

Record Offset Source name Range ×count pbdata.py
player +0x14, +0x18, +0x1c nationality, rank, apos0 0–146, 0–16, 0–13 ×3 nation, rank, position
player +0x28–+0x2c age0, height, weight, unumber, leg – age, height, weight, shirt, leg
player +0x30 castno – castno
player +0x32, +0x34 faceno, needstatus – face, req_status
player +0x36 tone 0–6 tone
player +0x37 comptype 0–3 ×8 dissatis
player +0x3f–+0x42 professional, pressure, loyalty, star 0–15 the same
player +0x43 explodetype 0–15 ×4 explodetype
player +0x47–+0x49 motitype, condtype, futuretype 0–7, 0–7, 0–31 moti_type, cond_type, potential
player +0x4a–+0x4c pgrowtype, sgrowtype, mgrowtype 0–15 growth
player +0x4d–+0x51 awaytype, weaktype, tiredtype, wildtype, reverseaccuracy 0–7 ×3, 0–15 ×2 travel, injury_res, recovery, foul_avoid, weak_foot
player +0x52–+0x56 policy, envtype, intelligence, balltouch, dribblestyle 0–31, 0–7, 0–15, 0–2, 0–3 policy, adapt, intelligence, ball_touch, dribble_style
player +0x57–+0x5d nicknack_sleeve, _wristband, _gloves, _gkcap, _gkglove, _pants, _spike 0–2, 0–4, 0–4, 0–3, 0–5, 0–1, 0–14 sleeves, wristband, gloves, gk_cap, gk_gloves, gk_pants, boots
player +0x5e, +0x63, +0x64 _pstyle_native, core_flag, skill_flag 0–22 ×5, 0–7 style, flags, skills
player +0x66, +0x74 ai, abil 0–4 ×11, 0–100 ×64 tendency, ability
manager +0x18, +0x1c, +0x20, +0x22 rank, coachtype0, faceno, age 0–16, 0–7, –, 0–32 rank, job, face, age
manager +0x26, +0x2a appeartype, comptype 0–9 ×4, 0–3 ×5 appeartype, comptype
manager +0x2f–+0x33 motitype, plan_appoint, plan_game, plan_grow, prac_off – moti_type, select_policy, match_policy, training_policy, rest_policy
manager +0x34, +0x3d _policyset (a structure: policy, okrange ×4, goodrange ×4), _formation ×3 policy, policy_range, policy_best, formation
manager +0x40–+0x46 offensetype, possession, offensecenterside, offenseside, presspos, presshard, offsidehard 0–7 attacking … offside
manager +0x47, +0x4c, +0x54 _favoriteattackid, _mpraclist, _praclist 0–29 ×5, −1–139 ×2, ×4 attack_pattern, manager_drill, coach_drill
manager +0x64–+0x66 real_name_flag, model_pattern, abil 0–1, 0–1, 0–100 ×48 real_name, model_pattern, ability
scout +0x1c, +0x20, +0x24 rank, faceno, appeartype 0–16, –, 0–10 ×4 rank, face, appeartype
scout +0x29 abil 0–100 ×49 search, ability

The table's ranges are the developers' editor limits, not checks the game makes: the database stays inside them, often well inside (tone 0–3, policy 0–24). The same table names many other structures (PlPinfo, the team data, the club's finances, the schedule), so it is worth reading before decoding a new one.

python SRC/sles_disasm.py ISO/SLES_541.51 fields                # every structure, one block each
python SRC/sles_disasm.py ISO/SLES_541.51 fields explodetype ai  # rows by name

Personality and condition fields

Confirmed from the readers in SLES_541.51 (each reads the field at PlPinfo +0x198 + its record offset):

Field Reader What the value does
dissatis +0x37–+0x3e pwkDissatis_Money, _Player, _Staff, _Position, _Match, _Policy, _CompeEnd, _Facility (0x237420, 0x2376a8, 0x237af8, 0x237fbc, 0x238350, 0x238558, 0x23a930, 0x238a84) each scales that cause of dissatisfaction by 1.0, 1.1, 1.2 or 1.3 (tables such as 0x391150)
professionalism +0x3f plPinfo_InitPower (0x21af98) power (+0x24c, 0–1000) starts at a random value in a range from the table at 0x532a38: 800–900 for 0, rising to 980–1000 for 15
loyalty +0x41 pwkTeam_GetYouthPromoteConyear (0x2714cc) value / 2 picks the row for a promoted youth player's contract years
star +0x42 plPinfo_GameCheck, MonthEndCheck, YearEndCheck, ClubTitleCheck, UpDownKeymanCaptaion added to the rank, capped at 30, picks the size of each popularity change (plPinfo_ChangePop)
moti_type +0x47 plPinfo_CalcMotiYear (0x21bfe4) yearly motivation gain from the table at 0x390860: 3000, 4500, 1500, 4500, 1500, 1500, 3000, 4500
moti_type, tendency +0x67 pwkDissatis_GetAffintyType (0x2370d0) how two people get on: an 8 × 8 table of u16 at 0x390fd0, row = one person's +0x67, column = the other's +0x47 (a manager's comes from his PlMbase +0x2f). Values 0–4
travel +0x4d plPinfo_CalcMatchMovePowerDecline (0x2197dc) with the age, the power lost to travelling for a match (the effect is read from the function's name)
injury_res +0x4e plPinfo_CheckKega (0x21aa64) with the age, fatigue, power and motivation, the injury check
recovery +0x4f plPinfo_CalcGTired, CalcPracTired, CalcRecover picks a pair of multipliers from 0x532870: (1.1, 0.9), (1.1, 1.0), (1.0, 0.9), (1.0, 1.0), (1.0, 1.1), (0.9, 0.9), (0.9, 1.0), (0.7, 0.9)

The match engine converts some of these fields into its own player parameters (below). What the match then does with them isn't traced. +0x57–+0x5d are the kit style (below).

The match engine's player parameters

Before a match, GAMEPRG.REL 0x143908–0x143968 builds a 0x108-byte record for each of a team's 25 players (team +0x20 + 0x108 × slot). 0x1435a8 and the helpers it calls fill a byte array at record +0x37: parameter k is record +0x37 + k. 0x146480 reads parameter k and returns 0 for a value of 100 or more. A record field +n sits at PlPinfo +n+0x198, and ability a at PlPinfo +0xa + 6a (a halfword; the conversions add 15 to it).

Confirmed from the code below. Three tables of 16-byte rows {s32 parameter, s32 ability or −1, u32 add, s32 max} turn abilities into parameters (ability + add, then clamped to max), and the rest is written directly:

Parameters Source Address
13 ability 23 (stamina) + 15 0x1435c0
26–42, 46–57 abilities 0–25 + 15, in order, except: 32 and 33 both ability 6, 41 ability 22, 42 ability 14, 54 ability 25 table 0x28bd08, 0x142ee0
43, 44, 45 0.7 × ability 14 (placekick) + 0.3 × ability 5 (long pass) + 15 0x142f60
58 7 − injury_res (+0x4e) 0x142fd0
59 weak_foot (+0x51) / 2 0x142fdc
60 recovery (+0x4f) 0x142fe8
61, 67–70 abilities 26, 28–31 + 15 table 0x28bf38, 0x143038
62 (15 − foul_avoid (+0x50)) / 2 0x1430c0
63 professionalism (+0x3f) / 2 0x143100
64 pressure (+0x40) / 2 0x14310c
65, 66 ability 32 (concentration) / 14, ability 27 (nerve) / 14 0x143118
71 1 if professionalism ≥ 3 0x143138
72, 75, 76 abilities 53, 58, 57 + 15 table 0x28bf38
73 0.7 × ability 55 + 0.3 × ability 54 + 15 0x143148
74 0.7 × ability 56 + 0.3 × ability 54 + 15 0x143180
77 PlPinfo +0x294 (clamped to 25) 0x1431c4
78–89 0.7 × an aptitude + 0.3 × a system fit (ability 45 + the third argument; 0x1435a8 passes 4, the 4-4-2) + 15. The aptitudes, in parameter order: 33 GK, 35 CB, 34 SB, 36 WB, 37 DM, 39 OM, 38 SM, the larger of 40 WG and 41 FW, 40 WG, 42 centre, 43 left side, 44 right side table 0x28c048, 0x143228
90–111 99 for the play style held in PlPinfo +0x278 (compared with 1–22; presumably the style the player plays), 0 for the others 0x1433e8
112–127 skill bits 0–15 (plPinfo_IsSkill), 0 or 1 0x143430
128–138 the 11 tendency values (+0x66), a value of 5 or more becomes 4 0x143490
139 ball_touch (+0x55), 3 becomes 2 0x1434d8
140 dribble_style (+0x56), 4 or more becomes 2 0x1434f8
141–149 1 if ability 1 (dribble skill) is at least 0x50 0x143518
153–155, 159–162 the kit style: boots, gloves, gk_cap, wristband, gk_gloves, gk_pants, sleeves 0x143558

Parameters 150–152 and 156–158 are set to 0. Abilities 59–63 (the attack patterns) and 45–52 (the system fits, except through 78–89) are not copied. Neither are tone, loyalty, star, explodetype, moti_type, cond_type, potential, growth, travel, policy, adapt and intelligence; those are only used outside the match.

The team-combo bonuses (pwkPCombo_Add) reach the match through parameters 128–138.

python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408     # Beckham: every field by name
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x5ad0 400 --sles ISO/SLES_541.51

Where the match reads the parameters

Confirmed from the code (GAMEPRG.REL):

Address What it shows
0x97b28 copies the 163 parameters (record +0x37 on) to +0x4 of the match's per-player object. +0xa7 holds a modifier for each, cleared by 0x97af8 and changed by 0x97aa8
0x9bdb8 the getter the match uses: base (+0x4 + k) plus modifier (+0xa7 + k), plus situation bonuses for some parameters, clamped to 0–99. It has 240 calls, nearly all with a constant k
0xa84a0 at match start, copies a tuning table of 1,951 8-byte entries (u32 kind × 2²⁴ + index, then the value) from 0x270000 to the buffer at *0x249194. 0xa8578 returns entry n's value

The record's own getter 0x146480 (above) is only used by its 13 calls in 0x146530–0x146c00, where 0x146560 picks a player's best play style from parameters 0x5a + n. Every parameter from 26 to 140 except 61, 64, 113 and 139 is read through 0x9bdb8 (below). Parameter 139, the ball touch, has no reader.

python SRC/snr2.py xref ISO/DLL/GAMEPRG.REL 0x9bdb8           # the 240 calls
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x9bdb8 120 --sles ISO/SLES_541.51

Who reads each parameter

python SRC/snr2.py callers ISO/DLL/GAMEPRG.REL 0x9bdb8 fb lists the 240 calls with the parameter each passes (236 pass a constant) and the fb:: class whose code makes the call, or reaches it through other calls. The classes come from their run-time type information (SNR2_FORMAT.md). That a class reads a parameter is confirmed; what the class does is read from its name, and how much the value changes the outcome isn't traced. "Helper" is a PlayerAI function reached only through virtual calls. 109 calls have no fb:: class this way, and 45 no class at all.

Four shared helpers do much of the reading:

Helper What it is Parameters
0x98a30 rates a kick by its kind (the byte at +0x1a of its argument, kinds −1 to 41, jump table 0x26e280; see The kick kinds); behind every DecisionFactor*_NonInterceptionRate and _NonDFBlockRate class, the chance that a pass, cross or one-two isn't intercepted and a shot isn't blocked. Its sub-helpers 0x98f50–0x99280 each read one skill. Several kinds share one call (0x98e24), so callers credits it only with 30 5, 28 shot skill, 29 shot technique, 30 short pass, 31 long pass, 32/33 cross, 34 header, 37 tackle, 42 placekick, 46 saving, 59 weak foot; skills 116–118, 120, 123–125, 127
0x9bc38, 0x9bcf8 probably a duel: the first reads ball keeping and the second tackle, both with the two contact strength parameters 36 ball keeping, 37 tackle, 54 and 57 contact strength
0x99520 the player's value for play style n + 1: argument n (0–21) reads parameter 90 + n, −1 returns 0 (a switch, jump table 0x26e3a0) 90–111
0x99340, 0x99468 the aptitude for a position: 0x992c8 maps it to one of 78–89 78–89

The kick kinds

A kick request carries its kind as a signed byte at +0x1a. Two switches give each kind a meaning (confirmed from the code):

  • 0x58000 (kind, variant) returns an entry of the kind's variant list, 12-byte entries {char *name, ...}. The variant is byte +0x25 of the GAMEDATA.BIN record the request names at +0x1c (0x45f44–0x45f84, GAMEDATA_FORMAT.md). Most kinds list the foot and body surfaces (INSIDE, OUTSIDE, HEEL, SOLE, TOE, INSTEP, INFRONT, OUTFRONT, CHEST, HEAD_STRONG, HEAD_WEAK, SLIDING, CHIP), but some lists name the kind.
  • 0x98a30 rates the kick from the kicker's parameters, by kind + 1 through the jump table at 0x26e280.
Kind Variant list Rated by
−1, 0, 5, 10, 27, 34, 36, 38, 41 surfaces (10: UNDERHAND, OVERHAND, a goalkeeper's throw; 27: KICKOFF) short pass (30)
1, 2 surfaces short pass × the playmaker skill (127)
3, 4, 11, 35 surfaces (11: INSTEP, SOUTH_AMERICAN, a goalkeeper's kick) long pass (31)
6–9 surfaces (9: UNDERHAND, OVERHAND) cross: 32 and 33 blended 0.9/0.1, 0.4/0.6, 0.7/0.3, 0.4/0.6. Both hold the cross ability, so the blends differ only by the parameters' match modifiers
12 SINGLE_HAND, DOUBLE_HAND and their _PK forms: a catch saving (46) × the one-on-one keeper skill (117)
13 HAND, BODY, KICK and their _PK forms: a save the same
14–18 surfaces plus OVERHEAD, JUMPING_VOLLEY, HEEL_VOLLEY: shots shot skill (28) × the factors of the one-on-one finisher, acrobatic shot, one-touch shot and goal machine skills (116, 123–125), kept to 0–99; kind 14 averages it with short pass, kinds 15, 16 and 18 with shot technique (29), kind 17 uses it alone
19–21 surfaces tackle (37)
22, 23, 30, 31, 33 surfaces; 30, 31, 33: THROWIN always 99
32 LONGTHROW long pass × the long throw skill (118)
24–26 surfaces; 25: LOST; 26: COLLISION parameter 5 (source not traced)
28, 37, 39, 40 28: KICKOFF; 40: four foot surfaces only placekick (42)
29 surfaces plus WEAK shot skill (28) alone

After the switch, unless the kind has flag 0x40 (the throw-ins, below), the rating is scaled by how the ball is struck, read from the same GAMEDATA.BIN record (+0xc bits 16–18: 0 the right foot, 1 the left, 2 the head, 3 the chest): parts 2 and up by min(header (34) + 20, 100) / 100, so a chest touch counts as a header too, and a kick with the player's weak foot by 0.14 × parameter 59 (weak_foot / 2, 0–7). Confirmed: 0x97be0 maps the player's leg value through 0x26e060 (0, 1, −1, −1) to the weak foot's part, so a left-footer (leg 0) is weak with part 0 and a right-footer with part 1, and a two-footed player (leg 2, 3) is never scaled; 0x97c00 maps it through 0x26e070 (1, 0, 2, 2) to the strong foot. The multiplications use the R5900's three-operand mult (0x98ed0, 0x98f08).

Two kinds are stored as constants (confirmed): kind 38 by the corner and free-kick plays' code (0x6f260, reached from PlayCKKick and PlayFKKick), and kind 25 by fb::Player's code (0x8083c, 0x8b78c). Kinds 4 and 10 are stored at 0x6001c–0x601dc and 0x6af40–0x6b170, in code reached from the pass, clear and cross intentions. Most kinds are copied from a candidate record (+0x18, a halfword) that the intentions choose from.

Where each kind is made

Confirmed. A candidate gets its kind from the first stack argument of one of six builder functions, and small wrapper functions call each builder with a constant kind. The wrappers' kinds and their callers:

Builder Kind of kick Kinds passed Called from
0x58f00, 0x5b148 short pass 27, 30, 34, 36, 38 the restart wrappers below
0x60738, 0x63350 through pass 1 (0x630e8), 2 (0x647f8), 33 IntentionFrontPass, IntentionKillerPass
0x5c4f8, 0x5eec8 long pass 3 (0x5ece8), 11 (0x6b310, 0x6b440), 35, 37, 39 IntentionFeedPass, IntentionKillerPass, IntentionSideChangePass; 11 from PlayEffector
0x650d0, 0x661a8 cross 6, 7 (0x66030, 0x66f80), 8, 9 (0x67448, 0x67920), 32 6 and 7 IntentionPinPointCross, 8 and 9 IntentionLinerCross
0x67c20 shot 14, 16 (0x68c10, 0x69090), 15, 17 (0x68e50, 0x692d0), 18 (0x69510), 40 14 and 16 IntentionTargetShoot, 15 and 17 IntentionPowerShoot, 18 IntentionLoopShoot
0x6b580 keeper 12, 13 (0x6c548, 0x6c3f0) the intentions' shared code

IntentionTargetShoot picks 14 or 16 by a weighted random draw (0x97910 against 0.5, 0xed394–0xed578), so the two are the same shot in two forms; the other pairs are probably chosen the same way.

The restarts (confirmed):

Play Field Kinds
PlayCKKick +0x98, set by 0x105700 from the play's code at +0x58 36 for codes 0xd01 and 0xd03–0xd06, else 37. 0x1055c0 sends 36 to the short-pass wrapper (0x1062e8 → 0x6de30) and 37 to the long-pass wrapper (0x6ec30), aimed with the placekick mixes (0x8e818)
PlayFKKick +0x98, set by 0x105df8 39 for code 0xe7f or 0xf00, 38 for 0xf01–0xf05 and the rest, and 40 when +0x5c (masked with 0xfffeffff) is 0xb or more. 40 goes to the shot wrapper 0x6f758
PlayTIThrow – 0x107e90 calls the kind 30 wrappers (0x6c7c8, 0x6c900) and the kind 32 ones (0x6cbb0, 0x6cce8). Kind 33's wrapper (0x6d0d0) is reached only from the intentions

So 36 is a short corner and 37 a corner cross; 38 a free kick passed short, 39 a free kick crossed or played long, 40 a shot from a free kick; 30 a short throw-in, 32 a long throw and 33 a throw-in played through.

Kinds 27, 34 and 35 also have wrappers (0x6d380; 0x6d5e8/0x6d738; 0x6d9e8/0x6db18), but these are reached only through two switches that re-plan a request by its kind (0x4bcb0, table 0x267e10, kinds −1 to 41; and 0x53954–0x53a1c), so the request already has its kind. 28, 29 and 31 have no wrapper: only a case in 0x4bcb0 (0x52040, 0x52238, 0x50390). Where these six kinds are first set wasn't found. No constant 27, 28, 29, 31, 34 or 35 is stored at +0x1a or +0x18, and the kick-off, goal kick and penalty plays (PlayKOKick, PlayGKKick, PlayPKKick) only send the player commands (0x113d98–0x113dc0, 0x113cb0/0x113d70, 0x114320).

python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x58000 12      # the variant switch (table 0x268010)
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x98a30 260     # the rating switch and the scaling

The kind table

Confirmed: GAMEPRG.REL 0x29f680 holds 41 entries of 12 bytes, {s32 kind, f32, u32 flags}, one per kind. The table is zero in the file. The overlay's static initialiser 0x704f0 fills it in kind order: it builds kinds 0–4, 6–10, 14–18 and 30–40 in code, and copies kinds 5, 11–13 and 19–29 from preset records at 0x2696cc–0x269774. The float is either 1/(0.5994 × mg::video_vsync_multiplier) or a much smaller value (0.0417 at a multiplier of 1); what it scales wasn't traced. About 150 places in the match code read the flags with the kind as the index.

Kind Flags Kind Flags Kind Flags Kind Flags
0 0x3001 11 0x2100 22 0x5200 33 0x00c4
1 0x3004 12 0x3d20 23 0x5200 34 0x2081
2 0x2104 13 0x3c20 24 0x3200 35 0x2182
3 0x2102 14 0x3810 25 0x1e00 36 0x0081
4 0x2102 15 0x3810 26 0x3e00 37 0x018a
5 0x3920 16 0x3910 27 0x2081 38 0x2081
6 0x2108 17 0x3810 28 0x2890 39 0x218a
7 0x2008 18 0x3910 29 0x2890 40 0x2890
8 0x2108 19 0x6e20 30 0x00c1
9 0x2008 20 0x6e20 31 0x00c1
10 0x2001 21 0x6e20 32 0x01ca

What the bits mean. Where a class is named, it reads that bit (confirmed). Each meaning comes from the kinds that carry the bit, their variant lists and their ratings (empirical):

Bit Kinds Meaning
0x1 0, 10, 27, 30, 31, 34, 36, 38 a short pass (rated by short pass, or a throw)
0x2 3, 4, 32, 35, 37, 39 a long ball (rated by long pass, the long throw or placekick)
0x4 1, 2, 33 a through pass (rated by short pass × the playmaker skill)
0x8 6–9, 32, 37, 39 a cross: read by DecisionFactorCross_PositionNearnessToEndLine, _NearGoalTeammates, IntentionPinPointCross and IntentionLinerCross
0x10 14–18, 28, 29, 40 a shot (rated by shot skill or placekick)
0x20 5, 12, 13, 19–21 – (the keeper's catch and save, the tackles, kind 5)
0x40 30–33 a throw-in (the THROWIN and LONGTHROW variant lists); turns off the scaling above
0x80 27–40 a restart: kick-off, throw-ins, set pieces
0x100 2–6, 8, 11, 12, 16, 18, 32, 35, 37, 39 probably a ball in the air: of each pair of otherwise equal kinds, only one has it (crosses 6 and 8, not 7 and 9; shots 16 and 18, not 14, 15 and 17; through pass 2, not 1)
0x4000 19–23 the tackles and the two kinds rated 99
0x200, 0x400, 0x800, 0x1000, 0x2000 many not worked out

fb::RoleCombination (0x128de8) treats a kick with any of bits 0x1–0x10 the same way: a pass, a cross or a shot (confirmed). fb::PlayEffector's handlers send a different event to 0x94018 for kinds 36 and 37 (0x68, 0x69) and for each throw-in kind 30–33 (0x6a–0x6d) (confirmed). What those events start wasn't traced.

So the kinds are, combining the table above with the ratings:

Kind What it is How sure
0 short pass flags and rating
1, 2 through pass, on the ground and in the air; made by the front and killer pass intentions flags, rating and code
3, 4 long pass in the air. Their flags are the same; 3 is made by the feed, killer and side-change pass intentions, 4 is the kind the pass, clear and cross intentions' shared code stores flags, rating and code
5 – (rated by short pass; flags like a keeper's) –
6 / 7 pinpoint cross, in the air / low flags and code
8 / 9 liner cross, in the air / low flags and code
10 goalkeeper's throw variant list and flags
11 goalkeeper's kick, in the air (made by PlayEffector) variant list (INSTEP, SOUTH_AMERICAN), flags and code
12, 13 goalkeeper's catch, save variant lists
14, 16 target (placed) shot; 16 in the air flags, rating and code
15, 17 power shot; neither has the air bit flags and code
18 loop (chipped) shot, in the air flags and code
19–21 tackles rating and flags
22, 23 always rated 99; flags like the tackles –
24–26 25 the ball lost, 26 a collision variant lists
27 kick-off pass variant list and flags
28, 29 shots at a restart; 28 rated by placekick (its variant list is KICKOFF), 29 by shot skill. Probably a penalty and a shot from a kick-off or drop ball, not confirmed flags and rating
30 / 31 / 32 / 33 throw-ins: short (made by PlayTIThrow) / short, never made by a wrapper / long (PlayTIThrow, rated by the long throw skill) / through flags, rating and code
34, 35 short and long pass at a restart: by elimination probably the goal kick, not confirmed flags and rating
36, 37 corner: short pass, cross (PlayCKKick) code
38, 39, 40 free kick: short pass, cross or long ball, shot (PlayFKKick) code

Still open: what tells 3 from 4 and the members of each in-play pair apart beyond the random draw, the three tackles, and where kinds 27–29, 31, 34 and 35 are first set (kick-off, penalty, goal kick).

python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x704f0 700     # builds the kind table
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x128de8 40     # RoleCombination: bits 0x1-0x10

The other readers:

Parameters Field Read by
26, 27, 35 dribble pace, dribble skill, trap IntentionRWTB, IntentionKeep, PlayADribble, PlayRWTB; 27 also by helpers 0x91438, 0x9b560, 0x9b768
38, 39, 41, 52, 53 intercept, ball winning, agility, jump, agility intercept by match::PlayerManipulator; ball winning and agility (41) by helper 0x9b650; jump by helper 0x9b7e0; agility (53) by many roles (RoleBallCarrierMarker, RoleFreeRunCommon, …)
40 marking RoleMarker, RoleBallCarrierMarker, helper 0x9b650, the set-piece assignment (CSetPlayAssign)
42, 43–45 placekick, the placekick mixes penalties (PlayPKKick) and free kicks and corners (PlayFKKick, PlayCKKick; 0x8e818 picks 44 or 45)
46–49, 115, 117, 122 saving, catching, aerial ability, rushing out; skills penalty stopper, one-on-one keeper, reflex saves saving by PlayPKKeeper and the kick helper; catching and aerial ability by PlayGoalKeep; rushing out and one-on-one keeper by RoleBallCarrier; penalty stopper by PlayPKKeeper; reflex saves by match::MatchManager (0x190e40)
50, 51, 55, 56 pace, acceleration, stamina, kick strength pace by the pass, cross and dribble rate classes; acceleration by RoleBallCarrier; stamina by fb::Player's 0x7a898; kick strength by the pass, clear and shot intentions and PlayFKKick
58, 60 7 − injury_res, recovery match::MatchManager (0x9b220, 0x9b360; 0x19db10)
62 (15 − foul_avoid) / 2 PlayBallCarrierMark (fouls when challenging) and 0x9b020
63 professionalism / 2 20 reads, 19 of them in the out-of-play roles' code (0x125980–0x128450, after RoleOutOfPlay)
65, 66 concentration / 14, nerve / 14 the kick rate helpers; nerve also PlayPKKick
67, 68 attack minded, defence minded 12 and 15 reads: the positioning roles (RoleSAAttacker, RoleSASupport, RoleFreeRunner, RoleMarker, RoleMarkAssist, …), PlayEffector and the phases (PhaseInPlay, PhaseSP, PhaseThrowIn)
69, 70, 75, 76 supportiveness, vision, pressing, line DF RoleSupporter and RoleSASupport; RoleBallCarrier; RoleMarkAssist; RoleSpaceMarker
71 professionalism ≥ 3 match::MatchManager (0x169670)
72–74 counterattack, the possession mixes fb::Combination's code (0xc16b4), the team's combination plays; 73 also RoleSASupport
77 PlPinfo +0x294 collision code (0x31998)
93, 97, 99, 100, 106–108 styles Dash out, Shadow striker, Dynamo, Man marker, Sweeper, Defensive Sweeper, Stopper besides 0x99520: 93, 97 and 99 by 0x99720 and 0x10df80 (roles); Man marker, the sweepers and Stopper by RoleBallCarrierMarker
112, 114, 118–121, 126 skills covering, penalty taker, long throw, super sub, through balls, positioning, poacher covering by RoleBallCarrierMarker; penalty taker by PlayPKKick; long throw by the throw-in taker RoleTIThrower; super sub by match::StatisticTask (0x190c00); through balls by RoleFKKicker; positioning by RoleMarker and CSetPlayAssign; poacher by helper 0x9a310
128–138 the tendencies see below
140 dribble_style helper 0x9b540 (see below)

These fit the names in pbdata.py and the skill and style names closely (long throw read by the throw-in taker, penalty taker by the penalty kick, super sub by the statistics task), which is a check on those names. Also read, from outside the table above: parameter 16 (0x193ce8) and 24 (0x9c518), whose sources aren't traced, and 164 (0xc1d00, fb::Combination's code), past the 163 copied parameters: its base byte +0xa8 is parameter 1's modifier.

Never read through 0x9bdb8 (confirmed, no call passes them): 61 leadership, 64 pressure / 2, 113 skill offside line, 139 ball_touch, 141–149 (dribble skill ≥ 0x50), and the kit style and zeros 150–162. The kit style reaches the player model another way (Kit style). Unless match code reads them straight from the player object, which wasn't searched for, leadership, pressure, the offside-line skill and the ball touch do nothing in a match.

The tendencies

The match AI's decision classes read the 11 values at +0x66. Each class gives its name through its run-time type information (the fb:: classes in GAMEPRG.REL, found from the vtable of each reading function). Most classes turn the level into one of five entries of the tuning table, which is then added to that kind of play's score, or multiplies it. Level 2 is neutral in every set that is added. Confirmed from the code:

Item Offset Parameter Read by Tuning entries for levels 0–4
0 dribble +0x66 128 IntentionRWTB (0xe4320, 0xe46b0), IntentionADribble (0xe7390), IntentionKeep (0xe6050), PlayBallKeep (0xf95c8), and 0xe4948, which the pass, cross and shoot intentions also call 0x52d: −0.15, −0.05, 0, +0.05, +0.15 (RWTB); 0x537: −0.3, −0.1, 0, +0.05, +0.15 (ADribble); 0x532: × 0.75, 0.9, 1.0, 3.0, 10.0 (0xe4948); 0x53c: 20, 5, 2, 0.5, 0.01 (BallKeep)
1 shoot +0x67 129 0xec508, called 10 times from the shoot intentions' code 0x546: × 0.6, 0.9, 1.0, 1.1, 1.35
2 carry +0x68 130 IntentionRWTB, IntentionKeep, IntentionADribble; and 0x1176d8 (through 0x117480), inside a helper (0x116ee8) that most roles call, on or off the ball (below) 0x55a: 0, 0, 0, +0.05, +0.15; 0x555: × 0.2, 0.6, 1.0, 1.5, 3.0 (0x1176d8)
3 feed pass +0x69 131 IntentionFeedPass (0xde6e0) 0x519: −0.1, −0.05, 0, +0.05, +0.15
4 side change +0x6a 132 IntentionSideChangePass (0xe08b0) 0x51e: −1.0, −0.5, 0, +0.05, +0.15
5 back pass +0x6b 133 IntentionBackPass (0xe2688) 0x523: 0, 0, 0, +0.05, +0.1
6 killer pass +0x6c 134 IntentionKillerPass (0xdc1d8) and IntentionLinerCross (0xeafc0) 0x528: 0, 0, 0, +0.05, +0.15
7 one-two +0x6d 135 IntentionOneTwo (0xe8300) 0x542–0x545: 0, 0, +0.05, +0.1 for levels 1–4 (level 0's entry isn't traced)
8 clear +0x6e 136 IntentionClear (0xf0448, and 0xf0ab0 from it) 0x550: 0, 0, 0, +0.05, +0.15; 0x54b: × 1.0, 1.0, 1.0, 1.2, 1.5
9 press +0x6f 137 PlayBallCarrierMark (0xfcb48, through 0x9a418 and 0x9a4c8) no tuning entries. The level is capped by a team value (match data +0x9764: 1 caps it at 0, 2 at 1, anything else at 4) and picks from the floats at 0x26e400 (rows 80, 70, 60, 50, 50 / 60, 50, 45, 30, 30 / 30 …) and 0x26e440 (5, 5, 10, 15, 20)
10 goalkeeping +0x70 138 PlayGoalKeep (0xff080, through 0x1002b0 and 0x100c28) 0x594–0x59c, every other entry: −0.3, −0.2, −0.1, +0.1, +0.3

The item names are the ones pbdata.py uses. They come from the class names, not from the game's text: no screen in the game shows the tendencies (Nexxus's report). "RWTB" is probably "run with the ball". The classes for dribble, shoot, clear, press and goalkeeping act for one player, the one on the ball or marking it. The pass intentions (feed, side change, back, killer and the liner cross) and the one-two read the passer's value: each takes the intention's own player id (intention +4 → +4 → +0), turns it into a team slot with 0x73df8 on match data +0x8e84, and reads that slot's player (GAMEPRG.REL 0xdeed8, 0xe110c, 0xe2ec8, 0xddf40, 0xebd84, 0xe8554).

Carry in the roles (confirmed). The helper 0x116ee8 is called by nearly every role class (RoleFreeRunCommon, RoleBallCarrierMarker, RoleSupporter, RoleMarker, RoleMarkAssist, RoleSpaceMarker, RoleSpaceMarkCover, RoleGoalKeeper, RoleSAAttacker, RoleSASupport and more; python SRC/snr2.py callers ISO/DLL/GAMEPRG.REL 0x116ee8). Through 0x117480 it reaches 0x1176d8, which reads the carry value of the role's own player: it takes the id at role +4 → +0 (the same chain as the pass intentions, one level shorter), turns it into a team slot with 0x73df8 and reads that slot's player (0x117800– 0x1178a8). The level picks a multiplier (0x555–0x559: × 0.2, 0.6, 1.0, 1.5, 3.0). The player's current play (found by 0xf19d0 from role +0x1b60) has three scores for its current step (play +8, +0x18, +0x28, indexed by +0x38). Each is multiplied by the factor and compared with a threshold, a set of three picked by a match value (+0x1738, 0–4, the team's situation, below) from tuning entries 0x78b–0x799 (jump table 0x27a690; any other value uses the first set). The play is kept only when all three pass (0x117b8c–0x117bd8). So a player with a higher carry value keeps running with a plan once he has one; a low one drops it sooner. In one branch the factor is raised to at least 1.0 first (max.s at 0x117b44).

Situation Thresholds
0 0.3, 0.4, 0.8
1 0.3, 0.55, 0.6
2 0.2, 0.4, 0.3
3 0.1, 0.25, 0.15
4 0.1, 0.2, 0.9

The team's situation (confirmed, GAMEPRG.REL). The value at +0x1738 belongs to the team AI object that the role reaches through +0xc (the one that also holds the current phase at +0x1708). Each update asks the phase for a situation (its virtual method at vtable +0x50) and stores it with 0xab100:

Phase Situation
Kick-off, goal kick, out of play 0
Attack (PhaseAttack) 1
Penalty kick 4 for the side 0xa9e20 picks, 3 for the other
Corner, free kick, other set plays 3 or 4 by the set play's step (jump table 0x278840), otherwise as in play
In play, build-up, goal, throw-in worked out from the ball (0xf5608, below)

In open play 0xf5608 compares the line of play (match data +0x1065c, described with the press floats below) with two points between the team's own goal line (y −527.81, turned round for the side) and the opponents' defensive line (match data +0x10400, the other side's entry). The first point is a fraction f of the way there, the second halfway from that point to the opponents' line. Behind the first point the situation is 0, before the second 1, and from the second on 2. Two checks then override it: 3 when 0xf5728 is true, 4 when 0xf5c18 is; what they test isn't traced. So 0 to 2 are the ball in your own part of the pitch, in the middle, and near the opponents' back line.

f is the team's +0x179c, set at each kick-off (0xaafd0, 0xaaff0) from 0xa9e58: 0.3 + v / 15 for a value v of 0–6, so 0.3 to 0.7. v (0xbc538) is a tactics value of the current formation (word +0x10 of the record 0x14af00 returns; which of the 7 sliders that is isn't traced), plus a step for the match situation: +1 when behind late and −1 when ahead late (0x2741a0; the mode 0–4 from the time and score, as for the match policy), or +3 and −3 when 0x9ce78 is true. It is clamped to 0–6.

A move into or out of situation 3 or 4 is held back: when match data +0x104d4 is set, 0xab100 only switches after the new value has come 5 updates running (counter +0x1740, pending value +0x173c).

What the press floats measure (confirmed, GAMEPRG.REL). Match positions are in tenths of a metre (BPB_FORMAT.md), so the table values are distances:

  • 0x9a418 (called four times by PlayBallCarrierMark, 0xfd498– 0xfd5a0) returns row r of 0x26e400 at the press level: row 0 8, 7, 6, 5, 5 m, row 1 6, 5, 4.5, 3, 3 m, row 2 3 m at every level. The result replaces the second of four distances (15, 10, 8 and 4 m by default, 0x278a4c) that the marker works with, so it is how far from the ball carrier he takes up his position. Row 0 is used when bit 0 of the play's +0x80 is set. Otherwise the row follows a distance along the pitch: the line of play (below) minus a side's defensive line (the side in the player id 0x86298 returns), both turned round for that side. From 30 m (tuning 0x5c9) it is row 1, up to 10 m (0x5ca) row 2, and in between a blend of the two. Then 1.5 m (tuning 0x5cb) is taken off when an angle worked out from one goal (0xa61d8, picked by the side; the goal centre at y ±527.81) is over 90 degrees (0xfd648–0xfd68c).
  • 0x9a4c8 (once, 0xfcd68) returns 0.5, 0.5, 1, 1.5 or 2 m from 0x26e440, or 0 when the side's press byte is 1. The value is squared and compared with a squared distance, worked out in VU0 code that the disassembler can't decode (0xfcdbc–0xfcf18); inside it, the mark sets a flag (0xfcf60). Probably the radius within which the marker counts as on the ball carrier.

The two positions both rows use (confirmed):

  • The line of play, match data +0x1065c, is a y position that 0x3e038 keeps up to date. While a player has the ball (match data +0xc) it follows his y, while a pass is on its way it follows the receiver's (+0x14), and while nobody has it, the ball's (0x41da8). It runs up to 5 m ahead of or behind that player by a factor from 0x41ca8, and never past the goal line (y ±527.81).
  • The defensive lines, match data +0x10400 + side × 4, are set by 0x37540 from the players sorted by y: for each side, the y of its second player counted from its own goal, the goalkeeper usually being the first. That is the side's offside line. +0x10408 and +0x1040c hold each side's front player in the same way. 0x3e4a8 reads the first pair and 0x3e4c8 the second.

So a higher press level means standing closer to the ball carrier (rows 0 and 1 shrink from 8 or 6 m at level 0 to 5 or 3 m at levels 3–4) and a wider closing-in radius. With the press cap at 2 (below), every level from 1 up gets level 1's values.

The press cap stays at 2. The team value is byte 0 of a 4-byte record per side at match data +0x8e84 + 0x8e0 + side × 4 (+0x9764, +0x9768). Its constructor (0x74320, called at 0x7399c) sets bytes 0 and 1 to 2 (confirmed). 0x74380 would fill byte 0 from match data +0x976d (always 2, GAMEPRG.REL 0x26f0), but only 0x73c10 calls it, from 0x161438, and in a career match it doesn't run: with 0x74380 patched to load 4, both records still read 02 02 07 00 over PINE in a German league match (2026-10-31). No other code writes byte 0. Both press readers clamp the player's level with the EE's pmaxw and pminw (0x9a48c, 0x9a53c) to 0–cap, the cap set to 4 and then, in the delay slot, to 1 for a 2 (movz at 0x9a474 and 0x9a52c). So in every career match a press tendency of 2, 3 or 4 acts as 1: only 0 against anything higher makes a difference, which in the second table (0x26e440, 5 and 5) is none at all. Read from a mid-match save state (England v England) too: both sides' records were 02 02 07 00. Byte 1 is set from a menu during the match (0x1e4afc, values 4, 3, 2, 1 from 0x29ab00) and isn't traced further. --mod press-tendency turns both movz into nop (LMASTER_MOD.md).

Empirical, over all 27,950 players: every item uses 0–4, mostly 1–3, and items 0–9 each have 2,000–3,600 players at 4. Averaged by main position, the values fit the names. Goalkeepers average about 0.3 for dribble, shoot, killer pass and one-two, and about 2 for goalkeeping. Other players average about 0.45 for goalkeeping. Forwards (cells 10–12) average about 1 for clear and press, and defenders (cells 1–3) about 1.5 for shoot and killer pass. PBDATA_JP.PAC has some values of 5 and 6, which the match reads as 4.

dribble_style (parameter 140) has one reader, 0x9b540, called from 0x8afdc. The value goes to 0x13d710/0x13d780, which pick one of four rows in the tables at 0x285888, 0x285a08 and 0x285ab8. These hold motion numbers (967–1026 in the first table): the four rows share their first 12 entries and differ in the dribbling ones. So the style chooses the player's set of dribble animations. Which style looks like what hasn't been checked in game.

python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408     # tendency, item 0 first
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0xdef30 40 --sles ISO/SLES_541.51   # feed pass: entries 0x519-0x51d
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x9a418 60 --sles ISO/SLES_541.51   # press

Abilities and the detail screen

The 64 player abilities are finer-grained than anything the game shows. The detail screen's 14 bars and the 6-axis "Evaluation" hexagon are worked out from them.

Groups (confirmed, plPinfo_Abil2PSM 0x216f10). Abilities 0–18 return 1, 19–25 return 0, and 26–63 return 2. plPinfo_InitAbil (0x21b470) uses the group to pick which growth byte (+0x4a, +0x4b, +0x4c) scales an ability's random start offset. From the bars below, 0–18 are skills, 19–25 physical and 26–63 mental, which fits the name (P/S/M).

Bars (confirmed, WP::CDetailManager::ConvertPlayer_Bar 0x285380). Each bar is the integer average of the listed abilities' levels, and it is drawn as (value + 10) / 99 of full width. The labels are detail messages 0x2774+. The last 7 bars depend on whether the player's main position (PlPinfo +4) is 0, the goalkeeper:

Bar Abilities Field bar Abilities GK bar Abilities
SPEED 0, 20, 22 DRIBB 0, 1 SAVIN 15
PHYSI 24, 25 SHOT 2, 3, 24 HANDL 16
STAMI 23 PASS 4, 5, 6 CROSS 17
MENTA 26, 27 FK 14 GO FW 18
SUPPO 30, 31 HEAD 7, 25, 21 DISTR 4, 5, 24
SYSTE 0–7 INTER 11 AGILI 22
TACTI 0–10 MARK 13 JUMP 21

Only abilities 0–32 feed the bars. Where a bar has a single source, it names that ability: 11 intercept, 13 marking, 14 free kick, 15 saving, 16 handling, 17 crosses, 18 going out, 21 jumping, 22 agility, 23 stamina. The labels SYSTE and TACTI (messages 200:10105/10106, "SYS" and "TAC" in Japanese) suggest the systems and tactics (45–52 and 53–63, see Ability names), but the code averages abilities 0–7 and 0–10. A check with well-known players fits (database values, before the random start offset): Terry has MARK 98, INTER 94 and HEAD 92, Pirlo has PASS 93 and FK 94, Henry has SPEED 94 and DRIBB 95, and Cech and Buffon have SAVIN 98.

Hexagon (confirmed computation, plPinfo_CalcHexagon 0x217ce0, plPinfo_CalcHexAbil 0x217850). PLRESOURCECOMMON.PAC entry 2, table 0 has 8 bytes per ability. Each 4-byte half holds two {u8 hexagon, u8 weight} pairs: +4 is the field-player variant, and +0 is the goalkeeper variant. Hexagon h is the weighted average of every ability with a pair naming h. CalcHexagonNG fills all six from the field variant, then CalcHexagon recomputes 0 and 1 with the goalkeeper variant for goalkeepers. The screen's labels are messages 670–675 of category 1 (Attacking, Physical, Teamwork, Defence, Attitude, Skills, clockwise from the top). Which index goes to which label is empirical. Hexagons 0 and 3 are told apart by Virtua Pro Football's categories (Ability names): 0's main abilities are its "Attack" page, and 3's are its "Skill" page.

Hexagon Main abilities (weight 80) Label
0 2, 4–7, 14 (shot skill, passes, cross, header, placekick) Attacking
1 10–13 (15–18 for goalkeepers) Defence (defenders score ~90)
2 33, 42–44, 53–58 (59–63 at weight 16) Teamwork
3 1, 3, 8, 9 (dribble skill, shot technique, trap, ball keeping) Skills
4 0, 19–25 Physical
5 26–32 Attitude

python SRC/pbdata.py show prints both the bars and the hexagon, and csv adds the bars as columns.

Ability names

The developers' label table (above) names all 64 abilities. This section is the independent check that came first: Virtua Pro Football's English names and values. pbdata.py uses VPF's English name where VPF has the same parameter.

Virtua Pro Football (VPF) runs on the same engine, and its Player Edit screen names its parameters on 8 pages. Nexxus supplied screenshots of that screen for four players in this database: Maik Taylor (player 0, a goalkeeper), Wayne Rooney (258, a forward), David Beckham (4408, a right midfielder), and Lucio (2102, a defender; Physical page only). VPF's pages follow this game's ability order, and most values match within a point or two. VPF has parameters this game lacks (cross technique, 1 on 1 response, balance and some tactical ones), so the lists are aligned by value, not by position.

The values column gives this game / VPF, for Taylor (T), Rooney (R) and Beckham (B). The evidence column says where each name comes from: code (the bars, the position grid or match growth above), VPF + data (VPF's name, and the players' values or a trend across the whole database fit), or VPF (VPF's name, with the values as shown).

Ability Name Values T, R, B Evidence
0 dribble pace 38/31, 88/92, 76/72 VPF + data (SPEED and DRIBB bars)
1 dribble skill 38/38, 90/90, 78/77 VPF + data (DRIBB bar)
2 shot skill 38/35, 86/91, 84/83 VPF + data (SHOT bar)
3 shot technique 38/35, 88/87, 82/81 VPF + data (SHOT bar). VPF's "Skill" page
4 short pass 54/54, 84/83, 86/85 VPF + data (PASS bar)
5 long pass 64/63, 82/81, 98/99 VPF + data (PASS bar)
6 cross 40/39, 80/80, 99/99 VPF + data (PASS bar)
7 header 38/35, 82/82, 70/70 VPF + data (HEAD bar)
8 trap 60/55, 88/88, 84/80 VPF + data. VPF's "Skill" page
9 ball keeping 38/36, 82/82, 74/73 VPF + data. VPF's "Skill" page
10 tackle 48/48, 66/65, 70/70 VPF + data
11 intercept 38/38, 46/46, 70/69 code (INTER bar)
12 ball winning 38/37, 48/48, 72/72 VPF + data
13 marking 38/35, 50/49, 76/76 code (MARK bar)
14 placekick 52/51, 80/80, 99/99 code (FK bar)
15–18 saving, catching, aerial ability, rushing out 78/77, 78/77, 82/81, 78/78 for T code (goalkeeper bars SAVIN, HANDL, CROSS, GO FW)
19 pace 74/70, 86/94, 80/76; Lucio 80/82 VPF + data. Lucio settles the pair: VPF gives him pace 82 and acceleration 71
20 acceleration 74/69, 92/94, 78/74; Lucio 76/71 VPF + data
21–23 jump, agility, stamina 76/71, 88/83, 62/57 for T; 82/82, 88/88, 84/84 for Lucio code (JUMP, AGILI, STAMI bars)
24 kick strength 74/73, 88/88, 86/86 VPF + data (SHOT and DISTR bars)
25 contact strength 80/80, 90/91, 80/80 VPF + data (PHYSI and HEAD bars)
26 leadership 40/40, 70/70, 90/90 VPF + data (MENTA bar)
27 nerve 40/51, 94/45, 94/66 developer label 度胸. VPF's "consistency" sits in this slot, but the values don't fit (MENTA bar)
28 attack minded 52/51, 96/96, 94/93 VPF + data: averages 45 for goalkeepers, 80 for forwards
29 defence minded 84/84, 54/70, 72/72 VPF + data: averages 80 for goalkeepers, 45 for forwards
30 supportiveness 40/40, 88/79, 90/89 VPF + data (SUPPO bar)
31 vision 60/59, 76/75, 99/99 VPF + data (SUPPO bar)
32 concentration T 78, R 72, B 86 developer label 集中力; no VPF counterpart (Attitude hexagon)
33–44 position aptitudes see below code (Positions)
45–52 fit with systems 3-4-3, 3-5-2, 3-6-1, 4-3-3, 4-4-2, 4-5-1, 5-3-2, 5-4-1 – code for "system k" (match growth); the order of the systems is empirical: the formation names (messages 801:0–7, 1:530–537) and their descriptions (700:270–293) are listed in this order
53 counterattack 82/82, 88/87, 82/81 VPF + data
54 possession T 42, R 88, B 46 developer label ポゼッション理解度; no VPF counterpart (Teamwork hexagon)
55 attacks down wings 80/79, 62/78, 40/39 VPF + data (Rooney is the one outlier)
56 attacks through middle 38/34, 86/86, 56/55 VPF + data
57 line DF 64/64, 58/57, 64/63 VPF + data. Beckham settles the pair (line DF 63, pressing 59)
58 pressing 68/68, 58/57, 60/59 VPF + data
59–63 attack patterns A–E – developer labels 攻撃パターンA–E理解度. The game only ever handles the five as a set (below), so which letter is which makes no difference

The attack patterns (confirmed). Nothing in the game reads one of abilities 59–63 on its own. Every use takes all five:

Where What it does with them
PLRESOURCECOMMON.PAC 2.0, the hexagon weights all five the same: Teamwork (2) weight 16 and Attacking (0) weight 20, for field players and goalkeepers
GP::PlTeamData2HEX_DATA (0x27b458, at 0x27b70c) adds their average to a player's rating in the GP::HEX_DATA hexagon window, with the averages of 42–44 and 53–58
TRAINING_LIST.TBB every practice grows the five by the same amount (+1, +4, +8 or +15)
--mod salesman-items (flag 336) raises all five in the match's copy of your squad (above)

The match doesn't copy them into its player parameters (The match engine's player parameters). A scan of the executable and every overlay for halfword loads at their PlPinfo offsets (+0x16c–+0x184), and for offsets or indices built from 59, found no other reader (the hits are virtual-call slots at the same offsets in other objects, and a per-player stride of 0x16c). So the letters are just names: which one is which doesn't change anything in the retail game. The developers may have meant them for the club's five pattern slots (PlTeamData +0x489f), but no code ties a slot to one of them.

VPF's aptitude page names the position cells. Rooney and Beckham agree on 39 = OM (86/85, 72/70), 42 = centre (94/93, 88/87), 43 = left (82/82, 54/53) and 44 = right (54/53, 98/97), and Beckham has 38 = SM (96/95). Rooney also fits 34 = SB (40/39), 35 = CB (44/44), 36 = WB (46/46) and 41 = FW (96/95). Taylor has 33 = GK (82/82). The defensive cells don't fit Beckham, whose VPF values there are a flat 48 (56, 70, 76 and 92 here for SB, CB, WB and DM). 40, the forward line's sides, has no VPF counterpart.

Across the database, abilities 26, 27, 32 and 45–63 average 51 for every position and play style. They are only higher across the board for stronger players (about 62 for the holders of any skill), which suggests filler values. That is why those names rest on the VPF players.

Kit style

+0x57–+0x5d say how the player wears the kit. The retail game has an editor for them, and its labels are in message category 2000: a "Dressing" page (1070) with Sleeves, Gloves, Wristband and Spikes (1074–1077), and a "GK Style" page (1071) with Hat, Pants, Gloves, Wristband and Spikes (1078–1082). The help texts (3074–3082) explain each one: sleeves are "Short all year", "Long all year" or "Change length according to season", outfield gloves are worn "during cold weather", and the goalkeeper's leg wear is "Normal" or "Long". Edit players carry the same settings; the database players mostly have 0.

Confirmed from the game code:

Address Symbol What it shows
GAMEPRG.REL 0x1e468 (called from 0x1e67c, 0x1e7c8, after get_player_uniform_style_info_ptr) fills a 16-byte player::SPlayerUniformStyleInfo from PlPinfo: bytes 0–6 are record +0x57–+0x5d, byte 7 the shirt number (+0x2b), +8 a pointer to the record (name and number for the shirt), +0xc a flag from 0x1e518, which compares the player with the one picked by team +0x41eb (probably the captain). Byte 2 (+0x59) is copied only when plGi_Get() +0x1f538 is set, and is 0 otherwise
0x2130d4 PlGiTask::InitStadium sets plGi +0x1f538 to IsLongSleeveUniformFromTempareture (0x21fbc8: temperature below 10) of plCity_GetTemperature for the player's club's city (pwkTeam_GetMyTeamData +4) on the match date. PlGiTask::init (0x210ffc) clears it. The only caller is ScheCallbackCommand_MatchBranch (0x113db8), the career's match step, so a VS match leaves it 0
GAMEPRG.REL 0xe12c (before set_team_uniform_style_info) copies the same flag into the team style's long-sleeve byte (+0x14), so outfield gloves show exactly when "by season" sleeves are long
0x103594–0x10364c CFaceLoader::LoadRequest for an edit player (plPinfo_IsEdit, id from 0x7cce) it copies the edit work (pwkTeam_GetEditWork, record at work +4) and passes record +0x5a ≠ 0 to CEditFaceObject::RequestCreate (0x1507c8), which keeps it at +0x288. Other players take LoadRequestNormal (0x1036a8), which has no cap
0x150d04 CEditFaceObject::CopyHair when +0x288 is set, adds the face pack's cap texture (GetCapTextureImage, vtable slot 0x10c) to the hair model
YRSTPRG.REL 0xc598, 0xf250, 0xf464 PLAYEREDIT_MODULE the retail player editor's face preview passes record +0x5a to CEditFaceEdit::ChangeHairType (0x14ff48) as its cap flag, which SetupHairModel (0x1503a0) uses the same way
0x2cd9c8 PlayerAssemblerBase::set_player_uniform_style_info clamps bytes 0–6 to at most 2, 4, 4, 3, 5, 1 and 14, then stores the 16 bytes at assembler +0x8460 + 16 × slot. Slot 0 is the goalkeeper (prepare_player_one sets up the GK textures for it)
0x2cf3a0 set_default_uniform_style the defaults: all 0 except byte 4 (+0x5b) = 1 and the shirt number 10
0x2cdd68 refresh_uniform_style byte 0 picks the arm models: 1 short, 2 long, 0 the team's choice (team style +0x14, set by the season, IsLongSleeveUniformFromTempareture 0x21fbc8). Byte 1 non-zero adds the wristband meshes, only when the sleeves are short. Byte 5 (slot 0 only) swaps the goalkeeper's pants model for the long one
0x2c9c28 set_hand hand models: the goalkeeper always has gloved hands; an outfield player gets gloved hands when byte 2 is non-zero
0x2cb240, 0x2cb2b0 prepare_player_one glove textures: the goalkeeper's from byte 4 through the table at 0x3a1de0 (common-pack entries 93–98, named gkglv_00–gkglv_05), an outfield player's from byte 2 through 0x3a1dc8 (1–4 → entries 89–92, fpglv_00–fpglv_03)
0x2cdad8 setup_player_uniform_style byte 6 picks a pair of palettes from the table at 0x3a2238 (15 pairs of entries 116–122) for common-pack texture 115, named spk_00 (the boots). Byte 1 picks the palette of texture 123, named wristband (4 palettes, entries 124–127)

The common pack is PLAYER/PLAYERDATACOMMONPACK1.MRG; the texture names come from its .svm entries (python SRC/svr.py info).

Offset Byte Name Values
+0x57 0 sleeves 0 by season, 1 short all year, 2 long all year
+0x58 1 wristband 0 none, 1–4 a colour (shown only with short sleeves)
+0x59 2 gloves outfield players, only in a career match when the club's city is below 10 degrees (below): 0 none, 1–4 glove texture fpglv_00–03
+0x5a 3 gk_cap edit players only: non-zero puts a cap on the head (below). The source name is nicknack_gkcap, the GK Style page's Hat. The match's style byte 3 has no reader; the cap goes through the face loader instead
+0x5b 4 gk_gloves goalkeeper's gloves gkglv_00–05
+0x5c 5 gk_pants goalkeeper only: 0 normal, 1 long
+0x5d 6 boots 0–14, one of 15 two-palette colourings

Tested in PCSX2 (a VS match with England's fixed squad, records 26,041–26,055 edited and renamed after their settings):

  • Boots: Gerrard (1), Joe Cole (7) and Wright-Phillips (14) each wore differently coloured boots.
  • Sleeves: Ashley Cole with sleeves 2 wore long sleeves.
  • Wristband: Neville (1) and Ferdinand (4), both with short sleeves, wore wristbands.
  • gk_cap = 3 showed no cap, neither on the keeper (Robinson) nor on an outfield player (Owen). The code explains it: both are database players with fixed faces, and only an edit player's face reads the cap.
  • Outfield gloves (Terry 4, Beckham 1) didn't show in a night match. The code explains it: a VS match never sets the cold-weather flag.
  • The keeper (gk_pants 1, gk_gloves 5) wore long pants and gloves that looked unlike the usual keeper gloves.

The Dressing and GK Style pages share bytes: both have a Wristband and Spikes item, and there is only one wristband and one boots byte.

Empirical: across all 27,950 players the values stay within the clamps (pbdata.py info prints the ranges and marks a value above them with !!). Sleeves are always 0. Boots value 1 is held by several goalkeepers (Dudek, Kiely, Hoult, Howarth); the other values look random, with no link to nationality or position.

Also read by the dissatisfaction code. pwkDissatis_PlayerResign (0x239a30) and pwkDissatis_StaffResign (0x23a018) use +0x58 and +0x59 as indexes into multiplier tables of 1.0, 1.1, 1.2, 1.3 (0x3917c8, 0x3919d0), like the eight dissatis causes. The data looks like kit settings, not sensitivities: 26,524 players have both at 0, and only 31 have both non-zero, where the eight dissatis causes are spread over 0–3. So a wristband or gloves also makes a player a little more upset when a teammate or a staff member he likes or dislikes leaves.

Gloves and cold weather (confirmed, table above). plGi_Get() +0x1f538 is the career match's long-sleeve flag: the temperature of the player's club's city on the match date is below 10. That is the month's average for the city's climate zone plus −2 to +2 (PLRESOURCESIM_FORMAT.md). The European zones average below 10 from about November to March. The flag counts for both teams, home or away, and only in career matches; in a VS match it is always 0, so no outfield player wears gloves there.

The cap (confirmed, table above). gk_cap is read only when an edit player's head is built from face parts: a non-zero value adds the cap texture to the hair model. Database players use their stored faces, so their gk_cap is never read. The match's copy of the byte (style byte 3) has no reader. So the GK Style page's Hat shows in the retail editor's face preview and on that edit player in a match, whatever his position. Values above 1 look the same as 1: both paths keep only whether the byte is non-zero.

explodetype and team combos

No code was found that reads +0x43–+0x46. The decoder keeps them (plBits_DecPlPbaseEx stores four 4-bit values at PlPbase +0x43), and the developers' field table names them explodetype, 0–15, four of them (The developers' field names). A scan of every byte load at their PlPinfo offsets (+0x1db–+0x1de) in SLES_541.51 and the overlays, and at +0x43–+0x46 in the executable, found only other structures (the manager record's tactics in the developers' editor, a youth candidate record at 0x25fd70). A scan for the offsets as immediates (0x1db–0x1de, which also finds indexed reads) adds only the save's serializers, which write and read the four values with every player (SAVEPRG.REL 0xeed0, 0x1043c). So they are kept in the save and read by nothing.

"Explode" is the game's word for a growth spurt: the pwkExplo_* functions (0x243348–0x2441c0) check monthly and yearly triggers (pwkExplo_Month 0x243b80), then pwkExplo_SetExploExp (0x243348) adds experience to all 64 abilities. Whether a player can have one depends on his age against a table picked by the skill growth type +0x4b (0x243020, table 0x54d6e0), and on PlPinfo +0x270/+0x271, which plPinfo_InitExplo clears. So explodetype looks like a planned input to this system that the retail game doesn't use.

Outside the match, only +0x67 of the 11 tendencies at +0x66 is read (the affinity row). pwkPCombo_Add (0x24e2e8) adds a bonus to all 11 and to the 64 abilities when the club reaches one of 45 "combos" (conditions at 0x54fbe8, 0x34 bytes each, checked by pwkPCombo_Recalc 0x24e0e8; bonuses at 0x550510, 0x98 bytes each), clamped to 0–4. The bonus to +0x67 is always 0. The match reads all 11 (The tendencies).

The tendencies are not the tactics screen's combination lines, nor the heart, "…" and skull icons it shows next to each player when one is selected. Both are per pair of players, stored in the save: the levels, their thresholds and the five icons are in SAVE_FORMAT.md ("Pair combinations"). The lines follow the same levels: in one screenshot the only red line joined the selected player to the one player with the big red heart, and a substitute who had just come on had only "…" icons and white lines. Which colour (white, yellow, orange, red) is which level is empirical, from those screenshots. How fast a pair grows (plCombi_GetCombinationGrow, 0x20fb90) depends on both players' PlPinfo +0x1ea, through an 8 × 8 table at 0x390698, not on +0x66.

python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 4408     # +0x57-+0x5d by name
python SRC/sles_disasm.py ISO/SLES_541.51 dis set_player_uniform_style_info set_hand__Q2 setup_player_uniform_style__Q2
python SRC/snr2.py dis ISO/DLL/GAMEPRG.REL 0x1e468 44 --sles ISO/SLES_541.51
python SRC/sles_disasm.py ISO/SLES_541.51 dis pwkPCombo_Add

Skills

+0x64 holds one bit per PlPlayerSkill (plPinfo_IsSkill, 0x218748). Bit n is described by message 6000 + n of category 2000. The short labels are summaries of those texts, as pbdata.py prints them:

Bit Label Description (message 2000:6000 + bit) VPF name
0 covering superb covering, bails the team out Covering
1 offside line holds the defensive line, works the offside trap Line Control
2 penalty taker superb penalty taker PK Taker
3 penalty stopper puts pressure on the penalty taker (a keeper) PK Goal Keeper
4 one-on-one finisher cool in one-on-ones with the keeper Shot on 1 on 1
5 one-on-one keeper saves one-on-ones GK on 1 on 1
6 long throw very long throw-ins Long Throw
7 super sub swings a match when brought on Super Sub
8 through balls vision for through balls Ball Feeding
9 positioning exquisite positioning Positioning
10 reflex saves miraculous reactions Fine Saving
11 acrobatic shot shoots even off balance Acrobatic Play
12 one-touch shot one-touch finishing Volleys
13 goal machine pin-point shooting Controlled Shot
14 poacher pounces on loose balls in the box Good Positioning
15 playmaker leads the team with killer passes Through Ball

VPF's Special Skill page lists its 16 skills in this order, and each name fits the description of the same bit, which backs up the bit order. VPF marks Rooney with Shot on 1 on 1, Acrobatic Play and Controlled Shot, and his +0x64 has exactly bits 4, 11 and 13. Beckham's PK Taker, Ball Feeding and Controlled Shot are exactly his bits 2, 8 and 13.

That bit n goes with message 6000 + n is empirical, from who holds which bit. Of 2,465 goalkeepers, 90, 91 and 86 hold bits 3, 5 and 10, and of 14,395 defenders and forwards only 1 holds any of the three. Bits 0 and 1 go mostly to defenders (237 of 238), and bits 2, 4 and 11–14 mostly to forwards. The code confirms one: the substitutions screen tests bit 7, the super sub (CTacticsMenuSubstitutionsImplement::Update_MessDisplay, 0x2f6558). In the match, the skills are parameters 112–127; which classes read each is in Who reads each parameter, and how much they change a play isn't traced.

Play styles

Confirmed (pwkPlayStyle_Init 0x24d8a8, AddPlate_PLAYSTYLE 0x289e50). +0x5e holds up to 5 play styles, 1–22, with 0 for an empty slot. When a player is set up, the game lists the stored styles, then adds random ones that suit the player's position until the position's count (table at 0x398e98) is reached. The first stored style is the one the detail screen shows. Style n is named by message 1:150 + n:

Style Name (message 1:150 + n) VPF name Players
1 Centre Forward Centre Forward forwards
2 Moving Moving forwards
3 Postplayer Target man forwards (Crouch)
4 Dash out Darting run forwards (Inzaghi, Shevchenko)
5 Second Striker Second Attacker forwards (Totti, Del Piero)
6 Wing Wings forwards and attacking midfielders
7 Play maker Playmaker midfielders (Pirlo, Zidane)
8 Shadow striker Shadow Striker midfielders (Lampard)
9 Attacker Attacker attacking midfielders (Robben, Ronaldinho)
10 Dynamo Dynamo midfielders (Gattuso, Vieira)
11 Man marker Hard Marker defensive midfielders (Makelele, Gattuso)
12 Covering Anchor defensive midfielders (Makelele)
13 Centre MF Central Midfielder midfielders (Lampard, Beckham)
14 Winger Side Attacker wide players (Beckham, Roberto Carlos)
15 Threaten to cut in Wing Forward wide players (Henry, Cafu)
16 Full back Wing half full-backs (Cafu)
17 Sweeper Sweeper defenders
18 Defensive Sweeper Libero defenders
19 Stopper Stopper defenders (Puyol, Nesta)
20 CB Centre Back defenders (Puyol, Nesta)
21 GK Orthodox goalkeepers only (Cech, Lehmann)
22 Attacking GK Libero GK goalkeepers only (Buffon, Barthez)

Across the database (empirical), 19,403 players have at least one style. Styles 21 and 22 are held only by goalkeepers (841 and 220), and 1–5 almost only by forwards. VPF shows its 22 styles in the same order, each with a rating. This game stores only which styles a player has. Beckham's three styles here (14, 13, 7) are his three best in VPF (Side Attacker 80, Central Midfielder 70, Playmaker 92); Rooney's 2 and 5 are his two best (Moving 87, Second Attacker 85), but his 9 (Attacker) is 48 there. The style names were already checked in game through a save (SAVE_FORMAT.md).

Tested in PCSX2: John Terry (101) has styles 20, 19, 18 (CB, Stopper, Defensive Sweeper). pbdata.py set ... 101 style.0=7 makes them 7, 19, 18. On a disc patched with patch_disc.py (with --skip-tutorial), his detail screen in a new career showed "Play maker" on its STYLE line, with three style icons. So the first stored style is the one shown, and the database value reaches the game unchanged.

Positions and aptitude

Confirmed, plPinfo_CalcAptPos (0x217f70), plPinfo_GetPositionFitValue (0x217e48), and the table at 0x532610. The detail screen's pitch grid has 13 cells, and they are the 13 position numbers. For each cell, the game computes a fit value, a weighted sum of up to four abilities:

Cell Name Fit
0 GK 33
1, 2, 3 DF left, right, centre 0.7 × 34 + 0.3 × 43 (left) or 44 (right); centre 0.7 × 35 + 0.2 × 42 + 0.05 × (43 + 44)
4, 5, 6 DM left, right, centre the same with 36 (sides) and 37 (centre)
7, 8, 9 AM left, right, centre 38 and 39
10, 11, 12 FW left, right, centre 40 and 41

So abilities 33–44 are position aptitudes: 33 goalkeeper, 34–41 each row's sides and centre, and 42/43/44 a leaning to the centre, the left and the right. The rows go from the goal upwards. The names DF/DM/AM/FW are descriptive, not the game's. Which of 43 and 44 is the left is empirical: players who play on the left (Robben, Edu) are higher in 43, and Tevez's top-right cell lights up with 44 = 88.

Each cell's fit is converted to a level from 0 to 4 against the player's best cell, using the table at 0x5327b0. A cell gets level 4, 3, 2 or 1 for the first row where fit ≥ 70/60/50/40 and either fit ≥ 1.0/0.95/0.9/0.8 × best, or fit ≥ 80/70/60/50. Then each listed position (+0x1c, up to three) gets +1, up to 4.

Tested in the game: Terry with abilities alternating 38 and 99 has 99 in 33, 35, 37, 39 and 41. His grid lit the goalkeeper box and the whole centre column, as the formula predicts. The vanilla Terry's grid (his centre-back cell, the centre cell in front of it and one side cell) also matches the computed levels. His two side cells score 66 and 68, just under the level-3 threshold, and the game's random start offset (plPinfo_InitAbil) can push one of them over. Which levels the screen draws, and in which colour, hasn't been traced.

python SRC/pbdata.py show prints the grid (forwards at the top), and list names the positions.

Manager, coach and scout bars

Confirmed, WP::CDetailManager::CalcManagerAbil (0x286cd0). PlMinfo is 4 bytes followed by the PlMbase (plMinfo_InitDb 0x2e9720 copies it to +4), so PlMinfo +0x6a is ability 0 and +0xa0 is the job at PlMbase +0x1c. Every manager and coach screen has 12 shared bars, then a set picked by the job through the jump table at 0x557630. All are single abilities except FLANK:

Bar Ability Bar Ability Job Extra bars (abilities)
ATTST 45 FLANK avg 29, 30 0, 1, 2 DRIBB 10, SHOT 11, PASS 12, HEAD 13, INTER 14, MARK 15
TEAMW 46 MOTIV 0 3 SPEED 18, PHYSI 20, STAMI 19, MENTA 21
FK 47 PHYSC 1 4 SAVIN 16, HND 17
TRAIN 5 COMMU 3 5+ FASTB 39, SLOWB 40, WINGP 41, DIREC 42, OFFSI 43, CLOSD 44
ATKDF 22 POPUL 2 – –
CENTA 28 ASSES 4 – –

The labels match the in-game help pages: 0–2 are assistant coaches, 3 physical coaches, 4 goalkeeper coaches. The database holds jobs 0 (439), 1 (786), 2 (714), 3 (678) and 4 (383), and no 5. A staff member hired as manager gets the job-5 bars. In a save, C. Collin (job 0 in the database) shows the manager bars, and they match abilities 39–44. So the job is changed at run time. The shared bars for Collin and M. Boismortier match their detail screens exactly.

What the jobs are. The developers' manager editor names them (confirmed, see The developers' staff editors): 0 balanced, 1 attacking and 2 defensive assistant, 3 physical coach, 4 GK coach, 5 manager, 6 youth manager. Averaging the coaching bars over each job in the database (empirical) agrees:

Job Count Coaching bars (DRIBB SHOT PASS HEAD INTER MARK) Is
0 439 68 68 68 60 68 68 a balanced assistant: all even. Hired as manager it becomes 5, as youth manager 6
1 786 80 80 80 68 55 55 an attacking coach
2 714 68 55 61 76 80 80 a defensive coach
3 678 physical bars physical coach
4 383 saving bars goalkeeper coach

In a save, M. Eulenburg and S. Saioni (job 0 in the database) are the manager (5) and youth manager (6), and the coaches keep jobs 1–4.

Where jobs 5 and 6 are set (confirmed). The job is the word at PlMinfo +0xa0; your club's manager is at team data +0x4854, so his job is at +0x48f4. pwkTeam_SignManager (0x269e20) copies the record as it is and sets only the contract years at +0x9e, so the job is set before it is called:

Address Symbol What it shows
0x26a240 pwkTeam_GetManagerCandidates builds each manager candidate's PlMinfo from the database (plMinfo_InitDb) and writes 5 into +0xa0. Every staff member hired from the manager list arrives with job 5, whatever his database job
SIMPRG.REL 0x78998 WP::CManagerChangeTask the manager change screen lists your manager and up to 4 of your coaches (pwkTeam_GetManager, pwkTeam_GetCoaches, 0x771b0, 0x77270). Picking a coach writes 5 into his +0xa0 and calls pwkTeam_SignManager; this is how a coach becomes your manager
SIMPRG.REL 0x78c3c WP::CManagerChangeTask sets 5 only while the detail screen shows a coach on that list, so it shows the manager bars, then puts the old job back
0x26bd7c pwkTeam_SetYManager writes 6 into the youth manager's +0xa0 after copying him in
SIMPRG.REL 0xd64d4 (next to pwkTeam_GetYManager and pwkTeam_GetCoachCandidates calls) writes 6 into a staff record it lists; which screen this is isn't traced

The manager candidates come from the manager search (pwkTeam_UpdateManagerCandidates 0x261500) and the automatic fill (pwkTeam_AutoListUpManagerCandidates 0x263650), both through pwkTeam_AddManagerCandidate (0x269658). They take staff from the whole staff database by id, and no test of the database job was found in them, so coaches can be found and hired as managers.

Former players become coaches or scouts (confirmed). A player of yours can be asked to change career. The talk module (TALK_MODULE::CTalkModule, SIMPRG.REL 0x11af8, 0x11b60) first asks pwkTeam_IsPossibleJobChange (0x269c28), which allows it when the player is not an edit player, his PlPinfo +0x21c (life, ECONOMY.md) is at least 5, pwkTeam_GetCompAverage is at most 45,000, a coach or scout job-change slot is free, and fewer than 3 of your players already have a job change set. The answer goes into PlPinfo +0x210 through plPinfo_SetChangeJob (SIMPRG.REL 0x12150): choice 0 takes a value from his position (the table at SIMPRG.REL 0x1cd288, by plMisc_Apos2Epos), and choices 1–3 store 4–6.

+0x210 Becomes Set by choice
0 nothing –
1 coach, job 0 (balanced assistant) 0, for a WB, DM or SM
2 coach, job 1 (attacking assistant) 0, for an OM, WG or FW
3 coach, job 2 (defensive assistant) 0, for a GK, SB or CB
4 coach, job 3 (physical coach) 1
5 coach, job 4 (GK coach) 2
6 scout 3

At the start of the next season ZinziYearStart::calcYearStartSecondHalf (YRSTPRG.REL 0x17164–0x17240) walks your squad. For each player with a job change it turns +0x210 into a coach job through the table at YRSTPRG.REL 0x33288 (7, 0, 1, 2, 3, 4, 7; 7 is none) and makes him a coach in one of the 3 coach job-change slots (0x1a698, which builds the PlMinfo with that job; EVS::SetChangeJobCoach), or a scout for 6 (0x1a5e8). When that succeeds the player leaves the squad (ZinziMyTeam::DiscardPlayer, reason 6). pwkTeam_PutCoachJobChangeWork (0x26bc88) then puts each new coach with job 0–4 on your coach candidate list (pwkTeam_AddCoachCandidate, source 8), where he can be hired. As a coach he can later become the manager through the manager change screen above. So no player becomes a manager directly: former players become coaches first.

The coach page's title (SetupDetailCoachPageCommon, jump table 0x5577d0) is the same string for jobs 0, 1 and 2 (Msg::GetString(0x18, 3)), with 4 for goalkeeper coaches and 5 for physical coaches. Those match messages 553 "Assistant Coach", 554 "GK Coach" and 555 "Physical Coach" of category 100001. Checked in game: S. Daerden (job 1) and L. Lynch (job 2) are both titled "Assistant Coach", and their comment lines call them an "outstanding forwards coach" and a "very good defence coach", which fits the averages above. So the game gives jobs 0–2 one title; what tells them apart is which abilities they are good at, which the comment line puts into words. GP::ConvertStaff (0x27ee68) likewise gives 0–2 one icon.

Confirmed, WP::CDetailManager::ConvertScout (0x287c60). PlSinfo is 4 bytes followed by the PlSbase (plSinfo_InitDb 0x21eb80). The 11 scout bars are single abilities (the 45 mapped scout abilities, starting at PlSbase +0x2d): CLB 0, PLAYE 1, FINDP 3, YOUTH 4, YOUNG 5, OLDER 6, VETER 7, MANAG 21, ACOAC 22, PCOAC 23, GCOAC 24. A 12th value, ability 25, is computed too but not labelled. Staff and scout bars are drawn as value / 99. Player bars add 10 first.

python SRC/pbdata.py show prints these bars for managers and scouts (the job's own set, plus the manager set for coaches), and csv adds them as columns.

The abilities no bar shows (confirmed, 2026-10-30). Most of them do something; the readers:

Abilities Read by What they do
Manager 23–27 (GK, DF, DM, OM, FW coaching), 31–38 (the 8 systems) the practice growth code, through table 9 of TRAINING_LIST.TBB (TRAINING_FORMAT.md): SIMPRG.REL 0x159b68 (my club's manager, team data +0x48be + n), 0x15ab58 (the youth team's coach) each coaches the matching aptitudes: GK coaching the GK aptitude, DF the side and centre backs, DM the wing backs and defensive midfield, OM the side and attacking midfield, FW the wingers and forwards, each system coaching that system's aptitude. From 50 up it adds (value × 0.2 − 1) % to the growth
Manager 7, 8, 9 (young, mid-career, veteran players) SIMPRG.REL 0x15a428, for the first team (called at 0x15864c and 0x159654) by the player's age (PlPinfo +8): under 23 ability 7, under 30 ability 8, else 9 (team data +0x48c5–+0x48c7); returns (value − 50) ÷ 100 as a growth factor, so 99 is +49 % and 38 −12 %
Manager 7, for the youth team SIMPRG.REL 0x15abe8 (called at 0x15a5c4), youth data +0x3f71 the same factor for every youth player, from the youth team's coach
Manager 6 (youth players) none found –
Scout 2 (money negotiation) pwkMoney_GetMoveResearchMoney, GetMoveScoutListMoney, GetRentalResearchMoney, GetRentalScoutListMoney (0x2484d8–0x248938), pwkMoney_GetScoutExpectSalary (0x249e80), plSinfo_CalcHexagon (0x218a30) what the scout's research and scout lists cost for transfers and loans, and his expected salary
Scout 8–20 (the 13 positions) plSinfo_CalcAptPos (0x218bd8, PlSinfo +0x39) each to a level 0–4: below 55, 65, 75, 85, and above
Scout 32–44 (the 13 regions) plSinfo_GoodRegion (0x218b48, PlSinfo +0x51) the scout's best region
Scout 26–31 (England … the Netherlands) none found –

(plSinfo_GoodAbil, 0x218b88, returns the best of all 49 values from PlSinfo +0x2d, the special searches included.) So the youth team's coach is judged on "young players" (7), and nothing reads "youth players" (6), which may have been meant there: 0x15abf8 reads +0x3f71 where +0x3f70 would be ability 6. Nothing reads a scout's six nations either.

Entries 2 and 3

Each is 27,950 u16. Entry 2 is a ranking of the player ids: players 0–25,590 sorted by rank (highest first), then main position, then nationality, followed by ids 25,591–27,949 in order. Entry 3 is its inverse, each player's place in the ranking. Both hold for all 27,950 players (empirical, sorted by the stored +0x18, +0x1c and +0x14; pbdata.py info checks it).

Confirmed from the code. A table of 16 × 13 s32 at 0x52fbf8, fixed in the executable, gives the first place of each group: row 15 − rank, column = main position, −1 for an empty group. Row 0 has two groups (place 0 in column 9, places 1–2 in column 12), and the last group starts at 25,585 (row 15, column 12). The readers:

  • serchPinfo (0x20d470) finds a group's range of places and reads the players through entry 2 (0x390670). The last range ends at 0x63f7. Given a nation, it narrows the range to that nationality, which is why each group is sorted by it.
  • getPinfoRank (0x20d908) and getPinfoApos0 (0x20d9a0) find the row and column that hold a player's entry-3 place. So for players below 0x63f7, the game's rank and main position come from the place, not from +0x18/+0x1c.

Nexxus's report: the game never shows a player's rank (no number, class or stars), so an edit to it can only be seen through what it controls: the Transfer List bands (PLRESOURCESIM_FORMAT.md), national call-ups and popularity changes.

Confirmed: the run-time rank ranges at 0x5eac08 are built from the group table when the database is set up (0x20c4a8, after initPlCoreInfoSub): each row runs from its first group's start to one before the next row's first start, the last to 0x63f6. Entries 2 and 3 are read only through getPlno (0x20c2b8), checkPlno (0x20c2f0), serchPinfo, getPinfoRank and getPinfoApos0, and the group table exists once on the disc. Inside a group there is no order by id or by ability (empirical: about half of the 18,920 neighbouring pairs go each way).

For editing: changing the rank or main position in a record alone has no effect in the game for ids below 25,591, and changing the nationality alone moves the player out of his nation's call-up search. pbdata.py set and import with --sles <SLES_541.51> <out SLES> re-sort entries 2 and 3 (keeping each group's order, so only moved players change place) and write a copy of the executable with the group table to match. They check first that the input executable's table belongs to the input pack. Without --sles they say when an edit leaves the ranking out of order. pbdata.py roundtrip --sles checks the rebuild: from the disc's records it gives back entries 2 and 3 and the executable's table exactly. An emulation of getPinfoRank and getPinfoApos0 on the result agrees with the records for all 25,591 players, before and after an edit.

Tested in PCSX2: free agent A.Rankin (12,804, goalkeeper, stored rank 9) was lowered to rank 5 with --sles and renamed RANKFIX.Rankin, and put in free-agent slot 627 (the slot where rank-5 players showed before, with slot 50 given a rank-14 player so the same slots pass the filter). In a new career he showed on the new club's Transfer List in 2006–07 (GK, 22, ENG, no team), which a rank-9 free agent can't (PLRESOURCESIM_FORMAT.md), and his detail page opened normally. A main-position change goes through the same table but wasn't tested on its own.

National team call-ups

Confirmed, SIMPRG.REL and SLES_541.51. In a career a national squad is picked from the club players when it is called up:

Address Symbol What it does
SIMPRG.REL 0x160b68 jmNT_CallCheck on a day where pwkSche_IsNationalConvene is true, calls up every squad (jmNT_CallAll) and sets event flags 0x13f–0x141
SIMPRG.REL 0x1603c8, 0x160210 jmNT_CallAll, jmNT_makeNationList lists the nations with an international competition on the schedule and runs plTeam_CreateNationTeam(team, squad, 23) for each
SIMPRG.REL 0x15fbe8 jmNT_NewCall(team) the same for one team
0x226338 (unnamed) the squad's shape: counts the positions in the national manager's three formations (PlMbase +0x3d, see the staff editors) and scales them to 25 places over the 13 positions
0x227470 plTeam_CreateNationTeam for each position, walks the ranking best first (serchPinfo with rank, position and the team's nation) and fills a pool of up to 38
0x534148 per-position limit at most 1 goalkeeper and 3 players per other position from one club
0x2272f8, 0x227200 _sortBaseListFromNationalTeamPoint, _getNationalTeamPoint sort the pool by a national-team score before the squad is kept

plTeam_CreateNationTeam takes only players whose current team (bits 22 and up of the run-time core info) is your club (1), another club (2–459) or team 465. From your club it takes at most 8, and none with flag 0x300 in PlPinfo +0x20c. The search stops at 0x63f7, so the fixed national records (26,041+) are never called up.

So to change who plays for a country in a career, edit the club players (their nationality and their place in the ranking, see above). The fixed blocks only change VS mode.

Nexxus's report (PCSX2): with record 26,046 renamed NT.Terry.VS, the career national squads list each player's club, so they are drawn only from club players. The renamed fixed record doesn't appear, as the search limit above says.

What _getNationalTeamPoint scores, why team 465 is accepted (it is the Netherlands' national team, PLRESOURCECOMMON_FORMAT.md), and what flag 0x300 means aren't traced.

PBDATA_JP.PAC

The same header and identical entries 2 and 3, but entry 1 is empty (0 bytes). The European disc carries no Japanese player records. pbdata.py info reports this as a note, not a problem.

Writing

pbdata.py can write the database back out. Each record is re-encoded from its stored field values in readBits order: the 19 name bytes as they were, every field at its width, then the padding bits as they were. Entry 1 is then replaced in a copy of the pack. Records are fixed-size, so the entry keeps its offset and size and nothing else in the file moves. pbdata.py roundtrip does this for all 31,950 records without changes, and the result is byte-identical to the disc's file (a regress.py check).

Edits are given as the values show prints, and are converted back:

  • add fields subtract their offset (age 30 is stored as 14).
  • Abilities must be one of the 32 table values, 38–99.
  • req_status must be one of the 16 table values, stored as its index. The game reads only the low 4 bits of the 16-bit field, so an unchanged value keeps its stored bits.
  • Signed manager fields must fit 9 bits.
  • Names are cp850, at most 18 bytes, zero-padded to 19 (every name on the disc has a terminator).

The limits are mostly the field widths, not what the game considers sensible. The exception is age, height and weight: the decoder adds their offset and stores the sum in a byte, so pbdata.py refuses values above 255. Height is the only one where that matters (the field alone would allow up to 405 cm). Otherwise the game takes whatever fits.

Tested in the game (PCSX2, new game, patched with patch_disc.py): Terry renamed J.Terry.MOD, 96 kg, left-footed, with abilities alternating 38 and 99. The name, weight, leg and the bar pattern all showed as predicted by pbdata.py show (low SPEED and FK, high STAMI, HEAD, INTER and MARK). A height of 313 cm showed as 57 cm, which is what led to the byte limit above. A second test with 255 cm showed 255 cm.

python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out.PAC 101 age=30 ability.13=99 name=J.Terry
python SRC/pbdata.py csv DAT/PARAM/PBDATA_EU.PAC players players.csv   # edit in a spreadsheet
python SRC/pbdata.py import DAT/PARAM/PBDATA_EU.PAC out.PAC players players.csv
# rank, main position or nationality: re-sort the ranking, patch the executable
python SRC/pbdata.py set DAT/PARAM/PBDATA_EU.PAC out.PAC 101 rank=14 position.0=12 --sles ISO/SLES_541.51 out_SLES_541.51
python SRC/patch_disc.py patch game.iso out.iso PARAM/PBDATA_EU.PAC=out.PAC disc:SLES_541.51=out_SLES_541.51 --copies --dat DAT

import writes only the values that differ from the pack, so an unedited CSV gives an identical file. The bar and entry2/entry3 columns are derived and ignored. patch_disc.py puts the edited pack on a disc (REBUILD.md).

The editor's People tab (SRC/editor.py) edits players, managers and scouts through the same code: pbdata.edit_spec gives each named field its range or named values, unnamed fields stay read-only, and a save that changes the ranking also writes the executable, as --sles does. Tested in PCSX2: Van der Sar (250) edited only in the People tab showed 255 cm, 45 kg, LEFT, full bars and every position cell lit on his detail screen; his age and shirt came from OTEAMMEMBER.TBB, as the tab's note says (INITTEAM_FORMAT.md).

Still unknown

  • Whether anything reads castno (+0x30, always 0); no reader was found.
  • Tendencies (The tendencies): what the two checks that make the team's situation 3 or 4 test (0xf5728, 0xf5c18), which tactics slider sets where situation 1 starts, which side 0xa9e20 picks at a penalty, which side's defensive line the press row uses, which distance the second press float is compared with (VU0 code), and what byte 1 of the per-side record at match data +0x9764 (set from a match menu) does. No screen shows the tendencies (Nexxus's report), so an edit can only be seen in how a player plays; reading the match's parameter copy over PINE during a match would at least confirm that an edited value reaches it.
  • Kit style: whether the dissatisfaction code's use of +0x58/+0x59 is intended (Kit style). The cap on an edit player and the gloves in a cold career match are traced in the code but not yet seen in the game.
  • How much each parameter changes what its readers do (Who reads each parameter gives the readers): what tells apart the kick kinds that share a builder (The kind table: 3 and 4, the in-play pairs, the tackles), where kinds 27–29, 31, 34 and 35 (kick-off, penalty, goal kick) are first set, flag bits 0x200–0x2000 and kind 5, the 45 calls with no class, and the sources of parameters 5, 16 and 24.
  • What each speech tone and condition type value is, what the ball touch does (match parameter 139 has no reader), and which dribble style looks like what.
  • The hexagon labels are matched to indices from the data and VPF's pages. The label order in GP::CHexWindowBase::DrawString (0x27c700) comes from a screen layout and hasn't been traced.
  • What the play styles do in a match, beyond which abilities grow and which roles read them (Who reads each parameter).
  • The code that turns the leg field into PlPinfo +0x1c4.
  • The job change: what pwkTeam_GetCompAverage measures (it must be at most 45,000), and which screen writes job 6 at SIMPRG.REL 0xd64d4.
  • What reads the staff fields only the source table names (rank, face, appeartype, and the manager's comptype), and what the model pattern (+0x65) does.
  • Whether the match's two readers of the selection policy (GAMEPRG.REL 0xd96e8 and 0xf3cb0, PlTeamData +0x4888) were meant to read the match policy (+0x4889), which nothing reads; what the word tables at 0xf3cb0 decide (probably the substitutions). An in-game test could tell: change a computer club's manager's selection or match policy and watch its tactics and substitutions (The four policies).
  • Which end of a tactics slider (0–6) means more attacking, and so whether plTeam_SetStyleTeamParam giving a strength ratio above 1.19 column 0 (the same as up to 0.79) instead of column 4 is intended.
  • Whether anything reads manager ability 6 (youth players) or the scouts' nation abilities 26–31; no reader was found (Manager, coach and scout bars), and whether the youth coach's growth factor was meant to read ability 6 instead of 7.
  • Header +0x14, +0x24 and the last 8 header bytes.
  • What _getNationalTeamPoint scores.

Checking the claims

python SRC/pbdata.py info DAT/PARAM/PBDATA_EU.PAC DAT/PARAM/PBDATA_JP.PAC
python SRC/pbdata.py list DAT/PARAM/PBDATA_EU.PAC --find terry
python SRC/pbdata.py show DAT/PARAM/PBDATA_EU.PAC 100 m:0 s:0     # every field
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x1c98 340 --sles ISO/SLES_541.51   # manager editor pages
python SRC/snr2.py dis ISO/DLL/DEBUGPRG.REL 0x97a0 70 --sles ISO/SLES_541.51    # scout editor, special searches
python SRC/pbdata.py csv DAT/PARAM/PBDATA_EU.PAC players players.csv
python SRC/initteam.py squads DAT/PARAM 7                          # Chelsea, with names
python SRC/sles_disasm.py ISO/SLES_541.51 dis initBpmaster getPbase readBits__Q25Param11PlBitsClassi
python SRC/sles_disasm.py ISO/SLES_541.51 dis plBits_DecPlPbaseEx plBits_DecPlMbaseEx plBits_DecPlSbaseEx