Files
AptXmlAI/ra3apt-cli/test/supported-bytecode.test.cjs
T
lanyi e0b8dedc04 Fix APT CLI roundtrip visual and script preservation
Preserve edittext channels, alpha/color transforms, inactive placement fields,
zero ratio/clipping and null ClipAction presence. Keep unlabelled flag bits,
quoted/escaped script strings, export declarations and correctly typed CONST
and pushdata values. Validate signed integers and write byte-aligned UTF-8.

Add 23 public CLI regressions and record the native 174-resource batch plus
bounded retests: four discovered pages are repaired, while the sound placeholder
and native float formatting remain explicit accuracy limits. Rebuild the 1.0.1
Windows executable and leave unrelated SDK changes untouched.
2026-10-13 02:40:37 +02:00

83 lines
3.7 KiB
JavaScript

'use strict';
const test = require('node:test');
const H = require('./public-cli-helper.cjs');
function rawAction(nodes) {
return `<aptdata>
<movieclip id="0" screensizex="1365" screensizey="768" unknown="33">
<imports/><exports/>
<frames><frame id="0"><action>
${nodes}
<noarg action="0"/>
</action></frame></frames>
</movieclip>
</aptdata>`;
}
test('public expand/build preserves an edited supported pushfloat without integer truncation', () => {
const work = H.testCase('pushfloat');
const source = H.expanded(work, rawAction('<float action="180" val="85.25"/>'));
H.assert.match(source.xml, /pushfloat 85\.25/);
const edited = source.xml.replace('pushfloat 85.25', 'pushfloat 24.5');
H.assert.notEqual(edited, source.xml);
const rebuilt = H.build(work, edited);
H.succeeded(rebuilt.result);
H.assert.equal(Number(H.element(rebuilt.raw, 'float', 'action', 180).val), 24.5);
});
test('public expand/build distinguishes a numeric-looking quoted string from an integer pool value', () => {
const work = H.testCase('quoted-numeric');
const source = H.expanded(work, rawAction(`<constantpool action="136">
<constant val="0" str="007"/>
<constant val="1" num="7"/>
</constantpool>
<byte action="162" val="0"/>
<byte action="162" val="1"/>`));
H.assert.match(source.xml, /pushconst "007"/);
H.assert.match(source.xml, /pushconst 7(?:\s|<)/);
const rebuilt = H.build(work, source.xml);
H.succeeded(rebuilt.result);
H.assert.equal(H.element(rebuilt.raw, 'constant', 'str', '007').str, '007');
H.assert.equal(H.element(rebuilt.raw, 'constant', 'num', '7').num, '7');
// This .const was generated by production build(), not by the fake converter.
const table = H.fs.readFileSync(rebuilt.output.replace(/\.apt$/, '.const'));
H.assert.equal(table.readUInt32LE(0x18), 2);
const values = [];
for (let i = 0; i < 2; i++) {
const offset = 0x20 + i * 8;
const type = table.readUInt32LE(offset);
const value = table.readUInt32LE(offset + 4);
values.push(type === 1 ? { type: 'string', value: table.subarray(value, table.indexOf(0, value)).toString('ascii') } :
{ type: 'integer', value });
}
H.assert.deepEqual(values, [{ type: 'string', value: '007' }, { type: 'integer', value: 7 }]);
});
test('public pushdata build retains quoted "007" and integer 7 as data nodes and correctly typed CONST values', () => {
const work = H.testCase('pushdata-types');
const source = H.expanded(work, rawAction(`<pushdata action="150">
<data val="0" str="007"/>
<data val="1" num="7"/>
</pushdata>`));
H.assert.match(source.xml, /pushdata "007", 7/);
const rebuilt = H.build(work, source.xml);
H.succeeded(rebuilt.result);
const body = rebuilt.raw.match(/<pushdata\b[^>]*>([\s\S]*?)<\/pushdata>/);
H.assert.ok(body);
const children = Array.from(body[1].matchAll(/<(data|constant)\b[^>]*>/g), match =>
({ tag: match[1], attrs: H.attributes(match[0]) }));
H.assert.equal(children.length, 2);
H.assert.deepEqual(children.map(child => child.tag), ['data', 'data'],
'xml2apt expects data children; constant children previously crashed the native converter');
H.assert.equal(children[0].attrs.str, '007');
H.assert.equal(children[0].attrs.num, undefined);
H.assert.equal(Number(children[1].attrs.num), 7);
H.assert.equal(children[1].attrs.str, undefined);
const records = H.constRecords(rebuilt.output);
H.assert.deepEqual(records.map(record => ({ type: record.type, value: record.value })),
[{ type: 1, value: '007' }, { type: 7, value: 7 }]);
H.assert.equal(Number(children[0].attrs.val), 0);
H.assert.equal(Number(children[1].attrs.val), 1);
});