@echecs/tunx - v1.0.1
    Preparing search index...

    @echecs/tunx - v1.0.1

    @echecs/tunx

    Parse Swiss-Manager .TUNX binary tournament files. Zero dependencies, strict TypeScript. Output types align with @echecs/trf.

    npm install @echecs/tunx
    
    import { parse } from '@echecs/tunx';
    import { readFileSync } from 'node:fs';

    // Parse a TUNX file
    const buffer = new Uint8Array(readFileSync('tournament.TUNX'));
    const tournament = parse(buffer);

    if (tournament) {
    console.log(tournament.name); // "IV Elllobregat Open Chess Tmnt Grupo A"
    console.log(tournament.rounds); // 9
    console.log(tournament.players.length); // 210

    // Player data
    const player = tournament.players[0];
    console.log(player.name); // "Fedoseev, Vladimir"
    console.log(player.rating); // 2675
    console.log(player.fideId); // "24130737"
    console.log(player.title); // "GM"
    console.log(player.points); // 6.5
    console.log(player.rank); // 1
    }

    Decode a TUNX binary buffer into a Tournament object.

    function parse(
    input: Uint8Array,
    options?: ParseOptions,
    ): Tournament | undefined;
    • Returns undefined for unrecoverable failures (bad magic, missing markers).
    • Calls options.onError before returning undefined.
    • Calls options.onWarning for recoverable issues — parsing continues.
    • Never throws.
    interface ParseOptions {
    onError?: (error: ParseError) => void;
    onWarning?: (warning: ParseWarning) => void;
    }
    interface ParseError {
    message: string;
    offset?: number;
    }
    interface ParseWarning {
    message: string;
    offset?: number;
    }

    Output types are compatible with @echecs/trf.

    interface Tournament {
    // TRF-compatible fields
    chiefArbiter?: string;
    city?: string;
    deputyArbiters?: string[];
    endDate?: string;
    federation?: string;
    name?: string;
    numberOfPlayers?: number;
    players: Player[];
    roundDates?: string[];
    rounds: number;
    startDate?: string;
    tiebreaks?: string[];
    timeControl?: string;
    tournamentType?: string;

    // TUNX-specific extensions
    currentRound?: number;
    header?: Header;
    pairings?: Pairing[][];
    roundTimes?: string[];
    subtitle?: string;
    venue?: string;
    }
    interface Player {
    birthDate?: string;
    federation?: string;
    fideId?: string;
    name: string;
    nationalRatings?: NationalRating[];
    pairingNumber: number;
    points: number;
    rank: number;
    rating?: number;
    results: RoundResult[];
    sex?: Sex;
    title?: Title;
    }
    interface RoundResult {
    color: '-' | 'b' | 'w';
    opponentId: number | null;
    result: ResultCode;
    round: number;
    }

    Per-board pairing record, grouped by round in Tournament.pairings.

    interface Pairing {
    black: number;
    board: number;
    result?: ResultCode;
    white: number;
    }

    TUNX-specific header metadata. Available on Tournament.header.

    interface Header {
    installSignature: Uint8Array;
    installedAt?: Date;
    licenseHash: Uint8Array;
    savedAt?: Date;
    tournamentId: number;
    }
    interface NationalRating {
    birthDate?: string;
    classification?: string;
    federation: string;
    name?: string;
    nationalId?: string;
    origin?: string;
    pairingNumber: number;
    rating: number;
    sex?: Sex;
    }
    type ResultCode =
    | '+'
    | '-'
    | '0'
    | '1'
    | '='
    | 'D'
    | 'F'
    | 'H'
    | 'L'
    | 'U'
    | 'W'
    | 'Z';
    Code Meaning
    1 Win
    0 Loss
    = Draw
    + Forfeit win
    - Forfeit loss
    D Draw by forfeit
    F Full-point bye
    H Half-point bye
    L Loss by forfeit (special)
    W Win by forfeit (special)
    Z Zero-point bye / unpaired
    U Unplayed
    type Sex = 'm' | 'w';
    

    Known tiebreak identifiers used as values in Tournament.tiebreaks.

    type Tiebreak =
    | 'average-rating'
    | 'buchholz'
    | 'buchholz-cut-1'
    | 'buchholz-cut-2'
    | 'buchholz-cut-3'
    | 'direct-encounter'
    | 'koya'
    | 'median-buchholz'
    | 'number-of-wins'
    | 'performance-rating'
    | 'progressive'
    | 'sonneborn-berger';
    type Title = 'CM' | 'FM' | 'GM' | 'IM' | 'WCM' | 'WFM' | 'WGM' | 'WIM';
    

    TUNX is the proprietary binary format used by Swiss-Manager. The format uses little-endian integers and UTF-16LE strings with U16LE length prefixes.

    1. Header (108 bytes) — magic 93 FF 89 44, tournament ID, license data
    2. Metadata strings — name, subtitle, arbiters, city, time control
    3. Config section (95 FF 89 44) — rounds, players, dates, tiebreaks
    4. A3 sub-section (A3 FF 89 44) — per-round schedule (dates, times)
    5. Player records (A5 FF 89 44) — 30 strings + 110-byte numeric block
    6. Pairings (B3 FF 89 44) — 21-byte records per pairing
    7. D3 section (D3 FF 89 44) — section offset table
    8. E3 section (E3 FF 89 44) — file terminator

    MIT