@microtronics/studio-cli 0.57.0 → 0.59.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.
@@ -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(this.variableNameForPawnDefinition(`${variableName}_scale`, field), field.scale);
539
+ this.addDefine(`${variableName}_scale`, field.scale);
539
540
  }
540
541
  if (field.offset !== undefined) {
541
- this.addDefine(this.variableNameForPawnDefinition(`${variableName}_offset`, field), field.offset);
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,384 @@
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, wstring, etc.)
13
+ */
14
+ function getHelperFormatType(tdefPwn) {
15
+ // Binary (memtostr) fields - check before printf since printf is null for binary
16
+ if (!tdefPwn.printf && tdefPwn.printfFormat === 'memtostr') {
17
+ return 'bin';
18
+ }
19
+ if (!tdefPwn.printf) {
20
+ return null;
21
+ }
22
+ const rwp = tdefPwn.rwp || '%n';
23
+ const hasCellArray = rwp.includes('[');
24
+ const hasPackedArray = rwp.includes('{');
25
+ const isComplex = hasPackedArray || hasCellArray;
26
+ if (tdefPwn.printfFormat === 'stampToISOString') {
27
+ return isComplex ? null : 'stamp';
28
+ }
29
+ switch (tdefPwn.printf) {
30
+ case '%d':
31
+ return isComplex ? null : 'd';
32
+ case '%f':
33
+ return isComplex ? null : 'f';
34
+ case '%s':
35
+ return hasCellArray ? null : 's';
36
+ default:
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Gets the format type for a field, handling shadow/skip filtering.
42
+ * Returns null if the field should be skipped or needs inline code.
43
+ */
44
+ function getFieldFormatType(field) {
45
+ if (field.type === 'shadow' || field.shadowRef) {
46
+ return null;
47
+ }
48
+ const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
49
+ if (!tdefPwn || tdefPwn === 'skip') {
50
+ return null;
51
+ }
52
+ return getHelperFormatType(tdefPwn);
53
+ }
54
+ /**
55
+ * Checks if a container is device-side (hx or cx) and not backend-only.
56
+ */
57
+ function isDeviceContainer(containerDefinition) {
58
+ return ([coreDefinitions_1.DDE_Core.ContainerType.hx, coreDefinitions_1.DDE_Core.ContainerType.cx].includes(containerDefinition.containerType) &&
59
+ containerDefinition.transferDirection !== TransferDirection.backendOnly);
60
+ }
61
+ /**
62
+ * Checks if a field is valid for code generation (not shadow, not skipped).
63
+ */
64
+ function isValidField(field) {
65
+ if (field.type === 'shadow' || field.shadowRef) {
66
+ return false;
67
+ }
68
+ const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
69
+ return !!tdefPwn && tdefPwn !== 'skip';
70
+ }
71
+ /**
72
+ * Adds an array field to the appropriate group in the group map.
73
+ */
74
+ function addFieldToGroup(field, formatType, groupMap) {
75
+ const bracketEnd = field.name.indexOf(']');
76
+ const subField = bracketEnd >= 0 ? field.name.substring(bracketEnd + 1) : '';
77
+ const groupKey = `${field.arrayRef.name}|${subField}|${formatType}`;
78
+ if (!groupMap.has(groupKey)) {
79
+ groupMap.set(groupKey, { fields: [], formatType });
80
+ }
81
+ groupMap.get(groupKey).fields.push(field);
82
+ }
83
+ /**
84
+ * Classifies fields into array-groupable and standalone categories.
85
+ */
86
+ function classifyFields(fields) {
87
+ const standaloneFields = [];
88
+ const groupMap = new Map();
89
+ const usedFormats = new Set();
90
+ let hasInlineFields = false;
91
+ for (const field of fields) {
92
+ if (!isValidField(field)) {
93
+ continue;
94
+ }
95
+ const formatType = getFieldFormatType(field);
96
+ if (field.arrayRef && formatType) {
97
+ addFieldToGroup(field, formatType, groupMap);
98
+ }
99
+ else {
100
+ standaloneFields.push(field);
101
+ if (formatType) {
102
+ usedFormats.add(formatType);
103
+ }
104
+ else {
105
+ hasInlineFields = true;
106
+ }
107
+ }
108
+ }
109
+ return { standaloneFields, groupMap, usedFormats, hasInlineFields };
110
+ }
111
+ /**
112
+ * Converts a group map entry into an ArrayFieldGroup if it has consistent stepping.
113
+ * Returns null if the group should be treated as standalone fields.
114
+ */
115
+ function tryBuildArrayGroup(containerTitle, groupKey, groupFields) {
116
+ groupFields.sort((a, b) => a.arrayRef.index - b.arrayRef.index);
117
+ const offsets = groupFields.map(f => f.byteOffset);
118
+ const step = offsets[1] - offsets[0];
119
+ const isConsistent = step > 0 && offsets.every((o, i) => i === 0 || o - offsets[i - 1] === step);
120
+ if (!isConsistent) {
121
+ return null;
122
+ }
123
+ const [arrayName, subField] = groupKey.split('|');
124
+ const fieldNamePrefix = groupFields[0].library
125
+ ? `_${helper_1.Helper.generateHashForLibraryName(groupFields[0].library)}`
126
+ : 'DDE_';
127
+ return {
128
+ arrayName,
129
+ subField,
130
+ fields: groupFields,
131
+ startOffset: offsets[0],
132
+ endOffset: offsets[offsets.length - 1],
133
+ step,
134
+ formatType: getFieldFormatType(groupFields[0]),
135
+ pawnVarBase: `${fieldNamePrefix}${containerTitle}_${arrayName}`
136
+ };
137
+ }
138
+ /**
139
+ * Analyzes container fields and groups array elements that can be collapsed
140
+ * into arithmetic if-blocks for reduced AMX binary size.
141
+ */
142
+ function analyzeContainerFields(containerTitle, fields) {
143
+ const { standaloneFields, groupMap, usedFormats, hasInlineFields } = classifyFields(fields);
144
+ const arrayGroups = [];
145
+ for (const [groupKey, groupData] of groupMap) {
146
+ const { fields: groupFields, formatType } = groupData;
147
+ if (groupFields.length < 2) {
148
+ standaloneFields.push(...groupFields);
149
+ usedFormats.add(formatType);
150
+ continue;
151
+ }
152
+ const group = tryBuildArrayGroup(containerTitle, groupKey, groupFields);
153
+ if (group) {
154
+ usedFormats.add(formatType);
155
+ arrayGroups.push(group);
156
+ }
157
+ else {
158
+ standaloneFields.push(...groupFields);
159
+ usedFormats.add(formatType);
160
+ }
161
+ }
162
+ standaloneFields.sort((a, b) => a.byteOffset - b.byteOffset);
163
+ return { arrayGroups, standaloneFields, usedFormats, hasInlineFields };
164
+ }
165
+ /**
166
+ * Scans all eligible container fields to determine which helper format types are needed.
167
+ */
168
+ function collectUsedFormats(ddeMap) {
169
+ const usedFormats = new Set();
170
+ for (const [, containerDefinition] of Object.entries(ddeMap.containers)) {
171
+ if (!isDeviceContainer(containerDefinition)) {
172
+ continue;
173
+ }
174
+ for (const field of containerDefinition.fields) {
175
+ const formatType = getFieldFormatType(field);
176
+ if (formatType) {
177
+ usedFormats.add(formatType);
178
+ }
179
+ }
180
+ }
181
+ return usedFormats;
182
+ }
183
+ /**
184
+ * Generates Pawn helper functions that encapsulate the sprintf+strcat pattern.
185
+ * Only generates the helpers that are actually used, reducing dead code.
186
+ * Each helper replaces 3 lines of repeated code per field with a single call.
187
+ */
188
+ function buildHelperFunctions(pawnSource, usedFormats) {
189
+ if (usedFormats.has('d')) {
190
+ pawnSource('stock _DDE_fmt_d(aData{}, value) {');
191
+ pawnSource(PAWN_SZTEMP_DECL);
192
+ pawnSource('\tsprintf(szTemp, _, "%d", value);');
193
+ pawnSource(PAWN_STRCAT_RETURN);
194
+ pawnSource('}');
195
+ }
196
+ if (usedFormats.has('f')) {
197
+ pawnSource('');
198
+ pawnSource('stock _DDE_fmt_f(aData{}, Float:value) {');
199
+ pawnSource(PAWN_SZTEMP_DECL);
200
+ pawnSource('\tsprintf(szTemp, _, "%.3f", value);');
201
+ pawnSource(PAWN_STRCAT_RETURN);
202
+ pawnSource('}');
203
+ }
204
+ if (usedFormats.has('s')) {
205
+ pawnSource('');
206
+ pawnSource('stock _DDE_fmt_s(aData{}, value{}) {');
207
+ pawnSource(PAWN_SZTEMP_DECL);
208
+ pawnSource('\tsprintf(szTemp, _, "%s", value);');
209
+ pawnSource(PAWN_STRCAT_RETURN);
210
+ pawnSource('}');
211
+ }
212
+ if (usedFormats.has('stamp')) {
213
+ pawnSource('');
214
+ pawnSource('stock _DDE_fmt_stamp(aData{}, value) {');
215
+ pawnSource(PAWN_SZTEMP_DECL);
216
+ pawnSource('\tsprintf(szTemp, _, "%s", (stampToISOString(value)));');
217
+ pawnSource(PAWN_STRCAT_RETURN);
218
+ pawnSource('}');
219
+ }
220
+ if (usedFormats.has('bin')) {
221
+ pawnSource('');
222
+ pawnSource('stock _DDE_fmt_bin(aData{}, mem{}, dim) {');
223
+ pawnSource(PAWN_SZTEMP_DECL);
224
+ pawnSource('\tmemtostr(szTemp, mem, 0, dim);');
225
+ pawnSource('\treturn dim;');
226
+ pawnSource('}');
227
+ }
228
+ }
229
+ /**
230
+ * Builds the Pawn index expression for accessing an array element by byte offset.
231
+ */
232
+ function buildIndexExpression(startOffset, step) {
233
+ if (step === 1) {
234
+ return startOffset === 0 ? 'idx' : `idx - ${startOffset}`;
235
+ }
236
+ return startOffset === 0 ? `idx / ${step}` : `(idx - ${startOffset}) / ${step}`;
237
+ }
238
+ /**
239
+ * Builds the Pawn condition for matching a byte offset to an array group.
240
+ */
241
+ function buildConditionExpression(startOffset, endOffset, step) {
242
+ const rangeCheck = `idx >= ${startOffset} && idx <= ${endOffset}`;
243
+ if (step === 1) {
244
+ return rangeCheck;
245
+ }
246
+ const modCheck = startOffset === 0 ? `idx % ${step} == 0` : `(idx - ${startOffset}) % ${step} == 0`;
247
+ return `${rangeCheck} && ${modCheck}`;
248
+ }
249
+ /**
250
+ * Generates an if-block that collapses an array group into arithmetic index calculation.
251
+ */
252
+ function buildArrayGroupBlock(pawnSource, group) {
253
+ const { startOffset, endOffset, step, formatType, pawnVarBase, subField } = group;
254
+ const indexExpr = buildIndexExpression(startOffset, step);
255
+ const condition = buildConditionExpression(startOffset, endOffset, step);
256
+ pawnSource(`\tif (${condition}) {`);
257
+ if (formatType === 'bin') {
258
+ const fieldAccess = `${pawnVarBase}[${indexExpr}]${subField}`;
259
+ pawnSource(`\t\treturn _DDE_fmt_bin(aData, ${fieldAccess}, ${fieldAccess}_dim);`);
260
+ }
261
+ else {
262
+ pawnSource(`\t\treturn _DDE_fmt_${formatType}(aData, ${pawnVarBase}[${indexExpr}]${subField});`);
263
+ }
264
+ pawnSource(`\t}`);
265
+ }
266
+ /**
267
+ * Builds a case statement for a standalone field.
268
+ * Uses compact helper function calls when available, falls back to inline code
269
+ * for packed types (s64, f64, stamp40) and memtostr (binary).
270
+ */
271
+ function buildOptimizedCaseForField(pawnSource, containerTitle, field) {
272
+ if (field.type === 'shadow' || field.shadowRef) {
273
+ return;
274
+ }
275
+ const tdefPwn = (0, dlo_1.getPawnTypeDefinition)(field);
276
+ if (!tdefPwn || tdefPwn === 'skip') {
277
+ return;
278
+ }
279
+ const fieldNamePrefix = field.library ? `_${helper_1.Helper.generateHashForLibraryName(field.library)}` : 'DDE_';
280
+ const ddeFieldName = `${fieldNamePrefix}${containerTitle}_${field.name}`;
281
+ const formatType = getHelperFormatType(tdefPwn);
282
+ if (formatType === 'bin') {
283
+ // Compact single-line case using binary helper function
284
+ pawnSource(`\t\tcase ${field.byteOffset}: return _DDE_fmt_bin(aData, ${ddeFieldName}, ${ddeFieldName}_dim);`);
285
+ }
286
+ else if (formatType) {
287
+ // Compact single-line case using helper function
288
+ pawnSource(`\t\tcase ${field.byteOffset}: return _DDE_fmt_${formatType}(aData, ${ddeFieldName});`);
289
+ }
290
+ else {
291
+ // Fall back to inline code (packed types, wstring, etc.)
292
+ pawnSource(`\t\tcase ${field.byteOffset}: {`);
293
+ if (tdefPwn.printf) {
294
+ let printfInsert = ddeFieldName;
295
+ if (tdefPwn.printfFormat) {
296
+ printfInsert = `(${tdefPwn.printfFormat}(${printfInsert}))`;
297
+ }
298
+ pawnSource(`\t\t\tsprintf(szTemp, _, "${tdefPwn.printf}", ${printfInsert});`);
299
+ pawnSource(`\t\t\treturn (strcat(aData, szTemp, sizeof(szTemp)));`);
300
+ }
301
+ pawnSource(`\t\t}`);
302
+ }
303
+ }
304
+ function buildCaseForContainerName(pawnSource, containerName, containerDefinition) {
305
+ pawnSource(`\t\t\tcase '${containerName.at(-1)}': {`);
306
+ pawnSource(`\t\t\t\treturn _DDE_local_${containerDefinition.title}(aData, idx);`);
307
+ pawnSource(`\t\t\t}`);
308
+ }
309
+ function buildIfWithSwitchForContainerType(pawnSource, ddeMap, type) {
310
+ const cname0 = type === coreDefinitions_1.DDE_Core.ContainerType.cx ? 'c' : 'h';
311
+ const nameFilter = type === coreDefinitions_1.DDE_Core.ContainerType.cx ? 'config' : 'histdata';
312
+ pawnSource(`\tif(cname{0} == '${cname0}'){`);
313
+ pawnSource(`\t\tswitch(cname{1}) {`);
314
+ for (const [containerName, containerDefinition] of Object.entries(ddeMap.containers)) {
315
+ if (type === containerDefinition.containerType &&
316
+ containerName.startsWith(nameFilter) &&
317
+ containerDefinition.transferDirection !== TransferDirection.backendOnly) {
318
+ buildCaseForContainerName(pawnSource, containerName, containerDefinition);
319
+ }
320
+ }
321
+ pawnSource(`\t\t\tdefault:`);
322
+ pawnSource(`\t\t\t\treturn OK;`);
323
+ pawnSource(`\t\t}`);
324
+ pawnSource(`\t\treturn OK;`);
325
+ pawnSource(`\t}`);
326
+ }
327
+ /**
328
+ * Generates an optimized per-container local data return function.
329
+ * Uses if-blocks for collapsed array groups and a switch for standalone fields.
330
+ * Only declares szTemp when inline (non-helper) fields are present.
331
+ */
332
+ function buildOptimizedLocalDataReturnFunction(pawnSource, containerDefinition) {
333
+ const analysis = analyzeContainerFields(containerDefinition.title, containerDefinition.fields);
334
+ pawnSource('');
335
+ pawnSource(`stock _DDE_local_${containerDefinition.title}(aData{}, idx) {`);
336
+ // Declare szTemp only if there are inline fields that need it
337
+ if (analysis.hasInlineFields) {
338
+ pawnSource(`\tnew szTemp{64};`);
339
+ }
340
+ // Generate if-blocks for array groups (before the switch)
341
+ for (const group of analysis.arrayGroups) {
342
+ buildArrayGroupBlock(pawnSource, group);
343
+ }
344
+ // Generate switch for standalone fields
345
+ if (analysis.standaloneFields.length > 0) {
346
+ pawnSource(`\tswitch(idx) {`);
347
+ for (const field of analysis.standaloneFields) {
348
+ buildOptimizedCaseForField(pawnSource, containerDefinition.title, field);
349
+ }
350
+ pawnSource(`\t\tdefault:`);
351
+ pawnSource(`\t\t\treturn OK;`);
352
+ pawnSource(`\t}`);
353
+ }
354
+ pawnSource(`\treturn OK;`);
355
+ pawnSource(`}`);
356
+ }
357
+ function buildOptimizedLocalDataReturnFunctions(pawnSource, ddeMap) {
358
+ for (const [, containerDefinition] of Object.entries(ddeMap.containers)) {
359
+ if (!isDeviceContainer(containerDefinition)) {
360
+ continue;
361
+ }
362
+ buildOptimizedLocalDataReturnFunction(pawnSource, containerDefinition);
363
+ }
364
+ }
365
+ function generateDloLocalDataIncFromMap(ddeMap) {
366
+ const _pwn = [];
367
+ const pwn = _pwn.push.bind(_pwn);
368
+ // Generate helper functions based on which format types are actually used
369
+ const usedFormats = collectUsedFormats(ddeMap);
370
+ buildHelperFunctions(pwn, usedFormats);
371
+ // Generate main dispatch function
372
+ if (usedFormats.size > 0) {
373
+ pwn('');
374
+ }
375
+ pwn(`stock DDE_get_local_data(cname{}, aData{}, idx) {`);
376
+ buildIfWithSwitchForContainerType(pwn, ddeMap, coreDefinitions_1.DDE_Core.ContainerType.cx);
377
+ buildIfWithSwitchForContainerType(pwn, ddeMap, coreDefinitions_1.DDE_Core.ContainerType.hx);
378
+ pwn(`\treturn OK;`);
379
+ pwn(`}`);
380
+ // Generate optimized per-container functions
381
+ buildOptimizedLocalDataReturnFunctions(pwn, ddeMap);
382
+ return _pwn.join('\n');
383
+ }
384
+ //# sourceMappingURL=dloLocalData.js.map
@@ -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}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.57.0",
3
+ "version": "0.59.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",