@carbonenginejs/runtime-resource 0.7.0 → 0.9.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.
Files changed (59) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +67 -537
  3. package/dist/formats/dds/core/bc6h.js +288 -0
  4. package/dist/formats/dds/core/bc6h.js.map +1 -0
  5. package/dist/formats/dds/core/bc7.js +251 -0
  6. package/dist/formats/dds/core/bc7.js.map +1 -0
  7. package/dist/formats/dds/core/helpers.js +6 -3
  8. package/dist/formats/dds/core/helpers.js.map +1 -1
  9. package/dist/formats/gr2/CjsGr2Format.js +46 -0
  10. package/dist/formats/gr2/CjsGr2Format.js.map +1 -0
  11. package/dist/formats/gr2/core/CjsFormatGr2.js +273 -0
  12. package/dist/formats/gr2/core/CjsFormatGr2.js.map +1 -0
  13. package/dist/formats/gr2/core/bitknit2.js +280 -0
  14. package/dist/formats/gr2/core/bitknit2.js.map +1 -0
  15. package/dist/formats/gr2/core/curves.js +1047 -0
  16. package/dist/formats/gr2/core/curves.js.map +1 -0
  17. package/dist/formats/gr2/core/gsf.js +72 -0
  18. package/dist/formats/gr2/core/gsf.js.map +1 -0
  19. package/dist/formats/gr2/core/helpers.js +332 -0
  20. package/dist/formats/gr2/core/helpers.js.map +1 -0
  21. package/dist/formats/gr2/core/json.js +622 -0
  22. package/dist/formats/gr2/core/json.js.map +1 -0
  23. package/dist/formats/gr2/core/oodle1.js +388 -0
  24. package/dist/formats/gr2/core/oodle1.js.map +1 -0
  25. package/dist/formats/gr2/core/reader.js +617 -0
  26. package/dist/formats/gr2/core/reader.js.map +1 -0
  27. package/dist/formats/gr2/core/tangents.js +48 -0
  28. package/dist/formats/gr2/core/tangents.js.map +1 -0
  29. package/dist/formats/gr2/core/targets.js +351 -0
  30. package/dist/formats/gr2/core/targets.js.map +1 -0
  31. package/dist/formats/gr2/index.js +3 -0
  32. package/dist/formats/gr2/index.js.map +1 -0
  33. package/dist/formats/index.js +7 -0
  34. package/dist/formats/index.js.map +1 -1
  35. package/dist/index.js +1 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/resources/Tr2TexturePipelineStepGenerateMips.js +22 -0
  38. package/dist/resources/Tr2TexturePipelineStepGenerateMips.js.map +1 -0
  39. package/dist/resources/TriGeometryRes.js +24 -1
  40. package/dist/resources/TriGeometryRes.js.map +1 -1
  41. package/docs/README.md +69 -0
  42. package/docs/architecture.md +86 -0
  43. package/docs/concepts/resource-lifecycle.md +217 -0
  44. package/docs/formats/README.md +104 -0
  45. package/{FORMAT-PROVENANCE.md → docs/formats/provenance.md} +34 -5
  46. package/docs/formats/stl.md +37 -0
  47. package/docs/formats/wwise.md +44 -0
  48. package/docs/reference/events.md +92 -0
  49. package/docs/reference/motherlode-cache.md +244 -0
  50. package/docs/reference/queues.md +102 -0
  51. package/docs/reference/reload.md +107 -0
  52. package/docs/reference/texture-arrays.md +113 -0
  53. package/docs/reference/texture-pipeline.md +53 -0
  54. package/docs/roadmap.md +104 -0
  55. package/format-notices/gr2/LICENSE +21 -0
  56. package/format-notices/gr2/NOTICE +60 -0
  57. package/format-notices/gr2/THIRD-PARTY-NOTICES.md +93 -0
  58. package/package.json +51 -50
  59. package/resource-lifecycle.md +0 -679
