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
4 changes: 2 additions & 2 deletions capi/bind_gen/src/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function, class_name: &str) -> R
}
}

write!(writer, r#"LiveSplitCoreNative.{}("#, &function.name)?;
write!(writer, r#"LiveSplitCoreNative.{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down Expand Up @@ -455,7 +455,7 @@ namespace LiveSplitCore
[DllImport("livesplit_core", CallingConvention = CallingConvention.Cdecl)]
public static extern {} {}("#,
get_ll_type(&function.output, true),
&function.name
function.name
)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/java/jna.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function, class_name: &str) -> R
write!(
writer,
r#"LiveSplitCoreNative.INSTANCE.{}("#,
&function.name
function.name
)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/jni_cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ extern "C" JNIEXPORT {} Java_livesplitcore_LiveSplitCoreNative_{}_1{}(JNIEnv* jn
}
}

write!(writer, r#"{}("#, &function.name)?;
write!(writer, r#"{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function, type_script: bool) ->
}
}

write!(writer, r#"liveSplitCoreNative.{}("#, &function.name)?;
write!(writer, r#"liveSplitCoreNative.{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function) -> Result<()> {
}
}

write!(writer, r#"livesplit_core_native.{}("#, &function.name)?;
write!(writer, r#"livesplit_core_native.{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/ruby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function) -> Result<()> {
}
}

