# Factory player paint linkage from installed PC data, r431

## Recovered selection

This supersedes the r357 player-selector/RGB boundary. The named effect
floats remain shader parameters; literal pigment lives in separate vehicle
part attributes. No retail runtime or XP GPU capture was used.

`GLOBALB.BUN` resource manager `0x80034602` at file `0x6e0a94` contains:

| Child | Payload file offset | Use |
| --- | --- | --- |
| `0x34604` | `0x70c54c` | 14-byte part descriptors |
| `0x3460c` | `0x6e9aec` | signed-word attribute index lists |
| `0x34605` | `0x6f54f8` | 8-byte attribute hash/value pairs |
| `0x3460a` | `0x6fe5e0` | descriptor group hashes |

The retail table at `0x802960 + 4*0x3f` maps logical category `0x3f` to
stored descriptor class byte `0x30`. `0x637040` initializes that category
using definition `+0x84c` when its argument is zero (`0x6370b6`) and
`+0x850` for a nonzero argument (`0x6370be`). Descriptor lookup `0x61b980`
returns a pointer stored at parent configuration `+0x68c` (`0x6370d6`).
`0x637190` explicitly supplies zero. This is a factory initialization path,
not evidence of the contents of a particular player's save. Saved category
values can overwrite defaults through `0x637270`, `0x503880` and generic
setter `0x631d20`; `0x6371e0` can choose a random available paint.

The `0x34600` definition CHUNK is at `0x6c5d00`; its aligned first RECORD is
`0x6c5d10`. 240SX record index 3 starts at `0x6c76c0`:

| Setup | Key field file offset | Descriptor key | Descriptor file offset | Selector/list offset | RGB | GLOSS | Effect |
| --- | --- | --- | --- | --- | --- | --- | --- |
| primary | `0x6c7f0c` | `0x98ee4397` | `0x71cba2` | 8650 / `0x6ede80` | 116,114,112 | 128 | `0x93c0eaf6` METPAINTSILVER |
| secondary | `0x6c7f10` | `0x3c6ef951` | `0x71c9c6` | 8412 / `0x6edca4` | 150,149,41 | 128 | `0xa177537d` METPAINTYELLOW |

Hash names reproduce exactly with initial `0xffffffff`, then `h=h*33+byte`:
RED=`0x0000d99a`, GREEN=`0x02ddc8f0`, BLUE=`0x00136707`,
GLOSS=`0x02daab07`, BRAND_NAME=`0xebb03e66`.
The brand values METAL=`0x03437a52` and GLOSS=`0x02daab07` correspond to
literal executable strings at `0x79aac0` and `0x79aab0`. `0x61b500` reads
the selected attribute's paired integer. Paint swatches also consume RGB,
but the following texture-generation path proves these are rendering inputs.

## Pigment reaches the generated body texture

1. `0x61c280` formats `DUMMY_SKIN%d` (literal at `0x7a8664`), hashes the
   name, and stores it at parent `+0x584` (`0x61c2cb`) when definition
   `+0x88c` is nonzero. It is 1 for every exported garage car. The integer
   argument identifies the destination slot, not the paint selector; e.g.
   `0x503a80` supplies its argument plus one, independently of its paint
   initialization argument passed to `0x637040`.
2. Texture preparation caller `0x633c1c` invokes `0x61d790` on the same
   parent. That routine resolves `+0x584` via `0x4901d0`, reads `+0x68c`
   (`0x61d7c0`) and fetches RED/GREEN/BLUE/GLOSS by hashing literal strings
   at `0x798534`, `0x798524`, `0x79852c`, `0x79aab0`.
3. `0x61d86e..0x61d87d` packs `(GLOSS<<24)|(BLUE<<16)|(GREEN<<8)|RED`.
   `0x611f10` locks the destination image and fills every pixel with this
   value at `0x612044..0x612079`, swapping R/B for D3D byte layout. Optional
   layer/vinyl composition follows. Thus stock pigment is a generated image
   fill, not a guessed RGB triplet from the effect floats or a missing atlas.
4. `0x631e20` copies nonzero parent `+0x584` into resource setup `+0x290`
   (`0x631f2e`). Render-owner construction copies this to slot `+0x6d8`
   (`0x6384e9`), alongside the geometry's generic car-skin slot registry.
   Separately `0x638d82..0x638d9e` resolves the descriptor's `0x6ba02c05`
   effect row via `0x61f700` and stores it at render-owner `+0x384`.
   Body draw `0x63065e..0x630670` replaces CARSKIN (`0xd6d6080a`) with that
   effect state before submission. These are the car resource/paint paths,
   independent of frontend swatch creation.

## Export and viewer scope

Run from the repository root:

```sh
python3 tools/export_nfsu2_stock_paint.py /tmp/nfsu2-xp-assets/GLOBAL/GLOBALB.BUN
```

The exporter writes [stock-player-paint-setups.json](stock-player-paint-setups.json),
29 cars with both factory setups, exact descriptor bytes/offsets, every
attribute pair, RGB, GLOSS, effect and brand. Primary examples:

| Car | RGB bytes / CSS swatch | Effect |
| --- | --- | --- |
| 240SX | 116,114,112 / `#747270` | METPAINTSILVER |
| 350Z | 145,63,16 / `#913f10` | METPAINTGOLD |
| Skyline | 8,60,161 / `#083ca1` | REGPAINTBLUE |
| RX7 | 20,43,38 / `#142b26` | REGPAINTGREEN |

The viewer now uses the primary source swatch when a car has no saved viewer
customization. Existing saved paint/finish/vinyl choices still override it.
The normal viewer color conversion is preserved. GLOSS is retained in the
export as texture alpha; it is **not** applied as transparent body opacity or
translated into a guessed clearcoat coefficient. The current preview finish
shader is unchanged. This change recovers stock pigment, not the complete
retail appearance: the 240SX stock pigment really is a silver/gray swatch.

Still unresolved: draw-time effect/technique flags, profile light selection,
normalization controls, environment cube binding, and a full reproduction of
the texture alpha/effect calculation and custom layers. The factory default
can be selected statically; a particular saved player configuration requires
its saved category values. Ambient key `0x253570af` remains a separate gap.

The source executable is `/tmp/nfsu2-SPEED2-from-xp.exe`, SHA-256
`f9dd86c054878ce6276beb07c1fd61874f7a1e4bf1f241b084c65b73e24168a7`.
