@blamnetwork/blf 1.0.0-alpha.1 → 1.0.0-alpha.4
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 +9 -9
- package/dist/blf_chunk.js +11 -11
- package/dist-cjs/blf_chunk.js +11 -11
- package/package.json +2 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @blamnetwork/blf
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@blamnetwork/blf)
|
|
4
4
|
|
|
5
5
|
TypeScript library for reading and writing Halo **BLF** (binary layout file) chunks — Reach, Halo 3, and Halo 3: ODST builds.
|
|
6
6
|
|
|
@@ -17,7 +17,7 @@ Built on [@craftycodie/cstruct](https://www.npmjs.com/package/@craftycodie/cstru
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install @
|
|
20
|
+
npm install @blamnetwork/blf @craftycodie/cstruct
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Consumers import from published **`dist/`** exports only. Decorators are lowered at build time — **you do not need `experimentalDecorators`** to use exported chunk classes.
|
|
@@ -28,11 +28,11 @@ Consumers import from published **`dist/`** exports only. Decorators are lowered
|
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
30
|
import { readFileSync } from "node:fs";
|
|
31
|
-
import { search_for_chunk } from "@
|
|
31
|
+
import { search_for_chunk } from "@blamnetwork/blf";
|
|
32
32
|
import {
|
|
33
33
|
s_blf_chunk_content_header,
|
|
34
34
|
s_blf_chunk_game_variant,
|
|
35
|
-
} from "@
|
|
35
|
+
} from "@blamnetwork/blf/haloreach/v12065_11_08_24_1738_tu1actual";
|
|
36
36
|
|
|
37
37
|
const file = new Uint8Array(readFileSync("map.blf"));
|
|
38
38
|
|
|
@@ -46,7 +46,7 @@ search_for_chunk(file, mpvr, "big");
|
|
|
46
46
|
### Bitstream (root export only)
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
|
-
import { bitstream } from "@
|
|
49
|
+
import { bitstream } from "@blamnetwork/blf";
|
|
50
50
|
|
|
51
51
|
const reader = new bitstream.BitstreamReader(buffer, "little");
|
|
52
52
|
const mode = reader.read_enum("game-mode", 4, e_game_mode);
|
|
@@ -58,9 +58,9 @@ Each implementation build is a single module:
|
|
|
58
58
|
|
|
59
59
|
| Import | Build |
|
|
60
60
|
|--------|--------|
|
|
61
|
-
| `@
|
|
62
|
-
| `@
|
|
63
|
-
| `@
|
|
61
|
+
| `@blamnetwork/blf/haloreach/v12065_11_08_24_1738_tu1actual` | Reach TU1 |
|
|
62
|
+
| `@blamnetwork/blf/halo3/v12070_08_09_05_2031_halo3_ship` | Halo 3 ship |
|
|
63
|
+
| `@blamnetwork/blf/halo3odst/v13895_09_04_27_2201_atlas_release` | ODST Atlas |
|
|
64
64
|
|
|
65
65
|
Add a build by creating `src/versions/<game>/<build_id>.ts` and re-exporting its chunks — wildcard `exports` in `package.json` pick it up automatically.
|
|
66
66
|
|
package/dist/blf_chunk.js
CHANGED
|
@@ -7,7 +7,7 @@ export { parse_blf_chunk_version, s_blf_header };
|
|
|
7
7
|
/** Default {@link BLFChunk.read} / {@link BLFChunk.write}. */
|
|
8
8
|
export class BLFChunkBase {
|
|
9
9
|
read(bytes, endian) {
|
|
10
|
-
const header = c.read(s_blf_header, bytes.subarray(0, c.
|
|
10
|
+
const header = c.read(s_blf_header, bytes.subarray(0, c.sizeof(s_blf_header)), endian);
|
|
11
11
|
const meta = getBlfChunkMeta(this);
|
|
12
12
|
if (header.signature !== meta.signature ||
|
|
13
13
|
header.major !== meta.major ||
|
|
@@ -17,13 +17,13 @@ export class BLFChunkBase {
|
|
|
17
17
|
if (bytes.length < header.chunk_length) {
|
|
18
18
|
throw new BlfError(`BLF chunk buffer too short: chunk_length ${header.chunk_length}, got ${bytes.length}`);
|
|
19
19
|
}
|
|
20
|
-
const payload = bytes.subarray(c.
|
|
20
|
+
const payload = bytes.subarray(c.sizeof(s_blf_header), header.chunk_length);
|
|
21
21
|
this.read_body(payload, endian);
|
|
22
22
|
}
|
|
23
23
|
write(endian) {
|
|
24
24
|
const body = this.write_body(endian);
|
|
25
25
|
const { signature, major, minor } = getBlfChunkMeta(this);
|
|
26
|
-
const header_bytes = c.write(s_blf_header, s_blf_header.create(signature, body.length + c.
|
|
26
|
+
const header_bytes = c.write(s_blf_header, s_blf_header.create(signature, body.length + c.sizeof(s_blf_header), major, minor), endian);
|
|
27
27
|
const out = new Uint8Array(header_bytes.length + body.length);
|
|
28
28
|
out.set(header_bytes, 0);
|
|
29
29
|
out.set(body, header_bytes.length);
|
|
@@ -38,7 +38,7 @@ export class CStructBLFChunk extends BLFChunkBase {
|
|
|
38
38
|
read_body(payload, endian) {
|
|
39
39
|
const ctor = this.constructor;
|
|
40
40
|
const info = this;
|
|
41
|
-
const size = c.
|
|
41
|
+
const size = c.sizeof(ctor);
|
|
42
42
|
if (payload.length < size) {
|
|
43
43
|
throw new BlfError(`Cannot read ${info.signature} chunk: need at least ${size} bytes, got ${payload.length}`);
|
|
44
44
|
}
|
|
@@ -50,11 +50,11 @@ export class CStructBLFChunk extends BLFChunkBase {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
function tryReadHeader(buffer, byte_offset, endian) {
|
|
53
|
-
if (byte_offset + c.
|
|
53
|
+
if (byte_offset + c.sizeof(s_blf_header) > buffer.length) {
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
56
|
try {
|
|
57
|
-
return c.read(s_blf_header, buffer.subarray(byte_offset, byte_offset + c.
|
|
57
|
+
return c.read(s_blf_header, buffer.subarray(byte_offset, byte_offset + c.sizeof(s_blf_header)), endian);
|
|
58
58
|
}
|
|
59
59
|
catch {
|
|
60
60
|
return null;
|
|
@@ -62,22 +62,22 @@ function tryReadHeader(buffer, byte_offset, endian) {
|
|
|
62
62
|
}
|
|
63
63
|
function isChunkLengthValid(chunk_length, byte_offset, buffer_length) {
|
|
64
64
|
return (Number.isInteger(chunk_length) &&
|
|
65
|
-
chunk_length >= c.
|
|
65
|
+
chunk_length >= c.sizeof(s_blf_header) &&
|
|
66
66
|
byte_offset + chunk_length <= buffer_length);
|
|
67
67
|
}
|
|
68
68
|
function loadChunkAt(buffer, byte_offset, header, chunk, endian) {
|
|
69
69
|
const chunk_end = byte_offset + header.chunk_length;
|
|
70
|
-
const payload = buffer.subarray(byte_offset + c.
|
|
70
|
+
const payload = buffer.subarray(byte_offset + c.sizeof(s_blf_header), chunk_end);
|
|
71
71
|
chunk.read_body(payload, endian);
|
|
72
72
|
}
|
|
73
73
|
/** Walk a BLF file sequentially until `chunk` is found, then read it in place. */
|
|
74
74
|
export function find_chunk(buffer, chunk, endian) {
|
|
75
75
|
const meta = getBlfChunkMeta(chunk);
|
|
76
76
|
let offset = 0;
|
|
77
|
-
while (offset + c.
|
|
77
|
+
while (offset + c.sizeof(s_blf_header) <= buffer.length) {
|
|
78
78
|
let header;
|
|
79
79
|
try {
|
|
80
|
-
header = c.read(s_blf_header, buffer.subarray(offset, offset + c.
|
|
80
|
+
header = c.read(s_blf_header, buffer.subarray(offset, offset + c.sizeof(s_blf_header)), endian);
|
|
81
81
|
}
|
|
82
82
|
catch {
|
|
83
83
|
return false;
|
|
@@ -99,7 +99,7 @@ export function find_chunk(buffer, chunk, endian) {
|
|
|
99
99
|
/** Scan every byte offset for `chunk`, then read it in place when found. */
|
|
100
100
|
export function search_for_chunk(buffer, chunk, endian) {
|
|
101
101
|
const meta = getBlfChunkMeta(chunk);
|
|
102
|
-
const last_offset = buffer.length - c.
|
|
102
|
+
const last_offset = buffer.length - c.sizeof(s_blf_header);
|
|
103
103
|
const sig = meta.signature;
|
|
104
104
|
const sig0 = endian === "little" ? sig.charCodeAt(3) : sig.charCodeAt(0);
|
|
105
105
|
const sig1 = endian === "little" ? sig.charCodeAt(2) : sig.charCodeAt(1);
|
package/dist-cjs/blf_chunk.js
CHANGED
|
@@ -18,7 +18,7 @@ Object.defineProperty(exports, "getBlfChunkMeta", { enumerable: true, get: funct
|
|
|
18
18
|
/** Default {@link BLFChunk.read} / {@link BLFChunk.write}. */
|
|
19
19
|
class BLFChunkBase {
|
|
20
20
|
read(bytes, endian) {
|
|
21
|
-
const header = cstruct_1.c.read(s_blf_header_1.s_blf_header, bytes.subarray(0, cstruct_1.c.
|
|
21
|
+
const header = cstruct_1.c.read(s_blf_header_1.s_blf_header, bytes.subarray(0, cstruct_1.c.sizeof(s_blf_header_1.s_blf_header)), endian);
|
|
22
22
|
const meta = (0, decorators_1.getBlfChunkMeta)(this);
|
|
23
23
|
if (header.signature !== meta.signature ||
|
|
24
24
|
header.major !== meta.major ||
|
|
@@ -28,13 +28,13 @@ class BLFChunkBase {
|
|
|
28
28
|
if (bytes.length < header.chunk_length) {
|
|
29
29
|
throw new error_1.BlfError(`BLF chunk buffer too short: chunk_length ${header.chunk_length}, got ${bytes.length}`);
|
|
30
30
|
}
|
|
31
|
-
const payload = bytes.subarray(cstruct_1.c.
|
|
31
|
+
const payload = bytes.subarray(cstruct_1.c.sizeof(s_blf_header_1.s_blf_header), header.chunk_length);
|
|
32
32
|
this.read_body(payload, endian);
|
|
33
33
|
}
|
|
34
34
|
write(endian) {
|
|
35
35
|
const body = this.write_body(endian);
|
|
36
36
|
const { signature, major, minor } = (0, decorators_1.getBlfChunkMeta)(this);
|
|
37
|
-
const header_bytes = cstruct_1.c.write(s_blf_header_1.s_blf_header, s_blf_header_1.s_blf_header.create(signature, body.length + cstruct_1.c.
|
|
37
|
+
const header_bytes = cstruct_1.c.write(s_blf_header_1.s_blf_header, s_blf_header_1.s_blf_header.create(signature, body.length + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header), major, minor), endian);
|
|
38
38
|
const out = new Uint8Array(header_bytes.length + body.length);
|
|
39
39
|
out.set(header_bytes, 0);
|
|
40
40
|
out.set(body, header_bytes.length);
|
|
@@ -50,7 +50,7 @@ class CStructBLFChunk extends BLFChunkBase {
|
|
|
50
50
|
read_body(payload, endian) {
|
|
51
51
|
const ctor = this.constructor;
|
|
52
52
|
const info = this;
|
|
53
|
-
const size = cstruct_1.c.
|
|
53
|
+
const size = cstruct_1.c.sizeof(ctor);
|
|
54
54
|
if (payload.length < size) {
|
|
55
55
|
throw new error_1.BlfError(`Cannot read ${info.signature} chunk: need at least ${size} bytes, got ${payload.length}`);
|
|
56
56
|
}
|
|
@@ -63,11 +63,11 @@ class CStructBLFChunk extends BLFChunkBase {
|
|
|
63
63
|
}
|
|
64
64
|
exports.CStructBLFChunk = CStructBLFChunk;
|
|
65
65
|
function tryReadHeader(buffer, byte_offset, endian) {
|
|
66
|
-
if (byte_offset + cstruct_1.c.
|
|
66
|
+
if (byte_offset + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header) > buffer.length) {
|
|
67
67
|
return null;
|
|
68
68
|
}
|
|
69
69
|
try {
|
|
70
|
-
return cstruct_1.c.read(s_blf_header_1.s_blf_header, buffer.subarray(byte_offset, byte_offset + cstruct_1.c.
|
|
70
|
+
return cstruct_1.c.read(s_blf_header_1.s_blf_header, buffer.subarray(byte_offset, byte_offset + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header)), endian);
|
|
71
71
|
}
|
|
72
72
|
catch {
|
|
73
73
|
return null;
|
|
@@ -75,22 +75,22 @@ function tryReadHeader(buffer, byte_offset, endian) {
|
|
|
75
75
|
}
|
|
76
76
|
function isChunkLengthValid(chunk_length, byte_offset, buffer_length) {
|
|
77
77
|
return (Number.isInteger(chunk_length) &&
|
|
78
|
-
chunk_length >= cstruct_1.c.
|
|
78
|
+
chunk_length >= cstruct_1.c.sizeof(s_blf_header_1.s_blf_header) &&
|
|
79
79
|
byte_offset + chunk_length <= buffer_length);
|
|
80
80
|
}
|
|
81
81
|
function loadChunkAt(buffer, byte_offset, header, chunk, endian) {
|
|
82
82
|
const chunk_end = byte_offset + header.chunk_length;
|
|
83
|
-
const payload = buffer.subarray(byte_offset + cstruct_1.c.
|
|
83
|
+
const payload = buffer.subarray(byte_offset + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header), chunk_end);
|
|
84
84
|
chunk.read_body(payload, endian);
|
|
85
85
|
}
|
|
86
86
|
/** Walk a BLF file sequentially until `chunk` is found, then read it in place. */
|
|
87
87
|
function find_chunk(buffer, chunk, endian) {
|
|
88
88
|
const meta = (0, decorators_1.getBlfChunkMeta)(chunk);
|
|
89
89
|
let offset = 0;
|
|
90
|
-
while (offset + cstruct_1.c.
|
|
90
|
+
while (offset + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header) <= buffer.length) {
|
|
91
91
|
let header;
|
|
92
92
|
try {
|
|
93
|
-
header = cstruct_1.c.read(s_blf_header_1.s_blf_header, buffer.subarray(offset, offset + cstruct_1.c.
|
|
93
|
+
header = cstruct_1.c.read(s_blf_header_1.s_blf_header, buffer.subarray(offset, offset + cstruct_1.c.sizeof(s_blf_header_1.s_blf_header)), endian);
|
|
94
94
|
}
|
|
95
95
|
catch {
|
|
96
96
|
return false;
|
|
@@ -112,7 +112,7 @@ function find_chunk(buffer, chunk, endian) {
|
|
|
112
112
|
/** Scan every byte offset for `chunk`, then read it in place when found. */
|
|
113
113
|
function search_for_chunk(buffer, chunk, endian) {
|
|
114
114
|
const meta = (0, decorators_1.getBlfChunkMeta)(chunk);
|
|
115
|
-
const last_offset = buffer.length - cstruct_1.c.
|
|
115
|
+
const last_offset = buffer.length - cstruct_1.c.sizeof(s_blf_header_1.s_blf_header);
|
|
116
116
|
const sig = meta.signature;
|
|
117
117
|
const sig0 = endian === "little" ? sig.charCodeAt(3) : sig.charCodeAt(0);
|
|
118
118
|
const sig1 = endian === "little" ? sig.charCodeAt(2) : sig.charCodeAt(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blamnetwork/blf",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
4
|
"description": "BLF chunk library for Halo (TypeScript)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist-cjs/index.js",
|
|
@@ -72,10 +72,7 @@
|
|
|
72
72
|
"halo",
|
|
73
73
|
"haloreach",
|
|
74
74
|
"halo3",
|
|
75
|
-
"halo3odst"
|
|
76
|
-
"chunks",
|
|
77
|
-
"binary",
|
|
78
|
-
"fileshare"
|
|
75
|
+
"halo3odst"
|
|
79
76
|
],
|
|
80
77
|
"license": "MIT",
|
|
81
78
|
"publishConfig": {
|