Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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).

Expand All @@ -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).
62 changes: 56 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<String, String> {
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);
}
}
}
Loading