Co je binární editor?

Binární editor je nástroj, který umožňuje upravovat soubory přímo v šestnáctkové nebo ASCII podobě. Zatímco běžný textový editor pracuje s „řetězci“, binární editor zpřístupňuje syrové byty, z nichž se soubor skládá, a dovolí vám změnit libovolný bit či byte na zvoleném offsetu.

V analýze softwaru, při obnově dat nebo zkoumání protokolů je nepostradatelný. Mezi desktopovými aplikacemi jsou známé například HxD nebo Binary Ninja. Tentokrát jsem si ale stanovil jiný cíl – vytvořit binární editor v podobě jediného souboru, který běží pouze v prohlížeči.


Jaký byl plán implementace

  • Kompletně klientská aplikace (žádný přenos dat)
  • Načítání lokálních souborů pomocí API FileReader
  • Levý sloupec zobrazí šestnáctkové hodnoty (Hex), pravý ASCII
  • Kliknutím se přepne do editačního režimu a hodnotu lze okamžitě přepsat
  • Upravená místa budou zvýrazněna červeně
  • Přepínač mezi režimy HEX a ASCII
  • Výsledek půjde znovu stáhnout jako soubor

Kde jsem zakopnul a jak jsem to opravil

1. Posunutý rámeček při editaci

V prvních verzích se žlutý rámeček kolem editované buňky zobrazoval trochu mimo střed. Mohl za to line-height a rozložení přes flex, které posunuly referenční bod mezi textem a rámečkem. Problém se objevil zejména u monospace fontů – jednotlivé prohlížeče totiž s různými fonty pracují jinak a baseline se liší. Výsledkem byla buňka vizuálně posunutá nahoru nebo dolů.

Stačilo nastavit vertical-align: middle a zároveň použít display: inline-block, aby se výška sjednotila.

.hex-cell.editing {
  outline: 2px solid yellow;
  vertical-align: middle;
}

2. Klávesa ESC vkládala znaky

V rané implementaci se po stisku ESC v editačním režimu objevovala v buňce třeba dvojice EE.

Důvodem bylo, že jsme neposlouchali událost keydown, takže prohlížeč poslal stisknutou klávesu do stejné cesty jako běžný vstup. Zatímco pro validní vstup jsem počítal jen s 0–9 a A–F, klávesa Escape má také vlastní kód (keyCode=27, nově key="Escape"). Ten pak logika vyhodnotila jako "E" a uložila do buňky.

Řešení je jednoduché: v keydown zachytit Escape, zrušit výchozí chování a vyvolat zrušení editace.

document.addEventListener("keydown", e => {
  if (e.key === "Escape") {
    cancelEdit();
    e.preventDefault(); // Nepředávat dál do vstupní logiky
  }
});

Od té chvíle ESC funguje jen jako „ukončit editaci“ a žádné znaky nepropadne.

3. Červené zvýraznění zůstávalo po opětovném načtení

Po načtení souboru, úpravě a následném načtení jiného souboru zůstávaly červené highlighty z předchozí relace.

Příčina byla prostá – pole či stav, který sledoval změněné buňky, se při načtení nového souboru nevymazal. Zobrazovací část se sice resetovala, ale interní „flag“ zůstal a nový soubor tak převzal staré zvýraznění.

Fix spočíval v tom, že se hned po načtení souboru zavolá clearModifiedState() a stav se kompletně vynuluje.

4. Přepínání mezi režimy HEX/ASCII

V první verzi šlo editovat Hex i ASCII zároveň, což vedlo k chaosu. Jeden byte totiž najednou ovládaly dva různé vstupy. Pokud jste psali do ASCII, okamžitě se přepsala hexadecimální reprezentace – ale během zadávání vznikaly dočasně neplatné hodnoty.

Abych se vyhnul zmatku, přidal jsem jednoduchý select s režimem editace a výchozí nastavení nechal na HEX. Uživatel tak přesně ví, které pole je „zdroj pravdy“, a synchronizační logika se zpřehlednila.


Kousek kódu

Samotnou zápisovou logiku jsem postavil takto:

function applyEdit(offset, newValue) {
  if (mode === "hex") {
    buffer[offset] = parseInt(newValue, 16);
  } else {
    buffer[offset] = newValue.charCodeAt(0);
  }
  markModified(offset);
  render();
}

markModified(offset) se stará o červené zvýraznění – porovná původní a novou hodnotu a podle výsledku přidá CSS třídu.


Shrnutí

Výsledný binární editor běží offline, je tedy bezpečný a tvoří z něj „čistě klientský“ nástroj.

  • Hodí se pro drobné zásahy do binárních souborů
  • Pomůže tam, kde není možné instalovat specializovanou aplikaci
  • Využijete ho i při výuce, když chcete intuitivně vysvětlit, co znamená „řetězec bytů“

Přesně v těchto situacích se osvědčil.