diff --git a/capi/src/run.rs b/capi/src/run.rs index 9c22078b0..7e5c52c09 100644 --- a/capi/src/run.rs +++ b/capi/src/run.rs @@ -146,6 +146,18 @@ pub unsafe extern "C" fn Run_set_category_name(this: &mut Run, category: *const this.set_category_name(unsafe { str(category) }); } +/// Accesses the name of the level of this Run. +#[no_mangle] +pub extern "C" fn Run_level_name(this: &Run) -> *const c_char { + output_str(this.level_name()) +} + +/// Sets the name of the level of this Run. +#[no_mangle] +pub unsafe extern "C" fn Run_set_level_name(this: &mut Run, level: *const c_char) { + this.set_level_name(str(level)); +} + /// Returns a file name (without the extension) suitable for this Run that /// is built the following way: /// diff --git a/capi/src/run_editor.rs b/capi/src/run_editor.rs index f5e5a0bb1..2c9a535f9 100644 --- a/capi/src/run_editor.rs +++ b/capi/src/run_editor.rs @@ -97,6 +97,15 @@ pub unsafe extern "C" fn RunEditor_set_category_name( this.set_category_name(unsafe { str(category) }); } +/// Sets the name of the level. +#[no_mangle] +pub unsafe extern "C" fn RunEditor_set_level_name( + this: &mut RunEditor, + level: *const c_char, +) { + this.set_level_name(str(level)); +} + /// Parses and sets the timer offset from the string provided. The timer /// offset specifies the time, the timer starts at when starting a new /// attempt. diff --git a/src/run/editor/mod.rs b/src/run/editor/mod.rs index 61f569766..bd975babd 100644 --- a/src/run/editor/mod.rs +++ b/src/run/editor/mod.rs @@ -237,6 +237,21 @@ impl Editor { self.run.clear_run_id(); } + /// Accesses the name of the level. + pub fn level_name(&self) -> &str { + self.run.level_name() + } + + /// Sets the name of the level. + pub fn set_level_name(&mut self, name: S) + where + S: PopulateString, + { + self.run.set_level_name(name); + self.raise_run_edited(); + self.run.clear_run_id(); + } + /// Accesses the timer offset. The timer offset specifies the time, the /// timer starts at when starting a new attempt. pub const fn offset(&self) -> TimeSpan { diff --git a/src/run/editor/state.rs b/src/run/editor/state.rs index b12a2dfb5..a1c52051e 100644 --- a/src/run/editor/state.rs +++ b/src/run/editor/state.rs @@ -20,6 +20,8 @@ pub struct State { pub game: String, /// The name of the category the Run is for. pub category: String, + /// The name of the level of the Run. + pub level: String, /// The timer offset specifies the time that the timer starts at when /// starting a new attempt. pub offset: String, @@ -125,6 +127,7 @@ impl Editor { let game = self.game_name().to_string(); let category = self.category_name().to_string(); + let level = self.level_name().to_string(); let offset = formatter.format(self.offset()).to_string(); let attempts = self.attempt_count(); let timing_method = self.selected_timing_method(); @@ -182,6 +185,7 @@ impl Editor { icon, game, category, + level, offset, attempts, timing_method, diff --git a/src/run/editor/tests/dissociate_run.rs b/src/run/editor/tests/dissociate_run.rs index 81d3f84a7..1b8aebb34 100644 --- a/src/run/editor/tests/dissociate_run.rs +++ b/src/run/editor/tests/dissociate_run.rs @@ -41,6 +41,13 @@ fn when_changing_category_name() { assert_eq!(editor.run().metadata().run_id, ""); } +#[test] +fn when_changing_level_name() { + let mut editor = base(); + editor.set_level_name("Hello"); + assert_eq!(editor.run().metadata().run_id, ""); +} + #[test] fn not_when_changing_attempt_count() { let mut editor = base(); diff --git a/src/run/editor/tests/mark_as_modified.rs b/src/run/editor/tests/mark_as_modified.rs index f7f8f8500..0d2e4dd45 100644 --- a/src/run/editor/tests/mark_as_modified.rs +++ b/src/run/editor/tests/mark_as_modified.rs @@ -141,6 +141,13 @@ fn when_changing_category_name() { assert!(editor.run().has_been_modified()); } +#[test] +fn when_changing_level_name() { + let mut editor = base(); + editor.set_level_name("Hello"); + assert!(editor.run().has_been_modified()); +} + #[test] fn when_changing_offset() { let mut editor = base(); diff --git a/src/run/mod.rs b/src/run/mod.rs index faf7d12d7..3b4c3bbfd 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -66,6 +66,7 @@ pub struct Run { game_icon: Image, game_name: String, category_name: String, + level_name: String, offset: TimeSpan, attempt_count: u32, attempt_history: Vec, @@ -119,6 +120,7 @@ impl Run { game_icon: Image::default(), game_name: String::new(), category_name: String::new(), + level_name: String::new(), offset: TimeSpan::zero(), attempt_count: 0, attempt_history: Vec::new(), @@ -176,6 +178,21 @@ impl Run { name.populate(&mut self.category_name); } + /// Accesses the name of the level of this Run. + #[inline] + pub fn level_name(&self) -> &str { + &self.level_name + } + + /// Sets the name of the level of this Run. + #[inline] + pub fn set_level_name(&mut self, name: S) + where + S: PopulateString, + { + name.populate(&mut self.level_name); + } + /// Returns the amount of runs that have been attempted with these splits. #[inline] pub const fn attempt_count(&self) -> u32 { diff --git a/src/run/parser/livesplit.rs b/src/run/parser/livesplit.rs index d36578ea6..cb5f58410 100644 --- a/src/run/parser/livesplit.rs +++ b/src/run/parser/livesplit.rs @@ -451,6 +451,10 @@ pub fn parse(source: &str) -> Result { required_flags |= 1 << 2; text(reader, |t| run.set_category_name(t)) } + "LevelName" => { + // not required, for compatibility + text(reader, |t| run.set_level_name(t)) + } "Offset" => { required_flags |= 1 << 3; time_span(reader, |t| run.set_offset(t)) diff --git a/src/run/saver/livesplit.rs b/src/run/saver/livesplit.rs index 27c6e4db5..700dc3286 100644 --- a/src/run/saver/livesplit.rs +++ b/src/run/saver/livesplit.rs @@ -178,6 +178,7 @@ pub fn save_run(run: &Run, writer: W) -> fmt::Result { image(writer, "GameIcon", run.game_icon(), base64_buf, image_buf)?; writer.tag_with_text_content("GameName", NO_ATTRIBUTES, run.game_name())?; writer.tag_with_text_content("CategoryName", NO_ATTRIBUTES, run.category_name())?; + writer.tag_with_text_content("LevelName", NO_ATTRIBUTES, run.level_name())?; writer.tag_with_content("Metadata", NO_ATTRIBUTES, |writer| { let metadata = run.metadata();