diff --git a/.gitignore b/.gitignore index d8fda6b..0c9c99d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /src/target/ +/target/debug/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..477280b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "librashader-presets/test/slang-shaders"] + path = librashader-presets/test/slang-shaders + url = https://github.com/libretro/slang-shaders diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 6c0b863..288b36b 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 127d8fd..12dbd01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,105 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + [[package]] name = "librashader-presets" version = "0.1.0" +dependencies = [ + "nom", + "nom_locate", + "thiserror", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom_locate" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" diff --git a/librashader-presets/Cargo.toml b/librashader-presets/Cargo.toml index 4ebab4e..9103155 100644 --- a/librashader-presets/Cargo.toml +++ b/librashader-presets/Cargo.toml @@ -6,3 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +thiserror = "1.0.37" +nom = "7.1.1" +nom_locate = "4.0.0" diff --git a/librashader-presets/src/error.rs b/librashader-presets/src/error.rs new file mode 100644 index 0000000..c030406 --- /dev/null +++ b/librashader-presets/src/error.rs @@ -0,0 +1,21 @@ +use crate::parse::Span; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ParsePresetError { + #[error("shader preset parse error")] + LexerError { offset: usize, row: u32, col: usize }, + #[error("shader preset parse error")] + ParserError { offset: usize, row: u32, col: usize }, + #[error("invalid scale type")] + InvalidScaleType(String), + #[error("exceeded maximum reference depth (16)")] + ExceededReferenceDepth, + #[error("shader presents must be resolved against an absolute path")] + RootPathWasNotAbsolute, + #[error("the file was not found during resolution")] + IOError(PathBuf, std::io::Error), + #[error("expected utf8 bytes but got invalid utf8")] + Utf8Error(Vec), +} diff --git a/librashader-presets/src/lib.rs b/librashader-presets/src/lib.rs index 663d783..d8fbce3 100644 --- a/librashader-presets/src/lib.rs +++ b/librashader-presets/src/lib.rs @@ -1,3 +1,6 @@ -mod preset; +#![feature(drain_filter)] +mod error; +pub mod parse; +mod preset; pub use preset::*; diff --git a/librashader-presets/src/parse/mod.rs b/librashader-presets/src/parse/mod.rs new file mode 100644 index 0000000..58b337a --- /dev/null +++ b/librashader-presets/src/parse/mod.rs @@ -0,0 +1,12 @@ +use nom::{ExtendInto, Offset}; +use nom_locate::LocatedSpan; +use std::str; + +mod token; +mod value; + +pub type Span<'a> = LocatedSpan<&'a str>; +use crate::error::ParsePresetError; +pub use token::do_lex; +pub use token::Token; +pub use value::parse_values; diff --git a/librashader-presets/src/parse/token.rs b/librashader-presets/src/parse/token.rs new file mode 100644 index 0000000..e05b8cb --- /dev/null +++ b/librashader-presets/src/parse/token.rs @@ -0,0 +1,600 @@ +use crate::error::ParsePresetError; +use crate::parse::Span; +use nom::branch::alt; +use nom::bytes::complete::{is_not, take_till, take_until, take_while}; +use nom::character::complete::{char, digit1, line_ending, multispace1, not_line_ending}; +use nom::character::is_space; +use nom::combinator::{eof, map_res, value}; +use nom::error::{ErrorKind, ParseError}; +use nom::multi::many_till; +use nom::sequence::{delimited, pair, preceded, separated_pair}; +use nom::{ + bytes::complete::tag, character::complete::multispace0, IResult, InputIter, InputLength, + InputTake, +}; +use nom_locate::LocatedSpan; +use std::num::ParseIntError; +use std::str::FromStr; + +#[derive(Debug)] +pub struct Token<'a> { + pub key: Span<'a>, + pub value: Span<'a>, +} + +/// Return the input slice up to the first occurrence of the parser, +/// and the result of the parser on match. +/// If the parser never matches, returns an error with code `ManyTill` +pub fn take_up_to, P>( + mut parser: P, +) -> impl FnMut(Input) -> IResult +where + P: FnMut(Input) -> IResult, + Input: InputLength + InputIter + InputTake, +{ + move |i: Input| { + let input = i; + for (index, _) in input.iter_indices() { + let (rest, front) = input.take_split(index); + match parser(rest) { + Ok((remainder, output)) => return Ok((remainder, (front, output))), + Err(_) => continue, + } + } + Err(nom::Err::Error(Error::from_error_kind( + input, + ErrorKind::ManyTill, + ))) + } +} + +fn from_float(input: &str) -> Result { + f32::from_str(input) +} + +// accepts "true" or "false" +fn from_bool(input: &str) -> Result { + bool::from_str(input) +} + +fn parse_assignment(input: Span) -> IResult { + let (input, _) = multispace0(input)?; + let (input, _) = tag("=")(input)?; + let (input, _) = multispace0(input)?; + Ok((input, ())) +} + +fn extract_from_quotes(input: Span) -> IResult { + let (input, between) = delimited(char('"'), is_not("\""), preceded(char('"'), eof))(input)?; + Ok((input, between)) +} + +fn multiline_comment(i: Span) -> IResult { + delimited(tag("/*"), is_not("*/"), tag("*/"))(i) +} + +fn single_comment(i: Span) -> IResult { + delimited(alt((tag("//"), tag("#"))), not_line_ending, line_ending)(i) +} + +fn whitespace(i: Span) -> IResult { + value( + (), // Output is thrown away. + multispace0, + )(i) +} + +fn optional_quotes(input: Span) -> IResult<(), Span> { + let input = if let Ok((_, between)) = extract_from_quotes(input) { + between + } else { + input + }; + Ok(((), input)) +} + +fn parse_reference(input: Span) -> IResult { + let (input, key) = tag("#reference")(input)?; + let (input, _) = multispace1(input)?; + let (input, (_, value)) = map_res(not_line_ending, optional_quotes)(input)?; + Ok((input, Token { key, value })) +} +fn parse_key_value(input: Span) -> IResult { + let (input, (key, _)) = take_up_to(parse_assignment)(input)?; + let (input, (_, value)) = map_res(not_line_ending, optional_quotes)(input)?; + Ok((input, Token { key, value })) +} + +fn parse_tokens(mut span: Span) -> IResult> { + let mut values = Vec::new(); + while !span.is_empty() { + // important to munch whitespace first. + if let Ok((input, _)) = whitespace(span) { + span = input; + } + // handle references before comments because comments can start with # + if let Ok((input, token)) = parse_reference(span) { + span = input; + values.push(token); + continue; + } + if let Ok((input, _)) = multiline_comment(span) { + span = input; + } + if let Ok((input, _)) = single_comment(span) { + span = input; + } + let (input, token) = parse_key_value(span)?; + span = input; + values.push(token) + } + Ok((span, values)) +} + +pub fn do_lex(input: &str) -> Result, ParsePresetError> { + let mut span = Span::new(input.trim_end()); + let (_, tokens) = parse_tokens(span).map_err(|e| match e { + nom::Err::Error(e) | nom::Err::Failure(e) => { + let input: Span = e.input; + ParsePresetError::LexerError { + offset: input.location_offset(), + row: input.location_line(), + col: input.get_column(), + } + } + _ => ParsePresetError::LexerError { + offset: 0, + row: 0, + col: 0, + }, + })?; + Ok(tokens) +} + +#[cfg(test)] +mod test { + use crate::parse::do_lex; + use crate::parse::token::{parse_key_value, parse_tokens, single_comment, Span}; + + #[test] + fn parses_single_line_comment() { + let parsed = + single_comment("// Define textures to be used by the different passes\ntetx=n".into()); + eprintln!("{:?}", parsed) + } + + #[test] + fn parses_key_value_line() { + let parsed = do_lex(TEST); + eprintln!("{:?}", parsed) + } + + const TEST2: &'static str = r#" + + shader0 = ../../shaders/base/add-params-all.slang + // no + "#; + const TEST: &'static str = r#" +#reference "../../alt" +shaders = 54 + +shader0 = ../../shaders/base/add-params-all.slang +alias0 = "CorePass" + +shader1 = ../../shaders/hyllian/cubic/hsm-drez-b-spline-x.slang +filter_linear1 = false +scale_type_x1 = absolute +scale_x1 = 640 +scale_type_y1 = viewport +scaley0 = 1.0 +wrap_mode1 = "clamp_to_edge" + +shader2 = ../../shaders/hyllian/cubic/hsm-drez-b-spline-y.slang +filter_linear2 = false +scale_type2 = absolute +scale_x2 = 640 +scale_y2 = 480 +wrap_mode2 = "clamp_to_edge" +alias2 = "DerezedPass" + +shader3 = ../../shaders/base/add-negative-crop-area.slang +filter_linear3 = false +mipmap_input3 = false +srgb_framebuffer3 = true +scale_type3 = source +scale_x3 = 1 +scale_y3 = 1 +alias3 = "NegativeCropAddedPass" + +shader4 = ../../shaders/base/cache-info-all-params.slang +filter_linear4 = false +scale_type4 = source +scale4 = 1.0 +alias4 = "InfoCachePass" + +shader5 = ../../shaders/base/text-std.slang +filter_linear5 = false +float_framebuffer5 = true +scale_type5 = source +scale5 = 1.0 +alias5 = "TextPass" + +shader6 = ../../shaders/base/intro.slang +filter_linear6 = false +float_framebuffer6 = true +scale_type6 = source +scale6 = 1.0 +alias6 = "IntroPass" + +shader7 = ../../shaders/dedither/dedither-gamma-prep-1-before.slang +alias7 = LinearGamma + +shader8 = ../../shaders/hyllian/checkerboard-dedither/checkerboard-dedither-pass1.slang +shader9 = ../../shaders/hyllian/checkerboard-dedither/checkerboard-dedither-pass2.slang +shader10 = ../../shaders/hyllian/checkerboard-dedither/checkerboard-dedither-pass3.slang +alias10 = "PreMdaptPass" + +// De-Dithering - Mdapt +shader11 = ../../shaders/mdapt/hsm-mdapt-pass0.slang +shader12 = ../../shaders/mdapt/hsm-mdapt-pass1.slang +shader13 = ../../shaders/mdapt/hsm-mdapt-pass2.slang +shader14 = ../../shaders/mdapt/hsm-mdapt-pass3.slang +shader15 = ../../shaders/mdapt/hsm-mdapt-pass4.slang + +shader16 = ../../shaders/dedither/dedither-gamma-prep-2-after.slang + +shader17 = ../../shaders/ps1dither/hsm-PS1-Undither-BoxBlur.slang + +shader18 = ../../shaders/guest/extras/hsm-sharpsmoother.slang + +shader19 = ../../shaders/base/stock.slang +alias19 = refpass + +shader20 = ../../shaders/scalefx/hsm-scalefx-pass0.slang +filter_linear20 = false +scale_type20 = source +scale20 = 1.0 +float_framebuffer20 = true +alias20 = scalefx_pass0 + +shader21 = ../../shaders/scalefx/hsm-scalefx-pass1.slang +filter_linear21 = false +scale_type21 = source +scale21 = 1.0 +float_framebuffer12 = true + +shader22 = ../../shaders/scalefx/hsm-scalefx-pass2.slang +filter_linear22 = false +scale_type22 = source +scale22 = 1.0 + +shader23 = ../../shaders/scalefx/hsm-scalefx-pass3.slang +filter_linear23 = false +scale_type23 = source +scale23 = 1.0 + +shader24 = ../../shaders/scalefx/hsm-scalefx-pass4.slang +filter_linear24 = false +scale_type24 = source +scale24 = 3 + +shader25 = ../../shaders/base/stock.slang +alias25 = "PreCRTPass" + +shader26 = ../../shaders/guest/hsm-afterglow0.slang +filter_linear26 = true +scale_type26 = source +scale26 = 1.0 +alias26 = "AfterglowPass" + +shader27 = ../../shaders/guest/hsm-pre-shaders-afterglow.slang +filter_linear27 = true +scale_type27 = source +mipmap_input27 = true +scale27 = 1.0 + +// Color Correction with Dogway's awesome Grade shader +// Grade is after Afterglow so that brightening the black level does not break the afterglow +shader28 = ../../shaders/dogway/hsm-grade.slang +filter_linear28 = true +scale_type28 = source +scale28 = 1.0 + +shader29 = ../../shaders/base/stock.slang +alias29 = "PrePass0" + +shader30 = ../../shaders/guest/ntsc/hsm-ntsc-pass1.slang +filter_linear30 = false +float_framebuffer30 = true +scale_type_x30 = source +scale_type_y30 = source +scale_x30 = 4.0 +scale_y30 = 1.0 +frame_count_mod30 = 2 +alias30 = NPass1 + +shader31 = ../../shaders/guest/ntsc/hsm-ntsc-pass2.slang +float_framebuffer31 = true +filter_linear31 = true +scale_type31 = source +scale_x31 = 0.5 +scale_y31 = 1.0 + +shader32 = ../../shaders/guest/ntsc/hsm-ntsc-pass3.slang +filter_linear32 = true +scale_type32 = source +scale_x32 = 1.0 +scale_y32 = 1.0 + +shader33 = ../../shaders/guest/hsm-custom-fast-sharpen.slang +filter_linear33 = true +scale_type33 = source +scale_x33 = 1.0 +scale_y33 = 1.0 + +shader34 = ../../shaders/base/stock.slang +filter_linear34 = true +scale_type34 = source +scale_x34 = 1.0 +scale_y34 = 1.0 +alias34 = "PrePass" +mipmap_input34 = true + +shader35 = ../../shaders/guest/hsm-avg-lum.slang +filter_linear35 = true +scale_type35 = source +scale35 = 1.0 +mipmap_input35 = true +alias35 = "AvgLumPass" + +// Pass referenced by subsequent blurring passes and crt pass +shader36 = ../../shaders/guest/hsm-interlace-and-linearize.slang +filter_linear36 = true +scale_type36 = source +scale36 = 1.0 +float_framebuffer36 = true +alias36 = "LinearizePass" + +shader37 = ../../shaders/guest/hsm-crt-guest-advanced-ntsc-pass1.slang +filter_linear37 = true +scale_type_x37 = viewport +scale_x37 = 1.0 +scale_type_y37 = source +scale_y37 = 1.0 +float_framebuffer37 = true +alias37 = Pass1 + +shader38 = ../../shaders/guest/hsm-gaussian_horizontal.slang +filter_linear38 = true +scale_type_x38 = absolute +scale_x38 = 640.0 +scale_type_y38 = source +scale_y38 = 1.0 +float_framebuffer38 = true + +shader39 = ../../shaders/guest/hsm-gaussian_vertical.slang +filter_linear39 = true +scale_type_x39 = absolute +scale_x39 = 640.0 +scale_type_y39 = absolute +scale_y39 = 480.0 +float_framebuffer39 = true +alias39 = GlowPass + +shader40 = ../../shaders/guest/hsm-bloom_horizontal.slang +filter_linear40 = true +scale_type_x40 = absolute +scale_x40 = 640.0 +scale_type_y40 = absolute +scale_y40 = 480.0 +float_framebuffer40 = true + +shader41 = ../../shaders/guest/hsm-bloom_vertical.slang +filter_linear41 = true +scale_type_x41 = absolute +scale_x41 = 640.0 +scale_type_y41 = absolute +scale_y41 = 480.0 +float_framebuffer41 = true +alias41 = BloomPass + +shader42 = ../../shaders/guest/hsm-crt-guest-advanced-ntsc-pass2.slang +filter_linear42 = true +float_framebuffer42 = true +scale_type42 = viewport +scale_x42 = 1.0 +scale_y42 = 1.0 + +shader43 = ../../shaders/guest/hsm-deconvergence.slang +filter_linear43 = true +scale_type43 = viewport +scale_x43 = 1.0 +scale_y43 = 1.0 + +shader44 = ../../shaders/base/post-crt-prep-image-layers.slang +alias44 = "MBZ_PostCRTPass" + +// Reduce Resolution ---------------------------------------------------------------- +// Reduce the resolution to a small static size regardless of final resolution +// Allows consistent look and faster at different final resolutions for blur +// Mipmap option allows downscaling without artifacts +shader45 = ../../shaders/base/linearize-crt.slang +mipmap_input45 = true +filter_linear45 = true +scale_type45 = absolute +// scale_x45 = 480 +// scale_y45 = 270 +// scale_x45 = 960 +// scale_y45 = 540 +scale_x45 = 800 +scale_y45 = 600 +alias45 = "BR_MirrorLowResPass" + +// Add Blur for the Reflection (Horizontal) ---------------------------------------------------------------- +shader46 = ../../shaders/base/blur-outside-screen-horiz.slang +mipmap_input46 = true +filter_linear46 = true + +// Add Blur for the Reflection (Vertical) ---------------------------------------------------------------- +shader47 = ../../shaders/base/blur-outside-screen-vert.slang +filter_linear47 = true +alias47 = "BR_MirrorBlurredPass" + +// Reduce resolution ---------------------------------------------------------------- +// Reduced to a very small amount so we can create a blur which will create a glow from the screen +// Mipmap option allows smoother downscaling +shader48 = ../../../../blurs/shaders/royale/blur9x9.slang +mipmap_input48 = true +filter_linear48 = true +scale_type48 = absolute +scale_x48 = 128 +scale_y48 = 128 +alias48 = "BR_MirrorReflectionDiffusedPass" + +// Add Diffused glow all around the screen ---------------------------------------------------------------- +// Blurred so much that it's non directional +// Mipmap option allows downscaling without artifacts +shader49 = ../../../../blurs/shaders/royale/blur9x9.slang +mipmap_input49 = true +filter_linear49 = true +scale_type49 = absolute +scale_x49 = 12 +scale_y49 = 12 +alias49 = "BR_MirrorFullscreenGlowPass" + +// Bezel Reflection ---------------------------------------------------------------- +shader50 = ../../shaders/base/reflection.slang +scale_type50 = viewport +float_framebuffer50 = true +alias50 = "BR_CRTAndReflectionPass" + +// Bezel Generation & Composite of Image Layers ---------------------------------------------------------------- + +shader51 = ../../shaders/base/bezel-images-under-crt.slang +filter_linear51 = true +scale_type51 = viewport +float_framebuffer51 = true +alias51 = "BR_LayersUnderCRTPass" + +shader52 = ../../shaders/base/bezel-images-over-crt.slang +filter_linear52 = true +scale_type52 = viewport +float_framebuffer52 = true +alias52 = "BR_LayersOverCRTPass" + +// Combine Passes ---------------------------------------------------------------- +shader53 = ../../shaders/base/combine-passes.slang +scale_type53 = viewport +alias53 = "CombinePass" +// Define textures to be used by the different passes +textures = "SamplerLUT1;SamplerLUT2;SamplerLUT3;SamplerLUT4;IntroImage;ScreenPlacementImage;TubeDiffuseImage;TubeColoredGelImage;TubeShadowImage;TubeStaticReflectionImage;BackgroundImage;BackgroundVertImage;ReflectionMaskImage;FrameTextureImage;CabinetGlassImage;DeviceImage;DeviceVertImage;DeviceLEDImage;DecalImage;NightLightingImage;NightLighting2Image;LEDImage;TopLayerImage;" + +SamplerLUT1 = ../../shaders/guest/lut/trinitron-lut.png +SamplerLUT1_linear = true +SamplerLUT2 = ../../shaders/guest/lut/inv-trinitron-lut.png +SamplerLUT2_linear = true +SamplerLUT3 = ../../shaders/guest/lut/nec-lut.png +SamplerLUT3_linear = true +SamplerLUT4 = ../../shaders/guest/lut/ntsc-lut.png +SamplerLUT4_linear = true + +IntroImage = ../../shaders/textures/IntroImage_MegaBezelLogo.png +IntroImage_linear = true +IntroImage_mipmap = 1 + +ScreenPlacementImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +ScreenPlacementImage_linear = false + +TubeDiffuseImage = ../../shaders/textures/Tube_Diffuse_2390x1792.png +TubeDiffuseImage_linear = true +TubeDiffuseImage_mipmap = 1 + +TubeColoredGelImage = ../../shaders/textures/Colored_Gel_Rainbow.png +TubeColoredGelImage_linear = true +TubeColoredGelImage_mipmap = 1 + +TubeShadowImage = ../../shaders/textures/Tube_Shadow_1600x1200.png +TubeShadowImage_linear = true +TubeShadowImage_mipmap = 1 + +TubeStaticReflectionImage = ../../shaders/textures/TubeGlassOverlayImageCropped_1440x1080.png +TubeStaticReflectionImage_linear = true +TubeStaticReflectionImage_mipmap = 1 + +ReflectionMaskImage = ../../shaders/textures/Placeholder_White_16x16.png +ReflectionMaskImage_linear = true +ReflectionMaskImage_mipmap = 1 + +FrameTextureImage = ../../shaders/textures/FrameTexture_2800x2120.png +FrameTextureImage_linear = true +FrameTextureImage_mipmap = 1 + +BackgroundImage = ../../shaders/textures/BackgroundImage_Carbon_3840x2160.png +BackgroundImage_linear = true +BackgroundImage_mipmap = 1 + +BackgroundVertImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +BackgroundVertImage_linear = true +BackgroundVertImage_mipmap = 1 + +CabinetGlassImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +CabinetGlassImage_linear = true +CabinetGlassImage_mipmap = 1 + +DeviceImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +DeviceImage_linear = true +DeviceImage_mipmap = 1 + +DeviceVertImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +DeviceVertImage_linear = true +DeviceVertImage_mipmap = 1 + +DeviceLEDImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +DeviceLEDImage_linear = true +DeviceLEDImage_mipmap = 1 + +DecalImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +DecalImage_linear = true +DecalImage_mipmap = 1 + +NightLightingImage = ../../shaders/textures/NightLightingClose_1920x1080.png +NightLightingImage_linear = true +NightLightingImage_mipmap = 1 + +NightLighting2Image = ../../shaders/textures/NightLightingFar_1920x1080.png +NightLighting2Image_linear = true +NightLighting2Image_mipmap = 1 + +LEDImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +LEDImage_linear = true +LEDImage_mipmap = 1 + +TopLayerImage = ../../shaders/textures/Placeholder_Transparent_16x16.png +TopLayerImage_linear = true +TopLayerImage_mipmap = 1 + +// Use for matching vanilla GDV-Advanced +// HSM_ASPECT_RATIO_MODE = 6 +// HSM_CURVATURE_MODE = 0 + +// SMOOTH-ADV +HSM_DEDITHER_MODE = 1 + +HSM_SCALEFX_ON = 1 + +HSM_CORE_RES_SAMPLING_MULT_SCANLINE_DIR = 300 +HSM_CORE_RES_SAMPLING_MULT_OPPOSITE_DIR = 125 +HSM_DOWNSAMPLE_BLUR_SCANLINE_DIR = 0 +HSM_DOWNSAMPLE_BLUR_OPPOSITE_DIR = 0 + +ntsc_scale = 0.4 + +shadowMask = 3 + +// NTSC Parameters +GAMMA_INPUT = 2.0 +gamma_out = 1.95 + +// DREZ Parameters +SHARPEN = 0"#; +} diff --git a/librashader-presets/src/parse/value.rs b/librashader-presets/src/parse/value.rs new file mode 100644 index 0000000..b428b48 --- /dev/null +++ b/librashader-presets/src/parse/value.rs @@ -0,0 +1,217 @@ +use crate::error::ParsePresetError; +use crate::parse::{Span, Token}; +use crate::{FilterMode, ScaleFactor, ScaleType, Scaling, WrapMode}; +use nom::bytes::complete::tag; +use nom::character::complete::digit1; +use nom::combinator::{eof, map_res}; +use nom::error::Error; +use nom::IResult; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +#[derive(Debug)] +pub enum Value { + ShaderCount(i32), + Shader(i32, PathBuf), + ScaleX(i32, ScaleFactor), + ScaleY(i32, ScaleFactor), + Scale(i32, ScaleFactor), + ScaleType(i32, ScaleType), + FilterMode(i32, FilterMode), + FloatFramebuffer(i32, bool), + SrgbFramebuffer(i32, bool), + MipmapInput(i32, bool), + WrapMode(i32, WrapMode), + Alias(String), + Parameter(String, f32), + TexturePath(String, FilterMode, bool, PathBuf), +} + +fn from_int(input: Span) -> Result { + i32::from_str(*input) +} + +fn parse_indexed_key<'a>(key: &'static str, input: Span<'a>) -> IResult, i32> { + let (input, _) = tag(key)(input)?; + let (input, idx) = map_res(digit1, from_int)(input)?; + let (input, _) = eof(input)?; + Ok((input, idx)) +} + +pub const SHADER_MAX_REFERENCE_DEPTH: usize = 16; + +fn load_child_reference_strings( + mut root_references: Vec, + root_path: impl AsRef, +) -> Result, ParsePresetError> { + let root_path = root_path.as_ref(); + let mut reference_depth = 0; + let mut reference_strings: Vec<(PathBuf, String)> = Vec::new(); + while let Some(reference_path) = root_references.pop() { + if reference_depth > SHADER_MAX_REFERENCE_DEPTH { + return Err(ParsePresetError::ExceededReferenceDepth); + } + let mut root_path = root_path.to_path_buf(); + root_path.push(reference_path); + let mut reference_root = root_path + .canonicalize() + .map_err(|e| ParsePresetError::IOError(root_path, e))?; + + let mut reference_contents = String::new(); + File::open(&reference_root) + .map_err(|e| ParsePresetError::IOError(reference_root.clone(), e))? + .read_to_string(&mut reference_contents) + .map_err(|e| ParsePresetError::IOError(reference_root.clone(), e))?; + + let mut new_tokens = super::do_lex(&reference_contents)?; + let mut new_references: Vec = new_tokens + .drain_filter(|token| *token.key.fragment() == "#reference") + .map(|value| PathBuf::from(*value.value.fragment())) + .collect(); + + root_references.append(&mut new_references); + + // return the relative root that shader and texture paths are to be resolved against. + if !reference_root.is_dir() { + reference_root.pop(); + } + + // trim end space + reference_strings.push((reference_root, reference_contents)); + } + + Ok(reference_strings) +} + +pub fn parse_preset(path: impl AsRef) -> Result, ParsePresetError> { + let path = path.as_ref(); + let path = path.canonicalize() + .map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?; + + let mut contents = String::new(); + File::open(&path).and_then(|mut f| f.read_to_string(&mut contents)) + .map_err(|e| ParsePresetError::IOError(path.to_path_buf(), e))?; + + let tokens = super::token::do_lex(&contents)?; + parse_values(tokens, path) +} + +pub fn parse_values( + mut tokens: Vec, + root_path: impl AsRef, +) -> Result, ParsePresetError> { + let mut root_path = root_path.as_ref().to_path_buf(); + if root_path.is_relative() { + return Err(ParsePresetError::RootPathWasNotAbsolute); + } + if !root_path.is_dir() { + // we don't really care if this doesn't do anything because a non-canonical root path will + // fail at a later stage during resolution. + root_path.pop(); + } + + let references: Vec = tokens + .drain_filter(|token| *token.key.fragment() == "#reference") + .map(|value| PathBuf::from(*value.value.fragment())) + .collect(); + + // unfortunately we need to lex twice because there's no way to know the references ahead of time. + let child_strings = load_child_reference_strings(references, &root_path)?; + let mut all_tokens: Vec<(&Path, Vec)> = Vec::new(); + all_tokens.push((root_path.as_path(), tokens)); + + for (path, string) in child_strings.iter() { + let mut tokens = crate::parse::do_lex(string.as_ref())?; + tokens.retain(|token| *token.key.fragment() != "#reference"); + all_tokens.push((path.as_path(), tokens)) + } + + // collect all possible parameter names. + let mut parameter_names: Vec<&str> = Vec::new(); + for (_, tokens) in all_tokens.iter_mut() { + for token in tokens.drain_filter(|token| *token.key.fragment() == "parameters") { + let parameter_name_string: &str = *token.value.fragment(); + for parameter_name in parameter_name_string.split(";") { + parameter_names.push(parameter_name); + } + } + } + + // collect all possible texture names. + let mut texture_names: Vec<&str> = Vec::new(); + for (_, tokens) in all_tokens.iter_mut() { + for token in tokens.drain_filter(|token| *token.key.fragment() == "textures") { + let texture_name_string: &str = *token.value.fragment(); + for texture_name in texture_name_string.split(";") { + texture_names.push(texture_name); + } + } + } + + let mut values = Vec::new(); + // resolve shader paths. + for (ref path, tokens) in all_tokens.iter_mut() { + for token in tokens.drain_filter(|token| parse_indexed_key("shader", token.key).is_ok()) { + let (_, index) = parse_indexed_key("shader", token.key).map_err(|e| match e { + nom::Err::Error(e) | nom::Err::Failure(e) => { + let input: Span = e.input; + ParsePresetError::ParserError { + offset: input.location_offset(), + row: input.location_line(), + col: input.get_column(), + } + } + _ => ParsePresetError::ParserError { + offset: 0, + row: 0, + col: 0, + }, + })?; + + let mut relative_path = path.to_path_buf(); + relative_path.push(*token.value.fragment()); + relative_path + .canonicalize() + .map_err(|e| ParsePresetError::IOError(relative_path.clone(), e))?; + values.push(Value::Shader(index, relative_path)) + } + } + + // resolve texture paths + let mut textures = Vec::new(); + for (ref path, tokens) in all_tokens.iter_mut() { + for token in tokens.drain_filter(|token| + texture_names.contains(token.key.fragment()) + ) { + let mut relative_path = path.to_path_buf(); + relative_path.push(*token.value.fragment()); + relative_path + .canonicalize() + .map_err(|e| ParsePresetError::IOError(relative_path.clone(), e))?; + textures.push((token.key, relative_path)) + } + } + + let tokens: Vec = all_tokens + .into_iter() + .flat_map(|(_, token)| token) + .collect(); + // all tokens should be ok to process now. + Ok(values) +} + +#[cfg(test)] +mod test { + use std::path::PathBuf; + use crate::parse::value::parse_preset; + + #[test] + pub fn parse_basic() { + let root = PathBuf::from("test/slang-shaders/bezel/Mega_Bezel/Presets/MBZ__5__POTATO.slangp"); + let basic = parse_preset(&root); + eprintln!("{:?}", basic); + } +} \ No newline at end of file diff --git a/librashader-presets/src/preset.rs b/librashader-presets/src/preset.rs index f58e632..391ea89 100644 --- a/librashader-presets/src/preset.rs +++ b/librashader-presets/src/preset.rs @@ -1,28 +1,43 @@ -use std::collections::HashSet; +use crate::error::ParsePresetError; use std::convert::Infallible; use std::path::PathBuf; use std::str::FromStr; -#[repr(C)] +#[repr(i32)] +#[derive(Debug)] pub enum FilterMode { - Linear, + Linear = 0, Nearest, - Unspecified + Unspecified, } -#[repr(C)] +#[repr(i32)] +#[derive(Debug)] pub enum WrapMode { - ClampToBorder, + ClampToBorder = 0, ClampToEdge, Repeat, MirroredRepeat, } -#[repr(C)] +#[repr(i32)] +#[derive(Debug)] pub enum ScaleType { - Input, + Input = 0, Absolute, - Viewport + Viewport, +} + +#[derive(Debug)] +pub enum ScaleFactor { + Float(f32), + Absolute(i32), +} + +impl Default for ScaleFactor { + fn default() -> Self { + ScaleFactor::Float(1.0f32) + } } impl FromStr for WrapMode { @@ -34,49 +49,47 @@ impl FromStr for WrapMode { "clamp_to_edge" => WrapMode::ClampToEdge, "repeat" => WrapMode::Repeat, "mirrored_repeat" => WrapMode::MirroredRepeat, - _ => WrapMode::ClampToBorder + _ => WrapMode::ClampToBorder, }) } } impl FromStr for ScaleType { - type Err = Infallible; + type Err = ParsePresetError; fn from_str(s: &str) -> Result { - Ok(match s { - "source" => ScaleType::Input, - "viewport" => ScaleType::Viewport, - "absolute" => ScaleType::Absolute, - _ => ScaleType::Input - }) + match s { + "source" => Ok(ScaleType::Input), + "viewport" => Ok(ScaleType::Viewport), + "absolute" => Ok(ScaleType::Absolute), + _ => Err(ParsePresetError::InvalidScaleType(s.to_string())), + } } } -pub enum ScaleFactor { - Float(f32), - Absolute(i32) -} - pub struct Scaling { pub scale_type: ScaleType, - pub factor: ScaleFactor + pub factor: ScaleFactor, } pub struct Scale2D { + pub valid: bool, pub x: Scaling, - pub y: Scaling + pub y: Scaling, } pub struct ShaderConfig { + pub id: usize, pub name: String, pub alias: String, pub filter: FilterMode, pub wrap_mode: WrapMode, - pub frame_count_mod: usize, + pub frame_count_mod: i32, pub srgb_framebuffer: bool, pub float_framebuffer: bool, + pub feedback_pass: i32, pub mipmap_input: bool, - pub scaling: Scale2D + pub scaling: Scale2D, } pub struct TextureConfig { @@ -84,7 +97,7 @@ pub struct TextureConfig { pub path: PathBuf, pub wrap_mode: WrapMode, pub filter: FilterMode, - pub mipmap: bool + pub mipmap: bool, } pub struct Parameter { @@ -96,5 +109,5 @@ pub struct Preset { // Everything is in Vecs because the expect number of values is well below 64. pub shaders: Vec, pub textures: Vec, - pub parameters: Vec -} \ No newline at end of file + pub parameters: Vec, +} diff --git a/librashader-presets/test/slang-shaders b/librashader-presets/test/slang-shaders new file mode 160000 index 0000000..9e89aaf --- /dev/null +++ b/librashader-presets/test/slang-shaders @@ -0,0 +1 @@ +Subproject commit 9e89aafe0f6a63645445ca8302b88e4060274c72 diff --git a/target/.rustc_info.json b/target/.rustc_info.json index 8cb86af..5f43b7e 100644 --- a/target/.rustc_info.json +++ b/target/.rustc_info.json @@ -1 +1 @@ -{"rustc_fingerprint":16488513895628825392,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\ronny\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"llvm14-builtins-abi\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0-nightly (750bd1a7f 2022-09-14)\nbinary: rustc\ncommit-hash: 750bd1a7ff3e010611b97ee75d30b7cbf5f3a03c\ncommit-date: 2022-09-14\nhost: x86_64-pc-windows-msvc\nrelease: 1.65.0-nightly\nLLVM version: 15.0.0\n","stderr":""}},"successes":{}} \ No newline at end of file +{"rustc_fingerprint":1842804115855352081,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\ronny\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"llvm14-builtins-abi\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0-nightly (750bd1a7f 2022-09-14)\nbinary: rustc\ncommit-hash: 750bd1a7ff3e010611b97ee75d30b7cbf5f3a03c\ncommit-date: 2022-09-14\nhost: x86_64-pc-windows-msvc\nrelease: 1.65.0-nightly\nLLVM version: 15.0.0\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\n","stderr":""}},"successes":{}} \ No newline at end of file