Add decode subcommand
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.
This commit is contained in:
@@ -31,6 +31,7 @@ function run(argv, { out, err }) {
|
|||||||
}
|
}
|
||||||
const handlers = {
|
const handlers = {
|
||||||
encode: require('./commands/encode'),
|
encode: require('./commands/encode'),
|
||||||
|
decode: require('./commands/decode'),
|
||||||
};
|
};
|
||||||
const handler = handlers[head];
|
const handler = handlers[head];
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
|
|||||||
39
src/commands/decode.js
Normal file
39
src/commands/decode.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { decodeGid } = require('../gid');
|
||||||
|
|
||||||
|
const USAGE = 'usage: sporel-mapper decode <gid>\n';
|
||||||
|
|
||||||
|
function parseGid(s) {
|
||||||
|
let n;
|
||||||
|
if (/^0x[0-9a-fA-F]+$/.test(s)) {
|
||||||
|
n = parseInt(s, 16);
|
||||||
|
} else if (/^\d+$/.test(s)) {
|
||||||
|
n = parseInt(s, 10);
|
||||||
|
} else {
|
||||||
|
throw new RangeError(`gid '${s}' is not a decimal or 0x-hex integer`);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(args, { out, err }) {
|
||||||
|
if (args.length !== 1) {
|
||||||
|
err.write(USAGE);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const gid = parseGid(args[0]);
|
||||||
|
if (gid === 0) {
|
||||||
|
out.write('empty\n');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const { atlas, tile, rotation } = decodeGid(gid);
|
||||||
|
out.write(`atlas=${atlas} tile=${tile} rotation=${rotation}\n`);
|
||||||
|
return 0;
|
||||||
|
} catch (e) {
|
||||||
|
err.write(`error: ${e.message}\n`);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { run };
|
||||||
61
tests/decode.test.js
Normal file
61
tests/decode.test.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const { run } = require('../src/cli');
|
||||||
|
|
||||||
|
class Sink {
|
||||||
|
constructor() { this.chunks = []; }
|
||||||
|
write(s) { this.chunks.push(s); }
|
||||||
|
get text() { return this.chunks.join(''); }
|
||||||
|
}
|
||||||
|
|
||||||
|
test('decode: 196 prints (0,12,1)', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', '196'], { out, err });
|
||||||
|
assert.equal(code, 0);
|
||||||
|
assert.equal(out.text, 'atlas=0 tile=12 rotation=1\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: 0 prints "empty"', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', '0'], { out, err });
|
||||||
|
assert.equal(code, 0);
|
||||||
|
assert.equal(out.text, 'empty\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: hex (0xc4) parses to 196', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', '0xc4'], { out, err });
|
||||||
|
assert.equal(code, 0);
|
||||||
|
assert.equal(out.text, 'atlas=0 tile=12 rotation=1\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: non-zero with tile=0 prints components (not "empty")', () => {
|
||||||
|
// gid=4 = (0<<24) | (0<<4) | (1<<2) → atlas=0 tile=0 rot=1
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', '4'], { out, err });
|
||||||
|
assert.equal(code, 0);
|
||||||
|
assert.equal(out.text, 'atlas=0 tile=0 rotation=1\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: 16777232 from demo_v2 wall layer → (1,1,0)', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', '16777232'], { out, err });
|
||||||
|
assert.equal(code, 0);
|
||||||
|
assert.equal(out.text, 'atlas=1 tile=1 rotation=0\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: no args prints usage, exit 1', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode'], { out, err });
|
||||||
|
assert.equal(code, 1);
|
||||||
|
assert.match(err.text, /usage: sporel-mapper decode/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('decode: bogus arg prints error, exit 1', () => {
|
||||||
|
const out = new Sink(); const err = new Sink();
|
||||||
|
const code = run(['decode', 'xyz'], { out, err });
|
||||||
|
assert.equal(code, 1);
|
||||||
|
assert.match(err.text, /error/i);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user