feat: FFI / C-ABI surface + Python bindings (#302) #316
No reviewers
Labels
No labels
area/ci-release
area/cli
area/docs
area/dungeon
area/ftl
area/graphics
area/gui
area/music
area/saves
area/tooling
port/amiga
port/dos
port/fm-towns
port/mac
port/pc98
port/sega-cd
priority/high
priority/low
priority/medium
type/bug
type/chore
type/docs
type/feature
type/research
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jqueuniet/dm2-tools!316
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/ffi-bindings"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #302 — a C ABI over the parser crates plus a pip-installable Python
consumer. The native counterpart to the
dm2-wasmbrowser bindings (#301):same thin-marshalling-layer philosophy, different boundary.
What's here
Three layers, four commits:
dm2-convert(new, pure, wasm-free) — the 14 conversion functions andthe unified error, extracted out of
dm2-wasmso both binding crates canshare them without either pulling in the other's toolchain.
dm2-wasmisnow a re-export shim over it; its public API (
dm2_wasm::core::*,dm2_wasm::WasmError) is unchanged and it still builds forwasm32-unknown-unknown. Two new functions land here:resfork_list_jsonand
resfork_extract— exposing the Mac resource-fork walker the issueflagged as useful beyond DM2.
dm2-ffi(new) — acdylib/staticlib/rlibwith a single statelessreturn convention:
Dm2Buffer { data, len, error }returned by value,error == NULL⇒ success, onedm2_buffer_freeper buffer. 16 conversionexports +
dm2_buffer_free+dm2_version. Every export is wrapped incatch_unwindso no panic can unwind across the C boundary. The C header(
crates/dm2-ffi/include/dm2_tools.h) is generated by cbindgen andcommitted, with a drift-check test that fails if it goes stale.
bindings/python/(new) — a Pythonic ctypes wrapper: JSON functionstake/return
dict, binary functions returnbytes, failures raiseDm2Error. A hatchling build hook builds and bundles the cdylib sopip install ./bindings/pythonyields a working wheel on the builder'splatform.
API surface
Stateless throughout — every function is bytes-in → bytes/JSON/bool-out, no
handles cross the boundary.
dm2_save_to_json/dm2_save_from_json(byte-exact round-trip),dm2_save_info_jsondm2_dungeon_to_json/dm2_dungeon_from_json(byte-exact round-trip),dm2_dungeon_skproject_jsondm2_graphics_header_json,dm2_graphics_decode_pngdm2_music_detect,dm2_music_hmp_to_smf,dm2_music_render_mod_wav,dm2_music_snd_to_wavdm2_ftl_info_json,dm2_ftl_roundtrip_okdm2_resfork_list_json,dm2_resfork_extractByte order for saves/dungeon is auto-detected in
dm2-convertby reusing thecrates' own try-both-endians predicates.
Proof
dm2-convert: 9 native tests (save + dungeon byte-exact round-trips throughthe JSON text; graphics/music/FTL read smokes; resfork list + extract +
bad-id error).
dm2-ffi: round-trip byte-exact through the FFI buffers, the error pathreturns a non-null
errorwith nulldata,dm2_versionnon-empty, and thecommitted header matches cbindgen output (drift test).
dm2-wasmstill builds forwasm32-unknown-unknownunchanged (theextraction is inert to the browser side).
cur_hpsurvivingreconstruct) plus the graphics/FTL/resfork reads, all against present
fixtures.
Scope / follow-ups
scope — the repo has no CI;
pip installbuilds the cdylib locally.separate effort).
built here.
The
.idea/dm2-tools.imlsource-folder entries the IDE auto-generates for thenew crates were intentionally left out of the commits.
https://claude.ai/code/session_01NvHsv9fmmZ7EmFnk7tBZLT
Stateless Dm2Buffer{data,len,error} return convention with one dm2_buffer_free, 16 panic-guarded exports plus dm2_version, a cbindgen-generated committed header, and a drift-check test. Refs #302