Two-pass-free line-based scanner. Each directive carries its source
line so downstream validation errors can be attributed correctly.
Parser checks syntax only — atlas/tile resolution and bounds checks
are deferred to the builder so each tier has one responsibility.
loadAtlasDirs walks one level deep into each --atlas-dir, picks up
*/tiles.atlas.json, and builds bidirectional name/id maps. On
duplicate atlas_id across paths the first registration wins and a
warning string is returned (callers decide whether to print).
Accepts decimal or 0x-hex GID input. gid=0 is rendered as the
literal 'empty'; non-zero values print the three components in a
parse-stable space-separated key=value form.
Wraps gid.encodeGid, prints the packed-u32 as decimal on stdout,
emits usage on stderr on bad arity, and a labelled error on
out-of-range or non-integer input.
Top-level run(argv,{out,err}) accepts an injectable output sink so
handlers can be unit-tested without spawning a subprocess. Handler
map is empty for now; subcommands are wired in as they land.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bit layout [atlas:8][tile_id:20][rot:2][res:2] matches
lib-core.maps init.lua so JS-produced GIDs are byte-identical to
engine-produced ones. >>> 0 forces unsigned u32 output.