wc3maptranslator 3.0.3 → 4.0.1

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.
Files changed (100) hide show
  1. package/.codeclimate.yml +0 -1
  2. package/.eslintignore +0 -1
  3. package/.github/workflows/codeql-analysis.yml +54 -0
  4. package/.nycrc +1 -2
  5. package/.travis.yml +1 -2
  6. package/CHANGELOG.md +78 -0
  7. package/LICENSE.md +23 -0
  8. package/README.md +68 -75
  9. package/index.js +11 -15
  10. package/index.ts +12 -30
  11. package/lib/CommonInterfaces.ts +22 -0
  12. package/lib/HexBuffer.ts +11 -14
  13. package/lib/W3Buffer.ts +7 -3
  14. package/lib/translators/CamerasTranslator.ts +36 -41
  15. package/lib/translators/DoodadsTranslator.ts +57 -55
  16. package/lib/translators/ImportsTranslator.ts +29 -31
  17. package/lib/translators/InfoTranslator.ts +252 -204
  18. package/lib/translators/{object/ObjectsTranslator.ts → ObjectsTranslator.ts} +84 -82
  19. package/lib/translators/RegionsTranslator.ts +40 -58
  20. package/lib/translators/SoundsTranslator.ts +112 -96
  21. package/lib/translators/StringsTranslator.ts +23 -49
  22. package/lib/translators/TerrainTranslator.ts +166 -97
  23. package/lib/translators/UnitsTranslator.ts +98 -97
  24. package/lib/translators/index.ts +0 -1
  25. package/package.json +23 -16
  26. package/test/.mocharc.json +4 -0
  27. package/test/HexBufferTest.ts +11 -15
  28. package/test/TranslatorReversion.ts +104 -115
  29. package/test/W3BufferTest.ts +19 -13
  30. package/test/data/cameras.json +16 -0
  31. package/test/data/doodads.json +2730 -0
  32. package/test/data/imports.json +386 -0
  33. package/test/data/info.json +250 -0
  34. package/test/data/obj-abilities.json +4892 -0
  35. package/test/data/obj-buffs.json +38 -0
  36. package/test/data/obj-destructables.json +31 -0
  37. package/test/data/obj-doodads.json +38 -0
  38. package/test/data/obj-items.json +31 -0
  39. package/test/data/obj-units.json +40 -0
  40. package/test/data/obj-upgrades.json +38 -0
  41. package/test/data/regions.json +206 -0
  42. package/test/data/sounds.json +50 -0
  43. package/test/data/strings.json +115 -0
  44. package/test/data/terrain.json +1 -0
  45. package/test/data/units.json +452 -0
  46. package/test/data/war3map.doo +0 -0
  47. package/test/data/war3map.imp +0 -0
  48. package/test/data/war3map.j +3442 -0
  49. package/test/data/war3map.shd +0 -0
  50. package/test/data/war3map.w3a +0 -0
  51. package/test/data/war3map.w3b +0 -0
  52. package/test/data/war3map.w3c +0 -0
  53. package/test/data/war3map.w3d +0 -0
  54. package/test/data/war3map.w3e +0 -0
  55. package/test/data/war3map.w3h +0 -0
  56. package/test/data/war3map.w3i +0 -0
  57. package/test/data/war3map.w3q +0 -0
  58. package/test/data/war3map.w3r +0 -0
  59. package/test/data/war3map.w3s +0 -0
  60. package/test/data/war3map.w3t +0 -0
  61. package/test/data/war3map.w3u +0 -0
  62. package/test/data/war3map.wts +631 -0
  63. package/test/data/war3mapUnits.doo +0 -0
  64. package/examples/index.js +0 -123
  65. package/examples/index.ts +0 -134
  66. package/examples/json/cameras.json +0 -16
  67. package/examples/json/doodads.json +0 -28499
  68. package/examples/json/imports.json +0 -10
  69. package/examples/json/info.json +0 -111
  70. package/examples/json/object-abilities.json +0 -31
  71. package/examples/json/object-buffs.json +0 -24
  72. package/examples/json/object-destructables.json +0 -31
  73. package/examples/json/object-doodads.json +0 -24
  74. package/examples/json/object-items.json +0 -45
  75. package/examples/json/object-units.json +0 -45
  76. package/examples/json/object-upgrades.json +0 -31
  77. package/examples/json/regions.json +0 -36
  78. package/examples/json/sounds.json +0 -46
  79. package/examples/json/strings.json +0 -83
  80. package/examples/json/terrain.json +0 -46633
  81. package/examples/json/units.json +0 -332
  82. package/examples/package.json +0 -17
  83. package/examples/tsconfig.json +0 -27
  84. package/examples/war/war3map.doo +0 -0
  85. package/examples/war/war3map.imp +0 -0
  86. package/examples/war/war3map.w3a +0 -0
  87. package/examples/war/war3map.w3b +0 -0
  88. package/examples/war/war3map.w3c +0 -0
  89. package/examples/war/war3map.w3d +0 -0
  90. package/examples/war/war3map.w3e +0 -0
  91. package/examples/war/war3map.w3h +0 -0
  92. package/examples/war/war3map.w3i +0 -0
  93. package/examples/war/war3map.w3q +0 -0
  94. package/examples/war/war3map.w3r +0 -0
  95. package/examples/war/war3map.w3s +0 -0
  96. package/examples/war/war3map.w3t +0 -0
  97. package/examples/war/war3map.w3u +0 -0
  98. package/examples/war/war3map.wts +0 -99
  99. package/lib/translators/index.js +0 -16
  100. package/test/mocha.opts +0 -4
