What a save holds¶
A save holds players, clubs and competitions across the game world. The available history varies by career; it is not a complete archive of every match or injury.
27 readers get at it. Each one returns a Table of records, and each one reads the save
you point it at and nothing else.
People¶
players(), contracts(), suspensions(), staff(), staff_lists()
Players carry names, birth date and age, nationality, club, height, positions, 52 attributes,
the 8 personality attributes, current and potential ability, reputation, transfer value,
condition, traits, contract and any unserved ban. contracts() is the same contract record as a
table of its own, which is what you want when you are looking across every deal in the save
rather than at one club’s players. Staff carry their people data, ability, personality and how
they prefer a side to be run. Of the attributes a staff profile rates, fmsave currently reads
only adaptability.
Clubs¶
clubs(), managed_clubs(), finances(), sponsorships(), facilities(), stadiums(),
affiliates(), job_vacancies()
managed_clubs() is the club you run, and is empty between jobs. finances() is monthly figures
per club, in the save’s own unit. sponsorships() includes historical contracts, so a row
does not necessarily describe current income. stadiums() holds capacities and ownership;
names are available for only some grounds.
Club data still has limits. The meaning of a zero corporate-facilities rating and some sponsorship amounts is unconfirmed. Some finance histories still fail continuity checks. Passing the reader checks does not confirm every field’s meaning. See Trusting a number before using these fields in an analysis.
Competitions¶
stages(), competitions(), fixtures(), league_tables(), competition_rules(),
transfer_windows(), player_match_stats(), player_season_stats()
Fixtures provide dates, teams, rounds and scores where fmsave can decode an unambiguous result.
player_season_stats() is each player’s current season, as the squad and player screens show it:
one row per competition type and team, with counts such as minutes, xG, passes and tackles, and
the per-90 and percentage figures the game shows. player_match_stats() reads individual match
histories stored within player records. Coverage varies by save and player; it is not a complete
career match log. Empty output passes validation when every player explicitly has no stored
history.
Incomplete or unresolved histories fail the reader checks.
Injuries¶
injury_types(), injuries()
injuries() reads retained injury history, not a complete career medical record.
Your own club’s work¶
training(), mentoring(), tactics(), set_pieces()
These readers return data for the club you manage. Other clubs return no rows. tactics()
includes stored preset and alternate tactics; a row is a copy for one team, so the same tactic
can appear more than once.
Limits and missing values¶
Names from the installed game database. fmsave ships no competition, league, nation or city names and reads nothing from your game install. Some stadium names are available in the save. See Competition names below.
Unresolved links. Some rules blocks cannot be linked to a competition. A club’s home ground
is inferred from fixtures where possible. Missing links remain None.
Unconfirmed meanings. Most tactic settings and staff job codes have no confirmed labels. They keep their raw numbers. Other fields can have a value whose meaning is still unconfirmed; Trusting a number explains how to check.
Partial history. A missing score can mean the result was not retained, is not yet decoded, or could not be joined unambiguously. Injury history also has limits. Neither reader promises complete career coverage.
Unsupported fields. fmsave does not expose today’s availability, a full matchday lineup, most staff attributes, club debt or asking prices. That does not establish whether the save stores them. Season statistics do include yellow and red cards; check each field’s status before using it.
Unreadable values remain None. Empty tables need context: the save may explicitly have no
records, or the reader may have failed to recover them. Use validation and table coverage to
check the distinction.
Competition names¶
Competitions carry a database_id, the id every outside name source is keyed on. Supply a map and
fmsave fills in name and every denormalised competition_name:
import fmsave
competition_names = fmsave.read_competition_names("competition-names.csv")
with fmsave.open("career.fm", competition_names=competition_names) as career_save:
for competition in career_save.competitions():
print(competition.database_id, competition.name) # 12345 "Example League"
The file is UTF-8, 2 columns, database_id then name; a leading header row is skipped. Any
mapping of database id to name works in its place, and fmsave export --competition-names PATH
takes the same file.
Without a matching map entry, name and competition_name remain None. Competitions without
a database_id cannot use this map; competitions created during a career may have no entry in
an outside name source.
Which reader has the field you want¶
The reference lists every record type and every field. If you are not sure a number means what you think it means, read Trusting a number first.