ZHS — Husqvarna Viking (newer) / Zeng Hsing

Reverse-engineered completamente da output reali di Artist Toolkit. Il mapping single-color è verificato: generare 028-B.vip con questa spec produce un file byte-identical a fixtures/028-B.zhs tranne una singola word di metadata a 0x83.

Non esiste alcuno ZHS writer open-source al mondo. pyembroidery / Ink-Stitch / libembroidery sanno leggere ZHS ma non scriverlo. Questa spec è il cuore del progetto — guardala con test di round-trip.

Header fisso (offset 0x00–0x85, single-color)

Tutti gli interi multi-byte sono little-endian salvo nota.

OffsetTipoSignificato
0x007 bytemagic ASCII "HSING12"
0x07uint32element count = nData + 1
0x0Buint32stitch count nStitches (record 0x02)
0x0Fuint32stitchStartOffset (155 / 0x9B)
0x13uint32headerStartOffset (134 / 0x86)
0x17uint32150 (0x96) — costante
0x1Cint16posX (max X)
0x1Eint16negX (min X)
0x22int16posY (max Y)
0x24int16negY (min Y)
0x68uint32stitch count (di nuovo)
0x6C/0x6Eint16×2firstX / firstY (delta del primo record)
0x74uint32140 (0x8C) — costante
0x7B/0x7Dint16×2lastX / lastY (posizione finale assoluta)
0x7Fuint16totalRecords − 2
0x83uint16UNKNOWN editor metadata (es: 486 vs 2) — vedi nota

Nota su 0x83

Tra i due campioni di riferimento questo campo vale 486 (001s-A) e 2 (028-B). Non correla con nessuna metrica del disegno (numero di punti, extent, somma byte, path, file size — tutte testate). Il reader ZHS non lo legge mai. Conclusione: è un valore interno dell'editor Artist Toolkit; lo si può mettere a 0. La cucitura fisica in macchina resta il test di accettazione finale.

Palette block (offset 0x86, single-color)

uint8   colorCount
colorCount × 3 byte   RGB, 24-bit BIG-ENDIAN
uint16  stringLength (LE)
bytes   thread-metadata string (UTF-8), length = stringLength
... padding zero fino a stitchStartOffset (0x9B)

La stringa è separata da &$/&# (chart, description, catalog). Per le lettere monogramma il blocco è costante:

01  34 8D 1A  08 00  "&$&#&#&%"

1 colore, RGB #348D1A, stringa di 8 byte &$&#&#&%, poi zero fino a 0x9B.

Stitch stream (offset 0x9B)

Lista piatta di record da 3 byte: [ctrl, b1, b2].

Control byte

ctrlmeaning
0x01MOVE / jump-in
0x02STITCH
0x04COLOR CHANGE
0x10CHECKSUM (vedi sotto)
0x80END
0x41visto dal reader come "unmapped" — significato ignoto
0x88?TRIM — ignoto, nessun sample. GAP aperto.

Packing delle coordinate (b1, b2)

dx e dy sono delta signed8 con i bit interleaved tra b1 e b2:

x.bit0 <- b1.bit0   x.bit1 <- b2.bit1   x.bit2 <- b1.bit2   x.bit3 <- b2.bit3
x.bit4 <- b1.bit4   x.bit5 <- b2.bit5   x.bit6 <- b1.bit6   x.bit7 <- b2.bit7
y.bit0 <- b2.bit0   y.bit1 <- b1.bit1   y.bit2 <- b2.bit2   y.bit3 <- b1.bit3
y.bit4 <- b2.bit4   y.bit5 <- b1.bit5   y.bit6 <- b2.bit6   y.bit7 <- b1.bit7

Dopo extraction, i valori sono signed8 con un range-extension tweak su decode: if v>=63: v+=1 e if v<=-63: v-=1. Su encode si inverte: if v>=64: store v-1 e if v<=-64: store v+1.

±63 hole: un delta di esattamente +63 o −63 non è rappresentabile (63 memorizzato decodifica come 64). Range decodificabile −129..128 meno ±63. Nessun fixture contiene tali delta (max |delta| = 32). Il writer TS calcola i delta contro la posizione decoded, così un ±63 non rappresentabile sfuma a uno scarto di 0,1 mm su quel singolo punto (con warning strutturata) invece di shiftare tutto il disegno.

Segno Y: il reader emette stitch(x, -y), o quindi lo stored y coincide con il VIP y. Convertendo VIP→ZHS si copiano i delta VIP direttamente — nessun flip di segno necessario.

Record di checksum

Dopo ogni 84 record dati, inserire un record di checksum:

[0x10, sum & 0xFF, (sum >> 8) & 0xFF]   // somma 16-bit LE di tutti i byte
                                          // (ctrl+b1+b2) del blocco di 84 record

Fine file

Dopo l'ultimo record dati: emettere END [0x80, 0, 0], poi un record di checksum finale sul blocco che contiene l'END.

VIP → ZHS record mapping (single-color, verificato)

  1. Primo record VIP → ZHS MOVE [0x01] con lo stesso (dx,dy).
  2. Subito dopo, una tie-in duplicate stitch [0x02] (0,0).
  3. Ogni stitch VIP successivo → ZHS STITCH [0x02] con (dx,dy).
  4. Drop del record END VIP; emettere ZHS END + checksum finale.

Vedi reference/zhs_vip_reference.py per l'implementazione eseguibile e testata.

Layout multicolore (campioni di fabbrica)

Decodificato da un campione reale di fabbrica a 6 colori / 10 blocchi / 9302 punti con gemello .pes. L'header “single-color” di §1 è in realtà il caso speciale a 1 blocco di una tabella per-blocco di righe da 20 byte. Vedi docs/ZHS_FORMAT.md §5 per i dettagli per-blocco (offsets +2/+9/+10/+14/+16/+18, terminator row, palette block con un'entry "&$chart&#desc&#cat&%" per blocco — non per colore unico), il record COLOR_CHANGE (0x04) il cui payload è palette index del blocco successivo (dy=0), e le aperture dei blocchi (MOVE run + tie-in stitch).

Tuttora ignoto

← VIP PES / PEC →