@microtronics/studio-cli 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -1
- package/dist/main.js +37 -36
- package/dist/studio-cli.d.ts +2 -0
- package/out/dde/coreDefinitions.js +18 -18
- package/out/dde/dde.js +5 -0
- package/out/dde/fileGenerators/dlo.js +6 -2
- package/out/dde/fileGenerators/dloLocalData.js +365 -0
- package/out/defaultFiles.js +2 -0
- package/package.json +1 -1
package/dist/studio-cli.d.ts
CHANGED
|
@@ -1766,6 +1766,7 @@ export declare namespace DefaultFiles {
|
|
|
1766
1766
|
autoUplinkInc: string;
|
|
1767
1767
|
autoDdeInc: string;
|
|
1768
1768
|
mainDdeInc: string;
|
|
1769
|
+
autoLocalDataInc: string;
|
|
1769
1770
|
legacyAutoInc: string;
|
|
1770
1771
|
legacyAutoDdeInc: string;
|
|
1771
1772
|
legacyAutoUplinkInc: string;
|
|
@@ -1782,6 +1783,7 @@ export declare namespace DefaultFiles {
|
|
|
1782
1783
|
autoInc: string;
|
|
1783
1784
|
defaultInc: string;
|
|
1784
1785
|
autoUplinkInc: string;
|
|
1786
|
+
autoLocalDataInc: string;
|
|
1785
1787
|
autoDdeInc: string;
|
|
1786
1788
|
mainDdeInc: string;
|
|
1787
1789
|
legacyAutoInc: string;
|
|
@@ -307,7 +307,7 @@ var DDE_Core;
|
|
|
307
307
|
...DDE_Core.TDEF_NUMBER,
|
|
308
308
|
rdef: 1,
|
|
309
309
|
xml: { type: 'u8' },
|
|
310
|
-
pwn: { rwfunc: 'u8', htype: 's32' },
|
|
310
|
+
pwn: { rwfunc: 'u8', htype: 's32', printf: '%d' },
|
|
311
311
|
openApi: { type: 'number', minimum: 0 }
|
|
312
312
|
},
|
|
313
313
|
u16: {
|
|
@@ -317,7 +317,7 @@ var DDE_Core;
|
|
|
317
317
|
...DDE_Core.TDEF_NUMBER,
|
|
318
318
|
rdef: 1,
|
|
319
319
|
xml: { type: 'u16' },
|
|
320
|
-
pwn: { rwfunc: 'u16', htype: 's32' },
|
|
320
|
+
pwn: { rwfunc: 'u16', htype: 's32', printf: '%d' },
|
|
321
321
|
openApi: { type: 'number', minimum: 0 }
|
|
322
322
|
},
|
|
323
323
|
u32: {
|
|
@@ -327,7 +327,7 @@ var DDE_Core;
|
|
|
327
327
|
...DDE_Core.TDEF_NUMBER,
|
|
328
328
|
rdef: 1,
|
|
329
329
|
xml: { type: 'u32' },
|
|
330
|
-
pwn: { rwfunc: 'u32', htype: 's32' },
|
|
330
|
+
pwn: { rwfunc: 'u32', htype: 's32', printf: '%d' },
|
|
331
331
|
openApi: { type: 'number', minimum: 0 }
|
|
332
332
|
},
|
|
333
333
|
// u64 - does not exist / not supported by backend
|
|
@@ -338,7 +338,7 @@ var DDE_Core;
|
|
|
338
338
|
...DDE_Core.TDEF_NUMBER,
|
|
339
339
|
rdef: 1,
|
|
340
340
|
xml: { type: 's8' },
|
|
341
|
-
pwn: { rwfunc: 's8', htype: 's32' },
|
|
341
|
+
pwn: { rwfunc: 's8', htype: 's32', printf: '%d' },
|
|
342
342
|
openApi: { type: 'number' }
|
|
343
343
|
},
|
|
344
344
|
s16: {
|
|
@@ -348,7 +348,7 @@ var DDE_Core;
|
|
|
348
348
|
...DDE_Core.TDEF_NUMBER,
|
|
349
349
|
rdef: 1,
|
|
350
350
|
xml: { type: 's16' },
|
|
351
|
-
pwn: { rwfunc: 's16', htype: 's32' },
|
|
351
|
+
pwn: { rwfunc: 's16', htype: 's32', printf: '%d' },
|
|
352
352
|
openApi: { type: 'number' }
|
|
353
353
|
},
|
|
354
354
|
s32: {
|
|
@@ -358,7 +358,7 @@ var DDE_Core;
|
|
|
358
358
|
...DDE_Core.TDEF_NUMBER,
|
|
359
359
|
rdef: 1,
|
|
360
360
|
xml: { type: 's32' },
|
|
361
|
-
pwn: { rwfunc: 's32' },
|
|
361
|
+
pwn: { rwfunc: 's32', printf: '%d' },
|
|
362
362
|
openApi: { type: 'number' }
|
|
363
363
|
},
|
|
364
364
|
s64: {
|
|
@@ -368,7 +368,7 @@ var DDE_Core;
|
|
|
368
368
|
...DDE_Core.TDEF_NUMBER,
|
|
369
369
|
rdef: 1,
|
|
370
370
|
xml: { type: 's64' },
|
|
371
|
-
pwn: { rwfunc: 's64', rwp: '%n{%l}', htype: '{%l}' },
|
|
371
|
+
pwn: { rwfunc: 's64', rwp: '%n{%l}', htype: '{%l}', printf: '%d' },
|
|
372
372
|
openApi: { type: 'number' }
|
|
373
373
|
},
|
|
374
374
|
f16: {
|
|
@@ -378,7 +378,7 @@ var DDE_Core;
|
|
|
378
378
|
...DDE_Core.TDEF_NUMBER,
|
|
379
379
|
rdef: 1,
|
|
380
380
|
xml: { type: 'floatm16' },
|
|
381
|
-
pwn: { rwfunc: 'f16', rwp: 'Float:%n', htype: 'f32' },
|
|
381
|
+
pwn: { rwfunc: 'f16', rwp: 'Float:%n', htype: 'f32', printf: '%f' },
|
|
382
382
|
openApi: { type: 'number' }
|
|
383
383
|
},
|
|
384
384
|
f32: {
|
|
@@ -388,7 +388,7 @@ var DDE_Core;
|
|
|
388
388
|
...DDE_Core.TDEF_NUMBER,
|
|
389
389
|
rdef: 1,
|
|
390
390
|
xml: { type: 'f32' },
|
|
391
|
-
pwn: { rwfunc: 'f32', rwp: 'Float:%n' },
|
|
391
|
+
pwn: { rwfunc: 'f32', rwp: 'Float:%n', printf: '%f' },
|
|
392
392
|
openApi: { type: 'number' }
|
|
393
393
|
},
|
|
394
394
|
f64: {
|
|
@@ -398,7 +398,7 @@ var DDE_Core;
|
|
|
398
398
|
...DDE_Core.TDEF_NUMBER,
|
|
399
399
|
rdef: 0,
|
|
400
400
|
xml: { type: 'f64' },
|
|
401
|
-
pwn: { rwfunc: 'f64', rwp: '%n{%l}', htype: '{%l}' },
|
|
401
|
+
pwn: { rwfunc: 'f64', rwp: '%n{%l}', htype: '{%l}', printf: '%f' },
|
|
402
402
|
openApi: { type: 'number' }
|
|
403
403
|
},
|
|
404
404
|
stamp32: {
|
|
@@ -408,7 +408,7 @@ var DDE_Core;
|
|
|
408
408
|
...TDEF_STAMP,
|
|
409
409
|
rdef: 0,
|
|
410
410
|
xml: { type: 'stamp4' },
|
|
411
|
-
pwn: { rwfunc: 'stamp32' },
|
|
411
|
+
pwn: { rwfunc: 'stamp32', printf: '%s', printfFormat: 'stampToISOString' },
|
|
412
412
|
openApi: { type: 'string', format: 'YYYYMMDDhhmmssSSS', description: 'UTC' }
|
|
413
413
|
},
|
|
414
414
|
stamp40: {
|
|
@@ -418,7 +418,7 @@ var DDE_Core;
|
|
|
418
418
|
...TDEF_STAMP,
|
|
419
419
|
rdef: 0,
|
|
420
420
|
xml: { type: 'stamp' },
|
|
421
|
-
pwn: { rwfunc: 'stamp40', rwp: '%n{%l}', htype: '{%l}' },
|
|
421
|
+
pwn: { rwfunc: 'stamp40', rwp: '%n{%l}', htype: '{%l}', printf: '%s', printfFormat: 'stampToISOString' },
|
|
422
422
|
openApi: { type: 'string', format: 'YYYYMMDDhhmmssSSS', description: 'UTC' }
|
|
423
423
|
},
|
|
424
424
|
bin: {
|
|
@@ -428,7 +428,7 @@ var DDE_Core;
|
|
|
428
428
|
rdef: 0,
|
|
429
429
|
lenUnits: 'byte',
|
|
430
430
|
xml: { type: 'string', editmask: '%HEX%' },
|
|
431
|
-
pwn: { rwfunc: 'bin', rwlen: 1, rwp: '%n{%l}', htype: '{%l}' },
|
|
431
|
+
pwn: { rwfunc: 'bin', rwlen: 1, rwp: '%n{%l}', htype: '{%l}', printf: null, printfFormat: 'memtostr' },
|
|
432
432
|
openApi: { type: 'string', description: 'HEX string' }
|
|
433
433
|
},
|
|
434
434
|
astr: {
|
|
@@ -438,7 +438,7 @@ var DDE_Core;
|
|
|
438
438
|
rdef: 1,
|
|
439
439
|
lenUnits: 'char',
|
|
440
440
|
xml: { type: 'string', param1: 'nocodepage' },
|
|
441
|
-
pwn: { rwfunc: 'astr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}' },
|
|
441
|
+
pwn: { rwfunc: 'astr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}', printf: '%s' },
|
|
442
442
|
openApi: { type: 'string' }
|
|
443
443
|
},
|
|
444
444
|
nstr: {
|
|
@@ -448,7 +448,7 @@ var DDE_Core;
|
|
|
448
448
|
rdef: 1,
|
|
449
449
|
lenUnits: 'byte',
|
|
450
450
|
xml: { type: 'string' },
|
|
451
|
-
pwn: { rwfunc: 'nstr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}' },
|
|
451
|
+
pwn: { rwfunc: 'nstr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}', printf: '%s' },
|
|
452
452
|
openApi: { type: 'string' }
|
|
453
453
|
},
|
|
454
454
|
wstr: {
|
|
@@ -458,7 +458,7 @@ var DDE_Core;
|
|
|
458
458
|
rdef: 1,
|
|
459
459
|
lenUnits: 'byte',
|
|
460
460
|
xml: { type: 'string' },
|
|
461
|
-
pwn: { rwfunc: 'wstr', rwlen: 1, rwp: '%n[%l+1]', htype: '[%l+1]' },
|
|
461
|
+
pwn: { rwfunc: 'wstr', rwlen: 1, rwp: '%n[%l+1]', htype: '[%l+1]', printf: '%s' },
|
|
462
462
|
openApi: { type: 'string' }
|
|
463
463
|
},
|
|
464
464
|
ustr: {
|
|
@@ -468,7 +468,7 @@ var DDE_Core;
|
|
|
468
468
|
rdef: 0,
|
|
469
469
|
lenUnits: 'byte',
|
|
470
470
|
xml: { type: 'string' },
|
|
471
|
-
pwn: { rwfunc: 'ustr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}' },
|
|
471
|
+
pwn: { rwfunc: 'ustr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}', printf: '%s' },
|
|
472
472
|
openApi: { type: 'string' }
|
|
473
473
|
},
|
|
474
474
|
cstr: {
|
|
@@ -478,7 +478,7 @@ var DDE_Core;
|
|
|
478
478
|
rdef: 1,
|
|
479
479
|
lenUnits: 'char',
|
|
480
480
|
xml: { type: 'string', editmask: '%HEX%' },
|
|
481
|
-
pwn: { rwfunc: 'cstr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}' },
|
|
481
|
+
pwn: { rwfunc: 'cstr', rwlen: 1, rwp: '%n{%l+1}', htype: '{%l+1}', printf: '%s' },
|
|
482
482
|
openApi: { type: 'string' }
|
|
483
483
|
},
|
|
484
484
|
// ^^^
|
package/out/dde/dde.js
CHANGED
|
@@ -44,6 +44,7 @@ const api_1 = require("./fileGenerators/api");
|
|
|
44
44
|
const helper_1 = require("../helper");
|
|
45
45
|
const registryAPI_1 = require("../registryAPI");
|
|
46
46
|
var yamlRangeToLineInformation = helper_1.Helper.yamlRangeToLineInformation;
|
|
47
|
+
const dloLocalData_1 = require("./fileGenerators/dloLocalData");
|
|
47
48
|
exports.DDE_PATH = defaultFiles_1.DefaultFiles.filePaths.dde.path;
|
|
48
49
|
// create file cache.
|
|
49
50
|
// yaml & json
|
|
@@ -413,12 +414,16 @@ var DDE;
|
|
|
413
414
|
}
|
|
414
415
|
const mainDDEInc = (0, dlo_1.generateMainDDEIncFromMap)(ddeJSON);
|
|
415
416
|
const autoUplink = (0, dlo_1.generateAutoUplinkIncFromMap)(ddeJSON, dependencies.map(dep => dep.libraryName));
|
|
417
|
+
const autoLocalData = (0, dloLocalData_1.generateDloLocalDataIncFromMap)(ddeJSON);
|
|
416
418
|
const autoIncPath = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoInc);
|
|
417
419
|
await fs.writeFile(autoIncPath, autoInc);
|
|
418
420
|
logger.log(`Generated`, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoInc);
|
|
419
421
|
const autoUplinkIncPath = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoUplinkInc);
|
|
420
422
|
await fs.writeFile(autoUplinkIncPath, autoUplink);
|
|
421
423
|
logger.log(`Generated`, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoUplinkInc);
|
|
424
|
+
const autoLocalDataIncPath = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoLocalDataInc);
|
|
425
|
+
await fs.writeFile(autoLocalDataIncPath, autoLocalData);
|
|
426
|
+
logger.log(`Generated`, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoLocalDataInc);
|
|
422
427
|
const autoDdeAppPath = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoDdeInc.replace('X', 'app'));
|
|
423
428
|
await fs.writeFile(autoDdeAppPath, [...appDDEInc.globals, ...appDDEInc.helper].join('\n'));
|
|
424
429
|
logger.log('Generated', defaultFiles_1.DefaultFiles.filePaths.studio.auto.dlo.autoDdeInc.replace('X', 'app'));
|
|
@@ -24,6 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.AutoDDEGenerator = void 0;
|
|
27
|
+
exports.getPawnTypeDefinition = getPawnTypeDefinition;
|
|
27
28
|
exports.generateAutoIncFromMap = generateAutoIncFromMap;
|
|
28
29
|
exports.generateAutoUplinkIncFromMap = generateAutoUplinkIncFromMap;
|
|
29
30
|
exports.generateMainDDEIncFromMap = generateMainDDEIncFromMap;
|
|
@@ -535,10 +536,10 @@ class AutoDDEGenerator {
|
|
|
535
536
|
createDefaultDefineForField(variableName, field) {
|
|
536
537
|
this.sourceType = 'defaultDefines';
|
|
537
538
|
if (field.scale !== undefined) {
|
|
538
|
-
this.addDefine(
|
|
539
|
+
this.addDefine(`${variableName}_scale`, field.scale);
|
|
539
540
|
}
|
|
540
541
|
if (field.offset !== undefined) {
|
|
541
|
-
this.addDefine(
|
|
542
|
+
this.addDefine(`${variableName}_offset`, field.offset);
|
|
542
543
|
}
|
|
543
544
|
if (field.defaultValue !== undefined) {
|
|
544
545
|
const { value, reason } = this.parseFieldDefault(field);
|
|
@@ -965,6 +966,9 @@ async function buildDefaultInc(cwd, fs) {
|
|
|
965
966
|
const autoDdeIncs = await getAutoDDEIncludes(cwd, fs, dloLibs);
|
|
966
967
|
for (const dloLib of dloLibs) {
|
|
967
968
|
// todo use relative path as include?
|
|
969
|
+
if (dloLib.name === 'local-data-interface') {
|
|
970
|
+
await addInclude(cwd, fs, fileContent, defaultFiles_1.DefaultFiles.fileNames.autoLocalDataInc);
|
|
971
|
+
}
|
|
968
972
|
fileContent.push(`#include <${dloLib.name}>`);
|
|
969
973
|
if (dloLib.name === 'dlo-core') {
|
|
970
974
|
fileContent.push(...autoDdeIncs);
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDloLocalDataIncFromMap = generateDloLocalDataIncFromMap;
|
|
4
|
+
const coreDefinitions_1 = require("../coreDefinitions");
|
|
5
|
+
var TransferDirection = coreDefinitions_1.DDE_Core.TransferDirection;
|
|
6
|
+
const dlo_1 = require("./dlo");
|
|
7
|
+
const helper_1 = require("../../helper");
|
|
8
|
+
const PAWN_SZTEMP_DECL = '\tnew szTemp{64};';
|
|
9
|
+
const PAWN_STRCAT_RETURN = '\treturn strcat(aData, szTemp, sizeof(szTemp));';
|
|
10
|
+
/**
|
|
11
|
+
* Determines if a field type can use a format helper function, and which one.
|
|
12
|
+
* Returns null if the field type needs inline code (packed types, memtostr, wstring, etc.)
|
|
13
|
+
*/
|
|
14
|
+
function getHelperFormatType(tdefPwn) {
|
|
15
|
+
if (!tdefPwn.printf) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const rwp = tdefPwn.rwp || '%n';
|
|
19
|
+
const hasCellArray = rwp.includes('[');
|
|
20
|
+
const hasPackedArray = rwp.includes('{');
|
|
21
|
+
if (tdefPwn.printfFormat === 'stampToISOString') {
|
|
22
|
+
return hasPackedArray || hasCellArray ? null : 'stamp';
|
|
23
|
+
}
|
|
24
|
+
switch (tdefPwn.printf) {
|
|
25
|
+
case '%d':
|
|
26
|
+
return hasPackedArray || hasCellArray ? null : 'd';
|
|
27
|
+
case '%f':
|
|
28
|
+
return hasPackedArray || hasCellArray ? null : 'f';
|
|
29
|
+
case '%s':
|
|
30
|
+
return hasCellArray ? null : 's';
|
|
31
|
+
default:
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Gets the format type for a field, handling shadow/skip filtering.
|
|
37
|
+
* Returns null if the field should be skipped or needs inline code.
|
|
38
|
+
*/
|
|
39
|
+
function getFieldFormatType(field) {
|
|
40
|
+
if (field.type === 'shadow' || field.shadowRef) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
|
|
44
|
+
if (!tdefPwn || tdefPwn === 'skip') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return getHelperFormatType(tdefPwn);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Checks if a container is device-side (hx or cx) and not backend-only.
|
|
51
|
+
*/
|
|
52
|
+
function isDeviceContainer(containerDefinition) {
|
|
53
|
+
return ([coreDefinitions_1.DDE_Core.ContainerType.hx, coreDefinitions_1.DDE_Core.ContainerType.cx].includes(containerDefinition.containerType) &&
|
|
54
|
+
containerDefinition.transferDirection !== TransferDirection.backendOnly);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a field is valid for code generation (not shadow, not skipped).
|
|
58
|
+
*/
|
|
59
|
+
function isValidField(field) {
|
|
60
|
+
if (field.type === 'shadow' || field.shadowRef) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
|
|
64
|
+
return !!tdefPwn && tdefPwn !== 'skip';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Adds an array field to the appropriate group in the group map.
|
|
68
|
+
*/
|
|
69
|
+
function addFieldToGroup(field, formatType, groupMap) {
|
|
70
|
+
const bracketEnd = field.name.indexOf(']');
|
|
71
|
+
const subField = bracketEnd >= 0 ? field.name.substring(bracketEnd + 1) : '';
|
|
72
|
+
const groupKey = `${field.arrayRef.name}|${subField}|${formatType}`;
|
|
73
|
+
if (!groupMap.has(groupKey)) {
|
|
74
|
+
groupMap.set(groupKey, { fields: [], formatType });
|
|
75
|
+
}
|
|
76
|
+
groupMap.get(groupKey).fields.push(field);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Classifies fields into array-groupable and standalone categories.
|
|
80
|
+
*/
|
|
81
|
+
function classifyFields(fields) {
|
|
82
|
+
const standaloneFields = [];
|
|
83
|
+
const groupMap = new Map();
|
|
84
|
+
const usedFormats = new Set();
|
|
85
|
+
let hasInlineFields = false;
|
|
86
|
+
for (const field of fields) {
|
|
87
|
+
if (!isValidField(field)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const formatType = getFieldFormatType(field);
|
|
91
|
+
if (field.arrayRef && formatType) {
|
|
92
|
+
addFieldToGroup(field, formatType, groupMap);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
standaloneFields.push(field);
|
|
96
|
+
if (formatType) {
|
|
97
|
+
usedFormats.add(formatType);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
hasInlineFields = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { standaloneFields, groupMap, usedFormats, hasInlineFields };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Converts a group map entry into an ArrayFieldGroup if it has consistent stepping.
|
|
108
|
+
* Returns null if the group should be treated as standalone fields.
|
|
109
|
+
*/
|
|
110
|
+
function tryBuildArrayGroup(containerTitle, groupKey, groupFields) {
|
|
111
|
+
groupFields.sort((a, b) => a.arrayRef.index - b.arrayRef.index);
|
|
112
|
+
const offsets = groupFields.map(f => f.byteOffset);
|
|
113
|
+
const step = offsets[1] - offsets[0];
|
|
114
|
+
const isConsistent = step > 0 && offsets.every((o, i) => i === 0 || o - offsets[i - 1] === step);
|
|
115
|
+
if (!isConsistent) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const [arrayName, subField] = groupKey.split('|');
|
|
119
|
+
const fieldNamePrefix = groupFields[0].library
|
|
120
|
+
? `_${helper_1.Helper.generateHashForLibraryName(groupFields[0].library)}`
|
|
121
|
+
: 'DDE_';
|
|
122
|
+
return {
|
|
123
|
+
arrayName,
|
|
124
|
+
subField,
|
|
125
|
+
fields: groupFields,
|
|
126
|
+
startOffset: offsets[0],
|
|
127
|
+
endOffset: offsets[offsets.length - 1],
|
|
128
|
+
step,
|
|
129
|
+
formatType: getFieldFormatType(groupFields[0]),
|
|
130
|
+
pawnVarBase: `${fieldNamePrefix}${containerTitle}_${arrayName}`
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Analyzes container fields and groups array elements that can be collapsed
|
|
135
|
+
* into arithmetic if-blocks for reduced AMX binary size.
|
|
136
|
+
*/
|
|
137
|
+
function analyzeContainerFields(containerTitle, fields) {
|
|
138
|
+
const { standaloneFields, groupMap, usedFormats, hasInlineFields } = classifyFields(fields);
|
|
139
|
+
const arrayGroups = [];
|
|
140
|
+
for (const [groupKey, groupData] of groupMap) {
|
|
141
|
+
const { fields: groupFields, formatType } = groupData;
|
|
142
|
+
if (groupFields.length < 2) {
|
|
143
|
+
standaloneFields.push(...groupFields);
|
|
144
|
+
usedFormats.add(formatType);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const group = tryBuildArrayGroup(containerTitle, groupKey, groupFields);
|
|
148
|
+
if (group) {
|
|
149
|
+
usedFormats.add(formatType);
|
|
150
|
+
arrayGroups.push(group);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
standaloneFields.push(...groupFields);
|
|
154
|
+
usedFormats.add(formatType);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
standaloneFields.sort((a, b) => a.byteOffset - b.byteOffset);
|
|
158
|
+
return { arrayGroups, standaloneFields, usedFormats, hasInlineFields };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Scans all eligible container fields to determine which helper format types are needed.
|
|
162
|
+
*/
|
|
163
|
+
function collectUsedFormats(ddeMap) {
|
|
164
|
+
const usedFormats = new Set();
|
|
165
|
+
for (const [, containerDefinition] of Object.entries(ddeMap.containers)) {
|
|
166
|
+
if (!isDeviceContainer(containerDefinition)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
for (const field of containerDefinition.fields) {
|
|
170
|
+
const formatType = getFieldFormatType(field);
|
|
171
|
+
if (formatType) {
|
|
172
|
+
usedFormats.add(formatType);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return usedFormats;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Generates Pawn helper functions that encapsulate the sprintf+strcat pattern.
|
|
180
|
+
* Only generates the helpers that are actually used, reducing dead code.
|
|
181
|
+
* Each helper replaces 3 lines of repeated code per field with a single call.
|
|
182
|
+
*/
|
|
183
|
+
function buildHelperFunctions(pawnSource, usedFormats) {
|
|
184
|
+
if (usedFormats.has('d')) {
|
|
185
|
+
pawnSource('stock _DDE_fmt_d(aData{}, value) {');
|
|
186
|
+
pawnSource(PAWN_SZTEMP_DECL);
|
|
187
|
+
pawnSource('\tsprintf(szTemp, _, "%d", value);');
|
|
188
|
+
pawnSource(PAWN_STRCAT_RETURN);
|
|
189
|
+
pawnSource('}');
|
|
190
|
+
}
|
|
191
|
+
if (usedFormats.has('f')) {
|
|
192
|
+
pawnSource('');
|
|
193
|
+
pawnSource('stock _DDE_fmt_f(aData{}, Float:value) {');
|
|
194
|
+
pawnSource(PAWN_SZTEMP_DECL);
|
|
195
|
+
pawnSource('\tsprintf(szTemp, _, "%.3f", value);');
|
|
196
|
+
pawnSource(PAWN_STRCAT_RETURN);
|
|
197
|
+
pawnSource('}');
|
|
198
|
+
}
|
|
199
|
+
if (usedFormats.has('s')) {
|
|
200
|
+
pawnSource('');
|
|
201
|
+
pawnSource('stock _DDE_fmt_s(aData{}, value{}) {');
|
|
202
|
+
pawnSource(PAWN_SZTEMP_DECL);
|
|
203
|
+
pawnSource('\tsprintf(szTemp, _, "%s", value);');
|
|
204
|
+
pawnSource(PAWN_STRCAT_RETURN);
|
|
205
|
+
pawnSource('}');
|
|
206
|
+
}
|
|
207
|
+
if (usedFormats.has('stamp')) {
|
|
208
|
+
pawnSource('');
|
|
209
|
+
pawnSource('stock _DDE_fmt_stamp(aData{}, value) {');
|
|
210
|
+
pawnSource(PAWN_SZTEMP_DECL);
|
|
211
|
+
pawnSource('\tsprintf(szTemp, _, "%s", (stampToISOString(value)));');
|
|
212
|
+
pawnSource(PAWN_STRCAT_RETURN);
|
|
213
|
+
pawnSource('}');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Builds the Pawn index expression for accessing an array element by byte offset.
|
|
218
|
+
*/
|
|
219
|
+
function buildIndexExpression(startOffset, step) {
|
|
220
|
+
if (step === 1) {
|
|
221
|
+
return startOffset === 0 ? 'idx' : `idx - ${startOffset}`;
|
|
222
|
+
}
|
|
223
|
+
return startOffset === 0 ? `idx / ${step}` : `(idx - ${startOffset}) / ${step}`;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Builds the Pawn condition for matching a byte offset to an array group.
|
|
227
|
+
*/
|
|
228
|
+
function buildConditionExpression(startOffset, endOffset, step) {
|
|
229
|
+
const rangeCheck = `idx >= ${startOffset} && idx <= ${endOffset}`;
|
|
230
|
+
if (step === 1) {
|
|
231
|
+
return rangeCheck;
|
|
232
|
+
}
|
|
233
|
+
const modCheck = startOffset === 0 ? `idx % ${step} == 0` : `(idx - ${startOffset}) % ${step} == 0`;
|
|
234
|
+
return `${rangeCheck} && ${modCheck}`;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Generates an if-block that collapses an array group into arithmetic index calculation.
|
|
238
|
+
*/
|
|
239
|
+
function buildArrayGroupBlock(pawnSource, group) {
|
|
240
|
+
const { startOffset, endOffset, step, formatType, pawnVarBase, subField } = group;
|
|
241
|
+
const indexExpr = buildIndexExpression(startOffset, step);
|
|
242
|
+
const condition = buildConditionExpression(startOffset, endOffset, step);
|
|
243
|
+
pawnSource(`\tif (${condition}) {`);
|
|
244
|
+
pawnSource(`\t\treturn _DDE_fmt_${formatType}(aData, ${pawnVarBase}[${indexExpr}]${subField});`);
|
|
245
|
+
pawnSource(`\t}`);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Builds a case statement for a standalone field.
|
|
249
|
+
* Uses compact helper function calls when available, falls back to inline code
|
|
250
|
+
* for packed types (s64, f64, stamp40) and memtostr (binary).
|
|
251
|
+
*/
|
|
252
|
+
function buildOptimizedCaseForField(pawnSource, containerTitle, field) {
|
|
253
|
+
if (field.type === 'shadow' || field.shadowRef) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
|
|
257
|
+
if (!tdefPwn || tdefPwn === 'skip') {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const fieldNamePrefix = field.library ? `_${helper_1.Helper.generateHashForLibraryName(field.library)}` : 'DDE_';
|
|
261
|
+
const ddeFieldName = `${fieldNamePrefix}${containerTitle}_${field.name}`;
|
|
262
|
+
const formatType = getHelperFormatType(tdefPwn);
|
|
263
|
+
if (formatType) {
|
|
264
|
+
// Compact single-line case using helper function
|
|
265
|
+
pawnSource(`\t\tcase ${field.byteOffset}: return _DDE_fmt_${formatType}(aData, ${ddeFieldName});`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// Fall back to inline code (memtostr, packed types, wstring, etc.)
|
|
269
|
+
pawnSource(`\t\tcase ${field.byteOffset}: {`);
|
|
270
|
+
if (tdefPwn.printf) {
|
|
271
|
+
let printfInsert = ddeFieldName;
|
|
272
|
+
if (tdefPwn.printfFormat) {
|
|
273
|
+
printfInsert = `(${tdefPwn.printfFormat}(${printfInsert}))`;
|
|
274
|
+
}
|
|
275
|
+
pawnSource(`\t\t\tsprintf(szTemp, _, "${tdefPwn.printf}", ${printfInsert});`);
|
|
276
|
+
pawnSource(`\t\t\treturn (strcat(aData, szTemp, sizeof(szTemp)));`);
|
|
277
|
+
}
|
|
278
|
+
else if (tdefPwn.printfFormat === 'memtostr') {
|
|
279
|
+
pawnSource(`\t\t\tmemtostr(szTemp, ${ddeFieldName}, 0, ${ddeFieldName}_dim);`);
|
|
280
|
+
pawnSource(`\t\t\treturn ${ddeFieldName}_dim;`);
|
|
281
|
+
}
|
|
282
|
+
pawnSource(`\t\t}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function buildCaseForContainerName(pawnSource, containerName, containerDefinition) {
|
|
286
|
+
pawnSource(`\t\t\tcase '${containerName.at(-1)}': {`);
|
|
287
|
+
pawnSource(`\t\t\t\treturn _DDE_local_${containerDefinition.title}(aData, idx);`);
|
|
288
|
+
pawnSource(`\t\t\t}`);
|
|
289
|
+
}
|
|
290
|
+
function buildIfWithSwitchForContainerType(pawnSource, ddeMap, type) {
|
|
291
|
+
const cname0 = type === coreDefinitions_1.DDE_Core.ContainerType.cx ? 'c' : 'h';
|
|
292
|
+
const nameFilter = type === coreDefinitions_1.DDE_Core.ContainerType.cx ? 'config' : 'histdata';
|
|
293
|
+
pawnSource(`\tif(cname{0} == '${cname0}'){`);
|
|
294
|
+
pawnSource(`\t\tswitch(cname{1}) {`);
|
|
295
|
+
for (const [containerName, containerDefinition] of Object.entries(ddeMap.containers)) {
|
|
296
|
+
if (type === containerDefinition.containerType &&
|
|
297
|
+
containerName.startsWith(nameFilter) &&
|
|
298
|
+
containerDefinition.transferDirection !== TransferDirection.backendOnly) {
|
|
299
|
+
buildCaseForContainerName(pawnSource, containerName, containerDefinition);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
pawnSource(`\t\t\tdefault:`);
|
|
303
|
+
pawnSource(`\t\t\t\treturn OK;`);
|
|
304
|
+
pawnSource(`\t\t}`);
|
|
305
|
+
pawnSource(`\t\treturn OK;`);
|
|
306
|
+
pawnSource(`\t}`);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Generates an optimized per-container local data return function.
|
|
310
|
+
* Uses if-blocks for collapsed array groups and a switch for standalone fields.
|
|
311
|
+
* Only declares szTemp when inline (non-helper) fields are present.
|
|
312
|
+
*/
|
|
313
|
+
function buildOptimizedLocalDataReturnFunction(pawnSource, containerDefinition) {
|
|
314
|
+
const analysis = analyzeContainerFields(containerDefinition.title, containerDefinition.fields);
|
|
315
|
+
pawnSource('');
|
|
316
|
+
pawnSource(`stock _DDE_local_${containerDefinition.title}(aData{}, idx) {`);
|
|
317
|
+
// Declare szTemp only if there are inline fields that need it
|
|
318
|
+
if (analysis.hasInlineFields) {
|
|
319
|
+
pawnSource(`\tnew szTemp{64};`);
|
|
320
|
+
}
|
|
321
|
+
// Generate if-blocks for array groups (before the switch)
|
|
322
|
+
for (const group of analysis.arrayGroups) {
|
|
323
|
+
buildArrayGroupBlock(pawnSource, group);
|
|
324
|
+
}
|
|
325
|
+
// Generate switch for standalone fields
|
|
326
|
+
if (analysis.standaloneFields.length > 0) {
|
|
327
|
+
pawnSource(`\tswitch(idx) {`);
|
|
328
|
+
for (const field of analysis.standaloneFields) {
|
|
329
|
+
buildOptimizedCaseForField(pawnSource, containerDefinition.title, field);
|
|
330
|
+
}
|
|
331
|
+
pawnSource(`\t\tdefault:`);
|
|
332
|
+
pawnSource(`\t\t\treturn OK;`);
|
|
333
|
+
pawnSource(`\t}`);
|
|
334
|
+
}
|
|
335
|
+
pawnSource(`\treturn OK;`);
|
|
336
|
+
pawnSource(`}`);
|
|
337
|
+
}
|
|
338
|
+
function buildOptimizedLocalDataReturnFunctions(pawnSource, ddeMap) {
|
|
339
|
+
for (const [, containerDefinition] of Object.entries(ddeMap.containers)) {
|
|
340
|
+
if (!isDeviceContainer(containerDefinition)) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
buildOptimizedLocalDataReturnFunction(pawnSource, containerDefinition);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function generateDloLocalDataIncFromMap(ddeMap) {
|
|
347
|
+
const _pwn = [];
|
|
348
|
+
const pwn = _pwn.push.bind(_pwn);
|
|
349
|
+
// Generate helper functions based on which format types are actually used
|
|
350
|
+
const usedFormats = collectUsedFormats(ddeMap);
|
|
351
|
+
buildHelperFunctions(pwn, usedFormats);
|
|
352
|
+
// Generate main dispatch function
|
|
353
|
+
if (usedFormats.size > 0) {
|
|
354
|
+
pwn('');
|
|
355
|
+
}
|
|
356
|
+
pwn(`stock DDE_get_local_data(cname{}, aData{}, idx) {`);
|
|
357
|
+
buildIfWithSwitchForContainerType(pwn, ddeMap, coreDefinitions_1.DDE_Core.ContainerType.cx);
|
|
358
|
+
buildIfWithSwitchForContainerType(pwn, ddeMap, coreDefinitions_1.DDE_Core.ContainerType.hx);
|
|
359
|
+
pwn(`\treturn OK;`);
|
|
360
|
+
pwn(`}`);
|
|
361
|
+
// Generate optimized per-container functions
|
|
362
|
+
buildOptimizedLocalDataReturnFunctions(pwn, ddeMap);
|
|
363
|
+
return _pwn.join('\n');
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=dloLocalData.js.map
|
package/out/defaultFiles.js
CHANGED
|
@@ -22,6 +22,7 @@ var DefaultFiles;
|
|
|
22
22
|
autoUplinkInc: 'app-uplink.inc',
|
|
23
23
|
autoDdeInc: 'X-dde.inc',
|
|
24
24
|
mainDdeInc: 'main-dde.inc',
|
|
25
|
+
autoLocalDataInc: 'auto-local-data.inc',
|
|
25
26
|
legacyAutoInc: 'legacy-auto.inc',
|
|
26
27
|
legacyAutoDdeInc: 'legacy-auto-dde.inc',
|
|
27
28
|
legacyAutoUplinkInc: 'legacy-auto-uplink.inc',
|
|
@@ -43,6 +44,7 @@ var DefaultFiles;
|
|
|
43
44
|
autoInc: `${studioAutoDloPath}${DefaultFiles.fileNames.autoInc}`,
|
|
44
45
|
defaultInc: `${studioAutoDloPath}${DefaultFiles.fileNames.defaultInc}`,
|
|
45
46
|
autoUplinkInc: `${studioAutoDloPath}${DefaultFiles.fileNames.autoUplinkInc}`,
|
|
47
|
+
autoLocalDataInc: `${studioAutoDloPath}${DefaultFiles.fileNames.autoLocalDataInc}`,
|
|
46
48
|
autoDdeInc: `${studioAutoDloPath}${DefaultFiles.fileNames.autoDdeInc}`,
|
|
47
49
|
mainDdeInc: `${studioAutoDloPath}${DefaultFiles.fileNames.mainDdeInc}`,
|
|
48
50
|
legacyAutoInc: `${studioAutoDloPath}${DefaultFiles.fileNames.legacyAutoInc}`,
|