s2cfgtojson 3.3.0 → 3.5.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/Struct.mts CHANGED
@@ -13,17 +13,33 @@ const KEYWORDS = [
13
13
  "bpatch", // allows patching only specific keys
14
14
  ];
15
15
  const REMOVE_NODE = "removenode";
16
- const INTERNAL_PROPS = new Set([
17
- "__internal__",
18
- "fork",
19
- "removeNode",
20
- "addNode",
21
- "clone",
22
- "forEach",
23
- "filter",
24
- "map",
25
- "toString",
26
- ] satisfies Array<keyof Struct>);
16
+ const INTERNAL_PROPS = new Map([
17
+ ["__internal__", "_"],
18
+ ["fork", ""], // methods
19
+ ["removeNode", ""], // methods
20
+ ["addNode", ""], // methods
21
+ ["clone", ""], // methods
22
+ ["forEach", ""], // methods
23
+ ["filter", ""], // methods
24
+ ["map", ""], // methods
25
+ ["toString", ""], // methods
26
+ ] as const);
27
+ const INTERNAL_PROPS_INV = new Map(
28
+ Array.from(INTERNAL_PROPS.entries()).map(([k, v]) => [v, k]),
29
+ );
30
+ const REF_INTERNAL_PROPS = new Map([
31
+ ["rawName", "w"],
32
+ ["refurl", "u"],
33
+ ["refkey", "k"],
34
+ ["bskipref", "s"],
35
+ ["bpatch", "p"],
36
+ ["isArray", "a"],
37
+ ["isRoot", "r"],
38
+ ["useAsterisk", "*"],
39
+ ] as const);
40
+ const REF_INTERNAL_PROPS_INV = new Map(
41
+ Array.from(REF_INTERNAL_PROPS.entries()).map(([k, v]) => [v, k]),
42
+ );
27
43
 
