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