diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5f2060..e8c792f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,4 +18,4 @@ jobs: run: curl https://drager.github.io/wasm-pack/installer/init.sh -sSf | bash - name: Build library - run: wasm-pack build --scope libresplit --target web --release + run: wasm-pack build --scope libresplit --target web --release --features wasm diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 329ef2a..3568b9f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ jobs: run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Build WASM library - run: wasm-pack build --scope libresplit --target web --release + run: wasm-pack build --scope libresplit --target web --release --features wasm - name: Set up Node.js uses: actions/setup-node@v4 @@ -36,4 +36,4 @@ jobs: - name: Publish to npm run: npm install -g npm@latest - - run: wasm-pack publish --target web --access public + - run: wasm-pack publish ./pkg --access public diff --git a/Cargo.lock b/Cargo.lock index a1d23d3..010ccf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" dependencies = [ "cfg-if", "once_cell", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" dependencies = [ "bumpalo", "proc-macro2", @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index 3863431..4ecb2bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,15 @@ version = "1.0.0" edition = "2024" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "staticlib", "rlib"] [dependencies] -wasm-bindgen = "0.2.104" +wasm-bindgen = { version = "0.2.123", optional = true } serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" spex = "0.2.2" + +[features] +default = [] +wasm = ["wasm-bindgen"] diff --git a/README.md b/README.md index c7df06e..70f8060 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # converter -A WASM library for converting [LiveSplit](https://livesplit.org) `.lss` files into the [LibreSplit](https://libresplit.org) `.json` format. +A library for converting [LiveSplit](https://livesplit.org) `.lss` files into the [LibreSplit](https://libresplit.org) `.json` format. An online converter using this library is available at [libresplit.org/converter](https://libresplit.org/converter). @@ -27,6 +27,13 @@ wasm-pack build --scope libresplit --target web --release This outputs a compiled WASM package under `pkg/`. +### Building (for C ABI) +```sh +git clone https://github.com/LibreSplit/converter +cd converter +cargo build --release +``` + --- Published as an npm package under [`@libresplit/converter`](https://npmjs.com/package/@libresplit/converter). diff --git a/src/lib.rs b/src/lib.rs index 272249e..bca0c9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,65 @@ -use std::io::Cursor; +use std::{ + ffi::{CStr, CString}, + io::Cursor, + os::raw::c_char, + ptr::null_mut, +}; use spex::parsing::XmlReader; -use wasm_bindgen::prelude::*; mod libresplit; mod livesplit; -#[wasm_bindgen] -pub fn convert(file: String) -> String { +// Shared logic for both interfaces. +fn convert_inner(file: &str) -> Result { let cursor = Cursor::new(file); - let xml = XmlReader::parse_auto(cursor).unwrap(); + let xml = XmlReader::parse_auto(cursor).map_err(|e| e.to_string())?; let livesplit_data = livesplit::LiveSplitFile::new(xml); - libresplit::LibreSplitFile::from_livesplit(livesplit_data).get() + Ok(libresplit::LibreSplitFile::from_livesplit(livesplit_data).get()) +} + +// Build the library for WASM targets. +// Used on the LibreSplit website, for converting splits. +// Accepts a LiveSplit XML file as a string and returns LibreSplit JSON. + +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +#[cfg_attr(feature = "wasm", wasm_bindgen)] +pub fn convert(file: String) -> String { + convert_inner(&file).unwrap_or_else(|e| format!("{{\"error\":\"{}\"}}", e)) +} + +// C FFI entrypoints used by the split editor. + +// 'converter_convert' takes a null-terminated UTF-8 string and returns an owned C string. +#[unsafe(no_mangle)] +pub extern "C" fn converter_convert(input: *const c_char) -> *mut c_char { + if input.is_null() { + return null_mut(); + } + + let input = unsafe { + match CStr::from_ptr(input).to_str() { + Ok(s) => s, + Err(_) => return null_mut(), + } + }; + + match convert_inner(input) { + Ok(output) => CString::new(output).unwrap().into_raw(), + Err(error) => CString::new(format!("{{\"error\":\"{}\"}}", error)) + .unwrap() + .into_raw(), + } +} + +// The caller must free the returned pointer with 'converter_free_string'. +#[unsafe(no_mangle)] +pub extern "C" fn converter_free_string(ptr: *mut c_char) { + if !ptr.is_null() { + unsafe { + let _ = CString::from_raw(ptr); + } + } }