28
44
  /**
29
45
  * This file is part of the Stalker 2 Modding Tools project.
@@ -152,11 +168,27 @@ export class Struct {
152
168
  return clone;
153
169
  }
154
170
 
155
- static fromJson<T>(obj: T): T extends object ? GetStructType<T> : T {
171
+ static fromJson<T>(
172
+ obj: T,
173
+ minified = false,
174
+ ): T extends object ? GetStructType<T> : T {
156
175
  if (typeof obj === "object" && !!obj) {
157
176
  const instance = new Struct();
158
177
  Object.entries(obj).forEach(([key, value]) => {
159
- instance[key] = Struct.fromJson(value);
178
+ const nKey = fromMinifiedKey(key, minified);
179
+
180
+ if (nKey === "__internal__") {
181
+ instance[nKey] = new Refs(
182
+ Object.fromEntries(
183
+ Object.entries(value).map(([k, v]) => [
184
+ fromMinifiedKey(k, minified),
185
+ v,
186
+ ]),
187
+ ),
188
+ );
189
+ } else {
190
+ instance[nKey] = Struct.fromJson(value, minified);
191
+ }
160
192
  });
161
193
  return instance as any;
162
194
  }
@@ -164,6 +196,24 @@ export class Struct {
164
196
  return obj as any;
165
197
  }
166
198
 
199
+ toJson<T extends object>(minify = false): T {
200
+ const obj = {};
201
+
202
+ Object.entries(this).forEach(([key, value]) => {
203
+ let nKey = maybeMinifyKey(key, minify);
204
+ if (value instanceof Struct) {
205
+ obj[nKey] = value.toJson(minify);
206
+ } else if (value instanceof Refs) {
207
+ obj[nKey] = Object.fromEntries(
208
+ Object.entries(value).map(([k, v]) => [maybeMinifyKey(k, minify), v]),
209
+ );
210
+ } else {
211
+ obj[nKey] = value;
212
+ }
213
+ });
214
+ return obj as T;
215
+ }
216
+
167
217
  toString(): string {
168
218
  if (!(this.__internal__ instanceof Refs)) {
169
219
  this.__internal__ = new Refs(this.__internal__);
@@ -431,3 +481,19 @@ function parseStructName(name: string): string {
431
481
  .replace(/_+/g, "_")
432
482
  .replace(/^_+/, "");
433
483
  }
484
+
485
+ function maybeMinifyKey(key: string, minify: boolean) {
486
+ return minify &&
487
+ (INTERNAL_PROPS.has(key as any) || REF_INTERNAL_PROPS.has(key as any))
488
+ ? INTERNAL_PROPS.get(key as any) || REF_INTERNAL_PROPS.get(key as any)
489
+ : key;
490
+ }
491
+
492
+ function fromMinifiedKey(key: string, minified: boolean) {
493
+ if (!minified) return key;
494
+ return (
495
+ INTERNAL_PROPS_INV.get(key as any) ||
496
+ REF_INTERNAL_PROPS_INV.get(key as any) ||
497
+ key
498
+ );
499
+ }
package/Struct.test.mts CHANGED
@@ -10,32 +10,36 @@ import {
10
10
  import fs from "node:fs";
11
11
 
12
12
  class ChimeraHPFix extends Struct {
13
- __internal__ = {
13
+ __internal__ = new Refs({
14
14
  rawName: "ChimeraHPFix",
15
15
  isRoot: true,
16
16
  bskipref: true,
17
- };
17
+ });
18
18
 
19
19
  MaxHP = 750;
20
20
  }
21
21
  class TradePrototype extends Struct {
22
- __internal__ = {
22
+ __internal__ = new Refs({
23
23
  rawName: "TradersDontBuyWeaponsArmor",
24
24
  refurl: "../TradePrototypes.cfg",
25
25
  refkey: 0,
26
26
  isRoot: true,
27
- };
27
+ });
28
28
 
29
29
  TradeGenerators = new TradeGenerators();
30
30
  }
31
31
  class TradeGenerators extends Struct {
32
32
  __internal__ = new Refs({
33
33
  isArray: true,
34
+ rawName: "TradeGenerators",
34
35
  //useAsterisk: true, this option is not supported for now
35
36
  });
36
37
  "0" = new TradeGenerator();
37
38
  }
38
39
  class TradeGenerator extends Struct {
40
+ __internal__ = new Refs({
41
+ rawName: "0",
42
+ });
39
43
  BuyLimitations = new BuyLimitations();
40
44
  }
41
45
 
@@ -270,7 +274,7 @@ struct.end`;
270
274
  expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("EItemType::Weapon");
271
275
  expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("EItemType::Armor");
272
276
  a.TradeGenerators[0].BuyLimitations.forEach(([k]) => {
273
- a.TradeGenerators[0].BuyLimitations[k] = "forEach";
277
+ a.TradeGenerators[0].BuyLimitations[k] = "forEach" as any;
274
278
  });
275
279
  expect(a.TradeGenerators[0].BuyLimitations[0]).toBe("forEach");
276
280
  expect(a.TradeGenerators[0].BuyLimitations[1]).toBe("forEach");
@@ -321,6 +325,39 @@ struct.end`;
321
325
  });
322
326
  });
323
327
 
328
+ describe("toJson", () => {
329
+ test("1", () => {
330
+ const struct = new TradePrototype();
331
+ expect(struct.toJson()).toEqual({
332
+ __internal__: {
333
+ rawName: "TradersDontBuyWeaponsArmor",
334
+ isRoot: true,
335
+ refurl: "../TradePrototypes.cfg",
336
+ refkey: 0,
337
+ },
338
+ TradeGenerators: {
339
+ __internal__: { rawName: "TradeGenerators", isArray: true },
340
+ "0": {
341
+ __internal__: { rawName: "0" },
342
+ BuyLimitations: {
343
+ __internal__: { rawName: "BuyLimitations", isArray: true },
344
+ "0": "EItemType::Weapon",
345
+ "1": "EItemType::Armor",
346
+ },
347
+ },
348
+ },
349
+ });
350
+ });
351
+
352
+ test("2", () => {
353
+ const struct = new ChimeraHPFix();
354
+ expect(struct.toJson(true)).toEqual({
355
+ _: { w: "ChimeraHPFix", r: true, s: true },
356
+ MaxHP: 750,
357
+ });
358
+ });
359
+ });
360
+
324
361
  describe("fromJson", () => {
325
362
  test("1", () => {
326
363
  const json = {
@@ -367,6 +404,54 @@ struct.end`;
367
404
  struct.end
368
405
  struct.end
369
406
  struct.end
407
+ struct.end`);
408
+ });
409
+
410
+ test("2", () => {
411
+ const json = {
412
+ _: { w: "Test", r: true },
413
+ MeshGenerator: {
414
+ _: { w: "MeshGenerator" },
415
+ Meshes: {
416
+ _: { w: "Meshes", a: true },
417
+ "0": {
418
+ _: { w: "0" },
419
+ MeshPath: "path/to/mesh",
420
+ Offset: {
421
+ _: { w: "Offset" },
422
+ X: 0,
423
+ Y: 0,
424
+ Z: 0,
425
+ },
426
+ Rotation: {
427
+ _: { w: "Rotation" },
428
+ Pitch: 0,
429
+ Yaw: 0,
430
+ Roll: 0,
431
+ },
432
+ },
433
+ },
434
+ },
435
+ };
436
+ const struct = Struct.fromJson(json, true);
437
+ expect(struct.toString()).toBe(`Test : struct.begin
438
+ MeshGenerator : struct.begin
439
+ Meshes : struct.begin
440
+ [0] : struct.begin
441
+ MeshPath = path/to/mesh
442
+ Offset : struct.begin
443
+ X = 0
444
+ Y = 0
445
+ Z = 0
446
+ struct.end
447
+ Rotation : struct.begin
448
+ Pitch = 0
449
+ Yaw = 0
450
+ Roll = 0
451
+ struct.end
452
+ struct.end
453
+ struct.end
454
+ struct.end
370
455
  struct.end`);
371
456
  });
372
457
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "s2cfgtojson",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "Converts Stalker 2 Cfg file into POJOs",
5
5
  "keywords": [
6
6
  "stalker",