From dfd9cbde353bc3d7dd5344f749a730ccaf40e5e8 Mon Sep 17 00:00:00 2001 From: Johannes Stein Date: Thu, 1 Oct 2026 13:18:38 +0100 Subject: [PATCH] feat!: target GocciaScript 0.14.0 and bind the complete raylib API GocciaScript 0.11 to 0.14 lifted the FFI limits the 0.10.0 bindings worked around, and 0.14 replaced the script loader and the unsafe-ffi switch with GocciaRunner and a deny-by-default permission model. - Generate all 600 raylib functions: mixed float arguments, more than eight arguments and variadic calls now bind natively. - Replace the seven Raw raw-pointer variants with FFI.nullable("utf8string") and keep AudioStream.buffer under its native name. - Move both goccia.json files to a permissions block and run every lane through GocciaRunner 0.14.0. - Make the package importable by name through an exports map. - Drop the example workarounds: Bunnymark uses GenMeshPlane and direct imports, DOOM scales with DrawTextureEx. Frames are byte-identical. BREAKING CHANGE: requires GocciaScript 0.14.0. The Raw exports and RAYLIB_FIELD_ALIASES are removed, and AudioStream.nativeBuffer is now AudioStream.buffer. --- .agent/HANDOFF.md | 88 +++ .github/workflows/ci.yml | 31 +- .gitignore | 1 + README.md | 161 +++-- SKIPPED.json | 684 +------------------- SKIPPED.md | 133 +--- benchmarks/import-binding-aliases.md | 79 +++ bindings/raylib.d.ts | 420 ++++++++++-- bindings/raylib.ts | 388 ++++++++++- examples/doom-gpl/Makefile | 16 +- examples/doom-gpl/README.md | 15 +- examples/doom-gpl/goccia.json | 5 +- examples/doom-gpl/src/i_video.ts | 18 +- examples/lib/bunny-instancing.ts | 43 +- examples/lib/run-bunnymark.ts | 82 +-- goccia.json | 4 +- metadata/gocciascript.json | 10 +- package.json | 15 +- scripts/generate-bindings.mjs | 2 +- scripts/lib/abi.mjs | 15 +- scripts/lib/bindings.mjs | 196 +++--- scripts/validate-package.mjs | 14 +- tests/abi/layout-probe.ts | 13 +- tests/fixtures/ffi-smoke.ts | 41 +- tests/integration/abi.test.mjs | 21 +- tests/integration/bunnymark-visual.test.mjs | 18 +- tests/integration/ffi.test.mjs | 25 +- tests/integration/helpers.mjs | 12 +- tests/linux/Dockerfile | 21 +- tests/types/consumer.ts | 47 ++ tests/unit/bindings.test.ts | 65 +- 31 files changed, 1451 insertions(+), 1232 deletions(-) create mode 100644 .agent/HANDOFF.md create mode 100644 benchmarks/import-binding-aliases.md create mode 100644 tests/types/consumer.ts diff --git a/.agent/HANDOFF.md b/.agent/HANDOFF.md new file mode 100644 index 0000000..74a2268 --- /dev/null +++ b/.agent/HANDOFF.md @@ -0,0 +1,88 @@ +# Handoff + +Last updated: 2026-10-01 + +## Current task + +Move the repository from GocciaScript 0.10.0 to 0.14.0 and replace the +workarounds that 0.10.0 forced with what 0.11.0 to 0.14.0 provide natively. + +State: implemented and validated locally on the branch `t3code/4b7db725`, and +published as a pull request. Merging is left to Johannes. After the merge the +repository gets its first tag; the tag name is not decided yet. + +## What changed + +- **Runtime pin**: `metadata/gocciascript.json`, CI, `tests/linux/Dockerfile`, + npm scripts, the Node test harness and the DOOM Makefile target 0.14.0 and + `GocciaRunner` (`GOCCIA_LOADER` is now `GOCCIA_RUNNER`). +- **Permissions**: `"unsafe-ffi"` is a hard error on 0.14.0. Both `goccia.json` + files use a `permissions` block. The DOOM config also requests read access to + `bindings/raylib.ts`, because that file is outside its project directory. + Unit and palette tests pass `--ignore-config-permissions`; native-call tests + and CI pass `-P`. +- **Generator**: all 600 functions are bound (was 488). `TraceLog` and + `TextFormat` are variadic, the seven `*Raw` duplicates became + `FFI.nullable("utf8string")`, and `AudioStream.buffer` keeps its native name. + The declarations gained a branded `FFIAggregate`, `FFIAggregateMetadata`, + `FFIVarargs` and `GocciaFFI`. +- **Examples**: Bunnymark uses `GenMeshPlane` and direct imports; DOOM scales + with `DrawTextureEx`. Frames are byte-identical before and after. +- **Package entry point**: `package.json` has an `exports` map, so an installed + copy is imported as `@frostney/gocciascript-raylib`; every other packaged file + stays reachable by subpath. +- **Tests**: `tests/types/consumer.ts` is compiled by `npm run test:types`; + `tests/fixtures/ffi-smoke.ts` covers mixed-float, variadic, nullable and + exact-field-name calls; CI and Docker now run `make test-palette`. +- **Docs**: both READMEs, `SKIPPED.md`, and the new + `benchmarks/import-binding-aliases.md`. + +## Decisions made + +- `allow-ffi` stays unscoped: library discovery falls back to a bare loader + name and to `RAYLIB_LIBRARY_PATH`, which a path scope cannot describe. +- `Goccia.gc()` every 10 frames stays in `examples/doom-gpl/run.ts`. Without it + the heap grows about 1.7 MB per frame until the memory ceiling forces a + collection; a `max-memory` setting was no faster than the explicit call. +- The Node `.mjs` adapters stay: `GocciaRunner` has no host process or hashing + API. +- `for...of` stays. On 0.14.0 a classic `for` is about 8 to 10% faster in the + isolated update loop, which does not change the whole-frame conclusion. +- The package version stays `0.1.0`; the package is not on npm and the + repository has no tags. +- The benchmark note keeps its software-rendered numbers for now. Johannes + wants numbers from several machines before it is updated. + +## Validation + +Run on Linux x86-64 against the 0.14.0 release binaries, raylib at the pinned +commit, and a private Xvfb with Mesa llvmpipe: `npm run generate`, `npm test`, +`check:generated`, `test:types`, `test:abi`, `test:ffi`, `pack:check`, the five +window smokes, `test:bunnymark:visual`, `make test-palette` and +`make smoke GOCCIA_FLAGS=-P` all pass. + +Not run locally: the macOS lane and the Docker build. + +A scratch consumer project that installed the packed tarball imported the +bindings by name, by subpath, and as types under `tsc`. + +On the Radeon RX 6600M the instanced Bunnymark path spends about 27.6 ms of a +28.2 ms frame in the JavaScript update loop at 10,000 sprites; drawing and +presenting take under 1 ms. The `DrawTextureV` path spends about 72 ms per +frame in its 10,000 native calls. + +## Open questions + +- Callback-taking raylib functions accept `FFI.callback(...).create(fn)` handles + at run time, but `FFIPointerInput` does not type them. This predates the + update. +- Which name the first tag gets. `github:` provider imports and npm + publication both depend on it. + +## Next steps + +1. Merge the pull request once CI is green on both lanes. +2. Tag the merge commit. +3. Collect Bunnymark numbers across machines, then update + `benchmarks/import-binding-aliases.md`. +4. Optimise the Bunnymark update loop, which now bounds the instanced path. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de155cf..ceab81e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,16 +76,16 @@ jobs: run: | case "$RUNNER_OS-$RUNNER_ARCH" in macOS-ARM64) - asset="gocciascript-0.10.0-macos-arm64.zip" + asset="gocciascript-0.14.0-macos-arm64.zip" ;; macOS-X64) - asset="gocciascript-0.10.0-macos-x64.zip" + asset="gocciascript-0.14.0-macos-x64.zip" ;; Linux-ARM64) - asset="gocciascript-0.10.0-linux-arm64.tar.gz" + asset="gocciascript-0.14.0-linux-arm64.tar.gz" ;; Linux-X64) - asset="gocciascript-0.10.0-linux-x64.tar.gz" + asset="gocciascript-0.14.0-linux-x64.tar.gz" ;; *) echo "Unsupported runner: $RUNNER_OS-$RUNNER_ARCH" >&2 @@ -93,7 +93,7 @@ jobs: ;; esac curl --fail --location \ - "https://github.com/frostney/GocciaScript/releases/download/0.10.0/$asset" \ + "https://github.com/frostney/GocciaScript/releases/download/0.14.0/$asset" \ --output "$RUNNER_TEMP/$asset" mkdir -p "$RUNNER_TEMP/gocciascript" if [[ "$asset" == *.zip ]]; then @@ -101,10 +101,10 @@ jobs: else tar -xzf "$RUNNER_TEMP/$asset" -C "$RUNNER_TEMP/gocciascript" fi - loader="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaScriptLoader -print -quit)" + runner="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaRunner -print -quit)" test_runner="$(find "$RUNNER_TEMP/gocciascript" -type f -name GocciaTestRunner -print -quit)" - chmod +x "$loader" "$test_runner" - echo "GOCCIA_LOADER=$loader" >> "$GITHUB_ENV" + chmod +x "$runner" "$test_runner" + echo "GOCCIA_RUNNER=$runner" >> "$GITHUB_ENV" echo "GOCCIA_TEST_RUNNER=$test_runner" >> "$GITHUB_ENV" - run: npm ci @@ -122,15 +122,18 @@ jobs: shell: bash run: | mkdir -p .cache - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/basic-window-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/basic-input-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/image-loading-smoke.ts - xvfb-run -a "$GOCCIA_LOADER" examples/smoke.ts + # -P accepts the ffi request in goccia.json for this run only. + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/basic-window-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/basic-input-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/image-loading-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P examples/smoke.ts xvfb-run -a npm run test:bunnymark:visual - xvfb-run -a "$GOCCIA_LOADER" tests/fixtures/doom-smoke.ts + xvfb-run -a "$GOCCIA_RUNNER" -P tests/fixtures/doom-smoke.ts test -s .cache/doom-smoke.png - name: Goccia-executed DOOM GPL smoke if: runner.os == 'Linux' shell: bash - run: xvfb-run -a make -C examples/doom-gpl smoke + run: | + make -C examples/doom-gpl test-palette + xvfb-run -a make -C examples/doom-gpl smoke GOCCIA_FLAGS=-P diff --git a/.gitignore b/.gitignore index e1c5fc7..ca34ba8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .cache/ +.agent/waits/ dist/ node_modules/ *.tgz diff --git a/README.md b/README.md index 4183224..f468927 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # GocciaScript raylib Generated, ABI-checked [raylib 6.0](https://github.com/raysan5/raylib/releases/tag/6.0) -bindings for [GocciaScript 0.10.0](https://github.com/frostney/GocciaScript/releases/tag/0.10.0). +bindings for [GocciaScript 0.14.0](https://github.com/frostney/GocciaScript/releases/tag/0.14.0). The bindings load raylib as a dynamic library and keep unsafe FFI use explicit. ## What is included -- deterministic TypeScript bindings and declarations generated from the pinned - official `raylib_api.json`; +- deterministic TypeScript bindings and declarations for all 600 functions of + the pinned official `raylib_api.json`; - all 35 native struct layouts checked against `raylib.h`; -- a machine-readable stable-runtime skip report; +- a machine-readable skip report, currently empty; - macOS and Linux dynamic-library discovery; - typed basic-window, keyboard-input, embedded-image, Bunnymark, and playable raycasting examples, all with an on-screen FPS counter; @@ -17,9 +17,10 @@ The bindings load raylib as a dynamic library and keep unsafe FFI use explicit. execute inside GocciaScript, with dynamic raylib used only for platform I/O; - package, generator, ABI, and representative native-call validation. -GocciaScript reserves aggregate properties named `buffer` and `byteOffset`. -raylib's `AudioStream.buffer` is therefore exposed as `nativeBuffer`; the -generated `RAYLIB_FIELD_ALIASES` object records this sole 6.0 rename. +Struct fields keep their exact native names. `AudioStream.buffer` is therefore +raylib's pointer field rather than the aggregate's backing store, which +`FFI.metadata(value)` returns as `buffer`, `byteOffset`, and `size` for every +aggregate. `const char *` values and non-owned text returns use GocciaScript's UTF-8 string descriptor. Mutable `char *` arguments and owned `char *` returns remain @@ -27,24 +28,31 @@ raw pointers so callers can mutate them and return the exact allocation to `UnloadFileText`, `UnloadUTF8`, or `MemFree`. Pointer arguments accept native pointers, buffers, typed arrays, aggregates, and `null`. -Stable FFI cannot express a parameter that accepts either a JavaScript string -or a null pointer with one descriptor. `LoadShaderRaw`, -`LoadShaderFromMemoryRaw`, and `LoadAutomationEventListRaw` are therefore -generated as additional raw-pointer bindings for their documented nullable -C-string parameters. The four text-replacement APIs likewise expose `Raw` -variants for their nullable replacement argument. The normal exports remain -convenient for non-null strings. - -The official raylib 6.0 API describes 600 functions. Three are intentionally -outside the 0.1 scope (`TraceLog`, `TextFormat`, and `DrawBillboardPro`). -GocciaScript 0.10.0 also rejects functions that mix a top-level `f32` argument -with other top-level argument types. Every other function is generated, and -[SKIPPED.md](SKIPPED.md) records the exact stable-version boundary. No shim is -presented as native coverage. +The documented nullable C-string parameters of `LoadShader`, +`LoadShaderFromMemory`, `LoadAutomationEventList`, and the four +text-replacement APIs use `FFI.nullable("utf8string")`, so they accept a string +or `null`. + +`TraceLog` and `TextFormat` are bound as variadic functions. GocciaScript +requires the C variadic tail as exactly one typed `FFI.varargs` argument, even +when it is empty: + +```ts +TextFormat("%s %03d", FFI.varargs(["utf8string", "i32"], ["raylib", 7])); +TraceLog(LOG_INFO, "ready", FFI.varargs([], [])); +``` + +GocciaScript ships no TypeScript declarations for its `FFI` global. Code that +is type-checked against `bindings/raylib.d.ts` declares it with the exported +`GocciaFFI` interface: `declare const FFI: GocciaFFI;`. + +The official raylib 6.0 API describes 600 functions and all 600 are generated. +[SKIPPED.md](SKIPPED.md) remains the place where a future runtime or generator +limit is recorded. No shim is presented as native coverage. ## Requirements -- GocciaScript 0.10.0 +- GocciaScript 0.14.0 - raylib 6.0 built as a shared/dynamic library - Node.js 24 or newer for host filesystem, hashing, compiler, and packaging orchestration @@ -64,17 +72,56 @@ bindings when raylib lives elsewhere. ## Run an example -The repository `goccia.json` opts into only the unsafe FFI extension. The -examples are TypeScript, which GocciaScript parses as types-as-comments, and +GocciaScript denies host access by default. The repository `goccia.json` +requests only the `ffi` capability, and a config's request applies once you +have reviewed and trusted it: + +```sh +GocciaRunner --trust goccia.json +``` + +Pass `-P` instead to accept the request for a single run without storing +trust. + +## Use from another project + +The package entry point is the generated bindings, so an installed copy is +imported by name: + +```ts +import { InitWindow, closeRaylib } from "@frostney/gocciascript-raylib"; +``` + +GocciaScript resolves a package name only when the importing project grants +`import` for `node_modules`, and it installs the `FFI` global only with the +`ffi` grant. That project's own `goccia.json` therefore needs both, trusted +or accepted with `-P` like the request above: + +```json +{ + "source-type": "module", + "permissions": { + "allow-ffi": true, + "allow-import": ["node_modules=."] + } +} +``` + +Other files in the package, such as `SKIPPED.json`, stay importable by subpath. +Bindings that are instead imported by relative path from outside the importing +project need an `allow-read` grant for `bindings/raylib.ts`. Without the `ffi` +grant the bindings throw a `TypeError` that names it. + +The examples are TypeScript, which GocciaScript parses as types-as-comments, and use `for...of`; neither the traditional-`for` nor `while` compatibility flag is required: ```sh -GocciaScriptLoader examples/basic-window.ts -GocciaScriptLoader examples/basic-input.ts -GocciaScriptLoader examples/image-loading.ts -GocciaScriptLoader examples/bunnymark.ts -GocciaScriptLoader examples/doom-clone.ts +GocciaRunner examples/basic-window.ts +GocciaRunner examples/basic-input.ts +GocciaRunner examples/image-loading.ts +GocciaRunner examples/bunnymark.ts +GocciaRunner examples/doom-clone.ts ``` Bunnymark uses an immutable ESM byte import of the pinned `raybunny.png`; it @@ -85,6 +132,7 @@ contains [an actual DOOM engine example](examples/doom-gpl/README.md) in the separately licensed `examples/doom-gpl/` subproject: ```sh +GocciaRunner --trust examples/doom-gpl/goccia.json make -C examples/doom-gpl run IWAD=/absolute/path/to/doom.wad # Or download checksummed, freely redistributable Freedoom data: make -C examples/doom-gpl run-freedoom @@ -98,8 +146,10 @@ upload, and FPS overlay. No proprietary IWAD is committed, and the entire GPL directory is excluded from the MIT npm tarball. The full engine legitimately uses classic `for`, `while`, and `do...while` -loops. Their compatibility flags are scoped to `examples/doom-gpl/goccia.json`; -the bindings and smaller MIT examples do not enable them. The initial +loops. Their compatibility flags are scoped to `examples/doom-gpl/goccia.json`, +which also requests `ffi` and read access to `bindings/raylib.ts`, because the +bindings live outside the subproject's own directory. The bindings and smaller MIT +examples do not enable those flags. The initial GocciaScript 0.10.0 implementation rendered at roughly 3 FPS on an Apple M1 Max. The packed-palette fallback reaches 4.97 FPS, and presenting the indexed framebuffer through a palette-texture shader reaches 6.32 FPS in controlled @@ -117,15 +167,21 @@ performance case for compatibility loops: | indexed `while` | 4.02 | 6.3692 s | The current `for...of` loop stays, and both legacy loop flags remain disabled. -That loop experiment predates the application-side import aliases and the -public-raylib instanced drawing path. With the import-binding fast path held -constant, the 10,000-sprite workload now measures 30.35 FPS versus the -12.99-FPS `DrawTextureV` baseline. The renderer uses `DrawMeshInstanced` and a -custom shader through the generated bindings; it has no companion native -library. [Loop methodology](benchmarks/bunnymark-loop-syntax.md), +That loop experiment predates the public-raylib instanced drawing path. With +GocciaScript's import-binding fast path held constant, the 10,000-sprite +workload measured 30.35 FPS versus the 12.99-FPS `DrawTextureV` baseline. The +renderer uses `DrawMeshInstanced` and a custom shader through the generated +bindings; it has no companion native library. + +On GocciaScript 0.10.0 every read of an imported binding was slow enough that +the example copied its imports into local constants. GocciaScript 0.11.0 and +later retain resolved import bindings, so the example now calls its imports +directly. +[Loop methodology](benchmarks/bunnymark-loop-syntax.md), [the preceding application baseline](benchmarks/application-performance.md), -and [the instancing validation](benchmarks/bunnymark-instancing.md) are -recorded in the repository. +[the instancing validation](benchmarks/bunnymark-instancing.md), and +[the import-alias removal](benchmarks/import-binding-aliases.md) are recorded +in the repository. ## Project scripts and tests @@ -133,12 +189,11 @@ The pure API-repair and normalization core is GocciaScript-compatible TypeScript in `scripts/lib/api-core.ts`, and the unit suite runs in GocciaScript's own test runner in both interpreter and bytecode modes. -The thin `.mjs` host adapters remain Node scripts because regeneration must -read and write the real checkout, compute SHA-256 digests, invoke the C -compiler, and inspect `npm pack`. Stable `GocciaScriptLoader` intentionally -does not expose ambient host filesystem or process APIs; the sandbox runner -uses a virtual filesystem whose writes are returned as a diff rather than -materialized into the checkout. +The thin `.mjs` host adapters remain Node scripts because regeneration and +validation must compute SHA-256 digests, invoke the C compiler, and inspect +`npm pack`. `GocciaRunner` exposes no host process or hashing API. Its sandbox +mode can write regenerated files back to the checkout through `--copy-rw`, but +that would move only the file I/O into GocciaScript, not those steps. ## Regenerate and validate @@ -149,14 +204,20 @@ npm test npm run check:generated npm run test:types -GOCCIA_LOADER=/path/to/GocciaScriptLoader npm run test:abi -GOCCIA_LOADER=/path/to/GocciaScriptLoader npm run test:ffi +GOCCIA_RUNNER=/path/to/GocciaRunner npm run test:abi +GOCCIA_RUNNER=/path/to/GocciaRunner npm run test:ffi npm run pack:check ``` -`RAYLIB_INCLUDE_DIR` may point at a non-standard header directory for the ABI -test. Regeneration verifies the pinned commit, checksums, repaired JSON defect, -inventory, and output determinism before writing artifacts. +`npm run test:types` checks the declarations and a small consumer program +against them. `RAYLIB_INCLUDE_DIR` may point at a non-standard header directory +for the ABI test. `GOCCIA_TEST_RUNNER` selects the `GocciaTestRunner` binary +for `npm test` in the same way. The unit suite runs with +`--ignore-config-permissions` because it needs no capability, and the +native-call tests accept the config's `ffi` request for their own run, so +neither depends on a trust store. Regeneration verifies the pinned commit, +checksums, repaired JSON defect, inventory, and output determinism before +writing artifacts. The Linux release lane can also be reproduced from macOS or Linux with: diff --git a/SKIPPED.json b/SKIPPED.json index a951f40..a608440 100644 --- a/SKIPPED.json +++ b/SKIPPED.json @@ -1,684 +1,10 @@ { - "schemaVersion": 1, + "schemaVersion": 2, "raylibVersion": "6.0", "raylibCommit": "dbc56a87da87d973a9c5baa4e7438a9d20121d28", - "gocciaScriptVersion": "0.10.0", + "gocciaScriptVersion": "0.14.0", "apiFunctions": 600, - "callableTargetAfterUpstreamSupport": 597, - "generatedFunctions": 488, - "skippedFunctions": 112, - "skipped": [ - { - "name": "TraceLog", - "reason": "varargs", - "category": "intentional-0.1-scope", - "signature": "void TraceLog(int, const char *, ...)" - }, - { - "name": "SetGamepadVibration", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetGamepadVibration(int, float, float, float)" - }, - { - "name": "UpdateCameraPro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateCameraPro(Camera *, Vector3, Vector3, float)" - }, - { - "name": "DrawLineEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawLineEx(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawLineBezier", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawLineBezier(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawCircle", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircle(int, int, float, Color)" - }, - { - "name": "DrawCircleV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleV(Vector2, float, Color)" - }, - { - "name": "DrawCircleGradient", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleGradient(Vector2, float, Color, Color)" - }, - { - "name": "DrawCircleSector", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleSector(Vector2, float, float, float, int, Color)" - }, - { - "name": "DrawCircleSectorLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleSectorLines(Vector2, float, float, float, int, Color)" - }, - { - "name": "DrawCircleLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleLines(int, int, float, Color)" - }, - { - "name": "DrawCircleLinesV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircleLinesV(Vector2, float, Color)" - }, - { - "name": "DrawEllipse", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipse(int, int, float, float, Color)" - }, - { - "name": "DrawEllipseV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseV(Vector2, float, float, Color)" - }, - { - "name": "DrawEllipseLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseLines(int, int, float, float, Color)" - }, - { - "name": "DrawEllipseLinesV", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawEllipseLinesV(Vector2, float, float, Color)" - }, - { - "name": "DrawRing", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRing(Vector2, float, float, float, float, int, Color)" - }, - { - "name": "DrawRingLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRingLines(Vector2, float, float, float, float, int, Color)" - }, - { - "name": "DrawRectanglePro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectanglePro(Rectangle, Vector2, float, Color)" - }, - { - "name": "DrawRectangleLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleLinesEx(Rectangle, float, Color)" - }, - { - "name": "DrawRectangleRounded", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRounded(Rectangle, float, int, Color)" - }, - { - "name": "DrawRectangleRoundedLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRoundedLines(Rectangle, float, int, Color)" - }, - { - "name": "DrawRectangleRoundedLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawRectangleRoundedLinesEx(Rectangle, float, int, float, Color)" - }, - { - "name": "DrawPoly", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPoly(Vector2, int, float, float, Color)" - }, - { - "name": "DrawPolyLines", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPolyLines(Vector2, int, float, float, Color)" - }, - { - "name": "DrawPolyLinesEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawPolyLinesEx(Vector2, int, float, float, float, Color)" - }, - { - "name": "DrawSplineLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineLinear(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBasis(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineCatmullRom(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBezierQuadratic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBezierQuadratic(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineBezierCubic(const Vector2 *, int, float, Color)" - }, - { - "name": "DrawSplineSegmentLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentLinear(Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBasis(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentCatmullRom(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBezierQuadratic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBezierQuadratic(Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "DrawSplineSegmentBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSplineSegmentBezierCubic(Vector2, Vector2, Vector2, Vector2, float, Color)" - }, - { - "name": "GetSplinePointLinear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointLinear(Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBasis", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBasis(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointCatmullRom", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointCatmullRom(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBezierQuad", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBezierQuad(Vector2, Vector2, Vector2, float)" - }, - { - "name": "GetSplinePointBezierCubic", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 GetSplinePointBezierCubic(Vector2, Vector2, Vector2, Vector2, float)" - }, - { - "name": "CheckCollisionCircles", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircles(Vector2, float, Vector2, float)" - }, - { - "name": "CheckCollisionCircleRec", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircleRec(Vector2, float, Rectangle)" - }, - { - "name": "CheckCollisionCircleLine", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionCircleLine(Vector2, float, Vector2, Vector2)" - }, - { - "name": "CheckCollisionPointCircle", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionPointCircle(Vector2, Vector2, float)" - }, - { - "name": "GenImageGradientRadial", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageGradientRadial(int, int, float, Color, Color)" - }, - { - "name": "GenImageGradientSquare", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageGradientSquare(int, int, float, Color, Color)" - }, - { - "name": "GenImageWhiteNoise", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImageWhiteNoise(int, int, float)" - }, - { - "name": "GenImagePerlinNoise", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image GenImagePerlinNoise(int, int, int, int, float)" - }, - { - "name": "ImageTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Image ImageTextEx(Font, const char *, float, float, Color)" - }, - { - "name": "ImageAlphaCrop", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageAlphaCrop(Image *, float)" - }, - { - "name": "ImageAlphaClear", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageAlphaClear(Image *, Color, float)" - }, - { - "name": "ImageColorContrast", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageColorContrast(Image *, float)" - }, - { - "name": "GetImageAlphaBorder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Rectangle GetImageAlphaBorder(Image, float)" - }, - { - "name": "ImageDrawTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void ImageDrawTextEx(Image *, Font, const char *, Vector2, float, float, Color)" - }, - { - "name": "DrawTextureEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextureEx(Texture2D, Vector2, float, float, Color)" - }, - { - "name": "DrawTexturePro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color)" - }, - { - "name": "DrawTextureNPatch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextureNPatch(Texture2D, NPatchInfo, Rectangle, Vector2, float, Color)" - }, - { - "name": "Fade", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color Fade(Color, float)" - }, - { - "name": "ColorBrightness", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorBrightness(Color, float)" - }, - { - "name": "ColorContrast", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorContrast(Color, float)" - }, - { - "name": "ColorAlpha", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorAlpha(Color, float)" - }, - { - "name": "ColorLerp", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Color ColorLerp(Color, Color, float)" - }, - { - "name": "DrawTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextEx(Font, const char *, Vector2, float, float, Color)" - }, - { - "name": "DrawTextPro", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextPro(Font, const char *, Vector2, Vector2, float, float, float, Color)" - }, - { - "name": "DrawTextCodepoint", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextCodepoint(Font, int, Vector2, float, Color)" - }, - { - "name": "DrawTextCodepoints", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawTextCodepoints(Font, const int *, int, Vector2, float, float, Color)" - }, - { - "name": "MeasureTextEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 MeasureTextEx(Font, const char *, float, float)" - }, - { - "name": "MeasureTextCodepoints", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Vector2 MeasureTextCodepoints(Font, const int *, int, float, float)" - }, - { - "name": "TextFormat", - "reason": "varargs", - "category": "intentional-0.1-scope", - "signature": "const char * TextFormat(const char *, ...)" - }, - { - "name": "DrawCircle3D", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCircle3D(Vector3, float, Vector3, float, Color)" - }, - { - "name": "DrawCube", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCube(Vector3, float, float, float, Color)" - }, - { - "name": "DrawCubeWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCubeWires(Vector3, float, float, float, Color)" - }, - { - "name": "DrawSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphere(Vector3, float, Color)" - }, - { - "name": "DrawSphereEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphereEx(Vector3, float, int, int, Color)" - }, - { - "name": "DrawSphereWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawSphereWires(Vector3, float, int, int, Color)" - }, - { - "name": "DrawCylinder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinder(Vector3, float, float, float, int, Color)" - }, - { - "name": "DrawCylinderEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderEx(Vector3, Vector3, float, float, int, Color)" - }, - { - "name": "DrawCylinderWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderWires(Vector3, float, float, float, int, Color)" - }, - { - "name": "DrawCylinderWiresEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCylinderWiresEx(Vector3, Vector3, float, float, int, Color)" - }, - { - "name": "DrawCapsule", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCapsule(Vector3, Vector3, float, int, int, Color)" - }, - { - "name": "DrawCapsuleWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawCapsuleWires(Vector3, Vector3, float, int, int, Color)" - }, - { - "name": "DrawGrid", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawGrid(int, float)" - }, - { - "name": "DrawModel", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModel(Model, Vector3, float, Color)" - }, - { - "name": "DrawModelEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelEx(Model, Vector3, Vector3, float, Vector3, Color)" - }, - { - "name": "DrawModelWires", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelWires(Model, Vector3, float, Color)" - }, - { - "name": "DrawModelWiresEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawModelWiresEx(Model, Vector3, Vector3, float, Vector3, Color)" - }, - { - "name": "DrawBillboard", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void DrawBillboard(Camera, Texture2D, Vector3, float, Color)" - }, - { - "name": "DrawBillboardPro", - "reason": "more-than-8-arguments", - "category": "intentional-0.1-scope", - "signature": "void DrawBillboardPro(Camera, Texture2D, Rectangle, Vector3, Vector3, Vector2, Vector2, float, Color)" - }, - { - "name": "GenMeshPoly", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshPoly(int, float)" - }, - { - "name": "GenMeshPlane", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshPlane(float, float, int, int)" - }, - { - "name": "GenMeshSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshSphere(float, int, int)" - }, - { - "name": "GenMeshHemiSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshHemiSphere(float, int, int)" - }, - { - "name": "GenMeshCylinder", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshCylinder(float, float, int)" - }, - { - "name": "GenMeshCone", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshCone(float, float, int)" - }, - { - "name": "GenMeshTorus", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshTorus(float, float, int, int)" - }, - { - "name": "GenMeshKnot", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "Mesh GenMeshKnot(float, float, int, int)" - }, - { - "name": "UpdateModelAnimation", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateModelAnimation(Model, ModelAnimation, float)" - }, - { - "name": "UpdateModelAnimationEx", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void UpdateModelAnimationEx(Model, ModelAnimation, float, ModelAnimation, float, float)" - }, - { - "name": "CheckCollisionSpheres", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionSpheres(Vector3, float, Vector3, float)" - }, - { - "name": "CheckCollisionBoxSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "bool CheckCollisionBoxSphere(BoundingBox, Vector3, float)" - }, - { - "name": "GetRayCollisionSphere", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "RayCollision GetRayCollisionSphere(Ray, Vector3, float)" - }, - { - "name": "SetSoundVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundVolume(Sound, float)" - }, - { - "name": "SetSoundPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundPitch(Sound, float)" - }, - { - "name": "SetSoundPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetSoundPan(Sound, float)" - }, - { - "name": "SeekMusicStream", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SeekMusicStream(Music, float)" - }, - { - "name": "SetMusicVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicVolume(Music, float)" - }, - { - "name": "SetMusicPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicPitch(Music, float)" - }, - { - "name": "SetMusicPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetMusicPan(Music, float)" - }, - { - "name": "SetAudioStreamVolume", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamVolume(AudioStream, float)" - }, - { - "name": "SetAudioStreamPitch", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamPitch(AudioStream, float)" - }, - { - "name": "SetAudioStreamPan", - "reason": "mixed-top-level-f32", - "category": "stable-runtime-limit", - "signature": "void SetAudioStreamPan(AudioStream, float)" - } - ] + "generatedFunctions": 600, + "skippedFunctions": 0, + "skipped": [] } diff --git a/SKIPPED.md b/SKIPPED.md index e9af94f..c2bb30d 100644 --- a/SKIPPED.md +++ b/SKIPPED.md @@ -1,134 +1,11 @@ # Skipped raylib functions Generated against raylib 6.0 at `dbc56a87da87d973a9c5baa4e7438a9d20121d28` and -GocciaScript 0.10.0. +GocciaScript 0.14.0. - Official API functions: 600 -- Generated and callable on the pinned stable runtime: 488 -- Deterministically skipped: 112 -- Target after mixed-top-level-`f32` support: 597 +- Generated and callable on the pinned stable runtime: 600 +- Deterministically skipped: 0 -The report is a compatibility boundary, not a claim that skipped functions are -implemented. Re-run the generator after the upstream FFI restriction is removed. -The three intentional 0.1 exclusions remain out of scope. - -## intentional-0.1-scope: varargs (2) - -- `void TraceLog(int, const char *, ...)` -- `const char * TextFormat(const char *, ...)` - -## stable-runtime-limit: mixed-top-level-f32 (109) - -- `void SetGamepadVibration(int, float, float, float)` -- `void UpdateCameraPro(Camera *, Vector3, Vector3, float)` -- `void DrawLineEx(Vector2, Vector2, float, Color)` -- `void DrawLineBezier(Vector2, Vector2, float, Color)` -- `void DrawCircle(int, int, float, Color)` -- `void DrawCircleV(Vector2, float, Color)` -- `void DrawCircleGradient(Vector2, float, Color, Color)` -- `void DrawCircleSector(Vector2, float, float, float, int, Color)` -- `void DrawCircleSectorLines(Vector2, float, float, float, int, Color)` -- `void DrawCircleLines(int, int, float, Color)` -- `void DrawCircleLinesV(Vector2, float, Color)` -- `void DrawEllipse(int, int, float, float, Color)` -- `void DrawEllipseV(Vector2, float, float, Color)` -- `void DrawEllipseLines(int, int, float, float, Color)` -- `void DrawEllipseLinesV(Vector2, float, float, Color)` -- `void DrawRing(Vector2, float, float, float, float, int, Color)` -- `void DrawRingLines(Vector2, float, float, float, float, int, Color)` -- `void DrawRectanglePro(Rectangle, Vector2, float, Color)` -- `void DrawRectangleLinesEx(Rectangle, float, Color)` -- `void DrawRectangleRounded(Rectangle, float, int, Color)` -- `void DrawRectangleRoundedLines(Rectangle, float, int, Color)` -- `void DrawRectangleRoundedLinesEx(Rectangle, float, int, float, Color)` -- `void DrawPoly(Vector2, int, float, float, Color)` -- `void DrawPolyLines(Vector2, int, float, float, Color)` -- `void DrawPolyLinesEx(Vector2, int, float, float, float, Color)` -- `void DrawSplineLinear(const Vector2 *, int, float, Color)` -- `void DrawSplineBasis(const Vector2 *, int, float, Color)` -- `void DrawSplineCatmullRom(const Vector2 *, int, float, Color)` -- `void DrawSplineBezierQuadratic(const Vector2 *, int, float, Color)` -- `void DrawSplineBezierCubic(const Vector2 *, int, float, Color)` -- `void DrawSplineSegmentLinear(Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBasis(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentCatmullRom(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBezierQuadratic(Vector2, Vector2, Vector2, float, Color)` -- `void DrawSplineSegmentBezierCubic(Vector2, Vector2, Vector2, Vector2, float, Color)` -- `Vector2 GetSplinePointLinear(Vector2, Vector2, float)` -- `Vector2 GetSplinePointBasis(Vector2, Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointCatmullRom(Vector2, Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointBezierQuad(Vector2, Vector2, Vector2, float)` -- `Vector2 GetSplinePointBezierCubic(Vector2, Vector2, Vector2, Vector2, float)` -- `bool CheckCollisionCircles(Vector2, float, Vector2, float)` -- `bool CheckCollisionCircleRec(Vector2, float, Rectangle)` -- `bool CheckCollisionCircleLine(Vector2, float, Vector2, Vector2)` -- `bool CheckCollisionPointCircle(Vector2, Vector2, float)` -- `Image GenImageGradientRadial(int, int, float, Color, Color)` -- `Image GenImageGradientSquare(int, int, float, Color, Color)` -- `Image GenImageWhiteNoise(int, int, float)` -- `Image GenImagePerlinNoise(int, int, int, int, float)` -- `Image ImageTextEx(Font, const char *, float, float, Color)` -- `void ImageAlphaCrop(Image *, float)` -- `void ImageAlphaClear(Image *, Color, float)` -- `void ImageColorContrast(Image *, float)` -- `Rectangle GetImageAlphaBorder(Image, float)` -- `void ImageDrawTextEx(Image *, Font, const char *, Vector2, float, float, Color)` -- `void DrawTextureEx(Texture2D, Vector2, float, float, Color)` -- `void DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color)` -- `void DrawTextureNPatch(Texture2D, NPatchInfo, Rectangle, Vector2, float, Color)` -- `Color Fade(Color, float)` -- `Color ColorBrightness(Color, float)` -- `Color ColorContrast(Color, float)` -- `Color ColorAlpha(Color, float)` -- `Color ColorLerp(Color, Color, float)` -- `void DrawTextEx(Font, const char *, Vector2, float, float, Color)` -- `void DrawTextPro(Font, const char *, Vector2, Vector2, float, float, float, Color)` -- `void DrawTextCodepoint(Font, int, Vector2, float, Color)` -- `void DrawTextCodepoints(Font, const int *, int, Vector2, float, float, Color)` -- `Vector2 MeasureTextEx(Font, const char *, float, float)` -- `Vector2 MeasureTextCodepoints(Font, const int *, int, float, float)` -- `void DrawCircle3D(Vector3, float, Vector3, float, Color)` -- `void DrawCube(Vector3, float, float, float, Color)` -- `void DrawCubeWires(Vector3, float, float, float, Color)` -- `void DrawSphere(Vector3, float, Color)` -- `void DrawSphereEx(Vector3, float, int, int, Color)` -- `void DrawSphereWires(Vector3, float, int, int, Color)` -- `void DrawCylinder(Vector3, float, float, float, int, Color)` -- `void DrawCylinderEx(Vector3, Vector3, float, float, int, Color)` -- `void DrawCylinderWires(Vector3, float, float, float, int, Color)` -- `void DrawCylinderWiresEx(Vector3, Vector3, float, float, int, Color)` -- `void DrawCapsule(Vector3, Vector3, float, int, int, Color)` -- `void DrawCapsuleWires(Vector3, Vector3, float, int, int, Color)` -- `void DrawGrid(int, float)` -- `void DrawModel(Model, Vector3, float, Color)` -- `void DrawModelEx(Model, Vector3, Vector3, float, Vector3, Color)` -- `void DrawModelWires(Model, Vector3, float, Color)` -- `void DrawModelWiresEx(Model, Vector3, Vector3, float, Vector3, Color)` -- `void DrawBillboard(Camera, Texture2D, Vector3, float, Color)` -- `Mesh GenMeshPoly(int, float)` -- `Mesh GenMeshPlane(float, float, int, int)` -- `Mesh GenMeshSphere(float, int, int)` -- `Mesh GenMeshHemiSphere(float, int, int)` -- `Mesh GenMeshCylinder(float, float, int)` -- `Mesh GenMeshCone(float, float, int)` -- `Mesh GenMeshTorus(float, float, int, int)` -- `Mesh GenMeshKnot(float, float, int, int)` -- `void UpdateModelAnimation(Model, ModelAnimation, float)` -- `void UpdateModelAnimationEx(Model, ModelAnimation, float, ModelAnimation, float, float)` -- `bool CheckCollisionSpheres(Vector3, float, Vector3, float)` -- `bool CheckCollisionBoxSphere(BoundingBox, Vector3, float)` -- `RayCollision GetRayCollisionSphere(Ray, Vector3, float)` -- `void SetSoundVolume(Sound, float)` -- `void SetSoundPitch(Sound, float)` -- `void SetSoundPan(Sound, float)` -- `void SeekMusicStream(Music, float)` -- `void SetMusicVolume(Music, float)` -- `void SetMusicPitch(Music, float)` -- `void SetMusicPan(Music, float)` -- `void SetAudioStreamVolume(AudioStream, float)` -- `void SetAudioStreamPitch(AudioStream, float)` -- `void SetAudioStreamPan(AudioStream, float)` - -## intentional-0.1-scope: more-than-8-arguments (1) - -- `void DrawBillboardPro(Camera, Texture2D, Rectangle, Vector3, Vector3, Vector2, Vector2, float, Color)` +The pinned runtime binds the complete official API. A function that a future +raylib or runtime version cannot bind is listed here rather than shimmed. diff --git a/benchmarks/import-binding-aliases.md b/benchmarks/import-binding-aliases.md new file mode 100644 index 0000000..6b30477 --- /dev/null +++ b/benchmarks/import-binding-aliases.md @@ -0,0 +1,79 @@ +# Import-binding alias removal + +Measured on 2026-10-01 while updating the bindings from GocciaScript 0.10.0 to +0.14.0, to decide whether Bunnymark should keep copying its raylib imports +into local constants. + +## Setup + +- AMD Ryzen 9 6900HX, Linux Mint 22.3 (x86-64) +- GocciaScript 0.10.0 and 0.14.0 release binaries, bytecode mode +- dynamically loaded raylib 6.0 at the pinned commit +- Xvfb with Mesa llvmpipe 25.2.8 (software OpenGL 4.5), so absolute frame rates + are not comparable with the Apple M1 Max results in the other reports + +## Imported binding versus local alias + +A module exports `add` and a numeric constant. A second module calls +`add(sum, K)` 300,000 times, once through the imported bindings and once +through module-level `const` copies of them. Each variant ran three times in +one process; the three runs agreed within 0.4%. + +| GocciaScript | Through imports | Through local aliases | Ratio | +|---|---:|---:|---:| +| 0.10.0 | 5773.1 ms | 192.6 ms | 29.97x | +| 0.14.0 | 218.7 ms | 149.1 ms | 1.47x | + +Each iteration reads two imported bindings. Compared with a local read, one +imported read cost about 9.3 microseconds more on 0.10.0 and costs about +0.12 microseconds more on 0.14.0. GocciaScript 0.11.0 lists the change as "retain resolved import +bindings". + +## Bunnymark with and without aliases + +The 0.14.0 example was run with the alias block from the previous revision and +with direct imports: 10,000 fixed-seed sprites (`1234`), uncapped target, +25 frames per run, three interleaved runs per variant. The values are the +example's own 25-frame time after startup. + +| Draw path | Direct imports | Local aliases | +|---|---|---| +| `DrawTextureV` | 2803, 2784, 2908 ms (mean 2831.7) | 2733, 2852, 2831 ms (mean 2805.3) | +| `DrawMeshInstanced` | 1118, 1142, 1196 ms (mean 1152.0) | 1125, 1168, 1184 ms (mean 1159.0) | + +The means differ by 0.9% on the per-sprite `DrawTextureV` path and by 0.6% in +the other direction on the instanced path. Both differences are smaller than +the spread between runs of one variant. + +For the whole update, the previous revision on 0.10.0 (aliases, heightmap +quad) was compared with this revision on 0.14.0 (direct imports, plane quad) +under the same workload, three interleaved runs each: + +| Draw path | Previous revision, 0.10.0 | This revision, 0.14.0 | +|---|---|---| +| `DrawTextureV` | 2946, 2916, 2940 ms (mean 2934.0) | 2875, 2877, 2877 ms (mean 2876.3) | +| `DrawMeshInstanced` | 1260, 1290, 1268 ms (mean 1272.7) | 1182, 1181, 1176 ms (mean 1179.7) | + +## Decision + +The alias block is removed. It no longer changes the measured frame time, and +the instanced path, which is the default, reads no imported binding per +sprite. + +## Rendering equivalence + +The same update replaced two other 0.10.0 workarounds with functions that +0.10.0 could not bind because they mix `float` with other argument types. +Both replacements were checked against the previous rendering: + +- Bunnymark's instanced quad now comes from `GenMeshPlane` instead of a + `GenMeshHeightmap` call on a throwaway 2x2 image. The graphical integration + test still finds the instanced and `DrawTextureV` frames byte-identical, with + the SHA-256 recorded for Linux in + [the instancing report](bunnymark-instancing.md): + `e22537b001590b497bad6c8da318d38339bef0fb4cdb95a8c8f449584e47d1a0`. +- DOOM scales its 320x200 texture with `DrawTextureEx` instead of a zoomed + `Camera2D`. Frame 8 of the Freedoom smoke scene, with the FPS overlay + disabled, is byte-identical before and after on both the indexed-shader and + RGBA display paths: + `2fdda7ef7a9f42f95e17a33e5e955618af5afd6b19a282781316024893c506e0`. diff --git a/bindings/raylib.d.ts b/bindings/raylib.d.ts index 6f5df10..cec82b5 100644 --- a/bindings/raylib.d.ts +++ b/bindings/raylib.d.ts @@ -1,5 +1,8 @@ // Generated from official raylib 6.0. Do not edit. +// Only the declarations marked export are part of the module. +export {}; + export interface FFIPointer { readonly address: number; readonly isNull: boolean; @@ -31,29 +34,58 @@ export type FFIPointerInput = | ArrayBuffer | SharedArrayBuffer | FFITypedArray - | FFIAggregateValue + | FFIAggregate | null; -export interface FFIAggregateValue { +declare const ffiAggregate: unique symbol; +/** Any GocciaScript FFI aggregate value. */ +export interface FFIAggregate { + readonly [ffiAggregate]: true; +} +/** Backing store of an aggregate, as returned by GocciaScript's `FFI.metadata(value)`. */ +export interface FFIAggregateMetadata { readonly buffer: ArrayBuffer; readonly byteOffset: number; readonly size: number; } +/** + * Aggregate that also exposes its backing store directly. A native field named + * like a metadata property takes precedence; `FFI.metadata(value)` always works. + */ +export interface FFIAggregateValue extends FFIAggregate, FFIAggregateMetadata {} export interface FFIArrayValue extends FFIAggregateValue { readonly length: number; [index: number]: T; } -export interface FFIStructDescriptor { +export interface FFIStructDescriptor { readonly kind: "struct"; readonly size: number; readonly alignment: number; create(initializer?: Partial): T; } +declare const ffiVarargs: unique symbol; +/** Typed variadic tail created by GocciaScript's `FFI.varargs(types, values)`. */ +export interface FFIVarargs { + readonly [ffiVarargs]: true; +} +/** + * The members of GocciaScript's `FFI` global that callers of these bindings + * use. GocciaScript ships no declarations, so type-checked code declares the + * global itself: `declare const FFI: GocciaFFI;`. + */ +export interface GocciaFFI { + varargs(types: readonly unknown[], values: readonly unknown[]): FFIVarargs; + metadata(value: FFIAggregate): FFIAggregateMetadata; +} export interface FFILibrary { readonly path: string; readonly closed: boolean; bind( name: string, - signature: { args: readonly unknown[]; returns: unknown }, + signature: { + args: readonly unknown[]; + returns: unknown; + variadic?: boolean; + }, ): (...args: unknown[]) => unknown; symbol(name: string): FFIPointer; close(): void; @@ -64,9 +96,6 @@ export declare const RAYLIB_BINDING_INFO: { gocciaScriptVersion: string; linkage: "dynamic"; }; -export declare const RAYLIB_FIELD_ALIASES: { - AudioStream: { buffer: "nativeBuffer" }; -}; export declare const raylibLibrary: FFILibrary; export declare function closeRaylib(): void; @@ -336,9 +365,9 @@ export interface WaveValue extends FFIAggregateValue { export declare const Wave: FFIStructDescriptor; /** AudioStream, custom audio stream */ -export interface AudioStreamValue extends FFIAggregateValue { - /** Native C field: buffer. */ - "nativeBuffer": FFIPointer; +export interface AudioStreamValue extends FFIAggregate, Omit { + /** Native C field; read the backing store with `FFI.metadata(value).buffer`. */ + "buffer": FFIPointer; "processor": FFIPointer; "sampleRate": number; "sampleSize": number; @@ -1374,16 +1403,10 @@ export declare function LoadVrStereoConfig(device: VrDeviceInfoValue): VrStereoC export declare function UnloadVrStereoConfig(config: VrStereoConfigValue): void; /** Load shader from files and bind default locations */ -export declare function LoadShader(vsFileName: string, fsFileName: string): ShaderValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadShaderRaw(vsFileName: FFIPointerInput, fsFileName: FFIPointerInput): ShaderValue; +export declare function LoadShader(vsFileName: string | null, fsFileName: string | null): ShaderValue; /** Load shader from code strings and bind default locations */ -export declare function LoadShaderFromMemory(vsCode: string, fsCode: string): ShaderValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadShaderFromMemoryRaw(vsCode: FFIPointerInput, fsCode: FFIPointerInput): ShaderValue; +export declare function LoadShaderFromMemory(vsCode: string | null, fsCode: string | null): ShaderValue; /** Check if a shader is valid (loaded on GPU) */ export declare function IsShaderValid(shader: ShaderValue): boolean; @@ -1478,6 +1501,9 @@ export declare function OpenURL(url: string): void; /** Set the current threshold (minimum) log level */ export declare function SetTraceLogLevel(logLevel: number): void; +/** Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) */ +export declare function TraceLog(logLevel: number, text: string, args: FFIVarargs): void; + /** Set custom trace log */ export declare function SetTraceLogCallback(callback: FFIPointerInput): void; @@ -1638,10 +1664,7 @@ export declare function ComputeSHA1(data: FFIPointerInput, dataSize: number): FF export declare function ComputeSHA256(data: FFIPointerInput, dataSize: number): FFIPointer; /** Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS */ -export declare function LoadAutomationEventList(fileName: string): AutomationEventListValue; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function LoadAutomationEventListRaw(fileName: FFIPointerInput): AutomationEventListValue; +export declare function LoadAutomationEventList(fileName: string | null): AutomationEventListValue; /** Unload automation events list from file */ export declare function UnloadAutomationEventList(list: AutomationEventListValue): void; @@ -1721,6 +1744,9 @@ export declare function GetGamepadAxisMovement(gamepad: number, axis: number): n /** Set internal gamepad mappings (SDL_GameControllerDB) */ export declare function SetGamepadMappings(mappings: string): number; +/** Set gamepad vibration for both motors (duration in seconds) */ +export declare function SetGamepadVibration(gamepad: number, leftMotor: number, rightMotor: number, duration: number): void; + /** Check if a mouse button has been pressed once */ export declare function IsMouseButtonPressed(button: number): boolean; @@ -1805,6 +1831,9 @@ export declare function GetGesturePinchAngle(): number; /** Update camera position for selected mode */ export declare function UpdateCamera(camera: FFIPointerInput, mode: number): void; +/** Update camera movement/rotation */ +export declare function UpdateCameraPro(camera: FFIPointerInput, movement: Vector3Value, rotation: Vector3Value, zoom: number): void; + /** Set texture and rectangle to be used on shapes drawing */ export declare function SetShapesTexture(texture: Texture2DValue, source: RectangleValue): void; @@ -1826,12 +1855,57 @@ export declare function DrawLine(startPosX: number, startPosY: number, endPosX: /** Draw a line (using gl lines) */ export declare function DrawLineV(startPos: Vector2Value, endPos: Vector2Value, color: ColorValue): void; +/** Draw a line (using triangles/quads) */ +export declare function DrawLineEx(startPos: Vector2Value, endPos: Vector2Value, thick: number, color: ColorValue): void; + /** Draw lines sequence (using gl lines) */ export declare function DrawLineStrip(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw line segment cubic-bezier in-out interpolation */ +export declare function DrawLineBezier(startPos: Vector2Value, endPos: Vector2Value, thick: number, color: ColorValue): void; + /** Draw a dashed line */ export declare function DrawLineDashed(startPos: Vector2Value, endPos: Vector2Value, dashSize: number, spaceSize: number, color: ColorValue): void; +/** Draw a color-filled circle */ +export declare function DrawCircle(centerX: number, centerY: number, radius: number, color: ColorValue): void; + +/** Draw a color-filled circle (Vector version) */ +export declare function DrawCircleV(center: Vector2Value, radius: number, color: ColorValue): void; + +/** Draw a gradient-filled circle */ +export declare function DrawCircleGradient(center: Vector2Value, radius: number, inner: ColorValue, outer: ColorValue): void; + +/** Draw a piece of a circle */ +export declare function DrawCircleSector(center: Vector2Value, radius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw circle sector outline */ +export declare function DrawCircleSectorLines(center: Vector2Value, radius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw circle outline */ +export declare function DrawCircleLines(centerX: number, centerY: number, radius: number, color: ColorValue): void; + +/** Draw circle outline (Vector version) */ +export declare function DrawCircleLinesV(center: Vector2Value, radius: number, color: ColorValue): void; + +/** Draw ellipse */ +export declare function DrawEllipse(centerX: number, centerY: number, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse (Vector version) */ +export declare function DrawEllipseV(center: Vector2Value, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse outline */ +export declare function DrawEllipseLines(centerX: number, centerY: number, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ellipse outline (Vector version) */ +export declare function DrawEllipseLinesV(center: Vector2Value, radiusH: number, radiusV: number, color: ColorValue): void; + +/** Draw ring */ +export declare function DrawRing(center: Vector2Value, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + +/** Draw ring outline */ +export declare function DrawRingLines(center: Vector2Value, innerRadius: number, outerRadius: number, startAngle: number, endAngle: number, segments: number, color: ColorValue): void; + /** Draw a color-filled rectangle */ export declare function DrawRectangle(posX: number, posY: number, width: number, height: number, color: ColorValue): void; @@ -1841,6 +1915,9 @@ export declare function DrawRectangleV(position: Vector2Value, size: Vector2Valu /** Draw a color-filled rectangle */ export declare function DrawRectangleRec(rec: RectangleValue, color: ColorValue): void; +/** Draw a color-filled rectangle with pro parameters */ +export declare function DrawRectanglePro(rec: RectangleValue, origin: Vector2Value, rotation: number, color: ColorValue): void; + /** Draw a vertical-gradient-filled rectangle */ export declare function DrawRectangleGradientV(posX: number, posY: number, width: number, height: number, top: ColorValue, bottom: ColorValue): void; @@ -1853,6 +1930,18 @@ export declare function DrawRectangleGradientEx(rec: RectangleValue, topLeft: Co /** Draw rectangle outline */ export declare function DrawRectangleLines(posX: number, posY: number, width: number, height: number, color: ColorValue): void; +/** Draw rectangle outline with extended parameters */ +export declare function DrawRectangleLinesEx(rec: RectangleValue, lineThick: number, color: ColorValue): void; + +/** Draw rectangle with rounded edges */ +export declare function DrawRectangleRounded(rec: RectangleValue, roundness: number, segments: number, color: ColorValue): void; + +/** Draw rectangle lines with rounded edges */ +export declare function DrawRectangleRoundedLines(rec: RectangleValue, roundness: number, segments: number, color: ColorValue): void; + +/** Draw rectangle with rounded edges outline */ +export declare function DrawRectangleRoundedLinesEx(rec: RectangleValue, roundness: number, segments: number, lineThick: number, color: ColorValue): void; + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export declare function DrawTriangle(v1: Vector2Value, v2: Vector2Value, v3: Vector2Value, color: ColorValue): void; @@ -1865,12 +1954,78 @@ export declare function DrawTriangleFan(points: FFIPointerInput, pointCount: num /** Draw a triangle strip defined by points */ export declare function DrawTriangleStrip(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw a regular polygon (Vector version) */ +export declare function DrawPoly(center: Vector2Value, sides: number, radius: number, rotation: number, color: ColorValue): void; + +/** Draw a polygon outline of n sides */ +export declare function DrawPolyLines(center: Vector2Value, sides: number, radius: number, rotation: number, color: ColorValue): void; + +/** Draw a polygon outline of n sides with extended parameters */ +export declare function DrawPolyLinesEx(center: Vector2Value, sides: number, radius: number, rotation: number, lineThick: number, color: ColorValue): void; + +/** Draw spline: Linear, minimum 2 points */ +export declare function DrawSplineLinear(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: B-Spline, minimum 4 points */ +export declare function DrawSplineBasis(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Catmull-Rom, minimum 4 points */ +export declare function DrawSplineCatmullRom(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] */ +export declare function DrawSplineBezierQuadratic(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] */ +export declare function DrawSplineBezierCubic(points: FFIPointerInput, pointCount: number, thick: number, color: ColorValue): void; + +/** Draw spline segment: Linear, 2 points */ +export declare function DrawSplineSegmentLinear(p1: Vector2Value, p2: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: B-Spline, 4 points */ +export declare function DrawSplineSegmentBasis(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Catmull-Rom, 4 points */ +export declare function DrawSplineSegmentCatmullRom(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Quadratic Bezier, 2 points, 1 control point */ +export declare function DrawSplineSegmentBezierQuadratic(p1: Vector2Value, c2: Vector2Value, p3: Vector2Value, thick: number, color: ColorValue): void; + +/** Draw spline segment: Cubic Bezier, 2 points, 2 control points */ +export declare function DrawSplineSegmentBezierCubic(p1: Vector2Value, c2: Vector2Value, c3: Vector2Value, p4: Vector2Value, thick: number, color: ColorValue): void; + +/** Get (evaluate) spline point: Linear */ +export declare function GetSplinePointLinear(startPos: Vector2Value, endPos: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: B-Spline */ +export declare function GetSplinePointBasis(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Catmull-Rom */ +export declare function GetSplinePointCatmullRom(p1: Vector2Value, p2: Vector2Value, p3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Quadratic Bezier */ +export declare function GetSplinePointBezierQuad(p1: Vector2Value, c2: Vector2Value, p3: Vector2Value, t: number): Vector2Value; + +/** Get (evaluate) spline point: Cubic Bezier */ +export declare function GetSplinePointBezierCubic(p1: Vector2Value, c2: Vector2Value, c3: Vector2Value, p4: Vector2Value, t: number): Vector2Value; + /** Check collision between two rectangles */ export declare function CheckCollisionRecs(rec1: RectangleValue, rec2: RectangleValue): boolean; +/** Check collision between two circles */ +export declare function CheckCollisionCircles(center1: Vector2Value, radius1: number, center2: Vector2Value, radius2: number): boolean; + +/** Check collision between circle and rectangle */ +export declare function CheckCollisionCircleRec(center: Vector2Value, radius: number, rec: RectangleValue): boolean; + +/** Check if circle collides with a line created betweeen two points [p1] and [p2] */ +export declare function CheckCollisionCircleLine(center: Vector2Value, radius: number, p1: Vector2Value, p2: Vector2Value): boolean; + /** Check if point is inside rectangle */ export declare function CheckCollisionPointRec(point: Vector2Value, rec: RectangleValue): boolean; +/** Check if point is inside circle */ +export declare function CheckCollisionPointCircle(point: Vector2Value, center: Vector2Value, radius: number): boolean; + /** Check if point is inside a triangle */ export declare function CheckCollisionPointTriangle(point: Vector2Value, p1: Vector2Value, p2: Vector2Value, p3: Vector2Value): boolean; @@ -1928,9 +2083,21 @@ export declare function GenImageColor(width: number, height: number, color: Colo /** Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient */ export declare function GenImageGradientLinear(width: number, height: number, direction: number, start: ColorValue, end: ColorValue): ImageValue; +/** Generate image: radial gradient */ +export declare function GenImageGradientRadial(width: number, height: number, density: number, inner: ColorValue, outer: ColorValue): ImageValue; + +/** Generate image: square gradient */ +export declare function GenImageGradientSquare(width: number, height: number, density: number, inner: ColorValue, outer: ColorValue): ImageValue; + /** Generate image: checked */ export declare function GenImageChecked(width: number, height: number, checksX: number, checksY: number, col1: ColorValue, col2: ColorValue): ImageValue; +/** Generate image: white noise */ +export declare function GenImageWhiteNoise(width: number, height: number, factor: number): ImageValue; + +/** Generate image: perlin noise */ +export declare function GenImagePerlinNoise(width: number, height: number, offsetX: number, offsetY: number, scale: number): ImageValue; + /** Generate image: cellular algorithm, bigger tileSize means bigger cells */ export declare function GenImageCellular(width: number, height: number, tileSize: number): ImageValue; @@ -1949,6 +2116,9 @@ export declare function ImageFromChannel(image: ImageValue, selectedChannel: num /** Create an image from text (default font) */ export declare function ImageText(text: string, fontSize: number, color: ColorValue): ImageValue; +/** Create an image from text (custom sprite font) */ +export declare function ImageTextEx(font: FontValue, text: string, fontSize: number, spacing: number, tint: ColorValue): ImageValue; + /** Convert image data to desired format */ export declare function ImageFormat(image: FFIPointerInput, newFormat: number): void; @@ -1958,6 +2128,12 @@ export declare function ImageToPOT(image: FFIPointerInput, fill: ColorValue): vo /** Crop an image to a defined rectangle */ export declare function ImageCrop(image: FFIPointerInput, crop: RectangleValue): void; +/** Crop image depending on alpha value */ +export declare function ImageAlphaCrop(image: FFIPointerInput, threshold: number): void; + +/** Clear alpha channel to desired color */ +export declare function ImageAlphaClear(image: FFIPointerInput, color: ColorValue, threshold: number): void; + /** Apply alpha mask to image */ export declare function ImageAlphaMask(image: FFIPointerInput, alphaMask: ImageValue): void; @@ -2009,6 +2185,9 @@ export declare function ImageColorInvert(image: FFIPointerInput): void; /** Modify image color: grayscale */ export declare function ImageColorGrayscale(image: FFIPointerInput): void; +/** Modify image color: contrast (-100 to 100) */ +export declare function ImageColorContrast(image: FFIPointerInput, contrast: number): void; + /** Modify image color: brightness (-255 to 255) */ export declare function ImageColorBrightness(image: FFIPointerInput, brightness: number): void; @@ -2027,6 +2206,9 @@ export declare function UnloadImageColors(colors: FFIPointerInput): void; /** Unload colors palette loaded with LoadImagePalette() */ export declare function UnloadImagePalette(colors: FFIPointerInput): void; +/** Get image alpha border rectangle */ +export declare function GetImageAlphaBorder(image: ImageValue, threshold: number): RectangleValue; + /** Get image pixel color at (x, y) position */ export declare function GetImageColor(image: ImageValue, x: number, y: number): ColorValue; @@ -2093,6 +2275,9 @@ export declare function ImageDraw(dst: FFIPointerInput, src: ImageValue, srcRec: /** Draw text (using default font) within an image (destination) */ export declare function ImageDrawText(dst: FFIPointerInput, text: string, posX: number, posY: number, fontSize: number, color: ColorValue): void; +/** Draw text (custom sprite font) within an image (destination) */ +export declare function ImageDrawTextEx(dst: FFIPointerInput, font: FontValue, text: string, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + /** Load texture from file into GPU memory (VRAM) */ export declare function LoadTexture(fileName: string): Texture2DValue; @@ -2138,12 +2323,24 @@ export declare function DrawTexture(texture: Texture2DValue, posX: number, posY: /** Draw a Texture2D with position defined as Vector2 */ export declare function DrawTextureV(texture: Texture2DValue, position: Vector2Value, tint: ColorValue): void; +/** Draw a Texture2D with extended parameters */ +export declare function DrawTextureEx(texture: Texture2DValue, position: Vector2Value, rotation: number, scale: number, tint: ColorValue): void; + /** Draw a part of a texture defined by a rectangle */ export declare function DrawTextureRec(texture: Texture2DValue, source: RectangleValue, position: Vector2Value, tint: ColorValue): void; +/** Draw a part of a texture defined by a rectangle with 'pro' parameters */ +export declare function DrawTexturePro(texture: Texture2DValue, source: RectangleValue, dest: RectangleValue, origin: Vector2Value, rotation: number, tint: ColorValue): void; + +/** Draws a texture (or part of it) that stretches or shrinks nicely */ +export declare function DrawTextureNPatch(texture: Texture2DValue, nPatchInfo: NPatchInfoValue, dest: RectangleValue, origin: Vector2Value, rotation: number, tint: ColorValue): void; + /** Check if two colors are equal */ export declare function ColorIsEqual(col1: ColorValue, col2: ColorValue): boolean; +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export declare function Fade(color: ColorValue, alpha: number): ColorValue; + /** Get hexadecimal value for a Color (0xRRGGBBAA) */ export declare function ColorToInt(color: ColorValue): number; @@ -2162,9 +2359,21 @@ export declare function ColorFromHSV(hue: number, saturation: number, value: num /** Get color multiplied with another color */ export declare function ColorTint(color: ColorValue, tint: ColorValue): ColorValue; +/** Get color with brightness correction, brightness factor goes from -1.0f to 1.0f */ +export declare function ColorBrightness(color: ColorValue, factor: number): ColorValue; + +/** Get color with contrast correction, contrast values between -1.0f and 1.0f */ +export declare function ColorContrast(color: ColorValue, contrast: number): ColorValue; + +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export declare function ColorAlpha(color: ColorValue, alpha: number): ColorValue; + /** Get src alpha-blended into dst color with tint */ export declare function ColorAlphaBlend(dst: ColorValue, src: ColorValue, tint: ColorValue): ColorValue; +/** Get color lerp interpolation between two colors, factor [0.0f..1.0f] */ +export declare function ColorLerp(color1: ColorValue, color2: ColorValue, factor: number): ColorValue; + /** Get Color structure from hexadecimal value */ export declare function GetColor(hexValue: number): ColorValue; @@ -2216,12 +2425,30 @@ export declare function DrawFPS(posX: number, posY: number): void; /** Draw text (using default font) */ export declare function DrawText(text: string, posX: number, posY: number, fontSize: number, color: ColorValue): void; +/** Draw text using font and additional parameters */ +export declare function DrawTextEx(font: FontValue, text: string, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + +/** Draw text using Font and pro parameters (rotation) */ +export declare function DrawTextPro(font: FontValue, text: string, position: Vector2Value, origin: Vector2Value, rotation: number, fontSize: number, spacing: number, tint: ColorValue): void; + +/** Draw one character (codepoint) */ +export declare function DrawTextCodepoint(font: FontValue, codepoint: number, position: Vector2Value, fontSize: number, tint: ColorValue): void; + +/** Draw multiple character (codepoint) */ +export declare function DrawTextCodepoints(font: FontValue, codepoints: FFIPointerInput, codepointCount: number, position: Vector2Value, fontSize: number, spacing: number, tint: ColorValue): void; + /** Set vertical line spacing when drawing with line-breaks */ export declare function SetTextLineSpacing(spacing: number): void; /** Measure string width for default font */ export declare function MeasureText(text: string, fontSize: number): number; +/** Measure string size for Font */ +export declare function MeasureTextEx(font: FontValue, text: string, fontSize: number, spacing: number): Vector2Value; + +/** Measure string size for an existing array of codepoints for Font */ +export declare function MeasureTextCodepoints(font: FontValue, codepoints: FFIPointerInput, length: number, fontSize: number, spacing: number): Vector2Value; + /** Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found */ export declare function GetGlyphIndex(font: FontValue, codepoint: number): number; @@ -2273,6 +2500,9 @@ export declare function TextIsEqual(text1: string, text2: string): boolean; /** Get text length, checks for '\0' ending */ export declare function TextLength(text: string): number; +/** Text formatting with variables (sprintf() style) */ +export declare function TextFormat(text: string, args: FFIVarargs): string; + /** Get a piece of a text string */ export declare function TextSubtext(text: string, position: number, length: number): string; @@ -2283,28 +2513,16 @@ export declare function TextRemoveSpaces(text: string): string; export declare function GetTextBetween(text: string, begin: string, end: string): string; /** Replace text string with new string */ -export declare function TextReplace(text: string, search: string, replacement: string): string; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceRaw(text: string, search: string, replacement: FFIPointerInput): string; +export declare function TextReplace(text: string, search: string, replacement: string | null): string; /** Replace text string with new string, memory must be MemFree() */ -export declare function TextReplaceAlloc(text: string, search: string, replacement: string): FFIPointer; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceAllocRaw(text: string, search: string, replacement: FFIPointerInput): FFIPointer; +export declare function TextReplaceAlloc(text: string, search: string, replacement: string | null): FFIPointer; /** Replace text between two specific strings */ -export declare function TextReplaceBetween(text: string, begin: string, end: string, replacement: string): string; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceBetweenRaw(text: string, begin: string, end: string, replacement: FFIPointerInput): string; +export declare function TextReplaceBetween(text: string, begin: string, end: string, replacement: string | null): string; /** Replace text between two specific strings, memory must be MemFree() */ -export declare function TextReplaceBetweenAlloc(text: string, begin: string, end: string, replacement: string): FFIPointer; - -/** Raw-pointer variant for nullable C string parameters. */ -export declare function TextReplaceBetweenAllocRaw(text: string, begin: string, end: string, replacement: FFIPointerInput): FFIPointer; +export declare function TextReplaceBetweenAlloc(text: string, begin: string, end: string, replacement: string | null): FFIPointer; /** Insert text in a defined byte position */ export declare function TextInsert(text: string, insert: string, position: number): string; @@ -2351,24 +2569,63 @@ export declare function DrawLine3D(startPos: Vector3Value, endPos: Vector3Value, /** Draw a point in 3D space, actually a small line */ export declare function DrawPoint3D(position: Vector3Value, color: ColorValue): void; +/** Draw a circle in 3D world space */ +export declare function DrawCircle3D(center: Vector3Value, radius: number, rotationAxis: Vector3Value, rotationAngle: number, color: ColorValue): void; + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export declare function DrawTriangle3D(v1: Vector3Value, v2: Vector3Value, v3: Vector3Value, color: ColorValue): void; /** Draw a triangle strip defined by points */ export declare function DrawTriangleStrip3D(points: FFIPointerInput, pointCount: number, color: ColorValue): void; +/** Draw cube */ +export declare function DrawCube(position: Vector3Value, width: number, height: number, length: number, color: ColorValue): void; + /** Draw cube (Vector version) */ export declare function DrawCubeV(position: Vector3Value, size: Vector3Value, color: ColorValue): void; +/** Draw cube wires */ +export declare function DrawCubeWires(position: Vector3Value, width: number, height: number, length: number, color: ColorValue): void; + /** Draw cube wires (Vector version) */ export declare function DrawCubeWiresV(position: Vector3Value, size: Vector3Value, color: ColorValue): void; +/** Draw sphere */ +export declare function DrawSphere(centerPos: Vector3Value, radius: number, color: ColorValue): void; + +/** Draw sphere with extended parameters */ +export declare function DrawSphereEx(centerPos: Vector3Value, radius: number, rings: number, slices: number, color: ColorValue): void; + +/** Draw sphere wires */ +export declare function DrawSphereWires(centerPos: Vector3Value, radius: number, rings: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder/cone */ +export declare function DrawCylinder(position: Vector3Value, radiusTop: number, radiusBottom: number, height: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder with base at startPos and top at endPos */ +export declare function DrawCylinderEx(startPos: Vector3Value, endPos: Vector3Value, startRadius: number, endRadius: number, sides: number, color: ColorValue): void; + +/** Draw a cylinder/cone wires */ +export declare function DrawCylinderWires(position: Vector3Value, radiusTop: number, radiusBottom: number, height: number, slices: number, color: ColorValue): void; + +/** Draw a cylinder wires with base at startPos and top at endPos */ +export declare function DrawCylinderWiresEx(startPos: Vector3Value, endPos: Vector3Value, startRadius: number, endRadius: number, sides: number, color: ColorValue): void; + +/** Draw a capsule with the center of its sphere caps at startPos and endPos */ +export declare function DrawCapsule(startPos: Vector3Value, endPos: Vector3Value, radius: number, slices: number, rings: number, color: ColorValue): void; + +/** Draw capsule wireframe with the center of its sphere caps at startPos and endPos */ +export declare function DrawCapsuleWires(startPos: Vector3Value, endPos: Vector3Value, radius: number, slices: number, rings: number, color: ColorValue): void; + /** Draw a plane XZ */ export declare function DrawPlane(centerPos: Vector3Value, size: Vector2Value, color: ColorValue): void; /** Draw a ray line */ export declare function DrawRay(ray: RayValue, color: ColorValue): void; +/** Draw a grid (centered at (0, 0, 0)) */ +export declare function DrawGrid(slices: number, spacing: number): void; + /** Load model from files (meshes and materials) */ export declare function LoadModel(fileName: string): ModelValue; @@ -2384,12 +2641,30 @@ export declare function UnloadModel(model: ModelValue): void; /** Compute model bounding box limits (considers all meshes) */ export declare function GetModelBoundingBox(model: ModelValue): BoundingBoxValue; +/** Draw a model (with texture if set) */ +export declare function DrawModel(model: ModelValue, position: Vector3Value, scale: number, tint: ColorValue): void; + +/** Draw a model with extended parameters */ +export declare function DrawModelEx(model: ModelValue, position: Vector3Value, rotationAxis: Vector3Value, rotationAngle: number, scale: Vector3Value, tint: ColorValue): void; + +/** Draw a model wires (with texture if set) */ +export declare function DrawModelWires(model: ModelValue, position: Vector3Value, scale: number, tint: ColorValue): void; + +/** Draw a model wires (with texture if set) with extended parameters */ +export declare function DrawModelWiresEx(model: ModelValue, position: Vector3Value, rotationAxis: Vector3Value, rotationAngle: number, scale: Vector3Value, tint: ColorValue): void; + /** Draw bounding box (wires) */ export declare function DrawBoundingBox(box: BoundingBoxValue, color: ColorValue): void; +/** Draw a billboard texture */ +export declare function DrawBillboard(camera: CameraValue, texture: Texture2DValue, position: Vector3Value, scale: number, tint: ColorValue): void; + /** Draw a billboard texture defined by source */ export declare function DrawBillboardRec(camera: CameraValue, texture: Texture2DValue, source: RectangleValue, position: Vector3Value, size: Vector2Value, tint: ColorValue): void; +/** Draw a billboard texture defined by source and rotation */ +export declare function DrawBillboardPro(camera: CameraValue, texture: Texture2DValue, source: RectangleValue, position: Vector3Value, up: Vector3Value, size: Vector2Value, origin: Vector2Value, rotation: number, tint: ColorValue): void; + /** Upload mesh vertex data in GPU and provide VAO/VBO ids */ export declare function UploadMesh(mesh: FFIPointerInput, dynamic: boolean): void; @@ -2417,9 +2692,33 @@ export declare function ExportMesh(mesh: MeshValue, fileName: string): boolean; /** Export mesh as code file (.h) defining multiple arrays of vertex attributes */ export declare function ExportMeshAsCode(mesh: MeshValue, fileName: string): boolean; +/** Generate polygonal mesh */ +export declare function GenMeshPoly(sides: number, radius: number): MeshValue; + +/** Generate plane mesh (with subdivisions) */ +export declare function GenMeshPlane(width: number, length: number, resX: number, resZ: number): MeshValue; + /** Generate cuboid mesh */ export declare function GenMeshCube(width: number, height: number, length: number): MeshValue; +/** Generate sphere mesh (standard sphere) */ +export declare function GenMeshSphere(radius: number, rings: number, slices: number): MeshValue; + +/** Generate half-sphere mesh (no bottom cap) */ +export declare function GenMeshHemiSphere(radius: number, rings: number, slices: number): MeshValue; + +/** Generate cylinder mesh */ +export declare function GenMeshCylinder(radius: number, height: number, slices: number): MeshValue; + +/** Generate cone/pyramid mesh */ +export declare function GenMeshCone(radius: number, height: number, slices: number): MeshValue; + +/** Generate torus mesh */ +export declare function GenMeshTorus(radius: number, size: number, radSeg: number, sides: number): MeshValue; + +/** Generate trefoil knot mesh */ +export declare function GenMeshKnot(radius: number, size: number, radSeg: number, sides: number): MeshValue; + /** Generate heightmap mesh from image data */ export declare function GenMeshHeightmap(heightmap: ImageValue, size: Vector3Value): MeshValue; @@ -2447,15 +2746,30 @@ export declare function SetModelMeshMaterial(model: FFIPointerInput, meshId: num /** Load model animations from file */ export declare function LoadModelAnimations(fileName: string, animCount: FFIPointerInput): FFIPointer; +/** Update model animation pose (vertex buffers and bone matrices) */ +export declare function UpdateModelAnimation(model: ModelValue, anim: ModelAnimationValue, frame: number): void; + +/** Update model animation pose, blending two animations */ +export declare function UpdateModelAnimationEx(model: ModelValue, animA: ModelAnimationValue, frameA: number, animB: ModelAnimationValue, frameB: number, blend: number): void; + /** Unload animation array data */ export declare function UnloadModelAnimations(animations: FFIPointerInput, animCount: number): void; /** Check model animation skeleton match */ export declare function IsModelAnimationValid(model: ModelValue, anim: ModelAnimationValue): boolean; +/** Check collision between two spheres */ +export declare function CheckCollisionSpheres(center1: Vector3Value, radius1: number, center2: Vector3Value, radius2: number): boolean; + /** Check collision between two bounding boxes */ export declare function CheckCollisionBoxes(box1: BoundingBoxValue, box2: BoundingBoxValue): boolean; +/** Check collision between box and sphere */ +export declare function CheckCollisionBoxSphere(box: BoundingBoxValue, center: Vector3Value, radius: number): boolean; + +/** Get collision info between ray and sphere */ +export declare function GetRayCollisionSphere(ray: RayValue, center: Vector3Value, radius: number): RayCollisionValue; + /** Get collision info between ray and box */ export declare function GetRayCollisionBox(ray: RayValue, box: BoundingBoxValue): RayCollisionValue; @@ -2537,6 +2851,15 @@ export declare function ResumeSound(sound: SoundValue): void; /** Check if a sound is currently playing */ export declare function IsSoundPlaying(sound: SoundValue): boolean; +/** Set volume for a sound (1.0 is max level) */ +export declare function SetSoundVolume(sound: SoundValue, volume: number): void; + +/** Set pitch for a sound (1.0 is base level) */ +export declare function SetSoundPitch(sound: SoundValue, pitch: number): void; + +/** Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) */ +export declare function SetSoundPan(sound: SoundValue, pan: number): void; + /** Copy a wave to a new wave */ export declare function WaveCopy(wave: WaveValue): WaveValue; @@ -2582,6 +2905,18 @@ export declare function PauseMusicStream(music: MusicValue): void; /** Resume playing paused music */ export declare function ResumeMusicStream(music: MusicValue): void; +/** Seek music to a position (in seconds) */ +export declare function SeekMusicStream(music: MusicValue, position: number): void; + +/** Set volume for music (1.0 is max level) */ +export declare function SetMusicVolume(music: MusicValue, volume: number): void; + +/** Set pitch for a music (1.0 is base level) */ +export declare function SetMusicPitch(music: MusicValue, pitch: number): void; + +/** Set pan for a music (-1.0 left, 0.0 center, 1.0 right) */ +export declare function SetMusicPan(music: MusicValue, pan: number): void; + /** Get music time length (in seconds) */ export declare function GetMusicTimeLength(music: MusicValue): number; @@ -2618,6 +2953,15 @@ export declare function IsAudioStreamPlaying(stream: AudioStreamValue): boolean; /** Stop audio stream */ export declare function StopAudioStream(stream: AudioStreamValue): void; +/** Set volume for audio stream (1.0 is max level) */ +export declare function SetAudioStreamVolume(stream: AudioStreamValue, volume: number): void; + +/** Set pitch for audio stream (1.0 is base level) */ +export declare function SetAudioStreamPitch(stream: AudioStreamValue, pitch: number): void; + +/** Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) */ +export declare function SetAudioStreamPan(stream: AudioStreamValue, pan: number): void; + /** Default size for new audio streams */ export declare function SetAudioStreamBufferSizeDefault(size: number): void; diff --git a/bindings/raylib.ts b/bindings/raylib.ts index afb3c0d..c0a24db 100644 --- a/bindings/raylib.ts +++ b/bindings/raylib.ts @@ -4,12 +4,19 @@ export const RAYLIB_BINDING_INFO = { raylibVersion: "6.0", raylibCommit: "dbc56a87da87d973a9c5baa4e7438a9d20121d28", - gocciaScriptVersion: "0.10.0", + gocciaScriptVersion: "0.14.0", linkage: "dynamic", }; -export const RAYLIB_FIELD_ALIASES = { - AudioStream: { buffer: "nativeBuffer" }, -}; + +if (typeof FFI === "undefined") { + throw new TypeError( + "The raylib bindings need GocciaScript's ffi capability. Grant it with " + + '--allow-ffi, or with "permissions": { "allow-ffi": true } in a ' + + "trusted goccia.json.", + ); +} + +const nullableUtf8String = FFI.nullable("utf8string"); const raylibCandidates = (): string[] => { const suffix = FFI.suffix; @@ -303,7 +310,7 @@ export const Wave = FFI.struct({ /** AudioStream, custom audio stream */ export const AudioStream = FFI.struct({ - "nativeBuffer": "pointer", + "buffer": "pointer", "processor": "pointer", "sampleRate": "u32", "sampleSize": "u32", @@ -1319,16 +1326,10 @@ export const LoadVrStereoConfig = raylibLibrary.bind("LoadVrStereoConfig", { arg export const UnloadVrStereoConfig = raylibLibrary.bind("UnloadVrStereoConfig", { args: [VrStereoConfig], returns: "void" }); /** Load shader from files and bind default locations */ -export const LoadShader = raylibLibrary.bind("LoadShader", { args: ["utf8string", "utf8string"], returns: Shader }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadShaderRaw = raylibLibrary.bind("LoadShader", { args: ["pointer", "pointer"], returns: Shader }); +export const LoadShader = raylibLibrary.bind("LoadShader", { args: [nullableUtf8String, nullableUtf8String], returns: Shader }); /** Load shader from code strings and bind default locations */ -export const LoadShaderFromMemory = raylibLibrary.bind("LoadShaderFromMemory", { args: ["utf8string", "utf8string"], returns: Shader }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadShaderFromMemoryRaw = raylibLibrary.bind("LoadShaderFromMemory", { args: ["pointer", "pointer"], returns: Shader }); +export const LoadShaderFromMemory = raylibLibrary.bind("LoadShaderFromMemory", { args: [nullableUtf8String, nullableUtf8String], returns: Shader }); /** Check if a shader is valid (loaded on GPU) */ export const IsShaderValid = raylibLibrary.bind("IsShaderValid", { args: [Shader], returns: "bool" }); @@ -1423,6 +1424,9 @@ export const OpenURL = raylibLibrary.bind("OpenURL", { args: ["utf8string"], ret /** Set the current threshold (minimum) log level */ export const SetTraceLogLevel = raylibLibrary.bind("SetTraceLogLevel", { args: ["i32"], returns: "void" }); +/** Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) */ +export const TraceLog = raylibLibrary.bind("TraceLog", { args: ["i32", "utf8string"], variadic: true, returns: "void" }); + /** Set custom trace log */ export const SetTraceLogCallback = raylibLibrary.bind("SetTraceLogCallback", { args: ["pointer"], returns: "void" }); @@ -1583,10 +1587,7 @@ export const ComputeSHA1 = raylibLibrary.bind("ComputeSHA1", { args: ["pointer", export const ComputeSHA256 = raylibLibrary.bind("ComputeSHA256", { args: ["pointer", "i32"], returns: "pointer" }); /** Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS */ -export const LoadAutomationEventList = raylibLibrary.bind("LoadAutomationEventList", { args: ["utf8string"], returns: AutomationEventList }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const LoadAutomationEventListRaw = raylibLibrary.bind("LoadAutomationEventList", { args: ["pointer"], returns: AutomationEventList }); +export const LoadAutomationEventList = raylibLibrary.bind("LoadAutomationEventList", { args: [nullableUtf8String], returns: AutomationEventList }); /** Unload automation events list from file */ export const UnloadAutomationEventList = raylibLibrary.bind("UnloadAutomationEventList", { args: [AutomationEventList], returns: "void" }); @@ -1666,6 +1667,9 @@ export const GetGamepadAxisMovement = raylibLibrary.bind("GetGamepadAxisMovement /** Set internal gamepad mappings (SDL_GameControllerDB) */ export const SetGamepadMappings = raylibLibrary.bind("SetGamepadMappings", { args: ["utf8string"], returns: "i32" }); +/** Set gamepad vibration for both motors (duration in seconds) */ +export const SetGamepadVibration = raylibLibrary.bind("SetGamepadVibration", { args: ["i32", "f32", "f32", "f32"], returns: "void" }); + /** Check if a mouse button has been pressed once */ export const IsMouseButtonPressed = raylibLibrary.bind("IsMouseButtonPressed", { args: ["i32"], returns: "bool" }); @@ -1750,6 +1754,9 @@ export const GetGesturePinchAngle = raylibLibrary.bind("GetGesturePinchAngle", { /** Update camera position for selected mode */ export const UpdateCamera = raylibLibrary.bind("UpdateCamera", { args: ["pointer", "i32"], returns: "void" }); +/** Update camera movement/rotation */ +export const UpdateCameraPro = raylibLibrary.bind("UpdateCameraPro", { args: ["pointer", Vector3, Vector3, "f32"], returns: "void" }); + /** Set texture and rectangle to be used on shapes drawing */ export const SetShapesTexture = raylibLibrary.bind("SetShapesTexture", { args: [Texture, Rectangle], returns: "void" }); @@ -1771,12 +1778,57 @@ export const DrawLine = raylibLibrary.bind("DrawLine", { args: ["i32", "i32", "i /** Draw a line (using gl lines) */ export const DrawLineV = raylibLibrary.bind("DrawLineV", { args: [Vector2, Vector2, Color], returns: "void" }); +/** Draw a line (using triangles/quads) */ +export const DrawLineEx = raylibLibrary.bind("DrawLineEx", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + /** Draw lines sequence (using gl lines) */ export const DrawLineStrip = raylibLibrary.bind("DrawLineStrip", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw line segment cubic-bezier in-out interpolation */ +export const DrawLineBezier = raylibLibrary.bind("DrawLineBezier", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + /** Draw a dashed line */ export const DrawLineDashed = raylibLibrary.bind("DrawLineDashed", { args: [Vector2, Vector2, "i32", "i32", Color], returns: "void" }); +/** Draw a color-filled circle */ +export const DrawCircle = raylibLibrary.bind("DrawCircle", { args: ["i32", "i32", "f32", Color], returns: "void" }); + +/** Draw a color-filled circle (Vector version) */ +export const DrawCircleV = raylibLibrary.bind("DrawCircleV", { args: [Vector2, "f32", Color], returns: "void" }); + +/** Draw a gradient-filled circle */ +export const DrawCircleGradient = raylibLibrary.bind("DrawCircleGradient", { args: [Vector2, "f32", Color, Color], returns: "void" }); + +/** Draw a piece of a circle */ +export const DrawCircleSector = raylibLibrary.bind("DrawCircleSector", { args: [Vector2, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw circle sector outline */ +export const DrawCircleSectorLines = raylibLibrary.bind("DrawCircleSectorLines", { args: [Vector2, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw circle outline */ +export const DrawCircleLines = raylibLibrary.bind("DrawCircleLines", { args: ["i32", "i32", "f32", Color], returns: "void" }); + +/** Draw circle outline (Vector version) */ +export const DrawCircleLinesV = raylibLibrary.bind("DrawCircleLinesV", { args: [Vector2, "f32", Color], returns: "void" }); + +/** Draw ellipse */ +export const DrawEllipse = raylibLibrary.bind("DrawEllipse", { args: ["i32", "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse (Vector version) */ +export const DrawEllipseV = raylibLibrary.bind("DrawEllipseV", { args: [Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse outline */ +export const DrawEllipseLines = raylibLibrary.bind("DrawEllipseLines", { args: ["i32", "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw ellipse outline (Vector version) */ +export const DrawEllipseLinesV = raylibLibrary.bind("DrawEllipseLinesV", { args: [Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw ring */ +export const DrawRing = raylibLibrary.bind("DrawRing", { args: [Vector2, "f32", "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw ring outline */ +export const DrawRingLines = raylibLibrary.bind("DrawRingLines", { args: [Vector2, "f32", "f32", "f32", "f32", "i32", Color], returns: "void" }); + /** Draw a color-filled rectangle */ export const DrawRectangle = raylibLibrary.bind("DrawRectangle", { args: ["i32", "i32", "i32", "i32", Color], returns: "void" }); @@ -1786,6 +1838,9 @@ export const DrawRectangleV = raylibLibrary.bind("DrawRectangleV", { args: [Vect /** Draw a color-filled rectangle */ export const DrawRectangleRec = raylibLibrary.bind("DrawRectangleRec", { args: [Rectangle, Color], returns: "void" }); +/** Draw a color-filled rectangle with pro parameters */ +export const DrawRectanglePro = raylibLibrary.bind("DrawRectanglePro", { args: [Rectangle, Vector2, "f32", Color], returns: "void" }); + /** Draw a vertical-gradient-filled rectangle */ export const DrawRectangleGradientV = raylibLibrary.bind("DrawRectangleGradientV", { args: ["i32", "i32", "i32", "i32", Color, Color], returns: "void" }); @@ -1798,6 +1853,18 @@ export const DrawRectangleGradientEx = raylibLibrary.bind("DrawRectangleGradient /** Draw rectangle outline */ export const DrawRectangleLines = raylibLibrary.bind("DrawRectangleLines", { args: ["i32", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw rectangle outline with extended parameters */ +export const DrawRectangleLinesEx = raylibLibrary.bind("DrawRectangleLinesEx", { args: [Rectangle, "f32", Color], returns: "void" }); + +/** Draw rectangle with rounded edges */ +export const DrawRectangleRounded = raylibLibrary.bind("DrawRectangleRounded", { args: [Rectangle, "f32", "i32", Color], returns: "void" }); + +/** Draw rectangle lines with rounded edges */ +export const DrawRectangleRoundedLines = raylibLibrary.bind("DrawRectangleRoundedLines", { args: [Rectangle, "f32", "i32", Color], returns: "void" }); + +/** Draw rectangle with rounded edges outline */ +export const DrawRectangleRoundedLinesEx = raylibLibrary.bind("DrawRectangleRoundedLinesEx", { args: [Rectangle, "f32", "i32", "f32", Color], returns: "void" }); + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export const DrawTriangle = raylibLibrary.bind("DrawTriangle", { args: [Vector2, Vector2, Vector2, Color], returns: "void" }); @@ -1810,12 +1877,78 @@ export const DrawTriangleFan = raylibLibrary.bind("DrawTriangleFan", { args: ["p /** Draw a triangle strip defined by points */ export const DrawTriangleStrip = raylibLibrary.bind("DrawTriangleStrip", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw a regular polygon (Vector version) */ +export const DrawPoly = raylibLibrary.bind("DrawPoly", { args: [Vector2, "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw a polygon outline of n sides */ +export const DrawPolyLines = raylibLibrary.bind("DrawPolyLines", { args: [Vector2, "i32", "f32", "f32", Color], returns: "void" }); + +/** Draw a polygon outline of n sides with extended parameters */ +export const DrawPolyLinesEx = raylibLibrary.bind("DrawPolyLinesEx", { args: [Vector2, "i32", "f32", "f32", "f32", Color], returns: "void" }); + +/** Draw spline: Linear, minimum 2 points */ +export const DrawSplineLinear = raylibLibrary.bind("DrawSplineLinear", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: B-Spline, minimum 4 points */ +export const DrawSplineBasis = raylibLibrary.bind("DrawSplineBasis", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Catmull-Rom, minimum 4 points */ +export const DrawSplineCatmullRom = raylibLibrary.bind("DrawSplineCatmullRom", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...] */ +export const DrawSplineBezierQuadratic = raylibLibrary.bind("DrawSplineBezierQuadratic", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...] */ +export const DrawSplineBezierCubic = raylibLibrary.bind("DrawSplineBezierCubic", { args: ["pointer", "i32", "f32", Color], returns: "void" }); + +/** Draw spline segment: Linear, 2 points */ +export const DrawSplineSegmentLinear = raylibLibrary.bind("DrawSplineSegmentLinear", { args: [Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: B-Spline, 4 points */ +export const DrawSplineSegmentBasis = raylibLibrary.bind("DrawSplineSegmentBasis", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Catmull-Rom, 4 points */ +export const DrawSplineSegmentCatmullRom = raylibLibrary.bind("DrawSplineSegmentCatmullRom", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Quadratic Bezier, 2 points, 1 control point */ +export const DrawSplineSegmentBezierQuadratic = raylibLibrary.bind("DrawSplineSegmentBezierQuadratic", { args: [Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Draw spline segment: Cubic Bezier, 2 points, 2 control points */ +export const DrawSplineSegmentBezierCubic = raylibLibrary.bind("DrawSplineSegmentBezierCubic", { args: [Vector2, Vector2, Vector2, Vector2, "f32", Color], returns: "void" }); + +/** Get (evaluate) spline point: Linear */ +export const GetSplinePointLinear = raylibLibrary.bind("GetSplinePointLinear", { args: [Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: B-Spline */ +export const GetSplinePointBasis = raylibLibrary.bind("GetSplinePointBasis", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Catmull-Rom */ +export const GetSplinePointCatmullRom = raylibLibrary.bind("GetSplinePointCatmullRom", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Quadratic Bezier */ +export const GetSplinePointBezierQuad = raylibLibrary.bind("GetSplinePointBezierQuad", { args: [Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + +/** Get (evaluate) spline point: Cubic Bezier */ +export const GetSplinePointBezierCubic = raylibLibrary.bind("GetSplinePointBezierCubic", { args: [Vector2, Vector2, Vector2, Vector2, "f32"], returns: Vector2 }); + /** Check collision between two rectangles */ export const CheckCollisionRecs = raylibLibrary.bind("CheckCollisionRecs", { args: [Rectangle, Rectangle], returns: "bool" }); +/** Check collision between two circles */ +export const CheckCollisionCircles = raylibLibrary.bind("CheckCollisionCircles", { args: [Vector2, "f32", Vector2, "f32"], returns: "bool" }); + +/** Check collision between circle and rectangle */ +export const CheckCollisionCircleRec = raylibLibrary.bind("CheckCollisionCircleRec", { args: [Vector2, "f32", Rectangle], returns: "bool" }); + +/** Check if circle collides with a line created betweeen two points [p1] and [p2] */ +export const CheckCollisionCircleLine = raylibLibrary.bind("CheckCollisionCircleLine", { args: [Vector2, "f32", Vector2, Vector2], returns: "bool" }); + /** Check if point is inside rectangle */ export const CheckCollisionPointRec = raylibLibrary.bind("CheckCollisionPointRec", { args: [Vector2, Rectangle], returns: "bool" }); +/** Check if point is inside circle */ +export const CheckCollisionPointCircle = raylibLibrary.bind("CheckCollisionPointCircle", { args: [Vector2, Vector2, "f32"], returns: "bool" }); + /** Check if point is inside a triangle */ export const CheckCollisionPointTriangle = raylibLibrary.bind("CheckCollisionPointTriangle", { args: [Vector2, Vector2, Vector2, Vector2], returns: "bool" }); @@ -1873,9 +2006,21 @@ export const GenImageColor = raylibLibrary.bind("GenImageColor", { args: ["i32", /** Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient */ export const GenImageGradientLinear = raylibLibrary.bind("GenImageGradientLinear", { args: ["i32", "i32", "i32", Color, Color], returns: Image }); +/** Generate image: radial gradient */ +export const GenImageGradientRadial = raylibLibrary.bind("GenImageGradientRadial", { args: ["i32", "i32", "f32", Color, Color], returns: Image }); + +/** Generate image: square gradient */ +export const GenImageGradientSquare = raylibLibrary.bind("GenImageGradientSquare", { args: ["i32", "i32", "f32", Color, Color], returns: Image }); + /** Generate image: checked */ export const GenImageChecked = raylibLibrary.bind("GenImageChecked", { args: ["i32", "i32", "i32", "i32", Color, Color], returns: Image }); +/** Generate image: white noise */ +export const GenImageWhiteNoise = raylibLibrary.bind("GenImageWhiteNoise", { args: ["i32", "i32", "f32"], returns: Image }); + +/** Generate image: perlin noise */ +export const GenImagePerlinNoise = raylibLibrary.bind("GenImagePerlinNoise", { args: ["i32", "i32", "i32", "i32", "f32"], returns: Image }); + /** Generate image: cellular algorithm, bigger tileSize means bigger cells */ export const GenImageCellular = raylibLibrary.bind("GenImageCellular", { args: ["i32", "i32", "i32"], returns: Image }); @@ -1894,6 +2039,9 @@ export const ImageFromChannel = raylibLibrary.bind("ImageFromChannel", { args: [ /** Create an image from text (default font) */ export const ImageText = raylibLibrary.bind("ImageText", { args: ["utf8string", "i32", Color], returns: Image }); +/** Create an image from text (custom sprite font) */ +export const ImageTextEx = raylibLibrary.bind("ImageTextEx", { args: [Font, "utf8string", "f32", "f32", Color], returns: Image }); + /** Convert image data to desired format */ export const ImageFormat = raylibLibrary.bind("ImageFormat", { args: ["pointer", "i32"], returns: "void" }); @@ -1903,6 +2051,12 @@ export const ImageToPOT = raylibLibrary.bind("ImageToPOT", { args: ["pointer", C /** Crop an image to a defined rectangle */ export const ImageCrop = raylibLibrary.bind("ImageCrop", { args: ["pointer", Rectangle], returns: "void" }); +/** Crop image depending on alpha value */ +export const ImageAlphaCrop = raylibLibrary.bind("ImageAlphaCrop", { args: ["pointer", "f32"], returns: "void" }); + +/** Clear alpha channel to desired color */ +export const ImageAlphaClear = raylibLibrary.bind("ImageAlphaClear", { args: ["pointer", Color, "f32"], returns: "void" }); + /** Apply alpha mask to image */ export const ImageAlphaMask = raylibLibrary.bind("ImageAlphaMask", { args: ["pointer", Image], returns: "void" }); @@ -1954,6 +2108,9 @@ export const ImageColorInvert = raylibLibrary.bind("ImageColorInvert", { args: [ /** Modify image color: grayscale */ export const ImageColorGrayscale = raylibLibrary.bind("ImageColorGrayscale", { args: ["pointer"], returns: "void" }); +/** Modify image color: contrast (-100 to 100) */ +export const ImageColorContrast = raylibLibrary.bind("ImageColorContrast", { args: ["pointer", "f32"], returns: "void" }); + /** Modify image color: brightness (-255 to 255) */ export const ImageColorBrightness = raylibLibrary.bind("ImageColorBrightness", { args: ["pointer", "i32"], returns: "void" }); @@ -1972,6 +2129,9 @@ export const UnloadImageColors = raylibLibrary.bind("UnloadImageColors", { args: /** Unload colors palette loaded with LoadImagePalette() */ export const UnloadImagePalette = raylibLibrary.bind("UnloadImagePalette", { args: ["pointer"], returns: "void" }); +/** Get image alpha border rectangle */ +export const GetImageAlphaBorder = raylibLibrary.bind("GetImageAlphaBorder", { args: [Image, "f32"], returns: Rectangle }); + /** Get image pixel color at (x, y) position */ export const GetImageColor = raylibLibrary.bind("GetImageColor", { args: [Image, "i32", "i32"], returns: Color }); @@ -2038,6 +2198,9 @@ export const ImageDraw = raylibLibrary.bind("ImageDraw", { args: ["pointer", Ima /** Draw text (using default font) within an image (destination) */ export const ImageDrawText = raylibLibrary.bind("ImageDrawText", { args: ["pointer", "utf8string", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw text (custom sprite font) within an image (destination) */ +export const ImageDrawTextEx = raylibLibrary.bind("ImageDrawTextEx", { args: ["pointer", Font, "utf8string", Vector2, "f32", "f32", Color], returns: "void" }); + /** Load texture from file into GPU memory (VRAM) */ export const LoadTexture = raylibLibrary.bind("LoadTexture", { args: ["utf8string"], returns: Texture }); @@ -2083,12 +2246,24 @@ export const DrawTexture = raylibLibrary.bind("DrawTexture", { args: [Texture, " /** Draw a Texture2D with position defined as Vector2 */ export const DrawTextureV = raylibLibrary.bind("DrawTextureV", { args: [Texture, Vector2, Color], returns: "void" }); +/** Draw a Texture2D with extended parameters */ +export const DrawTextureEx = raylibLibrary.bind("DrawTextureEx", { args: [Texture, Vector2, "f32", "f32", Color], returns: "void" }); + /** Draw a part of a texture defined by a rectangle */ export const DrawTextureRec = raylibLibrary.bind("DrawTextureRec", { args: [Texture, Rectangle, Vector2, Color], returns: "void" }); +/** Draw a part of a texture defined by a rectangle with 'pro' parameters */ +export const DrawTexturePro = raylibLibrary.bind("DrawTexturePro", { args: [Texture, Rectangle, Rectangle, Vector2, "f32", Color], returns: "void" }); + +/** Draws a texture (or part of it) that stretches or shrinks nicely */ +export const DrawTextureNPatch = raylibLibrary.bind("DrawTextureNPatch", { args: [Texture, NPatchInfo, Rectangle, Vector2, "f32", Color], returns: "void" }); + /** Check if two colors are equal */ export const ColorIsEqual = raylibLibrary.bind("ColorIsEqual", { args: [Color, Color], returns: "bool" }); +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export const Fade = raylibLibrary.bind("Fade", { args: [Color, "f32"], returns: Color }); + /** Get hexadecimal value for a Color (0xRRGGBBAA) */ export const ColorToInt = raylibLibrary.bind("ColorToInt", { args: [Color], returns: "i32" }); @@ -2107,9 +2282,21 @@ export const ColorFromHSV = raylibLibrary.bind("ColorFromHSV", { args: ["f32", " /** Get color multiplied with another color */ export const ColorTint = raylibLibrary.bind("ColorTint", { args: [Color, Color], returns: Color }); +/** Get color with brightness correction, brightness factor goes from -1.0f to 1.0f */ +export const ColorBrightness = raylibLibrary.bind("ColorBrightness", { args: [Color, "f32"], returns: Color }); + +/** Get color with contrast correction, contrast values between -1.0f and 1.0f */ +export const ColorContrast = raylibLibrary.bind("ColorContrast", { args: [Color, "f32"], returns: Color }); + +/** Get color with alpha applied, alpha goes from 0.0f to 1.0f */ +export const ColorAlpha = raylibLibrary.bind("ColorAlpha", { args: [Color, "f32"], returns: Color }); + /** Get src alpha-blended into dst color with tint */ export const ColorAlphaBlend = raylibLibrary.bind("ColorAlphaBlend", { args: [Color, Color, Color], returns: Color }); +/** Get color lerp interpolation between two colors, factor [0.0f..1.0f] */ +export const ColorLerp = raylibLibrary.bind("ColorLerp", { args: [Color, Color, "f32"], returns: Color }); + /** Get Color structure from hexadecimal value */ export const GetColor = raylibLibrary.bind("GetColor", { args: ["u32"], returns: Color }); @@ -2161,12 +2348,30 @@ export const DrawFPS = raylibLibrary.bind("DrawFPS", { args: ["i32", "i32"], ret /** Draw text (using default font) */ export const DrawText = raylibLibrary.bind("DrawText", { args: ["utf8string", "i32", "i32", "i32", Color], returns: "void" }); +/** Draw text using font and additional parameters */ +export const DrawTextEx = raylibLibrary.bind("DrawTextEx", { args: [Font, "utf8string", Vector2, "f32", "f32", Color], returns: "void" }); + +/** Draw text using Font and pro parameters (rotation) */ +export const DrawTextPro = raylibLibrary.bind("DrawTextPro", { args: [Font, "utf8string", Vector2, Vector2, "f32", "f32", "f32", Color], returns: "void" }); + +/** Draw one character (codepoint) */ +export const DrawTextCodepoint = raylibLibrary.bind("DrawTextCodepoint", { args: [Font, "i32", Vector2, "f32", Color], returns: "void" }); + +/** Draw multiple character (codepoint) */ +export const DrawTextCodepoints = raylibLibrary.bind("DrawTextCodepoints", { args: [Font, "pointer", "i32", Vector2, "f32", "f32", Color], returns: "void" }); + /** Set vertical line spacing when drawing with line-breaks */ export const SetTextLineSpacing = raylibLibrary.bind("SetTextLineSpacing", { args: ["i32"], returns: "void" }); /** Measure string width for default font */ export const MeasureText = raylibLibrary.bind("MeasureText", { args: ["utf8string", "i32"], returns: "i32" }); +/** Measure string size for Font */ +export const MeasureTextEx = raylibLibrary.bind("MeasureTextEx", { args: [Font, "utf8string", "f32", "f32"], returns: Vector2 }); + +/** Measure string size for an existing array of codepoints for Font */ +export const MeasureTextCodepoints = raylibLibrary.bind("MeasureTextCodepoints", { args: [Font, "pointer", "i32", "f32", "f32"], returns: Vector2 }); + /** Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found */ export const GetGlyphIndex = raylibLibrary.bind("GetGlyphIndex", { args: [Font, "i32"], returns: "i32" }); @@ -2218,6 +2423,9 @@ export const TextIsEqual = raylibLibrary.bind("TextIsEqual", { args: ["utf8strin /** Get text length, checks for '\0' ending */ export const TextLength = raylibLibrary.bind("TextLength", { args: ["utf8string"], returns: "u32" }); +/** Text formatting with variables (sprintf() style) */ +export const TextFormat = raylibLibrary.bind("TextFormat", { args: ["utf8string"], variadic: true, returns: "utf8string" }); + /** Get a piece of a text string */ export const TextSubtext = raylibLibrary.bind("TextSubtext", { args: ["utf8string", "i32", "i32"], returns: "utf8string" }); @@ -2228,28 +2436,16 @@ export const TextRemoveSpaces = raylibLibrary.bind("TextRemoveSpaces", { args: [ export const GetTextBetween = raylibLibrary.bind("GetTextBetween", { args: ["utf8string", "utf8string", "utf8string"], returns: "utf8string" }); /** Replace text string with new string */ -export const TextReplace = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", "utf8string"], returns: "utf8string" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceRaw = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", "pointer"], returns: "utf8string" }); +export const TextReplace = raylibLibrary.bind("TextReplace", { args: ["utf8string", "utf8string", nullableUtf8String], returns: "utf8string" }); /** Replace text string with new string, memory must be MemFree() */ -export const TextReplaceAlloc = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", "utf8string"], returns: "pointer" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceAllocRaw = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", "pointer"], returns: "pointer" }); +export const TextReplaceAlloc = raylibLibrary.bind("TextReplaceAlloc", { args: ["utf8string", "utf8string", nullableUtf8String], returns: "pointer" }); /** Replace text between two specific strings */ -export const TextReplaceBetween = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", "utf8string"], returns: "utf8string" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceBetweenRaw = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", "pointer"], returns: "utf8string" }); +export const TextReplaceBetween = raylibLibrary.bind("TextReplaceBetween", { args: ["utf8string", "utf8string", "utf8string", nullableUtf8String], returns: "utf8string" }); /** Replace text between two specific strings, memory must be MemFree() */ -export const TextReplaceBetweenAlloc = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", "utf8string"], returns: "pointer" }); - -/** Raw-pointer variant for nullable C string parameters. */ -export const TextReplaceBetweenAllocRaw = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", "pointer"], returns: "pointer" }); +export const TextReplaceBetweenAlloc = raylibLibrary.bind("TextReplaceBetweenAlloc", { args: ["utf8string", "utf8string", "utf8string", nullableUtf8String], returns: "pointer" }); /** Insert text in a defined byte position */ export const TextInsert = raylibLibrary.bind("TextInsert", { args: ["utf8string", "utf8string", "i32"], returns: "utf8string" }); @@ -2296,24 +2492,63 @@ export const DrawLine3D = raylibLibrary.bind("DrawLine3D", { args: [Vector3, Vec /** Draw a point in 3D space, actually a small line */ export const DrawPoint3D = raylibLibrary.bind("DrawPoint3D", { args: [Vector3, Color], returns: "void" }); +/** Draw a circle in 3D world space */ +export const DrawCircle3D = raylibLibrary.bind("DrawCircle3D", { args: [Vector3, "f32", Vector3, "f32", Color], returns: "void" }); + /** Draw a color-filled triangle (vertex in counter-clockwise order!) */ export const DrawTriangle3D = raylibLibrary.bind("DrawTriangle3D", { args: [Vector3, Vector3, Vector3, Color], returns: "void" }); /** Draw a triangle strip defined by points */ export const DrawTriangleStrip3D = raylibLibrary.bind("DrawTriangleStrip3D", { args: ["pointer", "i32", Color], returns: "void" }); +/** Draw cube */ +export const DrawCube = raylibLibrary.bind("DrawCube", { args: [Vector3, "f32", "f32", "f32", Color], returns: "void" }); + /** Draw cube (Vector version) */ export const DrawCubeV = raylibLibrary.bind("DrawCubeV", { args: [Vector3, Vector3, Color], returns: "void" }); +/** Draw cube wires */ +export const DrawCubeWires = raylibLibrary.bind("DrawCubeWires", { args: [Vector3, "f32", "f32", "f32", Color], returns: "void" }); + /** Draw cube wires (Vector version) */ export const DrawCubeWiresV = raylibLibrary.bind("DrawCubeWiresV", { args: [Vector3, Vector3, Color], returns: "void" }); +/** Draw sphere */ +export const DrawSphere = raylibLibrary.bind("DrawSphere", { args: [Vector3, "f32", Color], returns: "void" }); + +/** Draw sphere with extended parameters */ +export const DrawSphereEx = raylibLibrary.bind("DrawSphereEx", { args: [Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw sphere wires */ +export const DrawSphereWires = raylibLibrary.bind("DrawSphereWires", { args: [Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw a cylinder/cone */ +export const DrawCylinder = raylibLibrary.bind("DrawCylinder", { args: [Vector3, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder with base at startPos and top at endPos */ +export const DrawCylinderEx = raylibLibrary.bind("DrawCylinderEx", { args: [Vector3, Vector3, "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder/cone wires */ +export const DrawCylinderWires = raylibLibrary.bind("DrawCylinderWires", { args: [Vector3, "f32", "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a cylinder wires with base at startPos and top at endPos */ +export const DrawCylinderWiresEx = raylibLibrary.bind("DrawCylinderWiresEx", { args: [Vector3, Vector3, "f32", "f32", "i32", Color], returns: "void" }); + +/** Draw a capsule with the center of its sphere caps at startPos and endPos */ +export const DrawCapsule = raylibLibrary.bind("DrawCapsule", { args: [Vector3, Vector3, "f32", "i32", "i32", Color], returns: "void" }); + +/** Draw capsule wireframe with the center of its sphere caps at startPos and endPos */ +export const DrawCapsuleWires = raylibLibrary.bind("DrawCapsuleWires", { args: [Vector3, Vector3, "f32", "i32", "i32", Color], returns: "void" }); + /** Draw a plane XZ */ export const DrawPlane = raylibLibrary.bind("DrawPlane", { args: [Vector3, Vector2, Color], returns: "void" }); /** Draw a ray line */ export const DrawRay = raylibLibrary.bind("DrawRay", { args: [Ray, Color], returns: "void" }); +/** Draw a grid (centered at (0, 0, 0)) */ +export const DrawGrid = raylibLibrary.bind("DrawGrid", { args: ["i32", "f32"], returns: "void" }); + /** Load model from files (meshes and materials) */ export const LoadModel = raylibLibrary.bind("LoadModel", { args: ["utf8string"], returns: Model }); @@ -2329,12 +2564,30 @@ export const UnloadModel = raylibLibrary.bind("UnloadModel", { args: [Model], re /** Compute model bounding box limits (considers all meshes) */ export const GetModelBoundingBox = raylibLibrary.bind("GetModelBoundingBox", { args: [Model], returns: BoundingBox }); +/** Draw a model (with texture if set) */ +export const DrawModel = raylibLibrary.bind("DrawModel", { args: [Model, Vector3, "f32", Color], returns: "void" }); + +/** Draw a model with extended parameters */ +export const DrawModelEx = raylibLibrary.bind("DrawModelEx", { args: [Model, Vector3, Vector3, "f32", Vector3, Color], returns: "void" }); + +/** Draw a model wires (with texture if set) */ +export const DrawModelWires = raylibLibrary.bind("DrawModelWires", { args: [Model, Vector3, "f32", Color], returns: "void" }); + +/** Draw a model wires (with texture if set) with extended parameters */ +export const DrawModelWiresEx = raylibLibrary.bind("DrawModelWiresEx", { args: [Model, Vector3, Vector3, "f32", Vector3, Color], returns: "void" }); + /** Draw bounding box (wires) */ export const DrawBoundingBox = raylibLibrary.bind("DrawBoundingBox", { args: [BoundingBox, Color], returns: "void" }); +/** Draw a billboard texture */ +export const DrawBillboard = raylibLibrary.bind("DrawBillboard", { args: [Camera3D, Texture, Vector3, "f32", Color], returns: "void" }); + /** Draw a billboard texture defined by source */ export const DrawBillboardRec = raylibLibrary.bind("DrawBillboardRec", { args: [Camera3D, Texture, Rectangle, Vector3, Vector2, Color], returns: "void" }); +/** Draw a billboard texture defined by source and rotation */ +export const DrawBillboardPro = raylibLibrary.bind("DrawBillboardPro", { args: [Camera3D, Texture, Rectangle, Vector3, Vector3, Vector2, Vector2, "f32", Color], returns: "void" }); + /** Upload mesh vertex data in GPU and provide VAO/VBO ids */ export const UploadMesh = raylibLibrary.bind("UploadMesh", { args: ["pointer", "bool"], returns: "void" }); @@ -2362,9 +2615,33 @@ export const ExportMesh = raylibLibrary.bind("ExportMesh", { args: [Mesh, "utf8s /** Export mesh as code file (.h) defining multiple arrays of vertex attributes */ export const ExportMeshAsCode = raylibLibrary.bind("ExportMeshAsCode", { args: [Mesh, "utf8string"], returns: "bool" }); +/** Generate polygonal mesh */ +export const GenMeshPoly = raylibLibrary.bind("GenMeshPoly", { args: ["i32", "f32"], returns: Mesh }); + +/** Generate plane mesh (with subdivisions) */ +export const GenMeshPlane = raylibLibrary.bind("GenMeshPlane", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + /** Generate cuboid mesh */ export const GenMeshCube = raylibLibrary.bind("GenMeshCube", { args: ["f32", "f32", "f32"], returns: Mesh }); +/** Generate sphere mesh (standard sphere) */ +export const GenMeshSphere = raylibLibrary.bind("GenMeshSphere", { args: ["f32", "i32", "i32"], returns: Mesh }); + +/** Generate half-sphere mesh (no bottom cap) */ +export const GenMeshHemiSphere = raylibLibrary.bind("GenMeshHemiSphere", { args: ["f32", "i32", "i32"], returns: Mesh }); + +/** Generate cylinder mesh */ +export const GenMeshCylinder = raylibLibrary.bind("GenMeshCylinder", { args: ["f32", "f32", "i32"], returns: Mesh }); + +/** Generate cone/pyramid mesh */ +export const GenMeshCone = raylibLibrary.bind("GenMeshCone", { args: ["f32", "f32", "i32"], returns: Mesh }); + +/** Generate torus mesh */ +export const GenMeshTorus = raylibLibrary.bind("GenMeshTorus", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + +/** Generate trefoil knot mesh */ +export const GenMeshKnot = raylibLibrary.bind("GenMeshKnot", { args: ["f32", "f32", "i32", "i32"], returns: Mesh }); + /** Generate heightmap mesh from image data */ export const GenMeshHeightmap = raylibLibrary.bind("GenMeshHeightmap", { args: [Image, Vector3], returns: Mesh }); @@ -2392,15 +2669,30 @@ export const SetModelMeshMaterial = raylibLibrary.bind("SetModelMeshMaterial", { /** Load model animations from file */ export const LoadModelAnimations = raylibLibrary.bind("LoadModelAnimations", { args: ["utf8string", "pointer"], returns: "pointer" }); +/** Update model animation pose (vertex buffers and bone matrices) */ +export const UpdateModelAnimation = raylibLibrary.bind("UpdateModelAnimation", { args: [Model, ModelAnimation, "f32"], returns: "void" }); + +/** Update model animation pose, blending two animations */ +export const UpdateModelAnimationEx = raylibLibrary.bind("UpdateModelAnimationEx", { args: [Model, ModelAnimation, "f32", ModelAnimation, "f32", "f32"], returns: "void" }); + /** Unload animation array data */ export const UnloadModelAnimations = raylibLibrary.bind("UnloadModelAnimations", { args: ["pointer", "i32"], returns: "void" }); /** Check model animation skeleton match */ export const IsModelAnimationValid = raylibLibrary.bind("IsModelAnimationValid", { args: [Model, ModelAnimation], returns: "bool" }); +/** Check collision between two spheres */ +export const CheckCollisionSpheres = raylibLibrary.bind("CheckCollisionSpheres", { args: [Vector3, "f32", Vector3, "f32"], returns: "bool" }); + /** Check collision between two bounding boxes */ export const CheckCollisionBoxes = raylibLibrary.bind("CheckCollisionBoxes", { args: [BoundingBox, BoundingBox], returns: "bool" }); +/** Check collision between box and sphere */ +export const CheckCollisionBoxSphere = raylibLibrary.bind("CheckCollisionBoxSphere", { args: [BoundingBox, Vector3, "f32"], returns: "bool" }); + +/** Get collision info between ray and sphere */ +export const GetRayCollisionSphere = raylibLibrary.bind("GetRayCollisionSphere", { args: [Ray, Vector3, "f32"], returns: RayCollision }); + /** Get collision info between ray and box */ export const GetRayCollisionBox = raylibLibrary.bind("GetRayCollisionBox", { args: [Ray, BoundingBox], returns: RayCollision }); @@ -2482,6 +2774,15 @@ export const ResumeSound = raylibLibrary.bind("ResumeSound", { args: [Sound], re /** Check if a sound is currently playing */ export const IsSoundPlaying = raylibLibrary.bind("IsSoundPlaying", { args: [Sound], returns: "bool" }); +/** Set volume for a sound (1.0 is max level) */ +export const SetSoundVolume = raylibLibrary.bind("SetSoundVolume", { args: [Sound, "f32"], returns: "void" }); + +/** Set pitch for a sound (1.0 is base level) */ +export const SetSoundPitch = raylibLibrary.bind("SetSoundPitch", { args: [Sound, "f32"], returns: "void" }); + +/** Set pan for a sound (-1.0 left, 0.0 center, 1.0 right) */ +export const SetSoundPan = raylibLibrary.bind("SetSoundPan", { args: [Sound, "f32"], returns: "void" }); + /** Copy a wave to a new wave */ export const WaveCopy = raylibLibrary.bind("WaveCopy", { args: [Wave], returns: Wave }); @@ -2527,6 +2828,18 @@ export const PauseMusicStream = raylibLibrary.bind("PauseMusicStream", { args: [ /** Resume playing paused music */ export const ResumeMusicStream = raylibLibrary.bind("ResumeMusicStream", { args: [Music], returns: "void" }); +/** Seek music to a position (in seconds) */ +export const SeekMusicStream = raylibLibrary.bind("SeekMusicStream", { args: [Music, "f32"], returns: "void" }); + +/** Set volume for music (1.0 is max level) */ +export const SetMusicVolume = raylibLibrary.bind("SetMusicVolume", { args: [Music, "f32"], returns: "void" }); + +/** Set pitch for a music (1.0 is base level) */ +export const SetMusicPitch = raylibLibrary.bind("SetMusicPitch", { args: [Music, "f32"], returns: "void" }); + +/** Set pan for a music (-1.0 left, 0.0 center, 1.0 right) */ +export const SetMusicPan = raylibLibrary.bind("SetMusicPan", { args: [Music, "f32"], returns: "void" }); + /** Get music time length (in seconds) */ export const GetMusicTimeLength = raylibLibrary.bind("GetMusicTimeLength", { args: [Music], returns: "f32" }); @@ -2563,6 +2876,15 @@ export const IsAudioStreamPlaying = raylibLibrary.bind("IsAudioStreamPlaying", { /** Stop audio stream */ export const StopAudioStream = raylibLibrary.bind("StopAudioStream", { args: [AudioStream], returns: "void" }); +/** Set volume for audio stream (1.0 is max level) */ +export const SetAudioStreamVolume = raylibLibrary.bind("SetAudioStreamVolume", { args: [AudioStream, "f32"], returns: "void" }); + +/** Set pitch for audio stream (1.0 is base level) */ +export const SetAudioStreamPitch = raylibLibrary.bind("SetAudioStreamPitch", { args: [AudioStream, "f32"], returns: "void" }); + +/** Set pan for audio stream (-1.0 to 1.0 range, 0.0 is centered) */ +export const SetAudioStreamPan = raylibLibrary.bind("SetAudioStreamPan", { args: [AudioStream, "f32"], returns: "void" }); + /** Default size for new audio streams */ export const SetAudioStreamBufferSizeDefault = raylibLibrary.bind("SetAudioStreamBufferSizeDefault", { args: ["i32"], returns: "void" }); diff --git a/examples/doom-gpl/Makefile b/examples/doom-gpl/Makefile index a5ac672..7f8ddc4 100644 --- a/examples/doom-gpl/Makefile +++ b/examples/doom-gpl/Makefile @@ -17,8 +17,12 @@ FREEDOOM_SHA256 := 3f9b264f3e3ce503b4fb7f6bdcb1f419d93c7b546f4df3e874dd878db9688 FREEDOOM_WAD := $(CACHE_DIR)/freedoom-$(FREEDOOM_VERSION)/freedoom1.wad CACHED_IWAD := $(CACHE_DIR)/iwad.wad -GOCCIA_LOADER ?= GocciaScriptLoader +GOCCIA_RUNNER ?= GocciaRunner GOCCIA_TEST_RUNNER ?= GocciaTestRunner +# goccia.json requests the ffi and read capabilities. Trust it once with +# "GocciaRunner --trust examples/doom-gpl/goccia.json", or pass GOCCIA_FLAGS=-P +# to accept the request for a single run. +GOCCIA_FLAGS ?= GOCCIA_CONFIG := examples/doom-gpl/goccia.json .PHONY: all clean fetch freedoom prepare run run-freedoom smoke test-palette verify-source @@ -75,20 +79,20 @@ run: prepare fi @test -s "$(IWAD)" cp "$(IWAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" run-freedoom: prepare freedoom cp "$(FREEDOOM_WAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/run.ts --config="$(GOCCIA_CONFIG)" smoke: prepare freedoom cp "$(FREEDOOM_WAD)" "$(CACHED_IWAD)" - cd "$(PROJECT_ROOT)" && "$(GOCCIA_LOADER)" examples/doom-gpl/smoke.ts --config="$(GOCCIA_CONFIG)" + cd "$(PROJECT_ROOT)" && "$(GOCCIA_RUNNER)" $(GOCCIA_FLAGS) examples/doom-gpl/smoke.ts --config="$(GOCCIA_CONFIG)" @test -s "$(CACHE_DIR)/doom-goccia-smoke.png" test-palette: - cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=interpreted - cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=bytecode + cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=interpreted --ignore-config-permissions + cd "$(PROJECT_ROOT)" && "$(GOCCIA_TEST_RUNNER)" examples/doom-gpl/tests --config="$(GOCCIA_CONFIG)" --mode=bytecode --ignore-config-permissions clean: @if [ "$(CACHE_DIR)" = "$(CURDIR)/.cache" ]; then \ diff --git a/examples/doom-gpl/README.md b/examples/doom-gpl/README.md index 8997791..0101c1b 100644 --- a/examples/doom-gpl/README.md +++ b/examples/doom-gpl/README.md @@ -28,16 +28,23 @@ checkout explicitly excludes its bundled shareware WAD and music pack. Requirements: -- GocciaScript 0.10.0 +- GocciaScript 0.14.0 - raylib 6.0 as a dynamic library - Node.js, Git, Make, curl, shasum, and unzip -From the repository root: +This subproject's `goccia.json` requests the `ffi` capability and read access +to the repository's `bindings/raylib.ts`, which lies outside the subproject. +GocciaScript applies a config's requests once you have reviewed and trusted +them. From the repository root: ```sh +GocciaRunner --trust examples/doom-gpl/goccia.json make -C examples/doom-gpl run-freedoom ``` +Append `GOCCIA_FLAGS=-P` to any run target to accept the requests for that one +run instead of storing trust. + From this directory: ```sh @@ -71,8 +78,8 @@ Those compatibility features are enabled only by this subproject's default syntax. On an Apple M1 Max with GocciaScript 0.10.0 bytecode, the first working -320x200 build renders Freedoom E1M1 at roughly 3 FPS. This is a real engine -port, but it is not yet a real-time port. Stable 0.10.0 spends most of its time +320x200 build rendered Freedoom E1M1 at roughly 3 FPS. This is a real engine +port, but it is not yet a real-time port. Stable 0.10.0 spent most of its time in the software renderer and indexed-to-RGBA pixel loops. Audio, menus, save files, and full Doom II parity are not wired into this first diff --git a/examples/doom-gpl/goccia.json b/examples/doom-gpl/goccia.json index d94520d..3d1e78e 100644 --- a/examples/doom-gpl/goccia.json +++ b/examples/doom-gpl/goccia.json @@ -1,7 +1,10 @@ { "source-type": "module", "mode": "bytecode", - "unsafe-ffi": true, + "permissions": { + "allow-ffi": true, + "allow-read": ["../../bindings/raylib.ts"] + }, "compat-asi": true, "compat-function": true, "compat-traditional-for-loop": true, diff --git a/examples/doom-gpl/src/i_video.ts b/examples/doom-gpl/src/i_video.ts index da1f3e4..cff112d 100644 --- a/examples/doom-gpl/src/i_video.ts +++ b/examples/doom-gpl/src/i_video.ts @@ -3,15 +3,12 @@ import { BLACK, BeginDrawing, - BeginMode2D, BeginShaderMode, - Camera2D, ClearBackground, CloseWindow, DrawFPS, - DrawTexture, + DrawTextureEx, EndDrawing, - EndMode2D, EndShaderMode, GenImageColor, GetShaderLocation, @@ -76,12 +73,7 @@ void main() { vec2 paletteCoord = vec2((paletteIndex + 0.5)/256.0, 0.5); finalColor = texture(paletteTexture, paletteCoord)*colDiffuse*fragColor; }`; -const camera = Camera2D.create({ - offset: Vector2.create({ x: 0, y: 0 }), - target: Vector2.create({ x: 0, y: 0 }), - rotation: 0, - zoom: scale, -}); +const origin = Vector2.create({ x: 0, y: 0 }); let video = null; let playpal = null; @@ -212,16 +204,14 @@ const I_FinishUpdate = (): void => { BeginDrawing(); ClearBackground(BLACK); - BeginMode2D(camera); if (indexedShaderActive) { BeginShaderMode(shader); SetShaderValueTexture(shader, paletteLocation, paletteTexture); - DrawTexture(indexedTexture, 0, 0, WHITE); + DrawTextureEx(indexedTexture, origin, 0, scale, WHITE); EndShaderMode(); } else { - DrawTexture(rgbaTexture, 0, 0, WHITE); + DrawTextureEx(rgbaTexture, origin, 0, scale, WHITE); } - EndMode2D(); if (showFps) DrawFPS(8, 8); EndDrawing(); }; diff --git a/examples/lib/bunny-instancing.ts b/examples/lib/bunny-instancing.ts index d05b855..fc08030 100644 --- a/examples/lib/bunny-instancing.ts +++ b/examples/lib/bunny-instancing.ts @@ -2,20 +2,16 @@ import { BeginShaderMode, DrawMeshInstanced, EndShaderMode, - GenImageColor, - GenMeshHeightmap, + GenMeshPlane, IsShaderValid, LoadMaterialDefault, LoadShaderFromMemory, MATERIAL_MAP_DIFFUSE, SetMaterialTexture, - UnloadImage, UnloadMaterial, UnloadMesh, UnloadShader, UpdateMeshBuffer, - Vector3, - WHITE, } from "../../bindings/raylib.ts"; const vertexShaderSource = `#version 330 @@ -42,24 +38,6 @@ void main() { finalColor = texture(texture0, fragTexCoord)*colDiffuse*fragColor; }`; -const quadVertices = new Float32Array([ - 0, 0, 0, - 0, 32, 0, - 32, 32, 0, - 0, 0, 0, - 32, 32, 0, - 32, 0, 0, -]); - -const quadTexcoords = new Float32Array([ - 0, 0, - 0, 1, - 1, 1, - 0, 0, - 1, 1, - 1, 0, -]); - export const createBunnyInstancing = (texture) => { const shader = LoadShaderFromMemory( vertexShaderSource, @@ -70,16 +48,17 @@ export const createBunnyInstancing = (texture) => { return null; } - const meshImage = GenImageColor(2, 2, WHITE); - // GenMeshHeightmap supplies an uploaded 6-vertex mesh; both useful buffers - // are completely replaced below to form the sprite quad. - const mesh = GenMeshHeightmap( - meshImage, - Vector3.create({ x: texture.width, y: 0, z: texture.height }), - ); - UnloadImage(meshImage); + // GenMeshPlane supplies an uploaded one-quad mesh centred on the XZ plane. + // Its texture coordinates already match the sprite; its positions move to + // the screen plane with the sprite origin at the top-left corner. + const mesh = GenMeshPlane(texture.width, texture.height, 1, 1); + const quadVertices = new Float32Array([ + 0, 0, 0, + texture.width, 0, 0, + 0, texture.height, 0, + texture.width, texture.height, 0, + ]); UpdateMeshBuffer(mesh, 0, quadVertices, quadVertices.byteLength, 0); - UpdateMeshBuffer(mesh, 1, quadTexcoords, quadTexcoords.byteLength, 0); const material = LoadMaterialDefault(); material.shader = shader; diff --git a/examples/lib/run-bunnymark.ts b/examples/lib/run-bunnymark.ts index 2be9f8a..c8950fb 100644 --- a/examples/lib/run-bunnymark.ts +++ b/examples/lib/run-bunnymark.ts @@ -39,36 +39,6 @@ import { } from "./bunny-instancing.ts"; import { frameNumbers } from "./frames.ts"; -// Generated raylib bindings are immutable const exports. Reading them once -// avoids repeated import resolution while preserving the binding API. -const black = BLACK; -const beginDrawing = BeginDrawing; -const clearBackground = ClearBackground; -const closeWindow = CloseWindow; -const colorType = Color; -const drawFps = DrawFPS; -const drawText = DrawText; -const drawTextureV = DrawTextureV; -const endDrawing = EndDrawing; -const getFrameTime = GetFrameTime; -const getRandomValue = GetRandomValue; -const initWindow = InitWindow; -const isKeyDown = IsKeyDown; -const isMouseButtonDown = IsMouseButtonDown; -const keySpace = KEY_SPACE; -const loadImageFromMemory = LoadImageFromMemory; -const loadTextureFromImage = LoadTextureFromImage; -const mouseButtonLeft = MOUSE_BUTTON_LEFT; -const raywhite = RAYWHITE; -const setRandomSeed = SetRandomSeed; -const setTargetFps = SetTargetFPS; -const takeScreenshot = TakeScreenshot; -const unloadImage = UnloadImage; -const unloadTexture = UnloadTexture; -const vector2Type = Vector2; -const windowShouldClose = WindowShouldClose; -const closeRaylibBinding = closeRaylib; - const screenWidth: number = 1280; const screenHeight: number = 720; const maxBunnies: number = 50000; @@ -78,14 +48,14 @@ export const runBunnymark = ( maxFrames: number, ): void => { const startedAt = Date.now(); - const image = loadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); + const image = LoadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); if (image.width !== 32 || image.height !== 32) { throw new Error("raybunny PNG failed to decode"); } - initWindow(screenWidth, screenHeight, "GocciaScript + raylib: Bunnymark"); - const texture = loadTextureFromImage(image); - unloadImage(image); + InitWindow(screenWidth, screenHeight, "GocciaScript + raylib: Bunnymark"); + const texture = LoadTextureFromImage(image); + UnloadImage(image); const configuredSeed = globalThis.BUNNYMARK_RANDOM_SEED; const configuredInitialSprites = @@ -111,8 +81,8 @@ export const runBunnymark = ( : 0; const reportFinal = globalThis.BUNNYMARK_REPORT_FINAL === true; - if (typeof configuredSeed === "number") setRandomSeed(configuredSeed); - setTargetFps(targetFps); + if (typeof configuredSeed === "number") SetRandomSeed(configuredSeed); + SetTargetFPS(targetFps); const bunnies = createBunnyState(maxBunnies); addBunnies( @@ -120,14 +90,14 @@ export const runBunnymark = ( initialSprites, screenWidth / 2, screenHeight / 2, - getRandomValue, + GetRandomValue, ); const instancing = requestedDrawPath === "instanced" ? createBunnyInstancing(texture) : null; const drawPath = instancing === null ? "direct" : "instanced"; - const drawPosition = vector2Type.create({ x: 0, y: 0 }); - const drawColor = colorType.create({ r: 0, g: 0, b: 0, a: 255 }); + const drawPosition = Vector2.create({ x: 0, y: 0 }); + const drawColor = Color.create({ r: 0, g: 0, b: 0, a: 255 }); const startupMilliseconds = Date.now() - startedAt; console.log( "Bunnymark startup_ms=" + @@ -145,20 +115,20 @@ export const runBunnymark = ( try { for (const frame of frameNumbers(maxFrames)) { - if (windowShouldClose()) break; + if (WindowShouldClose()) break; const deltaScale = fixedDeltaScale === null - ? getFrameTime() * 60 + ? GetFrameTime() * 60 : fixedDeltaScale; if ( - isMouseButtonDown(mouseButtonLeft) || - isKeyDown(keySpace) + IsMouseButtonDown(MOUSE_BUTTON_LEFT) || + IsKeyDown(KEY_SPACE) ) { addBunnies( bunnies, 100, screenWidth / 2, screenHeight / 2, - getRandomValue, + GetRandomValue, ); } @@ -171,8 +141,8 @@ export const runBunnymark = ( screenHeight, ); - beginDrawing(); - clearBackground(black); + BeginDrawing(); + ClearBackground(BLACK); if (instancing !== null) { drawBunnyInstances(instancing, bunnies); } else { @@ -184,23 +154,23 @@ export const runBunnymark = ( drawColor.g = bunnies.colors[colorOffset + 1]; drawColor.b = bunnies.colors[colorOffset + 2]; drawColor.a = bunnies.colors[colorOffset + 3]; - drawTextureV(texture, drawPosition, drawColor); + DrawTextureV(texture, drawPosition, drawColor); } } if (showOverlay) { - drawText("bunnies: " + bunnies.length, 12, 10, 20, raywhite); - drawText( + DrawText("bunnies: " + bunnies.length, 12, 10, 20, RAYWHITE); + DrawText( "hold mouse-left or Space to add 100", 12, 34, 20, - raywhite, + RAYWHITE, ); - drawFps(screenWidth - 100, 10); + DrawFPS(screenWidth - 100, 10); } - endDrawing(); + EndDrawing(); if (typeof capturePath === "string" && frame === captureFrame) { - takeScreenshot(capturePath); + TakeScreenshot(capturePath); } framesRendered += 1; @@ -242,9 +212,9 @@ export const runBunnymark = ( observedFps.toFixed(2), ); } - if (instancing === null) unloadTexture(texture); + if (instancing === null) UnloadTexture(texture); else unloadBunnyInstancing(instancing); - closeWindow(); - closeRaylibBinding(); + CloseWindow(); + closeRaylib(); } }; diff --git a/goccia.json b/goccia.json index 886149b..9ccdb37 100644 --- a/goccia.json +++ b/goccia.json @@ -1,5 +1,7 @@ { "source-type": "module", "mode": "bytecode", - "unsafe-ffi": true + "permissions": { + "allow-ffi": true + } } diff --git a/metadata/gocciascript.json b/metadata/gocciascript.json index d7bc310..bdda7c8 100644 --- a/metadata/gocciascript.json +++ b/metadata/gocciascript.json @@ -1,10 +1,8 @@ { - "version": "0.10.0", - "commit": "c477011ed305be05b371ff5f54ad0b54de9ac0cc", - "tagObject": "776d31393b625ef24c190c9b7d5fba5bad100a8f", - "release": "https://github.com/frostney/GocciaScript/releases/tag/0.10.0", + "version": "0.14.0", + "commit": "72cb20505832bd2e0f4db0ef8d46d2a4ca85140c", + "release": "https://github.com/frostney/GocciaScript/releases/tag/0.14.0", "ffi": { - "maxArguments": 8, - "mixedTopLevelF32": false + "maxArguments": 64 } } diff --git a/package.json b/package.json index 5a5ee08..88e8430 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,15 @@ { "name": "@frostney/gocciascript-raylib", "version": "0.1.0", - "description": "Generated raylib 6.0 dynamic-library bindings for GocciaScript 0.10.0", + "description": "Generated raylib 6.0 dynamic-library bindings for GocciaScript 0.14.0", "type": "module", + "exports": { + ".": { + "types": "./bindings/raylib.d.ts", + "default": "./bindings/raylib.ts" + }, + "./*": "./*" + }, "license": "MIT", "engines": { "node": ">=24" @@ -30,9 +37,9 @@ "generate": "node scripts/normalize-api.mjs && node scripts/generate-bindings.mjs && node scripts/generate-abi.mjs", "check:generated": "node scripts/check-generated.mjs", "test": "npm run test:goccia:interpreter && npm run test:goccia:bytecode", - "test:goccia:interpreter": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit", - "test:goccia:bytecode": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=bytecode", - "test:types": "tsc --noEmit --strict --skipLibCheck false --lib ES2022 bindings/raylib.d.ts", + "test:goccia:interpreter": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=interpreted --ignore-config-permissions", + "test:goccia:bytecode": "\"${GOCCIA_TEST_RUNNER:-GocciaTestRunner}\" tests/unit --mode=bytecode --ignore-config-permissions", + "test:types": "tsc --noEmit --strict --skipLibCheck false --lib ES2022 --module esnext --moduleResolution bundler --moduleSuffixes .d bindings/raylib.d.ts tests/types/consumer.ts", "test:abi": "node tests/integration/abi.test.mjs", "test:ffi": "node tests/integration/ffi.test.mjs", "test:bunnymark:visual": "node tests/integration/bunnymark-visual.test.mjs", diff --git a/scripts/generate-bindings.mjs b/scripts/generate-bindings.mjs index 18b68db..4821f2d 100644 --- a/scripts/generate-bindings.mjs +++ b/scripts/generate-bindings.mjs @@ -13,5 +13,5 @@ await Promise.all([ console.log( `Generated ${artifacts.stats.generated} raylib functions; ` + - `skipped ${artifacts.stats.skipped}; post-upstream target ${artifacts.stats.target}.`, + `skipped ${artifacts.stats.skipped}.`, ); diff --git a/scripts/lib/abi.mjs b/scripts/lib/abi.mjs index ab7ea7a..48aca24 100644 --- a/scripts/lib/abi.mjs +++ b/scripts/lib/abi.mjs @@ -1,4 +1,4 @@ -import { ffiFieldName, renderBindingArtifacts } from "./bindings.mjs"; +import { renderBindingArtifacts } from "./bindings.mjs"; function cString(value) { return JSON.stringify(value); @@ -137,18 +137,17 @@ const probePointer = raylibLibrary.symbol("InitWindow"); const fieldOffset = (Type, field, kind) => { const value = Type.create(); - if (kind === "aggregate") return value[field].byteOffset; + // FFI.metadata stays valid when a native field is itself named "buffer". + if (kind === "aggregate") return FFI.metadata(value[field]).byteOffset; + const storage = new Uint8Array(FFI.metadata(value).buffer); if (kind === "pointer") { value[field] = probePointer; const width = Type.size > 4 ? 8 : 4; - return findBytes( - new Uint8Array(value.buffer), - pointerBytes(probePointer).subarray(0, width), - ); + return findBytes(storage, pointerBytes(probePointer).subarray(0, width)); } const probe = bytesFor(kind); value[field] = probe.value; - return findBytes(new Uint8Array(value.buffer), probe.bytes); + return findBytes(storage, probe.bytes); }; `; @@ -157,7 +156,7 @@ const fieldOffset = (Type, field, kind) => { source += `console.log("STRUCT\\t${struct.name}\\t" + ${struct.name}.size + "\\t" + ${struct.name}.alignment);\n`; for (const field of struct.fields) { const resolved = resolveType(field.type, "field"); - source += `console.log("FIELD\\t${struct.name}\\t${field.name}\\t" + fieldOffset(${struct.name}, ${cString(ffiFieldName(field.name))}, ${cString(probeKind(resolved))}));\n`; + source += `console.log("FIELD\\t${struct.name}\\t${field.name}\\t" + fieldOffset(${struct.name}, ${cString(field.name)}, ${cString(probeKind(resolved))}));\n`; } } diff --git a/scripts/lib/bindings.mjs b/scripts/lib/bindings.mjs index 604e521..0821e24 100644 --- a/scripts/lib/bindings.mjs +++ b/scripts/lib/bindings.mjs @@ -28,12 +28,6 @@ const scalarTypes = new Map([ ["void", "void"], ]); -const intentionalSkips = new Map([ - ["TraceLog", "varargs"], - ["TextFormat", "varargs"], - ["DrawBillboardPro", "more-than-8-arguments"], -]); - const nullableStringParameters = new Map([ ["LoadShader", [0, 1]], ["LoadShaderFromMemory", [0, 1]], @@ -53,11 +47,9 @@ const ownedTextReturns = new Set([ "TextInsertAlloc", ]); -export function ffiFieldName(name) { - if (name === "buffer") return "nativeBuffer"; - if (name === "byteOffset") return "nativeByteOffset"; - return name; -} +// Aggregate values expose these backing-store properties directly unless a +// native field of the same name takes precedence; FFI.metadata always works. +const aggregateMetadataNames = ["buffer", "byteOffset", "size"]; function cleanType(type) { return type @@ -178,9 +170,6 @@ function resolveType(type, env, context = "argument", stack = []) { aliasTarget: resolved, }; } - if (name === "...") { - throw new Error("varargs are unsupported"); - } throw new Error(`Unsupported C type: ${type}`); } @@ -244,9 +233,16 @@ export const RAYLIB_BINDING_INFO = { gocciaScriptVersion: "${goccia.version}", linkage: "dynamic", }; -export const RAYLIB_FIELD_ALIASES = { - AudioStream: { buffer: "nativeBuffer" }, -}; + +if (typeof FFI === "undefined") { + throw new TypeError( + "The raylib bindings need GocciaScript's ffi capability. Grant it with " + + '--allow-ffi, or with "permissions": { "allow-ffi": true } in a ' + + "trusted goccia.json.", + ); +} + +const nullableUtf8String = FFI.nullable("utf8string"); const raylibCandidates = (): string[] => { const suffix = FFI.suffix; @@ -307,6 +303,9 @@ function renderDefinitions(api, env, orderedStructs) { let js = ""; let dts = `// Generated from official raylib ${api.source.version}. Do not edit. +// Only the declarations marked export are part of the module. +export {}; + export interface FFIPointer { readonly address: number; readonly isNull: boolean; @@ -338,29 +337,58 @@ export type FFIPointerInput = | ArrayBuffer | SharedArrayBuffer | FFITypedArray - | FFIAggregateValue + | FFIAggregate | null; -export interface FFIAggregateValue { +declare const ffiAggregate: unique symbol; +/** Any GocciaScript FFI aggregate value. */ +export interface FFIAggregate { + readonly [ffiAggregate]: true; +} +/** Backing store of an aggregate, as returned by GocciaScript's \`FFI.metadata(value)\`. */ +export interface FFIAggregateMetadata { readonly buffer: ArrayBuffer; readonly byteOffset: number; readonly size: number; } +/** + * Aggregate that also exposes its backing store directly. A native field named + * like a metadata property takes precedence; \`FFI.metadata(value)\` always works. + */ +export interface FFIAggregateValue extends FFIAggregate, FFIAggregateMetadata {} export interface FFIArrayValue extends FFIAggregateValue { readonly length: number; [index: number]: T; } -export interface FFIStructDescriptor { +export interface FFIStructDescriptor { readonly kind: "struct"; readonly size: number; readonly alignment: number; create(initializer?: Partial): T; } +declare const ffiVarargs: unique symbol; +/** Typed variadic tail created by GocciaScript's \`FFI.varargs(types, values)\`. */ +export interface FFIVarargs { + readonly [ffiVarargs]: true; +} +/** + * The members of GocciaScript's \`FFI\` global that callers of these bindings + * use. GocciaScript ships no declarations, so type-checked code declares the + * global itself: \`declare const FFI: GocciaFFI;\`. + */ +export interface GocciaFFI { + varargs(types: readonly unknown[], values: readonly unknown[]): FFIVarargs; + metadata(value: FFIAggregate): FFIAggregateMetadata; +} export interface FFILibrary { readonly path: string; readonly closed: boolean; bind( name: string, - signature: { args: readonly unknown[]; returns: unknown }, + signature: { + args: readonly unknown[]; + returns: unknown; + variadic?: boolean; + }, ): (...args: unknown[]) => unknown; symbol(name: string): FFIPointer; close(): void; @@ -371,9 +399,6 @@ export declare const RAYLIB_BINDING_INFO: { gocciaScriptVersion: string; linkage: "dynamic"; }; -export declare const RAYLIB_FIELD_ALIASES: { - AudioStream: { buffer: "nativeBuffer" }; -}; export declare const raylibLibrary: FFILibrary; export declare function closeRaylib(): void; @@ -390,17 +415,26 @@ export declare function closeRaylib(): void; js += docComment(struct.description); js += `export const ${struct.name} = FFI.struct({\n`; for (const field of fields) { - js += ` ${JSON.stringify(ffiFieldName(field.name))}: ${field.resolved.descriptor},\n`; + js += ` ${JSON.stringify(field.name)}: ${field.resolved.descriptor},\n`; } js += "});\n\n"; + const shadowed = aggregateMetadataNames.filter((name) => + fields.some((field) => field.name === name), + ); + const base = + shadowed.length === 0 + ? "FFIAggregateValue" + : `FFIAggregate, Omit JSON.stringify(name)) + .join(" | ")}>`; dts += docComment(struct.description); - dts += `export interface ${struct.name}Value extends FFIAggregateValue {\n`; + dts += `export interface ${struct.name}Value extends ${base} {\n`; for (const field of fields) { - if (ffiFieldName(field.name) !== field.name) { - dts += ` /** Native C field: ${field.name}. */\n`; + if (shadowed.includes(field.name)) { + dts += ` /** Native C field; read the backing store with \`FFI.metadata(value).${field.name}\`. */\n`; } - dts += ` ${JSON.stringify(ffiFieldName(field.name))}: ${field.resolved.ts};\n`; + dts += ` ${JSON.stringify(field.name)}: ${field.resolved.ts};\n`; } dts += "}\n"; dts += `export declare const ${struct.name}: FFIStructDescriptor<${struct.name}Value>;\n\n`; @@ -469,23 +503,27 @@ export declare function closeRaylib(): void; } function classifyFunction(fn, env, goccia) { - if (intentionalSkips.has(fn.name)) { + const variadic = fn.params.at(-1)?.type === "..."; + const params = variadic ? fn.params.slice(0, -1) : fn.params; + if (params.length > goccia.ffi.maxArguments) { return { supported: false, - reason: intentionalSkips.get(fn.name), - category: "intentional-0.1-scope", - }; - } - if (fn.params.length > goccia.ffi.maxArguments) { - return { - supported: false, - reason: "more-than-8-arguments", - category: "stable-runtime-limit", + reason: `more-than-${goccia.ffi.maxArguments}-arguments`, + category: "runtime-limit", }; } try { - const args = fn.params.map((param) => resolveType(param.type, env, "argument")); + const nullableParameters = nullableStringParameters.get(fn.name) ?? []; + const args = params.map((param, index) => + nullableParameters.includes(index) + ? { + kind: "utf8string", + descriptor: "nullableUtf8String", + ts: "string | null", + } + : resolveType(param.type, env, "argument"), + ); let returns = resolveType(fn.returnType, env, "return"); if (ownedTextReturns.has(fn.name)) { returns = { @@ -494,18 +532,7 @@ function classifyFunction(fn, env, goccia) { ts: "FFIPointer", }; } - const hasF32 = args.some((arg) => arg.kind === "f32"); - const hasOther = args.some((arg) => arg.kind !== "f32"); - if (!goccia.ffi.mixedTopLevelF32 && hasF32 && hasOther) { - return { - supported: false, - reason: "mixed-top-level-f32", - category: "stable-runtime-limit", - args, - returns, - }; - } - return { supported: true, args, returns }; + return { supported: true, params, args, returns, variadic }; } catch (error) { return { supported: false, @@ -516,7 +543,24 @@ function classifyFunction(fn, env, goccia) { } } +// A stale entry would silently drop a nullable parameter, so drift in the +// pinned API fails generation instead. +function assertNullableStringParameters(api, env) { + const functions = new Map(api.functions.map((fn) => [fn.name, fn])); + for (const [name, indices] of nullableStringParameters) { + const fn = functions.get(name); + if (!fn) throw new Error(`Nullable string function is not in the API: ${name}`); + for (const index of indices) { + const type = fn.params[index]?.type; + if (!type || resolveType(type, env, "argument").kind !== "utf8string") { + throw new Error(`${name} parameter ${index} is not a C string`); + } + } + } +} + function renderFunctions(api, env, goccia, deferredDefines) { + assertNullableStringParameters(api, env); let js = ""; let dts = ""; const generated = []; @@ -537,28 +581,18 @@ function renderFunctions(api, env, goccia, deferredDefines) { generated.push(fn.name); const args = classification.args.map((arg) => arg.descriptor).join(", "); + const variadic = classification.variadic ? "variadic: true, " : ""; js += docComment(fn.description); - js += `export const ${fn.name} = raylibLibrary.bind("${fn.name}", { args: [${args}], returns: ${classification.returns.descriptor} });\n\n`; - + js += `export const ${fn.name} = raylibLibrary.bind("${fn.name}", { args: [${args}], ${variadic}returns: ${classification.returns.descriptor} });\n\n`; + + const parameters = classification.params.map( + (param, index) => + `${param.name || `arg${index}`}: ${classification.args[index].ts}`, + ); + // GocciaScript requires exactly one FFI.varargs(types, values) tail. + if (classification.variadic) parameters.push("args: FFIVarargs"); dts += docComment(fn.description); - dts += `export declare function ${fn.name}(${fn.params - .map((param, index) => `${param.name || `arg${index}`}: ${classification.args[index].ts}`) - .join(", ")}): ${classification.returns.ts};\n\n`; - - const nullableParameters = nullableStringParameters.get(fn.name); - if (nullableParameters) { - const rawArgs = classification.args.map((arg, index) => - nullableParameters.includes(index) - ? { descriptor: '"pointer"', ts: "FFIPointerInput" } - : arg, - ); - js += `/** Raw-pointer variant for nullable C string parameters. */\n`; - js += `export const ${fn.name}Raw = raylibLibrary.bind("${fn.name}", { args: [${rawArgs.map((arg) => arg.descriptor).join(", ")}], returns: ${classification.returns.descriptor} });\n\n`; - dts += `/** Raw-pointer variant for nullable C string parameters. */\n`; - dts += `export declare function ${fn.name}Raw(${fn.params - .map((param, index) => `${param.name || `arg${index}`}: ${rawArgs[index].ts}`) - .join(", ")}): ${classification.returns.ts};\n\n`; - } + dts += `export declare function ${fn.name}(${parameters.join(", ")}): ${classification.returns.ts};\n\n`; } for (const define of deferredDefines) { @@ -570,16 +604,12 @@ function renderFunctions(api, env, goccia, deferredDefines) { } function renderSkipped(api, goccia, generated, skipped) { - const intentionalCount = skipped.filter( - (item) => item.category === "intentional-0.1-scope", - ).length; const report = { - schemaVersion: 1, + schemaVersion: 2, raylibVersion: api.source.version, raylibCommit: api.source.commit, gocciaScriptVersion: goccia.version, apiFunctions: api.inventory.functions, - callableTargetAfterUpstreamSupport: api.inventory.functions - intentionalCount, generatedFunctions: generated.length, skippedFunctions: skipped.length, skipped, @@ -600,11 +630,14 @@ GocciaScript ${goccia.version}. - Official API functions: ${report.apiFunctions} - Generated and callable on the pinned stable runtime: ${report.generatedFunctions} - Deterministically skipped: ${report.skippedFunctions} -- Target after mixed-top-level-\`f32\` support: ${report.callableTargetAfterUpstreamSupport} -The report is a compatibility boundary, not a claim that skipped functions are -implemented. Re-run the generator after the upstream FFI restriction is removed. -The three intentional 0.1 exclusions remain out of scope. +${ + skipped.length === 0 + ? `The pinned runtime binds the complete official API. A function that a future +raylib or runtime version cannot bind is listed here rather than shimmed.` + : `The report is a compatibility boundary, not a claim that skipped functions are +implemented. Re-run the generator after the limit below is removed.` + } `; for (const [key, items] of groups) { @@ -651,7 +684,6 @@ export async function renderBindingArtifacts() { stats: { generated: functions.generated.length, skipped: functions.skipped.length, - target: skipped.report.callableTargetAfterUpstreamSupport, }, api, env, diff --git a/scripts/validate-package.mjs b/scripts/validate-package.mjs index f8901ae..7ba9ebf 100644 --- a/scripts/validate-package.mjs +++ b/scripts/validate-package.mjs @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; -import { fromRoot } from "./lib/project.mjs"; +import { fromRoot, readJson } from "./lib/project.mjs"; const result = spawnSync("npm", ["pack", "--dry-run", "--json"], { cwd: fromRoot(), @@ -54,4 +54,16 @@ assert.ok( ![...files].some((path) => path.toLowerCase().endsWith(".wad")), "IWAD data must never ship in the npm package", ); +const { exports: entryPoints } = await readJson(fromRoot("package.json")); +assert.deepEqual( + entryPoints["."], + { types: "./bindings/raylib.d.ts", default: "./bindings/raylib.ts" }, + "The package entry point must be the generated bindings", +); +for (const target of Object.values(entryPoints["."])) { + assert.ok( + files.has(target.slice(2)), + `Package entry point ${target} is not in the tarball`, + ); +} console.log(`Package contents validated (${manifest.files.length} files).`); diff --git a/tests/abi/layout-probe.ts b/tests/abi/layout-probe.ts index 2abe21f..0a5373f 100644 --- a/tests/abi/layout-probe.ts +++ b/tests/abi/layout-probe.ts @@ -120,18 +120,17 @@ const probePointer = raylibLibrary.symbol("InitWindow"); const fieldOffset = (Type, field, kind) => { const value = Type.create(); - if (kind === "aggregate") return value[field].byteOffset; + // FFI.metadata stays valid when a native field is itself named "buffer". + if (kind === "aggregate") return FFI.metadata(value[field]).byteOffset; + const storage = new Uint8Array(FFI.metadata(value).buffer); if (kind === "pointer") { value[field] = probePointer; const width = Type.size > 4 ? 8 : 4; - return findBytes( - new Uint8Array(value.buffer), - pointerBytes(probePointer).subarray(0, width), - ); + return findBytes(storage, pointerBytes(probePointer).subarray(0, width)); } const probe = bytesFor(kind); value[field] = probe.value; - return findBytes(new Uint8Array(value.buffer), probe.bytes); + return findBytes(storage, probe.bytes); }; console.log("STRUCT\tVector2\t" + Vector2.size + "\t" + Vector2.alignment); @@ -292,7 +291,7 @@ console.log("FIELD\tWave\tsampleSize\t" + fieldOffset(Wave, "sampleSize", "u32") console.log("FIELD\tWave\tchannels\t" + fieldOffset(Wave, "channels", "u32")); console.log("FIELD\tWave\tdata\t" + fieldOffset(Wave, "data", "pointer")); console.log("STRUCT\tAudioStream\t" + AudioStream.size + "\t" + AudioStream.alignment); -console.log("FIELD\tAudioStream\tbuffer\t" + fieldOffset(AudioStream, "nativeBuffer", "pointer")); +console.log("FIELD\tAudioStream\tbuffer\t" + fieldOffset(AudioStream, "buffer", "pointer")); console.log("FIELD\tAudioStream\tprocessor\t" + fieldOffset(AudioStream, "processor", "pointer")); console.log("FIELD\tAudioStream\tsampleRate\t" + fieldOffset(AudioStream, "sampleRate", "u32")); console.log("FIELD\tAudioStream\tsampleSize\t" + fieldOffset(AudioStream, "sampleSize", "u32")); diff --git a/tests/fixtures/ffi-smoke.ts b/tests/fixtures/ffi-smoke.ts index 67c0f2d..500347c 100644 --- a/tests/fixtures/ffi-smoke.ts +++ b/tests/fixtures/ffi-smoke.ts @@ -1,13 +1,16 @@ import bunnyBytes from "../../examples/assets/raybunny.png" with { type: "bytes" }; import { + AudioStream, + CheckCollisionCircles, Color, + ColorAlpha, ColorToInt, EncodeDataBase64, GetColor, GetFileExtension, GetRandomValue, IsPathFile, - LoadAutomationEventListRaw, + LoadAutomationEventList, LoadFileText, LoadImageFromMemory, MemAlloc, @@ -15,10 +18,13 @@ import { RAYLIB_BINDING_INFO, SetRandomSeed, TextCopy, + TextFormat, TextLength, + TextReplace, UnloadAutomationEventList, UnloadFileText, UnloadImage, + Vector2, closeRaylib, } from "../../bindings/raylib.ts"; @@ -58,9 +64,38 @@ const encoded = EncodeDataBase64( assert(encoded.isNull === false && outputSize[0] > 0, "owned encoded text pointer"); MemFree(encoded); -const emptyEvents = LoadAutomationEventListRaw(null); -assert(emptyEvents.count === 0, "nullable C string raw binding"); +const emptyEvents = LoadAutomationEventList(null); +// The integration test checks raylib's log to prove that NULL, not the text +// "null", reached the native call. +assert(emptyEvents.count === 0, "empty automation event list"); UnloadAutomationEventList(emptyEvents); +assert(TextReplace("a-b-c", "-", null) === "abc", "null replacement text"); +assert(TextReplace("a-b-c", "-", "+") === "a+b+c", "nullable C string text"); + +const faded = ColorAlpha(Color.create({ r: 18, g: 52, b: 86, a: 255 }), 0.5); +assert( + faded.r === 18 && faded.g === 52 && faded.b === 86 && faded.a === 127, + "aggregate argument mixed with a float argument", +); +const origin = Vector2.create({ x: 0, y: 0 }); +const apart = Vector2.create({ x: 10, y: 0 }); +assert(CheckCollisionCircles(origin, 6.5, apart, 4.5) === true, "overlapping circles"); +assert(CheckCollisionCircles(origin, 4.5, apart, 4.5) === false, "separate circles"); + +const formatted = TextFormat( + "%s %03d %.2f", + FFI.varargs(["utf8string", "i32", "f32"], ["raylib", 7, 1.5]), +); +assert(formatted === "raylib 007 1.50", "variadic native call"); + +const stream = AudioStream.create({ sampleRate: 44100, channels: 2 }); +assert(stream.buffer.isNull === true, "exact native field named buffer"); +const streamStorage = FFI.metadata(stream); +assert( + streamStorage.buffer instanceof ArrayBuffer && + streamStorage.size === AudioStream.size, + "aggregate backing store through FFI.metadata", +); const image = LoadImageFromMemory(".png", bunnyBytes, bunnyBytes.length); assert(image.width === 32 && image.height === 32, "byte-pointer image load"); diff --git a/tests/integration/abi.test.mjs b/tests/integration/abi.test.mjs index 3a551cb..57e3807 100644 --- a/tests/integration/abi.test.mjs +++ b/tests/integration/abi.test.mjs @@ -5,13 +5,14 @@ import { join } from "node:path"; import { tmpdir } from "node:os"; import { fromRoot } from "../../scripts/lib/project.mjs"; import { - findGocciaLoader, + acceptConfigPermissions, + findGocciaRunner, findRaylibInclude, parseLayoutLines, } from "./helpers.mjs"; -const [loader, includeDir] = await Promise.all([ - findGocciaLoader(), +const [runner, includeDir] = await Promise.all([ + findGocciaRunner(), findRaylibInclude(), ]); const temporaryDirectory = await mkdtemp(join(tmpdir(), "goccia-raylib-abi-")); @@ -41,11 +42,15 @@ assert.equal( const native = spawnSync(oracle, [], { encoding: "utf8" }); assert.equal(native.status, 0, `Native ABI oracle failed:\n${native.stderr}`); -const goccia = spawnSync(loader, [fromRoot("tests/abi/layout-probe.ts")], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, -}); +const goccia = spawnSync( + runner, + [acceptConfigPermissions, fromRoot("tests/abi/layout-probe.ts")], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, +); assert.equal( goccia.status, 0, diff --git a/tests/integration/bunnymark-visual.test.mjs b/tests/integration/bunnymark-visual.test.mjs index 1e6f332..7ad59e5 100644 --- a/tests/integration/bunnymark-visual.test.mjs +++ b/tests/integration/bunnymark-visual.test.mjs @@ -3,9 +3,9 @@ import { createHash } from "node:crypto"; import { mkdir, readFile, unlink } from "node:fs/promises"; import { spawnSync } from "node:child_process"; import { fromRoot } from "../../scripts/lib/project.mjs"; -import { findGocciaLoader } from "./helpers.mjs"; +import { acceptConfigPermissions, findGocciaRunner } from "./helpers.mjs"; -const loader = await findGocciaLoader(); +const runner = await findGocciaRunner(); const cacheDirectory = fromRoot(".cache"); const directPath = fromRoot(".cache/bunnymark-direct.png"); const instancedPath = fromRoot(".cache/bunnymark-instanced.png"); @@ -20,11 +20,15 @@ for (const path of [directPath, instancedPath]) { } const runFixture = (fixture, expectedPath) => { - const result = spawnSync(loader, [fromRoot(fixture)], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, - }); + const result = spawnSync( + runner, + [acceptConfigPermissions, fromRoot(fixture)], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, + ); assert.equal( result.status, 0, diff --git a/tests/integration/ffi.test.mjs b/tests/integration/ffi.test.mjs index 61f794a..6017ee2 100644 --- a/tests/integration/ffi.test.mjs +++ b/tests/integration/ffi.test.mjs @@ -1,20 +1,29 @@ import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { fromRoot } from "../../scripts/lib/project.mjs"; -import { findGocciaLoader } from "./helpers.mjs"; +import { acceptConfigPermissions, findGocciaRunner } from "./helpers.mjs"; -const loader = await findGocciaLoader(); -const result = spawnSync(loader, [fromRoot("tests/fixtures/ffi-smoke.ts")], { - cwd: fromRoot(), - encoding: "utf8", - env: process.env, -}); +const runner = await findGocciaRunner(); +const result = spawnSync( + runner, + [acceptConfigPermissions, fromRoot("tests/fixtures/ffi-smoke.ts")], + { + cwd: fromRoot(), + encoding: "utf8", + env: process.env, + }, +); assert.equal( result.status, 0, `Representative raylib FFI calls failed:\n${result.stdout}\n${result.stderr}`, ); assert.match(result.stdout, /raylib ffi smoke ok/); +assert.match( + result.stdout, + /AUTOMATION: New empty events list loaded successfully/, + "LoadAutomationEventList(null) did not pass a native NULL", +); console.log( - "Representative scalar, bool, pointer, aggregate, UTF-8, owned-text, and nullable calls passed.", + "Representative scalar, bool, pointer, aggregate, mixed-float, UTF-8, owned-text, nullable, and variadic calls passed.", ); diff --git a/tests/integration/helpers.mjs b/tests/integration/helpers.mjs index 28840b2..0f2ccc3 100644 --- a/tests/integration/helpers.mjs +++ b/tests/integration/helpers.mjs @@ -12,11 +12,15 @@ async function executable(path) { } } -export async function findGocciaLoader() { - const configured = await executable(process.env.GOCCIA_LOADER); +// The repository goccia.json requests the ffi capability. The fixtures accept +// that request for one run instead of depending on a per-user trust store. +export const acceptConfigPermissions = "--accept-config-permissions"; + +export async function findGocciaRunner() { + const configured = await executable(process.env.GOCCIA_RUNNER); if (configured) return configured; try { - const discovered = execFileSync("sh", ["-c", "command -v GocciaScriptLoader"], { + const discovered = execFileSync("sh", ["-c", "command -v GocciaRunner"], { encoding: "utf8", }).trim(); if (await executable(discovered)) return discovered; @@ -24,7 +28,7 @@ export async function findGocciaLoader() { // Report one actionable error below. } throw new Error( - "GocciaScriptLoader 0.10.0 was not found; set GOCCIA_LOADER to the release binary", + "GocciaRunner 0.14.0 was not found; set GOCCIA_RUNNER to the release binary", ); } diff --git a/tests/linux/Dockerfile b/tests/linux/Dockerfile index 37fd1ea..318c15f 100644 --- a/tests/linux/Dockerfile +++ b/tests/linux/Dockerfile @@ -44,18 +44,18 @@ RUN case "$TARGETARCH" in \ esac \ && mkdir -p /opt/gocciascript \ && curl --fail --location \ - "https://github.com/frostney/GocciaScript/releases/download/0.10.0/gocciascript-0.10.0-linux-${goccia_arch}.tar.gz" \ + "https://github.com/frostney/GocciaScript/releases/download/0.14.0/gocciascript-0.14.0-linux-${goccia_arch}.tar.gz" \ --output /tmp/gocciascript.tar.gz \ && tar -xzf /tmp/gocciascript.tar.gz -C /opt/gocciascript --strip-components=1 \ && chmod +x \ - /opt/gocciascript/GocciaScriptLoader \ + /opt/gocciascript/GocciaRunner \ /opt/gocciascript/GocciaTestRunner \ - && ln -s /opt/gocciascript/GocciaScriptLoader /usr/local/bin/GocciaScriptLoader \ + && ln -s /opt/gocciascript/GocciaRunner /usr/local/bin/GocciaRunner \ && ln -s /opt/gocciascript/GocciaTestRunner /usr/local/bin/GocciaTestRunner WORKDIR /workspace COPY . . -ENV GOCCIA_LOADER=/usr/local/bin/GocciaScriptLoader +ENV GOCCIA_RUNNER=/usr/local/bin/GocciaRunner ENV GOCCIA_TEST_RUNNER=/usr/local/bin/GocciaTestRunner ENV RAYLIB_INCLUDE_DIR=/usr/local/include @@ -68,11 +68,12 @@ RUN npm ci \ && npm run test:ffi \ && npm run pack:check \ && mkdir -p .cache \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/basic-window-smoke.ts \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/basic-input-smoke.ts \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/image-loading-smoke.ts \ - && xvfb-run -a GocciaScriptLoader examples/smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/basic-window-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/basic-input-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/image-loading-smoke.ts \ + && xvfb-run -a GocciaRunner -P examples/smoke.ts \ && xvfb-run -a npm run test:bunnymark:visual \ - && xvfb-run -a GocciaScriptLoader tests/fixtures/doom-smoke.ts \ + && xvfb-run -a GocciaRunner -P tests/fixtures/doom-smoke.ts \ && test -s .cache/doom-smoke.png \ - && xvfb-run -a make -C examples/doom-gpl smoke + && make -C examples/doom-gpl test-palette \ + && xvfb-run -a make -C examples/doom-gpl smoke GOCCIA_FLAGS=-P diff --git a/tests/types/consumer.ts b/tests/types/consumer.ts new file mode 100644 index 0000000..b75d6be --- /dev/null +++ b/tests/types/consumer.ts @@ -0,0 +1,47 @@ +// Type-only consumer of the generated declarations; `npm run test:types` +// compiles it and never runs it. That script passes `--moduleSuffixes .d` so +// the import resolves to raylib.d.ts rather than the untyped runtime module. +import type * as Raylib from "../../bindings/raylib"; +import type { + AudioStreamValue, + ColorValue, + FFIPointer, + FFIPointerInput, + GocciaFFI, +} from "../../bindings/raylib"; + +declare const raylib: typeof Raylib; +declare const FFI: GocciaFFI; + +const stream: AudioStreamValue = raylib.AudioStream.create({ + sampleRate: 44100, +}); +export const nativeField: FFIPointer = stream.buffer; +export const streamStorage: ArrayBuffer = FFI.metadata(stream).buffer; +export const streamSize: number = stream.size; +export const streamPointer: FFIPointerInput = stream; + +const color: ColorValue = raylib.Color.create({ r: 1, g: 2, b: 3, a: 4 }); +export const colorStorage: ArrayBuffer = color.buffer; + +raylib.LoadShader(null, "fragment.glsl"); +export const replaced: string = raylib.TextReplace("a-b", "-", null); +export const formatted: string = raylib.TextFormat( + "%d", + FFI.varargs(["i32"], [1]), +); +raylib.TraceLog(raylib.LOG_INFO, "ready", FFI.varargs([], [])); +raylib.DrawCircle(20, 20, 10.5, raylib.RED); + +// @ts-expect-error the variadic tail is required +raylib.TextFormat("%d"); +// @ts-expect-error a plain array is not a typed variadic tail +raylib.TextFormat("%d", [1]); +// @ts-expect-error only documented nullable C strings accept null +raylib.TextLength(null); +// @ts-expect-error AudioStream.buffer is the native pointer field +export const wrongStorage: ArrayBuffer = stream.buffer; +// @ts-expect-error plain objects are not FFI aggregates +export const notAggregate: FFIPointerInput = { size: 4 }; +// @ts-expect-error the aggregate brand is not an export +export type Brand = typeof Raylib.ffiAggregate; diff --git a/tests/unit/bindings.test.ts b/tests/unit/bindings.test.ts index 9a4fe6b..f6470e5 100644 --- a/tests/unit/bindings.test.ts +++ b/tests/unit/bindings.test.ts @@ -3,12 +3,11 @@ import declarations from "../../bindings/raylib.d.ts" with { type: "text" }; import skipped from "../../SKIPPED.json" with { type: "json" }; describe("generated raylib bindings", () => { - test("exposes the stable-safe dynamic API", () => { + test("exposes the complete dynamic API", () => { expect(skipped.apiFunctions).toBe(600); - expect(skipped.callableTargetAfterUpstreamSupport).toBe(597); - expect(skipped.generatedFunctions + skipped.skippedFunctions).toBe( - skipped.apiFunctions, - ); + expect(skipped.generatedFunctions).toBe(600); + expect(skipped.skipped).toEqual([]); + expect(raylibSource.match(/ = raylibLibrary\.bind\(/g)).toHaveLength(600); expect(raylibSource).toContain('linkage: "dynamic"'); for (const name of [ @@ -29,12 +28,18 @@ describe("generated raylib bindings", () => { expect(raylibSource).toContain("export const KEY_W = 87;"); expect(raylibSource).toContain("export const Quaternion = Vector4;"); expect(raylibSource).toContain('export const AudioCallback = "pointer";'); + expect(raylibSource).toMatch( + /export const AudioStream = FFI\.struct\(\{\n "buffer": "pointer",/, + ); + expect(declarations).toContain( + 'interface AudioStreamValue extends FFIAggregate, Omit', + ); expect(declarations).toContain("interface VrStereoConfigValue"); expect(declarations).toContain("export type FFITypedArray ="); expect(declarations).not.toContain("| ArrayBufferView"); }); - test("preserves owned pointers and nullable string variants", () => { + test("preserves owned pointers and nullable string parameters", () => { expect(raylibSource).toMatch( /export const LoadFileText = .*returns: "pointer"/, ); @@ -51,30 +56,36 @@ describe("generated raylib bindings", () => { /function LoadImageFromMemory\(fileType: string, fileData: FFIPointerInput/, ); - for (const name of [ - "LoadShaderRaw", - "LoadShaderFromMemoryRaw", - "LoadAutomationEventListRaw", - "TextReplaceRaw", - "TextReplaceAllocRaw", - "TextReplaceBetweenRaw", - "TextReplaceBetweenAllocRaw", - ]) { - expect(raylibSource).toContain(`export const ${name} =`); - expect(declarations).toContain(`function ${name}(`); - } + expect(raylibSource.match(/nullableUtf8String[,\]]/g)).toHaveLength(9); + expect(raylibSource).toMatch( + /export const LoadShader = .*args: \[nullableUtf8String, nullableUtf8String\]/, + ); + expect(raylibSource).toMatch( + /export const TextReplaceBetween = .*"utf8string", nullableUtf8String\]/, + ); + expect(declarations).toContain( + "function LoadAutomationEventList(fileName: string | null)", + ); + expect(raylibSource).not.toContain("export const LoadShaderRaw ="); + expect(raylibSource).not.toContain("export const TextReplaceRaw ="); }); - test("reports stable FFI and intentional exclusions explicitly", () => { - const byName = new Map( - skipped.skipped.map((item) => [item.name, item]), + test("binds variadic, high-arity, and mixed float signatures", () => { + expect(raylibSource).toMatch( + /export const TraceLog = .*args: \["i32", "utf8string"\], variadic: true, returns: "void"/, ); - - expect(byName.get("TraceLog").reason).toBe("varargs"); - expect(byName.get("TextFormat").reason).toBe("varargs"); - expect(byName.get("DrawBillboardPro").reason).toBe( - "more-than-8-arguments", + expect(raylibSource).toMatch( + /export const TextFormat = .*args: \["utf8string"\], variadic: true, returns: "utf8string"/, + ); + expect(raylibSource.match(/variadic: true/g)).toHaveLength(2); + expect(declarations).toContain( + "function TextFormat(text: string, args: FFIVarargs): string;", + ); + expect(raylibSource).toMatch( + /export const DrawBillboardPro = .*args: \[Camera3D, Texture, Rectangle, Vector3, Vector3, Vector2, Vector2, "f32", Color\]/, + ); + expect(raylibSource).toMatch( + /export const DrawCircle = .*args: \["i32", "i32", "f32", Color\]/, ); - expect(byName.get("DrawCircle").reason).toBe("mixed-top-level-f32"); }); });