@@ -0,0 +1,107 @@
1
+ # Candidate-first atomic reload
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource`
5
+ Audience: Users and integrators
6
+ Summary: Defines the reload contract that keeps the last published good handle canonical until a distinct candidate has fully succeeded.
7
+
8
+ ## Contract
9
+
10
+ Reload is candidate-first. When an owner already exists,
11
+ `GetResource(path, { reload: true })` constructs and initializes a distinct
12
+ off-registry candidate without changing ordinary lookup. Calling `Ready()` on
13
+ that candidate, using `GetObject()` / `FetchResource()` with `reload: true`,
14
+ or calling the explicit `ReloadObject()` / `ReloadResource()` helpers starts
15
+ one shared candidate operation that runs the same queued contract:
16
+
17
+ 1. purge-lock the exact former owner and invalidate reusable reads once;
18
+ 2. read, convert through the selected format, and publish payload state only
19
+ on the detached candidate;
20
+ 3. require the newest per-key reload token and exact former ownership;
21
+ 4. compare-and-swap the fully loaded CPU candidate into MotherLode;
22
+ 5. invalidate and clean the displaced handle after the lookup switch.
23
+
24
+ The manager captures the exact MotherLode, key, former handle, former
25
+ ownership generation, and a newest-request token. Reader, prepare, and
26
+ publication stages mutate only the detached candidate and validate candidate
27
+ authority before and after asynchronous boundaries. A fully loaded CPU
28
+ candidate commits through `CjsMotherLode.ReplaceExpected()` only if the exact
29
+ former owner and newest token still match. The final authority callback and
30
+ exact-record check run immediately before the synchronous map switch, with no
31
+ user cleanup or `await` between the comparison and publication.
32
+
33
+ After the switch, the displaced ownership generation is invalidated, the
34
+ candidate receives ordinary lifecycle/reconstruction callbacks, and the former
35
+ handle is cleaned exactly once. Existing JavaScript references are not
36
+ retargeted; they keep the displaced handle, while fresh lookup sees the
37
+ committed candidate.
38
+
39
+ ## Failure behavior
40
+
41
+ Source, format, or publication failure leaves the former handle, state,
42
+ payload, and adapters canonical; the failed candidate's attached payload and
43
+ adapters are cleaned and its original error is retained. An otherwise-
44
+ successful candidate that was superseded, deleted, cleared, or replaced
45
+ rejects with `CJS_RESMAN_STALE_RELOAD_CANDIDATE` and cannot resurrect the
46
+ key. Constructors that return the former singleton are rejected before
47
+ `Initialize()` can mutate it because staging requires a distinct handle.
48
+
49
+ Failed freshness attempts still invalidate reusable source/format records
50
+ when their work begins; the already-published canonical payload is not
51
+ dependent on those records.
52
+
53
+ Cleanup errors have explicit sides. Candidate cleanup failure aggregates with
54
+ the original preparation/stale error as
55
+ `CJS_RESMAN_RELOAD_CANDIDATE_CLEANUP_FAILED` while the former owner remains
56
+ canonical. A displaced-owner cleanup failure occurs after publication and
57
+ rejects as `CJS_MOTHERLODE_REPLACE_CLEANUP_FAILED`, whose result explicitly
58
+ reports `committed: true`; the already-good candidate remains canonical.
59
+
60
+ ## Read-cache interaction
61
+
62
+ `reload: true` synchronously detaches every queued/source/format read record
63
+ for the selected source/path before fresh work starts. Existing consumers
64
+ keep their detached promises; reload does not abort them. Fresh success
65
+ repopulates only caches explicitly requested with `cacheSource: true` or
66
+ `cacheFormat: true`. `InvalidateReadCache(path, { source, sourceRevision })`
67
+ provides the same no-abort invalidation explicitly; omitting
68
+ `sourceRevision` removes all revisions for that source/path. `Delete()`
69
+ remains canonical resource-identity-only, while `Clear()` resets all read
70
+ ledgers. A failed reload preserves the former payload but does not restore
71
+ reusable read-cache entries detached by its explicit freshness request.
72
+
73
+ ## Publication authority and staleness
74
+
75
+ Every queued, direct, standalone, and candidate resource preparation captures
76
+ an immutable publication authority: the exact MotherLode, canonical key,
77
+ resource handle, and a manager-local ownership generation. The manager
78
+ validates that authority before and after state changes, asynchronous
79
+ reader/format work, and publication. Delete, Clear, successful reload commit,
80
+ or exact-handle reinsertion therefore makes older work reject with
81
+ `CJS_RESMAN_STALE_RESOURCE_OPERATION` before it can enter another state or
82
+ publish. If stale work independently rejects, its original source/format
83
+ error is preserved and `SetError()` is suppressed on the detached handle.
84
+
85
+ Candidate work is a normal `Wait()` root and blocks synchronous MotherLode
86
+ replacement while active. `Register({ motherLode })` rejects with
87
+ `CJS_RESMAN_ACTIVE_RESOURCE_OPERATIONS` while queued or direct mutations are
88
+ active, including a reload candidate. Normal `Wait()` drains queued roots and
89
+ candidate lineages; a direct caller must await its own load/prepare promise
90
+ before retrying replacement. Canonical and candidate authority prevent late
91
+ publication, but started source or format work is not yet aborted;
92
+ deterministic cleanup applies to the staged candidate resource itself.
93
+
94
+ ## Divergence from Carbon
95
+
96
+ This availability contract intentionally differs from Carbon.
97
+ `BlueAsyncRes::Reload` cancels/joins work, releases dependent cached data, and
98
+ reloads the same canonical object in place; failure can therefore leave that
99
+ stable handle bad. Carbon MotherLode replacement also switches immediately and
100
+ has no prepare-success gate or rollback. Runtime-resource instead preserves
101
+ the last published good handle until a distinct candidate has succeeded, and
102
+ never silently retargets existing JavaScript references.
103
+
104
+ ## Related documentation
105
+
106
+ - [MotherLode identity, cache, and retention](motherlode-cache.md)
107
+ - [Queues and the Wait fence](queues.md)
@@ -0,0 +1,113 @@
1
+ # Texture arrays and update generations
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource`
5
+ Audience: Users and integrators
6
+ Summary: Defines the texture-array proxy surface, the requested/prepared revision boundary, and the adapter commit contract.
7
+
8
+ ## Layer proxies
9
+
10
+ Texture-array resources expose one ordinary-looking proxy per ordered layer:
11
+
12
+ ```js
13
+ const textureArray = new CjsTextureArrayRes({
14
+ paths: [
15
+ "res:/detail1.dds",
16
+ "res:/detail2.dds",
17
+ "res:/detail3.dds"
18
+ ],
19
+ layerNames: [ "Detail1Map", "Detail2Map", "Detail3Map" ],
20
+ updateScheduler: resource => frameQueue.add(resource)
21
+ });
22
+
23
+ const detail2 = textureArray.GetLayerParameter(1);
24
+ detail2.SetValue("res:/replacement.dds");
25
+
26
+ detail2.textureRes === textureArray; // true
27
+ ```
28
+
29
+ Proxy setters only update their source path and invalidate the parent. The
30
+ parent is scheduled once even if several proxies change in the same frame.
31
+ The next-frame consumer calls `Update()` or `ConsumeUpdateRequest()` to
32
+ obtain one immutable ordered snapshot. Runtime-resource does not know which
33
+ shader metadata caused the aggregate request; shader packages and engine
34
+ adapters map public parameter names to layer indices.
35
+
36
+ Public effect parameters remain separate from these internal proxies. Their
37
+ authored paths and individual 2D source resources are not replaced by the
38
+ aggregate. An engine-owned, non-persisted bridge mirrors public changes into
39
+ the fixed internal layers.
40
+
41
+ ## Update generations
42
+
43
+ `CjsTextureArrayRes` is a derived multi-source resource with an explicit
44
+ requested/prepared revision boundary:
45
+
46
+ ```text
47
+ proxy/source change
48
+ -> requested revision + dirty layer
49
+ -> one scheduled next-frame snapshot
50
+ -> consumed/in-flight request
51
+ -> adapter candidate preparation
52
+ -> guarded adapter + prepared-revision publication
53
+ ```
54
+
55
+ `ConsumeUpdateRequest()` produces an immutable snapshot and marks that
56
+ revision in flight. A current consumed revision may be completed through
57
+ `CommitPreparedAdapterRevision()`, failed through `FailUpdateRequest()`, or
58
+ returned to the queue through `RetryUpdateRequest()`. Commit-before-consume
59
+ and stale commits are rejected; rejected candidate allocations are destroyed
60
+ by default.
61
+
62
+ Publication installs the adapter allocation and prepared revision before
63
+ completion events run. The result returns the displaced allocation to the
64
+ adapter owner for post-publication destruction. A reentrant source change may
65
+ therefore request a newer revision without allowing stale completion to
66
+ replace it. The previous prepared allocation and `IsGood()` remain usable
67
+ while a replacement is pending or if replacement preparation fails.
68
+
69
+ `Ready()` is specialized for this derived resource: it resolves when the
70
+ generation requested at call time has been published, rather than delegating
71
+ to a single-source object loader. Initial preparation failure rejects it.
72
+
73
+ ## Adapter commit example
74
+
75
+ Consumed snapshots are explicit in-flight generations. An adapter either
76
+ publishes the current candidate atomically, requeues retryable work, or
77
+ records failure:
78
+
79
+ ```js
80
+ const request = textureArray.ConsumeUpdateRequest();
81
+
82
+ try {
83
+ const candidate = await adapter.PrepareTextureArray(request);
84
+ const result = textureArray.CommitPreparedAdapterRevision(
85
+ request.revision,
86
+ "webgpu",
87
+ candidate
88
+ );
89
+
90
+ // A rejected/stale candidate is destroyed by the commit method by default.
91
+ // The adapter owns disposal of a successfully displaced allocation.
92
+ result.displaced?.destroy();
93
+ } catch (error) {
94
+ textureArray.FailUpdateRequest(request.revision, error, { retry: true });
95
+ }
96
+
97
+ await textureArray.Ready(); // the generation requested at call time
98
+ ```
99
+
100
+ ## Sources and topology
101
+
102
+ Logical paths and resolved sources are independent. `SetLayerResource()`
103
+ attaches a resolved or LOD-specific source without rewriting the logical
104
+ requested path or persistence. `TouchLayer()` invalidates an in-place source
105
+ revision. `HandleAdapterLoss()` drops an unusable adapter allocation and
106
+ schedules a complete topology rebuild. Topology-changing snapshots set
107
+ `topologyChanged: true` and report only valid current layer indices in
108
+ `dirtyLayers`.
109
+
110
+ ## Related documentation
111
+
112
+ - [Architecture and boundaries](../architecture.md)
113
+ - [Texture CPU pipeline and LOD membership](texture-pipeline.md)
@@ -0,0 +1,53 @@
1
+ # Texture CPU pipeline and LOD membership
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource`
5
+ Audience: Users and integrators
6
+ Summary: Defines the `Tr2TexturePipeline` CPU-only step contract and `Tr2TextureLodManager` membership ownership.
7
+
8
+ ## Tr2TexturePipeline
9
+
10
+ `Tr2TexturePipeline` is the Carbon texture-specific CPU bitmap pipeline, not a
11
+ general resource prepare stage. `GetResourceDependencies()` returns the sorted
12
+ unique paths required by load and channel-pack steps. `Execute()` resolves
13
+ those inputs from an explicit `inputs` map/object, an async `load(path)`
14
+ callback, or an injected `CjsResMan`, then returns a canonical plain
15
+ `rgba8unorm` payload:
16
+
17
+ ```js
18
+ import {
19
+ Tr2TexturePipeline,
20
+ Tr2TexturePipelineStepLoad,
21
+ Tr2TexturePipelineStepLimitSize
22
+ } from "@carbonenginejs/runtime-resource";
23
+
24
+ const load = new Tr2TexturePipelineStepLoad();
25
+ load.path = "res:/texture/source.png";
26
+ const limit = new Tr2TexturePipelineStepLimitSize();
27
+ limit.maxWidth = 512;
28
+
29
+ const pipeline = new Tr2TexturePipeline();
30
+ pipeline.steps = [ load, limit ];
31
+ const rgba = await pipeline.Execute(0, 0, { resMan });
32
+ ```
33
+
34
+ The maintained runtime path currently accepts canonical `rgba8unorm` inputs.
35
+ Load copies the source bitmap, limit-size repeatedly performs a 2x2 CPU
36
+ downsample, pack builds logical RGBA channels from independent inputs, and
37
+ Carbon's present compress step remains validation-only because the native
38
+ method is itself a no-op. Unsupported step types fail explicitly.
39
+
40
+ For the decoded-DDS fallback contract used by 2D texture inputs, see the
41
+ [DDS notes in the format map](../formats/README.md#dds-decoded-fallback).
42
+
43
+ ## Tr2TextureLodManager
44
+
45
+ `Tr2TextureLodManager` owns only ordered resource membership through
46
+ `RegisterTexture()`, `UnregisterTexture()`, and `GetManagedTextures()`.
47
+ Engine packages continue to own GPU allocations, upload accounting, device
48
+ budgets, capability limits, and device-loss recovery.
49
+
50
+ ## Related documentation
51
+
52
+ - [Texture arrays and update generations](texture-arrays.md)
53
+ - [Format subpaths](../formats/README.md)
@@ -0,0 +1,104 @@
1
+ # Roadmap
2
+
3
+ Status: Evolving
4
+ Scope: `@carbonenginejs/runtime-resource`
5
+ Audience: Integrators and maintainers
6
+ Summary: Records approved future direction and open design questions; nothing on this page is implemented.
7
+
8
+ Everything below is future work. Current behavior is documented in the
9
+ [reference pages](reference/motherlode-cache.md); where this page and a
10
+ reference page disagree, the reference page describes the shipped package.
11
+
12
+ ## Runtime manager follow-up
13
+
14
+ The ResMan/MotherLode contract is complete for its current consumers. Future
15
+ work should be driven by measured application needs rather than another
16
+ resource preparation abstraction:
17
+
18
+ - main-queue priority and starvation policy;
19
+ - cancellation/abort propagation for work that has already started;
20
+ - `WaitUrgent()` after real priority and bounded fairness;
21
+ - queue-time and reader/format-time telemetry;
22
+ - application-level default retention policy selection;
23
+ - automatic resource/payload byte estimation and separate CPU/adapter
24
+ budgets;
25
+ - browser-aware source behavior such as fetch response type selection;
26
+ - purged-resource/device-loss recovery policy (backend device-loss recovery
27
+ belongs to the engine's realization operation).
28
+
29
+ ## Pre-adoption lifecycle API cleanup (approved, not implemented)
30
+
31
+ No released consumer currently depends on the ccpwgl-compatible liveness
32
+ names; the known ccpwgl format integrations are migration targets rather than
33
+ a reason to preserve them. Before runtime-core or another public consumer
34
+ adopts this contract, remove `KeepAlive()` and `KeepPayloadAlive()` instead
35
+ of retaining or deprecating compatibility aliases.
36
+
37
+ The public lifecycle should express caller intent directly:
38
+
39
+ - `Ready()` obtains or reconstructs the CPU payload;
40
+ - `AcquireLock()` returns a scoped hard-retention token;
41
+ - `ReleasePayload()` explicitly drops the CPU payload; and
42
+ - state queries remain pure.
43
+
44
+ ResMan should update identity activity when a canonical resource is acquired
45
+ and payload activity when it publishes or returns a ready payload. Those
46
+ timestamps are cache-policy implementation details, not calls consumers
47
+ should have to make. Cache admission and promotion must also be explicit
48
+ manager operations: merely accessing a resource must not permanently move it
49
+ from the byte-budget candidate set into an unbudgeted live set.
50
+
51
+ Do not add `TouchIdentity()` or `TouchPayload()` to the initial public API
52
+ unless a concrete soft-retention consumer appears. A consumer that needs a
53
+ residency guarantee should acquire a lock; one that merely uses a resource
54
+ should call `Ready()` and allow the configured cache policy to operate.
55
+
56
+ Raw `Lock()` / `Unlock()` is easy to mis-pair across asynchronous success,
57
+ failure, cancellation, and disposal. Prefer a JS-only acquired-lock API:
58
+
59
+ ```js
60
+ const hold = resource.AcquireLock();
61
+ try {
62
+ const payload = await resource.Ready();
63
+ await consume(payload);
64
+ } finally {
65
+ hold.Release();
66
+ }
67
+ ```
68
+
69
+ The acquired token should add exactly one lock, capture the canonical key and
70
+ ownership generation that received it, and release that exact lock at most
71
+ once. `Release()` must be idempotent. If the original record has been
72
+ deleted, replaced, purged, or rebound, releasing the stale token must not
73
+ decrement a new owner's lock. Acquisition and release must never fetch,
74
+ reconstruct, prepare, or release payload data themselves.
75
+
76
+ The token is an async-safety replacement for exposing the existing raw lock
77
+ count, not a new retention policy. For example, runtime-audio can hold
78
+ source/PCM payload through decode and release the token once it owns the
79
+ resulting WebAudio `AudioBuffer`; keeping the resource locked for the full
80
+ playback or decoded-cache lifetime would unnecessarily retain both
81
+ representations. Runtime-lifetime and group retention remain caller policy
82
+ built from explicit tokens, not an implicit default on every loaded resource.
83
+
84
+ ## Open design questions
85
+
86
+ - Should `Unload()` drop only adapter payloads by default, or CPU payloads
87
+ too?
88
+ - Should there be explicit `UnloadAdapterResources()`, `UnloadPayload()`, and
89
+ `Purge()` phases?
90
+ - Which format/resource-specific estimators should supply separate identity,
91
+ CPU-payload, and adapter byte weights without double-counting shared
92
+ buffers?
93
+ - Should manually attached/dynamic resources default to locked, like ccpwgl's
94
+ manual shader resources use `doNotPurge`?
95
+ - What explicit `Reload()`/reconstruction API should restore purged resources
96
+ without introducing surprising browser or network work?
97
+ - A resource-level `Purge()`/`Reload()` vocabulary remains future policy
98
+ work, as does whether `Unload()` should release engine adapter resources
99
+ and optionally CPU payloads.
100
+
101
+ ## Related documentation
102
+
103
+ - [MotherLode identity, cache, and retention](reference/motherlode-cache.md)
104
+ - [Candidate-first atomic reload](reference/reload.md)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cppctamber / CarbonEngineJS contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,60 @@
1
+ carbonenginejs/format-gr2
2
+ =========================
3
+
4
+ A pure-JavaScript reader for RAD Game Tools' Granny 3D (.gr2) file format.
5
+ Contains no RAD/Granny code and does not link granny2.dll. Section decompression
6
+ is reimplemented from clean-room / open reverse-engineering efforts, listed below.
7
+
8
+ CarbonEngineJS-facing code is copyright cppctamber / CarbonEngineJS
9
+ contributors. CarbonEngine source material, formats, class names, and schema
10
+ semantics remain copyright their original CarbonEngine authors.
11
+ Fenris Creations (CCP Games) source material, tools, assets, shader behavior,
12
+ and EVE-related data remain copyright their original holders.
13
+
14
+ Current repository license: MIT. See LICENSE.
15
+
16
+ The former EUPL-derived BitKnit implementation was replaced on 2026-07-24 by
17
+ an original clean-room implementation written solely from this package's
18
+ published format specification (docs/formats/bitknit2.md); no EUPL-derived
19
+ code remains.
20
+
21
+ Provenance and legal requirements
22
+ ---------------------------------
23
+
24
+ When this package copies, ports, translates, or derives from CarbonEngine,
25
+ ccpwgl, Fenris Creations (CCP Games) / EVE assets or tools, RAD/Granny
26
+ materials, or any third-party library, the relevant section below must record
27
+ the upstream copyright holders, project/repository, file path, revision when
28
+ known, license name, and required redistribution obligations. Behavioral
29
+ references with no copied expression should be marked as references only.
30
+
31
+ CarbonEngine and Fenris Creations (CCP Games) are mentioned for interoperability
32
+ and provenance context. This package is not affiliated with or endorsed by CCP
33
+ Games.
34
+
35
+ Third-party algorithm provenance
36
+ --------------------------------
37
+
38
+ Oodle1 / Oodle0 section decompressor (src/codecs/oodle1.js)
39
+ Ported from the open Granny2 decompressors:
40
+ - nwn2mdk "gr2_decompress" (Boost Software License 1.0)
41
+ - arves100/opengr2 "oodle1.c" (MPL-2.0) https://github.com/arves100/opengr2
42
+ This is Granny's own legacy codec, not RAD's modern Oodle (Kraken/BitKnit families).
43
+
44
+ BitKnit / BitKnit2 section decompressor (src/core/bitknit2.js)
45
+ Original CarbonEngineJS clean-room implementation (2026-07-24), written
46
+ solely from this package's published format specification
47
+ (docs/formats/bitknit2.md) by an isolated implementer, and validated
48
+ byte-exact against the EVE .gr2 corpus. It replaced a prior port of
49
+ neptuwunium/Knit (EUPL-1.2); no code from Knit, pybg3, ooz, or any other
50
+ BitKnit implementation remains. See THIRD-PARTY-NOTICES.md.
51
+
52
+ Granny curve decompression (src/curves/**, optional; enabled via decompressCurves)
53
+ Adapted from cppctamber's granny extension for ccpwgl2 (src/core/reader/granny/curves).
54
+
55
+ Container / relocation / type-tree reflection (src/container.js, src/reader.js)
56
+ Original to this project, derived from the on-disk Granny GRN format layout
57
+ (cross-referenced with the Granny SDK headers and the above projects' docs).
58
+
59
+ Please retain this NOTICE, LICENSE, THIRD-PARTY-NOTICES.md, and the upstream
60
+ licenses when redistributing.
@@ -0,0 +1,93 @@
1
+ # Third-party notices
2
+
3
+ `format-gr2` is licensed under **MIT**. It contains no RAD/Granny proprietary
4
+ code and does not link `granny2.dll`. The section decompressors are original
5
+ or ported from permissively licensed open reverse-engineering work and must
6
+ retain the notices and obligations listed below.
7
+
8
+ Do not treat copied or ported prior work as CarbonEngineJS-original code. Each
9
+ copied/ported/adapted component must list its upstream copyright holders,
10
+ source project, path, revision when known, license, and redistribution
11
+ requirements. Code used only as a behavioral reference should be marked as
12
+ reference-only.
13
+
14
+ ---
15
+
16
+ ## Oodle1 / Oodle0 decompressor - `src/core/oodle1.js`
17
+
18
+ **Boost Software License 1.0.** Ported from the Granny `gr2_decompress` implementation
19
+ shipped in [Arbos/nwn2mdk](https://github.com/Arbos/nwn2mdk) (the decompressor is
20
+ originally by *berenm* and is distributed under Boost 1.0, separate from nwn2mdk's
21
+ Apache-2.0 format code). Cross-checked for correctness against
22
+ [arves100/opengr2](https://github.com/arves100/opengr2) `oodle1.c` (MPL-2.0) - used as a
23
+ verification reference only; no opengr2 code was copied, so MPL-2.0 does not apply.
24
+
25
+ ```
26
+ Boost Software License - Version 1.0 - August 17th, 2003
27
+
28
+ Permission is hereby granted, free of charge, to any person or organization
29
+ obtaining a copy of the software and accompanying documentation covered by
30
+ this license (the "Software") to use, reproduce, display, distribute, execute,
31
+ and transmit the Software, and to prepare derivative works of the Software, and
32
+ to permit third-parties to whom the Software is furnished to do so, all subject
33
+ to the following:
34
+
35
+ The copyright notices in the Software and this entire statement, including the
36
+ above license grant, this restriction and the following disclaimer, must be
37
+ included in all copies of the Software, in whole or in part, and all derivative
38
+ works of the Software, unless such copies or derivative works are solely in the
39
+ form of machine-executable object code generated by a source language processor.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
44
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
45
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
46
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47
+ DEALINGS IN THE SOFTWARE.
48
+ ```
49
+
50
+ ---
51
+
52
+ ## BitKnit / BitKnit2 decompressor - `src/core/bitknit2.js`
53
+
54
+ **Original CarbonEngineJS code (MIT).** On 2026-07-24 the previous
55
+ EUPL-derived port (transcribed from
56
+ [neptuwunium/Knit](https://github.com/neptuwunium/Knit)
57
+ `GrannyBitKnitCompression.cs`, EUPL-1.2) was replaced by a clean-room
58
+ implementation produced through a documented two-party process: one party
59
+ analyzed the format and published a facts-only decoding specification
60
+ (`docs/formats/bitknit2.md`); a second, isolated party implemented the
61
+ decoder solely from that specification with no access to Knit, pybg3, ooz,
62
+ or any other BitKnit implementation. The result was validated byte-exact
63
+ against 539 BitKnit2 streams extracted from the EVE `.gr2` corpus (section
64
+ payloads and compressed pointer-fixup blocks) plus synthetic raw-quantum
65
+ streams. No third-party code, EUPL-derived or otherwise, remains in this
66
+ file. BitKnit is a RAD Game Tools codec; the specification records on-disk
67
+ format facts only.
68
+
69
+ ---
70
+
71
+ ## Granny animation-curve decompression - `src/core/curves.js`
72
+
73
+ Authored by **cppctamber** as part of ccpwgl2 (`src/core/reader/granny/curves`), the
74
+ same author/owner as this library; included here under this project's MIT
75
+ license.
76
+
77
+ ## Tangent-frame shader math - `src/core/tangents.js`
78
+
79
+ The packed tangent-frame decode/encode math is derived from Fenris Creations
80
+ (formerly CCP / CCP Games) EVE/Carbon shader behavior for packed tangent frames.
81
+ This package contains a JavaScript implementation of the tangent-frame math and
82
+ does not include copied shader source.
83
+
84
+ If shader source is later copied, ported, or translated directly, update this
85
+ notice with the exact upstream repository, file path, revision, copyright
86
+ holders, license text or required notice location, and redistribution
87
+ obligations before committing that work.
88
+
89
+ ## Container, relocation, type-tree reflection, emitter
90
+
91
+ Original to this project (cppctamber / carbonenginejs). Included here under
92
+ this project's MIT license. Derived from the public on-disk Granny
93
+ GRN format layout and the Granny SDK header type definitions.
package/package.json CHANGED
@@ -1,51 +1,52 @@
1
- {
2
- "name": "@carbonenginejs/runtime-resource",
3
- "version": "0.7.0",
4
- "description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
5
- "type": "module",
6
- "exports": {
7
- ".": "./dist/index.js",
8
- "./formats": "./dist/formats/index.js",
9
- "./formats/black": "./dist/formats/black/index.js",
10
- "./formats/black/schema": "./dist/formats/black/core/blackSchema.js",
11
- "./formats/black/enums": "./dist/formats/black/core/blackEnums.js",
12
- "./formats/black/version": "./dist/formats/black/core/blackVersion.js",
13
- "./formats/bnk": "./dist/formats/bnk/index.js",
14
- "./formats/cmf": "./dist/formats/cmf/index.js",
15
- "./formats/dds": "./dist/formats/dds/index.js",
16
- "./formats/fbx": "./dist/formats/fbx/index.js",
17
- "./formats/flac": "./dist/formats/flac/index.js",
18
- "./formats/gif": "./dist/formats/gif/index.js",
19
- "./formats/gltf": "./dist/formats/gltf/index.js",
20
- "./formats/jpeg": "./dist/formats/jpeg/index.js",
21
- "./formats/mp3": "./dist/formats/mp3/index.js",
22
- "./formats/mp4": "./dist/formats/mp4/index.js",
23
- "./formats/obj": "./dist/formats/obj/index.js",
24
- "./formats/ogg": "./dist/formats/ogg/index.js",
25
- "./formats/png": "./dist/formats/png/index.js",
26
- "./formats/red": "./dist/formats/red/index.js",
27
- "./formats/red/schema": "./dist/formats/red/core/blackDefinitions.js",
28
- "./formats/stl": "./dist/formats/stl/index.js",
29
- "./formats/tga": "./dist/formats/tga/index.js",
30
- "./formats/wav": "./dist/formats/wav/index.js",
31
- "./formats/webm": "./dist/formats/webm/index.js",
32
- "./formats/webp": "./dist/formats/webp/index.js",
33
- "./formats/wem": "./dist/formats/wem/index.js",
34
- "./formats/yaml": "./dist/formats/yaml/index.js"
35
- },
36
- "sideEffects": false,
37
- "engines": {
38
- "node": ">=18"
39
- },
40
- "dependencies": {
41
- "@carbonenginejs/core-math": "^0.1.3",
1
+ {
2
+ "name": "@carbonenginejs/runtime-resource",
3
+ "version": "0.9.0",
4
+ "description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./dist/index.js",
8
+ "./formats": "./dist/formats/index.js",
9
+ "./formats/black": "./dist/formats/black/index.js",
10
+ "./formats/black/schema": "./dist/formats/black/core/blackSchema.js",
11
+ "./formats/black/enums": "./dist/formats/black/core/blackEnums.js",
12
+ "./formats/black/version": "./dist/formats/black/core/blackVersion.js",
13
+ "./formats/bnk": "./dist/formats/bnk/index.js",
14
+ "./formats/cmf": "./dist/formats/cmf/index.js",
15
+ "./formats/dds": "./dist/formats/dds/index.js",
16
+ "./formats/fbx": "./dist/formats/fbx/index.js",
17
+ "./formats/flac": "./dist/formats/flac/index.js",
18
+ "./formats/gif": "./dist/formats/gif/index.js",
19
+ "./formats/gltf": "./dist/formats/gltf/index.js",
20
+ "./formats/gr2": "./dist/formats/gr2/index.js",
21
+ "./formats/jpeg": "./dist/formats/jpeg/index.js",
22
+ "./formats/mp3": "./dist/formats/mp3/index.js",
23
+ "./formats/mp4": "./dist/formats/mp4/index.js",
24
+ "./formats/obj": "./dist/formats/obj/index.js",
25
+ "./formats/ogg": "./dist/formats/ogg/index.js",
26
+ "./formats/png": "./dist/formats/png/index.js",
27
+ "./formats/red": "./dist/formats/red/index.js",
28
+ "./formats/red/schema": "./dist/formats/red/core/blackDefinitions.js",
29
+ "./formats/stl": "./dist/formats/stl/index.js",
30
+ "./formats/tga": "./dist/formats/tga/index.js",
31
+ "./formats/wav": "./dist/formats/wav/index.js",
32
+ "./formats/webm": "./dist/formats/webm/index.js",
33
+ "./formats/webp": "./dist/formats/webp/index.js",
34
+ "./formats/wem": "./dist/formats/wem/index.js",
35
+ "./formats/yaml": "./dist/formats/yaml/index.js"
36
+ },
37
+ "sideEffects": false,
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "dependencies": {
42
+ "@carbonenginejs/core-math": "^0.1.5",
42
43
  "@carbonenginejs/core-types": "^0.12.0",
43
- "@carbonenginejs/runtime-const": "^0.1.0",
44
- "meshoptimizer": "^1.2.0",
45
- "yaml": "^2.4.0"
46
- },
47
- "license": "MIT",
48
- "publishConfig": {
49
- "access": "public"
50
- }
51
- }
44
+ "@carbonenginejs/runtime-const": "^0.1.0",
45
+ "meshoptimizer": "^1.2.0",
46
+ "yaml": "^2.4.0"
47
+ },
48
+ "license": "MIT",
49
+ "publishConfig": {
50
+ "access": "public"
51
+ }
52
+ }