write!(writer, r#"Native.{}("#, &function.name)?;
write!(writer, r#"Native.{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/swift/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function) -> Result<()> {
write!(writer, "let result = ")?;
}

write!(writer, "CLiveSplitCore.{}(", &function.name)?;
write!(writer, "CLiveSplitCore.{}(", function.name)?;

for (i, (name, ty)) in function.inputs.iter().enumerate() {
if i != 0 {
Expand Down
2 changes: 1 addition & 1 deletion capi/bind_gen/src/wasm_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ fn write_fn<W: Write>(mut writer: W, function: &Function, type_script: bool) ->
}
}

write!(writer, r#"wasm.{}("#, &function.name)?;
write!(writer, r#"wasm.{}("#, function.name)?;

for (i, (name, typ)) in function.inputs.iter().enumerate() {
let type_name = get_hl_type_without_null(typ);
Expand Down
169 changes: 167 additions & 2 deletions src/run/parser/opensplit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
// https://github.com/ZellyDev-Games/OpenSplit

use crate::{
Run, Segment, Time, TimeSpan, platform::prelude::*, run::SegmentGroup, run::SegmentGroups,
Lang, Run, Segment, Time, TimeSpan,
comparison::world_record,
platform::prelude::*,
run::{SegmentGroup, SegmentGroups},
timing::formatter::{self, TimeFormatter},
};
use alloc::{borrow::Cow, collections::BTreeMap};
use core::result::Result as StdResult;
Expand Down Expand Up @@ -41,6 +45,23 @@ struct SplitFilePayload<'a> {
offset: i64,
#[serde(default, borrow)]
platform: Cow<'a, str>,
#[serde(default, borrow)]
variables: Vec<VariablePayload<'a>>,
#[serde(default)]
wr: Option<WorldRecordPayload>,
}

#[derive(Deserialize)]
struct VariablePayload<'a> {
#[serde(borrow)]
name: Cow<'a, str>,
#[serde(borrow)]
label: Cow<'a, str>,
}

#[derive(Deserialize)]
struct WorldRecordPayload {
real_time: f64,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -139,7 +160,26 @@ pub fn parse(source: &str) -> Result<Run> {
run.set_category_name(splits.game_category);
run.set_attempt_count(splits.attempts);
run.set_offset(integer_time(splits.offset));
run.metadata_mut().set_platform_name(splits.platform);

let metadata = run.metadata_mut();
metadata.set_platform_name(splits.platform);
for variable in splits.variables {
metadata.set_speedrun_com_variable(variable.name, variable.label);
}
if let Some(wr) = splits.wr
&& wr.real_time > 0.0
{
metadata
.custom_variable_mut(world_record::NAME)
.permanent()
// FIXME: This should probably depend on the locale or:
// FIXME: Custom variables should support TimeSpans directly.
.set_value(
formatter::Regular::new()
.format(Some(TimeSpan::from_seconds(wr.real_time)), Lang::English)
.to_string(),
);
}

let FlattenedSegments {
segments: leaf_segments,
Expand Down Expand Up @@ -201,3 +241,128 @@ pub fn parse(source: &str) -> Result<Run> {

Ok(run)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parses_speedrun_com_metadata() {
let run = parse(
r#"{
"game_name": "Game",
"speedrun_game_id": "game-id",
"game_category": "Any%",
"speedrun_game_category_id": "category-id",
"attempts": 0,
"platform": "GameCube",
"variables": [
{
"id": "difficulty-id",
"name": "Difficulty",
"value": "hard-id",
"label": "Hard"
},
{
"id": "players-id",
"name": "Players",
"value": "one-player-id",
"label": "1 Player"
}
],
"wr": {
"show": true,
"run_id": "world-record-run-id",
"players": ["Runner"],
"real_time": 3723.456,
"in_game_time": 3600.0
},
"segments": []
}"#,
)
.unwrap();

let metadata = run.metadata();
assert_eq!(metadata.platform_name(), "GameCube");
assert_eq!(
metadata
.speedrun_com_variables()
.map(|(name, value)| (name, value.as_str()))
.collect::<Vec<_>>(),
[("Difficulty", "Hard"), ("Players", "1 Player")]
);

let world_record = metadata.custom_variable(world_record::NAME).unwrap();
assert_eq!(world_record.value, "1:02:03");
assert!(world_record.is_permanent);
}

#[test]
fn ignores_unavailable_world_record() {
let run = parse(
r#"{
"game_name": "Game",
"game_category": "Any%",
"attempts": 0,
"wr": {
"show": false,
"run_id": "",
"players": [],
"real_time": 0,
"in_game_time": 0
},
"segments": []
}"#,
)
.unwrap();

assert!(run.metadata().custom_variable(world_record::NAME).is_none());
}

#[test]
fn segment_children_become_segment_groups() {
let run = parse(
r#"{
"game_name": "Game",
"game_category": "Any%",
"attempts": 0,
"segments": [
{ "id": "intro", "name": "Intro", "gold": 0, "pb": 0 },
{
"id": "chapter",
"name": "Chapter",
"gold": 0,
"pb": 0,
"children": [
{ "id": "a", "name": "A", "gold": 0, "pb": 0 },
{
"id": "nested",
"name": "Nested",
"gold": 0,
"pb": 0,
"children": [
{ "id": "b", "name": "B", "gold": 0, "pb": 0 },
{ "id": "c", "name": "C", "gold": 0, "pb": 0 }
]
}
]
},
{ "id": "outro", "name": "Outro", "gold": 0, "pb": 0 }
]
}"#,
)
.unwrap();

assert_eq!(
run.segments()
.iter()
.map(|segment| segment.name())
.collect::<Vec<_>>(),
["Intro", "A", "B", "C", "Outro"]
);
assert_eq!(run.segment_groups().groups().len(), 1);
let group = &run.segment_groups().groups()[0];
assert_eq!((group.start(), group.end()), (1, 4));
assert_eq!(group.name(), Some("Chapter"));
}
}
52 changes: 5 additions & 47 deletions tests/split_parsing.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
//! Integration tests for parsing representative real-world splits files and
//! detecting their timer kinds. The files in `run_files` are reserved for this
//! suite. Tests for specific fields, edge cases, and parser semantics should use
//! inline inputs in the corresponding parser's local test module.

mod run_files;

mod parse {
Expand Down Expand Up @@ -272,53 +277,6 @@ mod parse {
opensplit::parse(run_files::OPENSPLIT).unwrap();
}

#[test]
fn opensplit_segment_children_become_segment_groups() {
let run = opensplit::parse(
r#"{
"game_name": "Game",
"game_category": "Any%",
"attempts": 0,
"segments": [
{ "id": "intro", "name": "Intro", "gold": 0, "pb": 0 },
{
"id": "chapter",
"name": "Chapter",
"gold": 0,
"pb": 0,
"children": [
{ "id": "a", "name": "A", "gold": 0, "pb": 0 },
{
"id": "nested",
"name": "Nested",
"gold": 0,
"pb": 0,
"children": [
{ "id": "b", "name": "B", "gold": 0, "pb": 0 },
{ "id": "c", "name": "C", "gold": 0, "pb": 0 }
]
}
]
},
{ "id": "outro", "name": "Outro", "gold": 0, "pb": 0 }
]
}"#,
)
.unwrap();

assert_eq!(
run.segments()
.iter()
.map(|segment| segment.name())
.collect::<Vec<_>>(),
["Intro", "A", "B", "C", "Outro"]
);
assert_eq!(run.segment_groups().groups().len(), 1);
let group = &run.segment_groups().groups()[0];
assert_eq!((group.start(), group.end()), (1, 4));
assert_eq!(group.name(), Some("Chapter"));
}

#[test]
fn speedrun_igt_prefers_parsing_as_itself() {
let run = composite::parse(run_files::SPEEDRUN_IGT.as_bytes(), None).unwrap();
Expand Down
Loading