# Capcană: corupere diacritice la editarea `.sc2`/`.vc2` Partajat între proiectele ROA* — capcana ține de formatul `.sc2`/`.vc2` produs de FoxBin2Prg, nu de un proiect anume. ## De ce se întâmplă Fișierele `.sc2`/`.vc2` sunt **cp1250** (Windows-1250, Europa Centrală), nu UTF-8 — deși antetul FoxBin2Prg declară `CPID="1252"`. Eticheta din antet nu corespunde conținutului: controalele care poartă text cu diacritice au `FontCharSet = 238` (Europa de Est), adică VFP le randează tot ca cp1250. Diferența nu e cosmetică — un `ă` real scris prin cp1252 nu are echivalent în acel codepage și s-ar pierde; scris prin cp1250 (encoding-ul real al fișierului) e pur și simplu octetul `0xE3`, corect și stabil. Dacă un editor/tool citește fișierul ca UTF-8, fiecare byte >= 0x80 (diacritic) devine invalid ca secvență UTF-8 și e înlocuit cu U+FFFD. Dacă tool-ul apoi **salvează tot ca UTF-8**, acel U+FFFD e scris ca 3 octeți (`0xEF 0xBF 0xBD`) — care, citiți înapoi ca cp1250 (cum face FoxBin2Prg la write-back), apar ca `�`. Caracterul original e **ireversibil pierdut** (recuperabil DOAR din altă sursă: git history, backup). Octeții diacriticelor din codebase-ul ROA, cu adevărul (cp1250) și aparența dacă îi decodezi greșit ca cp1252: | Literă românească | Byte | Aparență dacă decodezi ca cp1252 | |---|---|---| | ă | `0xE3` | ã (a-tilde) | | Ă | `0xC3` | à | | î | `0xEE` | î (identic — zonă comună cp1250/cp1252) | | Î | `0xCE` | Î (identic — zonă comună cp1250/cp1252) | | â | `0xE2` | â (identic — zonă comună cp1250/cp1252) | | ț | `0xFE` | þ (thorn minuscul) | | Ș (majusculă) | `0xAA` | ª (ordinal masculin) | | ș (minusculă) | `0xBA` | º (ordinal feminin) | Decodate cp1250 (encoding-ul real), aceste litere sunt diacritice românești normale — exact ce apare în formular când fișierul e sănătos. Par "gunoi"/typo (`ã`, `þ`, `ª`, `º`) **doar dacă le decodezi cu codepage-ul greșit** — nu le "corecta" fără să verifici encoding-ul real. ## Cum se verifică dacă un fișier `.sc2`/`.vc2` e corupt Verificarea se face **pe octeți**, nu pe text decodat: un codepage single-byte (1250 sau 1252) mapează fiecare octet la un caracter, deci nu produce niciodată U+FFFD — o căutare de `[char]0xFFFD` în textul decodat nu găsește nimic nici pe un fișier stricat. Ce se caută e secvența de trei octeți pe care a lăsat-o tool-ul care a stricat fișierul: ```powershell $b = [System.IO.File]::ReadAllBytes(".vc2") $n = 0 for ($i = 0; $i -lt $b.Length - 2; $i++) { if ($b[$i] -eq 0xEF -and $b[$i+1] -eq 0xBF -and $b[$i+2] -eq 0xBD) { $n++; $i += 2 } } "EF BF BD (U+FFFD) x $n" # orice valoare > 0 = fisier stricat ``` Echivalentul din Bash, util și pentru censul complet înainte/după orice editare (vezi `reguli_lucru.md`, punctul 7): ```bash od -An -tx1 .vc2 | tr ' ' '\n' | grep -v '^$' | awk '$1>"7f"' | sort | uniq -c ``` Censul **crește** la stricare — un octet cp1250 devine trei octeți de U+FFFD — deci nu urmări doar scăderile. Decodat cp1250, `EF BF BD` apare ca `ďż˝` în caption. ## Cum se repară 1. **Nu edita orbește peste corupere** — caracterul original e deja pierdut din fișierul curent. 2. Dacă corupera s-a întâmplat într-o sesiune anterioară necomisă (HEAD-ul git încă e curat): recuperează originalul convertind binarul din HEAD în text: ```bash git show HEAD:.vcx > /tmp/head.vcx git show HEAD:.vct > /tmp/head.vct # (sau .VCT, dupa caz) powershell -File D:\ROA\UTIL\foxbin2prg\vcx2txt.ps1 -Source /tmp/head.vcx -CacheRoot /tmp/head_out ``` Dacă și HEAD e deja corupt (corupera a fost comisă), caută ultima versiune curată cu `git cat-file blob :` (din Bash, niciodată cu `>` din PowerShell 5.1 — adaugă BOM și re-encodează) sau `svn cat ` — ambele surse independente de encoding-ul curent al fișierului. Apoi decodează ambele fișiere (versiunea curată + curent corupt) ca cp1250 și compară linie cu linie (PowerShell, nu `diff`/Bash direct — nu decodează cp1250 corect la afișare, deși detectează totuși diferența de octeți). 3. Reconstruiește fișierul text pornind de la versiunea curată, reaplicând DOAR modificările intenționate din sesiunea curentă (nu invers — nu cârpi fișierul corupt caracter cu caracter). 4. Scrie rezultatul înapoi ca bytes cp1250 (`[System.Text.Encoding]::GetEncoding(1250).GetBytes(...)`), NU ca string UTF-8/`Out-File` implicit. 5. Rulează write-back (`txt2vcx.ps1 -Force`) și verifică fidelity check = zero diferențe. ## Cum se evită pe viitor Când editezi `.sc2`/`.vc2` cu tool-uri text (Read/Edit/Write), verifică întâi dacă fișierul conține diacritice (`ă`, `ț`, `Ș`, `ș`, `î`, `â` etc.) — dacă da, **orice rescrie completă trebuie făcută la nivel de octeți cu encoding cp1250 explicit**, nu presupunând UTF-8. Editările punctuale (Edit tool, string replace) sunt sigure DOAR dacă tool-ul păstrează restul fișierului byte-identic (nu rescrie tot fișierul reîncodat). ## Vezi și `PROMPT_cautare_vfp.md` din acest folder, pasul 3.c — menționează riscul pe scurt; acest fișier e detalierea completă (tabel de caractere, pași de recuperare din git).