A content pack is one file: a SQLite database sitting in your mods folder (%APPDATA%\TheUndercut\mods\ on Windows — see where the files go). It carries the rows and the logo images, so a pack is a single thing to share, back up or publish.
mods/
my_pack.db ← the whole mod
You do not need to know SQL to make one — the Mod Editor shipped with the game writes exactly this file. If you do want to open the file yourself — for bulk edits, or to generate a pack from a script — the SQL reference documents every table and column. This page documents what the file means, which is what you need whether you use the editor, a script of your own, or read someone else’s pack.
The golden rule of the format: every table and every column is optional. A pack that only renames two teams is valid. Anything you leave out stays procedural, and a pack authored for an older version still loads in a newer game.
The current format is v2. A pack records which version it was written in, and the game says so on the pack’s row when it only half understands one — you are never left wondering why part of a pack did nothing. The Mod Editor upgrades an older pack the moment you open it.
What is inside a pack
| Table | Holds |
|---|---|
meta | The pack’s own settings: its name, the section modes, the scenario, the starting year |
teams | The grid — identity, colours, car, budget, and (imposed packs) infrastructure and palmarès |
drivers | Driver profiles — abilities, ceilings, growth curves, traits, portrait, salary, seat |
sponsors | Brands available on the sponsor market |
suppliers | Engine, tyre and fuel makers |
circuits | Tracks imported from .circuit files — see circuit modding |
calendar | The season’s rounds, in order |
images | The PNG files the rows point at: logos and custom car sprites |
extend or replace
Each section carries a mode, stored in meta as <section>_mode:
extend is the default, and it is the base game’s own pattern: a static core plus procedural filler. replace is what you want for a full real-world season.
What replace means exactly depends on the section — worth knowing before you wonder why a supplier survived:
| Section | replace does |
|---|---|
teams | Trims the grid to your list — the grid ends up exactly as long as your team table |
drivers | The driver pool is only yours; nobody else is generated for your series |
sponsors | The sponsor market is only yours |
suppliers | Only the categories you cover are replaced. A pack with two engine makers and no tyre maker keeps the generated tyre makers, because a season cannot be run without one |
circuits | Applies to the world circuit pool — the venues future calendars are drawn from. replace means the world never drifts back to generated tracks; the calendar you build always races the tracks you placed on it either way |
One thing replace does not do
A pack’s drivers fill your series. The world also runs ten non-playable championships, each with a grid to fill, and the game tops the driver pool up so their seats are not empty — which is why a pack with a full grid of its own can still be surrounded by generated free agents.
meta.active_series is the answer: list the championships the world should run (PFS is the playable one) and every other series is switched off. Untick everything but your own and your pack’s drivers are the only people in the world. Say nothing and every series runs, as before.
Two ways a pack starts a career
meta.scenario decides what happens when the player picks your pack.
| Scenario | What the player gets |
|---|---|
| New Series (default) | The player creates a new team joining the grid and plays the pre-season. Your teams are rivals. |
| Imposed | The grid is fixed as you authored it — every team staffed, equipped and signed. The player takes over one of your teams and the season opens at race 1, with no pre-season. |
An imposed pack unlocks fields that make no sense otherwise, and that is the only difference in the format: infrastructure levels, team and driver palmarès, founding year, and the “signed to” bindings that pre-fill each team’s drivers, sponsors and suppliers. Use replace on teams for an imposed grid so only your teams race.
meta — the pack’s settings
| Key | Value | Default |
|---|---|---|
name | Shown in the pack picker | the file name |
author | Free text | — |
schema_version | Which version of the pack format this file was written in (current: 2) | 1 |
min_game_version | Game build the pack needs. An older game says so on the pack’s row instead of loading it half-applied | — |
scenario | new_series · imposed | new_series |
start_year | Calendar year of season 1 — a pack can open in any era | 1990 |
teams_mode, drivers_mode, sponsors_mode, suppliers_mode, circuits_mode | extend · replace | extend |
active_series | Championships the world runs, comma-separated (PFS,PF2) | absent = all of them |
rules | Series rule-set ids, comma-separated (points scale, the 107 % qualifying rule) | the game’s defaults |
teams
A row with no name is skipped. Teams are applied to the grid in order, so the first row is the first team.
| Column | Meaning | Leave it out |
|---|---|---|
id | Stable key. Every binding in the pack points at it, so a team can be renamed without losing its drivers, sponsors and suppliers | the display name is used instead |
name | Team name — what the game shows everywhere | required |
country | Country name or ISO code | generated |
budget_tier | small · medium · large — the behaviour profile: AI cash reserves, infrastructure cap, prestige, R&D ambition | derived from the budget |
budget_musd | Starting cash, in millions | 0 = the tier’s own amount ($40M / $60M / $90M) |
reputation | 0–100 | 0 = generated |
color1, color2, color3 | #RRGGBB — the three livery zones | left empty = keep the generated livery |
car_index | 0–11, which side-view car model | left empty = keep the generated model (0 is a real model, so it cannot mean “unset”) |
car_image | Name of an images row: a custom side-view sprite replacing the model | built-in model |
livery | Top-down livery pattern worn in live races | 255 = the game picks |
suit | Race suit worn by the team’s drivers | 255 = per driver |
logo | An images row name, or a number (one of the game’s shipped logos), or a path relative to mods/ | a procedural mark from the team name |
infra_engineering … infra_academy | The nine department levels (academy caps at 3, the rest at 10). In an imposed grid these are the team’s base car strength | 0 = generated |
career_wins, career_titles, best_championship, founding_year | All-time record shown on the team profile at season 1 | 0 = default (founding year 1990) |
history | Season-by-season rows, season:position:wins, joined by ; | none |
drivers
A row with neither first nor last name is skipped.
| Column | Meaning | Leave it out |
|---|---|---|
id | Stable key for this row | — |
first_name, last_name | — | one of them required |
code | Three letters, unique across the pack — the game indexes drivers by code and silently renames a clash | built from the surname |
country | Country name or ISO code | generated |
age | 1–55. Under 16 is mod-only: a child prospect ages every season, stays hidden from Scouting until 14, and cannot be seated before the series allows (18 in the PFS) | 25 |
intelligence, pace, consistency, adaptability, stamina, aggressivity | Abilities, 1–20 | 10 |
pot_* (same six) | Each stat’s ceiling. Never applied below the ability itself | 0 = generated |
curve_* (same six) | The stat’s growth shape with age — see below | 255 = that stat’s default shape |
traits | The game’s own trait format, kind:tier pairs — author it in the editor | generated |
fame | Starting renown 0–100. A starting value: the game can still move it | 0 = worked out from the abilities |
salary | Asking salary, per season | 500000 |
face_sheet, face_index, helmet_index, helmet_hue_0/1/2 | Portrait and helmet | 255 = generated |
team_id | Seat this driver at that team at season 1, by the team’s stable id — the binding the game resolves first | — |
team | Same, by the team’s display name. Used when there is no team_id | free agent |
role | primary · secondary · reserve — the seat is the car: primary drives car 1, secondary car 2 | primary |
contract_years | Contract length in seasons | 0 = 2 |
career_starts, career_wins, career_podiums, career_titles, best_finish | Palmarès shown from season 1 | 0 = none |
Growth curves
Each ability develops with age on one of five shapes. The game always had them, one hardcoded per stat; a pack can assign any shape to any stat.
| Curve | Improves | Declines | Default for |
|---|---|---|---|
| Early peak | fast to 26, slowly to 30, then never | from 30 | Pace |
| Physical | to 28, then never | from 28 | Stamina |
| Cerebral | always, slowing after 30 then 38 | never | Intelligence, Adaptability |
| Experience | to 30, half-speed to 34, then never | from 34 | Consistency |
| Youth | only to 24 | from 28 | Aggressivity |
The curve is the shape, not the speed: how fast a stat closes the gap to its ceiling remains a property of the stat.
sponsors
| Column | Meaning | Leave it out |
|---|---|---|
name | Brand name — required | — |
sector | automotive, technology, energy, finance, luxury, fashion, telecom, beverage, insurance, aviation, industry, retail | generated |
origin | Country name or ISO code | generated |
budget | 1–10 — what the deal is worth | 5 |
prominence | 1–10 — how often the brand appears and how hard it is to land | 5 |
color | #RRGGBB | — |
logo | An images row name, or a path relative to mods/ | a procedural logo |
team_id / team | Imposed grids: sign this brand to that team at season 1 — by stable id, or by name | open market |
id | Stable key for this row | — |
suppliers
A row whose category is not one of the three is skipped entirely — that is the one hard requirement in the table.
| Column | Meaning | Leave it out |
|---|---|---|
name | Required | — |
category | engine · tires · fuel | row skipped |
origin, color, logo | As for sponsors | generated |
budget, size | 1–10 | 5 |
power, reliability, endurance, potential, performance, heat, efficiency | Technical stats 1–10 under their real names — only the ones its category has (see below) | 0 = generated |
stat_a … stat_d | The same four values in generic slots, kept for older game builds. A named column wins over its generic slot | 0 = generated |
money_per_gp | Deal value in $/GP. Positive = partnership (they pay you), negative = client (you pay them) | 0 = generated |
contract | partnership · client — wins over the sign of money_per_gp when both are set | the sign decides |
duration | Contract length in seasons | 0 = generated |
team_id / team | Imposed grids: a list — Scarlet:partner; Azure:customer; Emerald — one supplier can back several teams, each at its own level. One partner per supplier; one supplier per category per team | open market |
id | Stable key for this row | — |
| Category | slot A | slot B | slot C | slot D |
|---|---|---|---|---|
| engine | power | reliability | endurance (shown, not simulated) | potential (shown, not simulated) |
| tires | performance | endurance | reliability | heat |
| fuel | efficiency | — | — | — |
Two things a pack deliberately cannot set:
- Engine architecture (V8/V10/V12) is not stored on the supplier — the game looks the name up in its world manufacturer pool. A name that pool does not know falls back to V10.
- Hidden tyre traits are seeded from the brand name and the season, so renaming a tyre maker rerolls them.
circuits and calendar
These two build a season. Circuits come from the Circuit Editor: the editor saves a .circuit file, and the Mod Editor imports it into the pack.
Each circuit carries an id, and a calendar round points at it with circuit_id (its name still works as a fallback).
circuits holds the track itself — its name, place, target length, visual preset, character, the drawn centre line, and the optional start/finish and pit markers. calendar holds the rounds, in order: each round is either a circuit round (naming one of your tracks) or an auto round (a generated track of a given character and length).
A pack carrying a calendar replaces the season with it. A pack carrying circuits but no calendar puts its tracks into the world pool and lets them take the season’s rounds.
Two fields are recorded for reference but not simulated: a circuit’s year, and its downforce / tire_wear / overtaking ratings — in play those three are derived from the track’s character.
images
name (the key rows point at) plus the PNG bytes. Logos are authored at 195×195; a custom car sprite is a side view facing right on a transparent background, 256 px tall. The game unpacks them to mods/.cache/ on first use.
What always stays generated
| Section | A pack overrides | The game keeps generating |
|---|---|---|
| Teams | Name, country, reputation, budget, behaviour profile, colours, car, livery, suit, logo | Budget evolution, roster behaviour, and (outside imposed) infrastructure |
| Drivers | Name, code, country, age, the six abilities and their ceilings, curves, traits, portrait, helmet, salary, fame | Ambition, season objectives, hidden personality |
| Sponsors | Name, sector, colour, budget, prominence, origin, logo | Contract terms, market behaviour |
| Suppliers | Name, category, colour, budget, size, origin, logo, technical stats, deal terms | Market behaviour, hidden tyre traits |
Traps worth knowing
- Bindings follow the id, not the name. A driver, sponsor or supplier points at a team’s stable
id, so renaming the team keeps them, and two teams may share a name (only the reading suffers — standings and news then show it twice). A pack written before ids existed, or hand-written with names only, still resolves by name and keeps the old behaviour: rename the team there and the driver joins the free-agent pool instead. Opening the pack in the Mod Editor once upgrades it. - Two drivers per team, one per seat. A team with two
primarydrivers leaves car 2 empty, and a car with no driver does not start. The editor’s Audit tab exists mostly for this. - Driver codes must be unique. A clash is renamed at load, silently, and everything keyed by code follows the rename.
- The pack file must stay where it is. Logos and custom car sprites are read from it; a career started with a pack that later disappears loses them.
- A pack applies at career creation only. Editing a pack never changes a career already under way — start a new one to see the change.