@@ -1,15 +1,17 @@
1
1
  import { HexBuffer } from '../HexBuffer';
2
2
  import { W3Buffer } from '../W3Buffer';
3
+ import { WarResult, JsonResult } from '../CommonInterfaces'
3
4
 
4
5
  interface Sound {
5
6
  name: string;
7
+ variableName: string;
6
8
  path: string;
7
- eax?: EAXEffect;
9
+ eax: string;
8
10
  flags: SoundFlags;
9
11
  fadeRate: FadeRate;
10
12
  volume: number;
11
13
  pitch: number;
12
- channel: SoundChannel;
14
+ channel: number;
13
15
  distance: Distance;
14
16
  }
15
17
 
@@ -31,65 +33,35 @@ interface Distance {
31
33
  cutoff: number;
32
34
  }
33
35
 
34
- enum EAXEffect {
35
- Default = 'DefaultEAXON',
36
- Combat = 'CombatSoundsEAX',
37
- Drums = 'KotoDrumsEAX',
38
- Spells = 'SpellsEAX',
39
- Missiles = 'MissilesEAX',
40
- HeroSpeech = 'HeroAcksEAX',
41
- Doodads = 'DoodadsEAX'
42
- }
43
-
44
- enum SoundChannel {
45
- General = 0,
46
- UnitSelection,
47
- UnitAcknowledgement,
48
- UnitMovement,
49
- UnitReady,
50
- Combat,
51
- Error,
52
- Music,
53
- UserInterface,
54
- LoopingMovement,
55
- LoopingAmbient,
56
- Animations,
57
- Constructions,
58
- Birth,
59
- Fire
60
- }
61
-
62
- export class SoundsTranslator {
63
-
64
- private _outBufferToWar: HexBuffer;
65
- private _outBufferToJSON: W3Buffer;
66
-
67
- constructor() { }
36
+ export abstract class SoundsTranslator {
68
37
 
69
- public jsonToWar(soundsJson: Sound[]) {
70
- this._outBufferToWar = new HexBuffer();
38
+ public static jsonToWar(soundsJson: Sound[]): WarResult {
39
+ const outBufferToWar = new HexBuffer();
71
40
 
72
41
  /*
73
42
  * Header
74
43
  */
75
- this._outBufferToWar.addInt(1); // file version
76
- this._outBufferToWar.addInt(soundsJson.length); // number of sounds
44
+ outBufferToWar.addInt(3); // file version
45
+ outBufferToWar.addInt(soundsJson.length); // number of sounds
77
46
 
78
47
  /*
79
48
  * Body
80
49
  */
81
50
  soundsJson.forEach((sound) => {
82
- // Name with null terminator (e.g. gg_snd_HumanGlueScreenLoop1)
83
- this._outBufferToWar.addString(sound.name);
84
- this._outBufferToWar.addNullTerminator();
85
-
86
- // Path with null terminator (e.g. Sound\Ambient\HumanGlueScreenLoop1.wav)
87
- this._outBufferToWar.addString(sound.path);
88
- this._outBufferToWar.addNullTerminator();
89
-
90
- // EAX effects enum
91
- this._outBufferToWar.addString(sound.eax || EAXEffect.Default); // defaults to "DefaultEAXON"
92
- this._outBufferToWar.addNullTerminator();
51
+ outBufferToWar.addString(sound.name); // e.g. gg_snd_HumanGlueScreenLoop1
52
+ outBufferToWar.addString(sound.path); // e.g. Sound\Ambient\HumanGlueScreenLoop1.wav
53
+
54
+ // EAX effects enum (e.g. missiles, speech, etc)
55
+ /*
56
+ default = DefaultEAXON
57
+ combat = CombatSoundsEAX
58
+ drums = KotoDrumsEAX
59
+ spells = SpellsEAX
60
+ missiles = MissilesEAX
61
+ hero speech = HeroAcksEAX
62
+ doodads = DoodadsEAX
63
+ */
64
+ outBufferToWar.addString(sound.eax || 'DefaultEAXON'); // defaults to "DefaultEAXON"
93
65
 
94
66
  // Flags, if present (optional)
95
67
  let flags = 0;
@@ -99,57 +71,88 @@ export class SoundsTranslator {
99
71
  if (sound.flags.stopOutOfRange) flags |= 0x4;
100
72
  if (sound.flags.music) flags |= 0x8;
101
73
  }
102
- this._outBufferToWar.addInt(flags);
74
+ outBufferToWar.addInt(flags);
103
75
 
104
76
  // Fade in and out rate (optional)
105
- this._outBufferToWar.addInt(sound.fadeRate ? sound.fadeRate.in || 10 : 10); // default to 10
106
- this._outBufferToWar.addInt(sound.fadeRate ? sound.fadeRate.out || 10 : 10); // default to 10
77
+ outBufferToWar.addInt(sound.fadeRate ? sound.fadeRate.in || 10 : 10); // default to 10
78
+ outBufferToWar.addInt(sound.fadeRate ? sound.fadeRate.out || 10 : 10); // default to 10
107
79
 
108
80
  // Volume (optional)
109
- this._outBufferToWar.addInt(sound.volume || -1); // default to -1 (for normal volume)
81
+ outBufferToWar.addInt(sound.volume || -1); // default to -1 (for normal volume)
110
82
 
111
83
  // Pitch (optional)
112
- this._outBufferToWar.addFloat(sound.pitch || 1.0); // default to 1.0 for normal pitch
84
+ outBufferToWar.addFloat(sound.pitch || 1.0); // default to 1.0 for normal pitch
113
85
 
114
86
  // Mystery numbers... their use is unknown by the w3x documentation, but they must be present
115
- this._outBufferToWar.addFloat(0);
116
- this._outBufferToWar.addInt(8); // or -1?
117
-
118
- // Sound channel from SoundChannel enum
119
- this._outBufferToWar.addInt(sound.channel || SoundChannel.General); // default to "General" sound channel (0)
87
+ outBufferToWar.addFloat(0);
88
+ outBufferToWar.addInt(8); // or -1?
89
+
90
+ // Which channel to use? Use the lookup table for more details (optional)
91
+ /*
92
+ 0=General
93
+ 1=Unit Selection
94
+ 2=Unit Acknowledgement
95
+ 3=Unit Movement
96
+ 4=Unit Ready
97
+ 5=Combat
98
+ 6=Error
99
+ 7=Music
100
+ 8=User Interface
101
+ 9=Looping Movement
102
+ 10=Looping Ambient
103
+ 11=Animations
104
+ 12=Constructions
105
+ 13=Birth
106
+ 14=Fire
107
+ */
108
+ outBufferToWar.addInt(sound.channel || 0); // default to 0
120
109
 
121
110
  // Distance fields
122
- this._outBufferToWar.addFloat(sound.distance.min);
123
- this._outBufferToWar.addFloat(sound.distance.max);
124
- this._outBufferToWar.addFloat(sound.distance.cutoff);
111
+ outBufferToWar.addFloat(sound.distance.min);
112
+ outBufferToWar.addFloat(sound.distance.max);
113
+ outBufferToWar.addFloat(sound.distance.cutoff);
125
114
 
126
115
  // More mystery numbers...
127
- this._outBufferToWar.addFloat(0);
128
- this._outBufferToWar.addFloat(0);
129
- this._outBufferToWar.addFloat(127); // or -1?
130
- this._outBufferToWar.addFloat(0);
131
- this._outBufferToWar.addFloat(0);
132
- this._outBufferToWar.addFloat(0);
116
+ outBufferToWar.addFloat(0);
117
+ outBufferToWar.addFloat(0);
118
+ outBufferToWar.addFloat(127); // or -1?
119
+ outBufferToWar.addFloat(0);
120
+ outBufferToWar.addFloat(0);
121
+ outBufferToWar.addFloat(0);
122
+
123
+ outBufferToWar.addString(sound.variableName);
124
+ outBufferToWar.addString('');
125
+ outBufferToWar.addString(sound.path);
126
+
127
+ // More unknowns
128
+ outBufferToWar.addFloat(0);
129
+ outBufferToWar.addByte(0);
130
+ outBufferToWar.addFloat(0);
131
+ outBufferToWar.addFloat(0);
132
+ outBufferToWar.addFloat(0);
133
+ outBufferToWar.addByte(0);
134
+ outBufferToWar.addFloat(0);
133
135
  });
134
136
 
135
137
  return {
136
138
  errors: [],
137
- buffer: this._outBufferToWar.getBuffer()
139
+ buffer: outBufferToWar.getBuffer()
138
140
  };
139
141
  }
140
142
 
141
- public warToJson(buffer: Buffer) {
143
+ public static warToJson(buffer: Buffer): JsonResult<Sound[]> {
142
144
  const result = [];
143
- this._outBufferToJSON = new W3Buffer(buffer);
145
+ const outBufferToJSON = new W3Buffer(buffer);
144
146
 
145
- const fileVersion = this._outBufferToJSON.readInt(), // File version
146
- numSounds = this._outBufferToJSON.readInt(); // # of sounds
147
+ const fileVersion = outBufferToJSON.readInt(), // File version
148
+ numSounds = outBufferToJSON.readInt(); // # of sounds
147
149
 
148
150
  for (let i = 0; i < numSounds; i++) {
149
151
  const sound: Sound = {
150
152
  name: '',
153
+ variableName: '',
151
154
  path: '',
152
- eax: EAXEffect.Default,
155
+ eax: '',
153
156
  volume: 0,
154
157
  pitch: 0,
155
158
  channel: 0,
@@ -170,11 +173,11 @@ export class SoundsTranslator {
170
173
  }
171
174
  };
172
175
 
173
- sound.name = this._outBufferToJSON.readString();
174
- sound.path = this._outBufferToJSON.readString();
175
- sound.eax = EAXEffect[this._outBufferToJSON.readString()];
176
+ sound.name = outBufferToJSON.readString();
177
+ sound.path = outBufferToJSON.readString();
178
+ sound.eax = outBufferToJSON.readString();
176
179
 
177
- const flags = this._outBufferToJSON.readInt();
180
+ const flags = outBufferToJSON.readInt();
178
181
  sound.flags = {
179
182
  'looping': !!(flags & 0b1), // 0x00000001=looping
180
183
  '3dSound': !!(flags & 0b10), // 0x00000002=3D sound
@@ -183,32 +186,45 @@ export class SoundsTranslator {
183
186
  };
184
187
 
185
188
  sound.fadeRate = {
186
- in: this._outBufferToJSON.readInt(),
187
- out: this._outBufferToJSON.readInt()
189
+ in: outBufferToJSON.readInt(),
190
+ out: outBufferToJSON.readInt()
188
191
  };
189
192
 
190
- sound.volume = this._outBufferToJSON.readInt();
191
- sound.pitch = this._outBufferToJSON.readFloat();
193
+ sound.volume = outBufferToJSON.readInt();
194
+ sound.pitch = outBufferToJSON.readFloat();
192
195
 
193
196
  // Unknown values
194
- this._outBufferToJSON.readFloat();
195
- this._outBufferToJSON.readInt();
197
+ outBufferToJSON.readFloat();
198
+ outBufferToJSON.readInt();
196
199
 
197
- sound.channel = this._outBufferToJSON.readInt();
200
+ sound.channel = outBufferToJSON.readInt();
198
201
 
199
202
  sound.distance = {
200
- min: this._outBufferToJSON.readFloat(),
201
- max: this._outBufferToJSON.readFloat(),
202
- cutoff: this._outBufferToJSON.readFloat()
203
+ min: outBufferToJSON.readFloat(),
204
+ max: outBufferToJSON.readFloat(),
205
+ cutoff: outBufferToJSON.readFloat()
203
206
  };
204
207
 
205
208
  // Unknown values
206
- this._outBufferToJSON.readFloat();
207
- this._outBufferToJSON.readFloat();
208
- this._outBufferToJSON.readFloat();
209
- this._outBufferToJSON.readFloat();
210
- this._outBufferToJSON.readFloat();
211
- this._outBufferToJSON.readFloat();
209
+ outBufferToJSON.readFloat();
210
+ outBufferToJSON.readFloat();
211
+ outBufferToJSON.readFloat();
212
+ outBufferToJSON.readFloat();
213
+ outBufferToJSON.readFloat();
214
+ outBufferToJSON.readFloat();
215
+
216
+ sound.variableName = outBufferToJSON.readString();
217
+
218
+ // Unknown values
219
+ outBufferToJSON.readString();
220
+ outBufferToJSON.readString();
221
+ outBufferToJSON.readChars(4);
222
+ outBufferToJSON.readChars(1);
223
+ outBufferToJSON.readChars(4);
224
+ outBufferToJSON.readChars(4);
225
+ outBufferToJSON.readChars(4);
226
+ outBufferToJSON.readChars(1);
227
+ outBufferToJSON.readChars(4);
212
228
 
213
229
  result.push(sound);
214
230
  }
@@ -1,70 +1,44 @@
1
1
  import { HexBuffer } from '../HexBuffer';
2
+ import { WarResult, JsonResult } from '../CommonInterfaces'
2
3
 
3
- interface StringDefinition {
4
- id: number;
5
- text: string;
6
- comment?: string;
7
- }
8
-
9
- export class StringsTranslator {
10
- private _outBufferToWar: HexBuffer;
11
-
12
- constructor() { }
4
+ export abstract class StringsTranslator {
13
5
 
14
- public jsonToWar(stringsJson: StringDefinition[]) {
15
- this._outBufferToWar = new HexBuffer();
16
-
17
- // Add UTF8 byte-order mark (BOM), which is in the .wts file generated by World Editor
18
- // You can see the BOM when printing the buffer of the .wts file
19
- this._outBufferToWar.addBytes([0xef, 0xbb, 0xbf]); // see: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
6
+ public static jsonToWar(stringsJson: object): WarResult {
7
+ const outBufferToWar = new HexBuffer();
20
8
 
21
9
  /*
22
10
  * Strings
23
11
  */
24
- for (const strDef of stringsJson) {
25
- this._outBufferToWar.addString('STRING ' + strDef.id);
26
- this._outBufferToWar.addNewLine();
27
-
28
- if (strDef.comment) { // Comment can be empty, in which case skip this party
29
- this._outBufferToWar.addString(strDef.comment);
30
- this._outBufferToWar.addNewLine();
31
- }
12
+ Object.keys(stringsJson).forEach((key) => {
13
+ outBufferToWar.addChars('STRING ' + key);
14
+ outBufferToWar.addNewLine();
15
+ outBufferToWar.addChars('{');
16
+ outBufferToWar.addNewLine();
17
+ outBufferToWar.addChars(stringsJson[key]);
18
+ outBufferToWar.addNewLine();
19
+ outBufferToWar.addChars('}');
20
+ outBufferToWar.addNewLine();
21
+ outBufferToWar.addNewLine();
22
+ });
32
23
 
33
- this._outBufferToWar.addString('{');
34
- this._outBufferToWar.addNewLine();
35
-
36
- this._outBufferToWar.addString(strDef.text);
37
- this._outBufferToWar.addNewLine();
38
-
39
- this._outBufferToWar.addString('}');
40
- this._outBufferToWar.addNewLine();
41
-
42
- this._outBufferToWar.addNewLine();
43
- }
44
24
  return {
45
25
  errors: [],
46
- buffer: this._outBufferToWar.getBuffer()
26
+ buffer: outBufferToWar.getBuffer()
47
27
  };
48
28
  }
49
29
 
50
- public warToJson(buffer: Buffer) {
30
+ public static warToJson(buffer: Buffer): JsonResult<object> {
51
31
  const wts = buffer.toString().replace(/\r\n/g, '\n'), // may contain Windows linebreaks (\r\n), but below regex just assumes \n
52
- matchStringDefinitionBlock = new RegExp('STRING ([0-9]+)\n?((?:.*\n))?\{\n((?:.|\n)*?)\n}', 'g'); // see: https://regexr.com/4r6m0
32
+ matchStringDefinitionBlock = new RegExp('STRING ([0-9]+)\n?(?:.*\n)?\{\n((?:.|\n)*?)\n}', 'g'); // see: https://regexr.com/3r572
53
33
 
54
- const result: StringDefinition[] = []; // stores the json form of strings file
55
- let match: RegExpExecArray; // stores individual matches as input is read
34
+ const result = {}; // stores the json form of strings file
35
+ let match; // stores individual matches as input is read
56
36
 
57
37
  // tslint:disable-next-line: no-conditional-assignment
58
38
  while ((match = matchStringDefinitionBlock.exec(wts)) !== null) {
59
- const num = parseInt(match[1], 10),
60
- comment = match[2]?.replace(/\n/gi, ''), // if the comment is present, the regex from above will have pulled out the newline, which needs to be stripped out
61
- body = match[3];
62
-
63
- result.push({
64
- id: num,
65
- text: body,
66
- comment
67
- });
39
+ const num = match[1],
40
+ body = match[2];
41
+ result[num] = body;
68
42
  }
69
43
 
70
44
  return {