diff --git a/Cargo.lock b/Cargo.lock index d35659b..986055f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "data-stream" version = "0.3.0" @@ -14,7 +29,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -28,6 +52,17 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dialogi" version = "0.3.4" @@ -44,6 +79,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "event-simulation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385e158790550ea8adf4f98b041f56495ffedad0fcb1c86ea269bd304a84fcb1" + [[package]] name = "hashbrown" version = "0.15.2" @@ -95,6 +136,15 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "logical-expressions" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb9eb33ca2350598da17f3d89f12624ebecad64c7484a87c7e5ff1d6275c1e83" +dependencies = [ + "thiserror", +] + [[package]] name = "maud" version = "0.27.0" @@ -117,6 +167,32 @@ dependencies = [ "syn", ] +[[package]] +name = "multilinear" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b2bafc947d0249a035c5d75c8ea5f05a120479660c0e579b3f30cb30f46850" +dependencies = [ + "bit-set", + "data-stream", + "derive_more 2.0.1", + "event-simulation", + "indexmap", + "thiserror", +] + +[[package]] +name = "multilinear-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91bd8cba78d3fe7ed3fb9699f5cb2d069f9f969063969c45e232082eedd17928" +dependencies = [ + "header-parsing", + "logical-expressions", + "multilinear", + "thiserror", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -160,7 +236,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e9bd9e9e6aeeeee9620b059c2f44784e7d1bd4fa299055b8e66de8b28e9d5d4" dependencies = [ - "derive_more", + "derive_more 1.0.0", ] [[package]] @@ -172,6 +248,7 @@ dependencies = [ "header-config", "indexmap", "maud", + "multilinear-parser", "percent-encoding", "pukram2html", "threadpool", diff --git a/Cargo.toml b/Cargo.toml index 0e64ecd..0cc6b9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ vn-settings = "0.1.1" threadpool = "1.8.1" header-config = "0.1.5" indexmap = "2.9.0" +multilinear-parser = "0.3.3" diff --git a/src/main.rs b/src/main.rs index c68a15e..a56d55a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -300,6 +300,7 @@ fn handle_connection( } let mut pk_path = path.clone(); + let mut mld_path = path.clone(); let mut data_path = path.clone(); let (pki_path, audio_path, start_level, relative_path) = if relative_path.is_empty() { @@ -325,6 +326,7 @@ fn handle_connection( } pk_path.push(format!("{path}.pk")); + mld_path.push(format!("{path}.mld")); if access == Access::Full { pki_path.push(format!("{path}.pki")); } else { @@ -338,6 +340,7 @@ fn handle_connection( let file_paths = DocumentPaths { pk: &pk_path, + mld: &mld_path, pki: pki_path.as_ref().map(PathBuf::as_ref), audio: audio_path.as_ref().map(PathBuf::as_ref), data: &data_path, @@ -429,6 +432,7 @@ fn reply_binary( #[derive(Copy, Clone)] struct DocumentPaths<'a> { pk: &'a Path, + mld: &'a Path, pki: Option<&'a Path>, audio: Option<&'a Path>, data: &'a Path, @@ -450,6 +454,8 @@ fn handle_relative_connection( let mut up = false; let mut down = false; + let mut choice = 0; + let mut progress = ""; for entry in body.split('&') { let Some((key, input)) = entry.split_once('=') else { @@ -486,6 +492,8 @@ fn handle_relative_connection( } "up" => up = true, "down" => down = true, + "choice" => choice = input.parse().unwrap_or_default(), + "progress" => progress = input, _ => (), } } @@ -601,7 +609,17 @@ fn handle_relative_connection( .unwrap_or_default(); if let Some(config_map) = config_map { - if render_novel(config_map, file_paths.pk, &mut stream, start_level).is_err() { + if render_novel( + config_map, + file_paths.pk, + file_paths.mld, + &mut stream, + start_level, + choice, + progress, + ) + .is_err() + { fail(stream); return; } diff --git a/src/vn.rs b/src/vn.rs index 583af31..d974b1c 100644 --- a/src/vn.rs +++ b/src/vn.rs @@ -1,7 +1,8 @@ -use std::{collections::HashMap, io::prelude::*, net::TcpStream, path::Path}; +use std::{collections::HashMap, fs::File, io::prelude::*, net::TcpStream, path::Path}; use indexmap::IndexMap; use maud::{Markup, html}; +use multilinear_parser::{NamedMultilinearInfo, parse_multilinear}; use pukram2html::convert_subheader; use vn_settings::{Change, Parameter, PlayerSettings, SettingsContext, extract_layers}; @@ -270,11 +271,22 @@ fn interactive_script(total_sections: usize) -> Markup { } } +fn load_multilinear(mld_path: &Path) -> Option { + let Ok(file) = File::open(mld_path) else { + return None; + }; + + parse_multilinear(file).ok() +} + pub fn render_novel( mut config_map: IndexMap, Box>, pk_path: &Path, + mld_path: &Path, stream: &mut TcpStream, start_level: usize, + choice: usize, + progress: &str, ) -> Result<(), dialogi::ParsingError> { let mut settings_context = SettingsContext::new(); extract_layers(&mut settings_context.layers, &mut config_map); @@ -282,41 +294,63 @@ pub fn render_novel( let mut player_settings = PlayerSettings::common(); player_settings.extract_settings(&mut settings_context, &mut config_map); + let named_multilinear_info = load_multilinear(mld_path); + let named_multilinear_info = named_multilinear_info.as_ref(); + let dialogs = parse_map(pk_path, &mut settings_context)?; - let (scenes, sections) = process_dialogs(dialogs, &mut player_settings, start_level); + let (scenes, sections) = process_dialog(&dialogs[choice], &mut player_settings, start_level); let html = generate_html(scenes, sections); let _ = write!(stream, "{}", html.into_string()); + if let Some(_named_multilinear_info) = named_multilinear_info { + for (i, dialog_sequence) in dialogs.iter().enumerate() { + if let Some(block) = dialog_sequence + .blocks + .iter() + .find(|block| !block.lines.is_empty()) + { + let line_text = &block.lines.first().unwrap().text; + + let html = html! { + form method="POST" { + input type="hidden" name="progress" value=(progress); + input type="hidden" name="choice" value=(i); + input type="submit" value=(line_text); + } + }; + let _ = write!(stream, "{}", html.into_string()); + } + } + } + Ok(()) } -fn process_dialogs( - dialogs: Vec>, +fn process_dialog( + dialog_sequence: &dialogi::DialogSequence, player_settings: &mut PlayerSettings, start_level: usize, ) -> (Vec, Vec) { let mut scenes = Vec::new(); let mut sections = Vec::new(); - for dialog_sequence in dialogs { - let mut states = initialize_change_states(&dialog_sequence.changes); + let mut states = initialize_change_states(&dialog_sequence.changes); - for block in dialog_sequence.blocks { - apply_block_changes( - &block, - &dialog_sequence.changes, - &mut states, - player_settings, - ); + for block in &dialog_sequence.blocks { + apply_block_changes( + block, + &dialog_sequence.changes, + &mut states, + player_settings, + ); - scenes.push(render_scene(player_settings, &block.name).into_string()); - sections.push(render_dialog_block(&block, start_level)); - } - - player_settings.reset(); + scenes.push(render_scene(player_settings, &block.name).into_string()); + sections.push(render_dialog_block(block, start_level)); } + player_settings.reset(); + (scenes, sections) }