Files
prep-step/docs/formato-prt-feature.md
davideandClaude Sonnet 5.5 1eac3ccaa7 Documenta posizioni, fori e facce dei .prt (9239902104)
Matrici 4x3 di fori e feature (regola degli zeri corretta), foro a punti
di schizzo con facce piane di Estrusione 1, profilo a L, quote per feature
e tipi 928/1104/1052. Corregge l'ipotesi errata sul codice 12266.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 16:21:18 +02:00

479 lines
27 KiB
Markdown

# Decodifica del `.prt` Creo: l'albero delle feature
Come leggere da un `.prt` (o dalla parte "feature" di un `.asm`) l'**albero delle feature** che
Creo mostra nel model tree: quali feature ci sono, in che ordine, di che tipo e con che nome.
Complementare a [`formato-asm-record.md`](formato-asm-record.md), che decodifica la sezione
`members` dei `.asm`: la serializzazione a marcatori e la codifica degli interi sono le stesse e
qui non si ripetono (§2 e §2.1 di quel doc).
**Stato: ricavato per reverse engineering, nessun codice nel progetto lo usa ancora.** Non c'è un
`PrtFeatureReader` accanto ad `AsmRecordReader`; la decodifica qui sotto è stata fatta con script
di prova usa-e-getta. Livelli di prova, da non confondere (come in `formato-asm-record.md`):
1. **Contro il model tree reale di Creo** (la prova forte): un solo file, `test.prt.1` (Creo
9.0.3.0, 11 voci nel tree più la radice, schermata di riferimento). L'albero ricostruito
dal file coincide voce per voce, per ordine, nome e tipo (§6).
2. **Sondaggio su `raw/`** (89 file `.prt`/`.asm` reali): solo conteggi e coerenza interna, **non**
contro il model tree. Serve a dire dove la decodifica regge e dove no (§7), non a provarla.
## 1. Il contenitore: header, indice delle sezioni, sezioni
Il file è un contenitore "UGC":
```
#UGC:2 PART 2038 1760 805 1 1 15 4000 2022414 000002cc \ <- tipo (PART/ASSEMBLY) e numeri
#- VERS ... #- UOBJ_ID ... #- CMNM 008test.prt <- campi a righe fisse, chiusi da '\'
#-END_OF_UGC_HEADER
#Creo TM 9.0 (c) 2026 by PTC Inc. ... 9.0.3.0 <- versione di Creo che ha salvato
#UGC_TOC 2 32 81 17####... <- indice delle sezioni
ND:0:Model_L05_PX:1 ab4 422 541 2038 a 1 b396 68b0####
SolidPersistTable 13af 878 e84 2038 a 1 a248 3b21####
...
BasicData 201c 15761 15756 2038 9 -1 e1de afe3####
```
Il secondo numero dopo `PART`/`ASSEMBLY` (`2038`, `2063`, `1967`, `1376`, `1322`, `1632`...)
distingue la **generazione del formato** (§1.2 e §7: da lei dipende se le sezioni sono leggibili).
### 1.1 Come trovare una sezione (qualsiasi generazione)
Ogni riga dell'indice è `nome offset(hex) lunghezza(hex) <campi>`, con i campi, in ordine:
`<dimensione decompressa(hex), oppure 1> <generazione> <tipo(hex)> <metodo> <crc1> <crc2>`. Il
`<metodo>` vale `-1` per le sezioni non compresse.
**Gli offset dell'indice non sono offset di file**: sono relativi a un'origine spostata di una
costante `K` che cambia da file a file (727 in `test.prt.1`, 1563 in `1701010009.prt.1`). Regola
ricavata e verificata, perché le sezioni sono contigue fino alla fine del file:
```
K = dimensioneFile - (offsetUltimaSezione + lunghezzaUltimaSezione) - 1
inizio sezione = K + offset
```
Verifica: con questa regola le sezioni non compresse cominciano esattamente con
`e0 00 Sld_...` (es. `Sld_BasicData`, `Sld_Features`) in tutti i file dove l'ho provata
(controllato su `AllFeatur`: 86 file su 89; i 3 che non tornano sono `.prt` del 2038 con `AllFeatur` vuoto, non ho indagato).
Nei file recenti il corpo è inoltre preceduto da una riga marcatore (`#`, a capo, `#<Nome>`, a capo),
che è un secondo modo di trovare la sezione (equivalente: l'inizio è la fine del marcatore).
```
#
#AllFeatur
<corpo della sezione> #
#BasFullData
...
```
### 1.2 Come si leggono i contenuti: tre modi di conservarli
Il `<tipo>` dell'indice dice come la sezione è stata scritta. Sul corpus di `raw/` (89 file):
| Tipo | Come è conservata | Dove | Decodifica |
|---|---|---|---|
| `9` | **in chiaro**: formato a marcatori, comincia con `e0 00 Sld_<nome>` | quasi tutte le sezioni nei file `1632`, `1967`, `2038`, `2063`; le sezioni "leggere" (`BasicData`, `LargeText`...) anche in quelli vecchi | si legge direttamente |
| `a`, `10`, `12`, `18` (metodo `1`/`2`) | compressa **Unix `compress` (`.Z`, LZW)**: dopo la riga del nome c'è la firma `1f 9d 10` | `ND:0:Model_*`, `SolidPersistTable`, `SolidPrimdata`, `DispDataTable` | decodificabile con un LZW standard (verificato: `gzip -dc` apre lo stream così com'è) |
| `11` | **non decodificata**: i primi 4 byte sono in chiaro (`e0 00` + le prime due lettere del nome, es. `Sl`), il resto ha entropia massima | `AllFeatur`, `VisibGeom`, `NovisGeom`, `IndentTag`, `FeatRefData` nei file `1322`, `1375`, `1376` | **da risolvere**, vedi §1.3 |
Le sezioni `.Z` non servono per le feature, ma una vale la pena di conoscere: `ND:0:Model_*`
decompressa è un documento XML (SOAP) con **nome del modello, tipo (`prt`/`asm`), numero di
revisione, e i parametri** (`ParamSet`). È un modo alternativo di leggere i parametri di un
file senza passare dal formato a marcatori. Non ho decodificato oltre il nome e la revisione.
### 1.3 Il blocco: le sezioni di tipo `11` (file 1322/1375/1376)
È il limite principale oggi: **54 dei 89 file** (tutti quelli con generazione `1322`, `1375` e
`1376`, cioè salvati da Pro/ENGINEER Wildfire 2-4) hanno `AllFeatur` in tipo `11`. Sono illeggibili
con quello che ho: in quei file l'albero delle feature, e la geometria, non si possono ricavare.
Fa eccezione solo ciò che sta nelle sezioni di tipo `9` (es. la sezione `members` degli
assiemi, che è la ragione per cui `AsmRecordReader` funziona anche su file vecchi).
Quello che ho escluso, per non rifarlo:
- **Non è un XOR a chiave fissa**: noto il testo in chiaro dei primi ~40 byte
(`Sld_Featuresãàfirst_feat_ptr...`, identico in tutti i file) e sceso il flusso
cifrante (cifrato XOR chiaro), questo **cambia da file a file**.
- **Non ha periodicità e non ripete sequenze**: indice di coincidenza = 1/256 a ogni periodo
provato (1-39), nessun gruppo di 4 byte ripetuto in 3829 byte. Statisticamente è compresso
bene o cifrato.
- **Non è zlib/deflate** (provato a ogni offset 0-11, formati zlib, raw, gzip) **né LZW `.Z`**
(provato a ogni offset 0-8, con 10 combinazioni di flag, via `gzip -dc`).
- Non dipende dai `crc` della riga dell'indice in modo che io abbia visto.
I 4 file in generazione `1632` hanno gli stessi dati in chiaro (tipo `9`): quindi la cifratura/
compressione è una caratteristica della generazione del programma, non del contenuto, e il modo
più semplice per leggere un file vecchio è **riaprirlo in Creo e risalvarlo** (diventa tipo `9`).
Possibili strade se serve leggere i vecchi senza Creo: confrontare lo stesso modello salvato da
Wildfire 4 e da Creo 9 (stesso contenuto, due forme) per avere coppie chiaro/cifrato, oppure
riaprire in Creo un file `1376` e risalvarlo con la stessa revisione.
Le sezioni sono lo stesso formato a marcatori dei `.asm` (`\xe0 <tipo> <nome campo>\0 <valore>`,
array `\xf8`, schemi `\xf7`...). Valore in più rispetto ai `.asm`: `\xeb` introduce un intero a 4
byte big-endian (osservato in `time`, `random_num`).
Le sezioni che servono per le feature:
| Sezione | Cosa contiene | Serve per |
|---|---|---|
| `AllFeatur` | la lista **ordinata** di tutte le feature, con id e tipo | ordine, tipo, esistenza |
| `MdlStatus` | lo storico delle modifiche; contiene i record di creazione con il nome | nome, icona, feature precedente |
| `MdlRefInfo` / `ActDatums` | geometria di riferimento dei piani e del sistema di assi | nome del sistema di assi (`PRT_CSYS_DEF`) |
## 2. Le feature: `AllFeatur`
`AllFeatur` è una **lista concatenata** (`first_feat_ptr`, poi `next_feat_ptr` in ogni record):
un record per feature, nell'ordine di rigenerazione. Il primo record è scritto con i nomi di
campo per esteso, quelli dopo sono posizionali (compressione per schema, `formato-asm-record.md`
§2.2).
I campi che contano, per ogni record:
| Campo | Significato | Dove si trova |
|---|---|---|
| `id` | identificatore della feature nel modello (intero §2.1) | all'inizio del record |
| `feat_type_ptr.type` | **tipo** di feature (§4) | `\xe3 \xf6 <2 byte>` |
| `sect_name` | nome, solo per alcuni tipi (piani di riferimento) | stringa `\0` verso la fine del record |
| `attributes` | flag della feature (§3) | subito dopo il tipo |
Come si individua il record nei byte, perché non serve decodificare tutto lo schema:
```
primo record: ... \x01id\0 <id> ... \x01table_id\0 \xf6 \xe0\x01type\0 <tipo 2 byte> ...
secondo record: \xf7 <schema> <id> \xeb ... \xe3 \xf6 <tipo 2 byte> ...
record successivi:\xe3 <id> \xeb <4 byte> ... \xe3 \xf6 <tipo 2 byte> ...
```
- `\xe3 \xf6 <b0 b1>` con `b0` in `0x80`-`0xbf` è l'àncora: `<b0 b1>` è il tipo come intero a 2
byte (§2.1: `(b0 & 0x3f) << 8 | b1`).
- L'`id` è l'intero che precede, nei 70 byte prima, il pattern `<id> \xeb`.
- Esempio dal campione, il record di `DTM1`: `e3 80 93 eb 04 00 10 01 00 e6 | e3 f6 83 9b | ...`
cioè `id = 0x93 = 147` (due byte: `0x80 0x93`) e `tipo = 0x839b = 923`.
Nel campione la lista contiene **13** record, ma il model tree ne mostra **10** più la radice:
| id | tipo | in `AllFeatur` | nel tree di Creo |
|---|---|---|---|
| 1 | 923 | RIGHT | RIGHT |
| 3 | 923 | TOP | TOP |
| 5 | 923 | FRONT | FRONT |
| 7 | 979 | (sistema di assi) | PRT_CSYS_DEF |
| 74 | 923 | piano interno | — |
| 73 | 949 | schizzo | Sketch 1 |
| 80 | 923 | piano interno | — |
| 79 | 917 | protrusione | Estrusione 1 |
| 100 | 911 | foro | Foro 1 |
| 125 | 913 | raccordo | Arrotondamento 1 |
| 147 | 923 | piano | DTM1 |
| 150 | 923 | piano interno | — |
| 149 | 917 | protrusione | Estrusione 2 |
| 188 | 914 | smusso | Smusso 1 |
Le tre righe senza voce nel tree sono **piani di riferimento interni**: Creo li crea da sé quando
si apre uno schizzo o un'estrusione e non li mostra. Vanno scartati, e il file lo dice (§3).
## 3. Quali feature sono nel tree: i piani interni
I piani interni (`74`, `80`, `150`) si distinguono perché il blocco `attributes` subito dopo il
tipo ha una forma diversa da quella di un piano che l'utente vede:
```
piano visibile (id 5, 147): e3 f6 83 9b | e1 00 00 e7 07 10 00 e5 00 ...
piano interno (id 74,80,150): e3 f6 83 9b | e1 00 03 00 e4 eb 40 00 00 00 00 e7 07 00 ...
```
Regola usata: il record è interno se entro 10 byte dal tipo compare la sequenza
`03 00 e4 eb 40`. Non ho decodificato i singoli bit di `attributes`: la regola è un riconoscitore
del *pattern*, non la lettura di un flag con nome.
Prova (livello 2, vale solo dove esistono i nomi di §5 e dove l'`id` è stato letto, vedi §7): sugli
11 file con record di creazione, i piani con quel pattern sono **13**, e **nessuno** ha un nome
nello storico; i piani senza il pattern e con un nome sono 7. Nessuna eccezione, ma sono pochi file.
## 4. Il tipo di feature
`feat_type_ptr.type` è un intero. I valori incontrati, con l'icona che lo storico (§5) associa
alle feature di quel tipo:
| Tipo | Icona (`icon_name`) | Significato | Verificato su |
|---|---|---|---|
| 911 | `feathole` | Foro | `test.prt.1` (Foro 1) |
| 913 | `featround` | Raccordo (Arrotondamento) | `test.prt.1` |
| 914 | `featchamfer` | Smusso | `test.prt.1` |
| 916 | `cutextrude`, `cutrevolve` | Taglio (estrusione o rivoluzione) | sondaggio `raw/` |
| 917 | `protextrude`, `protrevolve` | Protrusione (estrusione o rivoluzione) | `test.prt.1` (estrusione) |
| 923 | `dtmplane` | Piano di riferimento | `test.prt.1` |
| 926 | `dtmaxis` | Asse di riferimento | sondaggio `raw/` |
| 931 | `dtmpoint` | Punto di riferimento | sondaggio `raw/` |
| 949 | `dtmsktcurve`, `dtmcurve` | Schizzo / curva | `test.prt.1` (schizzo) |
| 979 | `dtmcoordsys` | Sistema di assi | `test.prt.1` |
| 1052 | `section_planar16X16` | Sezione | sondaggio `raw/` |
| 1118 | `def_compintfc` | Interfaccia di componente | sondaggio `raw/` |
| 1000 | — | Componente di assieme (nei `.asm`) | sondaggio `raw/`, nessun nome |
| 928 | — | Guscio | `9239901907` (Guscio 1, confermato dal tree) |
| 1104 | — | Gruppo (`LOCAL_GROUP`) o copia di gruppo | `9239901907` (Gruppo VITI), §9.2 |
| 1052 | — | Sezione (cartella "Sezioni") | `9239901907` (A-D), §9.3 |
Altri tipi compaiono in `AllFeatur` senza nessun nome nello storico, quindi non so cosa siano:
`214`, `258`, `932`, `1070`, `1104`, `1232`, `1238`. Vanno trattati come "feature di tipo N", non
inventati.
> **Il tipo non distingue estrusione da rivoluzione.** `917` vale per `protextrude` e per
> `protrevolve`, `916` per `cutextrude` e `cutrevolve`: la differenza sta solo nell'icona dello
> storico (§5). Senza quel record non si può dire se la protrusione è un'estrusione. Lo stesso
> vale per tutti i tipi: la tabella è "tipo → famiglia", non "tipo → nome del comando".
I valori coincidono con quelli che ricordo delle costanti `PRO_FEAT_*` di Creo Toolkit
(es. `PRO_FEAT_HOLE`=911), ma **non l'ho verificato su una fonte**: la tabella sopra poggia solo
sui file.
## 5. I nomi e l'ordine del tree: lo storico in `MdlStatus`
`AllFeatur` dà ordine e tipo, **non il nome**. Il nome che Creo mostra ("Estrusione 1", "Foro 1")
sta nello storico delle modifiche, sezione `MdlStatus`, in un record di creazione per ogni
feature creata dall'utente:
```
\xe0\x00 typed_data(MTTyped_CreateData) \0 \xe3
\xe0\x00 created_features \0 \xf8 <n> \xf7 <schema> \xfb \xe3
\xe0\x01 feat_id \0 <id>
\xe0\x01 ft_type \0 <tipo>
\xe0\x01 comp_type \0 <0>
\xe0\x01 prev_feat_id \0 <id della feature precedente nel tree>
\xe0\x0a feat_name \0 <nome>\0
\xe0\x01 pat_group_header_id \0 ...
\xe0\x01 is_header \0 ...
\xe0\x03 icon_name \0 <icona>\0
```
Il primo record porta i nomi di campo; i successivi sono posizionali e si riconoscono dalla
forma `<id> <tipo 2 byte> <comp> <prev> <nome>\0 <pat> \0 <icona>\0`. Dal campione, tutti e 7:
| `feat_id` | `ft_type` | `prev_feat_id` | `feat_name` | `icon_name` |
|---|---|---|---|---|
| 73 | 949 | 7 | Sketch 1 | `dtmsktcurve` |
| 79 | 917 | 73 | Estrusione 1 | `protextrude` |
| 100 | 911 | 79 | Foro 1 | `feathole` |
| 125 | 913 | 100 | Arrotondamento 1 | `featround` |
| 147 | 923 | 125 | DTM1 | `dtmplane` |
| 149 | 917 | 147 | Estrusione 2 | `protextrude` |
| 188 | 914 | 149 | Smusso 1 | `featchamfer` |
Due cose da notare:
- **`prev_feat_id` è la catena del tree.** Salta i piani interni: `73` punta a `7`, non a `74`,
che pure lo precede in `AllFeatur`. Seguire `prev_feat_id` dà quindi già l'ordine delle sole
feature visibili.
- **I nomi sono nella lingua della sessione che ha creato la feature** ("Estrusione", non
"Extrude"): `test.prt.1` è stato fatto con Creo in italiano. Non si può dedurre il nome dal
tipo da una tabella fissa.
I nomi di default sono eccezioni, e stanno altrove:
- **RIGHT, TOP, FRONT, DTM1**: nel record di `AllFeatur`, campo `sect_name` (stringa subito dopo
`attributes`). DTM1 è anche nello storico.
- **PRT_CSYS_DEF** (id 7): in `MdlRefInfo`, nella voce `ent_list(coord_sys)` con `feat_id` 7, e
in `GeomDepen`. Non compare in `AllFeatur`.
## 6. Algoritmo, con la prova sul campione
1. Leggere il file come Latin1 (come in `formato-asm-record.md` §2) e ritagliare le sezioni
`AllFeatur` e `MdlStatus` con i marcatori `#\n#<Nome>\n` (§1).
2. Da `AllFeatur`: elenco `(id, tipo, nome-di-default)` in ordine; scartare i record con il
pattern interno (§3).
3. Da `MdlStatus`: mappa `id → (nome, icona, prev)` dai record `MTTyped_CreateData` (§5).
4. Per ogni feature restante: nome = nome dello storico se c'è, altrimenti `sect_name`,
altrimenti il nome dell'asse in `MdlRefInfo` (solo `PRT_CSYS_DEF`). Se non c'è nessuno dei tre,
mostrare "feature tipo N" e non inventare il nome (vedi §7).
5. Ordine: quello di `AllFeatur` (equivalente a seguire `prev_feat_id`, §5).
Risultato su `test.prt.1`, confrontato con la schermata del model tree:
```
TEST.PRT
RIGHT (1) sect_name
TOP (3) sect_name
FRONT (5) sect_name
PRT_CSYS_DEF (7) MdlRefInfo
Sketch 1 (73) storico, 949 dtmsktcurve <- grigio nel tree: nascosto
Estrusione 1 (79) storico, 917 protextrude
Foro 1 (100) storico, 911 feathole
Arrotondamento 1 (125) storico, 913 featround
DTM1 (147) storico + sect_name, 923 dtmplane
Estrusione 2 (149) storico, 917 protextrude
Smusso 1 (188) storico, 914 featchamfer
```
Identico al tree di Creo per ordine, nomi e numero di voci (11, più la radice `TEST.PRT`). I
piani interni 74, 80 e 150 sono correttamente assenti.
**Non decodificato, e che la schermata mostra:**
- **Sketch 1 in grigio** (nascosto/non visualizzato): lo stato di visibilità non l'ho ricavato dal
file. Non è in `attributes` di `AllFeatur` in una forma che abbia riconosciuto; probabilmente sta
nello stato dei livelli o in `DispCntrl`. Va cercato confrontando due salvataggi dello stesso
modello con lo schizzo nascosto/visibile.
- **La freccia di espansione** di Estrusione 1 e 2 (le sotto-voci: la sezione dello schizzo
interno): i figli di una feature non li ho decodificati.
- **Soppressione, feature fuori ordine, pattern, gruppi:** non presenti nel campione, quindi mai
osservati (`pat_group_header_id` e `is_header` sono letti e ignorati).
## 7. Dove la decodifica NON regge
Sondaggio sui 89 file di `raw/` (livello 2, nessun model tree di riferimento). Per generazione
del formato (secondo numero dopo `PART`/`ASSEMBLY`) e modo di conservare `AllFeatur` (§1.2):
| Generazione | File | `AllFeatur` | Leggibile oggi |
|---|---|---|---|
| `2038`, `2063`, `1967` (Creo) | 31 | tipo `9`, in chiaro (3 con sezione vuota) | sì |
| `1632` | 4 | tipo `9`, in chiaro | sì (ma con una sola feature, tipo 932: nessun albero reale) |
| `1322`, `1375`, `1376` (Wildfire) | 54 | tipo `11` | **no** (§1.3) |
I 31 recenti sono quelli con i marcatori di sezione; con la regola di §1.1 diventano
leggibili anche i `1632` e si possono ritrovare le sezioni senza marcatori.
Dei 31 leggibili, solo **10 hanno record di creazione con nome** in `MdlStatus` (`MTTyped_CreateData`,
introdotto con le versioni recenti di Creo): gli altri 21 hanno `AllFeatur` ma **nessun nome**
nel file. Fra questi ci sono parti con una vera storia di modellazione (es. `9251200385.prt.3`:
schizzo, estrusioni, smusso...). Per loro ordine e tipo si leggono, il nome no, e il tipo non
basta a ricostruirlo (§4: estrusione e rivoluzione sono lo stesso tipo, e la numerazione "…1,
…2" non è una proprietà dei tipi). **Correzione (§9):** i nomi si possono **calcolare** (famiglia + contatore, §9.1) e riproducono il
tree di Creo anche senza lo storico; restano da leggere dallo storico solo i nomi non calcolabili
(assi, punti, rivoluzioni). Quanto scritto sopra vale quindi solo per quei nomi.
Altre cautele:
- Nei `.asm` `AllFeatur` elenca anche i componenti (tipo 1000) ma i nomi di quei componenti non
stanno nello storico: il modo giusto di avere i componenti resta `members`
(`formato-asm-record.md` §3).
- Con più record di creazione per lo stesso `feat_id` (feature rigenerate o modificate) non so
quale prevalga: sul campione ce n'è sempre uno solo per feature.
- **L'`id` letto da `AllFeatur` non è affidabile fuori dal campione.** Su 11 file con storico (i 10 di `raw/` più `test.prt.1`), in 7
lo storico cita `feat_id` che nel mio elenco di `AllFeatur` non ci sono (es. `4503220100_c03.prt.3`,
`9252000507.prt.11`). Nei file dove ho controllato, in `AllFeatur` il record c'è ma il pattern
con cui estraggo l'`id` (§2) non lo trova: il layout del record cambia (altro schema, o altri
campi prima). Quindi il collegamento `id` → nome di §5 è provato **solo su `test.prt.1`** e va
rifatto leggendo il record con il suo schema, non con l'àncora a pattern.
- `prev_feat_id` punta a volte a id che non sono in nessuno dei due elenchi (stessi 7 file): stessa
causa probabile, non verificata. Per l'ordine ci si fida di `AllFeatur`, non della catena.
- Un file su 11 ha due record di creazione con lo stesso `feat_id`; non so quale valga.
- La regola del piano interno (§3) è un pattern di byte validato su pochi file recenti.
## 8. Cosa serve per renderlo codice
Per portare tutto questo in `PrepStep/` (non fatto):
1. Un `PrtFeatureReader` accanto ad `AsmRecordReader`, che riusi la lettura Latin1 e `AsInt`, con
le stesse regole di sicurezza: se `AllFeatur` manca, o i record trovati non tornano con il
conteggio dichiarato (`f8 <n>` dell'array delle feature), lanciare `InvalidDataException` invece
di restituire un tree parziale.
2. Verificare i punti aperti di §6 e §7 con **altri model tree reali**, uno per caso: schizzo
nascosto/visibile, feature soppressa, feature rinominata a mano, pattern, un file della
generazione vecchia. Un campione solo non basta per fidarsi.
3. Per i file senza storico decidere cosa mostrare (§7): è una scelta di prodotto, non tecnica.
Quote, coordinate degli schizzi e cosa manca per ricostruire il solido: [`formato-prt-geometria.md`](formato-prt-geometria.md).
## 9. Ricostruire l'albero identico a quello di Creo
Verificato su `9239901907.prt.19` (63 record in `AllFeatur`, un albero con gruppo, guscio, sezioni:
~40 voci): l'algoritmo sotto riproduce **voce per voce, nell'ordine e con gli stessi nomi** il model
tree mostrato da Creo (RIGHT, TOP, FRONT, PRT_CSYS_DEF, Sketch 1, Estrusione 1, Guscio 1,
Sketch 2 ... Estrusione 6, Gruppo VITI con 7 voci dentro, DTM1, DTM2, Sketch 10, Estrusione 10, e
le sezioni A, B, C, D). Riproduce anche `test.prt.1` (§6). Lo storico (`MdlStatus`, §5) **non serve
per i nomi**: per quel file ne copriva solo 7 su ~40, e coincide al 100% con i nomi generati.
### 9.1 I nomi si calcolano: un contatore per famiglia
Il nome di una feature non è scritto nel file (salvo i pochi dello storico): è la **famiglia** più
un **contatore** che cresce nell'ordine di `AllFeatur`, contando *tutte* le feature della famiglia,
visibili o nascoste:
| Tipo (§4) | Famiglia, nome | Nota |
|---|---|---|
| 949 | `Sketch n` | tutti gli schizzi, anche quelli nascosti |
| 917 e 916 | `Estrusione n` | **protrusioni e tagli condividono un unico contatore**: 6 protrusioni e 4 tagli arrivano a "Estrusione 10" |
| 914 | `Smusso n` | |
| 913 | `Arrotondamento n` | |
| 928 | `Guscio n` | scoperto qui: 928 è il guscio |
| 911 | `Foro n` | |
| 923 non interno | nome nel record (`sect_name`: RIGHT, TOP, FRONT, DTM1...) | |
| 979 | `PRT_CSYS_DEF` | |
| 1104 | gruppo, vedi §9.2 | |
Prova interna: i 7 nomi dello storico di `9239901907` (Foro 1, Sketch 9, Estrusione 8, DTM1, DTM2,
Sketch 10, Estrusione 10) coincidono con quelli generati, e fra loro "Sketch 9" e "Estrusione 8" si
trovano solo se il contatore conta anche le feature senza nome.
### 9.2 Gruppi: `LOCAL_GROUP`, tipo 1104
Un gruppo è una feature di tipo 1104 in `AllFeatur`. Il suo nome sta in `LargeText`, tabella
`names_table`, voce con lo stesso `id` e `type 3`:
```
id <id del gruppo> àtype  à
name VITI
```
e il numero dei membri in `FeatDefs`, record `feat_defs_1104`, campo `index_count` (7 per VITI).
I membri sono le **7 feature visibili che seguono l'intestazione** in `AllFeatur`, nello stesso
ordine: `Sketch 7`, `Estrusione 7`, `Sketch 8`, `Foro 1`, `Sketch 9`, `Estrusione 8`,
`Estrusione 9`. Il nome mostrato è "Gruppo " + nome.
Un secondo record 1104 (`COPIED_GROUP`, id 1326) sta in mezzo ai membri: è un marcatore di copia,
non una voce dell'albero, e non va mostrato né contato fra i 7.
### 9.3 Le sezioni ("Sezioni" in fondo)
Le voci di tipo 1052 in `AllFeatur` sono le sezioni (`Sezioni` → A, B, C, D), **non** si
mostrano nel corpo dell'albero ma in una cartella a parte. I nomi stanno nella sezione
`Xsections`, lista `mdl_xsec_list`: ogni voce ha un `leader_id` e, subito dopo, il nome (`D`;
nelle voci posizionali è `<leader_id 2 byte><nome>`). Ordinate per id crescente danno A, B, C,
D, come in Creo.
### 9.4 L'`id` della feature (correzione di §2)
L'ancora che ho dato in §2 per l'`id` (`<id> ë ...`) vale per i file che hanno il primo
elemento degli attributi come intero a 4 byte (`eb 04 00 10 01`). In altri (`9252000507.prt.11`)
quel campo è `90 01 00` e l'`id` è seguito da quello: l'espressione corretta è
`(id | ÷<schema> | ã) <id> (ë | )`. È la causa dei "7 file su 11 con id
non letti" del §7, che con questa regola tornano leggibili.
### 9.5 Verifica sul corpus
Confronto fra i nomi generati e quelli dello storico, sui file leggibili (29 voci confrontabili):
| Esito | Voci | Spiegazione |
|---|---|---|
| Uguali | 18 | |
| Diversi: rivoluzione letta come estrusione | 2 | `Rivoluzione 1` (tipo 917) esce `Estrusione 1`: vedi sotto |
| Diversi: assi (`A_3`...) | 1 | nome non calcolato (contatore degli assi con i loro id interni) |
| Diversi: piani e sistemi di assi con nome non standard | 8 | `ADTM1`, `ASM_RIGHT`, `CS0`, `DTM2`...: file con un'altra *template*, il nome del piano va letto dal record con più cura (la mia lettura di `sect_name` guarda in avanti e può prendere quello del record successivo) |
| File non letti: primo record con layout diverso | 19 di 33 | assiemi e parti senza i campi del primo record come nei campioni |
I 58 file Wildfire restano fuori (§1.3).
### 9.6 Cosa non riproduco ancora
- **Rivoluzione e estrusione**: stesso tipo (917/916). Nell'unico caso con rivoluzione disponibile
(`9252000507`), il primo `type` delle istruzioni di `feat_defs_917` è 5209 (`0x1459`), mentre nelle
estrusioni è 1851, 1853 o 1854. È un indizio, **non provato** (un solo esempio).
- **Schizzi in grigio** (nascosti): in Creo `Sketch 1-6` e `Sketch 10` sono grigi. Tutti i record
di schizzo (tipo 949) di `AllFeatur` hanno gli stessi byte di attributi (eccetto `Sketch 9`, con
un flag `0x20` in più che compare anche su `Estrusione 8` e `9`, i membri del gruppo copiato): lo
stato "nascosto" non sta lì. Non ho trovato dove sia, e non ho un esempio con uno schizzo
*visibile* da confrontare.
- **Elementi progetto** (cartella in testa al tree in `9252000507`): corrispondono, probabilmente,
alle due voci di tipo 1234 in `AllFeatur`, che però non compaiono in `9239901907`.
- **Nomi degli assi** (`A_3`, `A_4`, `A_5`) e dei punti (`PNT0`): dallo storico, non calcolati.
- **Foro, gruppo, e figli di una feature**: l'espansione delle feature (la freccia ▸ davanti a
`Estrusione n`) non l'ho decodificata.
- **Il contatore dei nomi può non coincidere con Creo** (§9.1): in `9239902104.prt.1` lo schizzo di
*Foro 2* (id 1095) è il terzo schizzo nella lista, ma Creo lo chiama **Sketch 4**. Il numero 3 era
stato usato da una feature poi cancellata (o interna), che nel file non lascia traccia. Quindi
i nomi calcolati sono corretti finché non ci sono state cancellazioni; con cancellazioni i
contatori possono slittare. Nei tre file verificati (`test.prt.1`, `9252000507`, `9239901907`) non
era successo.
- **Numero di feature `F<n>` di Creo** (`Supfce:F6(ESTRUSIONE_1)`): è la posizione nell'albero,
contando anche gli schizzi, a partire da RIGHT = F1 (RIGHT, TOP, FRONT, PRT_CSYS_DEF, Sketch 1,
Estrusione 1 = F6). Verificato su `9239902104.prt.1`: `Foro 2` è F10.