Skip to Content
Game ImportersStar Wars: UnlimitedArchitecture Decision RecordsFoil Finish Detection via TCGCSV subTypeName

Foil Finish Detection via TCGCSV subTypeName

Date2026-04-07
StatusAccepted
Decision Makers@benjaminW78
AmendsTCGPlayer suffix-based parsing

Context and Problem Statement

Cards in early SWU sets (Spark of Rebellion, Shadows of the Galaxy, Twilight of the Republic) were missing their Foil finish on CardNexus — ~800 cards affected across 3 sets.

Root cause: TCGPlayer handles Foil differently between old and new sets:

BehaviorSetsTCGCSV structure
Legacy modelSOR, SHD, TWIONE productId per card, two CSV rows: subTypeName=Normal and subTypeName=Foil
Current modelJTL, LOF, SEC, LAWSeparate productId per finish, with (Foil) suffix in product name

The suffix-based parsing ADR chose to ignore subTypeName because it was believed to be “inconsistent across sets.” An exhaustive audit of all 27 SWU TCGCSV groups (9,847 CSV rows, 6,950 unique productIds) found this is not the case for SWU — subTypeName is consistently one of "Normal", "Foil", or empty (sealed/special products).

TCGCSV data (ProductsAndPrices.csv):

SetUnique PIDsCSV RowsNormalFoilEmpty
Spark of Rebellion5509855334511
Shadows of the Galaxy5469835284550
Twilight of the Republic5489855304541
Jump to Lightspeed1,1821,18260755817
Legends of the Force1,2031,20360558414
Secrets of Power1,2221,22259757253
A Lawless Time94594560832512

Early sets have more CSV rows than unique productIds because Normal and Foil are separate rows of the same product. Newer sets have 1:1 because Foil is a separate product.

Decision Drivers

  • TCGCSV is the source of truth — avoid depending on external APIs (SWU API hasFoil) for finish detection
  • subTypeName is consistently "Normal" / "Foil" / empty across all 27 SWU groups — verified exhaustively
  • Suffix-based parsing remains correct for newer sets — the fix must not break them
  • The base importer’s flattenCardsToPrintings already merges printings with different finishes into one product

Considered Options

  1. Use subTypeName as Foil fallback — when suffix parsing resolves to NORMAL but subTypeName is "Foil", override to FOIL
  2. Use SWU API hasFoil flag — extract hasFoil boolean from the SWU API to determine foil availability
  3. Hardcode early set codes — for SOR/SHD/TWI, always add Foil to Standard/Hyperspace cards

Decision Outcome

Chosen option: Use subTypeName as Foil fallback, because it keeps TCGCSV as the source of truth, requires no external API changes, and is self-detecting (no hardcoded set lists).

Implementation

Logic Flow

Impacted Files

FileChange
packages/games/game-importer/src/swu-importer.tsresolveVariantAndFinish() now accepts subTypeName as second parameter and handles Foil override internally

Key Code

const { variant, finish } = this.resolveVariantAndFinish(suffix, product.subTypeName)

The subTypeName override is handled inside resolveVariantAndFinish — single source of truth for finish resolution.

Consequences

Positive

  • Restores ~800 missing Foil finishes across SOR, SHD, TWI
  • TCGCSV remains the sole source of truth — no SWU API dependency for finish data
  • Self-detecting: works for any future set that uses the legacy or current model
  • No impact on newer sets — suffix parsing already produces the correct finish, and subTypeName agrees
  • Minimal code change (~3 lines)

Negative

  • Relies on TCGCSV maintaining consistent subTypeName values for SWU — if they change the format (e.g., to card types like “Unit”/“Leader” as seen in some other games), the fallback would need revisiting
  • For early sets, both finishes share the same TCGPlayer productId in externalIds — pricing relies on the price importer correctly matching subTypeName to finish

More Information

  • TCGPlayer suffix-based parsing ADR — the original decision to use suffix parsing; this ADR amends it by using subTypeName as a secondary signal
  • Variant/Finish model ADR — defines the variant + finish enum model
  • TCGCSV ProductsAndPrices.csv includes subTypeName on each row alongside product metadata and pricing
Last updated on