From 1ca607604581d0fddd6c8be48536e191e82f002d Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Fri, 27 Apr 2018 21:45:44 +0200 Subject: [PATCH] WIP --- capi/bind_gen/src/emscripten.rs | 40 ++++++ src/layout/parser/mod.rs | 245 +++++++++++++++++++++++++++++++- src/layout/parser/xml_util.rs | 3 + src/settings/color.rs | 2 +- 4 files changed, 287 insertions(+), 3 deletions(-) diff --git a/capi/bind_gen/src/emscripten.rs b/capi/bind_gen/src/emscripten.rs index dec17dbe..28162253 100644 --- a/capi/bind_gen/src/emscripten.rs +++ b/capi/bind_gen/src/emscripten.rs @@ -619,6 +619,46 @@ const liveSplitCoreNative = {};"# } } + if class_name == "Layout" { + if type_script { + write!( + writer, + "{}", + r#" + static parseOriginalLivesplitString(text: string): Layout | null { + const len = (text.length << 2) + 1; + const buf = emscriptenModule._malloc(len); + try { + const actualLen = emscriptenModule.stringToUTF8(text, buf, len); + return Layout.parseOriginalLivesplit(buf, actualLen); + } finally { + emscriptenModule._free(buf); + } + }"# + )?; + } else { + write!( + writer, + "{}", + r#" + /** + * @param {string} text + * @return {Layout | null} + */ + static parseOriginalLivesplitString(text) { + const len = (text.length << 2) + 1; + const buf = emscriptenModule._malloc(len); + try { + const actualLen = emscriptenModule.stringToUTF8(text, buf, len); + return Layout.parseOriginalLivesplit(buf, actualLen); + } finally { + emscriptenModule._free(buf); + } + }"# + )?; + } + } + if class_name == "Run" { if type_script { write!( diff --git a/src/layout/parser/mod.rs b/src/layout/parser/mod.rs index f5b0cf7e..080ea48c 100644 --- a/src/layout/parser/mod.rs +++ b/src/layout/parser/mod.rs @@ -2,13 +2,80 @@ mod xml_util; pub use self::xml_util::{Error, Result}; use std::io::BufRead; -use self::xml_util::{end_tag, parse_base, parse_children, text_err}; +use self::xml_util::{end_tag, parse_base, parse_children, text, text_err, text_parsed, Tag}; use super::{Component, Layout}; use component::{blank_space, current_comparison, current_pace, delta, detailed_timer, graph, possible_time_save, previous_segment, separator, splits, sum_of_best, text, timer, title, total_playtime}; use quick_xml::reader::Reader; -use settings::Color; +use settings::{Color, Gradient}; +use time::formatter::Accuracy; + +enum GradientKind { + Transparent, + Plain, + Vertical, + Horizontal, +} + +struct GradientBuilder { + kind: GradientKind, + first: Color, + second: Color, +} + +impl GradientBuilder { + fn new() -> Self { + Self { + kind: GradientKind::Transparent, + first: Color::transparent(), + second: Color::transparent(), + } + } + + fn parse_background<'a, R>( + &mut self, + reader: &mut Reader, + tag: Tag<'a>, + ) -> Result>> + where + R: BufRead, + { + if tag.name() == b"BackgroundColor" { + color(reader, tag.into_buf(), |c| self.first = c)?; + } else if tag.name() == b"BackgroundColor2" { + color(reader, tag.into_buf(), |c| self.second = c)?; + } else if tag.name() == b"BackgroundGradient" { + text_err(reader, tag.into_buf(), |text| { + self.kind = match &*text { + "Plain" => GradientKind::Plain, + "Vertical" => GradientKind::Vertical, + "Horizontal" => GradientKind::Horizontal, + _ => return Err(Error::UnexpectedGradientType), + }; + Ok(()) + })?; + } else { + return Ok(Some(tag)); + } + Ok(None) + } + + fn build(self) -> Gradient { + match self.kind { + GradientKind::Transparent => Gradient::Transparent, + GradientKind::Plain => { + if self.first == Color::transparent() { + Gradient::Transparent + } else { + Gradient::Plain(self.first) + } + } + GradientKind::Horizontal => Gradient::Horizontal(self.first, self.second), + GradientKind::Vertical => Gradient::Vertical(self.first, self.second), + } + } +} fn color(reader: &mut Reader, buf: &mut Vec, f: F) -> Result<()> where @@ -26,6 +93,160 @@ where }) } +fn parse_bool(reader: &mut Reader, buf: &mut Vec, f: F) -> Result<()> +where + R: BufRead, + F: FnOnce(bool), +{ + text_err(reader, buf, |t| match &*t { + "True" => { + f(true); + Ok(()) + } + "False" => { + f(false); + Ok(()) + } + _ => Err(Error::Bool), + }) +} + +fn accuracy(reader: &mut Reader, buf: &mut Vec, f: F) -> Result<()> +where + R: BufRead, + F: FnOnce(Accuracy), +{ + text_err(reader, buf, |t| { + f(match &*t { + "Tenths" => Accuracy::Tenths, + "Seconds" => Accuracy::Seconds, + "Hundredths" => Accuracy::Hundredths, + _ => return Err(Error::Accuracy), + }); + Ok(()) + }) +} + +fn blank_space_settings( + reader: &mut Reader, + buf: &mut Vec, + component: &mut blank_space::Component, +) -> Result<()> +where + R: BufRead, +{ + let settings = component.settings_mut(); + let mut background_builder = GradientBuilder::new(); + + parse_children(reader, buf, |reader, tag| { + if let Some(tag) = background_builder.parse_background(reader, tag)? { + if tag.name() == b"SpaceHeight" { + text_parsed(reader, tag.into_buf(), |h| settings.height = h) + } else { + end_tag(reader, tag.into_buf()) + } + } else { + Ok(()) + } + })?; + + settings.background = background_builder.build(); + + Ok(()) +} + +fn current_comparison_settings( + reader: &mut Reader, + buf: &mut Vec, + component: &mut current_comparison::Component, +) -> Result<()> +where + R: BufRead, +{ + let settings = component.settings_mut(); + let mut background_builder = GradientBuilder::new(); + let (mut override_label, mut override_value) = (false, false); + + parse_children(reader, buf, |reader, tag| { + if let Some(tag) = background_builder.parse_background(reader, tag)? { + if tag.name() == b"TextColor" { + color(reader, tag.into_buf(), |c| settings.label_color = Some(c)) + } else if tag.name() == b"OverrideTextColor" { + parse_bool(reader, tag.into_buf(), |b| override_label = b) + } else if tag.name() == b"TimeColor" { + color(reader, tag.into_buf(), |c| settings.value_color = Some(c)) + } else if tag.name() == b"OverrideTimeColor" { + parse_bool(reader, tag.into_buf(), |b| override_value = b) + } else { + end_tag(reader, tag.into_buf()) + } + } else { + Ok(()) + } + })?; + + if !override_label { + settings.label_color = None; + } + if !override_value { + settings.value_color = None; + } + settings.background = background_builder.build(); + + Ok(()) +} + +fn current_pace_settings( + reader: &mut Reader, + buf: &mut Vec, + component: &mut current_pace::Component, +) -> Result<()> +where + R: BufRead, +{ + let settings = component.settings_mut(); + let mut background_builder = GradientBuilder::new(); + let (mut override_label, mut override_value) = (false, false); + + parse_children(reader, buf, |reader, tag| { + if let Some(tag) = background_builder.parse_background(reader, tag)? { + if tag.name() == b"TextColor" { + color(reader, tag.into_buf(), |c| settings.label_color = Some(c)) + } else if tag.name() == b"OverrideTextColor" { + parse_bool(reader, tag.into_buf(), |b| override_label = b) + } else if tag.name() == b"TimeColor" { + color(reader, tag.into_buf(), |c| settings.value_color = Some(c)) + } else if tag.name() == b"OverrideTimeColor" { + parse_bool(reader, tag.into_buf(), |b| override_value = b) + } else if tag.name() == b"Comparison" { + text(reader, tag.into_buf(), |t| { + settings.comparison_override = if t == "Current Comparison" { + None + } else { + Some(t.into_owned()) + }; + }) + } else if tag.name() == b"Accuracy" { + accuracy(reader, tag.into_buf(), |a| settings.accuracy = a) + } else { + end_tag(reader, tag.into_buf()) + } + } else { + Ok(()) + } + })?; + + if !override_label { + settings.label_color = None; + } + if !override_value { + settings.value_color = None; + } + settings.background = background_builder.build(); + + Ok(()) +} + fn component(reader: &mut Reader, buf: &mut Vec, f: F) -> Result<()> where R: BufRead, @@ -58,6 +279,26 @@ where }); Ok(()) }) + } else if tag.name() == b"Settings" { + // Assumption: Settings always has to come after the Path. + // Otherwise we need to cache the settings and load them later. + if let Some(ref mut component) = component { + match *component { + Component::BlankSpace(ref mut c) => { + blank_space_settings(reader, tag.into_buf(), c) + } + Component::CurrentComparison(ref mut c) => { + current_comparison_settings(reader, tag.into_buf(), c) + } + Component::CurrentPace(ref mut c) => { + current_pace_settings(reader, tag.into_buf(), c) + } + // _ => unimplemented!(), + _ => end_tag(reader, tag.into_buf()), // ignore other components for now + } + } else { + end_tag(reader, tag.into_buf()) + } } else { end_tag(reader, tag.into_buf()) } diff --git a/src/layout/parser/xml_util.rs b/src/layout/parser/xml_util.rs index c0f4dddb..d743c147 100644 --- a/src/layout/parser/xml_util.rs +++ b/src/layout/parser/xml_util.rs @@ -33,6 +33,9 @@ quick_error! { ElementNotFound {} /// The length of a buffer was too large. LengthOutOfBounds {} + UnexpectedGradientType {} + Bool {} + Accuracy {} /// Failed to decode a string slice as UTF-8. Utf8Str(err: str::Utf8Error) { from() diff --git a/src/settings/color.rs b/src/settings/color.rs index 9321c95e..8532de3d 100644 --- a/src/settings/color.rs +++ b/src/settings/color.rs @@ -37,7 +37,7 @@ impl From<[f32; 4]> for Color { impl From<[u8; 4]> for Color { fn from(rgba: [u8; 4]) -> Self { Self { - rgba: Rgba::from_pixel(&rgba), + rgba: LinSrgba::from_pixel(&rgba), } } }