Skip to content

Platform, player and web

The player program, the platform layer (desktop and browser), the browser player, data packs and static SDL linking.

Files: player.odin, platform_native.odin (#+build !js), platform_web.odin (#+build js), player_web.odin (#+build js), pack.odin (#+build !js), sdl_static.odin (#+build linux, darwin, windows).

Platform services, desktop version (Linux, macOS, Windows). platform_web.odin has the same procedures for the browser (Emscripten), where core:os, virtual memory, SDL3_image and SDL3_mixer aren’t available. Code elsewhere calls these instead of those packages directly.

Platform services, browser version: a WebAssembly build linked by Emscripten (the editor’s Web export). Same procedures as platform_native.odin, without core:os (unsupported on js/wasm), virtual memory (there is none) and SDL3_image (no Emscripten build of it yet):

  • files are read through SDL, which reads Emscripten’s virtual file system (the exported game’s data is packaged into it)
  • arenas grow from the context allocator, which Web_Context makes Emscripten’s malloc
  • images: PNG and BMP, with SDL’s own loaders
  • audio: SDL3_mixer, built with Emscripten, as on the desktop (audio.odin)

The player in a browser. The editor’s Web export builds the game’s player (the same main package as a desktop build) for js_wasm32 with -build-mode:obj, and links it with Emscripten:

emcc <engine>/web/main.c game.wasm.o libSDL3_mixer.a -o index.html -sUSE_SDL=3 -sUSE_SDL_TTF=3
-sALLOW_MEMORY_GROWTH --js-library <engine>/web/odin_env.js
--pre-js <engine>/web/odin_env_pre.js --preload-file <project>@/data

Emscripten starts at C main (web/main.c), which calls odin_web_main below; the player’s own main isn’t used. The browser can’t run a blocking loop, so instead of Application_Run, Emscripten calls Application_Frame once per animation frame.

  • Memory: Emscripten owns the wasm heap, so the allocator is its malloc (Web_Context).
  • Files: the project is packaged into Emscripten’s virtual file system under /data.
  • web/odin_env.js and web/odin_env_pre.js supply what Odin’s js runtime imports from the “odin_env” module (console output, random bytes, some math).
  • Audio: SDL3_mixer, built for the web by the editor (tools/build_web_mixer.sh: Emscripten has no SDL3_mixer port). Browsers start sound only after the player clicks or presses a key.
  • Limits: images load with SDL’s built-in PNG and BMP loaders (no SDL_image port exists).

The data pack: a game’s files (project.xml, its scenes, its assets) appended to the end of its own executable, so an exported game is one file. Executables ignore what follows them (ELF on Linux, PE on Windows; macOS games keep their files in the .app instead: appending would break its code signature).

At start, the player looks at the end of its executable (Pack_Open). With a pack there, the project is PACK_ROOT and every file the engine reads under it comes from the pack: the platform layer’s Platform_ReadFile / Platform_FileExists / Platform_LoadTexture, sounds and music (Platform_OpenIO), fonts. A file is read from the executable the first time it’s asked for and kept: SDL keeps reading music and fonts as they play, from that memory.

Layout (little-endian), after the program:

the files' bytes, one after another
the index: per file, u32 path length, the path ("/"-separated, relative to the project),
u64 offset (from the start of the executable), u64 size
the footer (32 bytes): u64 index offset, u64 index size, u64 pack start, PACK_MAGIC (8 bytes)

Pack_Append writes one (the editor’s desktop exports use it).

Example

files := []string{"project.xml", "scenes/Main.scene.xml", "assets/ship.png"}
ok := e.Pack_Append("export/linux_x64/MyGame", "my_project", files)

Static SDL: when tools/build_sdl_static.sh has built SDL3, SDL3_image, SDL3_ttf and SDL3_mixer for this OS and CPU (sdl/lib/<os>_<arch>/), every program that imports the engine links them instead of the system’s shared libraries. An exported game is then one executable that needs only libc (and loads the OS’s window system, graphics and audio at run time, as SDL always does).

How: Odin’s vendor:sdl3 bindings ask the linker for -lSDL3 (and _image, _ttf, _mixer). The linker takes the first folder in its search path that has a match, so -L to a folder of only static archives wins over the system’s .so files. The archives are also listed again here, so symbols SDL3_ttf, _image and _mixer need from SDL3 resolve whatever order the -l flags came in.

Windows: the vendor bindings name Odin’s own SDL3.lib (and SDL3_image.lib…), import libraries of the DLLs, by path: no search path can replace them. But MSVC’s linker resolves a symbol from the first library on its command line that has it, and Odin orders foreign imports by priority_index first: this file’s, -1, come before the bindings’ (0), so the static libraries define every SDL function and the import libraries go unused (the game doesn’t load SDL3.dll). The static libraries need Windows’ own: user32, gdi32, winmm, imm32, ole32, oleaut32, version, uuid, advapi32, setupapi, shell32.

-define:SDL_STATIC=false links the system’s shared libraries (the DLLs on Windows) even when the static ones are built.

PlayerOptions :: struct {
// The project folder ("." when none was given)
projectDir: string,
// Play from this scene instead of the project's start scene ("" for the start scene)
scene: string,
// Print the registered scripts and exit
describe: bool,
// Draw the physics shapes, whatever the project says
physicsDebug: bool,
// Developer keys (`Game.devKeys`); the editor's Play passes it, exports never run with it
dev: bool,
// `--live`: answer an editor's live view on stdin / stdout (live.odin); the editor's Play
live: bool,
// A project folder was named on the command line (otherwise an exported game finds its own)
folderGiven: bool,
// `--renderer <name>`: draw with this one, whatever the project says ("" for the project's)
renderer: string,
// `--quit-after <seconds>`: quit once the game has run that long (0: run until it quits);
// CI starts exported games with it to check they start and stop cleanly
quitAfter: f64,
// `--audio-device <name>`: play sound on this device ("" for the system's default)
audioDevice: string,
// `--list-audio-devices`: print the playback devices' names and exit
listAudioDevices: bool,
// `--restore <file>`: start where a snapshot left off (its scene, globals, nodes: hot_reload.odin)
restore: string,
}

What the player was asked to do (Player_ParseArgs)

struct. Source: player.odin:189

MemoryArena :: vmem.Arena

A growing memory arena: nodes, scenes and the game each own one (virtual memory on the desktop)

type, only in builds with #+build !js. Source: platform_native.odin:23

MemoryArena :: runtime.Arena

A growing memory arena: nodes, scenes and the game each own one (blocks from the context allocator in the browser)

type, only in builds with #+build js. Source: platform_web.odin:25

PackEntry :: struct {
// Byte offset from the start of the executable, and length in bytes
offset, size: u64,
}

A file in a pack: where its bytes are in the executable

struct, only in builds with #+build !js. Source: pack.odin:44

Pack :: struct {
// An open was tried (`Pack_Open` only tries once)
opened: bool,
// The file the pack is in, nil when there's no pack
file: ^os.File,
// The index: each file's path (relative to the project) and where it is (heap-allocated)
entries: map[string]PackEntry,
// The files read so far, by path (heap-allocated, kept until `Pack_CloseFile`)
loaded: map[string][]byte,
}

An open pack: a file’s index, and the files read from it so far (kept: SDL streams music and fonts from them). The game’s is gamePack (Pack_Open); tests and tools open their own.

struct, only in builds with #+build !js. Source: pack.odin:53

Player_Run :: proc(args: []string) -> int

The game player: runs a project saved by the editor.

player an exported game: play the project next to the program
(its data/ folder), else the current folder
player <project folder> play it, from its start scene
player <project folder> --scene <name> play it from another scene (the editor's Play Scene)
player ... --physics-debug draw every physics shape, whatever the project says
player ... --dev developer keys (the editor's Play): F9 shows / hides shapes, F10 the debug overlay
player ... --renderer <name> draw with this renderer (vulkan, opengl, software, ...),
whatever the project says
player ... --quit-after <seconds> quit after that long (smoke tests of exported games)
player ... --audio-device <name> play sound on that device (the editor's Settings for Play)
player --describe-scripts print the registered scripts as JSON, and exit
player --list-audio-devices print the playback devices' names, one a line, and exit

Player_Run is the whole program. The engine’s own player/ package calls it with no game code; a project with scripts gets its own player, generated by the editor, that imports the project’s scripts package (which registers its scripts from @(init) procedures) and calls it:

package main
import "core:os"
import e "engine:."
import scripts "project:scripts"
_ :: scripts
main :: proc() {
os.exit(e.Player_Run(os.args))
}

Inputs

  • args: The command line, program name first

Returns

  • The exit code: 0 after a normal quit (or --describe-scripts), 1 if the command line is wrong or the project couldn’t be loaded or started

procedure. Source: player.odin:47

Player_Setup :: proc(options: PlayerOptions, app: ^Application) -> bool

Loads the project options names and opens its window, with the start scene (or options.scene) queued in app. Shared by Player_Run and the browser’s Player_StartWeb; the caller then runs frames and finally calls Application_Cleanup.

Inputs

  • options: What to play (Player_ParseArgs)
  • app: The application to set up (zero-initialized)

Returns

  • false (logged) if the project or the window couldn’t be set up

procedure. Source: player.odin:89

Player_FindExportedProject :: proc() -> string

Where an exported game’s project is: the data pack at the end of the program (a single-file export: PACK_ROOT), else the data folder next to the program (how the editor’s desktop exports lay a game out), or the program’s own folder, or a macOS app’s Contents/Resources/data (the program is in Contents/MacOS), or else the current folder. Asset paths are relative to it, so the game runs from wherever it’s started.

Returns

  • The folder (temp allocated)

procedure. Source: player.odin:227

Player_ParseArgs :: proc(args: []string) -> (PlayerOptions, bool)

Reads the player’s command line: [&lt;project folder>] [--scene &lt;name>] [--physics-debug] [--dev] [--renderer &lt;name>] [--audio-device &lt;name>] [--quit-after &lt;seconds>], or --describe-scripts, or --list-audio-devices (see Player_Run). The folder defaults to the current one; flags may come in any order.

Inputs

  • args: The command line, program name first

Returns

  • The options (strings point into args), and false if the command line isn’t valid (a second folder, or --scene / --renderer without a value)

Example

options, ok := e.Player_ParseArgs({"player", "mygame", "--scene", "Level2"})
if ok do log.infof("playing %s from %s", options.projectDir, options.scene)

procedure. Source: player.odin:258

MemoryArena_InitGrowing :: proc(arena: ^MemoryArena) -> bool

Sets up a growing arena, which reserves more memory as it fills.

Inputs

  • arena: The arena to set up

Returns

  • false if the memory couldn’t be reserved

procedure, only in builds with #+build !js. Source: platform_native.odin:34

MemoryArena_Allocator :: proc(arena: ^MemoryArena) -> runtime.Allocator

An allocator that allocates from the arena. Its memory is freed all at once by MemoryArena_Destroy; individual frees do nothing.

Inputs

Returns

  • The arena’s allocator

procedure, only in builds with #+build !js. Source: platform_native.odin:48

MemoryArena_Destroy :: proc(arena: ^MemoryArena)

Frees all of the arena’s memory.

Inputs

  • arena: The arena

procedure, only in builds with #+build !js. Source: platform_native.odin:58

MemoryArena_BootstrapNew :: proc($T: typeid, offset: uintptr, blockSize: uint) -> (^T, bool)

Allocates a T inside a new arena that it owns, at offset within T (the arena frees the T with everything else). Used for nodes.

Inputs

  • T: The type to allocate
  • offset: Byte offset of the MemoryArena field inside T (offset_of)
  • blockSize: Size in bytes of the arena’s memory blocks

Returns

  • The new T (zeroed) and true, or nil and false if the memory couldn’t be reserved

procedure, only in builds with #+build !js. Source: platform_native.odin:74

Platform_ReadFile :: proc(path: string) -> ([]byte, bool)

Reads a whole file, from the data pack if it’s there, else from disk.

Inputs

  • path: The file’s path

Returns

  • The contents (temp allocated, or the data pack’s memory: don’t free them) and true; false if it can’t be read

procedure, only in builds with #+build !js. Source: platform_native.odin:89

Platform_FileExists :: proc(path: string) -> bool

Whether a file exists, in the data pack or on disk.

Inputs

  • path: The file’s path

Returns

  • true if it exists

procedure, only in builds with #+build !js. Source: platform_native.odin:104

Platform_ExecutableDir :: proc() -> (string, bool)

The folder the program is in.

Returns

  • The folder’s path (temp allocated) and true; false if it couldn’t be found

procedure, only in builds with #+build !js. Source: platform_native.odin:114

Platform_LoadTexture :: proc(renderer: ^sdl.Renderer, path: string) -> ^sdl.Texture

Loads an image file as a texture: any format SDL3_image reads. Reads the data pack first, then the disk.

Most code loads through ResourceManager (loadTexture), which caches the texture.

Inputs

  • renderer: The renderer the texture is for
  • path: The image’s path

Returns

  • The texture (the caller destroys it), or nil if it can’t be read (SDL’s error says why)

procedure, only in builds with #+build !js. Source: platform_native.odin:132

Platform_LoadSurface :: proc(path: string) -> ^sdl.Surface

Reads an image file into a surface (any format SDL_image reads), from the data pack first.

Inputs

  • path: The file

Returns

  • The surface (the caller’s), or nil

procedure, only in builds with #+build !js. Source: platform_native.odin:148

Platform_DefaultContext :: proc "contextless" () -> runtime.Context

A context for contextless code (@(init) procedures): Odin’s default

procedure, only in builds with #+build !js. Source: platform_native.odin:156

Player_Main :: proc()

The whole player program: Player_Run with the command line, exiting with its code. A player’s main is just this call, so the same main builds for the desktop and the browser (where this does nothing: the page starts the game through odin_web_main).

procedure, only in builds with #+build !js. Source: platform_native.odin:165

Platform_SaveWrite :: proc(name: string, data: []byte) -> bool

Writes a save. On the desktop, <SDL's per-user folder for the game>/saves/<slot>.json, written to a temporary file first and then moved over the old save, so a crash mid-write can’t leave half a save. Use Save_Write (save.odin) rather than calling this directly.

Inputs

  • name: “<game>/<slot>”
  • data: The save’s contents

Returns

  • false if it couldn’t be written

procedure, only in builds with #+build !js. Source: platform_native.odin:203

Platform_SaveRead :: proc(name: string) -> ([]byte, bool)

Reads a save written by Platform_SaveWrite.

Inputs

  • name: “<game>/<slot>”

Returns

  • The contents (temp allocated) and true; false if there’s no such save or it can’t be read

procedure, only in builds with #+build !js. Source: platform_native.odin:221

Platform_SaveExists :: proc(name: string) -> bool

Whether a save exists.

Inputs

  • name: “<game>/<slot>”

Returns

  • true if it exists

procedure, only in builds with #+build !js. Source: platform_native.odin:237

Platform_SaveDelete :: proc(name: string)

Deletes a save, if it exists.

Inputs

  • name: “<game>/<slot>”

procedure, only in builds with #+build !js. Source: platform_native.odin:248

Platform_SetWindowIcon :: proc(window: ^sdl.Window, data: []byte) -> bool

Gives a window an icon from an image file’s bytes (PNG, JPEG…: what SDL_image reads).

Inputs

  • window: The window
  • data: The image file’s contents; nil for the engine’s default icon (the Crossfire logo)

Returns

  • false when the image can’t be read (logged)

procedure, only in builds with #+build !js. Source: platform_native.odin:266

Platform_WriteFile :: proc(path: string, data: []byte) -> bool

Writes a file (a hot reload’s snapshot: hot_reload.odin). Not the data pack: files the game makes.

Returns

  • false when it couldn’t be written

procedure, only in builds with #+build !js. Source: platform_native.odin:283

MemoryArena_InitGrowing :: proc(arena: ^MemoryArena) -> bool

Sets up a growing arena, which adds WEB_ARENA_BLOCK_SIZE blocks as it fills.

Inputs

  • arena: The arena to set up

Returns

  • false if the memory couldn’t be allocated

procedure, only in builds with #+build js. Source: platform_web.odin:39

MemoryArena_Allocator :: proc(arena: ^MemoryArena) -> runtime.Allocator

An allocator that allocates from the arena. Its memory is freed all at once by MemoryArena_Destroy; individual frees do nothing.

Inputs

Returns

  • The arena’s allocator

procedure, only in builds with #+build js. Source: platform_web.odin:53

MemoryArena_Destroy :: proc(arena: ^MemoryArena)

Frees all of the arena’s memory.

Inputs

  • arena: The arena

procedure, only in builds with #+build js. Source: platform_web.odin:63

MemoryArena_BootstrapNew :: proc($T: typeid, offset: uintptr, blockSize: uint) -> (^T, bool)

Allocates a T inside a new arena that it owns, at offset within T (the arena frees the T with everything else). Used for nodes.

Inputs

  • T: The type to allocate
  • offset: Byte offset of the MemoryArena field inside T (offset_of)
  • blockSize: Size in bytes of the arena’s memory blocks

Returns

  • The new T (zeroed) and true, or nil and false if the memory couldn’t be allocated

procedure, only in builds with #+build js. Source: platform_web.odin:79

Platform_ReadFile :: proc(path: string) -> ([]byte, bool)

Reads a whole file from Emscripten’s virtual file system (through SDL).

Inputs

  • path: The file’s path

Returns

  • The contents (temp allocated) and true; false if it can’t be read

procedure, only in builds with #+build js. Source: platform_web.odin:101

Pack_Open :: proc() -> bool

No data pack in the browser: always false

procedure, only in builds with #+build js. Source: platform_web.odin:115

Pack_Bytes :: proc(path: string) -> ([]byte, bool)

A file in the game’s data pack. The browser has no data pack.

Inputs

  • path: The file’s path (ignored)

Returns

  • Always nil, false

procedure, only in builds with #+build js. Source: platform_web.odin:125

Pack_Has :: proc(path: string) -> bool

Whether the game’s data pack has a file. The browser has no data pack.

Inputs

  • path: The file’s path (ignored)

Returns

  • Always false

procedure, only in builds with #+build js. Source: platform_web.odin:136

Platform_FileExists :: proc(path: string) -> bool

Whether a file exists in Emscripten’s virtual file system.

Inputs

  • path: The file’s path

Returns

  • true if it exists

procedure, only in builds with #+build js. Source: platform_web.odin:147

Platform_ExecutableDir :: proc() -> (string, bool)

The packaged game is at the root of the virtual file system

procedure, only in builds with #+build js. Source: platform_web.odin:152

Platform_LoadTexture :: proc(renderer: ^sdl.Renderer, path: string) -> ^sdl.Texture

Loads an image file as a texture. The browser build reads PNG and BMP only (SDL’s own loaders).

Most code loads through ResourceManager (loadTexture), which caches the texture.

Inputs

  • renderer: The renderer the texture is for
  • path: The image’s path

Returns

  • The texture (the caller destroys it), or nil (logged) if it can’t be read

procedure, only in builds with #+build js. Source: platform_web.odin:168

Platform_LoadSurface :: proc(path: string) -> ^sdl.Surface

Reads an image file into a surface: PNG or BMP in the browser.

Inputs

  • path: The file

Returns

  • The surface (the caller’s), or nil

procedure, only in builds with #+build js. Source: platform_web.odin:188

odin_storage_set :: proc(key: [^]u8, keyLength: i32, value: [^]u8, valueLength: i32) -> i32

Stores a value under a key; nonzero on success

procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:200

odin_storage_size :: proc(key: [^]u8, keyLength: i32) -> i32

The value’s size in bytes, or -1 if there’s none

procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:202

odin_storage_get :: proc(key: [^]u8, keyLength: i32, buffer: [^]u8, capacity: i32) -> i32

Copies the value into the buffer; the bytes copied, or -1 if there’s none

procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:204

odin_storage_remove :: proc(key: [^]u8, keyLength: i32)

Removes the key’s value

procedure, a foreign (C) procedure, only in builds with #+build js. Source: platform_web.odin:206

Platform_SaveWrite :: proc(name: string, data: []byte) -> bool

Writes a save: in the browser, to localStorage under “<game>/<slot>”. Use Save_Write (save.odin) rather than calling this directly.

Inputs

  • name: “<game>/<slot>”
  • data: The save’s contents

Returns

  • false if it couldn’t be stored

procedure, only in builds with #+build js. Source: platform_web.odin:220

Platform_SaveRead :: proc(name: string) -> ([]byte, bool)

Reads a save written by Platform_SaveWrite.

Inputs

  • name: “<game>/<slot>”

Returns

  • The contents (temp allocated) and true; false if there’s no such save

procedure, only in builds with #+build js. Source: platform_web.odin:233

Platform_SaveExists :: proc(name: string) -> bool

Whether a save exists.

Inputs

  • name: “<game>/<slot>”

Returns

  • true if it exists

procedure, only in builds with #+build js. Source: platform_web.odin:250

Platform_SaveDelete :: proc(name: string)

Deletes a save, if it exists.

Inputs

  • name: “<game>/<slot>”

procedure, only in builds with #+build js. Source: platform_web.odin:260

Platform_SetWindowIcon :: proc(window: ^sdl.Window, data: []byte) -> bool

Browsers show the page’s favicon: games’ window icons do nothing there

procedure, only in builds with #+build js. Source: platform_web.odin:265

Platform_WriteFile :: proc(path: string, data: []byte) -> bool

Writes a file: the browser has none to write (false)

procedure, only in builds with #+build js. Source: platform_web.odin:270

odin_web_main :: proc "c" ()

The web build’s entry point, called from web/main.c: runs the game’s @(init) procedures (script registration) and starts the project packaged under /data.

procedure, only in builds with #+build js. Source: player_web.odin:54

Player_Main :: proc()

Does nothing in the browser: a player’s main isn’t called there (Emscripten calls C main, which calls odin_web_main). On the desktop it runs the player; see platform_native.odin.

procedure, only in builds with #+build js. Source: player_web.odin:64

Platform_DefaultContext :: proc "contextless" () -> runtime.Context

A context for contextless code (@(init) procedures): Web_Context, so it allocates from Emscripten’s heap

procedure, only in builds with #+build js. Source: player_web.odin:74

Web_Context :: proc "contextless" () -> runtime.Context

A context for code running in the browser: Emscripten’s malloc as the allocator, a temporary arena (freed each frame), and a logger that writes to the browser console. Call at the start of every entry point the page calls into (context = e.Web_Context()).

procedure, only in builds with #+build js. Source: player_web.odin:83

Player_StartWeb :: proc(scene := "")

Starts the game in the browser: loads the packaged project and hands Emscripten the frame callback. Returns at once; the game keeps running from the browser’s animation frames.

Inputs

  • scene: Start in this scene instead of the project’s start scene (“” for the start scene)

procedure, only in builds with #+build js. Source: player_web.odin:102

Pack_Open :: proc() -> bool

Opens the pack at the end of the running executable, if it has one (once; later calls say whether it did).

Returns

  • Whether there’s a pack (paths under PACK_ROOT then read from it)

procedure, only in builds with #+build !js. Source: pack.odin:75

Pack_OpenFile :: proc(p: ^Pack, path: string) -> bool

Opens the pack at the end of a file into p (closing what it had). Pack_Open does it with the running executable; tools and tests open their own. Close it with Pack_CloseFile.

Inputs

  • p: The pack to open into
  • path: The file (an executable with a pack appended)

Returns

  • Whether the file ends in a pack

Example

pack: e.Pack
defer e.Pack_CloseFile(&pack)
if e.Pack_OpenFile(&pack, "export/linux_x64/MyGame") {
data, ok := e.Pack_FileBytes(&pack, "project.xml")
if ok do log.infof("project.xml: %d bytes", len(data))
}

procedure, only in builds with #+build !js. Source: pack.odin:101

Pack_CloseFile :: proc(p: ^Pack)

Closes a pack and frees what was read from it (slices from Pack_FileBytes become invalid).

Inputs

  • p: The pack (a closed or zero one is fine)

procedure, only in builds with #+build !js. Source: pack.odin:148

Pack_Bytes :: proc(path: string) -> ([]byte, bool)

A file in the game’s pack: its bytes (kept for the program’s life: don’t free them), or false when there’s no pack or no such file. Opens the pack on first use (Pack_Open).

Inputs

  • path: Under PACK_ROOT (“<pack>/assets/ship.png”) or relative to it (“assets/ship.png”); absolute paths are never in the pack

Returns

  • The bytes and true, or nil and false

procedure, only in builds with #+build !js. Source: pack.odin:172

Pack_Has :: proc(path: string) -> bool

Whether the game’s pack has a file, without reading it. Opens the pack on first use (Pack_Open).

Inputs

Returns

  • true when there’s a pack and it has the file

procedure, only in builds with #+build !js. Source: pack.odin:186

Pack_FileBytes :: proc(p: ^Pack, path: string) -> ([]byte, bool)

A file in an open pack (Pack_Bytes for the game’s own pack). The first call reads it from disk; later ones return the same bytes.

Inputs

Returns

  • The bytes (the pack’s: valid until Pack_CloseFile, don’t free them) and true, or nil and false when the pack isn’t open, has no such file, or it can’t be read

procedure, only in builds with #+build !js. Source: pack.odin:202

Pack_FileHas :: proc(p: ^Pack, path: string) -> bool

Whether an open pack has a file, without reading it.

Inputs

Returns

  • true when the pack is open and has the file

procedure, only in builds with #+build !js. Source: pack.odin:228

Pack_Append :: proc(executable, root: string, files: []string, extra: map[string]string = nil) -> string

Appends a pack of a project’s files to an executable (a desktop export’s last step).

Inputs

  • executable: The built game
  • root: The folder the files are relative to
  • files: Their paths under it, “/”-separated (they’re found in the pack by these)
  • extra: More files given by content (an export’s own project.xml), by path; they win over files

Returns

  • “” or what went wrong

procedure, only in builds with #+build !js. Source: pack.odin:246

PLATFORM_WEB :: false

Whether this is the browser build (false here; true in platform_web.odin)

constant, only in builds with #+build !js. Source: platform_native.odin:20

PLATFORM_WEB :: true

Whether this is the browser build (true here; false in platform_native.odin)

constant, only in builds with #+build js. Source: platform_web.odin:22

WEB_ARENA_BLOCK_SIZE :: 16 * 1024

Block size of web arenas; they grow by adding blocks

constant, only in builds with #+build js. Source: platform_web.odin:28

PACK_ROOT :: "<pack>"

The data pack is the desktop exports’ (pack.odin): the browser preloads files instead. The project folder of a packed game: paths under it are the pack’s

constant, only in builds with #+build js. Source: platform_web.odin:113

WEB_PROJECT_DIR :: "/data"

Where the Web export packages the project

constant, only in builds with #+build js. Source: player_web.odin:33

PACK_ROOT :: "<pack>"

The project folder of a packed game: paths under it are the pack’s

constant, only in builds with #+build !js. Source: pack.odin:36

PACK_MAGIC :: "ODINPAK1"

The footer’s last 8 bytes

constant, only in builds with #+build !js. Source: pack.odin:38

SDL_STATIC :: #config(SDL_STATIC, true)

Link the static SDL libraries when they’re built (false: always the system’s shared ones)

constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:28

SDL_STATIC_LIBS :: "sdl/lib/" + ODIN_OS_STRING + "_" + ODIN_ARCH_STRING

Where tools/build_sdl_static.sh puts this platform’s static SDL: relative to the engine (foreign imports take that) and absolute (for the linker’s -L, quoted: the linker runs through a shell, and the engine may be in a folder with a space, like a macOS app bundle’s)

constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:33

SDL_STATIC_DIR :: #directory + SDL_STATIC_LIBS

The same folder as an absolute path (for the linker’s -L)

constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:35

SDL_LINKED_STATICALLY :: SDL_STATIC && (#exists(SDL_STATIC_LIBS + "/libSDL3.a") || #exists(SDL_STATIC_LIBS + "/sdl3_static.lib"))

Whether this build links SDL statically

constant, only in builds with #+build linux, darwin, windows. Source: sdl_static.odin:38