libwz 0.1.0 → 0.1.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.
- package/README.md +462 -101
- package/dist/libwz.js +350 -390
- package/dist/libwz.wasm +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,55 +1,232 @@
|
|
|
1
1
|
# libwz
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
> C# reference (included as a submodule for test data only).
|
|
3
|
+
libwz is a C++23 library for reading, traversing, editing, and exporting
|
|
4
|
+
MapleStory `.wz` archive data. The core parser is implemented in native C++ and
|
|
5
|
+
is exposed through three public surfaces:
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- C++: static libraries for the parser and optional C ABI layer
|
|
8
|
+
- Java: JNI bindings plus Java wrapper classes
|
|
9
|
+
- JavaScript: one ESM package that uses a native Node addon in Node.js and a
|
|
10
|
+
WebAssembly backend when requested or selected by browser bundlers
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
The project is an independent clean-room reimplementation guided by the
|
|
13
|
+
HaRepacker/MapleLib behavior in the `Harepacker-resurrected/` submodule. That
|
|
14
|
+
submodule is used as a reference and as a source of test data; source code is not
|
|
15
|
+
copied from it.
|
|
16
|
+
|
|
17
|
+
## Status
|
|
18
|
+
|
|
19
|
+
This project is under active development. The parser already covers common read,
|
|
20
|
+
traversal, image, sound, Lua, UOL, and editing workflows, but APIs may still
|
|
21
|
+
change before a stable release.
|
|
12
22
|
|
|
13
23
|
## Features
|
|
14
24
|
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
- UOL
|
|
21
|
-
-
|
|
25
|
+
- PKG1 `.wz` archive parsing with regional MapleStory keys
|
|
26
|
+
- 32-bit and 64-bit WZ version handling
|
|
27
|
+
- Directory and image traversal
|
|
28
|
+
- Property support for null, numeric, string, sub, canvas, vector, convex,
|
|
29
|
+
sound, raw, UOL, Lua, PNG, and video-like/raw data properties
|
|
30
|
+
- UOL and canvas link resolution
|
|
31
|
+
- Canvas/PNG export to PNG bytes or files
|
|
32
|
+
- Sound export to raw MP3/WAV-compatible bytes or files
|
|
22
33
|
- Lua script decoding
|
|
23
|
-
-
|
|
24
|
-
- C
|
|
25
|
-
- JNI
|
|
26
|
-
-
|
|
27
|
-
- Node.js native addon and WebAssembly package entries for JavaScript users
|
|
34
|
+
- WZ editing and save-to-disk support in native-capable builds
|
|
35
|
+
- C ABI layer for FFI consumers
|
|
36
|
+
- JNI wrapper with `AutoCloseable` resource management
|
|
37
|
+
- JavaScript ESM wrapper with native Node and WebAssembly backends
|
|
28
38
|
|
|
29
39
|
## Requirements
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
41
|
+
Core C++ build:
|
|
42
|
+
|
|
43
|
+
- CMake 3.16+
|
|
44
|
+
- C++23 compiler with `<expected>` support
|
|
45
|
+
- Git with submodule support
|
|
46
|
+
|
|
47
|
+
Optional bindings:
|
|
48
|
+
|
|
49
|
+
- Java 21 and Maven for JNI/JAR builds
|
|
50
|
+
- Node.js `^20.19.0 || >=22.12.0` for the JavaScript package
|
|
51
|
+
- node-gyp toolchain for the native Node addon
|
|
52
|
+
- Emscripten for the WebAssembly build
|
|
53
|
+
|
|
54
|
+
## Repository Layout
|
|
55
|
+
|
|
56
|
+
| Path | Purpose |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| `include/wz/` | Public C++ headers |
|
|
59
|
+
| `include/wz/Properties/` | Concrete WZ property types |
|
|
60
|
+
| `include/wz/wz_api.h` | C ABI header |
|
|
61
|
+
| `src/` | C++ implementation |
|
|
62
|
+
| `src/capi/` | C ABI implementation |
|
|
63
|
+
| `src/jni/` | JNI native bridge |
|
|
64
|
+
| `src/node/` | Node-API / emnapi binding |
|
|
65
|
+
| `java/` | Java wrapper and Maven project |
|
|
66
|
+
| `js/` | TypeScript JavaScript wrapper source |
|
|
67
|
+
| `tests/` | C++ and JavaScript tests |
|
|
68
|
+
| `Harepacker-resurrected/` | C# reference and WZ test data submodule |
|
|
69
|
+
|
|
70
|
+
## Build From Source
|
|
34
71
|
|
|
35
|
-
|
|
72
|
+
Clone with submodules:
|
|
36
73
|
|
|
37
74
|
```bash
|
|
38
75
|
git clone --recurse-submodules https://github.com/toyobayashi/libwz.git
|
|
39
76
|
cd libwz
|
|
77
|
+
```
|
|
40
78
|
|
|
41
|
-
|
|
42
|
-
|
|
79
|
+
Build the core C++ libraries:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
83
|
+
cmake --build build --config Release
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Build with JNI enabled:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_JNI=ON
|
|
90
|
+
cmake --build build --config Release
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Build the JavaScript package from source:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npm install
|
|
97
|
+
npm run build
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Build the WebAssembly artifacts:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm install
|
|
104
|
+
npm run build:wasm
|
|
105
|
+
```
|
|
43
106
|
|
|
44
|
-
|
|
45
|
-
|
|
107
|
+
Run tests:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cmake -S . -B build -DBUILD_TESTS=ON
|
|
46
111
|
cmake --build build
|
|
47
112
|
ctest --test-dir build
|
|
113
|
+
|
|
114
|
+
npm test
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The parser tests use sample WZ files from
|
|
118
|
+
`Harepacker-resurrected/UnitTest_WzFile/WzFiles/`.
|
|
119
|
+
|
|
120
|
+
## C++ Usage
|
|
121
|
+
|
|
122
|
+
Include the public headers from `include/wz/` and link the `wz` target.
|
|
123
|
+
|
|
124
|
+
```cpp
|
|
125
|
+
#include <iostream>
|
|
126
|
+
#include "wz/WzFile.h"
|
|
127
|
+
#include "wz/WzImage.h"
|
|
128
|
+
#include "wz/Properties/WzCanvasProperty.h"
|
|
129
|
+
|
|
130
|
+
int main() {
|
|
131
|
+
wz::WzFile file("Character.wz", wz::WzMapleVersion::GMS);
|
|
132
|
+
|
|
133
|
+
if (file.ParseWzFile() != wz::WzFileParseStatus::Success) {
|
|
134
|
+
std::cerr << "failed to parse WZ file\n";
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
auto* root = file.GetWzDirectory();
|
|
139
|
+
if (root == nullptr) return 1;
|
|
140
|
+
|
|
141
|
+
std::cout << "root: " << root->Name() << "\n";
|
|
142
|
+
|
|
143
|
+
for (auto* image : root->WzImages()) {
|
|
144
|
+
auto parsed = image->ParseImage();
|
|
145
|
+
if (!parsed.has_value()) continue;
|
|
146
|
+
|
|
147
|
+
for (auto* prop : *image->WzProperties()) {
|
|
148
|
+
std::cout << image->Name() << "/" << prop->Name() << "\n";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Open a file when you already know the encrypted version or IV:
|
|
155
|
+
|
|
156
|
+
```cpp
|
|
157
|
+
wz::WzFile byVersion("Character.wz", 87, wz::WzMapleVersion::GMS);
|
|
158
|
+
wz::WzFile byIv("Character.wz", std::array<uint8_t, 4>{0x4D, 0x23, 0xC7, 0x2B});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Look up an object by WZ path:
|
|
162
|
+
|
|
163
|
+
```cpp
|
|
164
|
+
wz::WzObject* obj = file.GetObjectFromPath("Character/00002000.img/info");
|
|
165
|
+
if (obj != nullptr) {
|
|
166
|
+
std::cout << obj->FullPath() << "\n";
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Export a canvas or PNG property:
|
|
171
|
+
|
|
172
|
+
```cpp
|
|
173
|
+
auto* image = root->GetImageByName("00002000.img");
|
|
174
|
+
if (image != nullptr && image->ParseImage().has_value()) {
|
|
175
|
+
auto* prop = image->GetFromPath("stand/0");
|
|
176
|
+
if (prop != nullptr && prop->PropertyType() == wz::WzPropertyType::Canvas) {
|
|
177
|
+
auto* canvas = static_cast<wz::WzCanvasProperty*>(prop);
|
|
178
|
+
auto* png = canvas->PngProperty();
|
|
179
|
+
if (png != nullptr) {
|
|
180
|
+
auto saved = png->SaveToFile("stand-0.png");
|
|
181
|
+
if (!saved.has_value()) {
|
|
182
|
+
std::cerr << saved.error().message() << "\n";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
48
187
|
```
|
|
49
188
|
|
|
50
|
-
|
|
189
|
+
Create and save a small WZ file:
|
|
51
190
|
|
|
52
|
-
|
|
191
|
+
```cpp
|
|
192
|
+
#include "wz/Properties/WzIntProperty.h"
|
|
193
|
+
#include "wz/Properties/WzStringProperty.h"
|
|
194
|
+
|
|
195
|
+
wz::WzFile out(87, wz::WzMapleVersion::GMS);
|
|
196
|
+
auto* dir = out.GetWzDirectory();
|
|
197
|
+
auto imageResult = dir->CreateImage("Example.img");
|
|
198
|
+
if (!imageResult.has_value()) return 1;
|
|
199
|
+
|
|
200
|
+
auto* image = imageResult.value();
|
|
201
|
+
auto added = image->AddProperty(
|
|
202
|
+
std::make_unique<wz::WzStringProperty>("name", "libwz"));
|
|
203
|
+
if (!added.has_value()) return 1;
|
|
204
|
+
|
|
205
|
+
auto saved = out.SaveToDisk("Example.wz");
|
|
206
|
+
if (!saved.has_value()) return 1;
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
libwz is built with exceptions disabled. APIs that can fail return
|
|
210
|
+
`wz::Result<T>` (`std::expected<T, wz::Error>`) where practical. Check
|
|
211
|
+
`has_value()` before using a result.
|
|
212
|
+
|
|
213
|
+
## Java Usage
|
|
214
|
+
|
|
215
|
+
The Java layer lives in the Maven project under `java/`. It wraps the JNI bridge
|
|
216
|
+
with ordinary Java classes such as `WzFile`, `WzDirectory`, `WzImage`, and
|
|
217
|
+
`WzImageProperty`.
|
|
218
|
+
|
|
219
|
+
Build the JNI library and package the JAR locally:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_JNI=ON
|
|
223
|
+
cmake --build build --config Release
|
|
224
|
+
|
|
225
|
+
cd java
|
|
226
|
+
mvn package
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
When consuming a published artifact, the Maven coordinates are:
|
|
53
230
|
|
|
54
231
|
```xml
|
|
55
232
|
<dependency>
|
|
@@ -59,107 +236,291 @@ Add the dependency (available on Maven Central):
|
|
|
59
236
|
</dependency>
|
|
60
237
|
```
|
|
61
238
|
|
|
62
|
-
|
|
239
|
+
Open and traverse a WZ file:
|
|
240
|
+
|
|
241
|
+
```java
|
|
242
|
+
import io.github.toyobayashi.libwz.WzDirectory;
|
|
243
|
+
import io.github.toyobayashi.libwz.WzEnums.MapleVersion;
|
|
244
|
+
import io.github.toyobayashi.libwz.WzEnums.ParseStatus;
|
|
245
|
+
import io.github.toyobayashi.libwz.WzFile;
|
|
246
|
+
import io.github.toyobayashi.libwz.WzImage;
|
|
247
|
+
|
|
248
|
+
public final class ReadWz {
|
|
249
|
+
public static void main(String[] args) {
|
|
250
|
+
try (WzFile file = new WzFile("Character.wz", MapleVersion.GMS)) {
|
|
251
|
+
ParseStatus status = file.parseWzFile();
|
|
252
|
+
if (status != ParseStatus.SUCCESS) {
|
|
253
|
+
throw new IllegalStateException("parse failed: " + status);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
WzDirectory root = file.getWzDirectory();
|
|
257
|
+
System.out.println(root.getName());
|
|
258
|
+
|
|
259
|
+
for (WzImage image : root.wzImages()) {
|
|
260
|
+
image.parseImage();
|
|
261
|
+
System.out.println(image.getName());
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Read properties and export a canvas:
|
|
63
269
|
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
270
|
+
```java
|
|
271
|
+
import io.github.toyobayashi.libwz.WzCanvasProperty;
|
|
272
|
+
import io.github.toyobayashi.libwz.WzFile;
|
|
273
|
+
import io.github.toyobayashi.libwz.WzImage;
|
|
274
|
+
import io.github.toyobayashi.libwz.WzImageProperty;
|
|
275
|
+
import static io.github.toyobayashi.libwz.WzEnums.MapleVersion.GMS;
|
|
276
|
+
|
|
277
|
+
try (WzFile file = new WzFile("Character.wz", GMS)) {
|
|
278
|
+
file.parseWzFile();
|
|
279
|
+
|
|
280
|
+
WzImage image = file.getWzDirectory().getImageByName("00002000.img");
|
|
281
|
+
image.parseImage();
|
|
282
|
+
|
|
283
|
+
WzImageProperty prop = image.getFromPath("stand/0");
|
|
284
|
+
if (prop instanceof WzCanvasProperty canvas) {
|
|
285
|
+
canvas.saveToFile("stand-0.png");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
69
288
|
```
|
|
70
289
|
|
|
71
|
-
|
|
290
|
+
Look up by WZ path:
|
|
72
291
|
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
292
|
+
```java
|
|
293
|
+
var object = file.getObjectFromPath("Character/00002000.img/info");
|
|
294
|
+
if (object != null) {
|
|
295
|
+
System.out.println(object.getName());
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`WzFile` owns the native file handle and implements `AutoCloseable`; use
|
|
300
|
+
try-with-resources. Objects returned from a file, directory, image, or property
|
|
301
|
+
are borrowed wrappers and become invalid after the owning file is closed.
|
|
302
|
+
|
|
303
|
+
## JavaScript Usage
|
|
304
|
+
|
|
305
|
+
The JavaScript package is ESM-only. Its root entry exports the same wrapper
|
|
306
|
+
classes for native Node and WebAssembly:
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
import {
|
|
310
|
+
init,
|
|
311
|
+
MapleVersion,
|
|
312
|
+
ParseStatus,
|
|
313
|
+
WzBinaryProperty,
|
|
314
|
+
WzCanvasProperty,
|
|
315
|
+
WzFile,
|
|
316
|
+
getWzBindingType
|
|
317
|
+
} from 'libwz'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
When installed from npm after publication:
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
npm install libwz
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
When using the repository directly:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
npm install
|
|
330
|
+
npm run build
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Node.js Native Backend
|
|
334
|
+
|
|
335
|
+
In Node.js, the package tries to install the native addon when imported. Calling
|
|
336
|
+
`init()` is still supported and is a no-op when native loading succeeded.
|
|
337
|
+
|
|
338
|
+
```js
|
|
339
|
+
import { init, MapleVersion, ParseStatus, WzFile } from 'libwz'
|
|
340
|
+
|
|
341
|
+
await init()
|
|
342
|
+
|
|
343
|
+
using file = new WzFile('Character.wz', MapleVersion.GMS)
|
|
344
|
+
const status = file.parseWzFile()
|
|
345
|
+
if (status !== ParseStatus.SUCCESS) {
|
|
346
|
+
throw new Error(`parse failed: ${status}`)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const root = file.getWzDirectory()
|
|
350
|
+
for (const image of root.wzImages()) {
|
|
351
|
+
image.parseImage()
|
|
352
|
+
console.log(image.getName())
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
JavaScript wrappers support explicit resource management through
|
|
357
|
+
`[Symbol.dispose]()` and `close()`. If your runtime does not support `using`,
|
|
358
|
+
write the cleanup explicitly:
|
|
359
|
+
|
|
360
|
+
```js
|
|
361
|
+
const file = new WzFile('Character.wz', MapleVersion.GMS)
|
|
362
|
+
try {
|
|
363
|
+
file.parseWzFile()
|
|
364
|
+
console.log(file.getWzDirectory()?.getName())
|
|
365
|
+
} finally {
|
|
366
|
+
file.close()
|
|
367
|
+
}
|
|
80
368
|
```
|
|
81
369
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
370
|
+
### WebAssembly Backend
|
|
371
|
+
|
|
372
|
+
Force WebAssembly in Node.js:
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
import { init, MapleVersion, WzFile, getWzBindingType } from 'libwz'
|
|
376
|
+
|
|
377
|
+
await init({ forceWasm: true })
|
|
378
|
+
console.log(getWzBindingType()) // "wasm"
|
|
379
|
+
|
|
380
|
+
using file = new WzFile('Character.wz', MapleVersion.GMS)
|
|
381
|
+
file.parseWzFile()
|
|
382
|
+
```
|
|
85
383
|
|
|
86
|
-
|
|
384
|
+
Pass a custom Wasm URL when a bundler or CDN serves `libwz.wasm` from another
|
|
385
|
+
location:
|
|
87
386
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for the Wasm runtime.
|
|
387
|
+
```js
|
|
388
|
+
await init(new URL('./assets/libwz.wasm', import.meta.url))
|
|
389
|
+
```
|
|
92
390
|
|
|
93
|
-
|
|
94
|
-
in Node.js 20.16.0 and 22.3.0, but the package also relies on the modern
|
|
95
|
-
`require(esm)` interoperability that is available by default from Node.js
|
|
96
|
-
20.19.0 and 22.12.0; Node.js 24.x is covered by the `>=22.12.0` range.
|
|
391
|
+
Open from bytes:
|
|
97
392
|
|
|
98
393
|
```js
|
|
99
|
-
import {
|
|
394
|
+
import { MapleVersion, WzFile } from 'libwz'
|
|
395
|
+
|
|
396
|
+
const bytes = new Uint8Array(await fileInput.files[0].arrayBuffer())
|
|
397
|
+
using file = WzFile.fromBytes('Character.wz', bytes, MapleVersion.GMS)
|
|
398
|
+
file.parseWzFile()
|
|
399
|
+
```
|
|
100
400
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
401
|
+
For large browser files, use callback-backed input so the Wasm backend can read
|
|
402
|
+
ranges without copying the whole file. In a Worker, `FileReaderSync` is one
|
|
403
|
+
simple way to implement the callback:
|
|
404
|
+
|
|
405
|
+
```js
|
|
406
|
+
const reader = new FileReaderSync()
|
|
407
|
+
|
|
408
|
+
using file = WzFile.fromBlobSource(
|
|
409
|
+
'Character.wz',
|
|
410
|
+
blob.size,
|
|
411
|
+
MapleVersion.GMS,
|
|
412
|
+
(offset, length, destination) => {
|
|
413
|
+
const start = Number(offset)
|
|
414
|
+
const end = start + Number(length)
|
|
415
|
+
const bytes = new Uint8Array(
|
|
416
|
+
reader.readAsArrayBuffer(blob.slice(start, end))
|
|
417
|
+
)
|
|
418
|
+
if (destination !== undefined) {
|
|
419
|
+
destination.set(bytes)
|
|
420
|
+
return
|
|
421
|
+
}
|
|
422
|
+
return bytes
|
|
423
|
+
}
|
|
424
|
+
)
|
|
104
425
|
```
|
|
105
426
|
|
|
106
|
-
|
|
107
|
-
|
|
427
|
+
### JavaScript Properties
|
|
428
|
+
|
|
429
|
+
Traverse and inspect properties:
|
|
108
430
|
|
|
109
431
|
```js
|
|
110
|
-
|
|
432
|
+
const image = file.getWzDirectory()?.getImageByName('00002000.img')
|
|
433
|
+
image?.parseImage()
|
|
434
|
+
|
|
435
|
+
for (const prop of image?.wzProperties() ?? []) {
|
|
436
|
+
console.log(prop.getName(), prop.getPropertyType())
|
|
437
|
+
}
|
|
111
438
|
|
|
112
|
-
|
|
439
|
+
const info = image?.getFromPath('info')
|
|
440
|
+
const name = info?.getChildByName('name')?.getString()
|
|
113
441
|
```
|
|
114
442
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
443
|
+
Export images and sounds:
|
|
444
|
+
|
|
445
|
+
```js
|
|
446
|
+
const canvas = image?.getFromPath('stand/0')
|
|
447
|
+
if (canvas instanceof WzCanvasProperty) {
|
|
448
|
+
canvas.saveToFile('stand-0.png')
|
|
449
|
+
|
|
450
|
+
const png = canvas.getPngProperty()
|
|
451
|
+
console.log(png?.getWidth(), png?.getHeight())
|
|
452
|
+
}
|
|
453
|
+
```
|
|
120
454
|
|
|
121
|
-
|
|
122
|
-
|
|
455
|
+
```js
|
|
456
|
+
const sound = image?.getFromPath('sound')
|
|
457
|
+
if (sound instanceof WzBinaryProperty) {
|
|
458
|
+
sound.saveToFile('sound.mp3')
|
|
459
|
+
}
|
|
460
|
+
```
|
|
123
461
|
|
|
124
462
|
## Build Options
|
|
125
463
|
|
|
126
|
-
|
|
|
127
|
-
|
|
464
|
+
| CMake option | Default | Description |
|
|
465
|
+
|--------------|---------|-------------|
|
|
128
466
|
| `BUILD_TESTS` | `ON` | Build Google Test unit tests |
|
|
129
|
-
| `BUILD_CAPI` | `ON` | Build C ABI
|
|
130
|
-
| `BUILD_JNI` | `OFF` | Build JNI shared library
|
|
131
|
-
|
|
132
|
-
## Targets
|
|
467
|
+
| `BUILD_CAPI` | `ON` | Build the C ABI layer |
|
|
468
|
+
| `BUILD_JNI` | `OFF` | Build the JNI shared library |
|
|
469
|
+
| `BUILD_WASM` | `OFF` | Build the WebAssembly target |
|
|
133
470
|
|
|
134
471
|
| Target | Type | Description |
|
|
135
472
|
|--------|------|-------------|
|
|
136
|
-
| `wz` | Static | Core
|
|
137
|
-
| `wz_capi` | Static | C ABI
|
|
138
|
-
| `wz_jni` | Shared | JNI native
|
|
473
|
+
| `wz` | Static library | Core C++ parser |
|
|
474
|
+
| `wz_capi` | Static library | C ABI wrapper |
|
|
475
|
+
| `wz_jni` | Shared library | JNI native bridge |
|
|
476
|
+
| `wz_wasm` | Emscripten executable | JavaScript/Wasm artifact |
|
|
139
477
|
|
|
140
|
-
##
|
|
478
|
+
## Development Commands
|
|
141
479
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
480
|
+
```bash
|
|
481
|
+
# C++
|
|
482
|
+
cmake -S . -B build -DBUILD_TESTS=ON
|
|
483
|
+
cmake --build build
|
|
484
|
+
ctest --test-dir build
|
|
485
|
+
|
|
486
|
+
# Java
|
|
487
|
+
cmake -S . -B build -DBUILD_JNI=ON
|
|
488
|
+
cmake --build build
|
|
489
|
+
(cd java && mvn test)
|
|
490
|
+
|
|
491
|
+
# JavaScript
|
|
492
|
+
npm run build:ts
|
|
493
|
+
npm run build:native
|
|
494
|
+
npm test
|
|
495
|
+
npm run test:wasm
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Format and lint:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
./format.sh
|
|
502
|
+
./lint.sh
|
|
503
|
+
npm run lint:js
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Notes
|
|
507
|
+
|
|
508
|
+
- The C++ core is built with exceptions and RTTI disabled.
|
|
509
|
+
- C++ failure paths use `wz::Result<T>` where possible.
|
|
510
|
+
- Java and JavaScript wrappers translate native failures to Java exceptions or
|
|
511
|
+
JavaScript errors at the binding boundary.
|
|
512
|
+
- JavaScript `saveToDisk()` and path-based file APIs depend on the active
|
|
513
|
+
backend's runtime capabilities. The WebAssembly backend supports host paths in
|
|
514
|
+
Node.js through a mounted filesystem; browser code should prefer bytes or
|
|
515
|
+
callback-backed input.
|
|
516
|
+
- Published Java artifacts are intended to bundle native libraries for Windows
|
|
517
|
+
x86_64, Linux x86_64, and macOS ARM64 through the GitHub Actions release
|
|
518
|
+
workflow.
|
|
519
|
+
- The npm package publishes `dist/index.js`, `dist/index.d.ts`,
|
|
520
|
+
`dist/libwz.js`, and `dist/libwz.wasm`.
|
|
160
521
|
|
|
161
522
|
## License
|
|
162
523
|
|
|
163
|
-
MIT. The `Harepacker-resurrected/` submodule
|
|
164
|
-
libwz source)
|
|
165
|
-
|
|
524
|
+
libwz is licensed under the MIT License. The `Harepacker-resurrected/` submodule
|
|
525
|
+
(test data reference, not part of libwz source) has its own licenses; see that
|
|
526
|
+
submodule for details.
|