From ed5f25aa016a3cdadb0d8723b17cd2d0d0eb0d97 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Thu, 25 Jul 2024 15:11:22 +0200 Subject: [PATCH] Basic audio support and play-wav example (#28) * First version of play-wav examples. * Move RWMode to rw.ts and remove types.ts * Throw an error in denoFromPlatformCallback. * Mark SDL_AudioSpec.callback as defective and allow SDL_AudioSpec to be constructed wihtout any parameters. --- .vscode/launch.json | 10 + .vscode/settings.json | 6 +- .vscode/tasks.json | 17 + assets/powerup.wav | Bin 0 -> 28206 bytes deno.json | 1 + examples/play-wav/main.ts | 61 ++++ examples/play-wav/sdlConfig.ts | 17 + mod.SDL.ts | 2 + src/SDL/_callbacks.ts | 22 +- src/SDL/_symbols.ts | 47 +++ src/SDL/audio.ts | 4 + src/SDL/callbacks.ts | 12 +- src/SDL/functionMacros.ts | 24 +- src/SDL/functions.ts | 82 ++++- src/SDL/rw.ts | 13 + src/SDL/structs.ts | 159 ++++++++- src/SDL/types.ts | 3 - src/SDL_ttf/structs.ts | 3 + src/_types.ts | 54 ++- src/deno/_callbacks.ts | 12 +- src/deno/_dataView.ts | 169 +++++++-- src/deno/_platform.ts | 6 +- src/deno/_strings.ts | 18 +- tools/codegen-scraper.ts | 156 ++++---- tools/codegen/SDL.ts | 49 ++- tools/codegen/SDL/callbacks.ts | 17 + tools/codegen/SDL/functions.ts | 95 +++++ tools/codegen/SDL/structs.ts | 44 +++ tools/codegen/generators.ts | 635 ++++++++++++++++++++++++--------- 29 files changed, 1440 insertions(+), 298 deletions(-) create mode 100644 assets/powerup.wav create mode 100644 examples/play-wav/main.ts create mode 100644 examples/play-wav/sdlConfig.ts create mode 100644 src/SDL/audio.ts create mode 100644 src/SDL/rw.ts delete mode 100644 src/SDL/types.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 4519b24..0903704 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,6 +41,16 @@ "runtimeArgs": ["task", "run:hello-world-async"], "attachSimplePort": 9229 }, + { + "name": "Play Wav", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "env": { "DENO_FLAGS": "--inspect-brk" }, + "runtimeExecutable": "deno", + "runtimeArgs": ["task", "run:play-wav"], + "attachSimplePort": 9229 + }, { "name": "Renderer", "type": "node", diff --git a/.vscode/settings.json b/.vscode/settings.json index 9994940..e8e2447 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,9 @@ "editor.defaultFormatter": "denoland.vscode-deno", - "editor.tabSize": 2 + "editor.tabSize": 2, + + "[josn,ts]": { + "editor.defaultFormatter": "denoland.vscode-deno" + } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 04ebcf2..810e3b4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -94,6 +94,23 @@ }, "problemMatcher": ["$deno"] }, + { + "label": "Run Play Wav", + "group": { + "kind": "none", + "isDefault": true + }, + "type": "deno", + "command": "task", + "args": ["run:play-wav"], + "options": { + "env": { + "GDK_BACKEND": "wayland" + }, + "cwd": "${workspaceFolder}" + }, + "problemMatcher": ["$deno"] + }, { "label": "Run Renderer", "group": { diff --git a/assets/powerup.wav b/assets/powerup.wav new file mode 100644 index 0000000000000000000000000000000000000000..962df62cbce41194c5519383a2cf0546c41ed8d6 GIT binary patch literal 28206 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 literal 0 HcmV?d00001 diff --git a/deno.json b/deno.json index 8a2b620..2c54107 100644 --- a/deno.json +++ b/deno.json @@ -25,6 +25,7 @@ "run:doom-fire": "cd ./examples/doom-fire && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", "run:hello-world": "cd ./examples/hello-world && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", "run:hello-world-async": "cd ./examples/hello-world-async && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", + "run:play-wav": "cd ./examples/play-wav && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", "run:renderer": "cd ./examples/renderer && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", "run:same-game": "cd ./examples/same-game && SDL_TS_ENV_DIR=$INIT_CWD deno run --unstable-ffi --allow-env --allow-ffi --allow-read=../.. $DENO_FLAGS ./main.ts", "test": "deno test --unstable-ffi --allow-ffi" diff --git a/examples/play-wav/main.ts b/examples/play-wav/main.ts new file mode 100644 index 0000000..e3da875 --- /dev/null +++ b/examples/play-wav/main.ts @@ -0,0 +1,61 @@ +// Adapted from https://gigi.nullneuron.net/gigilabs/playing-a-wav-file-using-sdl2/ + +import { Box, Pointer, SDL, U32, U8, u32, u8 } from "SDL_ts"; +import { SDL_FUNCTIONS } from "./sdlConfig.ts"; +import { ASSETS_PATH } from "../../shared/constants.ts"; +import { path } from "../../deps.ts"; + +const main = (): number => { + if (SDL.Init(SDL.InitFlags.AUDIO, { functions: SDL_FUNCTIONS }) < 0) { + return 1; + } + + console.info("SDL Initialized."); + + const wavSpec = new SDL.AudioSpec(); + const wavBufferBox = new Box>(Pointer); + const wavLengthBox = new Box(U32); + + if ( + SDL.LoadWav( + path.join(ASSETS_PATH, "powerup.wav"), + wavSpec, + wavBufferBox, + wavLengthBox + ) == null + ) { + console.error("ERROR: Failed to load wav file."); + return 1; + } + + const audioDeviceID = SDL.OpenAudioDevice(null, 0, wavSpec, null, 0); + if (audioDeviceID <= 0) { + console.error("ERROR: Faield to open an audio device."); + return 1; + } + + if ( + SDL.QueueAudio(audioDeviceID, wavBufferBox.value, wavLengthBox.value) != 0 + ) { + console.error(`ERROR: Faield to queue audio: ${SDL.GetError()}`); + return 1; + } + + SDL.PauseAudioDevice(audioDeviceID, 0); + + SDL.Delay(1000); + + SDL.CloseAudioDevice(audioDeviceID); + SDL.FreeWAV(wavBufferBox.value); + SDL.Quit(); + console.info("SDL Shutdown."); + + return 0; +}; + +try { + Deno.exit(main()); +} catch (error) { + console.error(error); + Deno.exit(1); +} diff --git a/examples/play-wav/sdlConfig.ts b/examples/play-wav/sdlConfig.ts new file mode 100644 index 0000000..d1dfed0 --- /dev/null +++ b/examples/play-wav/sdlConfig.ts @@ -0,0 +1,17 @@ +import { SDL } from "SDL_ts"; + +// This file contains the list of functions that are used in the project. + +export const SDL_FUNCTIONS = [ + SDL.CloseAudioDevice, + SDL.Delay, + SDL.FreeWAV, + SDL.GetError, + SDL.Init, + SDL.LoadWAV_RW, + SDL.OpenAudioDevice, + SDL.PauseAudioDevice, + SDL.QueueAudio, + SDL.Quit, + SDL.RWFromFile, +] as const; diff --git a/mod.SDL.ts b/mod.SDL.ts index 051ef16..37eaa03 100644 --- a/mod.SDL.ts +++ b/mod.SDL.ts @@ -1,3 +1,4 @@ +export * from "./src/SDL/audio.ts"; export * from "./src/SDL/callbacks.ts"; export * from "./src/SDL/constants.ts"; export * from "./src/SDL/enums.ts"; @@ -5,4 +6,5 @@ export * from "./src/SDL/events.ts"; export * from "./src/SDL/functionMacros.ts"; export * from "./src/SDL/functions.ts"; export * from "./src/SDL/pixels.ts"; +export * from "./src/SDL/rw.ts"; export * from "./src/SDL/structs.ts"; diff --git a/src/SDL/_callbacks.ts b/src/SDL/_callbacks.ts index fa45bb1..253d8e5 100644 --- a/src/SDL/_callbacks.ts +++ b/src/SDL/_callbacks.ts @@ -5,10 +5,11 @@ import Platform from "../_platform.ts"; import { PlatformPointer } from "../_types.ts"; import { Event } from "./events.ts"; -import { i32, u32 } from "../types.ts"; +import { i32, u32, u8 } from "../types.ts"; -import { EventFilter } from "./callbacks.ts"; +import { AudioCallback, EventFilter } from "./callbacks.ts"; import { + AudioSpec, Color, DisplayMode, Keysym, @@ -27,6 +28,23 @@ import { } from "./structs.ts"; export const callbacks = { + SDL_AudioCallback: { + parameters: [ + /* void* userdata */ "pointer", + /* Uint8* stream */ "pointer", + /* int len */ "i32", + ], + result: /* void */ "void", + wrap: (callback: AudioCallback) => { + return (userdata: PlatformPointer, stream: PlatformPointer, len: i32): void => { + return callback( + Platform.fromPlatformPointer(userdata)!, + Platform.fromPlatformPointer(stream)!, + len!, + ); + }; + }, + }, SDL_EventFilter: { parameters: [ /* void* userdata */ "pointer", diff --git a/src/SDL/_symbols.ts b/src/SDL/_symbols.ts index 1d288bf..a0401cf 100644 --- a/src/SDL/_symbols.ts +++ b/src/SDL/_symbols.ts @@ -28,6 +28,12 @@ export const symbols = { ], result: /* int */ "i32", }, + SDL_CloseAudioDevice: { + parameters: [ + /* SDL_AudioDeviceID dev */ "u32", + ], + result: /* void */ "void", + }, SDL_ConvertSurface: { parameters: [ /* SDL_Surface* src */ "pointer", @@ -177,6 +183,12 @@ export const symbols = { ], result: /* int */ "i32", }, + SDL_FreeWAV: { + parameters: [ + /* Uint8* audio_buf */ "pointer", + ], + result: /* void */ "void", + }, SDL_FreeSurface: { parameters: [ /* SDL_Surface* surface */ "pointer", @@ -488,6 +500,16 @@ export const symbols = { ], result: /* SDL_Surface* */ "pointer", }, + SDL_LoadWAV_RW: { + parameters: [ + /* SDL_RWops* src */ "pointer", + /* int freesrc */ "i32", + /* SDL_AudioSpec* spec */ "pointer", + /* Uint8** audio_buf */ "pointer", + /* Uint32* audio_len */ "pointer", + ], + result: /* SDL_AudioSpec* */ "pointer", + }, SDL_LockSurface: { parameters: [ /* SDL_Surface* surface */ "pointer", @@ -525,6 +547,23 @@ export const symbols = { ], result: /* void */ "void", }, + SDL_OpenAudioDevice: { + parameters: [ + /* char* device */ "pointer", + /* int iscapture */ "i32", + /* SDL_AudioSpec* desired */ "pointer", + /* SDL_AudioSpec* obtained */ "pointer", + /* int allowed_changes */ "i32", + ], + result: /* SDL_AudioDeviceID */ "u32", + }, + SDL_PauseAudioDevice: { + parameters: [ + /* SDL_AudioDeviceID dev */ "u32", + /* int pause_on */ "i32", + ], + result: /* void */ "void", + }, SDL_PollEvent: { parameters: [ /* SDL_Event* event */ "pointer", @@ -545,6 +584,14 @@ export const symbols = { parameters: [], result: /* void */ "void", }, + SDL_QueueAudio: { + parameters: [ + /* SDL_AudioDeviceID dev */ "u32", + /* void* data */ "pointer", + /* Uint32 len */ "u32", + ], + result: /* int */ "i32", + }, SDL_RWFromFile: { parameters: [ /* char* file */ "pointer", diff --git a/src/SDL/audio.ts b/src/SDL/audio.ts new file mode 100644 index 0000000..f6514e1 --- /dev/null +++ b/src/SDL/audio.ts @@ -0,0 +1,4 @@ +import { u16, u32 } from "../types.ts"; + +export type AudioFormat = u16; +export type AudioDeviceID = u32; diff --git a/src/SDL/callbacks.ts b/src/SDL/callbacks.ts index 64216a5..c3a7671 100644 --- a/src/SDL/callbacks.ts +++ b/src/SDL/callbacks.ts @@ -3,9 +3,19 @@ // deno-lint-ignore-file no-unused-vars import { Pointer } from "../pointers.ts"; -import { Callback, i32, u32 } from "../types.ts"; +import { Callback, i32, u32, u8 } from "../types.ts"; import { Event } from "./events.ts"; +export type AudioCallback = + & ( + ( + userdata: Pointer | null, + stream: Pointer, + len: i32, + ) => void + ) + & Callback; + export type EventFilter = & ( ( diff --git a/src/SDL/functionMacros.ts b/src/SDL/functionMacros.ts index 6a862b5..3c8a634 100644 --- a/src/SDL/functionMacros.ts +++ b/src/SDL/functionMacros.ts @@ -2,14 +2,34 @@ // 1 to 1 mapping. import { LoadBMP_RW, RWFromFile } from "./functions.ts"; -import { Surface } from "./structs.ts"; +import { AudioSpec, Surface } from "./structs.ts"; +import { Box } from "../boxes.ts"; +import { SDLError } from "../error.ts"; +import { Pointer, PointerLike } from "../pointers.ts"; +import { LoadWAV_RW } from "../../mod.SDL.ts"; +import { u32, u8 } from "../types.ts"; export function LoadBMP(file: string): Surface | null { const rw = RWFromFile(file, "rb"); if (rw == null) { - throw new Error("RWFromFile failed."); + throw new SDLError("RWFromFile failed."); } return LoadBMP_RW(rw, 1); } + +export function LoadWav( + file: string, + spec: PointerLike, + audio_buf: Box>, + audio_len: PointerLike +): AudioSpec | null { + const rw = RWFromFile(file, "rb"); + + if (rw == null) { + throw new SDLError("RWFromFile failed."); + } + + return LoadWAV_RW(rw, 1, spec, audio_buf, audio_len); +} diff --git a/src/SDL/functions.ts b/src/SDL/functions.ts index 19df178..831b10c 100644 --- a/src/SDL/functions.ts +++ b/src/SDL/functions.ts @@ -12,7 +12,7 @@ import { callbacks } from "./_callbacks.ts"; import { getSymbolsFromFunctions } from "../_init.ts"; import { symbols } from "./_symbols.ts"; -import { EventFilter } from "./callbacks.ts"; +import { AudioCallback, EventFilter } from "./callbacks.ts"; import { ArrayOrder, BitmapOrder, @@ -38,6 +38,7 @@ import { WindowPos, } from "./enums.ts"; import { + AudioSpec, Color, DisplayMode, Keysym, @@ -55,8 +56,9 @@ import { Window, } from "./structs.ts"; +import { AudioDeviceID } from "./audio.ts"; import { Event } from "./events.ts"; -import { RWMode /*, TimerID */ } from "./types.ts"; +import { RWMode } from "./rw.ts"; let _library: DynamicLibrary = null!; @@ -104,6 +106,15 @@ export function BlitSurface( } BlitSurface.symbolName = "SDL_UpperBlit"; +export function CloseAudioDevice( + dev: AudioDeviceID, +): void { + _library.symbols.SDL_CloseAudioDevice( + dev, + ); +} +CloseAudioDevice.symbolName = "SDL_CloseAudioDevice"; + export function ConvertSurface( src: PointerLike, fmt: PointerLike, @@ -364,6 +375,15 @@ export function FlashWindow( } FlashWindow.symbolName = "SDL_FlashWindow"; +export function FreeWAV( + audio_buf: PointerLike, +): void { + _library.symbols.SDL_FreeWAV( + Platform.toPlatformPointer(Pointer.of(audio_buf)), + ); +} +FreeWAV.symbolName = "SDL_FreeWAV"; + export function FreeSurface( surface: PointerLike, ): void { @@ -841,6 +861,23 @@ export function LoadBMP_RW( } LoadBMP_RW.symbolName = "SDL_LoadBMP_RW"; +export function LoadWAV_RW( + src: PointerLike, + freesrc: i32, + spec: PointerLike, + audio_buf: Box>, + audio_len: PointerLike, +): AudioSpec | null { + return AudioSpec.of(Platform.fromPlatformPointer(_library.symbols.SDL_LoadWAV_RW( + Platform.toPlatformPointer(Pointer.of(src)), + freesrc, + Platform.toPlatformPointer(Pointer.of(spec)), + Platform.toPlatformPointer(Pointer.ofTypedArray(audio_buf._data)), + Platform.toPlatformPointer(Pointer.of(audio_len)), + ) as PlatformPointer)); +} +LoadWAV_RW.symbolName = "SDL_LoadWAV_RW"; + export function LockSurface( surface: PointerLike, ): i32 { @@ -900,6 +937,34 @@ export function MinimizeWindow( } MinimizeWindow.symbolName = "SDL_MinimizeWindow"; +export function OpenAudioDevice( + device: string | null, + iscapture: i32, + desired: PointerLike, + obtained: PointerLike | null, + allowed_changes: i32, +): AudioDeviceID { + return _library.symbols.SDL_OpenAudioDevice( + Platform.toPlatformString(device), + iscapture, + Platform.toPlatformPointer(Pointer.of(desired)), + Platform.toPlatformPointer(Pointer.of(obtained)), + allowed_changes, + ) as AudioDeviceID; +} +OpenAudioDevice.symbolName = "SDL_OpenAudioDevice"; + +export function PauseAudioDevice( + dev: AudioDeviceID, + pause_on: i32, +): void { + _library.symbols.SDL_PauseAudioDevice( + dev, + pause_on, + ); +} +PauseAudioDevice.symbolName = "SDL_PauseAudioDevice"; + export function PollEvent( event: PointerLike, ): i32 { @@ -932,6 +997,19 @@ export function Quit(): void { } Quit.symbolName = "SDL_Quit"; +export function QueueAudio( + dev: AudioDeviceID, + data: PointerLike, + len: u32, +): i32 { + return _library.symbols.SDL_QueueAudio( + dev, + Platform.toPlatformPointer(Pointer.of(data)), + len, + ) as i32; +} +QueueAudio.symbolName = "SDL_QueueAudio"; + export function RWFromFile( file: string, mode: RWMode, diff --git a/src/SDL/rw.ts b/src/SDL/rw.ts new file mode 100644 index 0000000..8153b46 --- /dev/null +++ b/src/SDL/rw.ts @@ -0,0 +1,13 @@ +export type RWMode = + | "a" + | "a+" + | "r" + | "r+" + | "w" + | "w+" + | "ab" + | "ab+" + | "rb" + | "rb+" + | "wb" + | "wb+"; \ No newline at end of file diff --git a/src/SDL/structs.ts b/src/SDL/structs.ts index 6fb5cf4..4384ad0 100644 --- a/src/SDL/structs.ts +++ b/src/SDL/structs.ts @@ -3,11 +3,14 @@ // deno-lint-ignore-file no-unused-vars import Platform from "../_platform.ts"; +import { callbacks } from "./_callbacks.ts"; import { PlatformDataView } from "../_types.ts"; import { isTypedArray } from "../_utils.ts"; import { Pointer } from "../pointers.ts"; import { AllocatableStruct, f32, f64, i16, i32, i64, i8, Struct, u16, u32, u64, u8 } from "../types.ts"; +import { AudioCallback, EventFilter } from "./callbacks.ts"; + import { ArrayOrder, BitmapOrder, @@ -129,12 +132,160 @@ export class Window implements Struct { } } +export class AudioSpec implements AllocatableStruct { + public static SIZE_IN_BYTES = 32; + + public readonly _data: Uint8Array | Pointer; + public readonly _view: PlatformDataView; + + constructor(); + constructor( + data: Uint8Array | Pointer, + byteOffset: number, + ); + constructor(props: Partial); + constructor( + freq: i32, + format: u16, + channels: u8, + silence: u8, + samples: u16, + padding: u16, + size: u32, + // callback: AudioCallback, + userdata: Pointer, + ); + constructor( + _1: Uint8Array | Pointer | Partial | i32 = {}, + _2?: number | u16, + _3?: u8, + _4?: u8, + _5?: u16, + _6?: u16, + _7?: u32, + // _8?: AudioCallback, + _9?: Pointer, + ) { + const dataPassedIn = isTypedArray(_1) || Pointer.isPointer(_1); + if (dataPassedIn) { + this._data = _1; + this._view = new Platform.DataView(this._data, _2); + } else { + this._data = new Uint8Array(AudioSpec.SIZE_IN_BYTES); + this._view = new Platform.DataView(this._data, 0); + } + + if (!dataPassedIn && _1 !== undefined) { + if (typeof _1 === "object") { + if (_1.freq !== undefined) this.freq = _1.freq; + if (_1.format !== undefined) this.format = _1.format; + if (_1.channels !== undefined) this.channels = _1.channels; + if (_1.silence !== undefined) this.silence = _1.silence; + if (_1.samples !== undefined) this.samples = _1.samples; + if (_1.padding !== undefined) this.padding = _1.padding; + if (_1.size !== undefined) this.size = _1.size; + // if (_1.callback !== undefined) this.callback = _1.callback; + if (_1.userdata !== undefined) this.userdata = _1.userdata; + } else { + if (_1 !== undefined) this.freq = _1; + if (_2 !== undefined) this.format = _2; + if (_3 !== undefined) this.channels = _3; + if (_4 !== undefined) this.silence = _4; + if (_5 !== undefined) this.samples = _5; + if (_6 !== undefined) this.padding = _6; + if (_7 !== undefined) this.size = _7; + // if (_8 !== undefined) this.callback = _8; + if (_9 !== undefined) this.userdata = _9; + } + } + } + + public static of( + data: Uint8Array | Pointer | null, + byteOffset: number = 0, + ): AudioSpec | null { + return data !== null ? new AudioSpec(data, byteOffset) : null; + } + + public get _byteOffset(): number { + return this._view.byteOffset; + } + + public get freq(): i32 { + return this._view.getI32(0); + } + + public set freq(value: i32) { + this._view.setI32(0, value); + } + + public get format(): u16 { + return this._view.getU16(4); + } + + public set format(value: u16) { + this._view.setU16(4, value); + } + + public get channels(): u8 { + return this._view.getU8(6); + } + + public set channels(value: u8) { + this._view.setU8(6, value); + } + + public get silence(): u8 { + return this._view.getU8(7); + } + + public set silence(value: u8) { + this._view.setU8(7, value); + } + + public get samples(): u16 { + return this._view.getU16(8); + } + + public set samples(value: u16) { + this._view.setU16(8, value); + } + + public get padding(): u16 { + return this._view.getU16(10); + } + + public set padding(value: u16) { + this._view.setU16(10, value); + } + + public get size(): u32 { + return this._view.getU32(12); + } + + public set size(value: u32) { + this._view.setU32(12, value); + } + + // TODO: Doesn't seem to work due to background thread. + // callback + + public get userdata(): Pointer { + return this._view.getPointer(24); + } + + public set userdata(value: Pointer) { + this._view.setPointer(24, value); + } +} + export class Color implements AllocatableStruct { public static SIZE_IN_BYTES = 4; public readonly _data: Uint8Array | Pointer; public readonly _view: PlatformDataView; + constructor(); constructor( data: Uint8Array | Pointer, byteOffset: number, @@ -142,7 +293,7 @@ export class Color implements AllocatableStruct { constructor(props: Partial); constructor(r: u8, g: u8, b: u8, a: u8); constructor( - _1?: Uint8Array | Pointer | Partial | u8, + _1: Uint8Array | Pointer | Partial | u8 = {}, _2?: number | u8, _3?: u8, _4?: u8, @@ -417,6 +568,7 @@ export class Point implements AllocatableStruct { public readonly _data: Uint8Array | Pointer; public readonly _view: PlatformDataView; + constructor(); constructor( data: Uint8Array | Pointer, byteOffset: number, @@ -424,7 +576,7 @@ export class Point implements AllocatableStruct { constructor(props: Partial); constructor(x: i32, y: i32); constructor( - _1?: Uint8Array | Pointer | Partial | i32, + _1: Uint8Array | Pointer | Partial | i32 = {}, _2?: number | i32, ) { const dataPassedIn = isTypedArray(_1) || Pointer.isPointer(_1); @@ -481,6 +633,7 @@ export class Rect implements AllocatableStruct { public readonly _data: Uint8Array | Pointer; public readonly _view: PlatformDataView; + constructor(); constructor( data: Uint8Array | Pointer, byteOffset: number, @@ -488,7 +641,7 @@ export class Rect implements AllocatableStruct { constructor(props: Partial); constructor(x: i32, y: i32, w: i32, h: i32); constructor( - _1?: Uint8Array | Pointer | Partial | i32, + _1: Uint8Array | Pointer | Partial | i32 = {}, _2?: number | i32, _3?: i32, _4?: i32, diff --git a/src/SDL/types.ts b/src/SDL/types.ts deleted file mode 100644 index d8ba7fc..0000000 --- a/src/SDL/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type RWMode = "a" | "a+" | "r" | "r+" | "w" | "w+" | "ab" | "ab+" | "rb" | "rb+" | "wb" | "wb+"; - -// export type TimerID = int; diff --git a/src/SDL_ttf/structs.ts b/src/SDL_ttf/structs.ts index 7bc71f1..a226397 100644 --- a/src/SDL_ttf/structs.ts +++ b/src/SDL_ttf/structs.ts @@ -3,11 +3,14 @@ // deno-lint-ignore-file no-unused-vars import Platform from "../_platform.ts"; +import { callbacks } from "./_callbacks.ts"; import { PlatformDataView } from "../_types.ts"; import { isTypedArray } from "../_utils.ts"; import { Pointer } from "../pointers.ts"; import { AllocatableStruct, f32, f64, i16, i32, i64, i8, Struct, u16, u32, u64, u8 } from "../types.ts"; +import {} from "./callbacks.ts"; + import {} from "./enums.ts"; export class Font implements Struct { diff --git a/src/_types.ts b/src/_types.ts index 1dda776..3764ede 100644 --- a/src/_types.ts +++ b/src/_types.ts @@ -1,6 +1,24 @@ import { Pointer } from "./pointers.ts"; -import { Callback, f32, f64, i16, i32, i64, i8, Struct, StructConstructor, u16, u32, u64, u8 } from "./types.ts"; -import { DynamicCallbackDefinition, DynamicLibrary, DynamicLibraryInterface } from "./_library.ts"; +import { + Callback, + f32, + f64, + i16, + i32, + i64, + i8, + Struct, + StructConstructor, + u16, + u32, + u64, + u8, +} from "./types.ts"; +import { + DynamicCallbackDefinition, + DynamicLibrary, + DynamicLibraryInterface, +} from "./_library.ts"; declare const _: unique symbol; @@ -9,10 +27,7 @@ export type PlatformPointer = { [_]: "PlatformPointer" }; export type PlatformString = { [_]: "PlatformString" }; export interface PlatformDataViewConstructor { - new ( - data: Uint8Array | Pointer, - offset?: number, - ): PlatformDataView; + new (data: Uint8Array | Pointer, offset?: number): PlatformDataView; } export interface PlatformDataView { @@ -20,6 +35,10 @@ export interface PlatformDataView { readonly byteOffset: number; getArray(byteLength: number, byteOffset: number): Uint8Array; + getCallback( + byteOffset: number, + definition: DynamicCallbackDefinition + ): T; getF32(byteOffset: number): f32; getF64(byteOffset: number): f64; getI8(byteOffset: number): i8; @@ -31,6 +50,11 @@ export interface PlatformDataView { getU16(byteOffset: number): u16; getU32(byteOffset: number): u32; getU64(byteOffset: number): u64; + setCallback( + byteOffset: number, + value: T, + definition: DynamicCallbackDefinition + ): void; setF32(byteOffset: number, value: f32): void; setF64(byteOffset: number, value: f64): void; setI8(byteOffset: number, value: i8): void; @@ -49,29 +73,37 @@ export interface Platform { DataView: PlatformDataViewConstructor; + fromPlatformCallback( + value: PlatformCallback, + definition: DynamicCallbackDefinition + ): T; + fromPlatformPointer(value: PlatformPointer | null): Pointer | null; fromPlatformString(value: Uint8Array | PlatformPointer): string; fromPlatformStruct( data: PlatformPointer, - structConstructor: StructConstructor, + structConstructor: StructConstructor ): T | null; loadLibrary( libraryName: string, symbols: T, - libraryPath?: string, + libraryPath?: string ): DynamicLibrary; - toPlatformCallback(value: T, definition: DynamicCallbackDefinition): PlatformCallback; + toPlatformCallback( + value: T, + definition: DynamicCallbackDefinition + ): PlatformCallback; toPlatformPointer(value: Pointer | null): PlatformPointer | null; - toPlatformString(value: string): PlatformString; + toPlatformString(value: string | null): PlatformString; toPlatformStruct( struct: T, - stuctConstructor: StructConstructor, + stuctConstructor: StructConstructor ): Uint8Array; } diff --git a/src/deno/_callbacks.ts b/src/deno/_callbacks.ts index f00a18d..9b7981e 100644 --- a/src/deno/_callbacks.ts +++ b/src/deno/_callbacks.ts @@ -1,14 +1,22 @@ import { DynamicCallbackDefinition } from "../_library.ts"; import { PlatformCallback } from "../_types.ts"; +import { SDLError } from "../error.ts"; import { Callback } from "../types.ts"; interface CallbackInternal extends Callback { denoCallback?: Deno.UnsafeCallback; } +export function denoFromPlatformCallback( + _callback: PlatformCallback, + _definition: DynamicCallbackDefinition +): T { + throw new SDLError(`${denoFromPlatformCallback.name} not implemented.`); +} + export function denoToPlatformCallback( callback: T, - definition: DynamicCallbackDefinition, + definition: DynamicCallbackDefinition ): PlatformCallback { const callbackInternal = callback as CallbackInternal; if (callbackInternal.denoCallback) { @@ -19,7 +27,7 @@ export function denoToPlatformCallback( // deno-lint-ignore no-explicit-any definition as any, // deno-lint-ignore no-explicit-any - definition.wrap(callback) as any, + definition.wrap(callback) as any ); return callbackInternal.denoCallback.pointer as unknown as PlatformCallback; diff --git a/src/deno/_dataView.ts b/src/deno/_dataView.ts index 423f426..b60a969 100644 --- a/src/deno/_dataView.ts +++ b/src/deno/_dataView.ts @@ -1,11 +1,27 @@ import { Pointer } from "../pointers.ts"; -import { f32, f64, i16, i32, i64, i8, u16, u32, u64, u8 } from "../types.ts"; -import { PlatformPointer } from "../_types.ts"; +import { + Callback, + f32, + f64, + i16, + i32, + i64, + i8, + u16, + u32, + u64, + u8, +} from "../types.ts"; +import { PlatformCallback, PlatformPointer } from "../_types.ts"; import { ENDIANNESS } from "../_utils.ts"; +import { denoFromPlatformCallback } from "./_callbacks.ts"; import { denoFromPlatformPointer, denoToPlatformPointer } from "./_pointers.ts"; +import { DynamicCallbackDefinition } from "../_library.ts"; +import { denoToPlatformCallback } from "./_callbacks.ts"; export class DenoPlatformDataView { - private static DATA_MUST_BE_ARRAY_BUFFER_ERROR = "data must be an instance of ArrayBuffer in order to set values."; + private static DATA_MUST_BE_ARRAY_BUFFER_ERROR = + "data must be an instance of ArrayBuffer in order to set values."; public static LITTLE_ENDIAN = ENDIANNESS === "LE"; @@ -13,18 +29,24 @@ export class DenoPlatformDataView { constructor( public readonly data: Uint8Array | Pointer, - public readonly byteOffset: number = 0, + public readonly byteOffset: number = 0 ) { if (this.data instanceof Uint8Array) { - this._view = new globalThis.DataView(this.data.buffer, this.data.byteOffset, this.data.byteLength); + this._view = new globalThis.DataView( + this.data.buffer, + this.data.byteOffset, + this.data.byteLength + ); } else { - // deno-lint-ignore no-explicit-any - this._view = new Deno.UnsafePointerView(denoToPlatformPointer(this.data) as any); + this._view = new Deno.UnsafePointerView( + // deno-lint-ignore no-explicit-any + denoToPlatformPointer(this.data) as any + ); } } private static ensureViewIsDataView( - view: globalThis.DataView | Deno.UnsafePointerView, + view: globalThis.DataView | Deno.UnsafePointerView ): asserts view is globalThis.DataView { if (!(view instanceof globalThis.DataView)) { throw new Error(DenoPlatformDataView.DATA_MUST_BE_ARRAY_BUFFER_ERROR); @@ -40,12 +62,31 @@ export class DenoPlatformDataView { } } + public getCallback( + byteOffset: number, + definition: DynamicCallbackDefinition + ): T { + return denoFromPlatformCallback( + this._view.getBigUint64( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as unknown as PlatformCallback, + definition + ); + } + public getF32(byteOffset: number): f32 { - return this._view.getFloat32(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as f32; + return this._view.getFloat32( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as f32; } public getF64(byteOffset: number): f64 { - return this._view.getFloat64(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as f64; + return this._view.getFloat64( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as f64; } public getI8(byteOffset: number): i8 { @@ -53,23 +94,35 @@ export class DenoPlatformDataView { } public getI16(byteOffset: number): i16 { - return this._view.getInt16(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as i16; + return this._view.getInt16( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as i16; } public getI32(byteOffset: number): i32 { - return this._view.getInt32(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as i32; + return this._view.getInt32( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as i32; } public getI64(byteOffset: number): i64 { - return this._view.getBigInt64(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as i64; + return this._view.getBigInt64( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as i64; } public getPointer(byteOffset: number): Pointer { // TODO: We should test here if we're on 32 or 64 bit. return denoFromPlatformPointer( Deno.UnsafePointer.create( - this._view.getBigUint64(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN), - ) as unknown as PlatformPointer, + this._view.getBigUint64( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) + ) as unknown as PlatformPointer ) as Pointer; } @@ -78,25 +131,55 @@ export class DenoPlatformDataView { } public getU16(byteOffset: number): u16 { - return this._view.getUint16(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as u16; + return this._view.getUint16( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as u16; } public getU32(byteOffset: number): u32 { - return this._view.getUint32(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as u32; + return this._view.getUint32( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as u32; } public getU64(byteOffset: number): u64 { - return this._view.getBigUint64(this.byteOffset + byteOffset, DenoPlatformDataView.LITTLE_ENDIAN) as u64; + return this._view.getBigUint64( + this.byteOffset + byteOffset, + DenoPlatformDataView.LITTLE_ENDIAN + ) as u64; + } + + public setCallback( + byteOffset: number, + value: T, + definition: DynamicCallbackDefinition + ): void { + DenoPlatformDataView.ensureViewIsDataView(this._view); + this._view.setBigInt64( + byteOffset, + denoToPlatformCallback(value, definition) as unknown as bigint, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setF32(byteOffset: number, value: f32): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setFloat32(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setFloat32( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setF64(byteOffset: number, value: f64): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setFloat64(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setFloat64( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setI8(byteOffset: number, value: i8): void { @@ -106,17 +189,29 @@ export class DenoPlatformDataView { public setI16(byteOffset: number, value: i16): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setInt16(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setInt16( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setI32(byteOffset: number, value: i32): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setInt32(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setInt32( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setI64(byteOffset: number, value: i64): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setBigInt64(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setBigInt64( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setPointer(byteOffset: number, value: Pointer): void { @@ -124,8 +219,14 @@ export class DenoPlatformDataView { // TODO: We should test here if we're on 32 or 64 bit. return this._view.setBigUint64( this.byteOffset + byteOffset, - BigInt(Deno.UnsafePointer.value(denoToPlatformPointer(value) as unknown as NonNullable)), - DenoPlatformDataView.LITTLE_ENDIAN, + BigInt( + Deno.UnsafePointer.value( + denoToPlatformPointer( + value + ) as unknown as NonNullable + ) + ), + DenoPlatformDataView.LITTLE_ENDIAN ); } @@ -136,16 +237,28 @@ export class DenoPlatformDataView { public setU16(byteOffset: number, value: u16): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setUint16(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setUint16( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setU32(byteOffset: number, value: u32): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setUint32(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setUint32( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } public setU64(byteOffset: number, value: u64): void { DenoPlatformDataView.ensureViewIsDataView(this._view); - this._view.setBigUint64(this.byteOffset + byteOffset, value, DenoPlatformDataView.LITTLE_ENDIAN); + this._view.setBigUint64( + this.byteOffset + byteOffset, + value, + DenoPlatformDataView.LITTLE_ENDIAN + ); } } diff --git a/src/deno/_platform.ts b/src/deno/_platform.ts index f94423b..4bdb566 100644 --- a/src/deno/_platform.ts +++ b/src/deno/_platform.ts @@ -1,4 +1,7 @@ -import { denoToPlatformCallback } from "./_callbacks.ts"; +import { + denoFromPlatformCallback, + denoToPlatformCallback, +} from "./_callbacks.ts"; import { DenoPlatformDataView } from "./_dataView.ts"; import { denoLoadLibrary } from "./_library.ts"; import { denoFromPlatformPointer, denoToPlatformPointer } from "./_pointers.ts"; @@ -11,6 +14,7 @@ export default class { public static DataView = DenoPlatformDataView; + public static fromPlatformCallback = denoFromPlatformCallback; public static fromPlatformPointer = denoFromPlatformPointer; public static fromPlatformString = denoFromPlatformString; public static fromPlatformStruct = denoFromPlatformStruct; diff --git a/src/deno/_strings.ts b/src/deno/_strings.ts index f644002..abc6c44 100644 --- a/src/deno/_strings.ts +++ b/src/deno/_strings.ts @@ -1,13 +1,23 @@ import { PlatformPointer, PlatformString } from "../_types.ts"; -export function denoFromPlatformString(value: Uint8Array | PlatformPointer): string { +export function denoFromPlatformString( + value: Uint8Array | PlatformPointer +): string { if (value instanceof Uint8Array) { return new TextDecoder().decode(value); } - return new Deno.UnsafePointerView(value as unknown as NonNullable).getCString(); + return new Deno.UnsafePointerView( + value as unknown as NonNullable + ).getCString(); } -export function denoToPlatformString(value: string): PlatformString { - return Deno.UnsafePointer.of(new TextEncoder().encode(value + "\0")) as unknown as PlatformString; +export function denoToPlatformString(value: string | null): PlatformString { + if (value === null) { + return null as unknown as PlatformString; + } + + return Deno.UnsafePointer.of( + new TextEncoder().encode(value + "\0") + ) as unknown as PlatformString; } diff --git a/tools/codegen-scraper.ts b/tools/codegen-scraper.ts index 92dd4ae..a086252 100644 --- a/tools/codegen-scraper.ts +++ b/tools/codegen-scraper.ts @@ -1,16 +1,21 @@ // This is a blunt tool that will produce output from the SDL headers // that can then be fed directly into the codegen. -import { CodeGenEnums, CodeGenFunctions, CodeGenStructs } from "./codegen/types.ts"; +// clang -Wno-everything $(sdl2-config --cflags --libs) ./tmp/out/codegen-scraper.c -o ./tmp/out/codegen-scraper +// ./tmp/out/codegen-scraper > ./tmp/out/codegen-scraper.txt + +import { + CodeGenEnums, + CodeGenFunctions, + CodeGenStructs, +} from "./codegen/types.ts"; const TMP_PATH = "../tmp"; -const SDL_PATH = `${TMP_PATH}/SDL`; -const KHRONOS_PATH = `${TMP_PATH}/khronos`; +// TODO: This should be read from sdl2-config +const SDL_PATH = `/usr/include/SDL2`; const OUTPUT_PATH = `${TMP_PATH}/out`; -const MACROS_TO_COMPUTE = [ - "SDL_SCANCODE_TO_KEYCODE", -]; +const MACROS_TO_COMPUTE = ["SDL_SCANCODE_TO_KEYCODE"]; let buffer = ""; @@ -42,10 +47,10 @@ function writePrintF(value: string, ...args: string[]): void { async function main(): Promise { writeStartCode(); - for await (const entry of Deno.readDir(`${SDL_PATH}/include`)) { + for await (const entry of Deno.readDir(`${SDL_PATH}`)) { if (entry.name.startsWith("SDL") && entry.name.endsWith(".h")) { writePrintF(`// ${entry.name}`); - await scrapeFile(`${SDL_PATH}/include/${entry.name}`); + await scrapeFile(`${SDL_PATH}/${entry.name}`); writePrintF(""); } } @@ -61,35 +66,42 @@ async function main(): Promise { const cOutputPath = `${OUTPUT_PATH}/codegen-scraper.c`; await Deno.writeTextFile(cOutputPath, buffer); - const exeOutputPath = `${OUTPUT_PATH}/codegen-scraper.exe`; + const sdlConfigCommand = new Deno.Command("sdl2-config", { + args: ["--cflags", "--libs"], + }); + const { stdout: sdlFlags } = await sdlConfigCommand.output(); + console.info(`sdlFlags: ${new TextDecoder().decode(sdlFlags)}`); + + const exeOutputPath = `${OUTPUT_PATH}/codegen-scraper`; const compileCommand = new Deno.Command("clang", { - "args": [ + args: [ + ...new TextDecoder().decode(sdlFlags).split("\n"), cOutputPath, "-o", exeOutputPath, - `-I${SDL_PATH}/include`, - `-I${KHRONOS_PATH}`, - `-L${SDL_PATH}/lib/x64`, - "-Wl,/SUBSYSTEM:CONSOLE", - "-lSDL2main", - "-lSDL2", - "-lShell32", ], }); - const { code: compileCode } = await compileCommand.output(); + const { code: compileCode, stderr: compileError } = + await compileCommand.output(); if (compileCode !== 0) { + console.error("Failed to compile with clang:"); + console.error(new TextDecoder().decode(compileError)); return 1; } - await Deno.copyFile( - `${SDL_PATH}/lib/x64/SDL2.dll`, - `${OUTPUT_PATH}/SDL2.dll`, - ); + // await Deno.copyFile( + // `${SDL_PATH}/lib/x64/SDL2.dll`, + // `${OUTPUT_PATH}/SDL2.dll` + // ); const exeCommand = new Deno.Command(exeOutputPath); - const { code: exeCode, stdout: exeStdout, stderr: exeStderr } = await exeCommand.output(); + const { + code: exeCode, + stdout: exeStdout, + stderr: exeStderr, + } = await exeCommand.output(); if (exeCode !== 0) { console.info(exeStdout); @@ -98,9 +110,15 @@ async function main(): Promise { } await Deno.writeFile(`${OUTPUT_PATH}/codegen-scraper.ts`, exeStdout); - const { enums, functions, structs } = await import(`${OUTPUT_PATH}/codegen-scraper.ts`); + const { enums, functions, structs } = await import( + `${OUTPUT_PATH}/codegen-scraper.ts` + ); - await updateCodeGenInput(enums as CodeGenEnums, functions as CodeGenFunctions, structs as CodeGenStructs); + await updateCodeGenInput( + enums as CodeGenEnums, + functions as CodeGenFunctions, + structs as CodeGenStructs + ); return 0; } @@ -114,7 +132,9 @@ function writeStartCode(): void { write(""); write("int main(int argc, char* args[]) {"); write("SDL_Init(SDL_INIT_VIDEO);"); - writePrintF('import { CodeGenEnums, CodeGenFunctions, CodeGenStructs } from "../../tools/codegen/types.ts";'); + writePrintF( + 'import { CodeGenEnums, CodeGenFunctions, CodeGenStructs } from "../../tools/codegen/types.ts";' + ); writePrintF("export const sizeOfInt = %llu;", "sizeof(int)"); writePrintF("export const enums: CodeGenEnums = {};"); writePrintF("export const functions: CodeGenFunctions = {};"); @@ -176,9 +196,7 @@ async function scrapeFile(filePath: string): Promise { flush(); captureMode = "function"; writeLineNumber(i); - } else if ( - line.startsWith("typedef struct") && !line.endsWith(";") - ) { + } else if (line.startsWith("typedef struct") && !line.endsWith(";")) { flush(); captureMode = "struct"; writeLineNumber(i); @@ -194,18 +212,18 @@ async function scrapeFile(filePath: string): Promise { // outputDefine(capture); shouldFlush = true; } else if ( - captureMode === "enum" && line.startsWith("} ") && + captureMode === "enum" && + line.startsWith("} ") && line.endsWith(";") ) { outputEnum(capture); shouldFlush = true; - } else if ( - captureMode === "function" && line.endsWith(";") - ) { + } else if (captureMode === "function" && line.endsWith(";")) { outputFunction(capture); shouldFlush = true; } else if ( - captureMode === "struct" && line.startsWith("} ") && + captureMode === "struct" && + line.startsWith("} ") && line.endsWith(";") ) { outputStruct(capture); @@ -219,11 +237,10 @@ async function scrapeFile(filePath: string): Promise { } function outputDefine(capture: string): void { - capture = capture - .replaceAll("#define", "") - .replaceAll("\\", ""); + capture = capture.replaceAll("#define", "").replaceAll("\\", ""); - const parts = capture.split(" ") + const parts = capture + .split(" ") .map((x) => x.trim()) .filter((x) => x !== ""); @@ -245,7 +262,8 @@ function outputEnum(capture: string): void { .replaceAll("' '", "_") // These 2 come from SDL_keycode .replaceAll("','", "__"); - const parts = capture.split(/(\,|\s)/) + const parts = capture + .split(/(\,|\s)/) .map((x) => x.trim()) .filter((x) => x !== "") .filter((x) => x !== ","); @@ -257,11 +275,7 @@ function outputEnum(capture: string): void { } if ( - [ - "SDL_KeyCode", - "SDL_PixelFormatEnum", - "SDL_WindowFlags", - ].includes(enumName) + ["SDL_KeyCode", "SDL_PixelFormatEnum", "SDL_WindowFlags"].includes(enumName) ) { return; } @@ -290,7 +304,9 @@ function outputEnum(capture: string): void { if (value !== null) { value = value.replaceAll("\\", "\\\\"); - const computeMacroValue = MACROS_TO_COMPUTE.some((x) => value!.startsWith(x)); + const computeMacroValue = MACROS_TO_COMPUTE.some((x) => + value!.startsWith(x) + ); if (computeMacroValue) { writePrintF(`\t\t${key}: "%d",`, value); @@ -315,7 +331,8 @@ function outputFunction(capture: string): void { .replaceAll("(", " ") .replaceAll(")", " "); - let parts = capture.split(/(\,|\s)/) + let parts = capture + .split(/(\,|\s)/) .map((x) => x.trim()) .filter((x) => x !== "") .filter((x) => x !== ","); @@ -347,7 +364,6 @@ function outputFunction(capture: string): void { "SDL_bsearch", "SDL_CreateShapedWindow", "SDL_hid_open_path", - "SDL_LoadWAV_RW", "SDL_LockMutex", "SDL_Log", "SDL_LogCritical", @@ -423,7 +439,8 @@ function outputStruct(capture: string): void { .replaceAll("unsigned short", "ushort") .replaceAll("char * ", "char* "); - const parts = capture.split(/(\;|\s)/) + const parts = capture + .split(/(\;|\s)/) .map((x) => x.trim()) .filter((x) => x !== "") .filter((x) => x !== ";"); @@ -515,23 +532,22 @@ function stringify(obj: unknown): string { } function sortObjectKeys>(input: T): T { - return Object.keys(input).sort().reduce( - (obj, key) => { + return Object.keys(input) + .sort() + .reduce((obj, key) => { obj[key] = input[key]; return obj; - }, - {} as Record, - ) as T; + }, {} as Record) as T; } async function formatFile(path: string): Promise { - await (new Deno.Command(Deno.execPath(), { "args": ["fmt", path] })).output(); + await new Deno.Command(Deno.execPath(), { args: ["fmt", path] }).output(); } async function updateCodeGenInput( scrapedEnums: CodeGenEnums, scrapedFunctions: CodeGenFunctions, - scrapedStructs: CodeGenStructs, + scrapedStructs: CodeGenStructs ): Promise { await updateCodeGenEnums(scrapedEnums); await updateCodeGenFunctions(scrapedFunctions); @@ -542,20 +558,27 @@ async function updateCodeGenEnums(scrapedEnums: CodeGenEnums): Promise { const { enums } = await import("./codegen/SDL/enums.ts"); for (const [enumName, _enum] of Object.entries(scrapedEnums)) { - if (["SDL_FlashOperation", "SDL_SYSWM_TYPE"].includes(enumName) && enums[enumName] === undefined) { + if ( + ["SDL_FlashOperation", "SDL_SYSWM_TYPE"].includes(enumName) && + enums[enumName] === undefined + ) { enums[enumName] = _enum; } } const output = `import { CodeGenEnums } from "../types.ts"; -export const enums: CodeGenEnums = ${stringify(sortObjectKeys(enums))} as const;`; +export const enums: CodeGenEnums = ${stringify( + sortObjectKeys(enums) + )} as const;`; await Deno.writeTextFile("./codegen/SDL/enums.ts", output); await formatFile("./codegen/SDL/enums.ts"); } -async function updateCodeGenFunctions(scrapedFunctions: CodeGenFunctions): Promise { +async function updateCodeGenFunctions( + scrapedFunctions: CodeGenFunctions +): Promise { const { functions } = await import("./codegen/SDL/functions.ts"); for (const [funcName, func] of Object.entries(scrapedFunctions)) { @@ -570,18 +593,25 @@ async function updateCodeGenFunctions(scrapedFunctions: CodeGenFunctions): Promi const output = `import { CodeGenFunctions } from "../types.ts"; -export const functions: CodeGenFunctions = ${stringify(sortObjectKeys(functions))} as const;`; +export const functions: CodeGenFunctions = ${stringify( + sortObjectKeys(functions) + )} as const;`; await Deno.writeTextFile("./codegen/SDL/functions.ts", output); await formatFile("./codegen/SDL/functions.ts"); } -async function updateCodeGenStructs(scrapedStructs: CodeGenStructs): Promise { +async function updateCodeGenStructs( + scrapedStructs: CodeGenStructs +): Promise { const { structs } = await import("./codegen/SDL/structs.ts"); for (const [structName, struct] of Object.entries(scrapedStructs)) { console.info(structName); - if (["SDL_DisplayMode", "SDL_SysWMinfo"].includes(structName) && structs[structName] === undefined) { + if ( + ["SDL_DisplayMode", "SDL_SysWMinfo"].includes(structName) && + structs[structName] === undefined + ) { structs[structName] = struct; } } @@ -597,7 +627,9 @@ export const opaqueStructs: CodeGenOpaqueStructs = [ "SDL_Window", ]; -export const structs: CodeGenStructs = ${stringify(sortObjectKeys(structs))} as const;`; +export const structs: CodeGenStructs = ${stringify( + sortObjectKeys(structs) + )} as const;`; await Deno.writeTextFile("./codegen/SDL/structs.ts", output); await formatFile("./codegen/SDL/structs.ts"); diff --git a/tools/codegen/SDL.ts b/tools/codegen/SDL.ts index d194ef7..cce3f93 100644 --- a/tools/codegen/SDL.ts +++ b/tools/codegen/SDL.ts @@ -19,11 +19,45 @@ const SDL_SRC_PATH = path.join(SRC_PATH, "SDL"); export async function codegenSDL(): Promise { await writeEnums(`${SDL_SRC_PATH}/enums.ts`, enums, []); - await writeEvents(`${SDL_SRC_PATH}/events.ts`, events, callbacks, enums, structs, opaqueStructs); - await writeStructs(`${SDL_SRC_PATH}/structs.ts`, callbacks, enums, structs, opaqueStructs); - await writeSymbols(`${SDL_SRC_PATH}/_symbols.ts`, functions, callbacks, enums, structs, opaqueStructs); - await writeCallbacksSymbols(`${SDL_SRC_PATH}/_callbacks.ts`, callbacks, enums, structs, opaqueStructs, []); - await writeCallbacks(`${SDL_SRC_PATH}/callbacks.ts`, callbacks, enums, structs, opaqueStructs, []); + await writeEvents( + `${SDL_SRC_PATH}/events.ts`, + events, + callbacks, + enums, + structs, + opaqueStructs + ); + await writeStructs( + `${SDL_SRC_PATH}/structs.ts`, + callbacks, + enums, + structs, + opaqueStructs + ); + await writeSymbols( + `${SDL_SRC_PATH}/_symbols.ts`, + functions, + callbacks, + enums, + structs, + opaqueStructs + ); + await writeCallbacksSymbols( + `${SDL_SRC_PATH}/_callbacks.ts`, + callbacks, + enums, + structs, + opaqueStructs, + [] + ); + await writeCallbacks( + `${SDL_SRC_PATH}/callbacks.ts`, + callbacks, + enums, + structs, + opaqueStructs, + [] + ); await writeFunctions( `${SDL_SRC_PATH}/functions.ts`, "SDL2", @@ -33,8 +67,9 @@ export async function codegenSDL(): Promise { structs, opaqueStructs, [ + `import { AudioDeviceID } from "./audio.ts"`, `import { Event } from "./events.ts";`, - `import { RWMode /*, TimerID */ } from "./types.ts";`, - ], + `import { RWMode } from "./rw.ts";`, + ] ); } diff --git a/tools/codegen/SDL/callbacks.ts b/tools/codegen/SDL/callbacks.ts index 7d379f8..85bd72a 100644 --- a/tools/codegen/SDL/callbacks.ts +++ b/tools/codegen/SDL/callbacks.ts @@ -1,6 +1,23 @@ import { CodeGenCallbacks } from "../types.ts"; export const callbacks: CodeGenCallbacks = { + SDL_AudioCallback: { + parameters: { + userdata: { + type: "void*", + nullable: true, + }, + stream: { + type: "Uint8*", + }, + len: { + type: "int", + }, + }, + result: { + type: "void", + }, + }, SDL_EventFilter: { parameters: { userdata: { diff --git a/tools/codegen/SDL/functions.ts b/tools/codegen/SDL/functions.ts index 4e3b6f4..af20974 100644 --- a/tools/codegen/SDL/functions.ts +++ b/tools/codegen/SDL/functions.ts @@ -77,6 +77,16 @@ export const functions: CodeGenFunctions = { type: "int", }, }, + SDL_CloseAudioDevice: { + parameters: { + dev: { + type: "SDL_AudioDeviceID", + }, + }, + result: { + type: "void", + }, + }, SDL_ConvertSurface: { parameters: { src: { @@ -408,6 +418,16 @@ export const functions: CodeGenFunctions = { type: "int", }, }, + SDL_FreeWAV: { + parameters: { + audio_buf: { + type: "Uint8*", + }, + }, + result: { + type: "void", + }, + }, SDL_FreeSurface: { parameters: { surface: { @@ -978,6 +998,28 @@ export const functions: CodeGenFunctions = { type: "SDL_Surface*", }, }, + SDL_LoadWAV_RW: { + parameters: { + src: { + type: "SDL_RWops*", + }, + freesrc: { + type: "int", + }, + spec: { + type: "SDL_AudioSpec*", + }, + audio_buf: { + type: "Uint8**", + }, + audio_len: { + type: "Uint32*", + }, + }, + result: { + type: "SDL_AudioSpec*", + }, + }, SDL_LockSurface: { parameters: { surface: { @@ -1049,6 +1091,43 @@ export const functions: CodeGenFunctions = { type: "void", }, }, + SDL_OpenAudioDevice: { + parameters: { + device: { + type: "char*", + nullable: true, + }, + iscapture: { + type: "int", + }, + desired: { + type: "SDL_AudioSpec*", + }, + obtained: { + type: "SDL_AudioSpec*", + nullable: true, + }, + allowed_changes: { + type: "int", + }, + }, + result: { + type: "SDL_AudioDeviceID", + }, + }, + SDL_PauseAudioDevice: { + parameters: { + dev: { + type: "SDL_AudioDeviceID", + }, + pause_on: { + type: "int", + }, + }, + result: { + type: "void", + }, + }, SDL_PollEvent: { parameters: { event: { @@ -1089,6 +1168,22 @@ export const functions: CodeGenFunctions = { type: "void", }, }, + SDL_QueueAudio: { + parameters: { + dev: { + type: "SDL_AudioDeviceID", + }, + data: { + type: "void*", + }, + len: { + type: "Uint32", + }, + }, + result: { + type: "int", + }, + }, SDL_RWFromFile: { parameters: { file: { diff --git a/tools/codegen/SDL/structs.ts b/tools/codegen/SDL/structs.ts index 1d01419..cf40a84 100644 --- a/tools/codegen/SDL/structs.ts +++ b/tools/codegen/SDL/structs.ts @@ -10,6 +10,50 @@ export const opaqueStructs: CodeGenOpaqueStructs = [ ]; export const structs: CodeGenStructs = { + SDL_AudioSpec: { + allocatable: true, + mutable: true, + size: 32, + members: { + freq: { + type: "int", + offset: 0, + }, + format: { + type: "SDL_AudioFormat", + offset: 4, + }, + channels: { + type: "Uint8", + offset: 6, + }, + silence: { + type: "Uint8", + offset: 7, + }, + samples: { + type: "Uint16", + offset: 8, + }, + padding: { + type: "Uint16", + offset: 10, + }, + size: { + type: "Uint32", + offset: 12, + }, + callback: { + todo: "Doesn't seem to work due to background thread.", + type: "SDL_AudioCallback", + offset: 16, + }, + userdata: { + type: "void*", + offset: 24, + }, + }, + }, SDL_Color: { allocatable: true, mutable: true, diff --git a/tools/codegen/generators.ts b/tools/codegen/generators.ts index da93a09..867065a 100644 --- a/tools/codegen/generators.ts +++ b/tools/codegen/generators.ts @@ -13,34 +13,47 @@ import { CodeGenStructs, } from "./types.ts"; -const PlatformDataViewGetMethods: Record string> = { - "f32": (offset, _) => `getF32(${offset})`, - "f64": (offset, _) => `getF64(${offset})`, - "i8": (offset, _) => `getI8(${offset})`, - "i16": (offset, _) => `getI16(${offset})`, - "i32": (offset, _) => `getI32(${offset})`, - "i64": (offset, _) => `getI64(${offset})`, - "u8": (offset, _) => `getU8(${offset})`, - "u16": (offset, _) => `getU16(${offset})`, - "u32": (offset, _) => `getU32(${offset})`, - "u64": (offset, _) => `getU64(${offset})`, - - "pointer": (offset, _) => `getPointer(${offset})`, - - "struct": (offset, length) => `getArray(${length}, ${offset})`, +const PlatformDataViewGetMethods: Record< + string, + (offset: number, infoParam: number | string) => string +> = { + f32: (offset, _) => `getF32(${offset})`, + f64: (offset, _) => `getF64(${offset})`, + i8: (offset, _) => `getI8(${offset})`, + i16: (offset, _) => `getI16(${offset})`, + i32: (offset, _) => `getI32(${offset})`, + i64: (offset, _) => `getI64(${offset})`, + u8: (offset, _) => `getU8(${offset})`, + u16: (offset, _) => `getU16(${offset})`, + u32: (offset, _) => `getU32(${offset})`, + u64: (offset, _) => `getU64(${offset})`, + + function: (offset, name) => `getCallback(${offset}, callbacks["${name}"])`, + + pointer: (offset, _) => `getPointer(${offset})`, + + struct: (offset, length) => `getArray(${length}, ${offset})`, } as const; -const PlatformDataViewSetMethods: Record string> = { - "f32": (offset, _) => `setF32(${offset}, value)`, - "f64": (offset, _) => `setF64(${offset}, value)`, - "i8": (offset, _) => `setI8(${offset}, value)`, - "i16": (offset, _) => `setI16(${offset}, value)`, - "i32": (offset, _) => `setI32(${offset}, value)`, - "i64": (offset, _) => `setI64(${offset}, value)`, - "u8": (offset, _) => `setU8(${offset}, value)`, - "u16": (offset, _) => `setU16(${offset}, value)`, - "u32": (offset, _) => `setU32(${offset}, value)`, - "u64": (offset, _) => `setU64(${offset}, value)`, +const PlatformDataViewSetMethods: Record< + string, + (offset: number, infoParam: number | string) => string +> = { + f32: (offset, _) => `setF32(${offset}, value)`, + f64: (offset, _) => `setF64(${offset}, value)`, + i8: (offset, _) => `setI8(${offset}, value)`, + i16: (offset, _) => `setI16(${offset}, value)`, + i32: (offset, _) => `setI32(${offset}, value)`, + i64: (offset, _) => `setI64(${offset}, value)`, + u8: (offset, _) => `setU8(${offset}, value)`, + u16: (offset, _) => `setU16(${offset}, value)`, + u32: (offset, _) => `setU32(${offset}, value)`, + u64: (offset, _) => `setU64(${offset}, value)`, + + function: (offset, name) => + `setCallback(${offset}, value, callbacks["${name}"])`, + + pointer: (offset, _) => `setPointer(${offset}, value)`, } as const; function createLines(): string[] { @@ -92,7 +105,7 @@ function stripPrefixes(value: string, ...prefixes: string[]): string { async function writeLinesToFile(path: string, lines: string[]): Promise { await Deno.writeTextFile(path, lines.join("\n")); - await (new Deno.Command(Deno.execPath(), { "args": ["fmt", path] })).output(); + await new Deno.Command(Deno.execPath(), { args: ["fmt", path] }).output(); } function mapEnumValue(value: string, enumData: CodeGenEnum): string { @@ -125,7 +138,7 @@ function mapEnumValue(value: string, enumData: CodeGenEnum): string { export async function writeEnums( filePath: string, enums: CodeGenEnums, - imports: string[], + imports: string[] ): Promise { const lines = createLines(); @@ -147,15 +160,21 @@ export async function writeEnums( enumValueName = "_" + enumValueName; } - lines.push(`\t${enumValueName} = ${mapEnumValue(enumData.values[key], enumData)},`); + lines.push( + `\t${enumValueName} = ${mapEnumValue(enumData.values[key], enumData)},` + ); } lines.push("} as const;"); lines.push(""); if (enumName.endsWith("Flags")) { - lines.push(`export type ${strippedEnumName} = Flags;`); + lines.push( + `export type ${strippedEnumName} = Flags;` + ); } else { - lines.push(`export type ${strippedEnumName} = Enum;`); + lines.push( + `export type ${strippedEnumName} = Enum;` + ); } lines.push(""); @@ -180,7 +199,11 @@ function isEnum(enums: CodeGenEnums, type: string): boolean { return false; } -function isStruct(structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, type: string): boolean { +function isStruct( + structs: CodeGenStructs, + opaqueStructs: CodeGenOpaqueStructs, + type: string +): boolean { for (const structName of Object.keys(structs)) { if (structName === type) { return true; @@ -196,7 +219,11 @@ function isStruct(structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, return false; } -function isStructPointer(structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, type: string): boolean { +function isStructPointer( + structs: CodeGenStructs, + opaqueStructs: CodeGenOpaqueStructs, + type: string +): boolean { if (!type.endsWith("*")) { return false; } @@ -213,7 +240,7 @@ function mapTypeToFFIType( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - type: string, + type: string ): string { if (type.endsWith("*")) { return "pointer"; @@ -227,11 +254,22 @@ function mapTypeToFFIType( const struct = structs[type]; if (!struct) { - throw new Error(`Failed to map "${type}" in ${mapTypeToFFIType.name}. Type seems to be an opaque struct.`); + throw new Error( + `Failed to map "${type}" in ${mapTypeToFFIType.name}. Type seems to be an opaque struct.` + ); } const members = Object.values(struct.members) - .map((member) => `"${mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, member.type)}"`) + .map( + (member) => + `"${mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + member.type + )}"` + ) .join(", "); return `{ "struct": [ ${members} ] }`; @@ -242,6 +280,12 @@ function mapTypeToFFIType( } switch (type) { + case "SDL_AudioDeviceID": + return "u32"; + + case "SDL_AudioFormat": + return "u16"; + case "SDL_bool": return "bool"; @@ -287,15 +331,21 @@ function mapStructMemberType( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - member: CodeGenStructMember, + member: CodeGenStructMember ): string { if (member.overrideType) { return member.overrideType; } + if (isCallback(callbacks, member.type)) { + return stripPrefixes(member.type); + } + if (isEnum(enums, member.type)) { const enumData = enums[member.type]; - return enumData?.prefixToStrip ? stripPrefixes(member.type, enumData.prefixToStrip) : stripPrefixes(member.type); + return enumData?.prefixToStrip + ? stripPrefixes(member.type, enumData.prefixToStrip) + : stripPrefixes(member.type); } if (isStruct(structs, opaqueStructs, member.type)) { @@ -314,19 +364,18 @@ function mapStructMemberType( return "Pointer"; } - const ffiType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, member.type); - - switch (ffiType) { - case "pointer": - return "Deno.PointerValue"; - } - - return ffiType; + return mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + member.type + ); } function sortStructMembers( a: [string, { type: string; offset: number }], - b: [string, { type: string; offset: number }], + b: [string, { type: string; offset: number }] ): number { const offsetDiff = a[1].offset - b[1].offset; @@ -343,7 +392,7 @@ export async function writeEvents( callbacks: CodeGenCallbacks, enums: CodeGenEnums, structs: CodeGenStructs, - opaqueStructs: CodeGenOpaqueStructs, + opaqueStructs: CodeGenOpaqueStructs ): Promise { const lines = createLines(); @@ -357,13 +406,16 @@ import { Pointer } from "../pointers.ts"; `); const eventPropName = (eventName: string, event: CodeGenEventType) => - event.unionName ?? stripPrefixes(eventName).slice(0, -"Event".length).toLowerCase(); + event.unionName ?? + stripPrefixes(eventName).slice(0, -"Event".length).toLowerCase(); for (const [eventName, event] of Object.entries(events)) { const className = stripPrefixes(eventName); lines.push(`export class ${className} {`); - const subStructMembers = Object.entries(event.members).filter((x) => isStruct(structs, opaqueStructs, x[1].type)); + const subStructMembers = Object.entries(event.members).filter((x) => + isStruct(structs, opaqueStructs, x[1].type) + ); if (subStructMembers.length > 0) { for (const [memberName, member] of subStructMembers) { @@ -381,7 +433,9 @@ import { Pointer } from "../pointers.ts"; for (const [memberName, member] of subStructMembers) { const memberTypeName = stripPrefixes(member.type); - lines.push(`this._${memberName} = ${memberTypeName}.of(this._data, ${member.offset}) as ${memberTypeName};`); + lines.push( + `this._${memberName} = ${memberTypeName}.of(this._data, ${member.offset}) as ${memberTypeName};` + ); } lines.push("\t}"); @@ -394,19 +448,31 @@ import { Pointer } from "../pointers.ts"; continue; } - const type = mapStructMemberType(callbacks, enums, structs, opaqueStructs, member); + const type = mapStructMemberType( + callbacks, + enums, + structs, + opaqueStructs, + member + ); lines.push(`\tpublic get ${memberName}(): ${type} {`); if (isStruct(structs, opaqueStructs, member.type)) { lines.push(`return this._${memberName};`); } else { - const ffiType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, member.type); + const ffiType = mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + member.type + ); const PlatformDataViewMethod = PlatformDataViewGetMethods[ffiType]; if (PlatformDataViewMethod === undefined) { console.error( - `PlatformDataViewMethods is missing ${ffiType}.`, + `PlatformDataViewMethods is missing "${ffiType}" member "${memberName}".` ); } @@ -415,12 +481,10 @@ import { Pointer } from "../pointers.ts"; const length = 0; lines.push( - `\t\treturn this._view.${ - PlatformDataViewGetMethods[ffiType]( - member.offset, - length, - ) - }${asEnum};`, + `\t\treturn this._view.${PlatformDataViewGetMethods[ffiType]( + member.offset, + length + )}${asEnum};` ); } lines.push("\t}"); @@ -440,7 +504,11 @@ import { Pointer } from "../pointers.ts"; `); for (const [eventName, event] of Object.entries(events)) { - lines.push(`public readonly ${eventPropName(eventName, event)}: ${stripPrefixes(eventName)};`); + lines.push( + `public readonly ${eventPropName(eventName, event)}: ${stripPrefixes( + eventName + )};` + ); } lines.push(` @@ -453,7 +521,11 @@ import { Pointer } from "../pointers.ts"; `); for (const [eventName, event] of Object.entries(events)) { - lines.push(`this.${eventPropName(eventName, event)} = new ${stripPrefixes(eventName)}(this._data, this._view);`); + lines.push( + `this.${eventPropName(eventName, event)} = new ${stripPrefixes( + eventName + )}(this._data, this._view);` + ); } lines.push(` @@ -487,22 +559,32 @@ export async function writeStructs( callbacks: CodeGenCallbacks, enums: CodeGenEnums, structs: CodeGenStructs, - opaqueStructs: CodeGenOpaqueStructs, + opaqueStructs: CodeGenOpaqueStructs ): Promise { const lines = createLines(); lines.push("// deno-lint-ignore-file no-unused-vars"); lines.push(""); lines.push(`import Platform from "../_platform.ts";`); + lines.push(`import { callbacks } from "./_callbacks.ts";`); lines.push(`import { PlatformDataView } from "../_types.ts";`); lines.push(`import { isTypedArray } from "../_utils.ts";`); lines.push(`import { Pointer } from "../pointers.ts";`); lines.push( - `import { AllocatableStruct, f32, f64, i16, i32, i64, i8, Struct, u16, u32, u64, u8 } from "../types.ts";`, + `import { AllocatableStruct, f32, f64, i16, i32, i64, i8, Struct, u16, u32, u64, u8 } from "../types.ts";` ); lines.push(""); - const enumNames = Object.keys(enums).map((x) => stripPrefixes(x)).join(", "); + const callbackNames = Object.entries(callbacks) + .filter(([_, value]) => !value.todo) + .map(([key, _]) => stripPrefixes(key)) + .join(", "); + lines.push(`import { ${callbackNames} } from "./callbacks.ts";`); + lines.push(""); + + const enumNames = Object.keys(enums) + .map((x) => stripPrefixes(x)) + .join(", "); lines.push(`import { ${enumNames} } from "./enums.ts";`); lines.push(""); @@ -539,7 +621,9 @@ export async function writeStructs( for (const [structName, struct] of Object.entries(structs)) { const className = stripPrefixes(structName); - const implementsExpression = struct.allocatable ? " implements AllocatableStruct" : " implements Struct"; + const implementsExpression = struct.allocatable + ? " implements AllocatableStruct" + : " implements Struct"; lines.push(`export class ${className}${implementsExpression} { public static SIZE_IN_BYTES = ${struct.size}; @@ -550,15 +634,26 @@ export async function writeStructs( lines.push(`public readonly _data: Uint8Array | Pointer<${className}>; public readonly _view: PlatformDataView; + constructor(); constructor( data: Uint8Array | Pointer<${className}>, byteOffset: number, ); constructor(props: Partial<${className}>);`); - const constructorParams = Object.entries(struct.members).map(([memberName, member]) => - `${memberName}: ${mapStructMemberType(callbacks, enums, structs, opaqueStructs, member)}` - ).join(", "); + const constructorParams = Object.entries(struct.members) + .map( + ([memberName, member]) => + (member.todo ? "// " : "") + + `${memberName}: ${mapStructMemberType( + callbacks, + enums, + structs, + opaqueStructs, + member + )}` + ) + .join(",\n"); lines.push(`constructor(${constructorParams});`); const firstMemberType = mapStructMemberType( @@ -566,33 +661,54 @@ export async function writeStructs( enums, structs, opaqueStructs, - Object.values(struct.members)[0], + Object.values(struct.members)[0] ); const secondMemberType = mapStructMemberType( callbacks, enums, structs, opaqueStructs, - Object.values(struct.members)[1], + Object.values(struct.members)[1] ); - const otherMembers = Object.values(struct.members).slice(2).map((member, index) => - `_${index + 3}?: ${mapStructMemberType(callbacks, enums, structs, opaqueStructs, member)}` - ).join(",\n"); + const otherMembers = Object.values(struct.members) + .slice(2) + .map( + (member, index) => + (member.todo ? "// " : "") + + `_${index + 3}?: ${mapStructMemberType( + callbacks, + enums, + structs, + opaqueStructs, + member + )}` + ) + .join(",\n"); lines.push( `constructor( - _1?: Uint8Array | Pointer<${className}> | Partial<${className}> | ${firstMemberType}, + _1: Uint8Array | Pointer<${className}> | Partial<${className}> | ${firstMemberType} = {}, _2?: number | ${secondMemberType}, ${otherMembers} - ) {`, + ) {` ); - const assignMemmbersFromObject = Object.keys(struct.members).map((memberName) => - `if (_1.${memberName} !== undefined) this.${memberName} = _1.${memberName};` - ).join("\n"); - - const assignMemmbersFromParameters = Object.keys(struct.members).map((memberName, index) => - `if (_${index + 1} !== undefined) this.${memberName} = _${index + 1};` - ).join("\n"); + const assignMemmbersFromObject = Object.entries(struct.members) + .map( + ([memberName, member]) => + (member.todo ? "// " : "") + + `if (_1.${memberName} !== undefined) this.${memberName} = _1.${memberName};` + ) + .join("\n"); + + const assignMemmbersFromParameters = Object.entries(struct.members) + .map( + ([memberName, member], index) => + (member.todo ? "// " : "") + + `if (_${index + 1} !== undefined) this.${memberName} = _${ + index + 1 + };` + ) + .join("\n"); lines.push(` const dataPassedIn = isTypedArray(_1) || Pointer.isPointer(_1); @@ -662,14 +778,42 @@ export async function writeStructs( continue; } - const memberFFIType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, member.type); + const memberFFIType = mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + member.type + ); + + const getMethod = isStruct(structs, opaqueStructs, member.type) + ? PlatformDataViewGetMethods["struct"] + : PlatformDataViewGetMethods[memberFFIType]; + + if (getMethod === undefined) { + console.error( + `Failure while generating "${structName}" member "${memberName}": PlatformDataViewGetMethods is missing ${memberFFIType}.` + ); + lines.push(`// ERROR: ${memberName}`); + lines.push(""); + continue; + } + let readOp = ""; let writeOp = ""; - let length = 0; - let memberType = mapStructMemberType(callbacks, enums, structs, opaqueStructs, member); + let lengthOrName: number | string = 0; + let memberType = mapStructMemberType( + callbacks, + enums, + structs, + opaqueStructs, + member + ); let memberStructName = ""; - if (memberType === "string") { + if (isCallback(callbacks, member.type)) { + lengthOrName = member.type; + } else if (memberType === "string") { readOp += `Platform.fromPlatformString(Platform.toPlatformPointer(`; } else if (isStructPointer(structs, opaqueStructs, member.type)) { memberStructName = stripPrefixes(stripPointerPostfix(member.type)); @@ -682,25 +826,14 @@ export async function writeStructs( memberStructName = stripPrefixes(member.type); memberType = memberStructName; readOp += `${memberStructName}.of(`; - length = structs[member.type].size; + lengthOrName = structs[member.type].size; } lines.push(`\tpublic get ${memberName}(): ${memberType} {`); - const getMethod = isStruct(structs, opaqueStructs, member.type) - ? PlatformDataViewGetMethods["struct"] - : PlatformDataViewGetMethods[memberFFIType]; - - if (getMethod === undefined) { - console.error( - `Failure while generating ${structName}: PlatformDataViewGetMethods is missing ${memberFFIType}.`, - ); - } - - readOp += `this._view.${getMethod(member.offset, length)}`; + readOp += `this._view.${getMethod(member.offset, lengthOrName)}`; - // FIXME: isEnum expects the type to be prefixed with SDL_ but mapStructMemberType chops it off already. - if (isEnum(enums, "SDL_" + memberType)) { + if (isEnum(enums, member.type)) { readOp += `as ${memberType}`; } else if (memberType === "string") { readOp += ")!)"; @@ -716,8 +849,8 @@ export async function writeStructs( lines.push(""); if (struct.mutable) { - // TODO: Can we write to pointers / structs? - if (memberFFIType === "pointer" || memberFFIType === "struct") { + // TODO: Can we write to structs? + if (memberFFIType === "struct") { continue; } @@ -727,11 +860,11 @@ export async function writeStructs( if (setMethod === undefined) { console.error( - `PlatformDataViewSetMethods is missing ${memberFFIType}.`, + `PlatformDataViewSetMethods is missing ${memberFFIType}.` ); } - writeOp += `this._view.${setMethod(member.offset, length)}`; + writeOp += `this._view.${setMethod(member.offset, lengthOrName)}`; // if (mapTypeToFFIType(member.type) === "pointer" || mapTypeToFFIType(member.type) === "struct") { // writeOp += ")"; @@ -757,13 +890,19 @@ export function writeSymbolParametersAndResult( callbacks: CodeGenCallbacks, enums: CodeGenEnums, structs: CodeGenStructs, - opaqueStructs: CodeGenOpaqueStructs, + opaqueStructs: CodeGenOpaqueStructs ): void { lines.push(`\t\tparameters: [`); for (const paramName of Object.keys(symbol.parameters)) { const param = symbol.parameters[paramName]; - const ffiType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, param.type); + const ffiType = mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + param.type + ); let line = `\t\t\t/* ${param.type} ${paramName} */ `; if (ffiType.startsWith("{")) { @@ -777,7 +916,13 @@ export function writeSymbolParametersAndResult( lines.push("\t\t],"); - const resultFFIType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, symbol.result.type); + const resultFFIType = mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + symbol.result.type + ); let resultLine = `\t\tresult: /* ${symbol.result.type} */ `; if (resultFFIType.startsWith("{")) { @@ -795,7 +940,7 @@ export async function writeSymbols( callbacks: CodeGenCallbacks, enums: CodeGenEnums, structs: CodeGenStructs, - opaqueStructs: CodeGenOpaqueStructs, + opaqueStructs: CodeGenOpaqueStructs ): Promise { const lines = createLines(); @@ -813,7 +958,14 @@ export async function writeSymbols( lines.push(`\t${funcName}: {`); } - writeSymbolParametersAndResult(lines, func, callbacks, enums, structs, opaqueStructs); + writeSymbolParametersAndResult( + lines, + func, + callbacks, + enums, + structs, + opaqueStructs + ); lines.push("\t},"); } @@ -823,7 +975,10 @@ export async function writeSymbols( await writeLinesToFile(filePath, lines); } -function isFunctionParamCallback(callbacks: CodeGenCallbacks, param: CodeGenFunctionParam): boolean { +function isFunctionParamCallback( + callbacks: CodeGenCallbacks, + param: CodeGenFunctionParam +): boolean { let callbackName = param.type; while (callbackName.endsWith("*")) { @@ -833,7 +988,10 @@ function isFunctionParamCallback(callbacks: CodeGenCallbacks, param: CodeGenFunc return Object.keys(callbacks).includes(callbackName); } -function isFunctionParamOpaqueStruct(opaqueStructs: CodeGenOpaqueStructs, param: CodeGenFunctionParam): boolean { +function isFunctionParamOpaqueStruct( + opaqueStructs: CodeGenOpaqueStructs, + param: CodeGenFunctionParam +): boolean { let structName = param.type; while (structName.endsWith("*")) { @@ -843,7 +1001,10 @@ function isFunctionParamOpaqueStruct(opaqueStructs: CodeGenOpaqueStructs, param: return opaqueStructs.includes(structName); } -function isFunctionParamStruct(structs: CodeGenStructs, param: CodeGenFunctionParam): boolean { +function isFunctionParamStruct( + structs: CodeGenStructs, + param: CodeGenFunctionParam +): boolean { let structName = param.type; while (structName.endsWith("*")) { @@ -857,7 +1018,10 @@ function isFunctionParamStruct(structs: CodeGenStructs, param: CodeGenFunctionPa return Object.keys(structs).includes(structName); } -function isFunctionParamStructByValue(structs: CodeGenStructs, param: CodeGenFunctionParam): boolean { +function isFunctionParamStructByValue( + structs: CodeGenStructs, + param: CodeGenFunctionParam +): boolean { if (!isFunctionParamStruct(structs, param)) { return false; } @@ -886,9 +1050,7 @@ function isFunctionParamString(param: CodeGenFunctionParam): boolean { } function isFunctionParamBigInt(param: CodeGenFunctionParam): boolean { - return [ - "Uint64", - ].includes(param.type); + return ["Uint64"].includes(param.type); } function mapFunctionReturnType( @@ -896,9 +1058,16 @@ function mapFunctionReturnType( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - param: CodeGenFunctionParam, + param: CodeGenFunctionParam ): string { - return mapFunctionParamType(callbacks, enums, structs, opaqueStructs, param, true); + return mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + param, + true + ); } function mapFunctionParamType( @@ -907,7 +1076,7 @@ function mapFunctionParamType( structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, param: CodeGenFunctionParam, - isReturnType = false, + isReturnType = false ): string { if (param.overrideType) { if (param.nullable) { @@ -921,7 +1090,10 @@ function mapFunctionParamType( return stripPrefixes(param.type); } - if (isFunctionParamOpaqueStruct(opaqueStructs, param) || isFunctionParamStruct(structs, param)) { + if ( + isFunctionParamOpaqueStruct(opaqueStructs, param) || + isFunctionParamStruct(structs, param) + ) { let structName = param.type.substring("SDL_".length); if (structName.endsWith("**")) { @@ -945,13 +1117,20 @@ function mapFunctionParamType( if (isEnum(enums, param.type)) { const enumData = enums[param.type]; - return enumData?.prefixToStrip ? stripPrefixes(param.type, enumData.prefixToStrip) : stripPrefixes(param.type); + return enumData?.prefixToStrip + ? stripPrefixes(param.type, enumData.prefixToStrip) + : stripPrefixes(param.type); } - if (param.type.endsWith("*") && isEnum(enums, param.type.substring(0, param.type.length - 1))) { + if ( + param.type.endsWith("*") && + isEnum(enums, param.type.substring(0, param.type.length - 1)) + ) { const enumName = param.type.substring(0, param.type.length - 1); const enumData = enums[enumName]; - const result = enumData?.prefixToStrip ? stripPrefixes(enumName, enumData.prefixToStrip) : stripPrefixes(enumName); + const result = enumData?.prefixToStrip + ? stripPrefixes(enumName, enumData.prefixToStrip) + : stripPrefixes(enumName); return isReturnType ? `PointerValue<${result}>` : `PointerLike<${result}>`; } @@ -983,6 +1162,10 @@ function mapFunctionParamType( result = isReturnType ? "Pointer" : "PointerLike"; break; + case "Uint8**": + result = isReturnType ? "Pointer>" : "Box>"; + break; + case "Uint16*": result = isReturnType ? "Pointer" : "PointerLike"; break; @@ -1004,10 +1187,25 @@ function mapFunctionParamType( return result; } - const ffiType = mapTypeToFFIType(callbacks, enums, structs, opaqueStructs, param.type); + if (param.type.startsWith("SDL_")) { + result = stripPrefixes(param.type); + } + + const ffiType = mapTypeToFFIType( + callbacks, + enums, + structs, + opaqueStructs, + param.type + ); + switch (ffiType) { case "pointer": - throw new Error(`Unable to map param in ${mapFunctionParamType.name}: ${JSON.stringify(param)}`); + throw new Error( + `Unable to map param in ${mapFunctionParamType.name}: ${JSON.stringify( + param + )}` + ); } if (result === "") { @@ -1033,15 +1231,18 @@ function getGenericParam(type: string): string { function getReturnTypePostfix( structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - result: CodeGenFunctionResult, + result: CodeGenFunctionResult ): string { - return isFunctionParamOpaqueStruct(opaqueStructs, result) || isFunctionParamStruct(structs, result) ? "| null" : ""; + return isFunctionParamOpaqueStruct(opaqueStructs, result) || + isFunctionParamStruct(structs, result) + ? "| null" + : ""; } function writeImportAllStructs( lines: string[], structs: CodeGenStructs, - opaqueStructs: CodeGenOpaqueStructs, + opaqueStructs: CodeGenOpaqueStructs ): void { const structNames = Object.entries(structs) .filter((x) => !x[1].doNotImport) @@ -1053,13 +1254,16 @@ function writeImportAllStructs( lines.push(`import { ${structNames} } from "./structs.ts";`); } -function writeImportAllCallbacks(lines: string[], callbacks: CodeGenCallbacks): void { - lines.push(`import { ${ - Object.entries(callbacks) +function writeImportAllCallbacks( + lines: string[], + callbacks: CodeGenCallbacks +): void { + lines.push( + `import { ${Object.entries(callbacks) .filter(([_, value]) => !value.todo) .map(([key, _]) => stripPrefixes(key)) - .join(", ") - } } from "./callbacks.ts";`); + .join(", ")} } from "./callbacks.ts";` + ); } export async function writeCallbacksSymbols( @@ -1068,7 +1272,7 @@ export async function writeCallbacksSymbols( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - imports: string[], + imports: string[] ): Promise { const lines = createLines(); lines.push(`// deno-lint-ignore-file no-unused-vars @@ -1076,7 +1280,7 @@ export async function writeCallbacksSymbols( import Platform from "../_platform.ts"; import { PlatformPointer } from "../_types.ts"; import { Event } from "./events.ts"; -import { i32, u32 } from "../types.ts"; +import { i32, u8, u32 } from "../types.ts"; `); writeImportAllCallbacks(lines, callbacks); @@ -1100,22 +1304,44 @@ import { i32, u32 } from "../types.ts"; lines.push(`\t${callbackName}: {`); - writeSymbolParametersAndResult(lines, callback, callbacks, enums, structs, opaqueStructs); - - const returnType = mapFunctionReturnType(callbacks, enums, structs, opaqueStructs, callback.result); + writeSymbolParametersAndResult( + lines, + callback, + callbacks, + enums, + structs, + opaqueStructs + ); + + const returnType = mapFunctionReturnType( + callbacks, + enums, + structs, + opaqueStructs, + callback.result + ); lines.push(`\t\twrap: (callback: ${stripPrefixes(callbackName)}) => {`); let platformParams = ""; for (const [paramName, param] of Object.entries(callback.parameters)) { platformParams += `${paramName}: `; - const paramType = mapFunctionParamType(callbacks, enums, structs, opaqueStructs, param); + const paramType = mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + param + ); if (isFunctionParamString(param)) { platformParams += "PlatformString"; } else if (isFunctionParamVoidPointer(param)) { platformParams += `PlatformPointer`; } else if (isFunctionParamPointer(param)) { - platformParams += paramType.replaceAll("PointerLike", "PlatformPointer"); + platformParams += paramType.replaceAll( + "PointerLike", + "PlatformPointer" + ); } else { platformParams += paramType; } @@ -1132,7 +1358,13 @@ import { i32, u32 } from "../types.ts"; lines.push(`\t\t\t\treturn callback(`); for (const [paramName, param] of Object.entries(callback.parameters)) { - const paramType = mapFunctionParamType(callbacks, enums, structs, opaqueStructs, param); + const paramType = mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + param + ); if (isFunctionParamString(param)) { lines.push(`\t\tPlatform.fromPlatformString(${paramName})`); @@ -1140,7 +1372,11 @@ import { i32, u32 } from "../types.ts"; isFunctionParamOpaqueStruct(opaqueStructs, param) || isFunctionParamStruct(structs, param) ) { - lines.push(`\t\t${getGenericParam(paramType)}.of(Platform.fromPlatformPointer(${paramName}))`); + lines.push( + `\t\t${getGenericParam( + paramType + )}.of(Platform.fromPlatformPointer(${paramName}))` + ); } else if (isFunctionParamPointer(param)) { lines.push(`\t\tPlatform.fromPlatformPointer(${paramName})`); } else { @@ -1166,13 +1402,13 @@ export async function writeCallbacks( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - imports: string[], + imports: string[] ): Promise { const lines = createLines(); lines.push(`// deno-lint-ignore-file no-unused-vars import { Pointer } from "../pointers.ts"; -import { Callback, i32, u32 } from "../types.ts"; +import { Callback, i32, u8, u32 } from "../types.ts"; import { Event } from "./events.ts"; `); @@ -1191,7 +1427,13 @@ import { Event } from "./events.ts"; lines.push("("); for (const [paramName, param] of Object.entries(callback.parameters)) { - let paramType = mapFunctionParamType(callbacks, enums, structs, opaqueStructs, param); + let paramType = mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + param + ); if ( isFunctionParamOpaqueStruct(opaqueStructs, param) || @@ -1205,7 +1447,13 @@ import { Event } from "./events.ts"; lines.push(`${paramName}: ${paramType},`); } - const returnType = mapFunctionReturnType(callbacks, enums, structs, opaqueStructs, callback.result); + const returnType = mapFunctionReturnType( + callbacks, + enums, + structs, + opaqueStructs, + callback.result + ); lines.push(`) => ${returnType}`); lines.push(") & Callback;"); @@ -1223,7 +1471,7 @@ export async function writeFunctions( enums: CodeGenEnums, structs: CodeGenStructs, opaqueStructs: CodeGenOpaqueStructs, - imports: string[], + imports: string[] ): Promise { const lines = createLines(); lines.push(`// deno-lint-ignore-file no-unused-vars @@ -1241,7 +1489,9 @@ import { symbols } from "./_symbols.ts"; writeImportAllCallbacks(lines, callbacks); - const enumNames = Object.keys(enums).map((x) => stripPrefixes(x)).join(", "); + const enumNames = Object.keys(enums) + .map((x) => stripPrefixes(x)) + .join(", "); lines.push(`import { ${enumNames} } from "./enums.ts";`); writeImportAllStructs(lines, structs, opaqueStructs); @@ -1273,7 +1523,7 @@ export function Init(flags: InitFlags | number, options?: InitOptions): number { lines.push( `const symbolsToLoad = options?.functions ? getSymbolsFromFunctions(symbols, options.functions) : symbols; - _library = Platform.loadLibrary("${libraryName}", symbolsToLoad, options?.libraryPath);`, + _library = Platform.loadLibrary("${libraryName}", symbolsToLoad, options?.libraryPath);` ); if (Object.values(func.parameters).length >= 1) { @@ -1290,38 +1540,69 @@ export function Init(flags: InitFlags | number, options?: InitOptions): number { }`); } else { for (const overload of func.overloads ?? []) { - const returnType = mapFunctionReturnType(callbacks, enums, structs, opaqueStructs, { - ...func.result, - ...overload.result, - }); + const returnType = mapFunctionReturnType( + callbacks, + enums, + structs, + opaqueStructs, + { + ...func.result, + ...overload.result, + } + ); lines.push(`export function ${stripPrefixes(funcName)}(`); for (const [paramName, param] of Object.entries(func.parameters)) { lines.push( - `${paramName}: ${ - mapFunctionParamType(callbacks, enums, structs, opaqueStructs, { + `${paramName}: ${mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + { ...param, ...overload?.parameters?.[paramName], - }) - },`, + } + )},` ); } - const returnTypePostfix = getReturnTypePostfix(structs, opaqueStructs, { ...func.result, ...overload.result }); + const returnTypePostfix = getReturnTypePostfix(structs, opaqueStructs, { + ...func.result, + ...overload.result, + }); lines.push(`): ${returnType}${returnTypePostfix};`); } - const returnType = mapFunctionReturnType(callbacks, enums, structs, opaqueStructs, func.result); + const returnType = mapFunctionReturnType( + callbacks, + enums, + structs, + opaqueStructs, + func.result + ); lines.push(`export function ${stripPrefixes(funcName)}(`); for (const [paramName, param] of Object.entries(func.parameters)) { - lines.push(`${paramName}: ${mapFunctionParamType(callbacks, enums, structs, opaqueStructs, param)},`); + lines.push( + `${paramName}: ${mapFunctionParamType( + callbacks, + enums, + structs, + opaqueStructs, + param + )},` + ); } - const returnTypePostfix = getReturnTypePostfix(structs, opaqueStructs, func.result); + const returnTypePostfix = getReturnTypePostfix( + structs, + opaqueStructs, + func.result + ); lines.push(`): ${returnType}${returnTypePostfix} {`); @@ -1356,23 +1637,31 @@ export function Init(flags: InitFlags | number, options?: InitOptions): number { if (isFunctionParamString(param)) { lines.push(`\t\tPlatform.toPlatformString(${paramName}),`); } else if (isFunctionParamVoidPointer(param)) { - lines.push(`\t\tPlatform.toPlatformPointer(Pointer.of(${paramName})),`); + lines.push( + `\t\tPlatform.toPlatformPointer(Pointer.of(${paramName})),` + ); } else if (isFunctionParamDoublePointer(param)) { - lines.push(`\t\tPlatform.toPlatformPointer(Pointer.ofTypedArray(${paramName}._data)),`); + lines.push( + `\t\tPlatform.toPlatformPointer(Pointer.ofTypedArray(${paramName}._data)),` + ); } else if (isFunctionParamCallback(callbacks, param)) { lines.push( - `\t\tPlatform.toPlatformCallback(${paramName}, callbacks["${param.type}"]),`, + `\t\tPlatform.toPlatformCallback(${paramName}, callbacks["${param.type}"]),` ); } else if (isFunctionParamStructByValue(structs, param)) { lines.push( - `\t\tPlatform.toPlatformStruct(${paramName}, ${stripPrefixes(param.type)}),`, + `\t\tPlatform.toPlatformStruct(${paramName}, ${stripPrefixes( + param.type + )}),` ); } else if ( isFunctionParamPointer(param) || isFunctionParamOpaqueStruct(opaqueStructs, param) || isFunctionParamStruct(structs, param) ) { - lines.push(`\t\tPlatform.toPlatformPointer(Pointer.of(${paramName})),`); + lines.push( + `\t\tPlatform.toPlatformPointer(Pointer.of(${paramName})),` + ); } else { lines.push(`\t\t${paramName},`); } @@ -1390,7 +1679,11 @@ export function Init(flags: InitFlags | number, options?: InitOptions): number { lines.push(`\t) as PlatformPointer<${returnType}>));`); } else if (isFunctionParamPointer(func.result)) { const nonNullAssertion = !func.result.nullable ? "!" : ""; - lines.push(`\t) as PlatformPointer<${getGenericParam(returnType)}>)${nonNullAssertion};`); + lines.push( + `\t) as PlatformPointer<${getGenericParam( + returnType + )}>)${nonNullAssertion};` + ); } else if (returnType === "bigint") { lines.push(`\t) as unknown as ${returnType};`); } else { @@ -1403,7 +1696,11 @@ export function Init(flags: InitFlags | number, options?: InitOptions): number { lines.push("}"); } - lines.push(`${stripPrefixes(funcName)}.symbolName = "${func.symbolName ? func.symbolName : funcName}";`); + lines.push( + `${stripPrefixes(funcName)}.symbolName = "${ + func.symbolName ? func.symbolName : funcName + }";` + ); lines.push(""); }