From 7f84b2fe9a077b81f562dcb59f4798f0ffe6f400 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 15:04:32 +0100 Subject: [PATCH 001/184] Add mod config option for loose files Just the field in the config file, for now. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmt/src/cmd/migrate.rs | 1 + lib/dtmt-shared/src/lib.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 55774aa..c43e973 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,6 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, + pub bundle: bool, #[data(ignore)] pub nexus: Option, } @@ -129,6 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, + bundle: cfg.bundle, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 1a4d605..8ecd648 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,6 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], + bundle: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3907928..a162d3b 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -30,6 +30,17 @@ pub enum ModDependency { Config { id: String, order: ModOrder }, } +// A bit dumb, but serde doesn't support literal values with the +// `default` attribute, only paths. +fn default_true() -> bool { + true +} + +// Similarly dumb, as the `skip_serializing_if` attribute needs a function +fn is_true(val: &bool) -> bool { + *val +} + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfig { #[serde(skip)] @@ -51,6 +62,8 @@ pub struct ModConfig { pub resources: ModConfigResources, #[serde(default)] pub depends: Vec, + #[serde(default = "default_true", skip_serializing_if = "is_true")] + pub bundle: bool, } pub const STEAMAPP_ID: u32 = 1361210; From 192f942927ade9041d1865295f1910fd7547eb70 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 15:06:53 +0100 Subject: [PATCH 002/184] Apply formatting --- crates/dtmm/src/controller/game.rs | 8 ++++---- crates/dtmt/src/cmd/dictionary.rs | 5 ++++- lib/nexusmods/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index a5f6fa8..5520c4a 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -583,12 +583,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { }, async { let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path) - .await - { + match read_sjson_file::<_, DeploymentData>(path).await { Ok(data) => Ok(Some(data)), Err(err) => { - if let Some(err) = err.downcast_ref::() && err.kind() == ErrorKind::NotFound { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { Ok(None) } else { Err(err).wrap_err("Failed to read deployment data") diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 0400519..62a5295 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -145,7 +145,10 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( .get_one::("group") .expect("required argument not found"); - let r: BufReader> = if let Some(name) = path.file_name() && name == "-" { + let r: BufReader> = if let Some(name) = + path.file_name() + && name == "-" + { let f = tokio::io::stdin(); BufReader::new(Box::new(f)) } else { diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index a0700ae..3dbbecd 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -215,7 +215,7 @@ impl Api { }; let user_id = query.get("user_id").and_then(|id| id.parse().ok()); - let Some(user_id) = user_id else { + let Some(user_id) = user_id else { return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); }; From 7a325b0361607866d374ff09a50615f4724677e3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 19:48:05 +0100 Subject: [PATCH 003/184] Apply clippy lints --- lib/color-eyre | 2 +- lib/nexusmods/src/lib.rs | 2 +- lib/sdk/src/bundle/mod.rs | 11 ++++------- lib/sdk/src/filetype/lua.rs | 4 ++-- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/color-eyre b/lib/color-eyre index 55f8c6b..dc427b0 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 55f8c6b7481d462e50ee4a03a43253d80d648ae2 +Subproject commit dc427b06422ec01c4938e88c3aabaf7079332171 diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 3dbbecd..c305db0 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -154,7 +154,7 @@ impl Api { self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) )?; - let Some(download_url) = download_info.get(0).map(|i| i.uri.clone()) else { + let Some(download_url) = download_info.first().map(|i| i.uri.clone()) else { return Err(Error::InvalidNXM("no download link", url)); }; diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 96a2ec2..813df06 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -227,13 +227,10 @@ impl Bundle { let _enter = span.enter(); tracing::trace!(num_files = self.files.len()); - self.files - .iter() - .fold(Ok::, Report>(Vec::new()), |data, file| { - let mut data = data?; - data.append(&mut file.to_binary()?); - Ok(data) - })? + self.files.iter().try_fold(Vec::new(), |mut data, file| { + data.append(&mut file.to_binary()?); + Ok::<_, Report>(data) + })? }; // Ceiling division (or division toward infinity) to calculate diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 3979b7a..8de212f 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -53,8 +53,8 @@ where let mut buf = vec![0u8; length]; r.read_exact(&mut buf)?; - let mut s = String::from_utf8(buf) - .wrap_err_with(|| format!("Invalid byte sequence for LuaJIT bytecode name"))?; + let mut s = + String::from_utf8(buf).wrap_err("Invalid byte sequence for LuaJIT bytecode name")?; // Remove the leading `@` s.remove(0); s From 00673be55799ea7a43ab65e24373ae1469c0d706 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 19:51:25 +0100 Subject: [PATCH 004/184] Update luajit2-sys --- lib/luajit2-sys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 24da35e..1912016 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 24da35e631099e914d6fc1bcc863228c48e540ec +Subproject commit 19120166f9fc7838b98c71fc348791abc820e323 From 78e4d644f0fc61d14f4a8c6f447a4b7015795ffc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 20:10:54 +0100 Subject: [PATCH 005/184] Implement deploying non-bundled mods Closes #113. --- Cargo.lock | 50 +- Cargo.toml | 5 +- crates/dtmm/Cargo.toml | 4 +- crates/dtmm/src/controller/app.rs | 170 +----- crates/dtmm/src/controller/deploy.rs | 875 +++++++++++++++++++++++++++ crates/dtmm/src/controller/game.rs | 622 +------------------ crates/dtmm/src/controller/import.rs | 475 +++++++++++++++ crates/dtmm/src/controller/mod.rs | 2 + crates/dtmm/src/controller/worker.rs | 2 + crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 4 +- crates/dtmm/src/ui/window/main.rs | 2 +- crates/dtmt/src/cmd/migrate.rs | 2 +- lib/dtmt-shared/src/lib.rs | 2 +- 14 files changed, 1396 insertions(+), 820 deletions(-) create mode 100644 crates/dtmm/src/controller/deploy.rs create mode 100644 crates/dtmm/src/controller/import.rs diff --git a/Cargo.lock b/Cargo.lock index a7885f2..011fa6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,10 @@ dependencies = [ [[package]] name = "ansi-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127" +version = "0.9.0" dependencies = [ "heapless", - "nom 4.2.3", + "nom", ] [[package]] @@ -381,7 +379,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -896,6 +894,7 @@ name = "dtmm" version = "0.1.0" dependencies = [ "ansi-parser", + "async-recursion", "bitflags 1.3.2", "clap", "color-eyre", @@ -906,6 +905,7 @@ dependencies = [ "dtmt-shared", "futures", "lazy_static", + "luajit2-sys", "nexusmods", "oodle", "path-slash", @@ -1390,7 +1390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1614,12 +1614,12 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heapless" -version = "0.5.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" dependencies = [ "as-slice", - "generic-array 0.13.3", + "generic-array 0.14.7", "hash32", "stable_deref_trait", ] @@ -1747,7 +1747,7 @@ dependencies = [ "serde", "sized-chunks", "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2175,16 +2175,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "nom" version = "7.1.3" @@ -2203,7 +2193,7 @@ checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" dependencies = [ "bytecount", "memchr", - "nom 7.1.3", + "nom", ] [[package]] @@ -2673,7 +2663,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2684,7 +2674,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3102,7 +3092,7 @@ dependencies = [ name = "serde_sjson" version = "1.0.0" dependencies = [ - "nom 7.1.3", + "nom", "nom_locate", "serde", ] @@ -3832,7 +3822,7 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3966,12 +3956,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -4384,7 +4368,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "ansi-parser" -version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 123b2ef..451cd09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,7 @@ members = [ "lib/oodle", "lib/sdk", "lib/serde_sjson", -] -exclude = [ - "lib/color-eyre", - "lib/ansi-parser", + "lib/luajit2-sys", ] [patch.crates-io] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 86444be..1842a62 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -31,5 +31,7 @@ lazy_static = "1.4.0" colors-transform = "0.2.11" usvg = "0.25.0" druid-widget-nursery = "0.1" -ansi-parser = "0.8.0" +ansi-parser = "0.9.0" string_template = "0.2.1" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +async-recursion = "1.0.5" diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 5c5d980..cb6e2f0 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -1,18 +1,17 @@ use std::collections::HashMap; -use std::io::{Cursor, ErrorKind, Read}; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::sync::Arc; use color_eyre::eyre::{self, Context}; use color_eyre::{Help, Report, Result}; use druid::im::Vector; -use druid::{FileInfo, ImageBuf}; +use druid::ImageBuf; use dtmt_shared::ModConfig; use nexusmods::Api as NexusApi; use tokio::fs::{self, DirEntry, File}; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; -use zip::ZipArchive; use crate::state::{ActionState, InitialLoadResult, ModInfo, ModOrder, NexusInfo, PackageInfo}; use crate::util; @@ -20,161 +19,6 @@ use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; -#[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { - let data = fs::read(&info.path) - .await - .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; - let data = Cursor::new(data); - - let nexus = if let Some((_, id, _, _)) = info - .path - .file_name() - .and_then(|s| s.to_str()) - .and_then(NexusApi::parse_file_name) - { - if !state.nexus_api_key.is_empty() { - let api = NexusApi::new(state.nexus_api_key.to_string())?; - let mod_info = api - .mods_id(id) - .await - .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; - Some(NexusInfo::from(mod_info)) - } else { - None - } - } else { - None - }; - - let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; - - if tracing::enabled!(tracing::Level::DEBUG) { - let names = archive.file_names().fold(String::new(), |mut s, name| { - s.push('\n'); - s.push_str(name); - s - }); - tracing::debug!("Archive contents:{}", names); - } - - let dir_name = { - let f = archive.by_index(0).wrap_err("Archive is empty")?; - - if !f.is_dir() { - let err = eyre::eyre!("archive does not have a top-level directory"); - return Err(err).with_suggestion(|| "Use 'dtmt build' to create the mod archive."); - } - - let name = f.name(); - // The directory name is returned with a trailing slash, which we don't want - name[..(name.len().saturating_sub(1))].to_string() - }; - - tracing::info!("Importing mod {}", dir_name); - - let names: Vec<_> = archive.file_names().map(|s| s.to_string()).collect(); - - let mod_cfg: ModConfig = { - let name = names - .iter() - .find(|name| name.ends_with("dtmt.cfg")) - .ok_or_else(|| eyre::eyre!("archive does not contain mod config"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read mod config from archive")?; - - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read mod config from archive")?; - - let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")? - }; - - tracing::debug!(?mod_cfg); - - let files: HashMap> = { - let name = names - .iter() - .find(|name| name.ends_with("files.sjson")) - .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read file index from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? - }; - - tracing::trace!(?files); - - let image = if let Some(path) = &mod_cfg.image { - let name = names - .iter() - .find(|name| name.ends_with(&path.display().to_string())) - .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read image file from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - // Druid somehow doesn't return an error compatible with eyre, here. - // So we have to wrap through `Display` manually. - let img = match ImageBuf::from_data(&buf) { - Ok(img) => img, - Err(err) => { - let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); - return Err(err).with_suggestion(|| { - "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() - }); - } - }; - - Some(img) - } else { - None - }; - - let mod_dir = state.mod_dir; - - tracing::trace!("Creating mods directory {}", mod_dir.display()); - fs::create_dir_all(Arc::as_ref(&mod_dir)) - .await - .wrap_err_with(|| format!("Failed to create data directory {}", mod_dir.display()))?; - - tracing::trace!("Extracting mod archive to {}", mod_dir.display()); - archive - .extract(Arc::as_ref(&mod_dir)) - .wrap_err_with(|| format!("Failed to extract archive to {}", mod_dir.display()))?; - - if let Some(nexus) = &nexus { - let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; - let path = mod_dir.join(&mod_cfg.id).join("nexus.sjson"); - fs::write(&path, data.as_bytes()) - .await - .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; - } - - let packages = files - .into_iter() - .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) - .collect(); - let info = ModInfo::new(mod_cfg, packages, image, nexus); - - Ok(info) -} - #[tracing::instrument(skip(state))] pub(crate) async fn delete_mod(state: ActionState, info: &ModInfo) -> Result<()> { let mod_dir = state.mod_dir.join(&info.id); @@ -229,9 +73,13 @@ async fn read_mod_dir_entry(res: Result) -> Result { Err(err) => return Err(err), }; - let files: HashMap> = read_sjson_file(&index_path) - .await - .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; + let files: HashMap> = if cfg.bundled { + read_sjson_file(&index_path) + .await + .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))? + } else { + Default::default() + }; let image = if let Some(path) = &cfg.image { let path = entry.path().join(path); diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs new file mode 100644 index 0000000..6a804e7 --- /dev/null +++ b/crates/dtmm/src/controller/deploy.rs @@ -0,0 +1,875 @@ +use std::collections::HashMap; +use std::io::{Cursor, ErrorKind}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::Arc; + +use color_eyre::eyre::Context; +use color_eyre::{eyre, Help, Report, Result}; +use futures::StreamExt; +use futures::{stream, TryStreamExt}; +use path_slash::PathBufExt; +use sdk::filetype::lua; +use sdk::filetype::package::Package; +use sdk::murmur::Murmur64; +use sdk::{ + Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, +}; +use serde::{Deserialize, Serialize}; +use string_template::Template; +use time::OffsetDateTime; +use tokio::fs::{self, DirEntry}; +use tokio::io::AsyncWriteExt; +use tracing::Instrument; + +use super::read_sjson_file; +use crate::controller::app::check_mod_order; +use crate::state::{ActionState, PackageInfo}; + +pub const MOD_BUNDLE_NAME: &str = "packages/mods"; +pub const BOOT_BUNDLE_NAME: &str = "packages/boot"; +pub const DML_BUNDLE_NAME: &str = "packages/dml"; +pub const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; +pub const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; +pub const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; +pub const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; +pub const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; + +#[derive(Debug, Serialize, Deserialize)] +pub struct DeploymentData { + pub bundles: Vec, + pub mod_folders: Vec, + #[serde(with = "time::serde::iso8601")] + pub timestamp: OffsetDateTime, +} + +#[tracing::instrument] +async fn read_file_with_backup

(path: P) -> Result> +where + P: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let backup_path = { + let mut p = PathBuf::from(path); + let ext = if let Some(ext) = p.extension() { + ext.to_string_lossy().to_string() + ".bak" + } else { + String::from("bak") + }; + p.set_extension(ext); + p + }; + + let file_name = path + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| String::from("file")); + + let bin = match fs::read(&backup_path).await { + Ok(bin) => bin, + Err(err) if err.kind() == ErrorKind::NotFound => { + // TODO: This doesn't need to be awaited here, yet. + // I only need to make sure it has finished before writing the changed bundle. + tracing::debug!( + "Backup does not exist. Backing up original {} to '{}'", + file_name, + backup_path.display() + ); + fs::copy(path, &backup_path).await.wrap_err_with(|| { + format!( + "Failed to back up {} '{}' to '{}'", + file_name, + path.display(), + backup_path.display() + ) + })?; + + tracing::debug!("Reading {} from original '{}'", file_name, path.display()); + fs::read(path).await.wrap_err_with(|| { + format!("Failed to read {} file: {}", file_name, path.display()) + })? + } + Err(err) => { + return Err(err).wrap_err_with(|| { + format!( + "Failed to read {} from backup '{}'", + file_name, + backup_path.display() + ) + }); + } + }; + Ok(bin) +} + +#[tracing::instrument(skip_all)] +async fn patch_game_settings(state: Arc) -> Result<()> { + let settings_path = state.game_dir.join("bundle").join(SETTINGS_FILE_PATH); + + let settings = read_file_with_backup(&settings_path) + .await + .wrap_err("Failed to read settings.ini")?; + let settings = String::from_utf8(settings).wrap_err("Settings.ini is not valid UTF-8")?; + + let mut f = fs::File::create(&settings_path) + .await + .wrap_err_with(|| format!("Failed to open {}", settings_path.display()))?; + + let Some(i) = settings.find("boot_script =") else { + eyre::bail!("couldn't find 'boot_script' field"); + }; + + f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(b"boot_script = \"scripts/mod_main\"").await?; + + let Some(j) = settings[i..].find('\n') else { + eyre::bail!("couldn't find end of 'boot_script' field"); + }; + + f.write_all(settings[(i + j)..].as_bytes()).await?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(package = info.name))] +fn make_package(info: &PackageInfo) -> Result { + let mut pkg = Package::new(info.name.clone(), PathBuf::new()); + + for f in &info.files { + let mut it = f.rsplit('.'); + let file_type = it + .next() + .ok_or_else(|| eyre::eyre!("missing file extension")) + .and_then(BundleFileType::from_str) + .wrap_err("Invalid file name in package info")?; + let name: String = it.collect(); + pkg.add_file(file_type, name); + } + + Ok(pkg) +} + +#[tracing::instrument] +async fn copy_recursive( + from: impl Into + std::fmt::Debug, + to: impl AsRef + std::fmt::Debug, +) -> Result<()> { + let to = to.as_ref(); + + #[tracing::instrument] + async fn handle_dir(from: PathBuf) -> Result> { + let mut dir = fs::read_dir(&from) + .await + .wrap_err("Failed to read directory")?; + let mut entries = Vec::new(); + + while let Some(entry) = dir.next_entry().await? { + let meta = entry.metadata().await.wrap_err_with(|| { + format!("Failed to get metadata for '{}'", entry.path().display()) + })?; + entries.push((meta.is_dir(), entry)); + } + + Ok(entries) + } + + let base = from.into(); + stream::unfold(vec![base.clone()], |mut state| async { + let from = state.pop()?; + let inner = match handle_dir(from).await { + Ok(entries) => { + for (is_dir, entry) in &entries { + if *is_dir { + state.push(entry.path()); + } + } + stream::iter(entries).map(Ok).left_stream() + } + Err(e) => stream::once(async { Err(e) }).right_stream(), + }; + + Some((inner, state)) + }) + .flatten() + .try_for_each(|(is_dir, entry)| { + let path = entry.path(); + let dest = path + .strip_prefix(&base) + .map(|suffix| to.join(suffix)) + .expect("all entries are relative to the directory we are walking"); + + async move { + if is_dir { + tracing::trace!("Creating directory '{}'", dest.display()); + fs::create_dir(&dest) + .await + .map(|_| ()) + .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + } else { + tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); + fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { + format!( + "Failed to copy file '{}' -> '{}'", + path.display(), + dest.display() + ) + }) + } + } + }) + .await + .map(|_| ()) +} + +#[tracing::instrument(skip(state))] +async fn copy_mod_folders(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut tasks = Vec::new(); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && !m.bundled) + { + let span = tracing::trace_span!("copying legacy mod", name = mod_info.name); + let _enter = span.enter(); + + let mod_id = mod_info.id.clone(); + let mod_dir = Arc::clone(&state.mod_dir); + let bundle_dir = Arc::clone(&bundle_dir); + + let task = async move { + let from = mod_dir.join(&mod_id); + let to = bundle_dir.join("mods").join(&mod_id); + + tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); + let _ = fs::create_dir_all(&to).await; + copy_recursive(&from, &to).await.wrap_err_with(|| { + format!( + "Failed to copy legacy mod from '{}' to '{}'", + from.display(), + to.display() + ) + })?; + + Ok::<_, Report>(mod_id) + }; + tasks.push(task); + } + + let ids = futures::future::try_join_all(tasks).await?; + Ok(ids) +} + +fn build_mod_data_lua(state: Arc) -> String { + let mut lua = String::from("return {\n"); + + // DMF is handled explicitely by the loading procedures, as it actually drives most of that + // and should therefore not show up in the load order. + for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { + lua.push_str(" {\n name = \""); + lua.push_str(&mod_info.name); + + lua.push_str("\",\n id = \""); + lua.push_str(&mod_info.id); + + lua.push_str("\",\n bundled = \""); + if mod_info.bundled { + lua.push_str("true"); + } else { + lua.push_str("false"); + } + + lua.push_str("\",\n run = function()\n"); + + let resources = &mod_info.resources; + if resources.data.is_some() || resources.localization.is_some() { + lua.push_str(" new_mod(\""); + lua.push_str(&mod_info.id); + lua.push_str("\", {\n mod_script = \""); + lua.push_str(&resources.init.to_slash_lossy()); + + if let Some(data) = resources.data.as_ref() { + lua.push_str("\",\n mod_data = \""); + lua.push_str(&data.to_slash_lossy()); + } + + if let Some(localization) = &resources.localization { + lua.push_str("\",\n mod_localization = \""); + lua.push_str(&localization.to_slash_lossy()); + } + + lua.push_str("\",\n })\n"); + } else { + lua.push_str(" return dofile(\""); + lua.push_str(&resources.init.to_slash_lossy()); + lua.push_str("\")\n"); + } + + lua.push_str(" end,\n packages = {\n"); + + for pkg_info in &mod_info.packages { + lua.push_str(" \""); + lua.push_str(&pkg_info.name); + lua.push_str("\",\n"); + } + + lua.push_str(" },\n },\n"); + } + + lua.push('}'); + + tracing::debug!("mod_data_lua:\n{}", lua); + + lua +} + +#[tracing::instrument(skip_all)] +async fn build_bundles(state: Arc) -> Result> { + let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); + let mut tasks = Vec::new(); + + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut bundles = Vec::new(); + + { + tracing::trace!("Building mod data script"); + + let span = tracing::debug_span!("Building mod data script"); + let _enter = span.enter(); + + let lua = build_mod_data_lua(state.clone()); + + tracing::trace!("Compiling mod data script"); + + let file = + lua::compile(MOD_DATA_SCRIPT, lua).wrap_err("Failed to compile mod data Lua file")?; + + tracing::trace!("Compile mod data script"); + + mod_bundle.add_file(file); + } + + tracing::trace!("Preparing tasks to deploy bundle files"); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && m.bundled) + { + let span = tracing::trace_span!("building mod packages", name = mod_info.name); + let _enter = span.enter(); + + let mod_dir = state.mod_dir.join(&mod_info.id); + for pkg_info in &mod_info.packages { + let span = tracing::trace_span!("building package", name = pkg_info.name); + let _enter = span.enter(); + + tracing::trace!( + "Building package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; + let mut variant = BundleFileVariant::new(); + let bin = pkg + .to_binary() + .wrap_err("Failed to serialize package to binary")?; + variant.set_data(bin); + let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); + file.add_variant(variant); + + tracing::trace!( + "Compiled package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + mod_bundle.add_file(file); + + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = mod_dir.join(&bundle_name); + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + // Explicitely drop the guard, so that we can move the span + // into the async operation + drop(_enter); + + let ctx = state.ctx.clone(); + + let task = async move { + let bundle = { + let bin = fs::read(&src).await.wrap_err_with(|| { + format!("Failed to read bundle file '{}'", src.display()) + })?; + let name = Bundle::get_name_from_path(&ctx, &src); + Bundle::from_binary(&ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? + }; + + tracing::debug!( + src = %src.display(), + dest = %dest.display(), + "Copying bundle '{}' for mod '{}'", + pkg_name, + mod_name, + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + + Ok::(bundle) + } + .instrument(span); + + tasks.push(task); + } + } + + tracing::debug!("Copying {} mod bundles", tasks.len()); + + let mut tasks = stream::iter(tasks).buffer_unordered(10); + + while let Some(res) = tasks.next().await { + let bundle = res?; + bundles.push(bundle); + } + + { + let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); + tracing::trace!("Writing mod bundle to '{}'", path.display()); + fs::write(&path, mod_bundle.to_binary()?) + .await + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; + } + + bundles.push(mod_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all)] +async fn patch_boot_bundle(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); + + let mut bundles = Vec::with_capacity(2); + + let mut boot_bundle = async { + let bin = read_file_with_backup(&bundle_path) + .await + .wrap_err("Failed to read boot bundle")?; + + Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) + .wrap_err("Failed to parse boot bundle") + } + .instrument(tracing::trace_span!("read boot bundle")) + .await + .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; + + { + tracing::trace!("Adding mod package file to boot bundle"); + let span = tracing::trace_span!("create mod package file"); + let _enter = span.enter(); + + let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); + + for mod_info in &state.mods { + for pkg_info in &mod_info.packages { + pkg.add_file(BundleFileType::Package, &pkg_info.name); + } + } + + pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); + + let mut variant = BundleFileVariant::new(); + variant.set_data(pkg.to_binary()?); + let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + tracing::trace!("Handling DML packages and bundle"); + let span = tracing::trace_span!("handle DML"); + let _enter = span.enter(); + + let mut variant = BundleFileVariant::new(); + + let mod_info = state + .mods + .iter() + .find(|m| m.id == "dml") + .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; + let pkg_info = mod_info + .packages + .get(0) + .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) + .with_suggestion(|| "Re-download and import the newest version.".to_string())?; + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); + + { + let bin = fs::read(&src) + .await + .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; + let name = Bundle::get_name_from_path(&state.ctx, &src); + + let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; + + bundles.push(dml_bundle); + }; + + { + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + tracing::debug!( + "Copying bundle {} for mod {}: {} -> {}", + pkg_name, + mod_name, + src.display(), + dest.display() + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + } + + let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; + variant.set_data(pkg.to_binary()?); + + let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + let span = tracing::debug_span!("Importing mod main script"); + let _enter = span.enter(); + + let is_io_enabled = format!("{}", state.is_io_enabled); + let mut data = HashMap::new(); + data.insert("is_io_enabled", is_io_enabled.as_str()); + + let tmpl = include_str!("../../assets/mod_main.lua"); + let lua = Template::new(tmpl).render(&data); + tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); + let file = + lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; + + boot_bundle.add_file(file); + } + + async { + let bin = boot_bundle + .to_binary() + .wrap_err("Failed to serialize boot bundle")?; + fs::write(&bundle_path, bin) + .await + .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) + } + .instrument(tracing::trace_span!("write boot bundle")) + .await?; + + bundles.push(boot_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); + + let mut db = { + let bin = read_file_with_backup(&database_path) + .await + .wrap_err("Failed to read bundle database")?; + let mut r = Cursor::new(bin); + let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; + tracing::trace!("Finished parsing bundle database"); + db + }; + + for bundle in bundles.as_ref() { + tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); + db.add_bundle(bundle); + } + + { + let bin = db + .to_binary() + .wrap_err("Failed to serialize bundle database")?; + fs::write(&database_path, bin).await.wrap_err_with(|| { + format!( + "failed to write bundle database to '{}'", + database_path.display() + ) + })?; + } + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn write_deployment_data( + state: Arc, + bundles: B, + mod_folders: Vec, +) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let info = DeploymentData { + timestamp: OffsetDateTime::now_utc(), + bundles: bundles + .as_ref() + .iter() + .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) + .collect(), + // TODO: + mod_folders, + }; + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; + + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields( + game_dir = %state.game_dir.display(), + mods = state.mods.len() +))] +pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { + let state = Arc::new(state); + let bundle_dir = state.game_dir.join("bundle"); + let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); + + if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) + .await + .is_ok() + { + let err = eyre::eyre!("Found dtkit-patch-based mod installation."); + return Err(err) + .with_suggestion(|| { + "If you're a mod author and saved projects directly in 'mods/', \ + use DTMT to migrate them to the new project structure." + .to_string() + }) + .with_suggestion(|| { + "Click 'Reset Game' to remove the previous mod installation.".to_string() + }); + } + + let (_, game_info, deployment_info) = tokio::try_join!( + async { + fs::metadata(&bundle_dir) + .await + .wrap_err("Failed to open game bundle directory") + .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") + }, + async { + tokio::task::spawn_blocking(dtmt_shared::collect_game_info) + .await + .map_err(Report::new) + }, + async { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + match read_sjson_file::<_, DeploymentData>(path).await { + Ok(data) => Ok(Some(data)), + Err(err) => { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { + Ok(None) + } else { + Err(err).wrap_err("Failed to read deployment data") + } + } + } + } + ) + .wrap_err("Failed to gather deployment information")?; + + tracing::debug!(?game_info, ?deployment_info); + + if let Some(game_info) = game_info { + if deployment_info + .as_ref() + .map(|i| game_info.last_updated > i.timestamp) + .unwrap_or(false) + { + tracing::warn!( + "Game was updated since last mod deployment. \ + Attempting to reconcile game files." + ); + + tokio::try_join!( + async { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for bundle database.") + }, + async { + let path = bundle_dir.join(boot_bundle_path); + let backup_path = path.with_extension("bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for boot bundle") + } + ) + .with_suggestion(|| { + "Reset the game using 'Reset Game', then verify game files.".to_string() + })?; + + tracing::info!( + "Successfully re-created game file backups. \ + Continuing mod deployment." + ); + } + } + + check_mod_order(&state)?; + + tracing::info!( + "Deploying {} mods to '{}'.", + state.mods.iter().filter(|i| i.enabled).count(), + bundle_dir.display() + ); + + tracing::info!("Copy legacy mod folders"); + let mod_folders = copy_mod_folders(state.clone()) + .await + .wrap_err("Failed to copy mod folders")?; + + tracing::info!("Build mod bundles"); + let mut bundles = build_bundles(state.clone()) + .await + .wrap_err("Failed to build mod bundles")?; + + tracing::info!("Patch boot bundle"); + let mut boot_bundles = patch_boot_bundle(state.clone()) + .await + .wrap_err("Failed to patch boot bundle")?; + bundles.append(&mut boot_bundles); + + if let Some(info) = &deployment_info { + let bundle_dir = Arc::new(bundle_dir); + // Remove bundles from the previous deployment that don't match the current one. + // I.e. mods that used to be installed/enabled but aren't anymore. + { + let tasks = info.bundles.iter().cloned().filter_map(|file_name| { + let is_being_deployed = bundles.iter().any(|b2| { + let name = format!("{:016x}", b2.name()); + file_name == name + }); + + if !is_being_deployed { + let bundle_dir = bundle_dir.clone(); + let task = async move { + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("Failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + + futures::future::join_all(tasks).await; + } + + // Do the same thing for mod folders + { + let tasks = info.mod_folders.iter().filter_map(|mod_id| { + let is_being_deployed = mod_folders.iter().any(|id| id == mod_id); + + if !is_being_deployed { + let path = bundle_dir.join("mods").join(mod_id); + tracing::debug!("Removing unused mod folder '{}'", path.display()); + + let task = async move { + if let Err(err) = fs::remove_dir_all(&path).await.wrap_err_with(|| { + format!("Failed to remove unused legacy mod '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + + Some(task) + } else { + None + } + }); + futures::future::join_all(tasks).await; + } + } + + tracing::info!("Patch game settings"); + patch_game_settings(state.clone()) + .await + .wrap_err("Failed to patch game settings")?; + + tracing::info!("Patching bundle database"); + patch_bundle_database(state.clone(), &bundles) + .await + .wrap_err("Failed to patch bundle database")?; + + tracing::info!("Writing deployment data"); + write_deployment_data(state.clone(), &bundles, mod_folders) + .await + .wrap_err("Failed to write deployment data")?; + + tracing::info!("Finished deploying mods"); + Ok(()) +} diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 5520c4a..6b91169 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,46 +1,19 @@ -use std::collections::HashMap; -use std::io::{self, Cursor, ErrorKind}; +use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::sync::Arc; use color_eyre::eyre::Context; -use color_eyre::{eyre, Help, Report, Result}; -use futures::stream; -use futures::StreamExt; -use path_slash::PathBufExt; -use sdk::filetype::lua; -use sdk::filetype::package::Package; +use color_eyre::{eyre, Result}; use sdk::murmur::Murmur64; -use sdk::{ - Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, -}; -use serde::{Deserialize, Serialize}; -use string_template::Template; -use time::OffsetDateTime; -use tokio::fs; +use tokio::fs::{self}; use tokio::io::AsyncWriteExt; -use tracing::Instrument; -use super::read_sjson_file; -use crate::controller::app::check_mod_order; -use crate::state::{ActionState, PackageInfo}; +use crate::controller::deploy::{ + DeploymentData, BOOT_BUNDLE_NAME, BUNDLE_DATABASE_NAME, DEPLOYMENT_DATA_PATH, +}; +use crate::state::ActionState; -const MOD_BUNDLE_NAME: &str = "packages/mods"; -const BOOT_BUNDLE_NAME: &str = "packages/boot"; -const DML_BUNDLE_NAME: &str = "packages/dml"; -const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; -const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; -const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; -const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; -const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; - -#[derive(Debug, Serialize, Deserialize)] -struct DeploymentData { - bundles: Vec, - #[serde(with = "time::serde::iso8601")] - timestamp: OffsetDateTime, -} +use super::deploy::SETTINGS_FILE_PATH; #[tracing::instrument] async fn read_file_with_backup

(path: P) -> Result> @@ -130,585 +103,6 @@ async fn patch_game_settings(state: Arc) -> Result<()> { Ok(()) } -#[tracing::instrument(skip_all, fields(package = info.name))] -fn make_package(info: &PackageInfo) -> Result { - let mut pkg = Package::new(info.name.clone(), PathBuf::new()); - - for f in &info.files { - let mut it = f.rsplit('.'); - let file_type = it - .next() - .ok_or_else(|| eyre::eyre!("missing file extension")) - .and_then(BundleFileType::from_str) - .wrap_err("Invalid file name in package info")?; - let name: String = it.collect(); - pkg.add_file(file_type, name); - } - - Ok(pkg) -} - -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); - } - - lua.push('}'); - - tracing::debug!("mod_data_lua:\n{}", lua); - - lua -} - -#[tracing::instrument(skip_all)] -async fn build_bundles(state: Arc) -> Result> { - let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); - let mut tasks = Vec::new(); - - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - - let mut bundles = Vec::new(); - - { - tracing::trace!("Building mod data script"); - - let span = tracing::debug_span!("Building mod data script"); - let _enter = span.enter(); - - let lua = build_mod_data_lua(state.clone()); - - tracing::trace!("Compiling mod data script"); - - let file = - lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; - - tracing::trace!("Compile mod data script"); - - mod_bundle.add_file(file); - } - - tracing::trace!("Preparing tasks to deploy bundle files"); - - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - let span = tracing::trace_span!("building mod packages", name = mod_info.name); - let _enter = span.enter(); - - let mod_dir = state.mod_dir.join(&mod_info.id); - for pkg_info in &mod_info.packages { - let span = tracing::trace_span!("building package", name = pkg_info.name); - let _enter = span.enter(); - - tracing::trace!( - "Building package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; - let mut variant = BundleFileVariant::new(); - let bin = pkg - .to_binary() - .wrap_err("Failed to serialize package to binary")?; - variant.set_data(bin); - let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); - file.add_variant(variant); - - tracing::trace!( - "Compiled package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - mod_bundle.add_file(file); - - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = mod_dir.join(&bundle_name); - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - // Explicitely drop the guard, so that we can move the span - // into the async operation - drop(_enter); - - let ctx = state.ctx.clone(); - - let task = async move { - let bundle = { - let bin = fs::read(&src).await.wrap_err_with(|| { - format!("Failed to read bundle file '{}'", src.display()) - })?; - let name = Bundle::get_name_from_path(&ctx, &src); - Bundle::from_binary(&ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? - }; - - tracing::debug!( - src = %src.display(), - dest = %dest.display(), - "Copying bundle '{}' for mod '{}'", - pkg_name, - mod_name, - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - - Ok::(bundle) - } - .instrument(span); - - tasks.push(task); - } - } - - tracing::debug!("Copying {} mod bundles", tasks.len()); - - let mut tasks = stream::iter(tasks).buffer_unordered(10); - - while let Some(res) = tasks.next().await { - let bundle = res?; - bundles.push(bundle); - } - - { - let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); - tracing::trace!("Writing mod bundle to '{}'", path.display()); - fs::write(&path, mod_bundle.to_binary()?) - .await - .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; - } - - bundles.push(mod_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); - - let mut bundles = Vec::with_capacity(2); - - let mut boot_bundle = async { - let bin = read_file_with_backup(&bundle_path) - .await - .wrap_err("Failed to read boot bundle")?; - - Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) - .wrap_err("Failed to parse boot bundle") - } - .instrument(tracing::trace_span!("read boot bundle")) - .await - .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; - - { - tracing::trace!("Adding mod package file to boot bundle"); - let span = tracing::trace_span!("create mod package file"); - let _enter = span.enter(); - - let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); - - for mod_info in &state.mods { - for pkg_info in &mod_info.packages { - pkg.add_file(BundleFileType::Package, &pkg_info.name); - } - } - - pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); - - let mut variant = BundleFileVariant::new(); - variant.set_data(pkg.to_binary()?); - let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - tracing::trace!("Handling DML packages and bundle"); - let span = tracing::trace_span!("handle DML"); - let _enter = span.enter(); - - let mut variant = BundleFileVariant::new(); - - let mod_info = state - .mods - .iter() - .find(|m| m.id == "dml") - .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; - let pkg_info = mod_info - .packages - .get(0) - .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) - .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); - - { - let bin = fs::read(&src) - .await - .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; - let name = Bundle::get_name_from_path(&state.ctx, &src); - - let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; - - bundles.push(dml_bundle); - }; - - { - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - tracing::debug!( - "Copying bundle {} for mod {}: {} -> {}", - pkg_name, - mod_name, - src.display(), - dest.display() - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - } - - let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; - variant.set_data(pkg.to_binary()?); - - let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - let span = tracing::debug_span!("Importing mod main script"); - let _enter = span.enter(); - - let is_io_enabled = format!("{}", state.is_io_enabled); - let mut data = HashMap::new(); - data.insert("is_io_enabled", is_io_enabled.as_str()); - - let tmpl = include_str!("../../assets/mod_main.lua"); - let lua = Template::new(tmpl).render(&data); - tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); - let file = - lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; - - boot_bundle.add_file(file); - } - - async { - let bin = boot_bundle - .to_binary() - .wrap_err("Failed to serialize boot bundle")?; - fs::write(&bundle_path, bin) - .await - .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) - } - .instrument(tracing::trace_span!("write boot bundle")) - .await?; - - bundles.push(boot_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); - - let mut db = { - let bin = read_file_with_backup(&database_path) - .await - .wrap_err("Failed to read bundle database")?; - let mut r = Cursor::new(bin); - let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; - tracing::trace!("Finished parsing bundle database"); - db - }; - - for bundle in bundles.as_ref() { - tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); - db.add_bundle(bundle); - } - - { - let bin = db - .to_binary() - .wrap_err("Failed to serialize bundle database")?; - fs::write(&database_path, bin).await.wrap_err_with(|| { - format!( - "failed to write bundle database to '{}'", - database_path.display() - ) - })?; - } - - Ok(()) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let info = DeploymentData { - timestamp: OffsetDateTime::now_utc(), - bundles: bundles - .as_ref() - .iter() - .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) - .collect(), - }; - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; - - fs::write(&path, &data) - .await - .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; - - Ok(()) -} - -#[tracing::instrument(skip_all, fields( - game_dir = %state.game_dir.display(), - mods = state.mods.len() -))] -pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { - let state = Arc::new(state); - let bundle_dir = state.game_dir.join("bundle"); - let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); - - if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) - .await - .is_ok() - { - let err = eyre::eyre!("Found dtkit-patch-based mod installation."); - return Err(err) - .with_suggestion(|| { - "If you're a mod author and saved projects directly in 'mods/', \ - use DTMT to migrate them to the new project structure." - .to_string() - }) - .with_suggestion(|| { - "Click 'Reset Game' to remove the previous mod installation.".to_string() - }); - } - - let (_, game_info, deployment_info) = tokio::try_join!( - async { - fs::metadata(&bundle_dir) - .await - .wrap_err("Failed to open game bundle directory") - .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") - }, - async { - tokio::task::spawn_blocking(dtmt_shared::collect_game_info) - .await - .map_err(Report::new) - }, - async { - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path).await { - Ok(data) => Ok(Some(data)), - Err(err) => { - if let Some(err) = err.downcast_ref::() - && err.kind() == ErrorKind::NotFound - { - Ok(None) - } else { - Err(err).wrap_err("Failed to read deployment data") - } - } - } - } - ) - .wrap_err("Failed to gather deployment information")?; - - tracing::debug!(?game_info, ?deployment_info); - - if let Some(game_info) = game_info { - if deployment_info - .as_ref() - .map(|i| game_info.last_updated > i.timestamp) - .unwrap_or(false) - { - tracing::warn!( - "Game was updated since last mod deployment. \ - Attempting to reconcile game files." - ); - - tokio::try_join!( - async { - let path = bundle_dir.join(BUNDLE_DATABASE_NAME); - let backup_path = path.with_extension("data.bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for bundle database.") - }, - async { - let path = bundle_dir.join(boot_bundle_path); - let backup_path = path.with_extension("bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for boot bundle") - } - ) - .with_suggestion(|| { - "Reset the game using 'Reset Game', then verify game files.".to_string() - })?; - - tracing::info!( - "Successfully re-created game file backups. \ - Continuing mod deployment." - ); - } - } - - check_mod_order(&state)?; - - tracing::info!( - "Deploying {} mods to '{}'.", - state.mods.iter().filter(|i| i.enabled).count(), - bundle_dir.display() - ); - - tracing::info!("Build mod bundles"); - let mut bundles = build_bundles(state.clone()) - .await - .wrap_err("Failed to build mod bundles")?; - - tracing::info!("Patch boot bundle"); - let mut more_bundles = patch_boot_bundle(state.clone()) - .await - .wrap_err("Failed to patch boot bundle")?; - bundles.append(&mut more_bundles); - - if let Some(info) = &deployment_info { - let bundle_dir = Arc::new(bundle_dir); - let tasks = info.bundles.iter().cloned().filter_map(|file_name| { - let contains = bundles.iter().any(|b2| { - let name = format!("{:016x}", b2.name()); - file_name == name - }); - - if !contains { - let bundle_dir = bundle_dir.clone(); - let task = async move { - let path = bundle_dir.join(&file_name); - - tracing::debug!("Removing unused bundle '{}'", file_name); - - if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("Failed to remove unused bundle '{}'", path.display()) - }) { - tracing::error!("{:?}", err); - } - }; - Some(task) - } else { - None - } - }); - - futures::future::join_all(tasks).await; - } - - tracing::info!("Patch game settings"); - patch_game_settings(state.clone()) - .await - .wrap_err("Failed to patch game settings")?; - - tracing::info!("Patching bundle database"); - patch_bundle_database(state.clone(), &bundles) - .await - .wrap_err("Failed to patch bundle database")?; - - tracing::info!("Writing deployment data"); - write_deployment_data(state.clone(), &bundles) - .await - .wrap_err("Failed to write deployment data")?; - - tracing::info!("Finished deploying mods"); - Ok(()) -} - #[tracing::instrument(skip_all)] async fn reset_dtkit_patch(state: ActionState) -> Result<()> { let bundle_dir = state.game_dir.join("bundle"); diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs new file mode 100644 index 0000000..3e17d4c --- /dev/null +++ b/crates/dtmm/src/controller/import.rs @@ -0,0 +1,475 @@ +use std::collections::HashMap; +use std::ffi::CStr; +use std::io::{Cursor, Read, Seek, Write}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use color_eyre::eyre::{self, Context}; +use color_eyre::{Help, Report, Result}; +use druid::im::Vector; +use druid::{FileInfo, ImageBuf}; +use dtmt_shared::{ModConfig, ModConfigResources}; +use luajit2_sys as lua; +use nexusmods::Api as NexusApi; +use tokio::fs; +use zip::ZipArchive; + +use crate::state::{ActionState, ModInfo, NexusInfo, PackageInfo}; + +fn find_archive_file( + archive: &ZipArchive, + name: impl AsRef, +) -> Option { + let path = archive + .file_names() + .find(|path| path.ends_with(name.as_ref())) + .map(|s| s.to_string()); + path +} + +// Runs the content of a `.mod` file to extract what data we can get +// from legacy mods. +// 1. Create a global function `new_mod` that stores +// the relevant bits in global variables. +// 2. Run the `.mod` file, which will merely return a table. +// 3. Run the `run` function from that table. +// 4. Access the global variables from #1. +#[tracing::instrument] +fn parse_mod_id_file(data: &str) -> Result<(String, ModConfigResources)> { + tracing::debug!("Parsing mod file:\n{}", data); + + let ret = unsafe { + let state = lua::luaL_newstate(); + lua::luaL_openlibs(state); + + let run = b" +function fassert() end +function new_mod(id, resources) + _G.id = id + _G.script = resources.mod_script + _G.data = resources.mod_data + _G.localization = resources.mod_localization +end +\0"; + match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to create `new_mod`") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run buffer: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run buffer") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let name = b".mod\0"; + match lua::luaL_loadbuffer( + state, + data.as_ptr() as _, + data.len() as _, + name.as_ptr() as _, + ) as u32 + { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to load `.mod` file buffer") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 1, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod` file: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod` file") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let key = b"run\0"; + lua::lua_pushstring(state, key.as_ptr() as _); + lua::lua_gettable(state, -2); + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod.run`: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod.run`") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let get_global = |state, key: &[u8]| { + lua::lua_getglobal(state, key.as_ptr() as _); + + if lua::lua_isnil(state, -1) != 0 { + return Ok(None); + } + + let s = lua::lua_tostring(state, -1); + + if s.is_null() { + eyre::bail!("Expected string, got NULL"); + } + + let ret = CStr::from_ptr(s).to_string_lossy().to_string(); + lua::lua_pop(state, 1); + Ok(Some(ret)) + }; + + let mod_id = get_global(state, b"id\0") + .and_then(|s| s.ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `id`")?; + + let resources = ModConfigResources { + init: get_global(state, b"script\0") + .and_then(|s| s.map(PathBuf::from).ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `script`.")?, + data: get_global(state, b"data\0") + .wrap_err("Failed to get `data`.")? + .map(PathBuf::from), + localization: get_global(state, b"localization\0") + .wrap_err("Failed to get `localization`")? + .map(PathBuf::from), + }; + + lua::lua_close(state); + + (mod_id, resources) + }; + + Ok(ret) +} + +// Extracts the mod configuration from the mod archive. +// This may either be a proper `dtmt.cfg`, or the legacy `.mod` ID file. +// +// It also returns the directory where this file was found, used as root path. This +// allows flexibility in what the directory structure is exactly, since many people +// still end up creating tarbombs and Nexus does its own re-packaging. +#[tracing::instrument(skip(archive))] +fn extract_mod_config(archive: &mut ZipArchive) -> Result<(ModConfig, String)> { + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read mod config from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read mod config from archive")?; + + let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; + + let cfg = serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")?; + let root = name + .strip_suffix("dtmt.cfg") + .expect("String must end with that suffix") + .to_string(); + + Ok((cfg, root)) + } else if let Some(name) = find_archive_file(archive, ".mod") { + let (mod_id, resources) = { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read `.mod` file from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read `.mod` file from archive")?; + + let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; + parse_mod_id_file(&data).wrap_err("Invalid `.mod` file")? + }; + + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: String::new(), + version: String::new(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + let root = if let Some(index) = name.rfind('/') { + name[..index].to_string() + } else { + String::new() + }; + + Ok((cfg, root)) + } else { + eyre::bail!( + "Mod needs either a config file or `.mod` file. \ + Please get in touch with the author to provide a properly packaged mod." + ); + } +} + +#[tracing::instrument(skip(archive))] +fn extract_bundled_mod( + archive: &mut ZipArchive, + root: String, + dest: impl AsRef + std::fmt::Debug, +) -> Result>> { + let files: HashMap> = { + let name = archive + .file_names() + .find(|name| name.ends_with("files.sjson")) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read file index from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; + serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? + }; + + tracing::trace!(?files); + + let dest = dest.as_ref(); + tracing::trace!("Extracting mod archive to {}", dest.display()); + archive + .extract(dest) + .wrap_err_with(|| format!("Failed to extract archive to {}", dest.display()))?; + + let packages = files + .into_iter() + .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) + .collect(); + + tracing::trace!(?packages); + + Ok(packages) +} + +#[tracing::instrument(skip(archive))] +fn extract_legacy_mod( + archive: &mut ZipArchive, + root: String, + dest: impl Into + std::fmt::Debug, +) -> Result<()> { + let dest = dest.into(); + let file_count = archive.len(); + + for i in 0..file_count { + let mut f = archive + .by_index(i) + .wrap_err_with(|| format!("Failed to get file at index {}", i))?; + + let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { + let err = eyre::eyre!("File name in archive is not a safe path value."); + return Err(err).with_suggestion(|| { + "Only use well-known applications to create the ZIP archive, \ + and don't create paths that point outside the archive directory." + }); + }; + + let Ok(suffix) = name.strip_prefix(&root) else { + tracing::warn!( + "Skipping file outside of the mod root directory: {}", + name.display() + ); + continue; + }; + let name = dest.join(suffix); + + if f.is_dir() { + // The majority of errors will actually be "X already exists". + // But rather than filter them invidually, we just ignore all of them. + // If there is a legitimate error of "couldn't create X", it will eventually fail when + // we try to put a file in there. + tracing::trace!("Creating directory '{}'", name.display()); + let _ = std::fs::create_dir_all(&name); + } else { + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err_with(|| format!("Failed to read file '{}'", name.display()))?; + + tracing::trace!("Writing file '{}'", name.display()); + let mut out = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(&name) + .wrap_err_with(|| format!("Failed to open file '{}'", name.display()))?; + + out.write_all(&buf) + .wrap_err_with(|| format!("Failed to write to '{}'", name.display()))?; + } + } + + Ok(()) +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { + let data = fs::read(&info.path) + .await + .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; + let data = Cursor::new(data); + + let nexus = if let Some((_, id, _, _)) = info + .path + .file_name() + .and_then(|s| s.to_str()) + .and_then(NexusApi::parse_file_name) + { + if !state.nexus_api_key.is_empty() { + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let mod_info = api + .mods_id(id) + .await + .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; + Some(NexusInfo::from(mod_info)) + } else { + None + } + } else { + None + }; + + let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; + + if tracing::enabled!(tracing::Level::DEBUG) { + let names = archive.file_names().fold(String::new(), |mut s, name| { + s.push('\n'); + s.push_str(name); + s + }); + tracing::debug!("Archive contents:{}", names); + } + + let (mod_cfg, root) = + extract_mod_config(&mut archive).wrap_err("Failed to extract mod configuration")?; + tracing::info!("Importing mod {} ({})", mod_cfg.name, mod_cfg.id); + tracing::debug!(root, ?mod_cfg); + + let image = if let Some(path) = &mod_cfg.image { + let name = archive + .file_names() + .find(|name| name.ends_with(&path.display().to_string())) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read image file from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + let img = match ImageBuf::from_data(&buf) { + Ok(img) => img, + Err(err) => { + let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); + return Err(err).with_suggestion(|| { + "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() + }); + } + }; + + Some(img) + } else { + None + }; + + tracing::trace!(?image); + + let mod_dir = state.data_dir.join(state.mod_dir.as_ref()); + let dest = mod_dir.join(&mod_cfg.id); + + tracing::trace!("Creating mods directory {}", dest.display()); + fs::create_dir_all(&dest) + .await + .wrap_err_with(|| format!("Failed to create data directory '{}'", dest.display()))?; + + let packages = if mod_cfg.bundled { + extract_bundled_mod(&mut archive, root, &mod_dir).wrap_err("Failed to extract mod")? + } else { + extract_legacy_mod(&mut archive, root, &dest).wrap_err("Failed to extract legacy mod")?; + + let data = serde_sjson::to_string(&mod_cfg).wrap_err("Failed to serialize mod config")?; + fs::write(dest.join("dtmt.cfg"), &data) + .await + .wrap_err("Failed to write mod config")?; + + Default::default() + }; + + if let Some(nexus) = &nexus { + let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; + let path = dest.join("nexus.sjson"); + fs::write(&path, data.as_bytes()) + .await + .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; + } + + let info = ModInfo::new(mod_cfg, packages, image, nexus); + Ok(info) +} diff --git a/crates/dtmm/src/controller/mod.rs b/crates/dtmm/src/controller/mod.rs index eacc3ef..9c75e84 100644 --- a/crates/dtmm/src/controller/mod.rs +++ b/crates/dtmm/src/controller/mod.rs @@ -5,7 +5,9 @@ use serde::Deserialize; use tokio::fs; pub mod app; +pub mod deploy; pub mod game; +pub mod import; pub mod worker; #[tracing::instrument] diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 238b3f3..518c0a8 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -13,7 +13,9 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::RwLock; use crate::controller::app::*; +use crate::controller::deploy::deploy_mods; use crate::controller::game::*; +use crate::controller::import::import_mod; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 9ba838c..8069e6d 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,6 +1,7 @@ #![recursion_limit = "256"] #![feature(let_chains)] #![feature(arc_unwrap_or_clone)] +#![feature(iterator_try_collect)] #![windows_subsystem = "windows"] use std::path::PathBuf; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index c43e973..8a461bd 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,7 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, - pub bundle: bool, + pub bundled: bool, #[data(ignore)] pub nexus: Option, } @@ -130,7 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, - bundle: cfg.bundle, + bundled: cfg.bundled, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7aa2819..7de4418 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -145,7 +145,7 @@ fn build_mod_list() -> impl Widget { let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); - Svg::new(Arc::new(tree)).fix_height(druid::theme::TEXT_SIZE_NORMAL) + Svg::new(tree).fix_height(druid::theme::TEXT_SIZE_NORMAL) }; Either::new( diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 8ecd648..2eacded 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,7 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], - bundle: true, + bundled: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index a162d3b..28e4694 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -63,7 +63,7 @@ pub struct ModConfig { #[serde(default)] pub depends: Vec, #[serde(default = "default_true", skip_serializing_if = "is_true")] - pub bundle: bool, + pub bundled: bool, } pub const STEAMAPP_ID: u32 = 1361210; From b1ff69fa08d22b431e411ebe1173634f54e59032 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 10 Nov 2023 11:13:08 +0100 Subject: [PATCH 006/184] Move deployment directory for legacy mods This moves it back to its original place at `$game_dir/mods`. --- crates/dtmm/src/controller/deploy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 6a804e7..ded840e 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -223,7 +223,7 @@ async fn copy_recursive( #[tracing::instrument(skip(state))] async fn copy_mod_folders(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let game_dir = Arc::clone(&state.game_dir); let mut tasks = Vec::new(); @@ -237,11 +237,11 @@ async fn copy_mod_folders(state: Arc) -> Result> { let mod_id = mod_info.id.clone(); let mod_dir = Arc::clone(&state.mod_dir); - let bundle_dir = Arc::clone(&bundle_dir); + let game_dir = Arc::clone(&game_dir); let task = async move { let from = mod_dir.join(&mod_id); - let to = bundle_dir.join("mods").join(&mod_id); + let to = game_dir.join("mods").join(&mod_id); tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); let _ = fs::create_dir_all(&to).await; From a90614f2e9fafa22fcb31e8c05069a58c53d0b92 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 13 Nov 2023 14:31:12 +0100 Subject: [PATCH 007/184] ci: Implement uploading build artifacts Closes #127. --- .ci/pipelines/base-pipeline.yml | 3 --- .ci/pipelines/pr.yml | 13 ++++++++++++- .ci/tasks/build.yml | 8 +++++--- .ci/tasks/upload.sh | 32 ++++++++++++++++++++++++++++++++ .ci/tasks/upload.yml | 24 ++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100755 .ci/tasks/upload.sh create mode 100644 .ci/tasks/upload.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index 88052a5..48d5bb4 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -22,7 +22,6 @@ resources: source: uri: https://git.sclu1034.dev/bitsquid_dt/dtmt - jobs: - name: set-pipelines plan: @@ -42,5 +41,3 @@ jobs: gitea_api_key: ((gitea_api_key)) instance_vars: pr: ((.:pr.number)) - - diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 6452c93..be1857d 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/Config --- # The actual CI pipeline that is run per branch @@ -6,7 +7,7 @@ resources: - name: repo type: git source: - uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) jobs: @@ -34,6 +35,7 @@ jobs: target: msvc output: artifact ref: ((.:ref)) + gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) - name: build-linux @@ -48,4 +50,13 @@ jobs: target: linux output: artifact ref: ((.:ref)) + gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - task: upload + file: repo/.ci/tasks/upload.yml + vars: + input: artifact + pr: ((.:pr)) + gitea_api_key: ((gitea_api_key)) + gitea_user: bitsquid_dt + gitea_url: http://forgejo:3000 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index ab3a433..c58ed38 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -1,7 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig --- platform: linux image_resource: + name: ctmt-bi-base-((target)) type: registry-image source: repository: registry.local:5000/dtmt-ci-base-((target)) @@ -11,14 +13,14 @@ inputs: - name: repo outputs: -- name: ((output)) +- name: artifacts params: - CI: true + CI: "true" TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) - OUTPUT: ((output)) + OUTPUT: artifacts run: path: .ci/util/run.sh diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh new file mode 100755 index 0000000..5c0dd59 --- /dev/null +++ b/.ci/tasks/upload.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eu + +artifacts="$PWD/artifacts" +repo="$PWD/repo" + +base_url="${GITEA_URL}/api/packages/${GITEA_USER}/generic" + +cd "$repo" + +if [ -n "$PR" ]; then + echo "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" +else + ref=$(git describe --tags) +fi + +echo "ref: $ref" + +# TODO: If this is a tag, check the tag name to determine which +# binary was affected and only upload that. +for f in dtmt dtmt.exe dtmm dtmm.exe; do + if [ -f "$artifacts/$f" ]; then + url="$base_url/$(basename -s .exe $f)/$ref/$f" + echo "$url" + curl -i -X 'PUT' \ + --user "concourse:$GITEA_API_KEY" \ + --upload-file "$artifacts/$f" \ + "$url" + fi +done diff --git a/.ci/tasks/upload.yml b/.ci/tasks/upload.yml new file mode 100644 index 0000000..de22cd2 --- /dev/null +++ b/.ci/tasks/upload.yml @@ -0,0 +1,24 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig +--- +platform: linux + +image_resource: + name: python-script + type: registry-image + source: + repository: registry.local:5000/python-script + tag: latest + +inputs: +- name: repo +- name: ((input)) + +params: + CI: "true" + GITEA_API_KEY: ((gitea_api_key)) + GITEA_URL: ((gitea_url)) + GITEA_USER: ((user)) + PR: ((pr)) + +run: + path: repo/.ci/tasks/upload.sh From c2cdeedb2cae21ad85f999db2b84b268e70cd7d3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 13 Nov 2023 15:00:32 +0100 Subject: [PATCH 008/184] ci: Add caches Closes #126. --- .ci/tasks/build.yml | 4 ++++ .ci/tasks/clippy.yml | 8 +++++++- .ci/tasks/upload.sh | 1 - 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index c58ed38..3dd4215 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -15,6 +15,10 @@ inputs: outputs: - name: artifacts +caches: + - path: repo/target + - path: /usr/local/cargo/registry + params: CI: "true" TARGET: ((target)) diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index 35735c3..cea0e76 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -1,7 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig --- platform: linux image_resource: + name: dtmt-ci-base-linux type: registry-image source: repository: registry.local:5000/dtmt-ci-base-linux @@ -10,8 +12,12 @@ image_resource: inputs: - name: repo +caches: + - path: repo/target + - path: /usr/local/cargo/registry + params: - CI: true + CI: "true" GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh index 5c0dd59..5e1103e 100755 --- a/.ci/tasks/upload.sh +++ b/.ci/tasks/upload.sh @@ -23,7 +23,6 @@ echo "ref: $ref" for f in dtmt dtmt.exe dtmm dtmm.exe; do if [ -f "$artifacts/$f" ]; then url="$base_url/$(basename -s .exe $f)/$ref/$f" - echo "$url" curl -i -X 'PUT' \ --user "concourse:$GITEA_API_KEY" \ --upload-file "$artifacts/$f" \ From d0e074ccce97ab13cb45c48abbcd9d1b8c23d3de Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 12 Nov 2023 23:20:10 +0100 Subject: [PATCH 009/184] Use template engine to build `mod_data.lua` The string-building version became too complex to maintain properly. --- Cargo.lock | 10 +++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/assets/mod_data.lua.j2 | 27 ++++++ crates/dtmm/assets/mod_main.lua | 10 +-- crates/dtmm/src/controller/deploy.rs | 119 +++++++++++++-------------- 5 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 crates/dtmm/assets/mod_data.lua.j2 diff --git a/Cargo.lock b/Cargo.lock index 011fa6b..8385e67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,6 +906,7 @@ dependencies = [ "futures", "lazy_static", "luajit2-sys", + "minijinja", "nexusmods", "oodle", "path-slash", @@ -2084,6 +2085,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minijinja" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" +dependencies = [ + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 1842a62..5f60220 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -35,3 +35,4 @@ ansi-parser = "0.9.0" string_template = "0.2.1" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } async-recursion = "1.0.5" +minijinja = "1.0.10" diff --git a/crates/dtmm/assets/mod_data.lua.j2 b/crates/dtmm/assets/mod_data.lua.j2 new file mode 100644 index 0000000..9f87ad1 --- /dev/null +++ b/crates/dtmm/assets/mod_data.lua.j2 @@ -0,0 +1,27 @@ +return { +{% for mod in mods %} +{ + id = "{{ mod.id }}", + name = "{{ mod.name }}", + bundled = {{ mod.bundled }}, + packages = { + {% for pkg in mod.packages %} + "{{ pkg }}", + {% endfor %} + }, + run = function() + {% if mod.data is none %} + return dofile("{{ mod.init }}") + {% else %} + new_mod("{{ mod.id }}", { + mod_script = "{{ mod.init }}", + mod_data = "{{ mod.data }}", + {% if not mod.localization is none %} + mod_localization = "{{ mod.localization }}", + {% endif %} + }) + {% endif %} + end, +}, +{% endfor %} +} diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 6dbf3e2..a17f5be 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -61,14 +61,14 @@ local function patch_mod_loading_state() if state == "load_package" and package_manager:update() then log("StateBootLoadMods", "Packages loaded, loading mods") self._state = "load_mods" - local ModLoader = require("scripts/mods/dml/init") + local DML = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - local mod_loader = ModLoader:new(mod_data, self._parent:gui()) + local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - self._mod_loader = mod_loader + self._dml = DML Managers.mod = mod_loader - elseif state == "load_mods" and self._mod_loader:update(dt) then + elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then log("StateBootLoadMods", "Mods loaded, exiting") return true, false end @@ -112,7 +112,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- It's also a valid table definition, thereby degrading gracefully when not replaced. -local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 +local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index ded840e..53c4ef1 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -8,7 +8,7 @@ use color_eyre::eyre::Context; use color_eyre::{eyre, Help, Report, Result}; use futures::StreamExt; use futures::{stream, TryStreamExt}; -use path_slash::PathBufExt; +use minijinja::Environment; use sdk::filetype::lua; use sdk::filetype::package::Package; use sdk::murmur::Murmur64; @@ -201,10 +201,10 @@ async fn copy_recursive( async move { if is_dir { tracing::trace!("Creating directory '{}'", dest.display()); - fs::create_dir(&dest) - .await - .map(|_| ()) - .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + // Instead of trying to filter "already exists" errors out explicitly, + // we just ignore all. It'll fail eventually with the next copy operation. + let _ = fs::create_dir(&dest).await; + Ok(()) } else { tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { @@ -262,67 +262,60 @@ async fn copy_mod_folders(state: Arc) -> Result> { Ok(ids) } -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n bundled = \""); - if mod_info.bundled { - lua.push_str("true"); - } else { - lua.push_str("false"); - } - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); +fn build_mod_data_lua(state: Arc) -> Result { + #[derive(Serialize)] + struct TemplateDataMod { + id: String, + name: String, + bundled: bool, + init: String, + data: Option, + localization: Option, + packages: Vec, } - lua.push('}'); + let mut env = Environment::new(); + env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2")) + .wrap_err("Failed to compile template for `mod_data.lua`")?; + let tmpl = env + .get_template("mod_data.lua") + .wrap_err("Failed to get template `mod_data.lua`")?; - tracing::debug!("mod_data_lua:\n{}", lua); + let data: Vec = state + .mods + .iter() + .filter_map(|m| { + if m.id == "dml" || !m.enabled { + return None; + } - lua + Some(TemplateDataMod { + id: m.id.clone(), + name: m.name.clone(), + bundled: m.bundled, + init: m.resources.init.to_string_lossy().to_string(), + data: m + .resources + .data + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + localization: m + .resources + .localization + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + packages: m.packages.iter().map(|p| p.name.clone()).collect(), + }) + }) + .collect(); + + let lua = tmpl + .render(minijinja::context!(mods => data)) + .wrap_err("Failed to render template `mod_data.lua`")?; + + tracing::debug!("mod_data.lua:\n{}", lua); + + Ok(lua) } #[tracing::instrument(skip_all)] @@ -340,7 +333,7 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::debug_span!("Building mod data script"); let _enter = span.enter(); - let lua = build_mod_data_lua(state.clone()); + let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?; tracing::trace!("Compiling mod data script"); From 89608498ef246d0561e2b7229d63e37a3c350572 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:06:19 +0100 Subject: [PATCH 010/184] Fix missing `Mods.original_require` --- crates/dtmm/assets/mod_main.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index a17f5be..2b329bf 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -138,7 +138,8 @@ Mods = { -- Fatshark's code scrubs them. -- The loader can then decide to pass them on to mods, or ignore them lua = setmetatable({}, { __index = lua_libs }), - require_store = require_store + require_store = require_store, + original_require = require, } local can_insert = function(filepath, new_result) From 0bc8779b9d7cbcd5de2e963b516d1471534f2991 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:07:07 +0100 Subject: [PATCH 011/184] Fix Nexusmods API key not being loaded from config --- crates/dtmm/src/controller/import.rs | 5 ++++- crates/dtmm/src/state/delegate.rs | 1 + crates/dtmm/src/util/config.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 3e17d4c..05feba9 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -384,7 +384,10 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result for Delegate { state.config_path = Arc::new(config.path); state.data_dir = Arc::new(config.data_dir); state.game_dir = Arc::new(config.game_dir.unwrap_or_default()); + state.nexus_api_key = Arc::new(config.nexus_api_key.unwrap_or_default()); state.is_io_enabled = config.unsafe_io; } diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 3a0d0b2..9affbb6 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -125,6 +125,9 @@ where .wrap_err_with(|| format!("Invalid config file {}", path.display()))?; cfg.path = path; + + tracing::debug!("Read config file '{}': {:?}", cfg.path.display(), cfg); + Ok(cfg) } Err(err) if err.kind() == ErrorKind::NotFound => { @@ -133,6 +136,11 @@ where .wrap_err_with(|| format!("Failed to read config file {}", path.display()))?; } + tracing::debug!( + "Config file not found at '{}', creating default.", + path.display() + ); + { let parent = default_path .parent() From dcf7faf45db7b12811dd1374893176c02219b428 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:38:51 +0100 Subject: [PATCH 012/184] Use mod name from Nexus if necessary Non-bundled mods come without `dtmt.cfg` and therefore no way to provide a user friendly name. Similar to the other fields, use the one from Nexus in that case. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/ui/window/main.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 8a461bd..64fdd28 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -73,6 +73,7 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { pub id: u64, + pub name: String, pub version: String, pub author: String, pub summary: Arc, @@ -83,6 +84,7 @@ impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { id: value.mod_id, + name: value.name, version: value.version, author: value.author, summary: Arc::new(value.summary), diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7de4418..baa8e22 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -135,8 +135,13 @@ fn build_mod_list() -> impl Widget { }) .lens(lens!((usize, Arc, bool), 1).then(ModInfo::enabled.in_arc())); - let name = - Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); + let name = Label::dynamic(|info: &Arc, _| { + info.nexus + .as_ref() + .map(|n| n.name.clone()) + .unwrap_or_else(|| info.name.clone()) + }) + .lens(lens!((usize, Arc, bool), 1)); let version = { let icon = { From 5ac66779c209f0b48ad7431cf69d483be96ca611 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 16:19:07 +0100 Subject: [PATCH 013/184] Use version number from Nexus import Non-bundled mods come without a `dtmt.cfg`, and therefore without a version number. But we need a version number at import to compare to for the Nexus update check. --- crates/dtmm/src/controller/import.rs | 17 ++++++++++++----- lib/nexusmods/src/lib.rs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 05feba9..c5b5948 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -372,7 +372,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Result Result Result Date: Thu, 16 Nov 2023 00:06:16 +0100 Subject: [PATCH 014/184] Prevent excessive debug logs --- crates/dtmm/src/controller/worker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 518c0a8..152030f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -38,7 +38,9 @@ async fn handle_action( action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { - tracing::debug!(?action); + if cfg!(debug_assertions) && !matches!(action, AsyncAction::Log(_)) { + tracing::debug!(?action); + } let event_sink = event_sink.clone(); match action { From 98827206751a7356c626e8d9675633b8beecbe97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Nov 2023 14:29:06 +0100 Subject: [PATCH 015/184] Delay mod loading The initial implementation of DML ended up loading mods quite late, which did give it the benefit of all `Manager`s being available. This change therefore moves mod loading until after those are initialized. But contrary to old DML, we still create a separate game state to make sure the game doesn't advance until mods are loaded. This avoids race conditions like the one where LogMeIn needs to come early in the load order. --- crates/dtmm/assets/mod_main.lua | 186 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 94 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 2b329bf..e4006f6 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -11,100 +11,6 @@ local log = function(category, format, ...) end end --- Patch `GameStateMachine.init` to add our own state for loading mods. --- In the future, Fatshark might provide us with a dedicated way to do this. -local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") - - -- A necessary override. - -- The original does not proxy `dt` to `_state_update`, but we need that. - StateBootSubStateBase.update = function(self, dt) - local done, error = self:_state_update(dt) - local params = self._params - - if error then - return StateError, { error } - elseif done then - local next_index = params.sub_state_index + 1 - params.sub_state_index = next_index - local next_state_data = params.states[next_index] - - if next_state_data then - return next_state_data[1], self._params - else - self._parent:sub_states_done() - end - end - end - - local StateBootLoadMods = class("StateBootLoadMods", "StateBootSubStateBase") - - StateBootLoadMods.on_enter = function(self, parent, params) - log("StateBootLoadMods", "Entered") - StateBootLoadMods.super.on_enter(self, parent, params) - - local state_params = self:_state_params() - local package_manager = state_params.package_manager - - self._state = "load_package" - self._package_manager = package_manager - self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadMods", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootLoadMods", nil), - } - end - - StateBootLoadMods._state_update = function(self, dt) - local state = self._state - local package_manager = self._package_manager - - if state == "load_package" and package_manager:update() then - log("StateBootLoadMods", "Packages loaded, loading mods") - self._state = "load_mods" - local DML = require("scripts/mods/dml/init") - - local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - - self._dml = DML - Managers.mod = mod_loader - elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then - log("StateBootLoadMods", "Mods loaded, exiting") - return true, false - end - - return false, false - end - - local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") - - local patched = false - - local GameStateMachine_init = GameStateMachine.init - GameStateMachine.init = function(self, parent, start_state, params, ...) - if not patched then - log("mod_main", "Injecting mod loading state") - patched = true - - -- Hardcoded position after `StateRequireScripts`. - -- We do want to wait until then, so that most of the game's core - -- systems are at least loaded and can be hooked, even if they aren't - -- running, yet. - local pos = 4 - table.insert(params.states, pos, { - StateBootLoadMods, - { - package_manager = params.package_manager, - }, - }) - end - - GameStateMachine_init(self, parent, start_state, params, ...) - end - - log("mod_main", "Mod patching complete") -end - log("mod_main", "Initializing mods...") local require_store = {} @@ -199,6 +105,98 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") +-- Inject our state into the game. The state needs to run after `StateGame._init_managers`, +-- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +local function patch_mod_loading_state() + local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") + local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") + local StateGameLoadMods = class("StateGameLoadMods") + + StateBootLoadDML.on_enter = function(self, parent, params) + log("StateBootLoadDML", "Entered") + StateBootLoadDML.super.on_enter(self, parent, params) + + local state_params = self:_state_params() + local package_manager = state_params.package_manager + + self._package_manager = package_manager + self._package_handles = { + ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), + ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + } + end + + StateBootLoadDML._state_update = function(self, dt) + local package_manager = self._package_manager + + if package_manager:update() then + local DML = require("scripts/mods/dml/init") + local mod_data = require("scripts/mods/mod_data") + local mod_loader = DML.create_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") + return true, false + end + + return false, false + end + + + function StateGameLoadMods:on_enter(_, params) + log("StateGameLoadMods", "Entered") + self._next_state = require("scripts/game_states/game/state_splash") + self._next_state_params = params + end + + function StateGameLoadMods:update(main_dt) + local state = self._loading_state + + -- We're relying on the fact that DML internally makes sure + -- that `Managers.mod:update()` is being called appropriately. + -- The implementation as of this writing is to hook `StateGame.update`. + if Managers.mod:all_mods_loaded() then + Log.info("StateGameLoadMods", "Mods loaded, exiting") + return self._next_state, self._next_state_params + end + end + + local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + local GameStateMachine_init = GameStateMachine.init + GameStateMachine.init = function(self, parent, start_state, params, creation_context, state_change_callbacks, name) + if name == "Main" then + log("mod_main", "Injecting StateBootLoadDML") + + -- Hardcoded position after `StateRequireScripts`. + -- We need to wait until then to even begin most of our stuff, + -- so that most of the game's core systems are at least loaded and can be hooked, + -- even if they aren't running, yet. + local pos = 4 + table.insert(params.states, pos, { + StateBootLoadDML, + { + package_manager = params.package_manager, + }, + }) + + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + elseif name == "Game" then + log("mod_main", "Injection StateGameLoadMods") + -- The second time around, we want to be the first, so we pass our own + -- 'start_state'. + -- We can't just have the state machine be initialized and then change its `_next_state`, as by the end of + -- `init`, a bunch of stuff will already be initialized. + GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) + -- And since we're done now, we can revert the function to its original + GameStateMachine.init = GameStateMachine_init + + return + else + -- In all other cases, simply call the original + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + end + end +end + -- Override `init` to run our injection function init() patch_mod_loading_state() From 46a61a7473c84017aa941ba8a610e48368b2ba97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 Nov 2023 09:58:53 +0100 Subject: [PATCH 016/184] Use exit status to determine LJD failure --- lib/sdk/src/filetype/lua.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 3979b7a..5cd371d 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -92,9 +92,10 @@ where let output = cmd.output().wrap_err("Failed to run ljd")?; - if !output.stderr.is_empty() { + if !output.status.success() { eyre::bail!( - "Decompilation failed: {}", + "LJD exited with code {:?}:\n{}", + output.status.code(), String::from_utf8_lossy(&output.stderr) ); } From 4ec44720b0a4c376225f95613950b4d51961a3ef Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 Nov 2023 15:10:20 +0100 Subject: [PATCH 017/184] sdk: Don't fail decompilation on LJD error --- lib/sdk/src/filetype/lua.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 5cd371d..2458dec 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -93,11 +93,12 @@ where let output = cmd.output().wrap_err("Failed to run ljd")?; if !output.status.success() { - eyre::bail!( + let err = eyre::eyre!( "LJD exited with code {:?}:\n{}", output.status.code(), String::from_utf8_lossy(&output.stderr) ); + tracing::error!("Failed to decompile '{}':\n{:?}", name, err); } let content = output.stdout; From f30dc95385edb07f4311777e9417c3147a4c51c5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 22 Nov 2023 09:55:09 +0100 Subject: [PATCH 018/184] ci: Use resource for Gitea package upload --- .ci/pipelines/base-pipeline.yml | 2 +- .ci/pipelines/pr.yml | 60 +++++++++++++++++++++++++++------ .ci/tasks/build.sh | 41 +++++++++++++++++----- .ci/tasks/build.yml | 10 +++--- .ci/tasks/upload.sh | 31 ----------------- .ci/tasks/upload.yml | 24 ------------- Justfile | 25 ++++++++++++++ 7 files changed, 113 insertions(+), 80 deletions(-) delete mode 100755 .ci/tasks/upload.sh delete mode 100644 .ci/tasks/upload.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index 48d5bb4..ae9e323 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -40,4 +40,4 @@ jobs: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) instance_vars: - pr: ((.:pr.number)) + n: ((.:pr.number)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index be1857d..d818490 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -2,6 +2,11 @@ --- # The actual CI pipeline that is run per branch +resource_types: +- name: gitea-package + type: registry-image + source: + repository: registry.local:5000/gitea-package resources: - name: repo @@ -9,6 +14,14 @@ resources: source: uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) +- name: gitea-package + type: gitea-package + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: concourse + type: generic + name: dtmt jobs: - name: clippy @@ -16,6 +29,7 @@ jobs: - get: repo trigger: true - load_var: ref + format: trim file: repo/.git/ref - task: check file: repo/.ci/tasks/clippy.yml @@ -28,35 +42,61 @@ jobs: - get: repo trigger: true - load_var: ref + format: trim file: repo/.git/ref - task: build file: repo/.ci/tasks/build.yml vars: target: msvc - output: artifact ref: ((.:ref)) + pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number + reveal: true + file: artifact/version + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe - name: build-linux plan: - get: repo trigger: true - load_var: ref + reveal: true file: repo/.git/ref - task: build file: repo/.ci/tasks/build.yml vars: target: linux - output: artifact ref: ((.:ref)) + pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) - - task: upload - file: repo/.ci/tasks/upload.yml - vars: - input: artifact - pr: ((.:pr)) - gitea_api_key: ((gitea_api_key)) - gitea_user: bitsquid_dt - gitea_url: http://forgejo:3000 + - load_var: version_number + reveal: true + file: artifact/version + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 7029f44..226f137 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -1,25 +1,48 @@ #!/bin/sh -set -eux +set -eu +if [ -n "$OUTPUT" ]; then + OUTPUT="$PWD/$OUTPUT" +else + OUTPUT=$(mktemp -d) +fi + +title() { + printf "\033[1m%s\033[0m\n" "$1" +} + +if [ -n "${PR:-}" ]; then + title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" +else + ref=$(git describe --tags) +fi + +title "Version is '$ref'" +echo "$ref" > "$OUTPUT/version" + +cd "repo" case "$TARGET" in msvc) cp /src/*.lib ./lib/oodle/ + + title "Build project for target $TARGET" cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std - if [ -d "$OUTPUT" ]; then - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe - fi + title "Install artifacts" + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ + + title "Build project for target $TARGET" cargo build --color always --locked --profile release-lto - if [ -d "$OUTPUT" ]; then - install -t "$OUTPUT/" target/release/dtmt - install -t "$OUTPUT/" target/release/dtmm - fi + title "Install artifacts" + install -t "$OUTPUT/" target/release-lto/dtmt + install -t "$OUTPUT/" target/release-lto/dtmm ;; *) echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 3dd4215..1a561f4 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -13,7 +13,7 @@ inputs: - name: repo outputs: -- name: artifacts +- name: artifact caches: - path: repo/target @@ -24,12 +24,12 @@ params: TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) - OUTPUT: artifacts + PR: ((pr)) + OUTPUT: artifact run: - path: .ci/util/run.sh - dir: repo + path: repo/.ci/util/run.sh args: - - .ci/tasks/build.sh + - repo/.ci/tasks/build.sh - build/((target)) - "Build for the target platform: ((target))" diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh deleted file mode 100755 index 5e1103e..0000000 --- a/.ci/tasks/upload.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -eu - -artifacts="$PWD/artifacts" -repo="$PWD/repo" - -base_url="${GITEA_URL}/api/packages/${GITEA_USER}/generic" - -cd "$repo" - -if [ -n "$PR" ]; then - echo "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" -else - ref=$(git describe --tags) -fi - -echo "ref: $ref" - -# TODO: If this is a tag, check the tag name to determine which -# binary was affected and only upload that. -for f in dtmt dtmt.exe dtmm dtmm.exe; do - if [ -f "$artifacts/$f" ]; then - url="$base_url/$(basename -s .exe $f)/$ref/$f" - curl -i -X 'PUT' \ - --user "concourse:$GITEA_API_KEY" \ - --upload-file "$artifacts/$f" \ - "$url" - fi -done diff --git a/.ci/tasks/upload.yml b/.ci/tasks/upload.yml deleted file mode 100644 index de22cd2..0000000 --- a/.ci/tasks/upload.yml +++ /dev/null @@ -1,24 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig ---- -platform: linux - -image_resource: - name: python-script - type: registry-image - source: - repository: registry.local:5000/python-script - tag: latest - -inputs: -- name: repo -- name: ((input)) - -params: - CI: "true" - GITEA_API_KEY: ((gitea_api_key)) - GITEA_URL: ((gitea_url)) - GITEA_USER: ((user)) - PR: ((pr)) - -run: - path: repo/.ci/tasks/upload.sh diff --git a/Justfile b/Justfile index 09ce0a3..964dc1f 100644 --- a/Justfile +++ b/Justfile @@ -1,3 +1,5 @@ +fly_target := "main" + ci-build: ci-build-msvc ci-build-linux ci-build-msvc: @@ -25,3 +27,26 @@ ci-image-linux: docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux .ci/image docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux docker push registry.sclu1034.dev/dtmt-ci-base-linux + +set-base-pipeline: + fly -t ((fly_target)) set-pipeline \ + --pipeline dtmt-prs \ + --config .ci/pipelines/base-pipeline.yml \ + -v gitea_api_key=${GITEA_API_KEY} \ + -v owner=bitsquid_dt \ + -v repo=dtmt + +set-pr-pipeline pr: + curl \ + -H "Authorization: ${GITEA_API_KEY}" \ + -H 'Accept: application/json' \ + 'https://git.sclu1034.dev/api/v1/repos/bitsquid_dt/dtmt/pulls/{{pr}}' \ + | yq -y '.' - > 'pr-{{pr}}.yaml' + fly -t main set-pipeline \ + --pipeline dtmt-pr \ + --config .ci/pipelines/pr.yml \ + -v gitea_api_key=${GITEA_API_KEY} \ + -i n={{pr}} \ + -y branch="$(yq -y '.head.ref' 'pr-{{pr}}.yaml')" \ + -y pr="$(cat 'pr-{{pr}}.yaml')" + From edac52e73f68b07f196908ecc8a26485c8826fb5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 11:44:44 +0100 Subject: [PATCH 019/184] ci: Use proper resource for commit statuses --- .ci/pipelines/pr.yml | 121 ++++++++++++++++++++++++++++++++++++++----- .ci/tasks/build.yml | 7 +-- .ci/tasks/clippy.yml | 7 +-- 3 files changed, 111 insertions(+), 24 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index d818490..267db09 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -8,12 +8,19 @@ resource_types: source: repository: registry.local:5000/gitea-package +- name: gitea-status + type: registry-image + source: + repository: registry.local:5000/gitea-status + + resources: - name: repo type: git source: uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) + - name: gitea-package type: gitea-package source: @@ -23,38 +30,109 @@ resources: type: generic name: dtmt +- name: pr-status-lint-clippy + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: lint/clippy + description: Checking for common mistakes and opportunities for code improvement + +- name: pr-status-build-msvc + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: build/msvc + description: "Build for the target platform: msvc" + +- name: pr-status-build-linux + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: build/linux + description: "Build for the target platform: linux" + + jobs: - name: clippy + on_success: + put: state-success + resource: pr-status-lint-clippy + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-lint-clippy + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-lint-clippy + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - format: trim - file: repo/.git/ref + - task: check file: repo/.ci/tasks/clippy.yml vars: - ref: ((.:ref)) gitea_api_key: ((gitea_api_key)) + - name: build-msvc + on_success: + put: state-success + resource: pr-status-build-msvc + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-build-msvc + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-build-msvc + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - format: trim - file: repo/.git/ref + - task: build file: repo/.ci/tasks/build.yml vars: target: msvc - ref: ((.:ref)) pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number reveal: true file: artifact/version + - put: package resource: gitea-package no_get: true @@ -70,23 +148,42 @@ jobs: - artifact/*.exe - name: build-linux + on_success: + put: state-success + resource: pr-status-build-linux + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-build-linux + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-build-linux + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - reveal: true - file: repo/.git/ref + - task: build file: repo/.ci/tasks/build.yml vars: target: linux - ref: ((.:ref)) pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number reveal: true file: artifact/version + - put: package resource: gitea-package no_get: true diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 1a561f4..dce44d0 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -23,13 +23,8 @@ params: CI: "true" TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) - REF: ((ref)) PR: ((pr)) OUTPUT: artifact run: - path: repo/.ci/util/run.sh - args: - - repo/.ci/tasks/build.sh - - build/((target)) - - "Build for the target platform: ((target))" + path: repo/.ci/tasks/build.sh diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index cea0e76..483cb30 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -19,13 +19,8 @@ caches: params: CI: "true" GITEA_API_KEY: ((gitea_api_key)) - REF: ((ref)) run: - path: .ci/util/run.sh + path: .ci/tasks/clippy.sh dir: repo - args: - - .ci/tasks/clippy.sh - - lint/clippy - - "Checking for common mistakes and opportunities for code improvement" From 96f3625b7a58d98c0feb05eae11e1a27e0526249 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:16:03 +0100 Subject: [PATCH 020/184] Fix submodule refs --- .gitmodules | 1 + lib/color-eyre | 2 +- lib/luajit2-sys | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9398bb8..a03eebf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,7 @@ [submodule "lib/color-eyre"] path = lib/color-eyre url = https://github.com/sclu1034/color-eyre.git + branch = "fork" [submodule "lib/ansi-parser"] path = lib/ansi-parser url = https://gitlab.com/lschwiderski/ansi-parser.git diff --git a/lib/color-eyre b/lib/color-eyre index 55f8c6b..0fa05eb 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 55f8c6b7481d462e50ee4a03a43253d80d648ae2 +Subproject commit 0fa05eba9954be223b06468c8760b97e660f9941 diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 24da35e..1912016 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 24da35e631099e914d6fc1bcc863228c48e540ec +Subproject commit 19120166f9fc7838b98c71fc348791abc820e323 From d956e75146c45e22aae10e11e7e99c73feaf7008 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:19:00 +0100 Subject: [PATCH 021/184] Fix step names --- .ci/pipelines/pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 267db09..146f987 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -74,7 +74,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-lint-clippy no_get: true params: @@ -105,7 +105,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-build-msvc no_get: true params: @@ -156,7 +156,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-build-linux no_get: true params: From 4c96bcf5ba25d8b6d73e128a6358d7551b478ef2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:29:12 +0100 Subject: [PATCH 022/184] Improve CI log output --- .ci/tasks/build.sh | 25 ++++++++++++++----------- .ci/tasks/clippy.sh | 10 +++++++++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 226f137..4c70a10 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -12,39 +12,42 @@ title() { printf "\033[1m%s\033[0m\n" "$1" } +cd "repo" + if [ -n "${PR:-}" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short $(cat .git/ref || echo "HEAD")) 2>/dev/null || echo 'manual')" else ref=$(git describe --tags) fi -title "Version is '$ref'" +title "Version: '$ref'" echo "$ref" > "$OUTPUT/version" -cd "repo" case "$TARGET" in msvc) cp /src/*.lib ./lib/oodle/ - title "Build project for target $TARGET" + title "Building project for target $TARGET" cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std title "Install artifacts" - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe + install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe + install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ - title "Build project for target $TARGET" + title "Building project for target $TARGET" cargo build --color always --locked --profile release-lto - title "Install artifacts" - install -t "$OUTPUT/" target/release-lto/dtmt - install -t "$OUTPUT/" target/release-lto/dtmm + title "Installing artifacts" + install -v -t "$OUTPUT/" target/release-lto/dtmt + install -v -t "$OUTPUT/" target/release-lto/dtmm ;; *) - echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 + echo -e "\033[31;1mEnv var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'.\033[0m" >&2 exit 1 esac + +title "Done" diff --git a/.ci/tasks/clippy.sh b/.ci/tasks/clippy.sh index a7b6e11..33901a9 100755 --- a/.ci/tasks/clippy.sh +++ b/.ci/tasks/clippy.sh @@ -1,7 +1,15 @@ #!/bin/sh -set -eux +set -eu +title() { + printf "\033[1m%s\033[0m\n" "$1" +} + +title "Install clippy" rustup component add clippy +title "Run clippy" cargo clippy --color always --no-deps + +title "Done" From b86ea337d1d24e0fe258780fcb9b70828b7c6ede Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 15:23:02 +0100 Subject: [PATCH 023/184] Fix build script --- .ci/image/Dockerfile.linux | 1 + .ci/tasks/build.sh | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index 4927810..df10059 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -5,6 +5,7 @@ RUN set -eux; \ apt-get install --no-install-recommends -y \ build-essential \ curl \ + git \ gpg \ jq \ libatk1.0-dev \ diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 4c70a10..9d294b8 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -eu @@ -16,7 +16,7 @@ cd "repo" if [ -n "${PR:-}" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short $(cat .git/ref || echo "HEAD")) 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else ref=$(git describe --tags) fi From 92546f6f5edddc5909d8ad938278aab905f41350 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 15:23:40 +0100 Subject: [PATCH 024/184] Fix pipeline status Since the PR pipeline is not re-applied on new commits, the `pr.head.ref` variable doesn't get updated, and isn't suitable for the status reporting. --- .ci/pipelines/base-pipeline.yml | 2 +- .ci/pipelines/pr.yml | 39 +++++++++++++++++++++++---------- Justfile | 4 ++-- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index ae9e323..f231fb7 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -40,4 +40,4 @@ jobs: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) instance_vars: - n: ((.:pr.number)) + number: ((.:pr.number)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 146f987..a9060f9 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -37,7 +37,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: lint/clippy description: Checking for common mistakes and opportunities for code improvement @@ -48,7 +47,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: build/msvc description: "Build for the target platform: msvc" @@ -59,7 +57,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: build/linux description: "Build for the target platform: linux" @@ -72,6 +69,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -79,16 +77,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-lint-clippy no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: check file: repo/.ci/tasks/clippy.yml @@ -103,6 +106,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -110,16 +114,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-build-msvc no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: build file: repo/.ci/tasks/build.yml @@ -154,6 +163,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -161,16 +171,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-build-linux no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: build file: repo/.ci/tasks/build.yml diff --git a/Justfile b/Justfile index 964dc1f..70af61a 100644 --- a/Justfile +++ b/Justfile @@ -29,7 +29,7 @@ ci-image-linux: docker push registry.sclu1034.dev/dtmt-ci-base-linux set-base-pipeline: - fly -t ((fly_target)) set-pipeline \ + fly -t {{fly_target}} set-pipeline \ --pipeline dtmt-prs \ --config .ci/pipelines/base-pipeline.yml \ -v gitea_api_key=${GITEA_API_KEY} \ @@ -46,7 +46,7 @@ set-pr-pipeline pr: --pipeline dtmt-pr \ --config .ci/pipelines/pr.yml \ -v gitea_api_key=${GITEA_API_KEY} \ - -i n={{pr}} \ + -i number={{pr}} \ -y branch="$(yq -y '.head.ref' 'pr-{{pr}}.yaml')" \ -y pr="$(cat 'pr-{{pr}}.yaml')" From 0cb86efe910fedd6887f58d534ad0af6f9f80d84 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 19:40:09 +0100 Subject: [PATCH 025/184] Use correct owner for packages --- .ci/pipelines/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index a9060f9..a59e6f9 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -26,7 +26,7 @@ resources: source: access_token: ((gitea_api_key)) url: http://forgejo:3000 - owner: concourse + owner: bitsquid_dt type: generic name: dtmt From fa3f517aed6e426144581cac5f5c40e3e7106fb9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 19:40:27 +0100 Subject: [PATCH 026/184] Generate checksums for artifacts Closes #138. --- .ci/pipelines/pr.yml | 3 +++ .ci/tasks/build.sh | 13 +++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index a59e6f9..bca0ebb 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -155,6 +155,7 @@ jobs: - artifact/dtmt - artifact/dtmm - artifact/*.exe + - artifact/*.sha256 - name: build-linux on_success: @@ -212,3 +213,5 @@ jobs: - artifact/dtmt - artifact/dtmm - artifact/*.exe + - artifact/*.sha256 + diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 9d294b8..5e60d7f 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -12,6 +12,11 @@ title() { printf "\033[1m%s\033[0m\n" "$1" } +install_artifact() { + install -v -t "$OUTPUT/" "$1" + sha256sum "$1" | cut -d' ' -f1 > "$OUTPUT/$(basename "$1").sha256" +} + cd "repo" if [ -n "${PR:-}" ]; then @@ -32,8 +37,8 @@ case "$TARGET" in cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std title "Install artifacts" - install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe + install_artifact target/x86_64-pc-windows-msvc/release/dtmt.exe + install_artifact target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ @@ -42,8 +47,8 @@ case "$TARGET" in cargo build --color always --locked --profile release-lto title "Installing artifacts" - install -v -t "$OUTPUT/" target/release-lto/dtmt - install -v -t "$OUTPUT/" target/release-lto/dtmm + install_artifact target/release-lto/dtmt + install_artifact target/release-lto/dtmm ;; *) echo -e "\033[31;1mEnv var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'.\033[0m" >&2 From 6f0fdc50862f9a281536a3b0ac7d3ed2c535ef11 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 10:13:59 +0100 Subject: [PATCH 027/184] dtmm: Document error dialog --- crates/dtmm/src/ui/window/dialog.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 06a6c02..11df4d5 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -7,11 +7,14 @@ use crate::ui::widget::button::Button; const WINDOW_SIZE: (f64, f64) = (600., 250.); +/// Show an error dialog. +/// The title and message are extracted from the error chain in the given `Report`. pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { let (title, msg) = { let count = err.chain().count(); if count == 1 { + // If there is only one error, that's all we can show. ( String::from("An error occurred!"), err.root_cause().to_string(), @@ -20,13 +23,20 @@ pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { let first = err.chain().next().unwrap(); let root = err.root_cause(); + // If there is more than one error in the chain we want to show + // - The first one: This will describe the overall operation that failed + // - The root cause: The actual thing that failed (e.g. 'No such file or directory') + // - The one before the root cause: With diligent `wrap_err` usage, this will provide + // context to the root cause (e.g. the file name we failed to access) + // + // If there are only two errors, the first one is also the context to the root cause. if count > 2 { // The second to last one, the context to the root cause let context = err.chain().nth(count - 2).unwrap(); (format!("{first}!"), format!("{}: {}", context, root)) } else { - (format!("{first}!"), root.to_string()) + ("An error occurred!".to_string(), format!("{}: {}", first, root)) } } }; From 6ab514c4280f5244829705e23c45f75a724d22d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 11:07:40 +0100 Subject: [PATCH 028/184] dtmm: Filter file dialog error log Druid's implementation makes it so that cancelling the file dialog logs an error tracing event. Closes #133. --- crates/dtmm/src/util/log.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index 09fc9a6..fa4a643 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -56,7 +56,7 @@ impl std::io::Write for ChannelWriter { } pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option) { - let env_layer = if let Some(level) = level { + let mut env_layer = if let Some(level) = level { EnvFilter::from(level) } else if cfg!(debug_assertions) { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")) @@ -64,6 +64,18 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option Date: Wed, 8 Nov 2023 15:04:32 +0100 Subject: [PATCH 029/184] Add mod config option for loose files Just the field in the config file, for now. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmt/src/cmd/migrate.rs | 1 + lib/dtmt-shared/src/lib.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 55774aa..c43e973 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,6 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, + pub bundle: bool, #[data(ignore)] pub nexus: Option, } @@ -129,6 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, + bundle: cfg.bundle, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 1a4d605..8ecd648 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,6 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], + bundle: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3907928..a162d3b 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -30,6 +30,17 @@ pub enum ModDependency { Config { id: String, order: ModOrder }, } +// A bit dumb, but serde doesn't support literal values with the +// `default` attribute, only paths. +fn default_true() -> bool { + true +} + +// Similarly dumb, as the `skip_serializing_if` attribute needs a function +fn is_true(val: &bool) -> bool { + *val +} + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfig { #[serde(skip)] @@ -51,6 +62,8 @@ pub struct ModConfig { pub resources: ModConfigResources, #[serde(default)] pub depends: Vec, + #[serde(default = "default_true", skip_serializing_if = "is_true")] + pub bundle: bool, } pub const STEAMAPP_ID: u32 = 1361210; From 871a54020ec1914e952ffadfdc9f3e72f426271f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 15:06:53 +0100 Subject: [PATCH 030/184] Apply formatting --- crates/dtmm/src/controller/game.rs | 8 ++++---- crates/dtmt/src/cmd/dictionary.rs | 5 ++++- lib/nexusmods/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index a5f6fa8..5520c4a 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -583,12 +583,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { }, async { let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path) - .await - { + match read_sjson_file::<_, DeploymentData>(path).await { Ok(data) => Ok(Some(data)), Err(err) => { - if let Some(err) = err.downcast_ref::() && err.kind() == ErrorKind::NotFound { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { Ok(None) } else { Err(err).wrap_err("Failed to read deployment data") diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 0400519..62a5295 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -145,7 +145,10 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( .get_one::("group") .expect("required argument not found"); - let r: BufReader> = if let Some(name) = path.file_name() && name == "-" { + let r: BufReader> = if let Some(name) = + path.file_name() + && name == "-" + { let f = tokio::io::stdin(); BufReader::new(Box::new(f)) } else { diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index a0700ae..3dbbecd 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -215,7 +215,7 @@ impl Api { }; let user_id = query.get("user_id").and_then(|id| id.parse().ok()); - let Some(user_id) = user_id else { + let Some(user_id) = user_id else { return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); }; From e633a571b5ebc5bef2db094d0424af890246ab39 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 19:48:05 +0100 Subject: [PATCH 031/184] Apply clippy lints --- lib/nexusmods/src/lib.rs | 2 +- lib/sdk/src/bundle/mod.rs | 11 ++++------- lib/sdk/src/filetype/lua.rs | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 3dbbecd..c305db0 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -154,7 +154,7 @@ impl Api { self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) )?; - let Some(download_url) = download_info.get(0).map(|i| i.uri.clone()) else { + let Some(download_url) = download_info.first().map(|i| i.uri.clone()) else { return Err(Error::InvalidNXM("no download link", url)); }; diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 96a2ec2..813df06 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -227,13 +227,10 @@ impl Bundle { let _enter = span.enter(); tracing::trace!(num_files = self.files.len()); - self.files - .iter() - .fold(Ok::, Report>(Vec::new()), |data, file| { - let mut data = data?; - data.append(&mut file.to_binary()?); - Ok(data) - })? + self.files.iter().try_fold(Vec::new(), |mut data, file| { + data.append(&mut file.to_binary()?); + Ok::<_, Report>(data) + })? }; // Ceiling division (or division toward infinity) to calculate diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 2458dec..bfe6de7 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -53,8 +53,8 @@ where let mut buf = vec![0u8; length]; r.read_exact(&mut buf)?; - let mut s = String::from_utf8(buf) - .wrap_err_with(|| format!("Invalid byte sequence for LuaJIT bytecode name"))?; + let mut s = + String::from_utf8(buf).wrap_err("Invalid byte sequence for LuaJIT bytecode name")?; // Remove the leading `@` s.remove(0); s From bd6c2366551c90bdb7c6add12e399fe45f960dea Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 20:10:54 +0100 Subject: [PATCH 032/184] Implement deploying non-bundled mods Closes #113. --- Cargo.lock | 50 +- Cargo.toml | 5 +- crates/dtmm/Cargo.toml | 4 +- crates/dtmm/src/controller/app.rs | 170 +----- crates/dtmm/src/controller/deploy.rs | 875 +++++++++++++++++++++++++++ crates/dtmm/src/controller/game.rs | 622 +------------------ crates/dtmm/src/controller/import.rs | 475 +++++++++++++++ crates/dtmm/src/controller/mod.rs | 2 + crates/dtmm/src/controller/worker.rs | 2 + crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 4 +- crates/dtmm/src/ui/window/main.rs | 2 +- crates/dtmt/src/cmd/migrate.rs | 2 +- lib/dtmt-shared/src/lib.rs | 2 +- 14 files changed, 1396 insertions(+), 820 deletions(-) create mode 100644 crates/dtmm/src/controller/deploy.rs create mode 100644 crates/dtmm/src/controller/import.rs diff --git a/Cargo.lock b/Cargo.lock index a7885f2..011fa6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,10 @@ dependencies = [ [[package]] name = "ansi-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127" +version = "0.9.0" dependencies = [ "heapless", - "nom 4.2.3", + "nom", ] [[package]] @@ -381,7 +379,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -896,6 +894,7 @@ name = "dtmm" version = "0.1.0" dependencies = [ "ansi-parser", + "async-recursion", "bitflags 1.3.2", "clap", "color-eyre", @@ -906,6 +905,7 @@ dependencies = [ "dtmt-shared", "futures", "lazy_static", + "luajit2-sys", "nexusmods", "oodle", "path-slash", @@ -1390,7 +1390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1614,12 +1614,12 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heapless" -version = "0.5.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" dependencies = [ "as-slice", - "generic-array 0.13.3", + "generic-array 0.14.7", "hash32", "stable_deref_trait", ] @@ -1747,7 +1747,7 @@ dependencies = [ "serde", "sized-chunks", "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2175,16 +2175,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "nom" version = "7.1.3" @@ -2203,7 +2193,7 @@ checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" dependencies = [ "bytecount", "memchr", - "nom 7.1.3", + "nom", ] [[package]] @@ -2673,7 +2663,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2684,7 +2674,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3102,7 +3092,7 @@ dependencies = [ name = "serde_sjson" version = "1.0.0" dependencies = [ - "nom 7.1.3", + "nom", "nom_locate", "serde", ] @@ -3832,7 +3822,7 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3966,12 +3956,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -4384,7 +4368,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "ansi-parser" -version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 123b2ef..451cd09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,7 @@ members = [ "lib/oodle", "lib/sdk", "lib/serde_sjson", -] -exclude = [ - "lib/color-eyre", - "lib/ansi-parser", + "lib/luajit2-sys", ] [patch.crates-io] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 86444be..1842a62 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -31,5 +31,7 @@ lazy_static = "1.4.0" colors-transform = "0.2.11" usvg = "0.25.0" druid-widget-nursery = "0.1" -ansi-parser = "0.8.0" +ansi-parser = "0.9.0" string_template = "0.2.1" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +async-recursion = "1.0.5" diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 5c5d980..cb6e2f0 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -1,18 +1,17 @@ use std::collections::HashMap; -use std::io::{Cursor, ErrorKind, Read}; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::sync::Arc; use color_eyre::eyre::{self, Context}; use color_eyre::{Help, Report, Result}; use druid::im::Vector; -use druid::{FileInfo, ImageBuf}; +use druid::ImageBuf; use dtmt_shared::ModConfig; use nexusmods::Api as NexusApi; use tokio::fs::{self, DirEntry, File}; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; -use zip::ZipArchive; use crate::state::{ActionState, InitialLoadResult, ModInfo, ModOrder, NexusInfo, PackageInfo}; use crate::util; @@ -20,161 +19,6 @@ use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; -#[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { - let data = fs::read(&info.path) - .await - .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; - let data = Cursor::new(data); - - let nexus = if let Some((_, id, _, _)) = info - .path - .file_name() - .and_then(|s| s.to_str()) - .and_then(NexusApi::parse_file_name) - { - if !state.nexus_api_key.is_empty() { - let api = NexusApi::new(state.nexus_api_key.to_string())?; - let mod_info = api - .mods_id(id) - .await - .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; - Some(NexusInfo::from(mod_info)) - } else { - None - } - } else { - None - }; - - let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; - - if tracing::enabled!(tracing::Level::DEBUG) { - let names = archive.file_names().fold(String::new(), |mut s, name| { - s.push('\n'); - s.push_str(name); - s - }); - tracing::debug!("Archive contents:{}", names); - } - - let dir_name = { - let f = archive.by_index(0).wrap_err("Archive is empty")?; - - if !f.is_dir() { - let err = eyre::eyre!("archive does not have a top-level directory"); - return Err(err).with_suggestion(|| "Use 'dtmt build' to create the mod archive."); - } - - let name = f.name(); - // The directory name is returned with a trailing slash, which we don't want - name[..(name.len().saturating_sub(1))].to_string() - }; - - tracing::info!("Importing mod {}", dir_name); - - let names: Vec<_> = archive.file_names().map(|s| s.to_string()).collect(); - - let mod_cfg: ModConfig = { - let name = names - .iter() - .find(|name| name.ends_with("dtmt.cfg")) - .ok_or_else(|| eyre::eyre!("archive does not contain mod config"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read mod config from archive")?; - - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read mod config from archive")?; - - let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")? - }; - - tracing::debug!(?mod_cfg); - - let files: HashMap> = { - let name = names - .iter() - .find(|name| name.ends_with("files.sjson")) - .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read file index from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? - }; - - tracing::trace!(?files); - - let image = if let Some(path) = &mod_cfg.image { - let name = names - .iter() - .find(|name| name.ends_with(&path.display().to_string())) - .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read image file from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - // Druid somehow doesn't return an error compatible with eyre, here. - // So we have to wrap through `Display` manually. - let img = match ImageBuf::from_data(&buf) { - Ok(img) => img, - Err(err) => { - let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); - return Err(err).with_suggestion(|| { - "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() - }); - } - }; - - Some(img) - } else { - None - }; - - let mod_dir = state.mod_dir; - - tracing::trace!("Creating mods directory {}", mod_dir.display()); - fs::create_dir_all(Arc::as_ref(&mod_dir)) - .await - .wrap_err_with(|| format!("Failed to create data directory {}", mod_dir.display()))?; - - tracing::trace!("Extracting mod archive to {}", mod_dir.display()); - archive - .extract(Arc::as_ref(&mod_dir)) - .wrap_err_with(|| format!("Failed to extract archive to {}", mod_dir.display()))?; - - if let Some(nexus) = &nexus { - let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; - let path = mod_dir.join(&mod_cfg.id).join("nexus.sjson"); - fs::write(&path, data.as_bytes()) - .await - .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; - } - - let packages = files - .into_iter() - .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) - .collect(); - let info = ModInfo::new(mod_cfg, packages, image, nexus); - - Ok(info) -} - #[tracing::instrument(skip(state))] pub(crate) async fn delete_mod(state: ActionState, info: &ModInfo) -> Result<()> { let mod_dir = state.mod_dir.join(&info.id); @@ -229,9 +73,13 @@ async fn read_mod_dir_entry(res: Result) -> Result { Err(err) => return Err(err), }; - let files: HashMap> = read_sjson_file(&index_path) - .await - .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; + let files: HashMap> = if cfg.bundled { + read_sjson_file(&index_path) + .await + .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))? + } else { + Default::default() + }; let image = if let Some(path) = &cfg.image { let path = entry.path().join(path); diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs new file mode 100644 index 0000000..6a804e7 --- /dev/null +++ b/crates/dtmm/src/controller/deploy.rs @@ -0,0 +1,875 @@ +use std::collections::HashMap; +use std::io::{Cursor, ErrorKind}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::Arc; + +use color_eyre::eyre::Context; +use color_eyre::{eyre, Help, Report, Result}; +use futures::StreamExt; +use futures::{stream, TryStreamExt}; +use path_slash::PathBufExt; +use sdk::filetype::lua; +use sdk::filetype::package::Package; +use sdk::murmur::Murmur64; +use sdk::{ + Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, +}; +use serde::{Deserialize, Serialize}; +use string_template::Template; +use time::OffsetDateTime; +use tokio::fs::{self, DirEntry}; +use tokio::io::AsyncWriteExt; +use tracing::Instrument; + +use super::read_sjson_file; +use crate::controller::app::check_mod_order; +use crate::state::{ActionState, PackageInfo}; + +pub const MOD_BUNDLE_NAME: &str = "packages/mods"; +pub const BOOT_BUNDLE_NAME: &str = "packages/boot"; +pub const DML_BUNDLE_NAME: &str = "packages/dml"; +pub const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; +pub const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; +pub const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; +pub const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; +pub const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; + +#[derive(Debug, Serialize, Deserialize)] +pub struct DeploymentData { + pub bundles: Vec, + pub mod_folders: Vec, + #[serde(with = "time::serde::iso8601")] + pub timestamp: OffsetDateTime, +} + +#[tracing::instrument] +async fn read_file_with_backup

(path: P) -> Result> +where + P: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let backup_path = { + let mut p = PathBuf::from(path); + let ext = if let Some(ext) = p.extension() { + ext.to_string_lossy().to_string() + ".bak" + } else { + String::from("bak") + }; + p.set_extension(ext); + p + }; + + let file_name = path + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| String::from("file")); + + let bin = match fs::read(&backup_path).await { + Ok(bin) => bin, + Err(err) if err.kind() == ErrorKind::NotFound => { + // TODO: This doesn't need to be awaited here, yet. + // I only need to make sure it has finished before writing the changed bundle. + tracing::debug!( + "Backup does not exist. Backing up original {} to '{}'", + file_name, + backup_path.display() + ); + fs::copy(path, &backup_path).await.wrap_err_with(|| { + format!( + "Failed to back up {} '{}' to '{}'", + file_name, + path.display(), + backup_path.display() + ) + })?; + + tracing::debug!("Reading {} from original '{}'", file_name, path.display()); + fs::read(path).await.wrap_err_with(|| { + format!("Failed to read {} file: {}", file_name, path.display()) + })? + } + Err(err) => { + return Err(err).wrap_err_with(|| { + format!( + "Failed to read {} from backup '{}'", + file_name, + backup_path.display() + ) + }); + } + }; + Ok(bin) +} + +#[tracing::instrument(skip_all)] +async fn patch_game_settings(state: Arc) -> Result<()> { + let settings_path = state.game_dir.join("bundle").join(SETTINGS_FILE_PATH); + + let settings = read_file_with_backup(&settings_path) + .await + .wrap_err("Failed to read settings.ini")?; + let settings = String::from_utf8(settings).wrap_err("Settings.ini is not valid UTF-8")?; + + let mut f = fs::File::create(&settings_path) + .await + .wrap_err_with(|| format!("Failed to open {}", settings_path.display()))?; + + let Some(i) = settings.find("boot_script =") else { + eyre::bail!("couldn't find 'boot_script' field"); + }; + + f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(b"boot_script = \"scripts/mod_main\"").await?; + + let Some(j) = settings[i..].find('\n') else { + eyre::bail!("couldn't find end of 'boot_script' field"); + }; + + f.write_all(settings[(i + j)..].as_bytes()).await?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(package = info.name))] +fn make_package(info: &PackageInfo) -> Result { + let mut pkg = Package::new(info.name.clone(), PathBuf::new()); + + for f in &info.files { + let mut it = f.rsplit('.'); + let file_type = it + .next() + .ok_or_else(|| eyre::eyre!("missing file extension")) + .and_then(BundleFileType::from_str) + .wrap_err("Invalid file name in package info")?; + let name: String = it.collect(); + pkg.add_file(file_type, name); + } + + Ok(pkg) +} + +#[tracing::instrument] +async fn copy_recursive( + from: impl Into + std::fmt::Debug, + to: impl AsRef + std::fmt::Debug, +) -> Result<()> { + let to = to.as_ref(); + + #[tracing::instrument] + async fn handle_dir(from: PathBuf) -> Result> { + let mut dir = fs::read_dir(&from) + .await + .wrap_err("Failed to read directory")?; + let mut entries = Vec::new(); + + while let Some(entry) = dir.next_entry().await? { + let meta = entry.metadata().await.wrap_err_with(|| { + format!("Failed to get metadata for '{}'", entry.path().display()) + })?; + entries.push((meta.is_dir(), entry)); + } + + Ok(entries) + } + + let base = from.into(); + stream::unfold(vec![base.clone()], |mut state| async { + let from = state.pop()?; + let inner = match handle_dir(from).await { + Ok(entries) => { + for (is_dir, entry) in &entries { + if *is_dir { + state.push(entry.path()); + } + } + stream::iter(entries).map(Ok).left_stream() + } + Err(e) => stream::once(async { Err(e) }).right_stream(), + }; + + Some((inner, state)) + }) + .flatten() + .try_for_each(|(is_dir, entry)| { + let path = entry.path(); + let dest = path + .strip_prefix(&base) + .map(|suffix| to.join(suffix)) + .expect("all entries are relative to the directory we are walking"); + + async move { + if is_dir { + tracing::trace!("Creating directory '{}'", dest.display()); + fs::create_dir(&dest) + .await + .map(|_| ()) + .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + } else { + tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); + fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { + format!( + "Failed to copy file '{}' -> '{}'", + path.display(), + dest.display() + ) + }) + } + } + }) + .await + .map(|_| ()) +} + +#[tracing::instrument(skip(state))] +async fn copy_mod_folders(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut tasks = Vec::new(); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && !m.bundled) + { + let span = tracing::trace_span!("copying legacy mod", name = mod_info.name); + let _enter = span.enter(); + + let mod_id = mod_info.id.clone(); + let mod_dir = Arc::clone(&state.mod_dir); + let bundle_dir = Arc::clone(&bundle_dir); + + let task = async move { + let from = mod_dir.join(&mod_id); + let to = bundle_dir.join("mods").join(&mod_id); + + tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); + let _ = fs::create_dir_all(&to).await; + copy_recursive(&from, &to).await.wrap_err_with(|| { + format!( + "Failed to copy legacy mod from '{}' to '{}'", + from.display(), + to.display() + ) + })?; + + Ok::<_, Report>(mod_id) + }; + tasks.push(task); + } + + let ids = futures::future::try_join_all(tasks).await?; + Ok(ids) +} + +fn build_mod_data_lua(state: Arc) -> String { + let mut lua = String::from("return {\n"); + + // DMF is handled explicitely by the loading procedures, as it actually drives most of that + // and should therefore not show up in the load order. + for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { + lua.push_str(" {\n name = \""); + lua.push_str(&mod_info.name); + + lua.push_str("\",\n id = \""); + lua.push_str(&mod_info.id); + + lua.push_str("\",\n bundled = \""); + if mod_info.bundled { + lua.push_str("true"); + } else { + lua.push_str("false"); + } + + lua.push_str("\",\n run = function()\n"); + + let resources = &mod_info.resources; + if resources.data.is_some() || resources.localization.is_some() { + lua.push_str(" new_mod(\""); + lua.push_str(&mod_info.id); + lua.push_str("\", {\n mod_script = \""); + lua.push_str(&resources.init.to_slash_lossy()); + + if let Some(data) = resources.data.as_ref() { + lua.push_str("\",\n mod_data = \""); + lua.push_str(&data.to_slash_lossy()); + } + + if let Some(localization) = &resources.localization { + lua.push_str("\",\n mod_localization = \""); + lua.push_str(&localization.to_slash_lossy()); + } + + lua.push_str("\",\n })\n"); + } else { + lua.push_str(" return dofile(\""); + lua.push_str(&resources.init.to_slash_lossy()); + lua.push_str("\")\n"); + } + + lua.push_str(" end,\n packages = {\n"); + + for pkg_info in &mod_info.packages { + lua.push_str(" \""); + lua.push_str(&pkg_info.name); + lua.push_str("\",\n"); + } + + lua.push_str(" },\n },\n"); + } + + lua.push('}'); + + tracing::debug!("mod_data_lua:\n{}", lua); + + lua +} + +#[tracing::instrument(skip_all)] +async fn build_bundles(state: Arc) -> Result> { + let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); + let mut tasks = Vec::new(); + + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut bundles = Vec::new(); + + { + tracing::trace!("Building mod data script"); + + let span = tracing::debug_span!("Building mod data script"); + let _enter = span.enter(); + + let lua = build_mod_data_lua(state.clone()); + + tracing::trace!("Compiling mod data script"); + + let file = + lua::compile(MOD_DATA_SCRIPT, lua).wrap_err("Failed to compile mod data Lua file")?; + + tracing::trace!("Compile mod data script"); + + mod_bundle.add_file(file); + } + + tracing::trace!("Preparing tasks to deploy bundle files"); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && m.bundled) + { + let span = tracing::trace_span!("building mod packages", name = mod_info.name); + let _enter = span.enter(); + + let mod_dir = state.mod_dir.join(&mod_info.id); + for pkg_info in &mod_info.packages { + let span = tracing::trace_span!("building package", name = pkg_info.name); + let _enter = span.enter(); + + tracing::trace!( + "Building package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; + let mut variant = BundleFileVariant::new(); + let bin = pkg + .to_binary() + .wrap_err("Failed to serialize package to binary")?; + variant.set_data(bin); + let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); + file.add_variant(variant); + + tracing::trace!( + "Compiled package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + mod_bundle.add_file(file); + + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = mod_dir.join(&bundle_name); + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + // Explicitely drop the guard, so that we can move the span + // into the async operation + drop(_enter); + + let ctx = state.ctx.clone(); + + let task = async move { + let bundle = { + let bin = fs::read(&src).await.wrap_err_with(|| { + format!("Failed to read bundle file '{}'", src.display()) + })?; + let name = Bundle::get_name_from_path(&ctx, &src); + Bundle::from_binary(&ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? + }; + + tracing::debug!( + src = %src.display(), + dest = %dest.display(), + "Copying bundle '{}' for mod '{}'", + pkg_name, + mod_name, + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + + Ok::(bundle) + } + .instrument(span); + + tasks.push(task); + } + } + + tracing::debug!("Copying {} mod bundles", tasks.len()); + + let mut tasks = stream::iter(tasks).buffer_unordered(10); + + while let Some(res) = tasks.next().await { + let bundle = res?; + bundles.push(bundle); + } + + { + let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); + tracing::trace!("Writing mod bundle to '{}'", path.display()); + fs::write(&path, mod_bundle.to_binary()?) + .await + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; + } + + bundles.push(mod_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all)] +async fn patch_boot_bundle(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); + + let mut bundles = Vec::with_capacity(2); + + let mut boot_bundle = async { + let bin = read_file_with_backup(&bundle_path) + .await + .wrap_err("Failed to read boot bundle")?; + + Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) + .wrap_err("Failed to parse boot bundle") + } + .instrument(tracing::trace_span!("read boot bundle")) + .await + .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; + + { + tracing::trace!("Adding mod package file to boot bundle"); + let span = tracing::trace_span!("create mod package file"); + let _enter = span.enter(); + + let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); + + for mod_info in &state.mods { + for pkg_info in &mod_info.packages { + pkg.add_file(BundleFileType::Package, &pkg_info.name); + } + } + + pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); + + let mut variant = BundleFileVariant::new(); + variant.set_data(pkg.to_binary()?); + let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + tracing::trace!("Handling DML packages and bundle"); + let span = tracing::trace_span!("handle DML"); + let _enter = span.enter(); + + let mut variant = BundleFileVariant::new(); + + let mod_info = state + .mods + .iter() + .find(|m| m.id == "dml") + .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; + let pkg_info = mod_info + .packages + .get(0) + .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) + .with_suggestion(|| "Re-download and import the newest version.".to_string())?; + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); + + { + let bin = fs::read(&src) + .await + .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; + let name = Bundle::get_name_from_path(&state.ctx, &src); + + let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; + + bundles.push(dml_bundle); + }; + + { + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + tracing::debug!( + "Copying bundle {} for mod {}: {} -> {}", + pkg_name, + mod_name, + src.display(), + dest.display() + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + } + + let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; + variant.set_data(pkg.to_binary()?); + + let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + let span = tracing::debug_span!("Importing mod main script"); + let _enter = span.enter(); + + let is_io_enabled = format!("{}", state.is_io_enabled); + let mut data = HashMap::new(); + data.insert("is_io_enabled", is_io_enabled.as_str()); + + let tmpl = include_str!("../../assets/mod_main.lua"); + let lua = Template::new(tmpl).render(&data); + tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); + let file = + lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; + + boot_bundle.add_file(file); + } + + async { + let bin = boot_bundle + .to_binary() + .wrap_err("Failed to serialize boot bundle")?; + fs::write(&bundle_path, bin) + .await + .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) + } + .instrument(tracing::trace_span!("write boot bundle")) + .await?; + + bundles.push(boot_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); + + let mut db = { + let bin = read_file_with_backup(&database_path) + .await + .wrap_err("Failed to read bundle database")?; + let mut r = Cursor::new(bin); + let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; + tracing::trace!("Finished parsing bundle database"); + db + }; + + for bundle in bundles.as_ref() { + tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); + db.add_bundle(bundle); + } + + { + let bin = db + .to_binary() + .wrap_err("Failed to serialize bundle database")?; + fs::write(&database_path, bin).await.wrap_err_with(|| { + format!( + "failed to write bundle database to '{}'", + database_path.display() + ) + })?; + } + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn write_deployment_data( + state: Arc, + bundles: B, + mod_folders: Vec, +) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let info = DeploymentData { + timestamp: OffsetDateTime::now_utc(), + bundles: bundles + .as_ref() + .iter() + .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) + .collect(), + // TODO: + mod_folders, + }; + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; + + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields( + game_dir = %state.game_dir.display(), + mods = state.mods.len() +))] +pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { + let state = Arc::new(state); + let bundle_dir = state.game_dir.join("bundle"); + let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); + + if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) + .await + .is_ok() + { + let err = eyre::eyre!("Found dtkit-patch-based mod installation."); + return Err(err) + .with_suggestion(|| { + "If you're a mod author and saved projects directly in 'mods/', \ + use DTMT to migrate them to the new project structure." + .to_string() + }) + .with_suggestion(|| { + "Click 'Reset Game' to remove the previous mod installation.".to_string() + }); + } + + let (_, game_info, deployment_info) = tokio::try_join!( + async { + fs::metadata(&bundle_dir) + .await + .wrap_err("Failed to open game bundle directory") + .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") + }, + async { + tokio::task::spawn_blocking(dtmt_shared::collect_game_info) + .await + .map_err(Report::new) + }, + async { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + match read_sjson_file::<_, DeploymentData>(path).await { + Ok(data) => Ok(Some(data)), + Err(err) => { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { + Ok(None) + } else { + Err(err).wrap_err("Failed to read deployment data") + } + } + } + } + ) + .wrap_err("Failed to gather deployment information")?; + + tracing::debug!(?game_info, ?deployment_info); + + if let Some(game_info) = game_info { + if deployment_info + .as_ref() + .map(|i| game_info.last_updated > i.timestamp) + .unwrap_or(false) + { + tracing::warn!( + "Game was updated since last mod deployment. \ + Attempting to reconcile game files." + ); + + tokio::try_join!( + async { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for bundle database.") + }, + async { + let path = bundle_dir.join(boot_bundle_path); + let backup_path = path.with_extension("bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for boot bundle") + } + ) + .with_suggestion(|| { + "Reset the game using 'Reset Game', then verify game files.".to_string() + })?; + + tracing::info!( + "Successfully re-created game file backups. \ + Continuing mod deployment." + ); + } + } + + check_mod_order(&state)?; + + tracing::info!( + "Deploying {} mods to '{}'.", + state.mods.iter().filter(|i| i.enabled).count(), + bundle_dir.display() + ); + + tracing::info!("Copy legacy mod folders"); + let mod_folders = copy_mod_folders(state.clone()) + .await + .wrap_err("Failed to copy mod folders")?; + + tracing::info!("Build mod bundles"); + let mut bundles = build_bundles(state.clone()) + .await + .wrap_err("Failed to build mod bundles")?; + + tracing::info!("Patch boot bundle"); + let mut boot_bundles = patch_boot_bundle(state.clone()) + .await + .wrap_err("Failed to patch boot bundle")?; + bundles.append(&mut boot_bundles); + + if let Some(info) = &deployment_info { + let bundle_dir = Arc::new(bundle_dir); + // Remove bundles from the previous deployment that don't match the current one. + // I.e. mods that used to be installed/enabled but aren't anymore. + { + let tasks = info.bundles.iter().cloned().filter_map(|file_name| { + let is_being_deployed = bundles.iter().any(|b2| { + let name = format!("{:016x}", b2.name()); + file_name == name + }); + + if !is_being_deployed { + let bundle_dir = bundle_dir.clone(); + let task = async move { + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("Failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + + futures::future::join_all(tasks).await; + } + + // Do the same thing for mod folders + { + let tasks = info.mod_folders.iter().filter_map(|mod_id| { + let is_being_deployed = mod_folders.iter().any(|id| id == mod_id); + + if !is_being_deployed { + let path = bundle_dir.join("mods").join(mod_id); + tracing::debug!("Removing unused mod folder '{}'", path.display()); + + let task = async move { + if let Err(err) = fs::remove_dir_all(&path).await.wrap_err_with(|| { + format!("Failed to remove unused legacy mod '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + + Some(task) + } else { + None + } + }); + futures::future::join_all(tasks).await; + } + } + + tracing::info!("Patch game settings"); + patch_game_settings(state.clone()) + .await + .wrap_err("Failed to patch game settings")?; + + tracing::info!("Patching bundle database"); + patch_bundle_database(state.clone(), &bundles) + .await + .wrap_err("Failed to patch bundle database")?; + + tracing::info!("Writing deployment data"); + write_deployment_data(state.clone(), &bundles, mod_folders) + .await + .wrap_err("Failed to write deployment data")?; + + tracing::info!("Finished deploying mods"); + Ok(()) +} diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 5520c4a..6b91169 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,46 +1,19 @@ -use std::collections::HashMap; -use std::io::{self, Cursor, ErrorKind}; +use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::sync::Arc; use color_eyre::eyre::Context; -use color_eyre::{eyre, Help, Report, Result}; -use futures::stream; -use futures::StreamExt; -use path_slash::PathBufExt; -use sdk::filetype::lua; -use sdk::filetype::package::Package; +use color_eyre::{eyre, Result}; use sdk::murmur::Murmur64; -use sdk::{ - Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, -}; -use serde::{Deserialize, Serialize}; -use string_template::Template; -use time::OffsetDateTime; -use tokio::fs; +use tokio::fs::{self}; use tokio::io::AsyncWriteExt; -use tracing::Instrument; -use super::read_sjson_file; -use crate::controller::app::check_mod_order; -use crate::state::{ActionState, PackageInfo}; +use crate::controller::deploy::{ + DeploymentData, BOOT_BUNDLE_NAME, BUNDLE_DATABASE_NAME, DEPLOYMENT_DATA_PATH, +}; +use crate::state::ActionState; -const MOD_BUNDLE_NAME: &str = "packages/mods"; -const BOOT_BUNDLE_NAME: &str = "packages/boot"; -const DML_BUNDLE_NAME: &str = "packages/dml"; -const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; -const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; -const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; -const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; -const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; - -#[derive(Debug, Serialize, Deserialize)] -struct DeploymentData { - bundles: Vec, - #[serde(with = "time::serde::iso8601")] - timestamp: OffsetDateTime, -} +use super::deploy::SETTINGS_FILE_PATH; #[tracing::instrument] async fn read_file_with_backup

(path: P) -> Result> @@ -130,585 +103,6 @@ async fn patch_game_settings(state: Arc) -> Result<()> { Ok(()) } -#[tracing::instrument(skip_all, fields(package = info.name))] -fn make_package(info: &PackageInfo) -> Result { - let mut pkg = Package::new(info.name.clone(), PathBuf::new()); - - for f in &info.files { - let mut it = f.rsplit('.'); - let file_type = it - .next() - .ok_or_else(|| eyre::eyre!("missing file extension")) - .and_then(BundleFileType::from_str) - .wrap_err("Invalid file name in package info")?; - let name: String = it.collect(); - pkg.add_file(file_type, name); - } - - Ok(pkg) -} - -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); - } - - lua.push('}'); - - tracing::debug!("mod_data_lua:\n{}", lua); - - lua -} - -#[tracing::instrument(skip_all)] -async fn build_bundles(state: Arc) -> Result> { - let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); - let mut tasks = Vec::new(); - - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - - let mut bundles = Vec::new(); - - { - tracing::trace!("Building mod data script"); - - let span = tracing::debug_span!("Building mod data script"); - let _enter = span.enter(); - - let lua = build_mod_data_lua(state.clone()); - - tracing::trace!("Compiling mod data script"); - - let file = - lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; - - tracing::trace!("Compile mod data script"); - - mod_bundle.add_file(file); - } - - tracing::trace!("Preparing tasks to deploy bundle files"); - - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - let span = tracing::trace_span!("building mod packages", name = mod_info.name); - let _enter = span.enter(); - - let mod_dir = state.mod_dir.join(&mod_info.id); - for pkg_info in &mod_info.packages { - let span = tracing::trace_span!("building package", name = pkg_info.name); - let _enter = span.enter(); - - tracing::trace!( - "Building package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; - let mut variant = BundleFileVariant::new(); - let bin = pkg - .to_binary() - .wrap_err("Failed to serialize package to binary")?; - variant.set_data(bin); - let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); - file.add_variant(variant); - - tracing::trace!( - "Compiled package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - mod_bundle.add_file(file); - - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = mod_dir.join(&bundle_name); - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - // Explicitely drop the guard, so that we can move the span - // into the async operation - drop(_enter); - - let ctx = state.ctx.clone(); - - let task = async move { - let bundle = { - let bin = fs::read(&src).await.wrap_err_with(|| { - format!("Failed to read bundle file '{}'", src.display()) - })?; - let name = Bundle::get_name_from_path(&ctx, &src); - Bundle::from_binary(&ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? - }; - - tracing::debug!( - src = %src.display(), - dest = %dest.display(), - "Copying bundle '{}' for mod '{}'", - pkg_name, - mod_name, - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - - Ok::(bundle) - } - .instrument(span); - - tasks.push(task); - } - } - - tracing::debug!("Copying {} mod bundles", tasks.len()); - - let mut tasks = stream::iter(tasks).buffer_unordered(10); - - while let Some(res) = tasks.next().await { - let bundle = res?; - bundles.push(bundle); - } - - { - let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); - tracing::trace!("Writing mod bundle to '{}'", path.display()); - fs::write(&path, mod_bundle.to_binary()?) - .await - .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; - } - - bundles.push(mod_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); - - let mut bundles = Vec::with_capacity(2); - - let mut boot_bundle = async { - let bin = read_file_with_backup(&bundle_path) - .await - .wrap_err("Failed to read boot bundle")?; - - Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) - .wrap_err("Failed to parse boot bundle") - } - .instrument(tracing::trace_span!("read boot bundle")) - .await - .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; - - { - tracing::trace!("Adding mod package file to boot bundle"); - let span = tracing::trace_span!("create mod package file"); - let _enter = span.enter(); - - let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); - - for mod_info in &state.mods { - for pkg_info in &mod_info.packages { - pkg.add_file(BundleFileType::Package, &pkg_info.name); - } - } - - pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); - - let mut variant = BundleFileVariant::new(); - variant.set_data(pkg.to_binary()?); - let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - tracing::trace!("Handling DML packages and bundle"); - let span = tracing::trace_span!("handle DML"); - let _enter = span.enter(); - - let mut variant = BundleFileVariant::new(); - - let mod_info = state - .mods - .iter() - .find(|m| m.id == "dml") - .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; - let pkg_info = mod_info - .packages - .get(0) - .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) - .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); - - { - let bin = fs::read(&src) - .await - .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; - let name = Bundle::get_name_from_path(&state.ctx, &src); - - let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; - - bundles.push(dml_bundle); - }; - - { - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - tracing::debug!( - "Copying bundle {} for mod {}: {} -> {}", - pkg_name, - mod_name, - src.display(), - dest.display() - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - } - - let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; - variant.set_data(pkg.to_binary()?); - - let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - let span = tracing::debug_span!("Importing mod main script"); - let _enter = span.enter(); - - let is_io_enabled = format!("{}", state.is_io_enabled); - let mut data = HashMap::new(); - data.insert("is_io_enabled", is_io_enabled.as_str()); - - let tmpl = include_str!("../../assets/mod_main.lua"); - let lua = Template::new(tmpl).render(&data); - tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); - let file = - lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; - - boot_bundle.add_file(file); - } - - async { - let bin = boot_bundle - .to_binary() - .wrap_err("Failed to serialize boot bundle")?; - fs::write(&bundle_path, bin) - .await - .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) - } - .instrument(tracing::trace_span!("write boot bundle")) - .await?; - - bundles.push(boot_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); - - let mut db = { - let bin = read_file_with_backup(&database_path) - .await - .wrap_err("Failed to read bundle database")?; - let mut r = Cursor::new(bin); - let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; - tracing::trace!("Finished parsing bundle database"); - db - }; - - for bundle in bundles.as_ref() { - tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); - db.add_bundle(bundle); - } - - { - let bin = db - .to_binary() - .wrap_err("Failed to serialize bundle database")?; - fs::write(&database_path, bin).await.wrap_err_with(|| { - format!( - "failed to write bundle database to '{}'", - database_path.display() - ) - })?; - } - - Ok(()) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let info = DeploymentData { - timestamp: OffsetDateTime::now_utc(), - bundles: bundles - .as_ref() - .iter() - .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) - .collect(), - }; - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; - - fs::write(&path, &data) - .await - .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; - - Ok(()) -} - -#[tracing::instrument(skip_all, fields( - game_dir = %state.game_dir.display(), - mods = state.mods.len() -))] -pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { - let state = Arc::new(state); - let bundle_dir = state.game_dir.join("bundle"); - let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); - - if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) - .await - .is_ok() - { - let err = eyre::eyre!("Found dtkit-patch-based mod installation."); - return Err(err) - .with_suggestion(|| { - "If you're a mod author and saved projects directly in 'mods/', \ - use DTMT to migrate them to the new project structure." - .to_string() - }) - .with_suggestion(|| { - "Click 'Reset Game' to remove the previous mod installation.".to_string() - }); - } - - let (_, game_info, deployment_info) = tokio::try_join!( - async { - fs::metadata(&bundle_dir) - .await - .wrap_err("Failed to open game bundle directory") - .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") - }, - async { - tokio::task::spawn_blocking(dtmt_shared::collect_game_info) - .await - .map_err(Report::new) - }, - async { - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path).await { - Ok(data) => Ok(Some(data)), - Err(err) => { - if let Some(err) = err.downcast_ref::() - && err.kind() == ErrorKind::NotFound - { - Ok(None) - } else { - Err(err).wrap_err("Failed to read deployment data") - } - } - } - } - ) - .wrap_err("Failed to gather deployment information")?; - - tracing::debug!(?game_info, ?deployment_info); - - if let Some(game_info) = game_info { - if deployment_info - .as_ref() - .map(|i| game_info.last_updated > i.timestamp) - .unwrap_or(false) - { - tracing::warn!( - "Game was updated since last mod deployment. \ - Attempting to reconcile game files." - ); - - tokio::try_join!( - async { - let path = bundle_dir.join(BUNDLE_DATABASE_NAME); - let backup_path = path.with_extension("data.bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for bundle database.") - }, - async { - let path = bundle_dir.join(boot_bundle_path); - let backup_path = path.with_extension("bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for boot bundle") - } - ) - .with_suggestion(|| { - "Reset the game using 'Reset Game', then verify game files.".to_string() - })?; - - tracing::info!( - "Successfully re-created game file backups. \ - Continuing mod deployment." - ); - } - } - - check_mod_order(&state)?; - - tracing::info!( - "Deploying {} mods to '{}'.", - state.mods.iter().filter(|i| i.enabled).count(), - bundle_dir.display() - ); - - tracing::info!("Build mod bundles"); - let mut bundles = build_bundles(state.clone()) - .await - .wrap_err("Failed to build mod bundles")?; - - tracing::info!("Patch boot bundle"); - let mut more_bundles = patch_boot_bundle(state.clone()) - .await - .wrap_err("Failed to patch boot bundle")?; - bundles.append(&mut more_bundles); - - if let Some(info) = &deployment_info { - let bundle_dir = Arc::new(bundle_dir); - let tasks = info.bundles.iter().cloned().filter_map(|file_name| { - let contains = bundles.iter().any(|b2| { - let name = format!("{:016x}", b2.name()); - file_name == name - }); - - if !contains { - let bundle_dir = bundle_dir.clone(); - let task = async move { - let path = bundle_dir.join(&file_name); - - tracing::debug!("Removing unused bundle '{}'", file_name); - - if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("Failed to remove unused bundle '{}'", path.display()) - }) { - tracing::error!("{:?}", err); - } - }; - Some(task) - } else { - None - } - }); - - futures::future::join_all(tasks).await; - } - - tracing::info!("Patch game settings"); - patch_game_settings(state.clone()) - .await - .wrap_err("Failed to patch game settings")?; - - tracing::info!("Patching bundle database"); - patch_bundle_database(state.clone(), &bundles) - .await - .wrap_err("Failed to patch bundle database")?; - - tracing::info!("Writing deployment data"); - write_deployment_data(state.clone(), &bundles) - .await - .wrap_err("Failed to write deployment data")?; - - tracing::info!("Finished deploying mods"); - Ok(()) -} - #[tracing::instrument(skip_all)] async fn reset_dtkit_patch(state: ActionState) -> Result<()> { let bundle_dir = state.game_dir.join("bundle"); diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs new file mode 100644 index 0000000..3e17d4c --- /dev/null +++ b/crates/dtmm/src/controller/import.rs @@ -0,0 +1,475 @@ +use std::collections::HashMap; +use std::ffi::CStr; +use std::io::{Cursor, Read, Seek, Write}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use color_eyre::eyre::{self, Context}; +use color_eyre::{Help, Report, Result}; +use druid::im::Vector; +use druid::{FileInfo, ImageBuf}; +use dtmt_shared::{ModConfig, ModConfigResources}; +use luajit2_sys as lua; +use nexusmods::Api as NexusApi; +use tokio::fs; +use zip::ZipArchive; + +use crate::state::{ActionState, ModInfo, NexusInfo, PackageInfo}; + +fn find_archive_file( + archive: &ZipArchive, + name: impl AsRef, +) -> Option { + let path = archive + .file_names() + .find(|path| path.ends_with(name.as_ref())) + .map(|s| s.to_string()); + path +} + +// Runs the content of a `.mod` file to extract what data we can get +// from legacy mods. +// 1. Create a global function `new_mod` that stores +// the relevant bits in global variables. +// 2. Run the `.mod` file, which will merely return a table. +// 3. Run the `run` function from that table. +// 4. Access the global variables from #1. +#[tracing::instrument] +fn parse_mod_id_file(data: &str) -> Result<(String, ModConfigResources)> { + tracing::debug!("Parsing mod file:\n{}", data); + + let ret = unsafe { + let state = lua::luaL_newstate(); + lua::luaL_openlibs(state); + + let run = b" +function fassert() end +function new_mod(id, resources) + _G.id = id + _G.script = resources.mod_script + _G.data = resources.mod_data + _G.localization = resources.mod_localization +end +\0"; + match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to create `new_mod`") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run buffer: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run buffer") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let name = b".mod\0"; + match lua::luaL_loadbuffer( + state, + data.as_ptr() as _, + data.len() as _, + name.as_ptr() as _, + ) as u32 + { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to load `.mod` file buffer") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 1, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod` file: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod` file") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let key = b"run\0"; + lua::lua_pushstring(state, key.as_ptr() as _); + lua::lua_gettable(state, -2); + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod.run`: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod.run`") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let get_global = |state, key: &[u8]| { + lua::lua_getglobal(state, key.as_ptr() as _); + + if lua::lua_isnil(state, -1) != 0 { + return Ok(None); + } + + let s = lua::lua_tostring(state, -1); + + if s.is_null() { + eyre::bail!("Expected string, got NULL"); + } + + let ret = CStr::from_ptr(s).to_string_lossy().to_string(); + lua::lua_pop(state, 1); + Ok(Some(ret)) + }; + + let mod_id = get_global(state, b"id\0") + .and_then(|s| s.ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `id`")?; + + let resources = ModConfigResources { + init: get_global(state, b"script\0") + .and_then(|s| s.map(PathBuf::from).ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `script`.")?, + data: get_global(state, b"data\0") + .wrap_err("Failed to get `data`.")? + .map(PathBuf::from), + localization: get_global(state, b"localization\0") + .wrap_err("Failed to get `localization`")? + .map(PathBuf::from), + }; + + lua::lua_close(state); + + (mod_id, resources) + }; + + Ok(ret) +} + +// Extracts the mod configuration from the mod archive. +// This may either be a proper `dtmt.cfg`, or the legacy `.mod` ID file. +// +// It also returns the directory where this file was found, used as root path. This +// allows flexibility in what the directory structure is exactly, since many people +// still end up creating tarbombs and Nexus does its own re-packaging. +#[tracing::instrument(skip(archive))] +fn extract_mod_config(archive: &mut ZipArchive) -> Result<(ModConfig, String)> { + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read mod config from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read mod config from archive")?; + + let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; + + let cfg = serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")?; + let root = name + .strip_suffix("dtmt.cfg") + .expect("String must end with that suffix") + .to_string(); + + Ok((cfg, root)) + } else if let Some(name) = find_archive_file(archive, ".mod") { + let (mod_id, resources) = { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read `.mod` file from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read `.mod` file from archive")?; + + let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; + parse_mod_id_file(&data).wrap_err("Invalid `.mod` file")? + }; + + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: String::new(), + version: String::new(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + let root = if let Some(index) = name.rfind('/') { + name[..index].to_string() + } else { + String::new() + }; + + Ok((cfg, root)) + } else { + eyre::bail!( + "Mod needs either a config file or `.mod` file. \ + Please get in touch with the author to provide a properly packaged mod." + ); + } +} + +#[tracing::instrument(skip(archive))] +fn extract_bundled_mod( + archive: &mut ZipArchive, + root: String, + dest: impl AsRef + std::fmt::Debug, +) -> Result>> { + let files: HashMap> = { + let name = archive + .file_names() + .find(|name| name.ends_with("files.sjson")) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read file index from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; + serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? + }; + + tracing::trace!(?files); + + let dest = dest.as_ref(); + tracing::trace!("Extracting mod archive to {}", dest.display()); + archive + .extract(dest) + .wrap_err_with(|| format!("Failed to extract archive to {}", dest.display()))?; + + let packages = files + .into_iter() + .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) + .collect(); + + tracing::trace!(?packages); + + Ok(packages) +} + +#[tracing::instrument(skip(archive))] +fn extract_legacy_mod( + archive: &mut ZipArchive, + root: String, + dest: impl Into + std::fmt::Debug, +) -> Result<()> { + let dest = dest.into(); + let file_count = archive.len(); + + for i in 0..file_count { + let mut f = archive + .by_index(i) + .wrap_err_with(|| format!("Failed to get file at index {}", i))?; + + let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { + let err = eyre::eyre!("File name in archive is not a safe path value."); + return Err(err).with_suggestion(|| { + "Only use well-known applications to create the ZIP archive, \ + and don't create paths that point outside the archive directory." + }); + }; + + let Ok(suffix) = name.strip_prefix(&root) else { + tracing::warn!( + "Skipping file outside of the mod root directory: {}", + name.display() + ); + continue; + }; + let name = dest.join(suffix); + + if f.is_dir() { + // The majority of errors will actually be "X already exists". + // But rather than filter them invidually, we just ignore all of them. + // If there is a legitimate error of "couldn't create X", it will eventually fail when + // we try to put a file in there. + tracing::trace!("Creating directory '{}'", name.display()); + let _ = std::fs::create_dir_all(&name); + } else { + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err_with(|| format!("Failed to read file '{}'", name.display()))?; + + tracing::trace!("Writing file '{}'", name.display()); + let mut out = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(&name) + .wrap_err_with(|| format!("Failed to open file '{}'", name.display()))?; + + out.write_all(&buf) + .wrap_err_with(|| format!("Failed to write to '{}'", name.display()))?; + } + } + + Ok(()) +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { + let data = fs::read(&info.path) + .await + .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; + let data = Cursor::new(data); + + let nexus = if let Some((_, id, _, _)) = info + .path + .file_name() + .and_then(|s| s.to_str()) + .and_then(NexusApi::parse_file_name) + { + if !state.nexus_api_key.is_empty() { + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let mod_info = api + .mods_id(id) + .await + .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; + Some(NexusInfo::from(mod_info)) + } else { + None + } + } else { + None + }; + + let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; + + if tracing::enabled!(tracing::Level::DEBUG) { + let names = archive.file_names().fold(String::new(), |mut s, name| { + s.push('\n'); + s.push_str(name); + s + }); + tracing::debug!("Archive contents:{}", names); + } + + let (mod_cfg, root) = + extract_mod_config(&mut archive).wrap_err("Failed to extract mod configuration")?; + tracing::info!("Importing mod {} ({})", mod_cfg.name, mod_cfg.id); + tracing::debug!(root, ?mod_cfg); + + let image = if let Some(path) = &mod_cfg.image { + let name = archive + .file_names() + .find(|name| name.ends_with(&path.display().to_string())) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read image file from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + let img = match ImageBuf::from_data(&buf) { + Ok(img) => img, + Err(err) => { + let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); + return Err(err).with_suggestion(|| { + "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() + }); + } + }; + + Some(img) + } else { + None + }; + + tracing::trace!(?image); + + let mod_dir = state.data_dir.join(state.mod_dir.as_ref()); + let dest = mod_dir.join(&mod_cfg.id); + + tracing::trace!("Creating mods directory {}", dest.display()); + fs::create_dir_all(&dest) + .await + .wrap_err_with(|| format!("Failed to create data directory '{}'", dest.display()))?; + + let packages = if mod_cfg.bundled { + extract_bundled_mod(&mut archive, root, &mod_dir).wrap_err("Failed to extract mod")? + } else { + extract_legacy_mod(&mut archive, root, &dest).wrap_err("Failed to extract legacy mod")?; + + let data = serde_sjson::to_string(&mod_cfg).wrap_err("Failed to serialize mod config")?; + fs::write(dest.join("dtmt.cfg"), &data) + .await + .wrap_err("Failed to write mod config")?; + + Default::default() + }; + + if let Some(nexus) = &nexus { + let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; + let path = dest.join("nexus.sjson"); + fs::write(&path, data.as_bytes()) + .await + .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; + } + + let info = ModInfo::new(mod_cfg, packages, image, nexus); + Ok(info) +} diff --git a/crates/dtmm/src/controller/mod.rs b/crates/dtmm/src/controller/mod.rs index eacc3ef..9c75e84 100644 --- a/crates/dtmm/src/controller/mod.rs +++ b/crates/dtmm/src/controller/mod.rs @@ -5,7 +5,9 @@ use serde::Deserialize; use tokio::fs; pub mod app; +pub mod deploy; pub mod game; +pub mod import; pub mod worker; #[tracing::instrument] diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 238b3f3..518c0a8 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -13,7 +13,9 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::RwLock; use crate::controller::app::*; +use crate::controller::deploy::deploy_mods; use crate::controller::game::*; +use crate::controller::import::import_mod; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 9ba838c..8069e6d 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,6 +1,7 @@ #![recursion_limit = "256"] #![feature(let_chains)] #![feature(arc_unwrap_or_clone)] +#![feature(iterator_try_collect)] #![windows_subsystem = "windows"] use std::path::PathBuf; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index c43e973..8a461bd 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,7 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, - pub bundle: bool, + pub bundled: bool, #[data(ignore)] pub nexus: Option, } @@ -130,7 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, - bundle: cfg.bundle, + bundled: cfg.bundled, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7aa2819..7de4418 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -145,7 +145,7 @@ fn build_mod_list() -> impl Widget { let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); - Svg::new(Arc::new(tree)).fix_height(druid::theme::TEXT_SIZE_NORMAL) + Svg::new(tree).fix_height(druid::theme::TEXT_SIZE_NORMAL) }; Either::new( diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 8ecd648..2eacded 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,7 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], - bundle: true, + bundled: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index a162d3b..28e4694 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -63,7 +63,7 @@ pub struct ModConfig { #[serde(default)] pub depends: Vec, #[serde(default = "default_true", skip_serializing_if = "is_true")] - pub bundle: bool, + pub bundled: bool, } pub const STEAMAPP_ID: u32 = 1361210; From 15498cc2e0f6ee5288f97a61aa1e14def69c5ff1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 10 Nov 2023 11:13:08 +0100 Subject: [PATCH 033/184] Move deployment directory for legacy mods This moves it back to its original place at `$game_dir/mods`. --- crates/dtmm/src/controller/deploy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 6a804e7..ded840e 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -223,7 +223,7 @@ async fn copy_recursive( #[tracing::instrument(skip(state))] async fn copy_mod_folders(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let game_dir = Arc::clone(&state.game_dir); let mut tasks = Vec::new(); @@ -237,11 +237,11 @@ async fn copy_mod_folders(state: Arc) -> Result> { let mod_id = mod_info.id.clone(); let mod_dir = Arc::clone(&state.mod_dir); - let bundle_dir = Arc::clone(&bundle_dir); + let game_dir = Arc::clone(&game_dir); let task = async move { let from = mod_dir.join(&mod_id); - let to = bundle_dir.join("mods").join(&mod_id); + let to = game_dir.join("mods").join(&mod_id); tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); let _ = fs::create_dir_all(&to).await; From 13e77a209784ddc62eecd90c70171e7bdc74d8c0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 12 Nov 2023 23:20:10 +0100 Subject: [PATCH 034/184] Use template engine to build `mod_data.lua` The string-building version became too complex to maintain properly. --- Cargo.lock | 10 +++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/assets/mod_data.lua.j2 | 27 ++++++ crates/dtmm/assets/mod_main.lua | 10 +-- crates/dtmm/src/controller/deploy.rs | 119 +++++++++++++-------------- 5 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 crates/dtmm/assets/mod_data.lua.j2 diff --git a/Cargo.lock b/Cargo.lock index 011fa6b..8385e67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,6 +906,7 @@ dependencies = [ "futures", "lazy_static", "luajit2-sys", + "minijinja", "nexusmods", "oodle", "path-slash", @@ -2084,6 +2085,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minijinja" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" +dependencies = [ + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 1842a62..5f60220 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -35,3 +35,4 @@ ansi-parser = "0.9.0" string_template = "0.2.1" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } async-recursion = "1.0.5" +minijinja = "1.0.10" diff --git a/crates/dtmm/assets/mod_data.lua.j2 b/crates/dtmm/assets/mod_data.lua.j2 new file mode 100644 index 0000000..9f87ad1 --- /dev/null +++ b/crates/dtmm/assets/mod_data.lua.j2 @@ -0,0 +1,27 @@ +return { +{% for mod in mods %} +{ + id = "{{ mod.id }}", + name = "{{ mod.name }}", + bundled = {{ mod.bundled }}, + packages = { + {% for pkg in mod.packages %} + "{{ pkg }}", + {% endfor %} + }, + run = function() + {% if mod.data is none %} + return dofile("{{ mod.init }}") + {% else %} + new_mod("{{ mod.id }}", { + mod_script = "{{ mod.init }}", + mod_data = "{{ mod.data }}", + {% if not mod.localization is none %} + mod_localization = "{{ mod.localization }}", + {% endif %} + }) + {% endif %} + end, +}, +{% endfor %} +} diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 6dbf3e2..a17f5be 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -61,14 +61,14 @@ local function patch_mod_loading_state() if state == "load_package" and package_manager:update() then log("StateBootLoadMods", "Packages loaded, loading mods") self._state = "load_mods" - local ModLoader = require("scripts/mods/dml/init") + local DML = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - local mod_loader = ModLoader:new(mod_data, self._parent:gui()) + local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - self._mod_loader = mod_loader + self._dml = DML Managers.mod = mod_loader - elseif state == "load_mods" and self._mod_loader:update(dt) then + elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then log("StateBootLoadMods", "Mods loaded, exiting") return true, false end @@ -112,7 +112,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- It's also a valid table definition, thereby degrading gracefully when not replaced. -local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 +local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index ded840e..53c4ef1 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -8,7 +8,7 @@ use color_eyre::eyre::Context; use color_eyre::{eyre, Help, Report, Result}; use futures::StreamExt; use futures::{stream, TryStreamExt}; -use path_slash::PathBufExt; +use minijinja::Environment; use sdk::filetype::lua; use sdk::filetype::package::Package; use sdk::murmur::Murmur64; @@ -201,10 +201,10 @@ async fn copy_recursive( async move { if is_dir { tracing::trace!("Creating directory '{}'", dest.display()); - fs::create_dir(&dest) - .await - .map(|_| ()) - .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + // Instead of trying to filter "already exists" errors out explicitly, + // we just ignore all. It'll fail eventually with the next copy operation. + let _ = fs::create_dir(&dest).await; + Ok(()) } else { tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { @@ -262,67 +262,60 @@ async fn copy_mod_folders(state: Arc) -> Result> { Ok(ids) } -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n bundled = \""); - if mod_info.bundled { - lua.push_str("true"); - } else { - lua.push_str("false"); - } - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); +fn build_mod_data_lua(state: Arc) -> Result { + #[derive(Serialize)] + struct TemplateDataMod { + id: String, + name: String, + bundled: bool, + init: String, + data: Option, + localization: Option, + packages: Vec, } - lua.push('}'); + let mut env = Environment::new(); + env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2")) + .wrap_err("Failed to compile template for `mod_data.lua`")?; + let tmpl = env + .get_template("mod_data.lua") + .wrap_err("Failed to get template `mod_data.lua`")?; - tracing::debug!("mod_data_lua:\n{}", lua); + let data: Vec = state + .mods + .iter() + .filter_map(|m| { + if m.id == "dml" || !m.enabled { + return None; + } - lua + Some(TemplateDataMod { + id: m.id.clone(), + name: m.name.clone(), + bundled: m.bundled, + init: m.resources.init.to_string_lossy().to_string(), + data: m + .resources + .data + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + localization: m + .resources + .localization + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + packages: m.packages.iter().map(|p| p.name.clone()).collect(), + }) + }) + .collect(); + + let lua = tmpl + .render(minijinja::context!(mods => data)) + .wrap_err("Failed to render template `mod_data.lua`")?; + + tracing::debug!("mod_data.lua:\n{}", lua); + + Ok(lua) } #[tracing::instrument(skip_all)] @@ -340,7 +333,7 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::debug_span!("Building mod data script"); let _enter = span.enter(); - let lua = build_mod_data_lua(state.clone()); + let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?; tracing::trace!("Compiling mod data script"); From 3af631348d3950d760f8829811e435a8a2e16f68 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:06:19 +0100 Subject: [PATCH 035/184] Fix missing `Mods.original_require` --- crates/dtmm/assets/mod_main.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index a17f5be..2b329bf 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -138,7 +138,8 @@ Mods = { -- Fatshark's code scrubs them. -- The loader can then decide to pass them on to mods, or ignore them lua = setmetatable({}, { __index = lua_libs }), - require_store = require_store + require_store = require_store, + original_require = require, } local can_insert = function(filepath, new_result) From 6c9472399562a6613e12da726ede4e45a249bad7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:07:07 +0100 Subject: [PATCH 036/184] Fix Nexusmods API key not being loaded from config --- crates/dtmm/src/controller/import.rs | 5 ++++- crates/dtmm/src/state/delegate.rs | 1 + crates/dtmm/src/util/config.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 3e17d4c..05feba9 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -384,7 +384,10 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result for Delegate { state.config_path = Arc::new(config.path); state.data_dir = Arc::new(config.data_dir); state.game_dir = Arc::new(config.game_dir.unwrap_or_default()); + state.nexus_api_key = Arc::new(config.nexus_api_key.unwrap_or_default()); state.is_io_enabled = config.unsafe_io; } diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 3a0d0b2..9affbb6 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -125,6 +125,9 @@ where .wrap_err_with(|| format!("Invalid config file {}", path.display()))?; cfg.path = path; + + tracing::debug!("Read config file '{}': {:?}", cfg.path.display(), cfg); + Ok(cfg) } Err(err) if err.kind() == ErrorKind::NotFound => { @@ -133,6 +136,11 @@ where .wrap_err_with(|| format!("Failed to read config file {}", path.display()))?; } + tracing::debug!( + "Config file not found at '{}', creating default.", + path.display() + ); + { let parent = default_path .parent() From a228ea465253c0f1b3647343b914fcd9a90e1d13 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:38:51 +0100 Subject: [PATCH 037/184] Use mod name from Nexus if necessary Non-bundled mods come without `dtmt.cfg` and therefore no way to provide a user friendly name. Similar to the other fields, use the one from Nexus in that case. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/ui/window/main.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 8a461bd..64fdd28 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -73,6 +73,7 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { pub id: u64, + pub name: String, pub version: String, pub author: String, pub summary: Arc, @@ -83,6 +84,7 @@ impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { id: value.mod_id, + name: value.name, version: value.version, author: value.author, summary: Arc::new(value.summary), diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7de4418..baa8e22 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -135,8 +135,13 @@ fn build_mod_list() -> impl Widget { }) .lens(lens!((usize, Arc, bool), 1).then(ModInfo::enabled.in_arc())); - let name = - Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); + let name = Label::dynamic(|info: &Arc, _| { + info.nexus + .as_ref() + .map(|n| n.name.clone()) + .unwrap_or_else(|| info.name.clone()) + }) + .lens(lens!((usize, Arc, bool), 1)); let version = { let icon = { From 3cbf383b189937c1909590a83e20317e575e6720 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 16:19:07 +0100 Subject: [PATCH 038/184] Use version number from Nexus import Non-bundled mods come without a `dtmt.cfg`, and therefore without a version number. But we need a version number at import to compare to for the Nexus update check. --- crates/dtmm/src/controller/import.rs | 17 ++++++++++++----- lib/nexusmods/src/lib.rs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 05feba9..c5b5948 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -372,7 +372,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Result Result Result Date: Thu, 16 Nov 2023 00:06:16 +0100 Subject: [PATCH 039/184] Prevent excessive debug logs --- crates/dtmm/src/controller/worker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 518c0a8..152030f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -38,7 +38,9 @@ async fn handle_action( action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { - tracing::debug!(?action); + if cfg!(debug_assertions) && !matches!(action, AsyncAction::Log(_)) { + tracing::debug!(?action); + } let event_sink = event_sink.clone(); match action { From 845b0114bbd6d728489dba308a188a9860bab3cb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Nov 2023 14:29:06 +0100 Subject: [PATCH 040/184] Delay mod loading The initial implementation of DML ended up loading mods quite late, which did give it the benefit of all `Manager`s being available. This change therefore moves mod loading until after those are initialized. But contrary to old DML, we still create a separate game state to make sure the game doesn't advance until mods are loaded. This avoids race conditions like the one where LogMeIn needs to come early in the load order. --- crates/dtmm/assets/mod_main.lua | 186 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 94 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 2b329bf..e4006f6 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -11,100 +11,6 @@ local log = function(category, format, ...) end end --- Patch `GameStateMachine.init` to add our own state for loading mods. --- In the future, Fatshark might provide us with a dedicated way to do this. -local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") - - -- A necessary override. - -- The original does not proxy `dt` to `_state_update`, but we need that. - StateBootSubStateBase.update = function(self, dt) - local done, error = self:_state_update(dt) - local params = self._params - - if error then - return StateError, { error } - elseif done then - local next_index = params.sub_state_index + 1 - params.sub_state_index = next_index - local next_state_data = params.states[next_index] - - if next_state_data then - return next_state_data[1], self._params - else - self._parent:sub_states_done() - end - end - end - - local StateBootLoadMods = class("StateBootLoadMods", "StateBootSubStateBase") - - StateBootLoadMods.on_enter = function(self, parent, params) - log("StateBootLoadMods", "Entered") - StateBootLoadMods.super.on_enter(self, parent, params) - - local state_params = self:_state_params() - local package_manager = state_params.package_manager - - self._state = "load_package" - self._package_manager = package_manager - self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadMods", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootLoadMods", nil), - } - end - - StateBootLoadMods._state_update = function(self, dt) - local state = self._state - local package_manager = self._package_manager - - if state == "load_package" and package_manager:update() then - log("StateBootLoadMods", "Packages loaded, loading mods") - self._state = "load_mods" - local DML = require("scripts/mods/dml/init") - - local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - - self._dml = DML - Managers.mod = mod_loader - elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then - log("StateBootLoadMods", "Mods loaded, exiting") - return true, false - end - - return false, false - end - - local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") - - local patched = false - - local GameStateMachine_init = GameStateMachine.init - GameStateMachine.init = function(self, parent, start_state, params, ...) - if not patched then - log("mod_main", "Injecting mod loading state") - patched = true - - -- Hardcoded position after `StateRequireScripts`. - -- We do want to wait until then, so that most of the game's core - -- systems are at least loaded and can be hooked, even if they aren't - -- running, yet. - local pos = 4 - table.insert(params.states, pos, { - StateBootLoadMods, - { - package_manager = params.package_manager, - }, - }) - end - - GameStateMachine_init(self, parent, start_state, params, ...) - end - - log("mod_main", "Mod patching complete") -end - log("mod_main", "Initializing mods...") local require_store = {} @@ -199,6 +105,98 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") +-- Inject our state into the game. The state needs to run after `StateGame._init_managers`, +-- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +local function patch_mod_loading_state() + local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") + local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") + local StateGameLoadMods = class("StateGameLoadMods") + + StateBootLoadDML.on_enter = function(self, parent, params) + log("StateBootLoadDML", "Entered") + StateBootLoadDML.super.on_enter(self, parent, params) + + local state_params = self:_state_params() + local package_manager = state_params.package_manager + + self._package_manager = package_manager + self._package_handles = { + ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), + ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + } + end + + StateBootLoadDML._state_update = function(self, dt) + local package_manager = self._package_manager + + if package_manager:update() then + local DML = require("scripts/mods/dml/init") + local mod_data = require("scripts/mods/mod_data") + local mod_loader = DML.create_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") + return true, false + end + + return false, false + end + + + function StateGameLoadMods:on_enter(_, params) + log("StateGameLoadMods", "Entered") + self._next_state = require("scripts/game_states/game/state_splash") + self._next_state_params = params + end + + function StateGameLoadMods:update(main_dt) + local state = self._loading_state + + -- We're relying on the fact that DML internally makes sure + -- that `Managers.mod:update()` is being called appropriately. + -- The implementation as of this writing is to hook `StateGame.update`. + if Managers.mod:all_mods_loaded() then + Log.info("StateGameLoadMods", "Mods loaded, exiting") + return self._next_state, self._next_state_params + end + end + + local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + local GameStateMachine_init = GameStateMachine.init + GameStateMachine.init = function(self, parent, start_state, params, creation_context, state_change_callbacks, name) + if name == "Main" then + log("mod_main", "Injecting StateBootLoadDML") + + -- Hardcoded position after `StateRequireScripts`. + -- We need to wait until then to even begin most of our stuff, + -- so that most of the game's core systems are at least loaded and can be hooked, + -- even if they aren't running, yet. + local pos = 4 + table.insert(params.states, pos, { + StateBootLoadDML, + { + package_manager = params.package_manager, + }, + }) + + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + elseif name == "Game" then + log("mod_main", "Injection StateGameLoadMods") + -- The second time around, we want to be the first, so we pass our own + -- 'start_state'. + -- We can't just have the state machine be initialized and then change its `_next_state`, as by the end of + -- `init`, a bunch of stuff will already be initialized. + GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) + -- And since we're done now, we can revert the function to its original + GameStateMachine.init = GameStateMachine_init + + return + else + -- In all other cases, simply call the original + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + end + end +end + -- Override `init` to run our injection function init() patch_mod_loading_state() From 8ecca087dea5de0f1bb473f5580ddd968486e7f8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 00:50:56 +0100 Subject: [PATCH 041/184] dtmm: Use `dtmt.cfg` for non-bundled mods Closes #144. --- crates/dtmm/src/controller/import.rs | 106 +++++++++++++++------------ 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index c5b5948..131d01f 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -31,7 +31,7 @@ fn find_archive_file( // from legacy mods. // 1. Create a global function `new_mod` that stores // the relevant bits in global variables. -// 2. Run the `.mod` file, which will merely return a table. +// 2. Run the `.mod` file, which will return a table. // 3. Run the `run` function from that table. // 4. Access the global variables from #1. #[tracing::instrument] @@ -203,6 +203,37 @@ end // still end up creating tarbombs and Nexus does its own re-packaging. #[tracing::instrument(skip(archive))] fn extract_mod_config(archive: &mut ZipArchive) -> Result<(ModConfig, String)> { + let legacy_mod_data = if let Some(name) = find_archive_file(archive, ".mod") { + let (mod_id, resources) = { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read `.mod` file from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read `.mod` file from archive")?; + + let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; + parse_mod_id_file(&data) + .wrap_err("Invalid `.mod` file") + .note( + "The `.mod` file's `run` function may not contain any additional logic \ + besides the default.", + ) + .suggestion("Contact the mod author to fix this.")? + }; + + let root = if let Some(index) = name.rfind('/') { + name[..index].to_string() + } else { + String::new() + }; + + Some((mod_id, resources, root)) + } else { + None + }; + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -214,52 +245,30 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; - let cfg = serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")?; - let root = name - .strip_suffix("dtmt.cfg") - .expect("String must end with that suffix") - .to_string(); + let mut cfg: ModConfig = serde_sjson::from_str(&data) + .wrap_err("Failed to deserialize mod config") + .suggestion("Contact the mod author to fix this.")?; - Ok((cfg, root)) - } else if let Some(name) = find_archive_file(archive, ".mod") { - let (mod_id, resources) = { - let mut f = archive - .by_name(&name) - .wrap_err("Failed to read `.mod` file from archive")?; + if let Some((mod_id, resources, root)) = legacy_mod_data { + if cfg.id != mod_id { + let err = eyre::eyre!("Mod ID in `dtmt.cfg` does not match mod ID in `.mod` file"); + return Err(err).suggestion("Contact the mod author to fix this."); + } - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read `.mod` file from archive")?; + cfg.resources = resources; - let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; - parse_mod_id_file(&data).wrap_err("Invalid `.mod` file")? - }; - - let cfg = ModConfig { - bundled: false, - dir: PathBuf::new(), - id: mod_id.clone(), - name: mod_id, - summary: String::new(), - version: String::new(), - description: None, - author: None, - image: None, - categories: Vec::new(), - packages: Vec::new(), - resources, - depends: Vec::new(), - }; - let root = if let Some(index) = name.rfind('/') { - name[..index].to_string() + Ok((cfg, root)) } else { - String::new() - }; + let root = name + .strip_suffix("dtmt.cfg") + .expect("String must end with that suffix") + .to_string(); - Ok((cfg, root)) + Ok((cfg, root)) + } } else { eyre::bail!( - "Mod needs either a config file or `.mod` file. \ + "Mod needs a config file or `.mod` file. \ Please get in touch with the author to provide a properly packaged mod." ); } @@ -322,11 +331,11 @@ fn extract_legacy_mod( .wrap_err_with(|| format!("Failed to get file at index {}", i))?; let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { - let err = eyre::eyre!("File name in archive is not a safe path value."); - return Err(err).with_suggestion(|| { + let err = eyre::eyre!("File name in archive is not a safe path value.").suggestion( "Only use well-known applications to create the ZIP archive, \ - and don't create paths that point outside the archive directory." - }); + and don't create paths that point outside the archive directory.", + ); + return Err(err); }; let Ok(suffix) = name.strip_prefix(&root) else { @@ -430,10 +439,11 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result img, Err(err) => { - let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); - return Err(err).with_suggestion(|| { - "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() - }); + let err = Report::msg(err.to_string()) + .wrap_err("Invalid image data") + .note("Supported formats are: PNG, JPEG, Bitmap and WebP") + .suggestion("Contact the mod author to fix this"); + return Err(err); } }; From 0b3c92d19e9d1ffe6bc09c7e901dd654c2b87154 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 11:49:11 +0100 Subject: [PATCH 042/184] dtmm: Fetch mod image from Nexus Closes #129. --- crates/dtmm/src/controller/import.rs | 56 ++++++++++++++++++++-------- crates/dtmm/src/state/data.rs | 2 + lib/nexusmods/src/lib.rs | 12 +++++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 131d01f..68c54a5 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -27,6 +27,16 @@ fn find_archive_file( path } +fn image_data_to_buffer(data: impl AsRef<[u8]>) -> Result { + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + ImageBuf::from_data(data.as_ref()).map_err(|err| { + Report::msg(err.to_string()) + .wrap_err("Invalid image data") + .suggestion("Supported formats are: PNG, JPEG, Bitmap and WebP") + }) +} + // Runs the content of a `.mod` file to extract what data we can get // from legacy mods. // 1. Create a global function `new_mod` that stores @@ -420,6 +430,9 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result img, - Err(err) => { - let err = Report::msg(err.to_string()) - .wrap_err("Invalid image data") - .note("Supported formats are: PNG, JPEG, Bitmap and WebP") - .suggestion("Contact the mod author to fix this"); - return Err(err); - } - }; - + let img = image_data_to_buffer(buf)?; Some(img) + } else if let Some((nexus, _)) = &nexus { + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let url = nexus.picture_url.as_ref(); + let data = api + .picture(url) + .await + .wrap_err_with(|| format!("Failed to download Nexus image from '{}'", url))?; + + let img = image_data_to_buffer(&data)?; + + let name = "image.bin"; + let path = dest.join(name); + match fs::write(&path, &data).await { + Ok(_) => { + mod_cfg.image = Some(name.into()); + Some(img) + } + Err(err) => { + let err = Report::new(err).wrap_err(format!( + "Failed to write Nexus picture to file '{}'", + path.display() + )); + tracing::error!("{:?}", err); + None + } + } } else { None }; tracing::trace!(?image); - let mod_dir = state.data_dir.join(state.mod_dir.as_ref()); - let dest = mod_dir.join(&mod_cfg.id); - tracing::trace!("Creating mods directory {}", dest.display()); fs::create_dir_all(&dest) .await diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 64fdd28..e5b70c4 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -78,6 +78,7 @@ pub(crate) struct NexusInfo { pub author: String, pub summary: Arc, pub description: Arc, + pub picture_url: Arc, } impl From for NexusInfo { @@ -89,6 +90,7 @@ impl From for NexusInfo { author: value.author, summary: Arc::new(value.summary), description: Arc::new(value.description), + picture_url: Arc::new(value.picture_url.into()), } } } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 145435d..1407fca 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -4,7 +4,7 @@ use std::convert::Infallible; use lazy_static::lazy_static; use regex::Regex; use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; -use reqwest::{Client, RequestBuilder, Url}; +use reqwest::{Client, IntoUrl, RequestBuilder, Url}; use serde::Deserialize; use thiserror::Error; @@ -102,6 +102,16 @@ impl Api { self.send(req).await } + #[tracing::instrument(skip(self))] + pub async fn picture(&self, url: impl IntoUrl + std::fmt::Debug) -> Result> { + let res = self.client.get(url).send().await?.error_for_status()?; + + res.bytes() + .await + .map(|bytes| bytes.to_vec()) + .map_err(From::from) + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { From 246564d00f032ba75ec1de7a9d3377d4c65df529 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 13:54:19 +0100 Subject: [PATCH 043/184] Fetch file version from Nexus When importing an archive file downloaded from Nexus, the file name does include a version field. But, presumably for compatibility reasons, Nexus replaces special characters with `-`, so that this field doesn't match common schemes like `1.0.0`. So instead we use the also included update timestamp to find the corresponding file info from Nexus and use the version data from that. Closes #131. --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++--- lib/nexusmods/src/lib.rs | 24 +++++++++++++++++++++++ lib/nexusmods/src/types.rs | 29 ++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 131d01f..522d94a 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -381,7 +381,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result version, + Err(err) => { + let err = Report::new(err); + tracing::warn!( + "Failed to fetch version for Nexus download. \ + Falling back to file name:\n{:?}", + err + ); + version + } + }; + + let info = NexusInfo::from(mod_info); + tracing::debug!(version, ?info); + Some((info, version)) } else { None diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 145435d..0cca768 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -39,6 +39,8 @@ pub enum Error { Infallible(#[from] Infallible), #[error("invalid NXM URL '{}': {0}", .1.as_str())] InvalidNXM(&'static str, Url), + #[error("{0}")] + Custom(String), } pub type Result = std::result::Result; @@ -102,6 +104,28 @@ impl Api { self.send(req).await } + #[tracing::instrument(skip(self))] + pub async fn file_version(&self, id: u64, timestamp: T) -> Result + where + T: std::fmt::Debug, + OffsetDateTime: PartialEq, + { + let url = BASE_URL_GAME.join(&format!("mods/{id}/files.json"))?; + let req = self.client.get(url); + let files: FileList = self.send(req).await?; + + let Some(file) = files + .files + .into_iter() + .find(|file| file.updated_timestamp == timestamp) + else { + let err = Error::Custom("Timestamp does not match any file".into()); + return Err(err); + }; + + Ok(file.version) + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index b0dffd5..b88911e 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -64,6 +64,35 @@ pub struct Mod { // pub contains_adult_content: bool, } +#[derive(Debug, Deserialize)] +pub struct File { + pub id: Vec, + pub uid: u64, + pub file_id: u64, + pub name: String, + pub version: String, + pub category_id: u64, + pub category_name: String, + pub is_primary: bool, + pub size: u64, + pub file_name: String, + #[serde(with = "time::serde::timestamp")] + pub updated_timestamp: OffsetDateTime, + pub mod_version: String, + pub external_virus_scan_url: String, + pub description: String, + pub size_kb: u64, + pub size_in_bytes: u64, + pub changelog_html: String, + pub content_preview_link: String, +} + +#[derive(Debug, Deserialize)] +pub struct FileList { + pub files: Vec, + // pub file_updates: Vec, +} + #[derive(Debug, Deserialize)] pub struct DownloadLink { pub name: String, From 440d0f505bf4390c3d70c46eea3ae6f3fb821fb9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:38:40 +0100 Subject: [PATCH 044/184] Add changelog entry --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4d1861c..31910d3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ - dtmt: add utility to migrate mod projects - dtmm: reset dtkit-patch installations - sdk: implement decompiling Lua files +- dtmm: fetch file version for Nexus mods === Fixed From edd363c3a621d705776ac8b8eeb9b97920504b5d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:40:03 +0100 Subject: [PATCH 045/184] Add changelog entry --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4d1861c..ee02b6c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ - dtmt: add utility to migrate mod projects - dtmm: reset dtkit-patch installations - sdk: implement decompiling Lua files +- dtmm: fetch cover image for Nexus mods === Fixed From 316a5aaa33cc650fb8fce8e3be10b20229d586d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 16:12:11 +0100 Subject: [PATCH 046/184] ci: Fix undefined variable --- .ci/tasks/build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 5e60d7f..bb96775 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -19,7 +19,9 @@ install_artifact() { cd "repo" -if [ -n "${PR:-}" ]; then +PR=${PR:-} + +if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else From b64ff9043c661c690bf23e5e8f7c89c793bcd8cb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 16:12:41 +0100 Subject: [PATCH 047/184] ci: Create build artifacts for commits on master --- .ci/pipelines/base-pipeline.yml | 43 ------- .ci/pipelines/base.yml | 204 ++++++++++++++++++++++++++++++++ Justfile | 4 +- 3 files changed, 206 insertions(+), 45 deletions(-) delete mode 100644 .ci/pipelines/base-pipeline.yml create mode 100644 .ci/pipelines/base.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml deleted file mode 100644 index f231fb7..0000000 --- a/.ci/pipelines/base-pipeline.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - -# The base pipeline that runs continuously, checks for branches and -# creates a new pipeline instance for each of them. - -resource_types: -- name: gitea-pr - type: registry-image - source: - repository: registry.local:5000/gitea-pr - -resources: -- name: repo-pr - type: gitea-pr - source: - access_token: ((gitea_api_key)) - owner: ((owner)) - repo: ((repo)) - url: https://git.sclu1034.dev -- name: repo - type: git - source: - uri: https://git.sclu1034.dev/bitsquid_dt/dtmt - -jobs: - - name: set-pipelines - plan: - - in_parallel: - - get: repo-pr - trigger: true - - get: repo - - load_var: prs - file: repo-pr/prs.json - - across: - - var: pr - values: ((.:prs)) - set_pipeline: dtmt-pr - file: repo/.ci/pipelines/pr.yml - vars: - pr: ((.:pr)) - gitea_api_key: ((gitea_api_key)) - instance_vars: - number: ((.:pr.number)) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml new file mode 100644 index 0000000..a731222 --- /dev/null +++ b/.ci/pipelines/base.yml @@ -0,0 +1,204 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/Config +--- + +# The actual CI pipeline that is run per branch +resource_types: +- name: gitea-package + type: registry-image + source: + repository: registry.local:5000/gitea-package + +- name: gitea-status + type: registry-image + source: + repository: registry.local:5000/gitea-status + +- name: gitea-pr + type: registry-image + source: + repository: registry.local:5000/gitea-pr + + +resources: +- name: repo + type: git + source: + uri: http://forgejo:3000/bitsquid_dt/dtmt + branch: master + +- name: repo-pr + type: gitea-pr + source: + access_token: ((gitea_api_key)) + owner: ((owner)) + repo: ((repo)) + url: https://git.sclu1034.dev + +- name: gitea-package + type: gitea-package + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + type: generic + name: dtmt + + +- name: status-build-msvc + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + context: build/msvc + description: "Build for the target platform: msvc" + +- name: status-build-linux + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + context: build/linux + description: "Build for the target platform: linux" + + +jobs: +- name: set-pipelines + plan: + - in_parallel: + - get: repo-pr + trigger: true + + - get: repo + + - load_var: prs + file: repo-pr/prs.json + + - across: + - var: pr + values: ((.:prs)) + set_pipeline: dtmt-pr + file: repo/.ci/pipelines/pr.yml + vars: + pr: ((.:pr)) + gitea_api_key: ((gitea_api_key)) + instance_vars: + number: ((.:pr.number)) + + +- name: build-msvc + on_success: + put: state-success + resource: status-build-msvc + no_get: true + params: + state: success + sha: ((.:git_sha)) + + on_failure: + put: state-failure + resource: status-build-msvc + no_get: true + params: + state: failure + sha: ((.:git_sha)) + + plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + + - put: state-pending + resource: status-build-msvc + no_get: true + params: + state: pending + sha: ((.:git_sha)) + + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: msvc + gitea_url: http://forgejo:3000 + gitea_api_key: ((gitea_api_key)) + + - load_var: version_number + reveal: true + file: artifact/version + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe + - artifact/*.sha256 + +- name: build-linux + on_success: + put: state-success + resource: status-build-linux + no_get: true + params: + state: success + sha: ((.:git_sha)) + + on_failure: + put: state-failure + resource: status-build-linux + no_get: true + params: + state: failure + sha: ((.:git_sha)) + + plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + + - put: state-pending + resource: status-build-linux + no_get: true + params: + state: pending + sha: ((.:git_sha)) + + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: linux + gitea_url: http://forgejo:3000 + gitea_api_key: ((gitea_api_key)) + + - load_var: version_number + reveal: true + file: artifact/version + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe + - artifact/*.sha256 diff --git a/Justfile b/Justfile index 70af61a..12fde29 100644 --- a/Justfile +++ b/Justfile @@ -30,8 +30,8 @@ ci-image-linux: set-base-pipeline: fly -t {{fly_target}} set-pipeline \ - --pipeline dtmt-prs \ - --config .ci/pipelines/base-pipeline.yml \ + --pipeline dtmt \ + --config .ci/pipelines/base.yml \ -v gitea_api_key=${GITEA_API_KEY} \ -v owner=bitsquid_dt \ -v repo=dtmt From d95f5dfe1f26ae2c2f81d588ad358ea82cb0d405 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:51:29 +0100 Subject: [PATCH 048/184] ci: Improve caching setup for image building The `RUN --mount` flag is much easier to maintain than having to set up all the crates. --- .ci/Dockerfile.linux | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux index 9e93e44..b46f7ff 100644 --- a/.ci/Dockerfile.linux +++ b/.ci/Dockerfile.linux @@ -1,35 +1,7 @@ FROM dtmt-ci-base-linux -# Create dummy crates and copy their Cargo.toml, so that dependencies can be cached -RUN set -e; \ - cargo new --bin crates/dtmt; \ - cargo new --bin crates/dtmm; \ - cargo new --lib lib/dtmt-shared; \ - cargo new --lib lib/nexusmods; \ - cargo new --lib lib/sdk; \ - cargo new --lib lib/serde_sjson; \ - cargo new --lib lib/ansi-parser - -COPY Cargo.toml Cargo.lock /src/dtmt/ -COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ -COPY crates/dtmm/Cargo.toml /src/dtmt/crates/dtmm/ -COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ -COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ -COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ -COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ -COPY lib/ansi-parser/Cargo.toml /src/dtmt/lib/ansi-parser/ - -# Crates with build scripts cannot be split that way, but they shouldn't change too often -COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys -COPY lib/oodle /src/dtmt/lib/oodle -# color-eyre needs to be copied, too, then, as it's used by `oodle` -COPY lib/color-eyre /src/dtmt/lib/color-eyre -COPY --from=dtmt-ci-base-linux /src/*.lib /src/dtmt/lib/oodle/ - -RUN cargo build --release --locked -RUN rm -r crates lib - COPY . /src/dtmt COPY --from=dtmt-ci-base-linux /src/*.lib /src/*.so /src/dtmt/lib/oodle/ - -RUN cargo build --release --locked +RUN --mount=type=cache,id=cargo-registry,target=/cargo/registry \ + --mount=type=cache,id=cargo-target,target=/src/dtmt/target \ + cargo build --release --locked From 227dff03efa67006393614bcd0fa1a7b1fbdd3f5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:52:55 +0100 Subject: [PATCH 049/184] ci: Fix base pipeline Concourse doesn't allow variables to be undefined. --- .ci/pipelines/base.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index a731222..474c090 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -123,6 +123,7 @@ jobs: - task: build file: repo/.ci/tasks/build.yml vars: + pr: "" target: msvc gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) @@ -180,6 +181,7 @@ jobs: - task: build file: repo/.ci/tasks/build.yml vars: + pr: "" target: linux gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) From 3555fc83d227052eda16bfbb6f81aa4c7aeede97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:53:33 +0100 Subject: [PATCH 050/184] ci: Rework MSVC image building This moves process of downloading and setting up the Windows libraries to a separate build step, freeing up the layers of the final image. --- .ci/image/Dockerfile.linux | 6 ++--- .ci/image/Dockerfile.msvc | 51 ++++++++++++++++++++++++++------------ Justfile | 6 ++--- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index df10059..5cbaf5d 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -8,9 +8,9 @@ RUN set -eux; \ git \ gpg \ jq \ - libatk1.0-dev \ + libatk1.0-dev \ libclang-13-dev \ - libglib2.0-dev \ + libglib2.0-dev \ libgtk-3-dev \ libpango1.0-dev \ libssl-dev \ @@ -22,4 +22,4 @@ RUN set -eux; \ WORKDIR /src/dtmt -COPY *.so *.a /src/ +COPY lib/oodle/*.so lib/oodle/*.a /src/ diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile.msvc index a9eab62..864dc73 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile.msvc @@ -1,13 +1,40 @@ # https://jake-shadle.github.io/xwin/ -FROM dtmt-ci-base-linux +FROM debian:bullseye-slim as xwin + +ARG XWIN_VERSION=0.5.0 +ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" +ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz + +RUN set -eux; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + tar \ + ; \ + # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. + tar -xzv -f /root/$XWIN_PREFIX.tar.gz -C /usr/bin --strip-components=1 $XWIN_PREFIX/xwin; \ + apt-get remove -y --auto-remove; \ + rm -rf \ + /var/lib/apt/lists/* \ + /root/$XWIN_PREFIX.tar.gz; + +RUN set -eux; \ + # Splat the CRT and SDK files to /xwin/crt and /xwin/sdk respectively + xwin \ + --log-level debug \ + --cache-dir /root/.xwin-cache \ + --manifest-version 16 \ + --accept-license \ + splat \ + --output /xwin; \ + rm -rf \ + /root/.xwin-cache; + +FROM dtmt-ci-base-linux as final ENV KEYRINGS /usr/local/share/keyrings -ARG XWIN_VERSION=0.2.11 -ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key ADD https://dl.winehq.org/wine-builds/winehq.key /root/winehq.key -ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz RUN set -eux; \ mkdir -p $KEYRINGS; \ @@ -26,7 +53,7 @@ RUN set -eux; \ llvm-13 \ lld-13 \ winehq-staging \ - tar; \ + ; \ # ensure that clang/clang++ are callable directly ln -s clang-13 /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-13 /usr/bin/ld.lld; \ # We also need to setup symlinks ourselves for the MSVC shims because they aren't in the debian packages @@ -44,19 +71,15 @@ RUN set -eux; \ update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld 100; \ rustup target add x86_64-pc-windows-msvc; \ rustup component add rust-src; \ - # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. - tar -xzv -f /root/$XWIN_PREFIX.tar.gz -C /usr/local/cargo/bin --strip-components=1 $XWIN_PREFIX/xwin; \ - # Splat the CRT and SDK files to /xwin/crt and /xwin/sdk respectively - xwin --accept-license splat --output /xwin; \ # Remove unneeded files to reduce image size apt-get remove -y --auto-remove; \ rm -rf \ - .xwin-cache \ - /usr/local/cargo/bin/xwin \ - /root/$XWIN_PREFIX.tar.gz \ /var/lib/apt/lists/* \ /root/*.key; +COPY lib/oodle/*.lib /src +COPY --from=xwin /xwin /xwin + # Note that we're using the full target triple for each variable instead of the # simple CC/CXX/AR shorthands to avoid issues when compiling any C/C++ code for # build dependencies that need to compile and execute in the host environment @@ -83,7 +106,3 @@ ENV CFLAGS_x86_64_pc_windows_msvc="$CL_FLAGS" \ # Run wineboot just to setup the default WINEPREFIX so we don't do it every # container run RUN wine wineboot --init - -WORKDIR /src/dtmt - -COPY *.lib /src diff --git a/Justfile b/Justfile index 12fde29..967619b 100644 --- a/Justfile +++ b/Justfile @@ -18,13 +18,13 @@ build-image-linux: ci-image: ci-image-msvc ci-image-linux -ci-image-msvc: ci-image-linux - docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc .ci/image +ci-image-msvc: + docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc . docker tag dtmt-ci-base-msvc registry.sclu1034.dev/dtmt-ci-base-msvc docker push registry.sclu1034.dev/dtmt-ci-base-msvc ci-image-linux: - docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux .ci/image + docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux . docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux docker push registry.sclu1034.dev/dtmt-ci-base-linux From 4be37f6e5e89640c4bca763fb216d6a9f21753ae Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 23:23:09 +0100 Subject: [PATCH 051/184] ci: Combine Dockerfiles into multi-stage build Closes #134. --- .ci/image/{Dockerfile.msvc => Dockerfile} | 34 +++++++++++++++++++++-- .ci/image/Dockerfile.linux | 25 ----------------- Justfile | 15 +++++----- 3 files changed, 38 insertions(+), 36 deletions(-) rename .ci/image/{Dockerfile.msvc => Dockerfile} (85%) delete mode 100644 .ci/image/Dockerfile.linux diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile similarity index 85% rename from .ci/image/Dockerfile.msvc rename to .ci/image/Dockerfile index 864dc73..4e3433b 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile @@ -26,10 +26,38 @@ RUN set -eux; \ --accept-license \ splat \ --output /xwin; \ - rm -rf \ - /root/.xwin-cache; + # Even though this build step only exists temporary, to copy the + # final data out of, it still generates a cache entry on the Docker host. + # And to keep that to a minimum, we still delete the stuff we don't need. + rm -rf /root/.xwin-cache; -FROM dtmt-ci-base-linux as final +FROM rust:slim-bullseye as linux + +RUN set -eux; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + build-essential \ + curl \ + git \ + gpg \ + jq \ + libatk1.0-dev \ + libclang-13-dev \ + libglib2.0-dev \ + libgtk-3-dev \ + libpango1.0-dev \ + libssl-dev \ + libzstd-dev \ + pkg-config; \ + apt-get remove -y --auto-remove; \ + rm -rf /var/lib/apt/lists/*; \ + rustup default nightly + +WORKDIR /src/dtmt + +COPY lib/oodle/*.so lib/oodle/*.a /src/ + +FROM linux as msvc ENV KEYRINGS /usr/local/share/keyrings diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux deleted file mode 100644 index 5cbaf5d..0000000 --- a/.ci/image/Dockerfile.linux +++ /dev/null @@ -1,25 +0,0 @@ -FROM rust:slim-bullseye - -RUN set -eux; \ - apt-get update; \ - apt-get install --no-install-recommends -y \ - build-essential \ - curl \ - git \ - gpg \ - jq \ - libatk1.0-dev \ - libclang-13-dev \ - libglib2.0-dev \ - libgtk-3-dev \ - libpango1.0-dev \ - libssl-dev \ - libzstd-dev \ - pkg-config; \ - apt-get remove -y --auto-remove; \ - rm -rf /var/lib/apt/lists/*; \ - rustup default nightly - -WORKDIR /src/dtmt - -COPY lib/oodle/*.so lib/oodle/*.a /src/ diff --git a/Justfile b/Justfile index 967619b..f9b37bc 100644 --- a/Justfile +++ b/Justfile @@ -16,16 +16,15 @@ build-image-msvc: build-image-linux: docker build -f .ci/Dockerfile.linux . -ci-image: ci-image-msvc ci-image-linux - -ci-image-msvc: - docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc . +ci-image: + # The MSVC image depends on the Linux image. So by building that first, + # we actually build both, and cache them, so that "building" the + # Linux image afterwards merely needs to pull the cache. + docker build --target msvc -t dtmt-ci-base-msvc -f .ci/image/Dockerfile . + docker build --target linux -t dtmt-ci-base-linux -f .ci/image/Dockerfile . docker tag dtmt-ci-base-msvc registry.sclu1034.dev/dtmt-ci-base-msvc - docker push registry.sclu1034.dev/dtmt-ci-base-msvc - -ci-image-linux: - docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux . docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux + docker push registry.sclu1034.dev/dtmt-ci-base-msvc docker push registry.sclu1034.dev/dtmt-ci-base-linux set-base-pipeline: From b3305e87b8231a42f1e70faa29e1db9d9ef7e996 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:21:07 +0100 Subject: [PATCH 052/184] dtmm: Fix importing from `.mod` file --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 68c54a5..c5bbce7 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -244,6 +244,8 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo None }; + tracing::debug!(?legacy_mod_data); + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -276,6 +278,24 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo Ok((cfg, root)) } + } else if let Some((mod_id, resources, root)) = legacy_mod_data { + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: "A mod for the game Warhammer 40,000: Darktide".into(), + version: "N/A".into(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + + Ok((cfg, root)) } else { eyre::bail!( "Mod needs a config file or `.mod` file. \ From 61e78e97185907e4300c7bb4fd399a37e62dcc86 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:21:34 +0100 Subject: [PATCH 053/184] dtmm: Fix writing Nexus image to disk --- crates/dtmm/src/controller/import.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index c5bbce7..5ca4068 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -434,6 +434,8 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Date: Wed, 29 Nov 2023 15:21:07 +0100 Subject: [PATCH 054/184] dtmm: Fix importing from `.mod` file --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 522d94a..68f2b05 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -234,6 +234,8 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo None }; + tracing::debug!(?legacy_mod_data); + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -266,6 +268,24 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo Ok((cfg, root)) } + } else if let Some((mod_id, resources, root)) = legacy_mod_data { + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: "A mod for the game Warhammer 40,000: Darktide".into(), + version: "N/A".into(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + + Ok((cfg, root)) } else { eyre::bail!( "Mod needs a config file or `.mod` file. \ From a0fe5d3f816396d586f443dfc5e881f6e374ee9d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:37:37 +0100 Subject: [PATCH 055/184] nexusmods: Fix File type --- lib/nexusmods/src/lib.rs | 2 +- lib/nexusmods/src/types.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 0cca768..06c1f01 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -117,7 +117,7 @@ impl Api { let Some(file) = files .files .into_iter() - .find(|file| file.updated_timestamp == timestamp) + .find(|file| file.uploaded_timestamp == timestamp) else { let err = Error::Custom("Timestamp does not match any file".into()); return Err(err); diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index b88911e..db0f624 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -77,13 +77,13 @@ pub struct File { pub size: u64, pub file_name: String, #[serde(with = "time::serde::timestamp")] - pub updated_timestamp: OffsetDateTime, + pub uploaded_timestamp: OffsetDateTime, pub mod_version: String, pub external_virus_scan_url: String, pub description: String, pub size_kb: u64, pub size_in_bytes: u64, - pub changelog_html: String, + pub changelog_html: Option, pub content_preview_link: String, } From 6f848bb837c028e090278b605ed6be0aba54149f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:32:39 +0100 Subject: [PATCH 056/184] dtmm: Implement NXM URI handler Closes #31. --- CHANGELOG.adoc | 1 + Cargo.lock | 30 ++++++++ crates/dtmm/Cargo.toml | 34 +++++---- crates/dtmm/src/controller/import.rs | 29 +++++++- crates/dtmm/src/controller/worker.rs | 26 ++++++- crates/dtmm/src/main.rs | 105 +++++++++++++++++++++++++-- crates/dtmm/src/state/delegate.rs | 19 +++++ crates/dtmm/src/util/log.rs | 16 ++-- lib/nexusmods/src/lib.rs | 34 ++++++--- 9 files changed, 252 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1fd318f..358bc33 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -18,6 +18,7 @@ - sdk: implement decompiling Lua files - dtmm: fetch cover image for Nexus mods - dtmm: fetch file version for Nexus mods +- dtmm: handle `nxm://` URIs via IPC and import the corresponding mod === Fixed diff --git a/Cargo.lock b/Cargo.lock index 8385e67..1817853 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.64.0" @@ -895,6 +904,7 @@ version = "0.1.0" dependencies = [ "ansi-parser", "async-recursion", + "bincode", "bitflags 1.3.2", "clap", "color-eyre", @@ -904,6 +914,7 @@ dependencies = [ "druid-widget-nursery", "dtmt-shared", "futures", + "interprocess", "lazy_static", "luajit2-sys", "minijinja", @@ -1833,6 +1844,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "cfg-if", + "libc", + "rustc_version", + "to_method", + "winapi", +] + [[package]] name = "intl-memoizer" version = "0.5.1" @@ -3517,6 +3541,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + [[package]] name = "tokio" version = "1.33.0" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 5f60220..c159295 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -6,33 +6,35 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ansi-parser = "0.9.0" +async-recursion = "1.0.5" +bincode = "1.3.3" bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" +colors-transform = "0.2.11" confy = "0.5.1" druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } +druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -oodle = { path = "../../lib/oodle", version = "*" } -sdk = { path = "../../lib/sdk", version = "*" } +interprocess = { version = "1.2.1", default-features = false } +lazy_static = "1.4.0" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +minijinja = "1.0.10" nexusmods = { path = "../../lib/nexusmods", version = "*" } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } +oodle = { path = "../../lib/oodle", version = "*" } +path-slash = "0.2.1" +sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } +serde_sjson = { path = "../../lib/serde_sjson", version = "*" } +string_template = "0.2.1" +strip-ansi-escapes = "0.1.1" +time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } +tokio-stream = { version = "0.1.12", features = ["fs"] } tracing = "0.1.37" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -zip = "0.6.4" -tokio-stream = { version = "0.1.12", features = ["fs"] } -path-slash = "0.2.1" -time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } -strip-ansi-escapes = "0.1.1" -lazy_static = "1.4.0" -colors-transform = "0.2.11" usvg = "0.25.0" -druid-widget-nursery = "0.1" -ansi-parser = "0.9.0" -string_template = "0.2.1" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -async-recursion = "1.0.5" -minijinja = "1.0.10" +zip = "0.6.4" diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index cef7ee4..0a7a6ce 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -405,11 +405,10 @@ fn extract_legacy_mod( } #[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { +pub(crate) async fn import_from_file(state: ActionState, info: FileInfo) -> Result { let data = fs::read(&info.path) .await .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; - let data = Cursor::new(data); let nexus = if let Some((_, id, version, timestamp)) = info .path @@ -450,6 +449,32 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result { + let url = uri + .parse() + .wrap_err_with(|| format!("Invalid Uri '{}'", uri))?; + + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let (mod_info, file_info, data) = api + .handle_nxm(url) + .await + .wrap_err_with(|| format!("Failed to download mod from NXM uri '{}'", uri))?; + + let nexus = NexusInfo::from(mod_info); + import_mod(state, Some((nexus, file_info.version)), data).await +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn import_mod( + state: ActionState, + nexus: Option<(NexusInfo, String)>, + data: Vec, +) -> Result { + let data = Cursor::new(data); let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; if tracing::enabled!(tracing::Level::DEBUG) { diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 152030f..6ee498f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -15,7 +15,7 @@ use tokio::sync::RwLock; use crate::controller::app::*; use crate::controller::deploy::deploy_mods; use crate::controller::game::*; -use crate::controller::import::import_mod; +use crate::controller::import::*; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; @@ -57,7 +57,7 @@ async fn handle_action( .expect("failed to send command"); }), AsyncAction::AddMod(state, info) => tokio::spawn(async move { - match import_mod(state, info) + match import_from_file(state, info) .await .wrap_err("Failed to import mod") { @@ -186,6 +186,28 @@ async fn handle_action( let _ = f.write_all(&line).await; } }), + AsyncAction::NxmDownload(state, uri) => tokio::spawn(async move { + match import_from_nxm(state, uri) + .await + .wrap_err("Failed to handle NXM URI") + { + Ok(mod_info) => { + event_sink + .write() + .await + .submit_command( + ACTION_FINISH_ADD_MOD, + SingleUse::new(Arc::new(mod_info)), + Target::Auto, + ) + .expect("failed to send command"); + } + Err(err) => { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; + } + } + }), }; } } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 8069e6d..b9c1daf 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -10,12 +10,13 @@ use std::sync::Arc; use clap::parser::ValueSource; use clap::{command, value_parser, Arg}; use color_eyre::eyre::{self, Context}; -use color_eyre::{Report, Result}; +use color_eyre::{Report, Result, Section}; use druid::AppLauncher; +use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use tokio::sync::RwLock; use crate::controller::worker::work_thread; -use crate::state::AsyncAction; +use crate::state::{AsyncAction, ACTION_HANDLE_NXM}; use crate::state::{Delegate, State}; use crate::ui::theme; use crate::util::log::LogLevel; @@ -29,6 +30,37 @@ mod util { } mod ui; +// As explained in https://docs.rs/interprocess/latest/interprocess/local_socket/enum.NameTypeSupport.html +// namespaces are supported on both platforms we care about: Windows and Linux. +const IPC_ADDRESS: &str = "@dtmm.sock"; + +#[tracing::instrument] +fn notify_nxm_download( + uri: impl AsRef + std::fmt::Debug, + level: Option, +) -> Result<()> { + util::log::create_tracing_subscriber(level, None); + + tracing::debug!("Received Uri '{}', sending to main process.", uri.as_ref()); + + let mut stream = LocalSocketStream::connect(IPC_ADDRESS) + .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) + .suggestion("Make sure the main window is open.")?; + + tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); + + bincode::serialize_into(&mut stream, uri.as_ref()).wrap_err("Failed to send URI")?; + + // We don't really care what the message is, we just need an acknowledgement. + let _: String = bincode::deserialize_from(&mut stream).wrap_err("Failed to receive reply")?; + + tracing::info!( + "Notified DTMM with uri '{}'. Check the main window.", + uri.as_ref() + ); + Ok(()) +} + #[tracing::instrument] fn main() -> Result<()> { color_eyre::install()?; @@ -53,15 +85,25 @@ fn main() -> Result<()> { .value_parser(value_parser!(LogLevel)) .default_value("info"), ) + .arg( + Arg::new("nxm") + .help("An `nxm://` URI to download") + .required(false), + ) .get_matches(); - let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); let level = if matches.value_source("log-level") == Some(ValueSource::DefaultValue) { None } else { matches.get_one::("log-level").cloned() }; - util::log::create_tracing_subscriber(log_tx, level); + + if let Some(uri) = matches.get_one::("nxm") { + return notify_nxm_download(uri, level).wrap_err("Failed to send NXM Uri to main window."); + } + + let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); + util::log::create_tracing_subscriber(level, Some(log_tx)); let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); @@ -84,6 +126,59 @@ fn main() -> Result<()> { let event_sink = launcher.get_external_handle(); + { + let span = tracing::info_span!(IPC_ADDRESS, "nxm-socket"); + let _guard = span.enter(); + + let event_sink = event_sink.clone(); + let server = + LocalSocketListener::bind(IPC_ADDRESS).wrap_err("Failed to create IPC listener")?; + + tracing::debug!("IPC server listening on '{}'", IPC_ADDRESS); + + // Drop the guard here, so that we can re-enter the same span in the thread. + drop(_guard); + + std::thread::Builder::new() + .name("nxm-socket".into()) + .spawn(move || { + let _guard = span.enter(); + + loop { + let res = server.accept().wrap_err_with(|| { + format!("IPC server failed to listen on '{}'", IPC_ADDRESS) + }); + + match res { + Ok(mut stream) => { + let res = bincode::deserialize_from(&mut stream) + .wrap_err("Failed to read message") + .and_then(|uri: String| { + tracing::trace!(uri, "Received NXM uri"); + + event_sink + .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) + .wrap_err("Failed to start NXM download") + }); + match res { + Ok(()) => { + let _ = bincode::serialize_into(&mut stream, "Ok"); + } + Err(err) => { + tracing::error!("{:?}", err); + let _ = bincode::serialize_into(&mut stream, "Error"); + } + } + } + Err(err) => { + tracing::error!("Failed to receive client connection: {:?}", err) + } + } + } + }) + .wrap_err("Failed to create thread")?; + } + std::thread::Builder::new() .name("work-thread".into()) .spawn(move || { @@ -97,7 +192,7 @@ fn main() -> Result<()> { } } }) - .wrap_err("Work thread panicked")?; + .wrap_err("Failed to create thread")?; launcher.launch(State::new()).map_err(Report::new) } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 47d56e8..e30b878 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -32,6 +32,7 @@ pub(crate) const ACTION_START_RESET_DEPLOYMENT: Selector = pub(crate) const ACTION_FINISH_RESET_DEPLOYMENT: Selector = Selector::new("dtmm.action.finish-reset-deployment"); +pub(crate) const ACTION_HANDLE_NXM: Selector = Selector::new("dtmm.action.handle-nxm"); pub(crate) const ACTION_ADD_MOD: Selector = Selector::new("dtmm.action.add-mod"); pub(crate) const ACTION_FINISH_ADD_MOD: Selector>> = Selector::new("dtmm.action.finish-add-mod"); @@ -97,6 +98,7 @@ pub(crate) enum AsyncAction { CheckUpdates(ActionState), LoadInitial((PathBuf, bool)), Log((ActionState, Vec)), + NxmDownload(ActionState, String), } impl std::fmt::Debug for AsyncAction { @@ -116,6 +118,9 @@ impl std::fmt::Debug for AsyncAction { path, is_default ), AsyncAction::Log(_) => write!(f, "AsyncAction::Log(_)"), + AsyncAction::NxmDownload(_, uri) => { + write!(f, "AsyncAction::NxmDownload(_state, {})", uri) + } } } } @@ -250,6 +255,20 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_HANDLE_NXM) => { + let uri = cmd + .get(ACTION_HANDLE_NXM) + .expect("command type match but didn't contain the expected value"); + + if self + .sender + .send(AsyncAction::NxmDownload(state.clone().into(), uri.clone())) + .is_err() + { + tracing::error!("Failed to queue action to download NXM mod"); + } + Handled::Yes + } cmd if cmd.is(ACTION_ADD_MOD) => { let info = cmd .get(ACTION_ADD_MOD) diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index fa4a643..4b7c15a 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -8,7 +8,7 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; -#[derive(Clone, Copy, ValueEnum)] +#[derive(Clone, Copy, Debug, ValueEnum)] pub enum LogLevel { Trace, Debug, @@ -55,7 +55,7 @@ impl std::io::Write for ChannelWriter { } } -pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option) { +pub fn create_tracing_subscriber(level: Option, tx: Option>>) { let mut env_layer = if let Some(level) = level { EnvFilter::from(level) } else if cfg!(debug_assertions) { @@ -78,11 +78,13 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option Result { + let url = BASE_URL_GAME.join(&format!("mods/{mod_id}/files/{file_id}.json"))?; + let req = self.client.get(url); + self.send(req).await + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { @@ -174,7 +181,7 @@ impl Api { self.send(req).await } - pub async fn handle_nxm(&self, url: Url) -> Result<(Mod, Vec)> { + pub async fn handle_nxm(&self, url: Url) -> Result<(Mod, File, Vec)> { let nxm = Self::parse_nxm(url.clone())?; let user = self.user_validate().await?; @@ -183,8 +190,9 @@ impl Api { return Err(Error::InvalidNXM("user_id mismtach", url)); } - let (mod_data, download_info) = futures::try_join!( + let (mod_data, file_info, download_info) = futures::try_join!( self.mods_id(nxm.mod_id), + self.get_file_by_id(nxm.mod_id, nxm.file_id), self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) )?; @@ -195,7 +203,7 @@ impl Api { let req = self.client.get(download_url); let data = req.send().await?.bytes().await?; - Ok((mod_data, data.to_vec())) + Ok((mod_data, file_info, data.to_vec())) } pub fn parse_nxm(nxm: Url) -> Result { @@ -204,17 +212,20 @@ impl Api { } // Now it makes sense, why Nexus calls this field `game_domain_name`, when it's just - // another path segmentin the regular API calls. + // another path segment in the regular API calls. if nxm.host_str() != Some(GAME_ID) { return Err(Error::InvalidNXM("Invalid game domain name", nxm)); } let Some(mut segments) = nxm.path_segments() else { - return Err(Error::InvalidNXM("Cannot be a base", nxm)); + return Err(Error::InvalidNXM("Missing path segments", nxm)); }; if segments.next() != Some("mods") { - return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + return Err(Error::InvalidNXM( + "Unexpected path segment, expected 'mods'", + nxm, + )); } let Some(mod_id) = segments.next().and_then(|id| id.parse().ok()) else { @@ -222,7 +233,10 @@ impl Api { }; if segments.next() != Some("files") { - return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + return Err(Error::InvalidNXM( + "Unexpected path segment, expected 'files'", + nxm, + )); } let Some(file_id) = segments.next().and_then(|id| id.parse().ok()) else { @@ -237,7 +251,7 @@ impl Api { } let Some(key) = query.get("key") else { - return Err(Error::InvalidNXM("Missing 'key'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'key'", nxm)); }; let expires = query @@ -245,12 +259,12 @@ impl Api { .and_then(|expires| expires.parse().ok()) .and_then(|expires| OffsetDateTime::from_unix_timestamp(expires).ok()); let Some(expires) = expires else { - return Err(Error::InvalidNXM("Missing 'expires'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'expires'", nxm)); }; let user_id = query.get("user_id").and_then(|id| id.parse().ok()); let Some(user_id) = user_id else { - return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'user_id'", nxm)); }; Ok(Nxm { From 031c03480dd6661ae705a1a7a8936655489f55d8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 30 Nov 2023 17:50:31 +0100 Subject: [PATCH 057/184] dtmm: Add .desktop file A basic Desktop Entry file for the Linux, which includes the configuration for the `nxm://` scheme handling. --- crates/dtmm/assets/dtmm.desktop | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 crates/dtmm/assets/dtmm.desktop diff --git a/crates/dtmm/assets/dtmm.desktop b/crates/dtmm/assets/dtmm.desktop new file mode 100644 index 0000000..4c2e0a9 --- /dev/null +++ b/crates/dtmm/assets/dtmm.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=DTMM +GenericName=Mod Manager +Comment=A graphical mod manager for Warhammer 40,000: Darktide +Exec=dtmm %u +Type=Application +Keywords=Mod; +StartupNotify=true +Categories=Utility; +MimeType=x-scheme-handler/nxm; From 4c6ad1aaed7472e71a39e4d4173dc1013b2480a1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 30 Nov 2023 18:11:53 +0100 Subject: [PATCH 058/184] Reduce debug verbosity Prevent binary buffers inflating the log output. --- crates/dtmm/src/controller/import.rs | 2 +- crates/dtmm/src/state/data.rs | 35 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 0a7a6ce..27aebdf 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -468,7 +468,7 @@ pub(crate) async fn import_from_nxm(state: ActionState, uri: String) -> Result, diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index e5b70c4..6b5a706 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -95,7 +95,7 @@ impl From for NexusInfo { } } -#[derive(Clone, Data, Debug, Lens)] +#[derive(Clone, Data, Lens)] pub(crate) struct ModInfo { pub id: String, pub name: String, @@ -118,6 +118,39 @@ pub(crate) struct ModInfo { pub nexus: Option, } +impl std::fmt::Debug for ModInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ModInfo") + .field("id", &self.id) + .field("name", &self.name) + .field("summary", &self.summary) + .field( + "description", + &(match &self.description { + Some(desc) => format!("Some(String[0..{}])", desc.len()), + None => "None".to_string(), + }), + ) + .field("categories", &self.categories) + .field("author", &self.author) + .field( + "image", + &(match &self.image { + Some(image) => format!("Some(ImageBuf[{}x{}])", image.width(), image.height()), + None => "None".to_string(), + }), + ) + .field("version", &self.version) + .field("enabled", &self.enabled) + .field("packages", &format!("Vec[0..{}]", self.packages.len())) + .field("resources", &self.resources) + .field("depends", &self.depends) + .field("bundled", &self.bundled) + .field("nexus", &self.nexus) + .finish() + } +} + impl ModInfo { pub fn new( cfg: ModConfig, From 5278041ddbb83127f44e896cab95b8841eda4067 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 4 Dec 2023 13:40:00 +0100 Subject: [PATCH 059/184] Use Nexus mod name when available Fixes #154. --- crates/dtmm/src/ui/window/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index baa8e22..ad808cb 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -307,13 +307,11 @@ fn build_mod_details_info() -> impl Widget { // Force the label to take up the entire details' pane width, // so that we can center-align it. .expand_width() - .lens(ModInfo::name.in_arc()); + .lens(NexusInfoLens::new(NexusInfo::name, ModInfo::name).in_arc()); let summary = Label::raw() .with_line_break_mode(LineBreaking::WordWrap) .lens(NexusInfoLens::new(NexusInfo::summary, ModInfo::summary).in_arc()); - // TODO: Image/icon? - let version_line = Label::dynamic(|info: &Arc, _| { let author = info .nexus @@ -366,8 +364,6 @@ fn build_mod_details_info() -> impl Widget { .must_fill_main_axis(true) .cross_axis_alignment(CrossAxisAlignment::Start) .with_child(image) - // .with_spacer(4.) - // .with_flex_child(details, 1.) .with_child(details) }, Flex::column, From 57771617ffb7ffbb137025aa95cc249198f817d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 4 Dec 2023 16:47:09 +0100 Subject: [PATCH 060/184] dtmm: Add link to open mod on Nexus Closes #157. --- CHANGELOG.adoc | 1 + Cargo.lock | 37 ++++++++++++++++++++++++++++++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/state/delegate.rs | 16 +++++++++++++ crates/dtmm/src/ui/theme/colors.rs | 1 + crates/dtmm/src/ui/window/main.rs | 31 ++++++++++++++++++++++--- 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 358bc33..db30865 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -19,6 +19,7 @@ - dtmm: fetch cover image for Nexus mods - dtmm: fetch file version for Nexus mods - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod +- dtmm: Add button to open mod on nexusmods.com === Fixed diff --git a/Cargo.lock b/Cargo.lock index 1817853..93568af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,6 +920,7 @@ dependencies = [ "minijinja", "nexusmods", "oodle", + "open", "path-slash", "sdk", "serde", @@ -1882,6 +1883,25 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itoa" version = "1.0.9" @@ -2340,6 +2360,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "open" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.59" @@ -2478,6 +2509,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c159295..05dc160 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -24,6 +24,7 @@ luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } minijinja = "1.0.10" nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } +open = "5.0.1" path-slash = "0.2.1" sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index e30b878..f3c4711 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -61,6 +61,8 @@ pub(crate) type InitialLoadResult = (Config, Vector>); pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector>> = Selector::new("dtmm.action.finish-load-initial"); +pub(crate) const ACTION_OPEN_LINK: Selector> = Selector::new("dtmm.action.open-link"); + // A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are // `Send + Sync` pub(crate) struct ActionState { @@ -438,6 +440,20 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_OPEN_LINK) => { + let url = cmd + .get(ACTION_OPEN_LINK) + .expect("command type matched but didn't contain the expected value"); + + if let Err(err) = open::that_detached(Arc::as_ref(url)) { + tracing::error!( + "{:?}", + Report::new(err).wrap_err(format!("Failed to open url '{}'", url)) + ); + } + + Handled::Yes + } _ => Handled::No, } } diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 078dab0..2c2bf78 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -17,6 +17,7 @@ macro_rules! make_color { } make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1); +make_color!(LINK_COLOR, COLOR_ACCENT); #[allow(dead_code)] pub mod gruvbox_dark { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index baa8e22..4116932 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; +use druid::text::RichTextBuilder; use druid::widget::{ Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, @@ -16,9 +17,10 @@ use druid_widget_nursery::WidgetExt as _; use lazy_static::lazy_static; use crate::state::{ - ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, - ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, - ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, + ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_OPEN_LINK, + ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, + ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, + ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; use crate::ui::widget::border::Border; @@ -345,6 +347,28 @@ fn build_mod_details_info() -> impl Widget { } }); + let nexus_link = Maybe::or_empty(|| { + let link = Label::raw().lens(NexusInfo::id.map( + |id| { + let url = format!("https://nexusmods.com/warhammer40kdarktide/mods/{}", id); + let mut builder = RichTextBuilder::new(); + builder + .push("Open on Nexusmods") + .underline(true) + .text_color(theme::LINK_COLOR) + .link(ACTION_OPEN_LINK.with(Arc::new(url))); + builder.build() + }, + |_, _| {}, + )); + Flex::column() + .cross_axis_alignment(CrossAxisAlignment::Start) + .main_axis_alignment(MainAxisAlignment::Start) + .with_child(link) + .with_spacer(4.) + }) + .lens(ModInfo::nexus.in_arc()); + let details = Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::Start) @@ -352,6 +376,7 @@ fn build_mod_details_info() -> impl Widget { .with_spacer(4.) .with_child(summary) .with_spacer(4.) + .with_child(nexus_link) .with_child(version_line) .with_spacer(4.) .with_child(categories) From 0fb10d9d69387a3fcb539fad5a3a68ef99c7fa20 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 10:11:51 +0100 Subject: [PATCH 061/184] dtmm: Enforce skipping packages for non-bundled mods Fixes #161. --- crates/dtmm/src/controller/import.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 27aebdf..8e47d23 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -269,6 +269,10 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo cfg.resources = resources; + // Enforce that packages are skipped + cfg.bundled = false; + cfg.packages = vec![]; + Ok((cfg, root)) } else { let root = name From a0791cba412942c4d68d4c356b0359441e0d4912 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 10:40:29 +0100 Subject: [PATCH 062/184] dtmm: Extend NexusInfo This provides forward compatibility, in case I ever want to use those fields. If I only added them at the time when they are needed, I would need to come up with a process to load the `nexus.sjson` with missing fields. Closes #130. --- crates/dtmm/src/state/data.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 6b5a706..23a4ae0 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -72,25 +72,35 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { + pub author: String, + pub category_id: u64, + pub created_timestamp: i64, + pub description: Arc, pub id: u64, pub name: String, - pub version: String, - pub author: String, - pub summary: Arc, - pub description: Arc, pub picture_url: Arc, + pub summary: Arc, + pub uid: u64, + pub updated_timestamp: i64, + pub uploaded_by: String, + pub version: String, } impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { + author: value.author, + category_id: value.category_id, + created_timestamp: value.created_timestamp.unix_timestamp(), + description: Arc::new(value.description), id: value.mod_id, name: value.name, - version: value.version, - author: value.author, - summary: Arc::new(value.summary), - description: Arc::new(value.description), picture_url: Arc::new(value.picture_url.into()), + summary: Arc::new(value.summary), + uid: value.uid, + updated_timestamp: value.updated_timestamp.unix_timestamp(), + uploaded_by: value.uploaded_by, + version: value.version, } } } @@ -106,14 +116,14 @@ pub(crate) struct ModInfo { pub image: Option, pub version: String, pub enabled: bool, + pub depends: Vector, + pub bundled: bool, #[lens(ignore)] #[data(ignore)] pub packages: Vector>, #[lens(ignore)] #[data(ignore)] pub resources: ModResourceInfo, - pub depends: Vector, - pub bundled: bool, #[data(ignore)] pub nexus: Option, } From 103775e03290d81fcc505a8116e6eb5a6a5a721c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 14:28:12 +0100 Subject: [PATCH 063/184] dtmm: Replace icon for mod update notification Closes #158. --- crates/dtmm/assets/tabler-icons/cloud-download.svg | 8 ++++++++ crates/dtmm/src/ui/theme/icons.rs | 2 +- crates/dtmm/src/ui/window/main.rs | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 crates/dtmm/assets/tabler-icons/cloud-download.svg diff --git a/crates/dtmm/assets/tabler-icons/cloud-download.svg b/crates/dtmm/assets/tabler-icons/cloud-download.svg new file mode 100644 index 0000000..5b62734 --- /dev/null +++ b/crates/dtmm/assets/tabler-icons/cloud-download.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/crates/dtmm/src/ui/theme/icons.rs b/crates/dtmm/src/ui/theme/icons.rs index 8d82fc1..50ecbe3 100644 --- a/crates/dtmm/src/ui/theme/icons.rs +++ b/crates/dtmm/src/ui/theme/icons.rs @@ -4,7 +4,7 @@ use usvg::{ }; pub static ALERT_CIRCLE: &str = include_str!("../../../assets/tabler-icons/alert-circle.svg"); -pub static ALERT_TRIANGLE: &str = include_str!("../../../assets/tabler-icons/alert-triangle.svg"); +pub static CLOUD_DOWNLOAD: &str = include_str!("../../../assets/tabler-icons/cloud-download.svg"); pub fn parse_svg(svg: &str) -> Result { let opt = Options::default(); diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index e3ca4b0..022a780 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -22,7 +22,7 @@ use crate::state::{ ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; -use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; +use crate::ui::theme::{self, ColorExt, COLOR_GREEN_LIGHT}; use crate::ui::widget::border::Border; use crate::ui::widget::button::Button; use crate::ui::widget::controller::{ @@ -148,9 +148,9 @@ fn build_mod_list() -> impl Widget { let version = { let icon = { let tree = - theme::icons::parse_svg(theme::icons::ALERT_TRIANGLE).expect("invalid SVG"); + theme::icons::parse_svg(theme::icons::CLOUD_DOWNLOAD).expect("invalid SVG"); - let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); + let tree = theme::icons::recolor_icon(tree, true, COLOR_GREEN_LIGHT); Svg::new(tree).fix_height(druid::theme::TEXT_SIZE_NORMAL) }; From 4ad30a8a12425bd73d59955b6ec5eebc3d704e4c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 17:44:44 +0200 Subject: [PATCH 064/184] Update color-eyre We no longer need to patch `ansi-parser`. --- Cargo.lock | 2 ++ Cargo.toml | 1 - lib/color-eyre | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93568af..0faafbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,8 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad5bd94a775101bd68c2de2bb28ca2eccd69f395ae3aec4ac4f6da3c1cd2c6a" dependencies = [ "heapless", "nom", diff --git a/Cargo.toml b/Cargo.toml index 451cd09..4bab0cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ [patch.crates-io] color-eyre = { path = "lib/color-eyre" } -ansi-parser = { path = "lib/ansi-parser" } [profile.dev.package.backtrace] opt-level = 3 diff --git a/lib/color-eyre b/lib/color-eyre index 0fa05eb..8a3d454 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 0fa05eba9954be223b06468c8760b97e660f9941 +Subproject commit 8a3d4544046f956e8e28c906553dc54d325368c1 From cfee6d91214b0926b4ab07e1773e0131ee2c3852 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 19:15:45 +0200 Subject: [PATCH 065/184] Fix clippy lints --- Cargo.toml | 1 + crates/dtmm/src/main.rs | 1 - crates/dtmm/src/state/lens.rs | 1 + crates/dtmm/src/ui/theme/colors.rs | 13 ------------- crates/dtmm/src/ui/widget/mod.rs | 5 ----- lib/color-eyre | 2 +- 6 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4bab0cd..11831e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "lib/serde_sjson", "lib/luajit2-sys", ] +exclude = ["lib/color-eyre"] [patch.crates-io] color-eyre = { path = "lib/color-eyre" } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index b9c1daf..aa223f0 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,6 +1,5 @@ #![recursion_limit = "256"] #![feature(let_chains)] -#![feature(arc_unwrap_or_clone)] #![feature(iterator_try_collect)] #![windows_subsystem = "windows"] diff --git a/crates/dtmm/src/state/lens.rs b/crates/dtmm/src/state/lens.rs index c26c298..983cb2e 100644 --- a/crates/dtmm/src/state/lens.rs +++ b/crates/dtmm/src/state/lens.rs @@ -42,6 +42,7 @@ impl Lens>> for SelectedModLens { /// A Lens that maps an `im::Vector` to `im::Vector<(usize, T)>`, /// where each element in the destination vector includes its index in the /// source vector. +#[allow(dead_code)] pub(crate) struct IndexedVectorLens; impl Lens, Vector<(usize, T)>> for IndexedVectorLens { diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 2c2bf78..1051539 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -69,23 +69,10 @@ pub mod gruvbox_dark { } pub trait ColorExt { - fn lighten(&self, fac: f32) -> Self; fn darken(&self, fac: f32) -> Self; } impl ColorExt for Color { - fn lighten(&self, fac: f32) -> Self { - let (r, g, b, a) = self.as_rgba(); - let rgb = Rgb::from(r as f32, g as f32, b as f32); - let rgb = rgb.lighten(fac); - Self::rgba( - rgb.get_red() as f64, - rgb.get_green() as f64, - rgb.get_blue() as f64, - a, - ) - } - fn darken(&self, fac: f32) -> Self { let (r, g, b, a) = self.as_rgba(); let rgb = Rgb::from(r as f32, g as f32, b as f32); diff --git a/crates/dtmm/src/ui/widget/mod.rs b/crates/dtmm/src/ui/widget/mod.rs index 05c91c7..06ccedd 100644 --- a/crates/dtmm/src/ui/widget/mod.rs +++ b/crates/dtmm/src/ui/widget/mod.rs @@ -2,16 +2,11 @@ use std::path::PathBuf; use std::sync::Arc; use druid::text::Formatter; -use druid::{Data, Widget}; pub mod border; pub mod button; pub mod controller; -pub trait ExtraWidgetExt: Widget + Sized + 'static {} - -impl + 'static> ExtraWidgetExt for W {} - pub(crate) struct PathBufFormatter; impl PathBufFormatter { diff --git a/lib/color-eyre b/lib/color-eyre index 8a3d454..b40962a 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 8a3d4544046f956e8e28c906553dc54d325368c1 +Subproject commit b40962a61c748756d7da293d9fff26aca019603e From 86ed5c327fc20e1291421ee5a8abfabf1e694752 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 12 May 2024 20:57:09 +0200 Subject: [PATCH 066/184] Update crates `steamlocate` changed its API again. `shlex` deprecated `quote`, but that will be addressed later. --- Cargo.lock | 1055 ++++++++++++++------------ crates/dtmm/src/controller/app.rs | 14 +- crates/dtmm/src/controller/deploy.rs | 8 + lib/dtmt-shared/src/lib.rs | 37 +- 4 files changed, 608 insertions(+), 506 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0faafbc..4da0fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -58,57 +58,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arrayref" @@ -148,13 +149,13 @@ checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -183,15 +184,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -210,9 +211,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -259,9 +260,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -287,34 +288,23 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "bstr" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" -dependencies = [ - "memchr", - "regex-automata 0.4.3", - "serde", -] - [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -324,9 +314,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2" @@ -376,12 +366,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -395,9 +386,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -421,20 +412,20 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.3", ] [[package]] name = "clap" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -442,9 +433,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -456,21 +447,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cli-table" @@ -550,9 +541,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" dependencies = [ "once_cell", "owo-colors", @@ -568,9 +559,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colors-transform" @@ -608,9 +599,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -618,9 +609,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" @@ -637,9 +628,9 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -660,40 +651,51 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] -name = "crc32fast" -version = "1.3.2" +name = "crc" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -707,11 +709,10 @@ dependencies = [ [[package]] name = "csv-async" -version = "1.2.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71933d3f2d0481d5111cb2817b15b6961961458ec58adf8008194e6c850046f4" +checksum = "d37fe5b0d07f4a8260ce1e9a81413e88f459af0f2dfc55c15e96868a2f99c0f0" dependencies = [ - "bstr", "cfg-if", "csv-core", "futures", @@ -739,9 +740,9 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -764,16 +765,16 @@ version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] name = "dirs" -version = "3.0.2" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -797,6 +798,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -816,7 +829,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -953,7 +966,7 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading", + "libloading 0.7.4", "luajit2-sys", "nanorand", "notify", @@ -1004,15 +1017,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1031,12 +1044,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1051,9 +1064,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1070,9 +1083,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fd-lock" @@ -1087,9 +1100,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -1100,27 +1113,27 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.0", + "memoffset 0.9.1", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -1134,16 +1147,16 @@ checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" [[package]] name = "fluent-bundle" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" dependencies = [ "fluent-langneg", "fluent-syntax", "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -1159,9 +1172,9 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ "thiserror", ] @@ -1174,11 +1187,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" dependencies = [ - "roxmltree 0.18.1", + "roxmltree 0.19.0", ] [[package]] @@ -1210,9 +1223,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1234,9 +1247,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1249,9 +1262,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1259,15 +1272,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1276,38 +1289,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1410,9 +1423,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1431,9 +1444,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio" @@ -1497,7 +1510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" dependencies = [ "anyhow", - "heck", + "heck 0.4.1", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -1589,9 +1602,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1599,7 +1612,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -1617,15 +1630,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapless" @@ -1646,10 +1653,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "hermit-abi" -version = "0.3.3" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hmac" @@ -1662,18 +1675,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1682,9 +1695,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1705,9 +1718,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1720,7 +1733,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1742,9 +1755,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1767,15 +1780,14 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "jpeg-decoder", - "num-rational", "num-traits", "png", ] @@ -1788,22 +1800,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown", ] [[package]] @@ -1862,9 +1864,9 @@ dependencies = [ [[package]] name = "intl-memoizer" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" dependencies = [ "type-map", "unic-langid", @@ -1905,31 +1907,37 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.9" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1945,9 +1953,9 @@ dependencies = [ [[package]] name = "keyvalues-parser" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d990301996c856ea07a84bc291e76f1273db52683663efc05c8d355976897e5" +checksum = "7e4c8354918309196302015ac9cae43362f1a13d0d5c5539a33b4c2fd2cd6d25" dependencies = [ "pest", "pest_derive", @@ -1956,14 +1964,11 @@ dependencies = [ [[package]] name = "keyvalues-serde" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f" +checksum = "0447866c47c00f8bd1949618e8f63017cf93e985b4684dc28d784527e2882390" dependencies = [ "keyvalues-parser", - "once_cell", - "paste", - "regex", "serde", "thiserror", ] @@ -2021,9 +2026,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" @@ -2036,27 +2041,36 @@ dependencies = [ ] [[package]] -name = "libredox" -version = "0.0.1" +name = "libloading" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ - "bitflags 2.4.1", + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", ] [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "luajit2-sys" @@ -2094,9 +2108,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -2118,9 +2132,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -2133,9 +2147,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minijinja" -version = "1.0.10" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" +checksum = "55e877d961d4f96ce13615862322df7c0b6d169d40cab71a7ef3f9b9e594451e" dependencies = [ "serde", ] @@ -2148,9 +2162,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -2158,9 +2172,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -2281,31 +2295,16 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -2322,9 +2321,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -2340,18 +2339,18 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oodle" @@ -2364,9 +2363,9 @@ dependencies = [ [[package]] name = "open" -version = "5.0.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" dependencies = [ "is-wsl", "libc", @@ -2375,11 +2374,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.59" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -2396,7 +2395,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -2407,9 +2406,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2417,6 +2416,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -2493,12 +2498,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "path-clean" version = "1.0.1" @@ -2537,15 +2536,15 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -2554,9 +2553,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -2564,22 +2563,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -2681,9 +2680,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2693,15 +2692,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2752,9 +2751,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -2770,9 +2769,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2808,15 +2807,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2828,9 +2818,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -2839,14 +2829,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -2860,13 +2850,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -2877,17 +2867,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -2905,9 +2895,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -2957,18 +2949,15 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" -dependencies = [ - "xmlparser", -] +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2987,15 +2976,24 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", ] [[package]] @@ -3040,9 +3038,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -3055,11 +3053,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3081,7 +3079,7 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading", + "libloading 0.7.4", "luajit2-sys", "nanorand", "oodle", @@ -3097,11 +3095,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -3110,9 +3108,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -3120,41 +3118,50 @@ dependencies = [ [[package]] name = "self_cell" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -3172,9 +3179,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -3224,15 +3231,15 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3279,28 +3286,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.10" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3311,15 +3308,16 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "steamlocate" -version = "2.0.0-alpha.0" +version = "2.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b1568c4a70a26c4373fe1131ffa4eff055459631b6e40c6bc118615f2d870c3" +checksum = "c3b6a4810c4e7fecb0123a9a8ba99b335c17d92e636c265ef99108ee4734c812" dependencies = [ + "crc", "dirs", "keyvalues-parser", "keyvalues-serde", "serde", - "winreg 0.10.1", + "winreg 0.51.0", ] [[package]] @@ -3357,9 +3355,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -3399,15 +3397,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3431,70 +3435,69 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.0" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", - "toml 0.8.6", + "toml 0.8.12", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall 0.4.1", + "fastrand 2.1.0", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3502,13 +3505,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -3524,10 +3528,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -3558,9 +3563,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" +checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" dependencies = [ "displaydoc", ] @@ -3588,9 +3593,9 @@ checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" [[package]] name = "tokio" -version = "1.33.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -3599,7 +3604,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -3607,13 +3612,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -3628,9 +3633,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -3639,16 +3644,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -3662,14 +3666,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.6" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.7", + "toml_edit 0.22.12", ] [[package]] @@ -3687,22 +3691,22 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.1.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.8", ] [[package]] @@ -3730,7 +3734,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -3755,9 +3759,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", @@ -3766,9 +3770,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -3795,9 +3799,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" @@ -3807,9 +3811,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[package]] name = "type-map" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ "rustc-hash", ] @@ -3859,18 +3863,18 @@ checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" [[package]] name = "unic-langid" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" dependencies = [ "tinystr", ] @@ -3906,9 +3910,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-bidi-mirroring" @@ -3936,24 +3940,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-vo" @@ -3963,15 +3967,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -4031,9 +4035,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -4064,9 +4068,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4089,9 +4093,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4099,24 +4103,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4126,9 +4130,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4136,28 +4140,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4165,9 +4169,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -4199,11 +4203,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4230,6 +4234,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -4260,6 +4273,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4272,6 +4301,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4284,6 +4319,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4296,6 +4337,18 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4308,6 +4361,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4320,6 +4379,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4332,6 +4397,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4345,21 +4416,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winnow" -version = "0.5.19" +name = "windows_x86_64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.10.1" +name = "winnow" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ - "winapi", + "memchr", ] [[package]] @@ -4372,6 +4449,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wio" version = "0.2.2" @@ -4440,9 +4527,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index cb6e2f0..7ac703f 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -286,12 +286,18 @@ pub(crate) async fn load_initial(path: PathBuf, is_default: bool) -> Result game_info, + Err(err) => { + tracing::error!("Failed to collect game info: {:?}", err); + None } + }; + + if config.game_dir.is_none() && game_info.is_none() { + tracing::error!("No Game Directory set. Head to the 'Settings' tab to set it manually",); } let mod_dir = config.data_dir.join("mods"); diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 53c4ef1..4d5f831 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -723,6 +723,14 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { ) .wrap_err("Failed to gather deployment information")?; + let game_info = match game_info { + Ok(game_info) => game_info, + Err(err) => { + tracing::error!("Failed to collect game info: {:#?}", err); + None + } + }; + tracing::debug!(?game_info, ?deployment_info); if let Some(game_info) = game_info { diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 28e4694..3c9530e 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,12 +1,15 @@ use std::path::PathBuf; -mod log; - -pub use log::*; +use color_eyre::eyre::{OptionExt as _, WrapErr as _}; +use color_eyre::Result; use serde::{Deserialize, Serialize}; use steamlocate::SteamDir; use time::OffsetDateTime; +pub use log::*; + +mod log; + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfigResources { pub init: PathBuf, @@ -74,25 +77,23 @@ pub struct GameInfo { pub last_updated: OffsetDateTime, } -pub fn collect_game_info() -> Option { - let mut dir = if let Some(dir) = SteamDir::locate() { - dir - } else { - tracing::debug!("Failed to locate Steam installation"); - return None; - }; +pub fn collect_game_info() -> Result> { + let dir = SteamDir::locate().wrap_err("Failed to locate Steam installation")?; let found = dir - .app(&STEAMAPP_ID) - .and_then(|app| app.last_updated.map(|v| (app.path.clone(), v))); + .find_app(STEAMAPP_ID) + .wrap_err("Failed to look up game by Steam app ID")?; - let Some((path, last_updated)) = found else { - tracing::debug!("Found Steam, but failed to find game installation"); - return None; + let Some((app, _)) = found else { + return Ok(None); }; - Some(GameInfo { - path, + let last_updated = app + .last_updated + .ok_or_eyre("Missing field 'last_updated'")?; + + Ok(Some(GameInfo { + path: app.install_dir.into(), last_updated: last_updated.into(), - }) + })) } From ec578f4953e8757523919d6a63a7784b98c0ed28 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 15:30:54 +0200 Subject: [PATCH 067/184] Update CI image Updates for - Rust - LLVM - Xwin --- .ci/image/Dockerfile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 4e3433b..7ab9c0c 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -1,7 +1,7 @@ # https://jake-shadle.github.io/xwin/ FROM debian:bullseye-slim as xwin -ARG XWIN_VERSION=0.5.0 +ARG XWIN_VERSION=0.5.2 ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz @@ -59,6 +59,7 @@ COPY lib/oodle/*.so lib/oodle/*.a /src/ FROM linux as msvc +ARG LLVM_VERSION=18 ENV KEYRINGS /usr/local/share/keyrings ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key @@ -70,22 +71,22 @@ RUN set -eux; \ gpg --dearmor > $KEYRINGS/llvm.gpg < /root/llvm-snapshot.gpg.key; \ # wine gpg --dearmor > $KEYRINGS/winehq.gpg < /root/winehq.key; \ - echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-13 main" > /etc/apt/sources.list.d/llvm.list; \ + echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list; \ echo "deb [signed-by=$KEYRINGS/winehq.gpg] https://dl.winehq.org/wine-builds/debian/ bullseye main" > /etc/apt/sources.list.d/winehq.list; \ dpkg --add-architecture i386; \ apt-get update; \ apt-get install --no-install-recommends -y \ - libclang-13-dev \ + libclang-${LLVM_VERSION}-dev \ gcc-mingw-w64-x86-64 \ - clang-13 \ - llvm-13 \ - lld-13 \ + clang-${LLVM_VERSION} \ + llvm-${LLVM_VERSION} \ + lld-${LLVM_VERSION} \ winehq-staging \ ; \ # ensure that clang/clang++ are callable directly - ln -s clang-13 /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-13 /usr/bin/ld.lld; \ + ln -s clang-${LLVM_VERSION} /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-${LLVM_VERSION} /usr/bin/ld.lld; \ # We also need to setup symlinks ourselves for the MSVC shims because they aren't in the debian packages - ln -s clang-13 /usr/bin/clang-cl && ln -s llvm-ar-13 /usr/bin/llvm-lib && ln -s lld-link-13 /usr/bin/lld-link; \ + ln -s clang-${LLVM_VERSION} /usr/bin/clang-cl && ln -s llvm-ar-${LLVM_VERSION} /usr/bin/llvm-lib && ln -s lld-link-${LLVM_VERSION} /usr/bin/lld-link; \ # Verify the symlinks are correct clang++ -v; \ ld.lld -v; \ From 535a30a7ca6f77195da6bf54ab4d9b9120ff3334 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 May 2024 00:50:01 +0200 Subject: [PATCH 068/184] Add simpler shell parser This obsoletes `shlex`. The quoting turned out unnecessary, and the splitting supported a lot more than we need. It also forced unncessary allocations: The splitting doesn't add any characters and keeps UTF-8 intact, so returning slices from the input is perfectly possible. Though this particular implementation will only come to use in the future, as `CmdLine` still requires that the slices are cloned. Still, the custom implementation performs about 3x faster. --- crates/dtmt/Cargo.toml | 5 +- crates/dtmt/src/cmd/bundle/extract.rs | 32 +++-- crates/dtmt/src/main.rs | 2 + crates/dtmt/src/shell_parse.rs | 189 ++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 crates/dtmt/src/shell_parse.rs diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 69bbc31..688066f 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -33,7 +33,10 @@ path-slash = "0.2.1" async-recursion = "1.0.2" notify = "5.1.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -shlex = "1.2.0" +shlex = { version = "1.2.0", optional = true } [dev-dependencies] tempfile = "3.3.0" + +[features] +shlex-bench = ["dep:shlex"] diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 5e1c03b..9a0f1dd 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; -use color_eyre::eyre::{self, Context, Result}; +use color_eyre::eyre::{self, bail, Context, Result}; use color_eyre::{Help, Report}; use futures::future::try_join_all; use futures::StreamExt; @@ -12,7 +12,9 @@ use sdk::{Bundle, BundleFile, CmdLine}; use tokio::fs; use crate::cmd::util::resolve_bundle_paths; +use crate::shell_parse::ShellParser; +#[inline] fn parse_glob_pattern(s: &str) -> Result { match Pattern::new(s) { Ok(p) => Ok(p), @@ -20,6 +22,7 @@ fn parse_glob_pattern(s: &str) -> Result { } } +#[inline] fn flatten_name(s: &str) -> String { s.replace('/', "_") } @@ -131,26 +134,29 @@ async fn parse_command_line_template(tmpl: &String) -> Result { let mut cmd = if matches!(fs::try_exists(tmpl).await, Ok(true)) { let path = PathBuf::from(tmpl); if path.file_name() == Some(OsStr::new("main.py")) { - let arg = path.display().to_string(); let mut cmd = CmdLine::new("python"); - cmd.arg(shlex::quote(&arg).to_string()); + cmd.arg(path); cmd } else { CmdLine::new(path) } } else { - let Some(args) = shlex::split(tmpl) else { - eyre::bail!("Invalid shell syntax"); - }; + let mut parsed = ShellParser::new(tmpl.as_bytes()); + // Safety: The initial `tmpl` was a `&String` (i.e. valid UTF-8), and `shlex` does not + // insert or remove characters, nor does it split UTF-8 characters. + // So the resulting byte stream is still valid UTF-8. + let mut cmd = CmdLine::new(unsafe { + let bytes = parsed.next().expect("Template is not empty"); + String::from_utf8_unchecked(bytes.to_vec()) + }); - // We already checked that the template is not empty - let mut cmd = CmdLine::new(args[0].clone()); - let mut it = args.iter(); - // Skip the first one, that's the command name - it.next(); + while let Some(arg) = parsed.next() { + // Safety: See above. + cmd.arg(unsafe { String::from_utf8_unchecked(arg.to_vec()) }); + } - for arg in it { - cmd.arg(arg); + if parsed.errored { + bail!("Invalid command line template"); } cmd diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index bd419e7..2e10b17 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -1,6 +1,7 @@ #![feature(io_error_more)] #![feature(let_chains)] #![feature(result_flattening)] +#![feature(test)] #![windows_subsystem = "console"] use std::path::PathBuf; @@ -27,6 +28,7 @@ mod cmd { mod util; pub mod watch; } +mod shell_parse; #[derive(Default, Deserialize, Serialize)] struct GlobalConfig { diff --git a/crates/dtmt/src/shell_parse.rs b/crates/dtmt/src/shell_parse.rs new file mode 100644 index 0000000..6f35a5f --- /dev/null +++ b/crates/dtmt/src/shell_parse.rs @@ -0,0 +1,189 @@ +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ParserState { + Start, + Word, + SingleQuote, + DoubleQuote, +} + +pub struct ShellParser<'a> { + bytes: &'a [u8], + offset: usize, + pub errored: bool, +} + +impl<'a> ShellParser<'a> { + pub fn new(bytes: &'a [u8]) -> Self { + Self { + bytes, + offset: 0, + errored: false, + } + } + + fn parse_word(&mut self) -> Option<&'a [u8]> { + // The start of the current word. Certain leading characters should be ignored, + // so this might change. + let mut start = self.offset; + let mut state = ParserState::Start; + + while self.offset < self.bytes.len() { + let c = self.bytes[self.offset]; + self.offset += 1; + + match state { + ParserState::Start => match c { + // Ignore leading whitespace + b' ' | b'\t' | b'\n' => start += 1, + b'\'' => { + state = ParserState::SingleQuote; + start += 1; + } + b'"' => { + state = ParserState::DoubleQuote; + start += 1; + } + _ => { + state = ParserState::Word; + } + }, + ParserState::Word => match c { + // Unquoted whitespace ends the current word + b' ' | b'\t' | b'\n' => { + return Some(&self.bytes[start..self.offset - 1]); + } + _ => {} + }, + ParserState::SingleQuote => match c { + b'\'' => { + return Some(&self.bytes[start..(self.offset - 1)]); + } + _ => {} + }, + ParserState::DoubleQuote => match c { + b'"' => { + return Some(&self.bytes[start..(self.offset - 1)]); + } + _ => {} + }, + } + } + + match state { + ParserState::Start => None, + ParserState::Word => Some(&self.bytes[start..self.offset]), + ParserState::SingleQuote | ParserState::DoubleQuote => { + self.errored = true; + None + } + } + } +} + +impl<'a> Iterator for ShellParser<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + self.parse_word() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_one_word() { + let mut it = ShellParser::new(b"hello"); + assert_eq!(it.next(), Some("hello".as_bytes())); + assert_eq!(it.next(), None); + } + + #[test] + fn test_one_single() { + let mut it = ShellParser::new(b"'hello'"); + assert_eq!(it.next(), Some("hello".as_bytes())); + assert_eq!(it.next(), None); + } + + #[test] + fn test_open_quote() { + let mut it = ShellParser::new(b"'hello"); + assert_eq!(it.next(), None); + assert!(it.errored) + } + + #[test] + fn test_ww2ogg() { + let mut it = ShellParser::new( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + assert_eq!(it.next(), Some("ww2ogg.exe".as_bytes())); + assert_eq!(it.next(), Some("--pcb".as_bytes())); + assert_eq!( + it.next(), + Some("/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin".as_bytes()) + ); + assert_eq!(it.next(), None); + } +} + +#[cfg(test)] +mod bench { + extern crate test; + + use super::*; + #[cfg(feature = "shlex-bench")] + use shlex::bytes::Shlex; + use test::Bencher; + + mod ww2ogg { + use super::*; + + #[bench] + fn custom(b: &mut Bencher) { + let val = test::black_box( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + b.iter(|| { + let it = ShellParser::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + + #[cfg(feature = "shlex-bench")] + #[bench] + fn shlex(b: &mut Bencher) { + let val = test::black_box( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + b.iter(|| { + let it = Shlex::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + } + + mod one_single { + use super::*; + + #[bench] + fn custom(b: &mut Bencher) { + let val = test::black_box(b"'hello'"); + b.iter(|| { + let it = ShellParser::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + + #[cfg(feature = "shlex-bench")] + #[bench] + fn shlex(b: &mut Bencher) { + let val = test::black_box(b"'hello'"); + b.iter(|| { + let it = Shlex::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + } +} From dfd51513dade1eb4ad39f5bfecb19469f7b34fee Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 16:30:39 +0200 Subject: [PATCH 069/184] Update strip-ansi-escapes Removes duplicate dependency of arrayvec. --- Cargo.lock | 21 +++++++-------------- crates/dtmm/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4da0fb9..ed230d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -1999,7 +1993,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", ] [[package]] @@ -2008,7 +2002,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", "serde", ] @@ -3346,9 +3340,9 @@ dependencies = [ [[package]] name = "strip-ansi-escapes" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" dependencies = [ "vte", ] @@ -3543,7 +3537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec", "bytemuck", "cfg-if", "png", @@ -4047,11 +4041,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vte" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ - "arrayvec 0.5.2", "utf8parse", "vte_generate_state_changes", ] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 05dc160..9e4960a 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -30,7 +30,7 @@ sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } string_template = "0.2.1" -strip-ansi-escapes = "0.1.1" +strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } tokio-stream = { version = "0.1.12", features = ["fs"] } From 6030917ade914885b3ef413e9fa222908ead8e5c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 16:37:28 +0200 Subject: [PATCH 070/184] Update steamlocate The actual update already happened, but `cargo oudated` cannot handle the suffix, so we must update the `Cargo.toml` as well. --- lib/dtmt-shared/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index 57b4a8c..b547dbe 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" ansi_term = "0.12.1" color-eyre = "0.6.2" serde = "1.0.152" -steamlocate = "2.0.0-alpha.0" +steamlocate = "2.0.0-beta.2" time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } tracing = "0.1.37" tracing-error = "0.2.0" From 21df6cfc5c9a2276620f8b78ec7318c8b769da75 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 18:52:58 +0200 Subject: [PATCH 071/184] Update reqwest --- Cargo.lock | 205 ++++++++++++++++++++++++++++++++------- Cargo.toml | 1 + lib/nexusmods/Cargo.toml | 2 +- 3 files changed, 173 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed230d8..d6dd126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,9 +205,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -524,13 +524,20 @@ dependencies = [ name = "color-eyre" version = "0.6.2" dependencies = [ + "ansi-parser", "backtrace", "color-spantrace", "eyre", "indenter", "once_cell", "owo-colors", + "pretty_assertions", + "thiserror", + "tracing", "tracing-error", + "tracing-subscriber", + "url", + "wasm-bindgen-test", ] [[package]] @@ -742,6 +749,12 @@ dependencies = [ "serde", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -1596,9 +1609,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", @@ -1678,9 +1691,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1689,12 +1702,24 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", "pin-project-lite", ] @@ -1704,47 +1729,60 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "hyper" -version = "0.14.28" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -2672,6 +2710,26 @@ dependencies = [ "xi-unicode", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.62", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2709,6 +2767,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2867,11 +2935,11 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", @@ -2879,8 +2947,10 @@ dependencies = [ "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2902,7 +2972,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.50.0", + "winreg 0.52.0", ] [[package]] @@ -2983,13 +3053,20 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustybuzz" version = "0.6.0" @@ -3054,6 +3131,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -3703,6 +3786,28 @@ dependencies = [ "winnow 0.6.8", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3715,6 +3820,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4150,6 +4256,31 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.62", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -4434,9 +4565,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -4444,9 +4575,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.51.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -4479,6 +4610,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 11831e4..d44a5a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "lib/sdk", "lib/serde_sjson", "lib/luajit2-sys", + "lib/color-eyre", ] exclude = ["lib/color-eyre"] diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index d7967ef..2f90a46 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" futures = "0.3.26" lazy_static = "1.4.0" regex = "1.7.1" -reqwest = { version = "0.11.14" } +reqwest = { version = "0.12.4" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" thiserror = "1.0.39" From bac75e1c9aad1f9a3737e7e78c3e8d5e15b901db Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 18:58:08 +0200 Subject: [PATCH 072/184] Update confy --- Cargo.lock | 36 ++++++++---------------------------- crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6dd126..7f3772a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,14 +572,14 @@ checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" [[package]] name = "confy" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" +checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" dependencies = [ "directories", "serde", "thiserror", - "toml 0.5.11", + "toml", ] [[package]] @@ -768,11 +768,11 @@ dependencies = [ [[package]] name = "directories" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "dirs-sys 0.3.7", + "dirs-sys", ] [[package]] @@ -781,7 +781,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -794,17 +794,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys" version = "0.4.1" @@ -3519,7 +3508,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.12", + "toml", "version-compare", ] @@ -3732,15 +3721,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.12" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 9e4960a..627981b 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -13,7 +13,7 @@ bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" colors-transform = "0.2.11" -confy = "0.5.1" +confy = "0.6.1" druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 688066f..4a98244 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "unicode"] } cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } color-eyre = "0.6.2" -confy = "0.5.1" +confy = "0.6.1" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" From ecd235be058233677a767663c60d0db9615380a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:14:07 +0200 Subject: [PATCH 073/184] Update ansi-parser Patched to update heapless while waiting for the merge request. --- Cargo.lock | 49 ++++++++----------------------------------------- Cargo.toml | 1 + 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f3772a..7411d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,8 +40,7 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad5bd94a775101bd68c2de2bb28ca2eccd69f395ae3aec4ac4f6da3c1cd2c6a" +source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#af1951d16951a37101139763593be44103e3a477" dependencies = [ "heapless", "nom", @@ -123,18 +122,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "as-slice" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" -dependencies = [ - "generic-array 0.12.4", - "generic-array 0.13.3", - "generic-array 0.14.7", - "stable_deref_trait", -] - [[package]] name = "associative-cache" version = "1.0.1" @@ -279,7 +266,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -704,7 +691,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.7", + "generic-array", "typenum", ] @@ -1389,24 +1376,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1617,9 +1586,9 @@ dependencies = [ [[package]] name = "hash32" -version = "0.1.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] @@ -1632,12 +1601,10 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapless" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "as-slice", - "generic-array 0.14.7", "hash32", "stable_deref_trait", ] @@ -1855,7 +1822,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d44a5a6..54cf161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["lib/color-eyre"] [patch.crates-io] color-eyre = { path = "lib/color-eyre" } +ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } [profile.dev.package.backtrace] opt-level = 3 From 647cb1b8bdaf75e6404b623ec2839953475e23f4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:16:34 +0200 Subject: [PATCH 074/184] Update fastrand --- Cargo.lock | 13 ++----------- lib/sdk/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7411d5e..660e7cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,15 +1055,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.1.0" @@ -3108,7 +3099,7 @@ dependencies = [ "byteorder", "color-eyre", "csv-async", - "fastrand 1.9.0", + "fastrand", "futures", "futures-util", "glob", @@ -3492,7 +3483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand", "rustix", "windows-sys 0.52.0", ] diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 63f789b..6745ff4 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -8,7 +8,7 @@ bitflags = "1.3.2" byteorder = "1.4.3" color-eyre = "0.6.2" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -fastrand = "1.8.0" +fastrand = "2.1.0" futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" From ae30499a4901d9ef5ce0ad182d6ea81ed9519b24 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:19:54 +0200 Subject: [PATCH 075/184] Remove unused dependency --- Cargo.lock | 14 +------------- crates/dtmt/Cargo.toml | 1 - lib/sdk/Cargo.toml | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 660e7cc..336f957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading 0.8.3", + "libloading", ] [[package]] @@ -949,7 +949,6 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading 0.7.4", "luajit2-sys", "nanorand", "notify", @@ -2009,16 +2008,6 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libloading" version = "0.8.3" @@ -3103,7 +3092,6 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading 0.7.4", "luajit2-sys", "nanorand", "oodle", diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 4a98244..60cd70c 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -13,7 +13,6 @@ dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" -libloading = "0.7.4" nanorand = "0.7.0" oodle = { path = "../../lib/oodle", version = "*" } pin-project-lite = "0.2.9" diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 6745ff4..f060b4b 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -12,7 +12,6 @@ fastrand = "2.1.0" futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" -libloading = "0.7.4" nanorand = "0.7.0" pin-project-lite = "0.2.9" serde = { version = "1.0.147", features = ["derive"] } From 0c4c078b100b71f2226f42bbc9398dae2593c328 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:24:57 +0200 Subject: [PATCH 076/184] Update dependencies --- Cargo.lock | 62 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 336f957..88c8664 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -283,9 +283,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" @@ -435,7 +435,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -812,7 +812,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -1274,7 +1274,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2363,7 +2363,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2539,7 +2539,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2663,7 +2663,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3151,22 +3151,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3191,9 +3191,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3409,9 +3409,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.62" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -3502,7 +3502,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3630,7 +3630,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3669,21 +3669,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -3701,9 +3701,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -3760,7 +3760,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -4137,7 +4137,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -4171,7 +4171,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4204,7 +4204,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] From 4bc5777a4b921a28bbc1d5dc675503144f04e9c3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 20:04:47 +0200 Subject: [PATCH 077/184] Update notify --- Cargo.lock | 75 +++--------------------------------------- crates/dtmt/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88c8664..8e0f2f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,20 +2236,21 @@ dependencies = [ [[package]] name = "notify" -version = "5.2.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", + "log", "mio", "walkdir", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -4266,15 +4267,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4293,21 +4285,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -4339,12 +4316,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4357,12 +4328,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4375,12 +4340,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4399,12 +4358,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4417,12 +4370,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4435,12 +4382,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4453,12 +4394,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 60cd70c..baa21f8 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -30,7 +30,7 @@ zip = "0.6.3" path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" -notify = "5.1.0" +notify = "6.1.1" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } shlex = { version = "1.2.0", optional = true } From 3546bc8faa243605015768f3bdf25621ac40397a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 21:57:45 +0200 Subject: [PATCH 078/184] Update bindgen --- Cargo.lock | 36 +++++++++++++++++++++++++----------- lib/luajit2-sys | 2 +- lib/oodle/Cargo.toml | 2 +- lib/oodle/build.rs | 4 +--- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e0f2f1..a4be1c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,23 +213,24 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.64.0" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "cexpr", "clang-sys", + "itertools", "lazy_static", "lazycell", "log", - "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 1.0.109", + "syn 2.0.63", "which", ] @@ -1890,6 +1891,15 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2497,12 +2507,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -2714,6 +2718,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.63", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 1912016..5d1a075 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 19120166f9fc7838b98c71fc348791abc820e323 +Subproject commit 5d1a075742395f767c79d9c0d7466c6fb442f106 diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index 3283592..6fc5039 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = "0.6.2" tracing = "0.1.37" [build-dependencies] -bindgen = "0.64.0" +bindgen = "0.69.4" diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs index e33c7d5..1a1d4e9 100644 --- a/lib/oodle/build.rs +++ b/lib/oodle/build.rs @@ -1,5 +1,3 @@ -extern crate bindgen; - use std::env; use std::path::PathBuf; @@ -33,7 +31,7 @@ fn main() { .blocklist_file("stdlib.h") // Tell cargo to invalidate the built crate whenever any of the // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) // Finish the builder and generate the bindings. .generate() // Unwrap the Result and panic on failure. From b8ac80562ad6514af17220764f25cb8e8ebb0e97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 22:46:21 +0200 Subject: [PATCH 079/184] Update zip --- Cargo.lock | 135 ++++++++------------------------- Cargo.toml | 3 + crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/package.rs | 28 ++----- 5 files changed, 43 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4be1c4..bd1f426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -110,6 +99,15 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -196,12 +194,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - [[package]] name = "bincode" version = "1.3.3" @@ -382,16 +374,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.7.0" @@ -580,12 +562,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "core-foundation" version = "0.9.4" @@ -737,6 +713,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "diff" version = "0.1.13" @@ -751,7 +738,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -1618,15 +1604,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.9" @@ -1807,15 +1784,6 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - [[package]] name = "instant" version = "0.1.12" @@ -2466,17 +2434,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "path-clean" version = "1.0.1" @@ -2495,18 +2452,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -3225,17 +3170,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.8" @@ -3386,12 +3320,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - [[package]] name = "svgfilters" version = "0.4.0" @@ -4493,40 +4421,37 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zip" -version = "0.6.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" dependencies = [ - "aes", - "byteorder", + "arbitrary", "bzip2", - "constant_time_eq", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", - "hmac", - "pbkdf2", - "sha1", + "indexmap", + "thiserror", "time", "zstd", ] [[package]] name = "zstd" -version = "0.11.2+zstd.1.5.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" dependencies = [ - "libc", "zstd-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 54cf161..39d8f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ members = [ ] exclude = ["lib/color-eyre"] +[workspace.dependencies] +zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } + [patch.crates-io] color-eyre = { path = "lib/color-eyre" } ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 627981b..ef862f3 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -38,4 +38,4 @@ tracing = "0.1.37" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = "0.6.4" +zip = { workspace = true } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index baa21f8..d836a50 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -26,7 +26,7 @@ tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "m tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing = { version = "0.1.37", features = ["async-await"] } -zip = "0.6.3" +zip = { workspace = true } path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index e922b6e..5ded885 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -1,6 +1,5 @@ use std::io::{Cursor, Write}; use std::path::{Path, PathBuf}; -use std::sync::Arc; use clap::{value_parser, Arg, ArgMatches, Command}; use color_eyre::eyre::{Context, Result}; @@ -8,9 +7,9 @@ use color_eyre::Help; use dtmt_shared::ModConfig; use path_slash::{PathBufExt, PathExt}; use tokio::fs; -use tokio::sync::Mutex; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; +use zip::write::SimpleFileOptions; use zip::ZipWriter; use crate::cmd::build::read_project_config; @@ -51,11 +50,7 @@ pub(crate) fn command_definition() -> Command { } #[async_recursion::async_recursion] -async fn process_directory( - zip: Arc>>, - path: P1, - prefix: P2, -) -> Result<()> +async fn process_directory(zip: &mut ZipWriter, path: P1, prefix: P2) -> Result<()> where P1: AsRef + std::marker::Send, P2: AsRef + std::marker::Send, @@ -64,9 +59,7 @@ where let path = path.as_ref(); let prefix = prefix.as_ref(); - zip.lock() - .await - .add_directory(prefix.to_slash_lossy(), Default::default())?; + zip.add_directory(prefix.to_slash_lossy(), SimpleFileOptions::default())?; let read_dir = fs::read_dir(&path) .await @@ -87,12 +80,11 @@ where .await .wrap_err_with(|| format!("Failed to read '{}'", in_path.display()))?; { - let mut zip = zip.lock().await; - zip.start_file(out_path.to_slash_lossy(), Default::default())?; + zip.start_file(out_path.to_slash_lossy(), SimpleFileOptions::default())?; zip.write_all(&data)?; } } else if t.is_dir() { - process_directory(zip.clone(), in_path, out_path).await?; + process_directory(zip, in_path, out_path).await?; } } @@ -107,16 +99,12 @@ where let path = path.as_ref(); let dest = dest.as_ref(); - let data = Cursor::new(Vec::new()); - let zip = ZipWriter::new(data); - let zip = Arc::new(Mutex::new(zip)); + let mut zip = ZipWriter::new(Cursor::new(Vec::with_capacity(1024))); - process_directory(zip.clone(), path, PathBuf::from(&cfg.id)) + process_directory(&mut zip, path, PathBuf::from(&cfg.id)) .await .wrap_err("Failed to add directory to archive")?; - let mut zip = zip.lock().await; - { let name = PathBuf::from(&cfg.id).join("dtmt.cfg"); let path = cfg.dir.join("dtmt.cfg"); @@ -125,7 +113,7 @@ where .await .wrap_err_with(|| format!("Failed to read mod config at {}", path.display()))?; - zip.start_file(name.to_slash_lossy(), Default::default())?; + zip.start_file(name.to_slash_lossy(), SimpleFileOptions::default())?; zip.write_all(&data)?; } From 189c3199a0aec79e6ca2cba6ee9bd3ad455e0e32 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 22:53:39 +0200 Subject: [PATCH 080/184] Update bitflags --- Cargo.lock | 4 ++-- crates/dtmm/Cargo.toml | 2 +- lib/sdk/Cargo.toml | 2 +- lib/sdk/src/bundle/file.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd1f426..d103e9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,7 +890,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 1.3.2", + "bitflags 2.5.0", "clap", "color-eyre", "colors-transform", @@ -3044,7 +3044,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 1.3.2", + "bitflags 2.5.0", "byteorder", "color-eyre", "csv-async", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index ef862f3..c1184ac 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" ansi-parser = "0.9.0" async-recursion = "1.0.5" bincode = "1.3.3" -bitflags = "1.3.2" +bitflags = "2.5.0" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" colors-transform = "0.2.11" diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index f060b4b..b164d47 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -4,7 +4,7 @@ version = "0.3.0" edition = "2021" [dependencies] -bitflags = "1.3.2" +bitflags = "2.5.0" byteorder = "1.4.3" color-eyre = "0.6.2" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index ab59884..4d6c56e 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -501,7 +501,7 @@ impl BundleFileVariant { } bitflags! { - #[derive(Default)] + #[derive(Default, Clone, Copy, Debug)] pub struct Properties: u32 { const DATA = 0b100; } From 9577d704235da55e96ad901df74ecc9a2eb5963d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 May 2024 10:58:03 +0200 Subject: [PATCH 081/184] Add missing build tools to CI image --- .ci/image/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 7ab9c0c..f115929 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -37,6 +37,7 @@ RUN set -eux; \ apt-get update; \ apt-get install --no-install-recommends -y \ build-essential \ + cmake \ curl \ git \ gpg \ From ef4c2a1d94b316cc52640358cb9521e0c0dd8dd2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 May 2024 11:06:05 +0200 Subject: [PATCH 082/184] Update interprocess --- Cargo.lock | 33 +++++++++++++++++++-------------- crates/dtmm/Cargo.toml | 4 ++-- crates/dtmm/src/main.rs | 26 ++++++++++++++++++-------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d103e9c..7cbb4b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,15 +1798,14 @@ dependencies = [ [[package]] name = "interprocess" -version = "1.2.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" dependencies = [ - "cfg-if", "libc", - "rustc_version", - "to_method", - "winapi", + "recvmsg", + "widestring", + "windows-sys 0.52.0", ] [[package]] @@ -2093,9 +2092,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minijinja" -version = "1.0.21" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e877d961d4f96ce13615862322df7c0b6d169d40cab71a7ef3f9b9e594451e" +checksum = "7165d0e94806d52ad5295e4b54a95176d831814840bc067298ca647e1c956338" dependencies = [ "serde", ] @@ -2765,6 +2764,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3540,12 +3545,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - [[package]] name = "tokio" version = "1.37.0" @@ -4178,6 +4177,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c1184ac..f57d0c8 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -18,10 +18,10 @@ druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "b druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -interprocess = { version = "1.2.1", default-features = false } +interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -minijinja = "1.0.10" +minijinja = { version = "2.0.1", default-features = false } nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } open = "5.0.1" diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index aa223f0..6c0bb48 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -11,7 +11,7 @@ use clap::{command, value_parser, Arg}; use color_eyre::eyre::{self, Context}; use color_eyre::{Report, Result, Section}; use druid::AppLauncher; -use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; +use interprocess::local_socket::{prelude::*, GenericNamespaced, ListenerOptions}; use tokio::sync::RwLock; use crate::controller::worker::work_thread; @@ -29,9 +29,9 @@ mod util { } mod ui; -// As explained in https://docs.rs/interprocess/latest/interprocess/local_socket/enum.NameTypeSupport.html +// As explained in https://docs.rs/interprocess/2.1.0/interprocess/local_socket/struct.Name.html // namespaces are supported on both platforms we care about: Windows and Linux. -const IPC_ADDRESS: &str = "@dtmm.sock"; +const IPC_ADDRESS: &str = "dtmm.sock"; #[tracing::instrument] fn notify_nxm_download( @@ -42,9 +42,13 @@ fn notify_nxm_download( tracing::debug!("Received Uri '{}', sending to main process.", uri.as_ref()); - let mut stream = LocalSocketStream::connect(IPC_ADDRESS) - .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) - .suggestion("Make sure the main window is open.")?; + let mut stream = LocalSocketStream::connect( + IPC_ADDRESS + .to_ns_name::() + .expect("Invalid socket name"), + ) + .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) + .suggestion("Make sure the main window is open.")?; tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); @@ -130,8 +134,14 @@ fn main() -> Result<()> { let _guard = span.enter(); let event_sink = event_sink.clone(); - let server = - LocalSocketListener::bind(IPC_ADDRESS).wrap_err("Failed to create IPC listener")?; + let server = ListenerOptions::new() + .name( + IPC_ADDRESS + .to_ns_name::() + .expect("Invalid socket name"), + ) + .create_sync() + .wrap_err("Failed to create IPC listener")?; tracing::debug!("IPC server listening on '{}'", IPC_ADDRESS); From 96a7eeb1e0cf1f1af484febb8278a9bb679c0586 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 May 2024 14:51:17 +0200 Subject: [PATCH 083/184] Implement faster hex string parsing --- Cargo.toml | 6 + Justfile | 8 + lib/sdk/src/lib.rs | 2 + lib/sdk/src/murmur/mod.rs | 377 +----------------------------------- lib/sdk/src/murmur/types.rs | 365 ++++++++++++++++++++++++++++++++++ lib/sdk/src/murmur/util.rs | 132 +++++++++++++ 6 files changed, 518 insertions(+), 372 deletions(-) create mode 100644 lib/sdk/src/murmur/types.rs create mode 100644 lib/sdk/src/murmur/util.rs diff --git a/Cargo.toml b/Cargo.toml index 39d8f38..8cbe52c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,9 @@ strip = "debuginfo" [profile.release-lto] inherits = "release" lto = true + +[profile.perf] +inherits = "release" +strip = false +lto = true +debug = "line-tables-only" diff --git a/Justfile b/Justfile index f9b37bc..dbecc22 100644 --- a/Justfile +++ b/Justfile @@ -1,5 +1,13 @@ +set positional-arguments + fly_target := "main" +build-perf-dtmt: + cargo build --profile perf --bin dtmt + +perf-dtmt *args='': build-perf-dtmt + perf record --call-graph dwarf ./target/perf/dtmt "$@" + ci-build: ci-build-msvc ci-build-linux ci-build-msvc: diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index 37a4d67..a24b3bd 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(test)] + mod binary; mod bundle; mod context; diff --git a/lib/sdk/src/murmur/mod.rs b/lib/sdk/src/murmur/mod.rs index a2a9ef3..87a8473 100644 --- a/lib/sdk/src/murmur/mod.rs +++ b/lib/sdk/src/murmur/mod.rs @@ -1,15 +1,16 @@ use std::fmt; use color_eyre::eyre::Context; -use color_eyre::Report; +use color_eyre::{Report, Result}; use serde::de::Visitor; -use serde::{Deserialize, Serialize}; -use serde::{Deserializer, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; mod dictionary; // Currently unused // mod murmurhash32; mod murmurhash64; +mod types; +mod util; pub const SEED: u32 = 0; @@ -18,372 +19,4 @@ pub use murmurhash64::hash; pub use murmurhash64::hash32; pub use murmurhash64::hash_inverse as inverse; -fn _swap_bytes_u32(value: u32) -> u32 { - u32::from_le_bytes(value.to_be_bytes()) -} - -fn _swap_bytes_u64(value: u64) -> u64 { - u64::from_le_bytes(value.to_be_bytes()) -} - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct Murmur64(u64); - -impl Murmur64 { - pub fn hash(s: B) -> Self - where - B: AsRef<[u8]>, - { - hash(s.as_ref(), SEED as u64).into() - } -} - -impl From for Murmur64 { - fn from(value: u64) -> Self { - Self(value) - } -} - -impl From for u64 { - fn from(value: Murmur64) -> Self { - value.0 - } -} - -impl TryFrom<&str> for Murmur64 { - type Error = Report; - - fn try_from(value: &str) -> Result { - u64::from_str_radix(value, 16) - .map(Self) - .wrap_err_with(|| format!("Failed to convert value to Murmur64: {value}")) - } -} - -impl fmt::UpperHex for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl fmt::LowerHex for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::Display for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl<'de> Visitor<'de> for Murmur64 { - type Value = Self; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "an usigned 64 bit integer \ - or a string in hexadecimal format encoding such an integer", - ) - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - let bytes = value.to_le_bytes(); - Ok(Self::from(u64::from_le_bytes(bytes))) - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(Self::from(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match Murmur64::try_from(value) { - Ok(hash) => Ok(hash), - Err(err) => Err(E::custom(format!( - "failed to convert '{value}' to Murmur64: {err}" - ))), - } - } -} - -impl<'de> Deserialize<'de> for Murmur64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(Self(0)) - } -} - -impl Serialize for Murmur64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self:016X}")) - } -} - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct Murmur32(u32); - -impl Murmur32 { - pub fn hash(s: B) -> Self - where - B: AsRef<[u8]>, - { - hash32(s.as_ref(), SEED).into() - } -} - -impl From for Murmur32 { - fn from(value: u32) -> Self { - Self(value) - } -} - -impl From for u32 { - fn from(value: Murmur32) -> Self { - value.0 - } -} - -impl TryFrom<&str> for Murmur32 { - type Error = Report; - - fn try_from(value: &str) -> Result { - u32::from_str_radix(value, 16) - .map(Self) - .wrap_err_with(|| format!("Failed to convert value to Murmur32: {value}")) - } -} - -impl fmt::UpperHex for Murmur32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl fmt::Display for Murmur32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl Serialize for Murmur32 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self:08X}")) - } -} - -impl<'de> Visitor<'de> for Murmur32 { - type Value = Self; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "an usigned 32 bit integer \ - or a string in hexadecimal format encoding such an integer", - ) - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - let bytes = value.to_le_bytes(); - self.visit_u32(u64::from_le_bytes(bytes) as u32) - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - self.visit_u32(value as u32) - } - - fn visit_u32(self, value: u32) -> Result - where - E: serde::de::Error, - { - Ok(Self::from(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match Murmur32::try_from(value) { - Ok(hash) => Ok(hash), - Err(err) => Err(E::custom(format!( - "failed to convert '{value}' to Murmur32: {err}" - ))), - } - } -} - -impl<'de> Deserialize<'de> for Murmur32 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(Self(0)) - } -} - -// This type encodes the fact that when reading in a bundle, we don't always have a dictionary -// entry for every hash in there. So we do want to have the real string available when needed, -// but at the same time retain the original hash information for when we don't. -// This is especially important when wanting to write back the read bundle, as the hashes need to -// stay the same. -// The previous system of always turning hashes into strings worked well for the purpose of -// displaying hashes, but would have made it very hard to turn a stringyfied hash back into -// an actual hash. -#[derive(Clone, Debug, Eq)] -pub enum IdString64 { - Hash(Murmur64), - String(String), -} - -impl IdString64 { - pub fn to_murmur64(&self) -> Murmur64 { - match self { - Self::Hash(hash) => *hash, - Self::String(s) => Murmur64::hash(s.as_bytes()), - } - } - - pub fn display(&self) -> IdString64Display { - let s = match self { - IdString64::Hash(hash) => hash.to_string(), - IdString64::String(s) => s.clone(), - }; - - IdString64Display(s) - } - - pub fn is_string(&self) -> bool { - match self { - IdString64::Hash(_) => false, - IdString64::String(_) => true, - } - } - - pub fn is_hash(&self) -> bool { - match self { - IdString64::Hash(_) => true, - IdString64::String(_) => false, - } - } -} - -impl> From for IdString64 { - fn from(value: S) -> Self { - Self::String(value.into()) - } -} - -impl From for IdString64 { - fn from(value: Murmur64) -> Self { - Self::Hash(value) - } -} - -impl From for Murmur64 { - fn from(value: IdString64) -> Self { - value.to_murmur64() - } -} - -impl PartialEq for IdString64 { - fn eq(&self, other: &Self) -> bool { - self.to_murmur64() == other.to_murmur64() - } -} - -impl std::hash::Hash for IdString64 { - fn hash(&self, state: &mut H) { - state.write_u64(self.to_murmur64().into()); - } -} - -impl serde::Serialize for IdString64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u64(self.to_murmur64().into()) - } -} - -struct IdString64Visitor; - -impl<'de> serde::de::Visitor<'de> for IdString64Visitor { - type Value = IdString64; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an u64 or a string") - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::Hash(value.into())) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v.to_string())) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v)) - } -} - -impl<'de> serde::Deserialize<'de> for IdString64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_u64(IdString64Visitor) - } -} - -pub struct IdString64Display(String); - -impl std::fmt::Display for IdString64Display { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::UpperHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::UpperHex::fmt(&self.to_murmur64(), f) - } -} - -impl std::fmt::LowerHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::LowerHex::fmt(&self.to_murmur64(), f) - } -} +pub use types::*; diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs new file mode 100644 index 0000000..1146494 --- /dev/null +++ b/lib/sdk/src/murmur/types.rs @@ -0,0 +1,365 @@ +use self::util::{parse_hex32, parse_hex64}; + +use super::*; + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct Murmur64(u64); + +impl Murmur64 { + pub fn hash(s: B) -> Self + where + B: AsRef<[u8]>, + { + hash(s.as_ref(), SEED as u64).into() + } +} + +impl From for Murmur64 { + fn from(value: u64) -> Self { + Self(value) + } +} + +impl From for u64 { + fn from(value: Murmur64) -> Self { + value.0 + } +} + +impl TryFrom<&str> for Murmur64 { + type Error = Report; + + fn try_from(value: &str) -> Result { + parse_hex64(value) + .map(Self) + .wrap_err_with(|| format!("Failed to convert value to Murmur64: {value}")) + } +} + +impl fmt::UpperHex for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::LowerHex for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::Display for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl<'de> Visitor<'de> for Murmur64 { + type Value = Self; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "an usigned 64 bit integer \ + or a string in hexadecimal format encoding such an integer", + ) + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + let bytes = value.to_le_bytes(); + Ok(Self::from(u64::from_le_bytes(bytes))) + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(Self::from(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match Murmur64::try_from(value) { + Ok(hash) => Ok(hash), + Err(err) => Err(E::custom(format!( + "failed to convert '{value}' to Murmur64: {err}" + ))), + } + } +} + +impl<'de> Deserialize<'de> for Murmur64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(Self(0)) + } +} + +impl Serialize for Murmur64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{self:016X}")) + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct Murmur32(u32); + +impl Murmur32 { + pub fn hash(s: B) -> Self + where + B: AsRef<[u8]>, + { + hash32(s.as_ref(), SEED).into() + } +} + +impl From for Murmur32 { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + fn from(value: Murmur32) -> Self { + value.0 + } +} + +impl TryFrom<&str> for Murmur32 { + type Error = Report; + + fn try_from(value: &str) -> Result { + parse_hex32(value) + .map(Self) + .wrap_err_with(|| format!("Failed to convert value to Murmur32: {value}")) + } +} + +impl fmt::UpperHex for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::Display for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl Serialize for Murmur32 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{self:08X}")) + } +} + +impl<'de> Visitor<'de> for Murmur32 { + type Value = Self; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "an usigned 32 bit integer \ + or a string in hexadecimal format encoding such an integer", + ) + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + let bytes = value.to_le_bytes(); + self.visit_u32(u64::from_le_bytes(bytes) as u32) + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + self.visit_u32(value as u32) + } + + fn visit_u32(self, value: u32) -> Result + where + E: serde::de::Error, + { + Ok(Self::from(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match Murmur32::try_from(value) { + Ok(hash) => Ok(hash), + Err(err) => Err(E::custom(format!( + "failed to convert '{value}' to Murmur32: {err}" + ))), + } + } +} + +impl<'de> Deserialize<'de> for Murmur32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(Self(0)) + } +} + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString64 { + Hash(Murmur64), + String(String), +} + +impl IdString64 { + pub fn to_murmur64(&self) -> Murmur64 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur64::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString64Display { + let s = match self { + IdString64::Hash(hash) => hash.to_string(), + IdString64::String(s) => s.clone(), + }; + + IdString64Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString64::Hash(_) => false, + IdString64::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString64::Hash(_) => true, + IdString64::String(_) => false, + } + } +} + +impl> From for IdString64 { + fn from(value: S) -> Self { + Self::String(value.into()) + } +} + +impl From for IdString64 { + fn from(value: Murmur64) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur64 { + fn from(value: IdString64) -> Self { + value.to_murmur64() + } +} + +impl PartialEq for IdString64 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur64() == other.to_murmur64() + } +} + +impl std::hash::Hash for IdString64 { + fn hash(&self, state: &mut H) { + state.write_u64(self.to_murmur64().into()); + } +} + +impl serde::Serialize for IdString64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.to_murmur64().into()) + } +} + +struct IdString64Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString64Visitor { + type Value = IdString64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u64 or a string") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u64(IdString64Visitor) + } +} + +pub struct IdString64Display(String); + +impl std::fmt::Display for IdString64Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur64(), f) + } +} + +impl std::fmt::LowerHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur64(), f) + } +} diff --git a/lib/sdk/src/murmur/util.rs b/lib/sdk/src/murmur/util.rs new file mode 100644 index 0000000..134c4e7 --- /dev/null +++ b/lib/sdk/src/murmur/util.rs @@ -0,0 +1,132 @@ +use color_eyre::eyre::bail; +use color_eyre::Result; + +// Generates tables similar to these: +// https://github.com/zbjornson/fast-hex/blob/a3487bca95127634a61bfeae8f8bfc8f0e5baa3f/src/hex.cc#L20-L89 +// `upper` determines upper vs. lower bits (first character is `upper`). +const fn generate_byte_map(upper: bool) -> [u8; 256] { + let mut out = [0u8; 256]; + let factor = if upper { 16 } else { 1 }; + + let mut i = 0; + + while i < 256 { + match i { + 0x30..=0x39 => out[i] = factor * (i as u8 - 0x30), + 0x41..=0x46 => out[i] = factor * (9 + i as u8 - 0x40), + 0x61..=0x66 => out[i] = factor * (9 + i as u8 - 0x60), + _ => out[i] = u8::MAX, + } + i += 1; + } + + out +} + +const BYTE_MAP_UPPER: [u8; 256] = generate_byte_map(true); +const BYTE_MAP_LOWER: [u8; 256] = generate_byte_map(false); + +macro_rules! make_parse_hex { + ($name:ident, $ty:ty, $len:expr) => { + #[inline] + pub fn $name(s: impl AsRef) -> Result<$ty> { + // For the string to be valid hex characters, it needs to be ASCII. + // So we can simply treat it as a byte stream. + let s = s.as_ref().as_bytes(); + + if s.len() != $len { + bail!( + "String length doesn't match. Expected {}, got {}", + $len, + s.len() + ); + } + + let n = $len / 2; + let mut out: $ty = 0; + let mut i = 0; + + while i < n { + let j = i * 2; + + let c1 = BYTE_MAP_UPPER[s[j] as usize]; + if c1 == u8::MAX { + bail!("Invalid character '{:?}' ({})", char::from(c1), c1); + } + + let c2 = BYTE_MAP_LOWER[s[j + 1] as usize]; + if c2 == u8::MAX { + bail!("Invalid character '{:?}' ({})", char::from(c2), c2); + } + + out |= ((c1 + c2) as $ty) << (n - i - 1) * 8; + + i += 1; + } + + Ok(out) + } + }; +} + +make_parse_hex!(parse_hex64, u64, 16); +make_parse_hex!(parse_hex32, u32, 8); + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parse_32() { + let hash = "A14E8DFA"; + assert_eq!(parse_hex32(hash).unwrap(), 0xA14E8DFA); + } + + #[test] + fn parse_64() { + let hash = "A14E8DFA2CD117E2"; + assert_eq!(parse_hex64(hash).unwrap(), 0xA14E8DFA2CD117E2); + } + + #[test] + fn std_from_radix_32() { + let hash = "A14E8DFA"; + assert_eq!(u32::from_str_radix(hash, 16).unwrap(), 0xA14E8DFA); + } + + #[test] + fn std_from_radix_64() { + let hash = "A14E8DFA2CD117E2"; + assert_eq!(u64::from_str_radix(hash, 16).unwrap(), 0xA14E8DFA2CD117E2); + } +} + +#[cfg(test)] +mod bench { + use super::{parse_hex32, parse_hex64}; + + extern crate test; + + const HASH32: &str = "A14E8DFA"; + const HASH64: &str = "A14E8DFA2CD117E2"; + + #[bench] + fn custom_32(b: &mut test::Bencher) { + b.iter(|| test::black_box(parse_hex32(test::black_box(HASH32)))) + } + + #[bench] + fn std_32(b: &mut test::Bencher) { + b.iter(|| test::black_box(u32::from_str_radix(test::black_box(HASH32), 16))) + } + + #[bench] + fn custom_64(b: &mut test::Bencher) { + b.iter(|| test::black_box(parse_hex64(test::black_box(HASH64)))) + } + + #[bench] + fn std_64(b: &mut test::Bencher) { + b.iter(|| test::black_box(u64::from_str_radix(test::black_box(HASH64), 16))) + } +} From b40375122877d62c6715ad7ff24b9177a97c8287 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 8 Jul 2024 16:55:32 +0200 Subject: [PATCH 084/184] Update ansi-parser --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cbb4b5..2df8581 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,8 +28,8 @@ dependencies = [ [[package]] name = "ansi-parser" -version = "0.9.0" -source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#af1951d16951a37101139763593be44103e3a477" +version = "0.9.1" +source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#bfcd2689677fa93ce72c55833e891af36c65b2aa" dependencies = [ "heapless", "nom", diff --git a/Cargo.toml b/Cargo.toml index 8cbe52c..8c0e7c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzi [patch.crates-io] color-eyre = { path = "lib/color-eyre" } -ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } +ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless", version = "0.9.1" } [profile.dev.package.backtrace] opt-level = 3 From 94b64b461917ad17c2137a01ad0ce2b09700f152 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 18:40:52 +0200 Subject: [PATCH 085/184] Update zip --- Cargo.lock | 42 ++++++++++++++++++++++++++++++++---------- Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2df8581..faad8f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,9 +640,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -2011,6 +2011,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.21" @@ -3435,18 +3441,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -4426,9 +4432,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zip" -version = "1.3.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" +checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" dependencies = [ "arbitrary", "bzip2", @@ -4437,11 +4443,27 @@ dependencies = [ "displaydoc", "flate2", "indexmap", + "memchr", "thiserror", "time", + "zopfli", "zstd", ] +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + [[package]] name = "zstd" version = "0.13.1" diff --git a/Cargo.toml b/Cargo.toml index 8c0e7c5..f38ac22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ exclude = ["lib/color-eyre"] [workspace.dependencies] -zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [patch.crates-io] color-eyre = { path = "lib/color-eyre" } From 0f14834e2d162fe6b734407ce6359cbc49127e91 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 18:41:38 +0200 Subject: [PATCH 086/184] Remove string_template Use minijinja for all templates --- Cargo.lock | 1 - crates/dtmm/Cargo.toml | 1 - .../assets/{mod_main.lua => mod_main.lua.j2} | 4 +++- crates/dtmm/src/controller/deploy.rs | 24 ++++++++++++------- 4 files changed, 18 insertions(+), 12 deletions(-) rename crates/dtmm/assets/{mod_main.lua => mod_main.lua.j2} (98%) diff --git a/Cargo.lock b/Cargo.lock index faad8f6..55cdf0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,6 @@ dependencies = [ "sdk", "serde", "serde_sjson", - "string_template", "strip-ansi-escapes", "time", "tokio", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index f57d0c8..661eb17 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -29,7 +29,6 @@ path-slash = "0.2.1" sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -string_template = "0.2.1" strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua.j2 similarity index 98% rename from crates/dtmm/assets/mod_main.lua rename to crates/dtmm/assets/mod_main.lua.j2 index e4006f6..c1131be 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -17,7 +17,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. --- It's also a valid table definition, thereby degrading gracefully when not replaced. +-- When not replaced, it's also a valid table definition, thereby degrading gracefully. local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, @@ -207,3 +207,5 @@ function init() Main:init() end + +-- vim: ft=lua diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 4d5f831..a59a1fe 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::io::{Cursor, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -16,7 +15,6 @@ use sdk::{ Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, }; use serde::{Deserialize, Serialize}; -use string_template::Template; use time::OffsetDateTime; use tokio::fs::{self, DirEntry}; use tokio::io::AsyncWriteExt; @@ -572,12 +570,17 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let span = tracing::debug_span!("Importing mod main script"); let _enter = span.enter(); - let is_io_enabled = format!("{}", state.is_io_enabled); - let mut data = HashMap::new(); - data.insert("is_io_enabled", is_io_enabled.as_str()); + let mut env = Environment::new(); + env.add_template("mod_main.lua", include_str!("../../assets/mod_main.lua.j2")) + .wrap_err("Failed to compile template for `mod_main.lua`")?; + let tmpl = env + .get_template("mod_main.lua") + .wrap_err("Failed to get template `mod_main.lua`")?; + + let lua = tmpl + .render(minijinja::context!(is_io_enabled => if state.is_io_enabled { "true" } else {"false"})) + .wrap_err("Failed to render template `mod_main.lua`")?; - let tmpl = include_str!("../../assets/mod_main.lua"); - let lua = Template::new(tmpl).render(&data); tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); let file = lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; @@ -707,7 +710,7 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { }, async { let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path).await { + match read_sjson_file::<_, DeploymentData>(&path).await { Ok(data) => Ok(Some(data)), Err(err) => { if let Some(err) = err.downcast_ref::() @@ -715,7 +718,10 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { { Ok(None) } else { - Err(err).wrap_err("Failed to read deployment data") + Err(err).wrap_err(format!( + "Failed to read deployment data from: {}", + path.display() + )) } } } From 05df72635a440a08ce307a2a3f5fa3396bcb7d28 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 2 Dec 2023 19:48:03 +0100 Subject: [PATCH 087/184] dtmm: Pull ModLoader in Closes #155. --- crates/dtmm/assets/init.lua | 70 +++++ crates/dtmm/assets/mod_loader.lua | 405 +++++++++++++++++++++++++++ crates/dtmm/assets/mod_main.lua.j2 | 25 +- crates/dtmm/src/controller/app.rs | 8 - crates/dtmm/src/controller/deploy.rs | 112 ++------ 5 files changed, 509 insertions(+), 111 deletions(-) create mode 100644 crates/dtmm/assets/init.lua create mode 100644 crates/dtmm/assets/mod_loader.lua diff --git a/crates/dtmm/assets/init.lua b/crates/dtmm/assets/init.lua new file mode 100644 index 0000000..e2acdbd --- /dev/null +++ b/crates/dtmm/assets/init.lua @@ -0,0 +1,70 @@ +local StateGame = require("scripts/game_states/state_game") +local StateSplash = require("scripts/game_states/game/state_splash") +local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + +local function hook(obj, fn_name, cb) + local orig = obj[fn_name] + + obj[fn_name] = function(...) + return cb(orig, ...) + end +end + +function init(mod_data, boot_gui) + local ModLoader = require("scripts/mods/mod_loader") + local mod_loader = ModLoader:new(mod_data, boot_gui) + + -- The mod loader needs to remain active during game play, to + -- enable reloads + hook(StateGame, "update", function(func, dt, ...) + mod_loader:update(dt) + return func(dt, ...) + end) + + -- Skip splash view + hook(StateSplash, "on_enter", function(func, self, ...) + local result = func(self, ...) + + self._should_skip = true + self._continue = true + + return result + end) + + -- Trigger state change events + hook(GameStateMachine, "_change_state", function(func, self, ...) + local old_state = self._state + local old_state_name = old_state and self:current_state_name() + + if old_state_name then + mod_loader:on_game_state_changed("exit", old_state_name, old_state) + end + + local result = func(self, ...) + + local new_state = self._state + local new_state_name = new_state and self:current_state_name() + + if new_state_name then + mod_loader:on_game_state_changed("enter", new_state_name, new_state) + end + + return result + end) + + -- Trigger ending state change event + hook(GameStateMachine, "destroy", function(func, self, ...) + local old_state = self._state + local old_state_name = old_state and self:current_state_name() + + if old_state_name then + mod_loader:on_game_state_changed("exit", old_state_name) + end + + return func(self, ...) + end) + + return mod_loader +end + +return init diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua new file mode 100644 index 0000000..4da08a4 --- /dev/null +++ b/crates/dtmm/assets/mod_loader.lua @@ -0,0 +1,405 @@ +-- Copyright on this file is owned by Fatshark. +-- It is extracted, used and modified with permission only for +-- the purpose of loading mods within Warhammer 40,000: Darktide. +local ModLoader = class("ModLoader") + +local table_unpack = table.unpack or unpack +local table_pack = table.pack or pack + +local ScriptGui = require("scripts/foundation/utilities/script_gui") + +local FONT_MATERIAL = "content/ui/fonts/arial" + +local LOG_LEVELS = { + spew = 4, + info = 3, + warning = 2, + error = 1 +} +local DEFAULT_SETTINGS = { + log_level = LOG_LEVELS.error, + developer_mode = false +} + +local Keyboard = Keyboard +local BUTTON_INDEX_R = Keyboard.button_index("r") +local BUTTON_INDEX_LEFT_SHIFT = Keyboard.button_index("left shift") +local BUTTON_INDEX_LEFT_CTRL = Keyboard.button_index("left ctrl") + +ModLoader.init = function(self, mod_data, boot_gui) + table.dump(mod_data, nil, 5, function(...) Log.info("ModLoader", ...) end) + + self._mod_data = mod_data + self._gui = boot_gui + + self._settings = Application.user_setting("mod_settings") or DEFAULT_SETTINGS + + self._mods = {} + self._num_mods = nil + self._chat_print_buffer = {} + self._reload_data = {} + self._ui_time = 0 + + self._state = "scanning" +end + +ModLoader.developer_mode_enabled = function(self) + return self._settings.developer_mode +end + +ModLoader.set_developer_mode = function(self, enabled) + self._settings.developer_mode = enabled +end + +ModLoader._draw_state_to_gui = function(self, gui, dt) + local state = self._state + local t = self._ui_time + dt + self._ui_time = t + local status_str = "Loading mods" + + if state == "scanning" then + status_str = "Scanning for mods" + elseif state == "loading" or state == "initializing" then + local mod = self._mods[self._mod_load_index] + status_str = string.format("Loading mod %q", mod.name) + end + + local msg = status_str .. string.rep(".", (2 * t) % 4) + ScriptGui.text(gui, msg, FONT_MATERIAL, 25, Vector3(20, 30, 1), Color.white()) +end + +ModLoader.remove_gui = function(self) + self._gui = nil +end + +ModLoader.mod_data = function(self, id) + -- Since this primarily exists for DMF, + -- we can optimize the search for its use case of looking for the + -- mod currently being loaded + local mod_data = self._mods[self._mod_load_index] + + if mod_data.id ~= id then + mod_data = nil + + for _, v in ipairs(self._mods) do + if v.id == id then + mod_data = v + end + end + end + + return mod_data +end + +ModLoader._check_reload = function() + return Keyboard.pressed(BUTTON_INDEX_R) and + Keyboard.button(BUTTON_INDEX_LEFT_SHIFT) + + Keyboard.button(BUTTON_INDEX_LEFT_CTRL) == 2 +end + +ModLoader.update = function(self, dt) + local chat_print_buffer = self._chat_print_buffer + local num_delayed_prints = #chat_print_buffer + + if num_delayed_prints > 0 and Managers.chat then + for i = 1, num_delayed_prints, 1 do + -- TODO: Use new chat system + -- Managers.chat:add_local_system_message(1, chat_print_buffer[i], true) + + chat_print_buffer[i] = nil + end + end + + local old_state = self._state + + if self._settings.developer_mode and self:_check_reload() then + self._reload_requested = true + end + + if self._reload_requested and old_state == "done" then + self:_reload_mods() + end + + if old_state == "done" then + self:_run_callbacks("update", dt) + elseif old_state == "scanning" then + Log.info("ModLoader", "Scanning for mods") + self:_build_mod_table() + + self._state = self:_load_mod(1) + self._ui_time = 0 + elseif old_state == "loading" then + local handle = self._loading_resource_handle + + if ResourcePackage.has_loaded(handle) then + ResourcePackage.flush(handle) + + local mod = self._mods[self._mod_load_index] + local next_index = mod.package_index + 1 + local mod_data = mod.data + + if next_index <= #mod_data.packages then + self:_load_package(mod, next_index) + else + self._state = "initializing" + end + end + elseif old_state == "initializing" then + local mod = self._mods[self._mod_load_index] + local mod_data = mod.data + + Log.info("ModLoader", "Initializing mod %q", mod.name) + + mod.state = "running" + local ok, object = xpcall(mod_data.run, function(err) + if type(err) == "string" then + return err .. "\n" .. Script.callstack() + else + return err + end + end) + + if not ok then + if object.error then + object = string.format( + "%s\n<>\n%s\n<>\n<>\n%s\n<>\n<>\n%s\n<>", + object.error, object.traceback, object.locals, object.self) + end + + Log.error("ModLoader", "Failed 'run' for %q: %s", mod.name, object) + end + + mod.object = object or {} + + self:_run_callback(mod, "init", self._reload_data[mod.id]) + + Log.info("ModLoader", "Finished loading %q", mod.name) + + self._state = self:_load_mod(self._mod_load_index + 1) + end + + local gui = self._gui + if gui then + self:_draw_state_to_gui(gui, dt) + end + + if old_state ~= self._state then + Log.info("ModLoader", "%s -> %s", old_state, self._state) + end +end + +ModLoader.all_mods_loaded = function(self) + return self._state == "done" +end + +ModLoader.destroy = function(self) + self:_run_callbacks("on_destroy") + self:unload_all_mods() +end + +ModLoader._run_callbacks = function(self, callback_name, ...) + for i = 1, self._num_mods, 1 do + local mod = self._mods[i] + + if mod and not mod.callbacks_disabled then + self:_run_callback(mod, callback_name, ...) + end + end +end + +ModLoader._run_callback = function(self, mod, callback_name, ...) + local object = mod.object + local cb = object[callback_name] + + if not cb then + return + end + + local args = table_pack(...) + + local success, val = xpcall( + function() return cb(object, table_unpack(args)) end, + function(err) + if type(err) == "string" then + return err .. "\n" .. Script.callstack() + else + return err + end + end + ) + + if success then + return val + else + Log.error("ModLoader", "Failed to run callback %q for mod %q with id %q. Disabling callbacks until reload.", + callback_name, mod.name, mod.id) + if val.error then + Log.error("ModLoader", + "Error: %s\n<>\n%s<>\n<>\n%s<>\n<>\n%s<>", + val.error, val.traceback, val.locals, val.self) + else + Log.error("ModLoader", "Error: %s", val or "[unknown error]") + end + + mod.callbacks_disabled = true + end +end + +ModLoader._start_scan = function(self) + Log.info("ModLoader", "Starting mod scan") + self._state = "scanning" +end + +ModLoader._build_mod_table = function(self) + fassert(table.is_empty(self._mods), "Trying to add mods to non-empty mod table") + + for i, mod_data in ipairs(self._mod_data) do + Log.info("ModLoader", "mods[%d] = id=%q | name=%q | bundled=%s", i, mod_data.id, mod_data.name, + tostring(mod_data.bundled)) + + self._mods[i] = { + id = mod_data.id, + state = "not_loaded", + callbacks_disabled = false, + name = mod_data.name, + loaded_packages = {}, + packages = mod_data.packages, + data = mod_data, + bundled = mod_data.bundled or false, + } + end + + self._num_mods = #self._mods + + Log.info("ModLoader", "Found %i mods", self._num_mods) +end + +ModLoader._load_mod = function(self, index) + self._ui_time = 0 + local mods = self._mods + local mod = mods[index] + + if not mod then + table.clear(self._reload_data) + + return "done" + end + + Log.info("ModLoader", "Loading mod %q", mod.id) + + mod.state = "loading" + + Crashify.print_property(string.format("Mod:%s:%s", mod.id, mod.name), true) + + self._mod_load_index = index + + if mod.bundled and mod.packages[1] then + self:_load_package(mod, 1) + return "loading" + else + return "initializing" + end +end + +ModLoader._load_package = function(self, mod, index) + mod.package_index = index + local package_name = mod.packages[index] + + if not package_name then + return + end + + Log.info("ModLoader", "Loading package %q", package_name) + + local resource_handle = Application.resource_package(package_name) + self._loading_resource_handle = resource_handle + + ResourcePackage.load(resource_handle) + + table.insert(mod.loaded_packages, resource_handle) +end + +ModLoader.unload_all_mods = function(self) + if self._state ~= "done" then + Log.error("ModLoader", "Mods can't be unloaded, mod state is not \"done\". current: %q", self._state) + + return + end + + Log.info("ModLoader", "Unload all mod packages") + + for i = self._num_mods, 1, -1 do + local mod = self._mods[i] + + if mod then + self:unload_mod(i) + end + + self._mods[i] = nil + end + + self._num_mods = nil + self._state = "unloaded" +end + +ModLoader.unload_mod = function(self, index) + local mod = self._mods[index] + + if mod then + Log.info("ModLoader", "Unloading %q.", mod.name) + + for _, handle in ipairs(mod.loaded_packages) do + ResourcePackage.unload(handle) + Application.release_resource_package(handle) + end + + mod.state = "not_loaded" + else + Log.error("ModLoader", "Mod index %i can't be unloaded, has not been loaded", index) + end +end + +ModLoader._reload_mods = function(self) + Log.info("ModLoader", "reloading mods") + + for i = 1, self._num_mods, 1 do + local mod = self._mods[i] + + if mod and mod.state == "running" then + Log.info("ModLoader", "reloading %s", mod.name) + + self._reload_data[mod.id] = self:_run_callback(mod, "on_reload") + else + Log.info("ModLoader", "not reloading mod, state: %s", mod.state) + end + end + + self:unload_all_mods() + self:_start_scan() + + self._reload_requested = false +end + +ModLoader.on_game_state_changed = function(self, status, state_name, state_object) + if self._state == "done" then + self:_run_callbacks("on_game_state_changed", status, state_name, state_object) + else + Log.warning("ModLoader", "Ignored on_game_state_changed call due to being in state %q", self._state) + end +end + +ModLoader.print = function(self, level, str, ...) + local f = Log[level] + if f then + f("ModLoader", str, ...) + else + local message = string.format("[ModLoader][" .. level .. "] " .. str, ...) + local log_level = LOG_LEVELS[level] or 99 + + if log_level <= 2 then + print(message) + end + end +end + +return ModLoader diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index c1131be..c93a75c 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -18,7 +18,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- When not replaced, it's also a valid table definition, thereby degrading gracefully. -local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 +local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { @@ -105,8 +105,15 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") --- Inject our state into the game. The state needs to run after `StateGame._init_managers`, --- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +-- We need to inject two states into two different state machines: +-- First, we inject one into the `"Main"` state machine at a specific location, so that we're +-- still early in the process, but right after `StateRequireScripts` where most game files +-- are already available to `require` and hook. +-- This is where the `ModLoader` is created initially. +-- Then, we inject into the very first position of the `"Game"` state machine. This runs right +-- after `StateGame._init_managers`, at which point all the parts needed for DMF and other mods +-- have been initialized. +-- This is where `ModLoader` will finally start loading mods. local function patch_mod_loading_state() local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") @@ -121,8 +128,7 @@ local function patch_mod_loading_state() self._package_manager = package_manager self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadDML", nil), } end @@ -130,10 +136,13 @@ local function patch_mod_loading_state() local package_manager = self._package_manager if package_manager:update() then - local DML = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data) + + local create_mod_loader = require("scripts/mods/init") + local mod_loader = create_mod_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") return true, false end @@ -188,8 +197,6 @@ local function patch_mod_loading_state() GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) -- And since we're done now, we can revert the function to its original GameStateMachine.init = GameStateMachine_init - - return else -- In all other cases, simply call the original GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 7ac703f..44842e0 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -161,14 +161,6 @@ where } pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { - { - let first = state.mods.get(0); - if first.is_none() || !(first.unwrap().id == "dml" && first.unwrap().enabled) { - // TODO: Add a suggestion where to get it, once that's published - eyre::bail!("'Darktide Mod Loader' needs to be installed, enabled and at the top of the load order"); - } - } - if tracing::enabled!(tracing::Level::DEBUG) { let order = state.mods.iter().filter(|i| i.enabled).enumerate().fold( String::new(), diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index a59a1fe..f0cfe96 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -26,7 +26,6 @@ use crate::state::{ActionState, PackageInfo}; pub const MOD_BUNDLE_NAME: &str = "packages/mods"; pub const BOOT_BUNDLE_NAME: &str = "packages/boot"; -pub const DML_BUNDLE_NAME: &str = "packages/dml"; pub const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; pub const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; pub const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; @@ -225,11 +224,7 @@ async fn copy_mod_folders(state: Arc) -> Result> { let mut tasks = Vec::new(); - for mod_info in state - .mods - .iter() - .filter(|m| m.id != "dml" && m.enabled && !m.bundled) - { + for mod_info in state.mods.iter().filter(|m| m.enabled && !m.bundled) { let span = tracing::trace_span!("copying legacy mod", name = mod_info.name); let _enter = span.enter(); @@ -283,7 +278,7 @@ fn build_mod_data_lua(state: Arc) -> Result { .mods .iter() .filter_map(|m| { - if m.id == "dml" || !m.enabled { + if !m.enabled { return None; } @@ -325,31 +320,29 @@ async fn build_bundles(state: Arc) -> Result> { let mut bundles = Vec::new(); - { - tracing::trace!("Building mod data script"); - - let span = tracing::debug_span!("Building mod data script"); + let mut add_lua_asset = |name, data: &str| { + let span = tracing::info_span!("Compiling Lua", name, data_len = data.len()); let _enter = span.enter(); - let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?; - - tracing::trace!("Compiling mod data script"); - - let file = - lua::compile(MOD_DATA_SCRIPT, lua).wrap_err("Failed to compile mod data Lua file")?; - - tracing::trace!("Compile mod data script"); + let file = lua::compile(name, data).wrap_err("Failed to compile Lua")?; mod_bundle.add_file(file); - } + + Ok::<_, Report>(()) + }; + + build_mod_data_lua(state.clone()) + .wrap_err("Failed to build 'mod_data.lua'") + .and_then(|data| add_lua_asset(MOD_DATA_SCRIPT, &data))?; + add_lua_asset("scripts/mods/init", include_str!("../../assets/init.lua"))?; + add_lua_asset( + "scripts/mods/mod_loader", + include_str!("../../assets/mod_loader.lua"), + )?; tracing::trace!("Preparing tasks to deploy bundle files"); - for mod_info in state - .mods - .iter() - .filter(|m| m.id != "dml" && m.enabled && m.bundled) - { + for mod_info in state.mods.iter().filter(|m| m.enabled && m.bundled) { let span = tracing::trace_span!("building mod packages", name = mod_info.name); let _enter = span.enter(); @@ -497,75 +490,6 @@ async fn patch_boot_bundle(state: Arc) -> Result> { boot_bundle.add_file(f); } - { - tracing::trace!("Handling DML packages and bundle"); - let span = tracing::trace_span!("handle DML"); - let _enter = span.enter(); - - let mut variant = BundleFileVariant::new(); - - let mod_info = state - .mods - .iter() - .find(|m| m.id == "dml") - .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; - let pkg_info = mod_info - .packages - .get(0) - .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) - .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); - - { - let bin = fs::read(&src) - .await - .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; - let name = Bundle::get_name_from_path(&state.ctx, &src); - - let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; - - bundles.push(dml_bundle); - }; - - { - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - tracing::debug!( - "Copying bundle {} for mod {}: {} -> {}", - pkg_name, - mod_name, - src.display(), - dest.display() - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - } - - let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; - variant.set_data(pkg.to_binary()?); - - let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - { let span = tracing::debug_span!("Importing mod main script"); let _enter = span.enter(); From 91651a8467f850f2ea45cad177736be34f507042 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 19:53:46 +0200 Subject: [PATCH 088/184] Apply linter fixes in mod_main.lua --- crates/dtmm/assets/mod_main.lua.j2 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index c93a75c..4dd2787 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -18,7 +18,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- When not replaced, it's also a valid table definition, thereby degrading gracefully. -local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 +local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { @@ -115,7 +115,6 @@ log("mod_main", "'scripts/main' loaded") -- have been initialized. -- This is where `ModLoader` will finally start loading mods. local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") local StateGameLoadMods = class("StateGameLoadMods") @@ -132,7 +131,7 @@ local function patch_mod_loading_state() } end - StateBootLoadDML._state_update = function(self, dt) + StateBootLoadDML._state_update = function(self, _) local package_manager = self._package_manager if package_manager:update() then @@ -157,9 +156,7 @@ local function patch_mod_loading_state() self._next_state_params = params end - function StateGameLoadMods:update(main_dt) - local state = self._loading_state - + function StateGameLoadMods:update(_) -- We're relying on the fact that DML internally makes sure -- that `Managers.mod:update()` is being called appropriately. -- The implementation as of this writing is to hook `StateGame.update`. From e6f1e7c117e6160458a23e5ad2120c5ba76d7ae1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 21:54:51 +0200 Subject: [PATCH 089/184] Fix load order verification With `.enumerate()` after `.filter()`, the resulting indices didn't properly map back to the overall mod list anymore. But the checks afterwards relied on that. Moving the `.enumerate()` before the `.filter()` makes sure that the indices are correct. --- crates/dtmm/src/controller/app.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 44842e0..d5b397c 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -162,18 +162,20 @@ where pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { if tracing::enabled!(tracing::Level::DEBUG) { - let order = state.mods.iter().filter(|i| i.enabled).enumerate().fold( - String::new(), - |mut s, (i, info)| { + let order = state + .mods + .iter() + .enumerate() + .filter(|(_, i)| i.enabled) + .fold(String::new(), |mut s, (i, info)| { s.push_str(&format!("{}: {} - {}\n", i, info.id, info.name)); s - }, - ); + }); tracing::debug!("Mod order:\n{}", order); } - for (i, mod_info) in state.mods.iter().filter(|i| i.enabled).enumerate() { + for (i, mod_info) in state.mods.iter().enumerate().filter(|(_, i)| i.enabled) { for dep in &mod_info.depends { let dep_info = state.mods.iter().enumerate().find(|(_, m)| m.id == dep.id); From 84606814fdd1fd3fae8987e7aafd21a0d70a1bd8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 16:54:57 +0200 Subject: [PATCH 090/184] Add application icon --- .gitattributes | 6 +++++ Cargo.lock | 23 ++++++++++++++++++-- crates/dtmm/Cargo.toml | 9 ++++++++ crates/dtmm/assets/DTMM_logo.xcf | 3 +++ crates/dtmm/assets/DTMM_logo_256.png | 3 +++ crates/dtmm/assets/DTMM_logo_48.png | 3 +++ crates/dtmm/assets/DTMM_logo_64.png | 3 +++ crates/dtmm/assets/DTMM_logo_border.png | 3 +++ crates/dtmm/assets/DTMM_logo_faint_glow.png | 3 +++ crates/dtmm/assets/DTMM_logo_small.png | 3 +++ crates/dtmm/assets/dtmm.desktop | 1 + crates/dtmm/assets/dtmm.ico | 3 +++ crates/dtmm/build.rs | 7 ++++++ docs/screenshots/dtmm.png | Bin 58994 -> 130 bytes 14 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 crates/dtmm/assets/DTMM_logo.xcf create mode 100644 crates/dtmm/assets/DTMM_logo_256.png create mode 100644 crates/dtmm/assets/DTMM_logo_48.png create mode 100644 crates/dtmm/assets/DTMM_logo_64.png create mode 100644 crates/dtmm/assets/DTMM_logo_border.png create mode 100644 crates/dtmm/assets/DTMM_logo_faint_glow.png create mode 100644 crates/dtmm/assets/DTMM_logo_small.png create mode 100644 crates/dtmm/assets/dtmm.ico create mode 100644 crates/dtmm/build.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a16e3d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto + +*.xcf filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/Cargo.lock b/Cargo.lock index 55cdf0d..a251de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,7 +549,7 @@ dependencies = [ "directories", "serde", "thiserror", - "toml", + "toml 0.8.13", ] [[package]] @@ -918,6 +918,7 @@ dependencies = [ "tracing-error", "tracing-subscriber", "usvg", + "winres", "zip", ] @@ -3407,7 +3408,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.13", "version-compare", ] @@ -3614,6 +3615,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.8.13" @@ -4396,6 +4406,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 661eb17..f947fc6 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -2,6 +2,12 @@ name = "dtmm" version = "0.1.0" edition = "2021" +authors = ["Lucas Schwiderski "] +description = "DTMM is a GUI application to install and manage mods for the game." +documentation = "https://git.sclu1034.dev/bitsquid_dt/dtmt/wiki" +repository = "https://git.sclu1034.dev/bitsquid_dt/dtmt" +homepage = "https://git.sclu1034.dev/bitsquid_dt/dtmt" +license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -38,3 +44,6 @@ tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" zip = { workspace = true } + +[build-dependencies] +winres = "0.1.12" diff --git a/crates/dtmm/assets/DTMM_logo.xcf b/crates/dtmm/assets/DTMM_logo.xcf new file mode 100644 index 0000000..00de67d --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:144903129d56235895e433435ecee90528ceb5c4db98f5b02e637a215dde1881 +size 17736337 diff --git a/crates/dtmm/assets/DTMM_logo_256.png b/crates/dtmm/assets/DTMM_logo_256.png new file mode 100644 index 0000000..e53931f --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_256.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62096b0f6e3798820c9ad04ba61fdff45522b7c67c4b254dc8fd11bdde984c76 +size 39017 diff --git a/crates/dtmm/assets/DTMM_logo_48.png b/crates/dtmm/assets/DTMM_logo_48.png new file mode 100644 index 0000000..33c1d11 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_48.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b0524e05b8f2c6ca061aa7edc6f7e62efb8bcddf347e4e9344187efb437436c +size 3082 diff --git a/crates/dtmm/assets/DTMM_logo_64.png b/crates/dtmm/assets/DTMM_logo_64.png new file mode 100644 index 0000000..e5d5407 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0bf9431d5f46f4437c21fe38ebbd86e8b3872acaf3a13f0bc8f4a9e8e78e118 +size 4287 diff --git a/crates/dtmm/assets/DTMM_logo_border.png b/crates/dtmm/assets/DTMM_logo_border.png new file mode 100644 index 0000000..bf610e4 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_border.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cefec60ffe91eb4d827e1c7c9b4bfebdec528236809e02ccc9f15b15ee290442 +size 537707 diff --git a/crates/dtmm/assets/DTMM_logo_faint_glow.png b/crates/dtmm/assets/DTMM_logo_faint_glow.png new file mode 100644 index 0000000..1066370 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_faint_glow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d579be0297e78ef0c9cfb4ce357dd61ed13cc65084d5a38c322913cdcdbe5b99 +size 605023 diff --git a/crates/dtmm/assets/DTMM_logo_small.png b/crates/dtmm/assets/DTMM_logo_small.png new file mode 100644 index 0000000..0020520 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_small.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be2a3cb6a94828b3df9368bcf9ea335ad08590a69b128a15a92fb1cf751d06b6 +size 502425 diff --git a/crates/dtmm/assets/dtmm.desktop b/crates/dtmm/assets/dtmm.desktop index 4c2e0a9..cb9185c 100644 --- a/crates/dtmm/assets/dtmm.desktop +++ b/crates/dtmm/assets/dtmm.desktop @@ -8,3 +8,4 @@ Keywords=Mod; StartupNotify=true Categories=Utility; MimeType=x-scheme-handler/nxm; +Icon=dtmm diff --git a/crates/dtmm/assets/dtmm.ico b/crates/dtmm/assets/dtmm.ico new file mode 100644 index 0000000..b862839 --- /dev/null +++ b/crates/dtmm/assets/dtmm.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d4bea2d7af89a86c3f052240a2acd137a0df5e9b6e85ecfe3f163032010652 +size 37519 diff --git a/crates/dtmm/build.rs b/crates/dtmm/build.rs new file mode 100644 index 0000000..9e551d4 --- /dev/null +++ b/crates/dtmm/build.rs @@ -0,0 +1,7 @@ +fn main() { + if cfg!(target_os = "windows") { + let mut res = winres::WindowsResource::new(); + res.set_icon("assets/dtmm.ico"); + res.compile().unwrap(); + } +} diff --git a/docs/screenshots/dtmm.png b/docs/screenshots/dtmm.png index af2a9805d643bfc8d38e348d4ad09aa5f10760a1..d51c253f997dbdb4b526f67e2f7ee34566e11863 100644 GIT binary patch literal 130 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 iwSWmqA#PFVmcT-UE1d8-u5-pUXO@geCyn1PZPI From 0539b6b4566c48fe10329ea3cb78e149842d1dcc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:34:23 +0200 Subject: [PATCH 091/184] Strip block whitespace in templates Ref: #168 --- crates/dtmm/src/controller/deploy.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index f0cfe96..957db5d 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -268,6 +268,8 @@ fn build_mod_data_lua(state: Arc) -> Result { } let mut env = Environment::new(); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2")) .wrap_err("Failed to compile template for `mod_data.lua`")?; let tmpl = env @@ -495,14 +497,17 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let _enter = span.enter(); let mut env = Environment::new(); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); env.add_template("mod_main.lua", include_str!("../../assets/mod_main.lua.j2")) .wrap_err("Failed to compile template for `mod_main.lua`")?; let tmpl = env .get_template("mod_main.lua") .wrap_err("Failed to get template `mod_main.lua`")?; + let is_io_enabled = if state.is_io_enabled { "true" } else { "false" }; let lua = tmpl - .render(minijinja::context!(is_io_enabled => if state.is_io_enabled { "true" } else {"false"})) + .render(minijinja::context!(is_io_enabled => is_io_enabled)) .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); From ba31d5109817b2908ac40402baad79841a87a336 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:39:03 +0200 Subject: [PATCH 092/184] Align Crashify property with Fatshark They recently submitted a PR to DML with their preferred property names, so we should match that. Ref: #168 --- crates/dtmm/assets/mod_loader.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua index 4da08a4..e000292 100644 --- a/crates/dtmm/assets/mod_loader.lua +++ b/crates/dtmm/assets/mod_loader.lua @@ -289,7 +289,7 @@ ModLoader._load_mod = function(self, index) mod.state = "loading" - Crashify.print_property(string.format("Mod:%s:%s", mod.id, mod.name), true) + Crashify.print_property(string.format("Mod:%s", mod.name), true) self._mod_load_index = index From 1020efe53d314d282b8b3de416e132d072bc1379 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:40:34 +0200 Subject: [PATCH 093/184] Add version field to mod loader logging Ref: #168 --- crates/dtmm/assets/mod_data.lua.j2 | 1 + crates/dtmm/assets/mod_loader.lua | 11 +++++++++-- crates/dtmm/src/controller/deploy.rs | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/assets/mod_data.lua.j2 b/crates/dtmm/assets/mod_data.lua.j2 index 9f87ad1..b5e7f17 100644 --- a/crates/dtmm/assets/mod_data.lua.j2 +++ b/crates/dtmm/assets/mod_data.lua.j2 @@ -4,6 +4,7 @@ return { id = "{{ mod.id }}", name = "{{ mod.name }}", bundled = {{ mod.bundled }}, + version = {{ mod.version }}, packages = { {% for pkg in mod.packages %} "{{ pkg }}", diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua index e000292..126c0eb 100644 --- a/crates/dtmm/assets/mod_loader.lua +++ b/crates/dtmm/assets/mod_loader.lua @@ -254,8 +254,15 @@ ModLoader._build_mod_table = function(self) fassert(table.is_empty(self._mods), "Trying to add mods to non-empty mod table") for i, mod_data in ipairs(self._mod_data) do - Log.info("ModLoader", "mods[%d] = id=%q | name=%q | bundled=%s", i, mod_data.id, mod_data.name, - tostring(mod_data.bundled)) + Log.info( + "ModLoader", + "mods[%d] = id=%q | name=%q | version=%q | bundled=%s", + i, + mod_data.id, + mod_data.name, + mod_data.version, + tostring(mod_data.bundled) + ) self._mods[i] = { id = mod_data.id, diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 957db5d..dcedd5b 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -261,6 +261,7 @@ fn build_mod_data_lua(state: Arc) -> Result { id: String, name: String, bundled: bool, + version: String, init: String, data: Option, localization: Option, @@ -288,6 +289,7 @@ fn build_mod_data_lua(state: Arc) -> Result { id: m.id.clone(), name: m.name.clone(), bundled: m.bundled, + version: m.version.clone(), init: m.resources.init.to_string_lossy().to_string(), data: m .resources From a4e78f1c6b1d83d942ce31ddce0dd1217d863157 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 14:35:43 +0200 Subject: [PATCH 094/184] Log deployment data Closes #168 --- crates/dtmm/assets/mod_main.lua.j2 | 1 + crates/dtmm/src/controller/deploy.rs | 49 +++++++++++++++------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index 4dd2787..29caa79 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -12,6 +12,7 @@ local log = function(category, format, ...) end log("mod_main", "Initializing mods...") +log("mod_main", "[DTMM] Deployment data:\n{{ deployment_info }}") local require_store = {} diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index dcedd5b..e2d9c0e 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -453,7 +453,10 @@ async fn build_bundles(state: Arc) -> Result> { } #[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { +async fn patch_boot_bundle( + state: Arc, + deployment_info: &String, +) -> Result> { let bundle_dir = Arc::new(state.game_dir.join("bundle")); let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); @@ -508,8 +511,9 @@ async fn patch_boot_bundle(state: Arc) -> Result> { .wrap_err("Failed to get template `mod_main.lua`")?; let is_io_enabled = if state.is_io_enabled { "true" } else { "false" }; + let deployment_info = deployment_info.replace("\"", "\\\"").replace("\n", "\\n"); let lua = tmpl - .render(minijinja::context!(is_io_enabled => is_io_enabled)) + .render(minijinja::context!(is_io_enabled => is_io_enabled, deployment_info => deployment_info)) .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); @@ -574,14 +578,10 @@ where } #[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data( - state: Arc, - bundles: B, - mod_folders: Vec, -) -> Result<()> -where - B: AsRef<[Bundle]>, -{ +fn build_deployment_data( + bundles: impl AsRef<[Bundle]>, + mod_folders: impl AsRef<[String]>, +) -> Result { let info = DeploymentData { timestamp: OffsetDateTime::now_utc(), bundles: bundles @@ -590,16 +590,13 @@ where .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) .collect(), // TODO: - mod_folders, + mod_folders: mod_folders + .as_ref() + .iter() + .map(|folder| folder.clone()) + .collect(), }; - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; - - fs::write(&path, &data) - .await - .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; - - Ok(()) + serde_sjson::to_string(&info).wrap_err("Failed to serizalize deployment data") } #[tracing::instrument(skip_all, fields( @@ -728,8 +725,11 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .await .wrap_err("Failed to build mod bundles")?; + let new_deployment_info = build_deployment_data(&bundles, &mod_folders) + .wrap_err("Failed to build new deployment data")?; + tracing::info!("Patch boot bundle"); - let mut boot_bundles = patch_boot_bundle(state.clone()) + let mut boot_bundles = patch_boot_bundle(state.clone(), &new_deployment_info) .await .wrap_err("Failed to patch boot bundle")?; bundles.append(&mut boot_bundles); @@ -804,9 +804,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .wrap_err("Failed to patch bundle database")?; tracing::info!("Writing deployment data"); - write_deployment_data(state.clone(), &bundles, mod_folders) - .await - .wrap_err("Failed to write deployment data")?; + { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + fs::write(&path, &new_deployment_info) + .await + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; + } tracing::info!("Finished deploying mods"); Ok(()) From a47167b7356de45e9539f720c03827df18616508 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 15:58:39 +0200 Subject: [PATCH 095/184] Fix printing hashes with leading zeroes Closes #179 --- lib/sdk/src/murmur/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index 1146494..e96b992 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -50,7 +50,7 @@ impl fmt::LowerHex for Murmur64 { impl fmt::Display for Murmur64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) + write!(f, "{:016X}", self) } } @@ -152,7 +152,7 @@ impl fmt::UpperHex for Murmur32 { impl fmt::Display for Murmur32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) + write!(f, "{:08X}", self) } } From b7e26eee5754ac1272a297e5752d21f861e9e410 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 15:32:41 +0100 Subject: [PATCH 096/184] refactor(sdk): Split BundleFileType into its own file --- lib/sdk/src/bundle/database.rs | 2 +- lib/sdk/src/bundle/file.rs | 397 +------------------------------ lib/sdk/src/bundle/filetype.rs | 400 ++++++++++++++++++++++++++++++++ lib/sdk/src/bundle/mod.rs | 4 +- lib/sdk/src/filetype/package.rs | 11 +- 5 files changed, 408 insertions(+), 406 deletions(-) create mode 100644 lib/sdk/src/bundle/filetype.rs diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index 6152ede..fce26ee 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -13,7 +13,7 @@ use crate::binary::ToBinary; use crate::murmur::Murmur64; use crate::Bundle; -use super::file::BundleFileType; +use super::filetype::BundleFileType; const DATABASE_VERSION: u32 = 0x6; const FILE_VERSION: u32 = 0x4; diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 4d6c56e..1780a5d 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -5,407 +5,12 @@ use bitflags::bitflags; use color_eyre::eyre::Context; use color_eyre::{eyre, Result}; use futures::future::join_all; -use serde::Serialize; use crate::binary::sync::*; use crate::filetype::*; use crate::murmur::{HashGroup, IdString64, Murmur64}; -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] -pub enum BundleFileType { - Animation, - AnimationCurves, - Apb, - BakedLighting, - Bik, - BlendSet, - Bones, - Chroma, - CommonPackage, - Config, - Crypto, - Data, - Entity, - Flow, - Font, - Ies, - Ini, - Input, - Ivf, - Keys, - Level, - Lua, - Material, - Mod, - MouseCursor, - NavData, - NetworkConfig, - OddleNet, - Package, - Particles, - PhysicsProperties, - RenderConfig, - RtPipeline, - Scene, - Shader, - ShaderLibrary, - ShaderLibraryGroup, - ShadingEnvionmentMapping, - ShadingEnvironment, - Slug, - SlugAlbum, - SoundEnvironment, - SpuJob, - StateMachine, - StaticPVS, - Strings, - SurfaceProperties, - Texture, - TimpaniBank, - TimpaniMaster, - Tome, - Ugg, - Unit, - Upb, - VectorField, - Wav, - WwiseBank, - WwiseDep, - WwiseEvent, - WwiseMetadata, - WwiseStream, - Xml, - - Unknown(Murmur64), -} - -impl BundleFileType { - pub fn ext_name(&self) -> String { - match self { - BundleFileType::AnimationCurves => String::from("animation_curves"), - BundleFileType::Animation => String::from("animation"), - BundleFileType::Apb => String::from("apb"), - BundleFileType::BakedLighting => String::from("baked_lighting"), - BundleFileType::Bik => String::from("bik"), - BundleFileType::BlendSet => String::from("blend_set"), - BundleFileType::Bones => String::from("bones"), - BundleFileType::Chroma => String::from("chroma"), - BundleFileType::CommonPackage => String::from("common_package"), - BundleFileType::Config => String::from("config"), - BundleFileType::Crypto => String::from("crypto"), - BundleFileType::Data => String::from("data"), - BundleFileType::Entity => String::from("entity"), - BundleFileType::Flow => String::from("flow"), - BundleFileType::Font => String::from("font"), - BundleFileType::Ies => String::from("ies"), - BundleFileType::Ini => String::from("ini"), - BundleFileType::Input => String::from("input"), - BundleFileType::Ivf => String::from("ivf"), - BundleFileType::Keys => String::from("keys"), - BundleFileType::Level => String::from("level"), - BundleFileType::Lua => String::from("lua"), - BundleFileType::Material => String::from("material"), - BundleFileType::Mod => String::from("mod"), - BundleFileType::MouseCursor => String::from("mouse_cursor"), - BundleFileType::NavData => String::from("nav_data"), - BundleFileType::NetworkConfig => String::from("network_config"), - BundleFileType::OddleNet => String::from("oodle_net"), - BundleFileType::Package => String::from("package"), - BundleFileType::Particles => String::from("particles"), - BundleFileType::PhysicsProperties => String::from("physics_properties"), - BundleFileType::RenderConfig => String::from("render_config"), - BundleFileType::RtPipeline => String::from("rt_pipeline"), - BundleFileType::Scene => String::from("scene"), - BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), - BundleFileType::ShaderLibrary => String::from("shader_library"), - BundleFileType::Shader => String::from("shader"), - BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), - BundleFileType::ShadingEnvironment => String::from("shading_environment"), - BundleFileType::SlugAlbum => String::from("slug_album"), - BundleFileType::Slug => String::from("slug"), - BundleFileType::SoundEnvironment => String::from("sound_environment"), - BundleFileType::SpuJob => String::from("spu_job"), - BundleFileType::StateMachine => String::from("state_machine"), - BundleFileType::StaticPVS => String::from("static_pvs"), - BundleFileType::Strings => String::from("strings"), - BundleFileType::SurfaceProperties => String::from("surface_properties"), - BundleFileType::Texture => String::from("texture"), - BundleFileType::TimpaniBank => String::from("timpani_bank"), - BundleFileType::TimpaniMaster => String::from("timpani_master"), - BundleFileType::Tome => String::from("tome"), - BundleFileType::Ugg => String::from("ugg"), - BundleFileType::Unit => String::from("unit"), - BundleFileType::Upb => String::from("upb"), - BundleFileType::VectorField => String::from("vector_field"), - BundleFileType::Wav => String::from("wav"), - BundleFileType::WwiseBank => String::from("wwise_bank"), - BundleFileType::WwiseDep => String::from("wwise_dep"), - BundleFileType::WwiseEvent => String::from("wwise_event"), - BundleFileType::WwiseMetadata => String::from("wwise_metadata"), - BundleFileType::WwiseStream => String::from("wwise_stream"), - BundleFileType::Xml => String::from("xml"), - - BundleFileType::Unknown(s) => format!("{s:016X}"), - } - } - - pub fn decompiled_ext_name(&self) -> String { - match self { - BundleFileType::Texture => String::from("dds"), - BundleFileType::WwiseBank => String::from("bnk"), - BundleFileType::WwiseStream => String::from("ogg"), - _ => self.ext_name(), - } - } - - pub fn hash(&self) -> Murmur64 { - Murmur64::from(*self) - } -} - -impl std::str::FromStr for BundleFileType { - type Err = color_eyre::Report; - - fn from_str(s: &str) -> Result { - let val = match s { - "animation_curves" => BundleFileType::AnimationCurves, - "animation" => BundleFileType::Animation, - "apb" => BundleFileType::Apb, - "baked_lighting" => BundleFileType::BakedLighting, - "bik" => BundleFileType::Bik, - "blend_set" => BundleFileType::BlendSet, - "bones" => BundleFileType::Bones, - "chroma" => BundleFileType::Chroma, - "common_package" => BundleFileType::CommonPackage, - "config" => BundleFileType::Config, - "crypto" => BundleFileType::Crypto, - "data" => BundleFileType::Data, - "entity" => BundleFileType::Entity, - "flow" => BundleFileType::Flow, - "font" => BundleFileType::Font, - "ies" => BundleFileType::Ies, - "ini" => BundleFileType::Ini, - "input" => BundleFileType::Input, - "ivf" => BundleFileType::Ivf, - "keys" => BundleFileType::Keys, - "level" => BundleFileType::Level, - "lua" => BundleFileType::Lua, - "material" => BundleFileType::Material, - "mod" => BundleFileType::Mod, - "mouse_cursor" => BundleFileType::MouseCursor, - "nav_data" => BundleFileType::NavData, - "network_config" => BundleFileType::NetworkConfig, - "oodle_net" => BundleFileType::OddleNet, - "package" => BundleFileType::Package, - "particles" => BundleFileType::Particles, - "physics_properties" => BundleFileType::PhysicsProperties, - "render_config" => BundleFileType::RenderConfig, - "rt_pipeline" => BundleFileType::RtPipeline, - "scene" => BundleFileType::Scene, - "shader_library_group" => BundleFileType::ShaderLibraryGroup, - "shader_library" => BundleFileType::ShaderLibrary, - "shader" => BundleFileType::Shader, - "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, - "shading_environment" => BundleFileType::ShadingEnvironment, - "slug_album" => BundleFileType::SlugAlbum, - "slug" => BundleFileType::Slug, - "sound_environment" => BundleFileType::SoundEnvironment, - "spu_job" => BundleFileType::SpuJob, - "state_machine" => BundleFileType::StateMachine, - "static_pvs" => BundleFileType::StaticPVS, - "strings" => BundleFileType::Strings, - "surface_properties" => BundleFileType::SurfaceProperties, - "texture" => BundleFileType::Texture, - "timpani_bank" => BundleFileType::TimpaniBank, - "timpani_master" => BundleFileType::TimpaniMaster, - "tome" => BundleFileType::Tome, - "ugg" => BundleFileType::Ugg, - "unit" => BundleFileType::Unit, - "upb" => BundleFileType::Upb, - "vector_field" => BundleFileType::VectorField, - "wav" => BundleFileType::Wav, - "wwise_bank" => BundleFileType::WwiseBank, - "wwise_dep" => BundleFileType::WwiseDep, - "wwise_event" => BundleFileType::WwiseEvent, - "wwise_metadata" => BundleFileType::WwiseMetadata, - "wwise_stream" => BundleFileType::WwiseStream, - "xml" => BundleFileType::Xml, - s => eyre::bail!("Unknown type string '{}'", s), - }; - - Ok(val) - } -} - -impl Serialize for BundleFileType { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let value = self.ext_name(); - value.serialize(serializer) - } -} - -impl From for BundleFileType { - fn from(value: Murmur64) -> Self { - Self::from(Into::::into(value)) - } -} - -impl From for BundleFileType { - fn from(hash: u64) -> BundleFileType { - match hash { - 0x931e336d7646cc26 => BundleFileType::Animation, - 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, - 0x3eed05ba83af5090 => BundleFileType::Apb, - 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, - 0xaa5965f03029fa18 => BundleFileType::Bik, - 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, - 0x18dead01056b72e9 => BundleFileType::Bones, - 0xb7893adf7567506a => BundleFileType::Chroma, - 0xfe9754bd19814a47 => BundleFileType::CommonPackage, - 0x82645835e6b73232 => BundleFileType::Config, - 0x69108ded1e3e634b => BundleFileType::Crypto, - 0x8fd0d44d20650b68 => BundleFileType::Data, - 0x9831ca893b0d087d => BundleFileType::Entity, - 0x92d3ee038eeb610d => BundleFileType::Flow, - 0x9efe0a916aae7880 => BundleFileType::Font, - 0x8f7d5a2c0f967655 => BundleFileType::Ies, - 0xd526a27da14f1dc5 => BundleFileType::Ini, - 0x2bbcabe5074ade9e => BundleFileType::Input, - 0xfa4a8e091a91201e => BundleFileType::Ivf, - 0xa62f9297dc969e85 => BundleFileType::Keys, - 0x2a690fd348fe9ac5 => BundleFileType::Level, - 0xa14e8dfa2cd117e2 => BundleFileType::Lua, - 0xeac0b497876adedf => BundleFileType::Material, - 0x3fcdd69156a46417 => BundleFileType::Mod, - 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, - 0x169de9566953d264 => BundleFileType::NavData, - 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, - 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, - 0xad9c6d9ed1e5e77a => BundleFileType::Package, - 0xa8193123526fad64 => BundleFileType::Particles, - 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, - 0x27862fe24795319c => BundleFileType::RenderConfig, - 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, - 0x9d0a795bfe818d19 => BundleFileType::Scene, - 0xcce8d5b5f5ae333f => BundleFileType::Shader, - 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, - 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, - 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, - 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, - 0xa27b4d04a9ba6f9e => BundleFileType::Slug, - 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, - 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, - 0xf97af9983c05b950 => BundleFileType::SpuJob, - 0xa486d4045106165c => BundleFileType::StateMachine, - 0xe3f0baa17d620321 => BundleFileType::StaticPVS, - 0x0d972bab10b40fd3 => BundleFileType::Strings, - 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, - 0xcd4238c6a0c69e32 => BundleFileType::Texture, - 0x99736be1fff739a4 => BundleFileType::TimpaniBank, - 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, - 0x19c792357c99f49b => BundleFileType::Tome, - 0x712d6e3dd1024c9c => BundleFileType::Ugg, - 0xe0a48d0be9a7453f => BundleFileType::Unit, - 0xa99510c6e86dd3c2 => BundleFileType::Upb, - 0xf7505933166d6755 => BundleFileType::VectorField, - 0x786f65c00a816b19 => BundleFileType::Wav, - 0x535a7bd3e650d799 => BundleFileType::WwiseBank, - 0xaf32095c82f2b070 => BundleFileType::WwiseDep, - 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, - 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, - 0x504b55235d21440e => BundleFileType::WwiseStream, - 0x76015845a6003765 => BundleFileType::Xml, - - _ => BundleFileType::Unknown(Murmur64::from(hash)), - } - } -} - -impl From for u64 { - fn from(t: BundleFileType) -> u64 { - match t { - BundleFileType::Animation => 0x931e336d7646cc26, - BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, - BundleFileType::Apb => 0x3eed05ba83af5090, - BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, - BundleFileType::Bik => 0xaa5965f03029fa18, - BundleFileType::BlendSet => 0xe301e8af94e3b5a3, - BundleFileType::Bones => 0x18dead01056b72e9, - BundleFileType::Chroma => 0xb7893adf7567506a, - BundleFileType::CommonPackage => 0xfe9754bd19814a47, - BundleFileType::Config => 0x82645835e6b73232, - BundleFileType::Crypto => 0x69108ded1e3e634b, - BundleFileType::Data => 0x8fd0d44d20650b68, - BundleFileType::Entity => 0x9831ca893b0d087d, - BundleFileType::Flow => 0x92d3ee038eeb610d, - BundleFileType::Font => 0x9efe0a916aae7880, - BundleFileType::Ies => 0x8f7d5a2c0f967655, - BundleFileType::Ini => 0xd526a27da14f1dc5, - BundleFileType::Input => 0x2bbcabe5074ade9e, - BundleFileType::Ivf => 0xfa4a8e091a91201e, - BundleFileType::Keys => 0xa62f9297dc969e85, - BundleFileType::Level => 0x2a690fd348fe9ac5, - BundleFileType::Lua => 0xa14e8dfa2cd117e2, - BundleFileType::Material => 0xeac0b497876adedf, - BundleFileType::Mod => 0x3fcdd69156a46417, - BundleFileType::MouseCursor => 0xb277b11fe4a61d37, - BundleFileType::NavData => 0x169de9566953d264, - BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, - BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, - BundleFileType::Package => 0xad9c6d9ed1e5e77a, - BundleFileType::Particles => 0xa8193123526fad64, - BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, - BundleFileType::RenderConfig => 0x27862fe24795319c, - BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, - BundleFileType::Scene => 0x9d0a795bfe818d19, - BundleFileType::Shader => 0xcce8d5b5f5ae333f, - BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, - BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, - BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, - BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, - BundleFileType::Slug => 0xa27b4d04a9ba6f9e, - BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, - BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, - BundleFileType::SpuJob => 0xf97af9983c05b950, - BundleFileType::StateMachine => 0xa486d4045106165c, - BundleFileType::StaticPVS => 0xe3f0baa17d620321, - BundleFileType::Strings => 0x0d972bab10b40fd3, - BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, - BundleFileType::Texture => 0xcd4238c6a0c69e32, - BundleFileType::TimpaniBank => 0x99736be1fff739a4, - BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, - BundleFileType::Tome => 0x19c792357c99f49b, - BundleFileType::Ugg => 0x712d6e3dd1024c9c, - BundleFileType::Unit => 0xe0a48d0be9a7453f, - BundleFileType::Upb => 0xa99510c6e86dd3c2, - BundleFileType::VectorField => 0xf7505933166d6755, - BundleFileType::Wav => 0x786f65c00a816b19, - BundleFileType::WwiseBank => 0x535a7bd3e650d799, - BundleFileType::WwiseDep => 0xaf32095c82f2b070, - BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, - BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, - BundleFileType::WwiseStream => 0x504b55235d21440e, - BundleFileType::Xml => 0x76015845a6003765, - - BundleFileType::Unknown(hash) => hash.into(), - } - } -} -impl From for Murmur64 { - fn from(t: BundleFileType) -> Murmur64 { - let hash: u64 = t.into(); - Murmur64::from(hash) - } -} - -impl std::fmt::Display for BundleFileType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.ext_name()) - } -} +use super::filetype::BundleFileType; #[derive(Debug)] struct BundleFileHeader { diff --git a/lib/sdk/src/bundle/filetype.rs b/lib/sdk/src/bundle/filetype.rs new file mode 100644 index 0000000..0b4f292 --- /dev/null +++ b/lib/sdk/src/bundle/filetype.rs @@ -0,0 +1,400 @@ +use color_eyre::{eyre, Result}; +use serde::Serialize; + +use crate::murmur::Murmur64; + +#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] +pub enum BundleFileType { + Animation, + AnimationCurves, + Apb, + BakedLighting, + Bik, + BlendSet, + Bones, + Chroma, + CommonPackage, + Config, + Crypto, + Data, + Entity, + Flow, + Font, + Ies, + Ini, + Input, + Ivf, + Keys, + Level, + Lua, + Material, + Mod, + MouseCursor, + NavData, + NetworkConfig, + OddleNet, + Package, + Particles, + PhysicsProperties, + RenderConfig, + RtPipeline, + Scene, + Shader, + ShaderLibrary, + ShaderLibraryGroup, + ShadingEnvionmentMapping, + ShadingEnvironment, + Slug, + SlugAlbum, + SoundEnvironment, + SpuJob, + StateMachine, + StaticPVS, + Strings, + SurfaceProperties, + Texture, + TimpaniBank, + TimpaniMaster, + Tome, + Ugg, + Unit, + Upb, + VectorField, + Wav, + WwiseBank, + WwiseDep, + WwiseEvent, + WwiseMetadata, + WwiseStream, + Xml, + + Unknown(Murmur64), +} + +impl BundleFileType { + pub fn ext_name(&self) -> String { + match self { + BundleFileType::AnimationCurves => String::from("animation_curves"), + BundleFileType::Animation => String::from("animation"), + BundleFileType::Apb => String::from("apb"), + BundleFileType::BakedLighting => String::from("baked_lighting"), + BundleFileType::Bik => String::from("bik"), + BundleFileType::BlendSet => String::from("blend_set"), + BundleFileType::Bones => String::from("bones"), + BundleFileType::Chroma => String::from("chroma"), + BundleFileType::CommonPackage => String::from("common_package"), + BundleFileType::Config => String::from("config"), + BundleFileType::Crypto => String::from("crypto"), + BundleFileType::Data => String::from("data"), + BundleFileType::Entity => String::from("entity"), + BundleFileType::Flow => String::from("flow"), + BundleFileType::Font => String::from("font"), + BundleFileType::Ies => String::from("ies"), + BundleFileType::Ini => String::from("ini"), + BundleFileType::Input => String::from("input"), + BundleFileType::Ivf => String::from("ivf"), + BundleFileType::Keys => String::from("keys"), + BundleFileType::Level => String::from("level"), + BundleFileType::Lua => String::from("lua"), + BundleFileType::Material => String::from("material"), + BundleFileType::Mod => String::from("mod"), + BundleFileType::MouseCursor => String::from("mouse_cursor"), + BundleFileType::NavData => String::from("nav_data"), + BundleFileType::NetworkConfig => String::from("network_config"), + BundleFileType::OddleNet => String::from("oodle_net"), + BundleFileType::Package => String::from("package"), + BundleFileType::Particles => String::from("particles"), + BundleFileType::PhysicsProperties => String::from("physics_properties"), + BundleFileType::RenderConfig => String::from("render_config"), + BundleFileType::RtPipeline => String::from("rt_pipeline"), + BundleFileType::Scene => String::from("scene"), + BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), + BundleFileType::ShaderLibrary => String::from("shader_library"), + BundleFileType::Shader => String::from("shader"), + BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), + BundleFileType::ShadingEnvironment => String::from("shading_environment"), + BundleFileType::SlugAlbum => String::from("slug_album"), + BundleFileType::Slug => String::from("slug"), + BundleFileType::SoundEnvironment => String::from("sound_environment"), + BundleFileType::SpuJob => String::from("spu_job"), + BundleFileType::StateMachine => String::from("state_machine"), + BundleFileType::StaticPVS => String::from("static_pvs"), + BundleFileType::Strings => String::from("strings"), + BundleFileType::SurfaceProperties => String::from("surface_properties"), + BundleFileType::Texture => String::from("texture"), + BundleFileType::TimpaniBank => String::from("timpani_bank"), + BundleFileType::TimpaniMaster => String::from("timpani_master"), + BundleFileType::Tome => String::from("tome"), + BundleFileType::Ugg => String::from("ugg"), + BundleFileType::Unit => String::from("unit"), + BundleFileType::Upb => String::from("upb"), + BundleFileType::VectorField => String::from("vector_field"), + BundleFileType::Wav => String::from("wav"), + BundleFileType::WwiseBank => String::from("wwise_bank"), + BundleFileType::WwiseDep => String::from("wwise_dep"), + BundleFileType::WwiseEvent => String::from("wwise_event"), + BundleFileType::WwiseMetadata => String::from("wwise_metadata"), + BundleFileType::WwiseStream => String::from("wwise_stream"), + BundleFileType::Xml => String::from("xml"), + + BundleFileType::Unknown(s) => format!("{s:016X}"), + } + } + + pub fn decompiled_ext_name(&self) -> String { + match self { + BundleFileType::Texture => String::from("dds"), + BundleFileType::WwiseBank => String::from("bnk"), + BundleFileType::WwiseStream => String::from("ogg"), + _ => self.ext_name(), + } + } + + pub fn hash(&self) -> Murmur64 { + Murmur64::from(*self) + } +} + +impl std::str::FromStr for BundleFileType { + type Err = color_eyre::Report; + + fn from_str(s: &str) -> Result { + let val = match s { + "animation_curves" => BundleFileType::AnimationCurves, + "animation" => BundleFileType::Animation, + "apb" => BundleFileType::Apb, + "baked_lighting" => BundleFileType::BakedLighting, + "bik" => BundleFileType::Bik, + "blend_set" => BundleFileType::BlendSet, + "bones" => BundleFileType::Bones, + "chroma" => BundleFileType::Chroma, + "common_package" => BundleFileType::CommonPackage, + "config" => BundleFileType::Config, + "crypto" => BundleFileType::Crypto, + "data" => BundleFileType::Data, + "entity" => BundleFileType::Entity, + "flow" => BundleFileType::Flow, + "font" => BundleFileType::Font, + "ies" => BundleFileType::Ies, + "ini" => BundleFileType::Ini, + "input" => BundleFileType::Input, + "ivf" => BundleFileType::Ivf, + "keys" => BundleFileType::Keys, + "level" => BundleFileType::Level, + "lua" => BundleFileType::Lua, + "material" => BundleFileType::Material, + "mod" => BundleFileType::Mod, + "mouse_cursor" => BundleFileType::MouseCursor, + "nav_data" => BundleFileType::NavData, + "network_config" => BundleFileType::NetworkConfig, + "oodle_net" => BundleFileType::OddleNet, + "package" => BundleFileType::Package, + "particles" => BundleFileType::Particles, + "physics_properties" => BundleFileType::PhysicsProperties, + "render_config" => BundleFileType::RenderConfig, + "rt_pipeline" => BundleFileType::RtPipeline, + "scene" => BundleFileType::Scene, + "shader_library_group" => BundleFileType::ShaderLibraryGroup, + "shader_library" => BundleFileType::ShaderLibrary, + "shader" => BundleFileType::Shader, + "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, + "shading_environment" => BundleFileType::ShadingEnvironment, + "slug_album" => BundleFileType::SlugAlbum, + "slug" => BundleFileType::Slug, + "sound_environment" => BundleFileType::SoundEnvironment, + "spu_job" => BundleFileType::SpuJob, + "state_machine" => BundleFileType::StateMachine, + "static_pvs" => BundleFileType::StaticPVS, + "strings" => BundleFileType::Strings, + "surface_properties" => BundleFileType::SurfaceProperties, + "texture" => BundleFileType::Texture, + "timpani_bank" => BundleFileType::TimpaniBank, + "timpani_master" => BundleFileType::TimpaniMaster, + "tome" => BundleFileType::Tome, + "ugg" => BundleFileType::Ugg, + "unit" => BundleFileType::Unit, + "upb" => BundleFileType::Upb, + "vector_field" => BundleFileType::VectorField, + "wav" => BundleFileType::Wav, + "wwise_bank" => BundleFileType::WwiseBank, + "wwise_dep" => BundleFileType::WwiseDep, + "wwise_event" => BundleFileType::WwiseEvent, + "wwise_metadata" => BundleFileType::WwiseMetadata, + "wwise_stream" => BundleFileType::WwiseStream, + "xml" => BundleFileType::Xml, + s => eyre::bail!("Unknown type string '{}'", s), + }; + + Ok(val) + } +} + +impl Serialize for BundleFileType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let value = self.ext_name(); + value.serialize(serializer) + } +} + +impl From for BundleFileType { + fn from(value: Murmur64) -> Self { + Self::from(Into::::into(value)) + } +} + +impl From for BundleFileType { + fn from(hash: u64) -> BundleFileType { + match hash { + 0x931e336d7646cc26 => BundleFileType::Animation, + 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, + 0x3eed05ba83af5090 => BundleFileType::Apb, + 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, + 0xaa5965f03029fa18 => BundleFileType::Bik, + 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, + 0x18dead01056b72e9 => BundleFileType::Bones, + 0xb7893adf7567506a => BundleFileType::Chroma, + 0xfe9754bd19814a47 => BundleFileType::CommonPackage, + 0x82645835e6b73232 => BundleFileType::Config, + 0x69108ded1e3e634b => BundleFileType::Crypto, + 0x8fd0d44d20650b68 => BundleFileType::Data, + 0x9831ca893b0d087d => BundleFileType::Entity, + 0x92d3ee038eeb610d => BundleFileType::Flow, + 0x9efe0a916aae7880 => BundleFileType::Font, + 0x8f7d5a2c0f967655 => BundleFileType::Ies, + 0xd526a27da14f1dc5 => BundleFileType::Ini, + 0x2bbcabe5074ade9e => BundleFileType::Input, + 0xfa4a8e091a91201e => BundleFileType::Ivf, + 0xa62f9297dc969e85 => BundleFileType::Keys, + 0x2a690fd348fe9ac5 => BundleFileType::Level, + 0xa14e8dfa2cd117e2 => BundleFileType::Lua, + 0xeac0b497876adedf => BundleFileType::Material, + 0x3fcdd69156a46417 => BundleFileType::Mod, + 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, + 0x169de9566953d264 => BundleFileType::NavData, + 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, + 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, + 0xad9c6d9ed1e5e77a => BundleFileType::Package, + 0xa8193123526fad64 => BundleFileType::Particles, + 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, + 0x27862fe24795319c => BundleFileType::RenderConfig, + 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, + 0x9d0a795bfe818d19 => BundleFileType::Scene, + 0xcce8d5b5f5ae333f => BundleFileType::Shader, + 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, + 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, + 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, + 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, + 0xa27b4d04a9ba6f9e => BundleFileType::Slug, + 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, + 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, + 0xf97af9983c05b950 => BundleFileType::SpuJob, + 0xa486d4045106165c => BundleFileType::StateMachine, + 0xe3f0baa17d620321 => BundleFileType::StaticPVS, + 0x0d972bab10b40fd3 => BundleFileType::Strings, + 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, + 0xcd4238c6a0c69e32 => BundleFileType::Texture, + 0x99736be1fff739a4 => BundleFileType::TimpaniBank, + 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, + 0x19c792357c99f49b => BundleFileType::Tome, + 0x712d6e3dd1024c9c => BundleFileType::Ugg, + 0xe0a48d0be9a7453f => BundleFileType::Unit, + 0xa99510c6e86dd3c2 => BundleFileType::Upb, + 0xf7505933166d6755 => BundleFileType::VectorField, + 0x786f65c00a816b19 => BundleFileType::Wav, + 0x535a7bd3e650d799 => BundleFileType::WwiseBank, + 0xaf32095c82f2b070 => BundleFileType::WwiseDep, + 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, + 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, + 0x504b55235d21440e => BundleFileType::WwiseStream, + 0x76015845a6003765 => BundleFileType::Xml, + + _ => BundleFileType::Unknown(Murmur64::from(hash)), + } + } +} + +impl From for u64 { + fn from(t: BundleFileType) -> u64 { + match t { + BundleFileType::Animation => 0x931e336d7646cc26, + BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, + BundleFileType::Apb => 0x3eed05ba83af5090, + BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, + BundleFileType::Bik => 0xaa5965f03029fa18, + BundleFileType::BlendSet => 0xe301e8af94e3b5a3, + BundleFileType::Bones => 0x18dead01056b72e9, + BundleFileType::Chroma => 0xb7893adf7567506a, + BundleFileType::CommonPackage => 0xfe9754bd19814a47, + BundleFileType::Config => 0x82645835e6b73232, + BundleFileType::Crypto => 0x69108ded1e3e634b, + BundleFileType::Data => 0x8fd0d44d20650b68, + BundleFileType::Entity => 0x9831ca893b0d087d, + BundleFileType::Flow => 0x92d3ee038eeb610d, + BundleFileType::Font => 0x9efe0a916aae7880, + BundleFileType::Ies => 0x8f7d5a2c0f967655, + BundleFileType::Ini => 0xd526a27da14f1dc5, + BundleFileType::Input => 0x2bbcabe5074ade9e, + BundleFileType::Ivf => 0xfa4a8e091a91201e, + BundleFileType::Keys => 0xa62f9297dc969e85, + BundleFileType::Level => 0x2a690fd348fe9ac5, + BundleFileType::Lua => 0xa14e8dfa2cd117e2, + BundleFileType::Material => 0xeac0b497876adedf, + BundleFileType::Mod => 0x3fcdd69156a46417, + BundleFileType::MouseCursor => 0xb277b11fe4a61d37, + BundleFileType::NavData => 0x169de9566953d264, + BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, + BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, + BundleFileType::Package => 0xad9c6d9ed1e5e77a, + BundleFileType::Particles => 0xa8193123526fad64, + BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, + BundleFileType::RenderConfig => 0x27862fe24795319c, + BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, + BundleFileType::Scene => 0x9d0a795bfe818d19, + BundleFileType::Shader => 0xcce8d5b5f5ae333f, + BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, + BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, + BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, + BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, + BundleFileType::Slug => 0xa27b4d04a9ba6f9e, + BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, + BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, + BundleFileType::SpuJob => 0xf97af9983c05b950, + BundleFileType::StateMachine => 0xa486d4045106165c, + BundleFileType::StaticPVS => 0xe3f0baa17d620321, + BundleFileType::Strings => 0x0d972bab10b40fd3, + BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, + BundleFileType::Texture => 0xcd4238c6a0c69e32, + BundleFileType::TimpaniBank => 0x99736be1fff739a4, + BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, + BundleFileType::Tome => 0x19c792357c99f49b, + BundleFileType::Ugg => 0x712d6e3dd1024c9c, + BundleFileType::Unit => 0xe0a48d0be9a7453f, + BundleFileType::Upb => 0xa99510c6e86dd3c2, + BundleFileType::VectorField => 0xf7505933166d6755, + BundleFileType::Wav => 0x786f65c00a816b19, + BundleFileType::WwiseBank => 0x535a7bd3e650d799, + BundleFileType::WwiseDep => 0xaf32095c82f2b070, + BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, + BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, + BundleFileType::WwiseStream => 0x504b55235d21440e, + BundleFileType::Xml => 0x76015845a6003765, + + BundleFileType::Unknown(hash) => hash.into(), + } + } +} +impl From for Murmur64 { + fn from(t: BundleFileType) -> Murmur64 { + let hash: u64 = t.into(); + Murmur64::from(hash) + } +} + +impl std::fmt::Display for BundleFileType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.ext_name()) + } +} diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 813df06..ca36393 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -12,8 +12,10 @@ use crate::murmur::{HashGroup, IdString64, Murmur64}; pub(crate) mod database; pub(crate) mod file; +pub(crate) mod filetype; -pub use file::{BundleFile, BundleFileType, BundleFileVariant}; +pub use file::{BundleFile, BundleFileVariant}; +pub use filetype::BundleFileType; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] enum BundleFormat { diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 1f50f81..a36719e 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -11,7 +11,8 @@ use path_slash::PathBufExt; use tokio::fs; use crate::binary::sync::{ReadExt, WriteExt}; -use crate::bundle::file::{BundleFileType, UserFile}; +use crate::bundle::file::UserFile; +use crate::bundle::filetype::BundleFileType; use crate::murmur::{HashGroup, Murmur64}; #[tracing::instrument] @@ -280,17 +281,11 @@ where Ok(vec![UserFile::new(s.into_bytes())]) } -// #[tracing::instrument(skip_all)] -// pub fn compile(_ctx: &crate::Context, data: String) -> Result> { -// let pkg = Package::from_sjson(data)?; -// pkg.to_binary() -// } - #[cfg(test)] mod test { use std::path::PathBuf; - use crate::BundleFileType; + use crate::bundle::filetype::BundleFileType; use super::resolve_wildcard; use super::Package; From 95fc6c160b2176770ab54c4380f208bb664b76f4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 4 Oct 2023 09:47:25 +0200 Subject: [PATCH 097/184] dtmt: Implement name overrides For most of the game files, we don't know the actual name, only the hash of that name. To still allow building bundles that contain files with that name (e.g. to override a game file with a custom one), there needs to be a way to tell DTMT to name a file such that its hash is the same as the one in the game. The initial idea was to just expect the file name on disk to be the hash, but that wouldn't allow for arbitrary folder structures anymore. So instead, there is now a new, optional setting in `dtmt.cfg`, where the modder can map a file path to an override name. --- crates/dtmm/src/controller/deploy.rs | 8 ++-- crates/dtmm/src/controller/import.rs | 1 + crates/dtmt/src/cmd/build.rs | 68 ++++++++++++++-------------- crates/dtmt/src/cmd/migrate.rs | 1 + crates/dtmt/src/cmd/watch.rs | 17 +++---- lib/dtmt-shared/src/lib.rs | 3 ++ lib/sdk/src/bundle/file.rs | 26 ++++------- lib/sdk/src/filetype/lua.rs | 13 ++---- lib/sdk/src/filetype/package.rs | 48 +++++++++++--------- lib/sdk/src/murmur/types.rs | 28 ++++++++++-- 10 files changed, 116 insertions(+), 97 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index e2d9c0e..02b6860 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -324,11 +324,11 @@ async fn build_bundles(state: Arc) -> Result> { let mut bundles = Vec::new(); - let mut add_lua_asset = |name, data: &str| { + let mut add_lua_asset = |name: &str, data: &str| { let span = tracing::info_span!("Compiling Lua", name, data_len = data.len()); let _enter = span.enter(); - let file = lua::compile(name, data).wrap_err("Failed to compile Lua")?; + let file = lua::compile(name.to_string(), data).wrap_err("Failed to compile Lua")?; mod_bundle.add_file(file); @@ -517,8 +517,8 @@ async fn patch_boot_bundle( .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); - let file = - lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; + let file = lua::compile(MOD_BOOT_SCRIPT.to_string(), lua) + .wrap_err("Failed to compile mod main Lua file")?; boot_bundle.add_file(file); } diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 8e47d23..2f5f90b 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -297,6 +297,7 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo packages: Vec::new(), resources, depends: Vec::new(), + name_overrides: Default::default(), }; Ok((cfg, root)) diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index 77ab629..fab072b 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -103,38 +103,41 @@ async fn find_project_config(dir: Option) -> Result { } #[tracing::instrument(skip_all)] -async fn compile_package_files

(pkg: &Package, root: P) -> Result> -where - P: AsRef + std::fmt::Debug, -{ - let root = Arc::new(root.as_ref()); +async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result> { + let root = Arc::new(&cfg.dir); + let name_overrides = &cfg.name_overrides; let tasks = pkg .iter() - .flat_map(|(file_type, paths)| { - paths.iter().map(|path| { + .flat_map(|(file_type, names)| { + names.iter().map(|name| { ( *file_type, - path, + name, // Cloning the `Arc` here solves the issue that in the next `.map`, I need to // `move` the closure parameters, but can't `move` `root` before it was cloned. root.clone(), ) }) }) - .map(|(file_type, path, root)| async move { - let sjson = fs::read_to_string(&path).await?; + .map(|(file_type, name, root)| async move { + let path = PathBuf::from(name); + let sjson = fs::read_to_string(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; - let mut path = path.clone(); - path.set_extension(""); - - BundleFile::from_sjson( - path.to_slash_lossy().to_string(), - file_type, - sjson, - root.as_ref(), - ) - .await + let name = path.with_extension("").to_slash_lossy().to_string(); + let name = if let Some(new_name) = name_overrides.get(&name) { + let new_name = match u64::from_str_radix(new_name, 16) { + Ok(hash) => IdString64::from(hash), + Err(_) => IdString64::from(new_name.clone()), + }; + tracing::info!("Overriding '{}' -> '{}'", name, new_name.display()); + new_name + } else { + IdString64::from(name.clone()) + }; + BundleFile::from_sjson(name, file_type, sjson, root.as_ref()).await }); let results = futures::stream::iter(tasks) @@ -146,12 +149,11 @@ where } #[tracing::instrument] -async fn build_package(package: P1, root: P2) -> Result -where - P1: AsRef + std::fmt::Debug, - P2: AsRef + std::fmt::Debug, -{ - let root = root.as_ref(); +async fn build_package( + cfg: &ModConfig, + package: impl AsRef + std::fmt::Debug, +) -> Result { + let root = &cfg.dir; let package = package.as_ref(); let mut path = root.join(package); @@ -165,7 +167,7 @@ where .await .wrap_err_with(|| format!("Invalid package file {}", &pkg_name))?; - let files = compile_package_files(&pkg, root).await?; + let files = compile_package_files(&pkg, cfg).await?; let mut bundle = Bundle::new(pkg_name); for file in files { bundle.add_file(file); @@ -254,14 +256,14 @@ pub(crate) async fn read_project_config(dir: Option) -> Result( +#[tracing::instrument] +pub(crate) async fn build

( cfg: &ModConfig, - out_path: P1, - game_dir: Arc>, + out_path: impl AsRef + std::fmt::Debug, + game_dir: Arc>, ) -> Result<()> where - P1: AsRef, - P2: AsRef, + P: AsRef + std::fmt::Debug, { let out_path = out_path.as_ref(); @@ -286,7 +288,7 @@ where ); } - let bundle = build_package(path, &cfg.dir).await.wrap_err_with(|| { + let bundle = build_package(&cfg, path).await.wrap_err_with(|| { format!( "Failed to build package '{}' at '{}'", path.display(), diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 2eacded..d7bfa19 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -351,6 +351,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> }, depends: vec![ModDependency::ID(String::from("DMF"))], bundled: true, + name_overrides: HashMap::new(), }; tracing::debug!(?dtmt_cfg); diff --git a/crates/dtmt/src/cmd/watch.rs b/crates/dtmt/src/cmd/watch.rs index 79fab67..2abd0f7 100644 --- a/crates/dtmt/src/cmd/watch.rs +++ b/crates/dtmt/src/cmd/watch.rs @@ -77,17 +77,14 @@ pub(crate) fn command_definition() -> Command { ) } -async fn compile( +#[tracing::instrument] +async fn compile( cfg: &ModConfig, - out_path: P1, - archive_path: P2, - game_dir: Arc>, -) -> Result<()> -where - P1: AsRef + std::marker::Copy, - P2: AsRef, - P3: AsRef, -{ + out_path: impl AsRef + std::fmt::Debug, + archive_path: impl AsRef + std::fmt::Debug, + game_dir: Arc + std::fmt::Debug>>, +) -> Result<()> { + let out_path = out_path.as_ref(); build(cfg, out_path, game_dir) .await .wrap_err("Failed to build bundles")?; diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3c9530e..d1f69c7 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::PathBuf; use color_eyre::eyre::{OptionExt as _, WrapErr as _}; @@ -67,6 +68,8 @@ pub struct ModConfig { pub depends: Vec, #[serde(default = "default_true", skip_serializing_if = "is_true")] pub bundled: bool, + #[serde(default)] + pub name_overrides: HashMap, } pub const STEAMAPP_ID: u32 = 1361210; diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 1780a5d..f387409 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -120,7 +120,7 @@ pub struct BundleFile { } impl BundleFile { - pub fn new(name: String, file_type: BundleFileType) -> Self { + pub fn new(name: impl Into, file_type: BundleFileType) -> Self { Self { file_type, name: name.into(), @@ -252,20 +252,15 @@ impl BundleFile { Ok(w.into_inner()) } - #[tracing::instrument(name = "File::from_sjson", skip(sjson))] - pub async fn from_sjson( - name: String, + #[tracing::instrument("File::from_sjson", skip(sjson, name), fields(name = %name.display()))] + pub async fn from_sjson( + name: IdString64, file_type: BundleFileType, - sjson: S, - root: P, - ) -> Result - where - P: AsRef + std::fmt::Debug, - S: AsRef, - { + sjson: impl AsRef, + root: impl AsRef + std::fmt::Debug, + ) -> Result { match file_type { - BundleFileType::Lua => lua::compile(name.clone(), sjson) - .wrap_err_with(|| format!("Failed to compile Lua file '{}'", name)), + BundleFileType::Lua => lua::compile(name, sjson).wrap_err("Failed to compile Lua file"), BundleFileType::Unknown(_) => { eyre::bail!("Unknown file type. Cannot compile from SJSON"); } @@ -304,10 +299,7 @@ impl BundleFile { s } - pub fn matches_name(&self, name: S) -> bool - where - S: Into, - { + pub fn matches_name(&self, name: impl Into) -> bool { let name = name.into(); if self.name == name { return true; diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index bfe6de7..14f0d6b 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -15,6 +15,7 @@ use tokio::fs; use crate::binary::sync::ReadExt; use crate::binary::sync::WriteExt; use crate::bundle::file::{BundleFileVariant, UserFile}; +use crate::murmur::IdString64; use crate::{BundleFile, BundleFileType}; const BITSQUID_LUAJIT_HEADER: u32 = 0x8253461B; @@ -117,17 +118,13 @@ where } #[tracing::instrument(skip_all)] -pub fn compile(name: S, code: C) -> Result -where - S: Into, - C: AsRef, -{ +pub fn compile(name: impl Into, code: impl AsRef) -> Result { let name = name.into(); let code = code.as_ref(); tracing::trace!( "Compiling '{}', {} bytes of code", - name, + name.display(), code.as_bytes().len() ); @@ -135,8 +132,8 @@ where let state = lua::luaL_newstate(); lua::luaL_openlibs(state); - let name = CString::new(format!("@{name}").into_bytes()) - .wrap_err_with(|| format!("Cannot convert name into CString: {}", name))?; + let name = CString::new(format!("@{}", name.display()).into_bytes()) + .wrap_err_with(|| format!("Cannot convert name into CString: {}", name.display()))?; match lua::luaL_loadbuffer( state, code.as_ptr() as _, diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index a36719e..6394e42 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -7,13 +7,12 @@ use std::str::FromStr; use async_recursion::async_recursion; use color_eyre::eyre::{self, Context}; use color_eyre::Result; -use path_slash::PathBufExt; use tokio::fs; use crate::binary::sync::{ReadExt, WriteExt}; use crate::bundle::file::UserFile; use crate::bundle::filetype::BundleFileType; -use crate::murmur::{HashGroup, Murmur64}; +use crate::murmur::{HashGroup, IdString64, Murmur64}; #[tracing::instrument] #[async_recursion] @@ -91,12 +90,12 @@ where Ok(paths) } -type PackageType = HashMap>; +type PackageType = HashMap>; type PackageDefinition = HashMap>; #[derive(Default)] pub struct Package { - _name: String, + _name: IdString64, _root: PathBuf, inner: PackageType, flags: u8, @@ -117,9 +116,9 @@ impl DerefMut for Package { } impl Package { - pub fn new(name: String, root: PathBuf) -> Self { + pub fn new(name: impl Into, root: PathBuf) -> Self { Self { - _name: name, + _name: name.into(), _root: root, inner: Default::default(), flags: 1, @@ -130,17 +129,22 @@ impl Package { self.values().fold(0, |total, files| total + files.len()) } - pub fn add_file>(&mut self, file_type: BundleFileType, name: P) { + pub fn add_file(&mut self, file_type: BundleFileType, name: impl Into) { self.inner.entry(file_type).or_default().insert(name.into()); } #[tracing::instrument("Package::from_sjson", skip(sjson), fields(sjson_len = sjson.as_ref().len()))] - pub async fn from_sjson(sjson: S, name: String, root: P) -> Result + pub async fn from_sjson( + sjson: S, + name: impl Into + std::fmt::Debug, + root: P, + ) -> Result where P: AsRef + std::fmt::Debug, S: AsRef, { let root = root.as_ref(); + let name = name.into(); let definition: PackageDefinition = serde_sjson::from_str(sjson.as_ref())?; let mut inner: PackageType = Default::default(); @@ -174,7 +178,11 @@ impl Package { continue; }; - inner.entry(t).or_default().insert(path); + tracing::debug!("Adding file {}", path.display()); + inner + .entry(t) + .or_default() + .insert(path.display().to_string()); } } } @@ -193,11 +201,9 @@ impl Package { pub fn to_sjson(&self) -> Result { let mut map: PackageDefinition = Default::default(); - for (t, paths) in self.iter() { - for path in paths.iter() { - map.entry(t.ext_name()) - .or_default() - .insert(path.display().to_string()); + for (t, names) in self.iter() { + for name in names.iter() { + map.entry(t.ext_name()).or_default().insert(name.clone()); } } @@ -223,11 +229,11 @@ impl Package { for _ in 0..file_count { let t = BundleFileType::from(r.read_u64()?); let hash = Murmur64::from(r.read_u64()?); - let path = ctx.lookup_hash(hash, HashGroup::Filename); + let name = ctx.lookup_hash(hash, HashGroup::Filename); inner .entry(t) .or_default() - .insert(PathBuf::from(path.display().to_string())); + .insert(name.display().to_string()); } let flags = r.read_u8()?; @@ -240,7 +246,7 @@ impl Package { let pkg = Self { inner, - _name: name, + _name: name.into(), _root: PathBuf::new(), flags, }; @@ -256,12 +262,10 @@ impl Package { w.write_u32(0x2b)?; w.write_u32(self.values().flatten().count() as u32)?; - for (t, paths) in self.iter() { - for path in paths.iter() { + for (t, names) in self.iter() { + for name in names.iter() { w.write_u64(t.hash().into())?; - - let hash = Murmur64::hash(path.to_slash_lossy().as_bytes()); - w.write_u64(hash.into())?; + w.write_u64(Murmur64::hash(name.as_bytes()).into())?; } } diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index e96b992..20bf46a 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -1,3 +1,7 @@ +use std::path::Path; + +use path_slash::PathExt; + use self::util::{parse_hex32, parse_hex64}; use super::*; @@ -263,11 +267,23 @@ impl IdString64 { IdString64::String(_) => false, } } + + // Would love to have this as a proper `impl From`, but + // rustc will complain that it overlaps with the `impl From>`. + pub fn from_path(p: impl AsRef) -> Self { + Self::String(p.as_ref().to_slash_lossy().to_string()) + } } -impl> From for IdString64 { - fn from(value: S) -> Self { - Self::String(value.into()) +impl From for IdString64 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString64 { + fn from(value: u64) -> Self { + Self::Hash(value.into()) } } @@ -283,6 +299,12 @@ impl From for Murmur64 { } } +impl Default for IdString64 { + fn default() -> Self { + Self::Hash(0.into()) + } +} + impl PartialEq for IdString64 { fn eq(&self, other: &Self) -> bool { self.to_murmur64() == other.to_murmur64() From 74a7aaa6e5dacc758a7846ab720999756203bbc6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 16 Sep 2023 18:50:40 +0200 Subject: [PATCH 098/184] dtmt-shared: Write log lines to stderr Ideally, I would prefer the usual split per logging level, but that seems to be somewhat complex with `tracing_subscriber`, so this simply switches everything over to stderr, so that some of the experiment commands can write results to stdout. --- lib/dtmt-shared/src/log.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dtmt-shared/src/log.rs b/lib/dtmt-shared/src/log.rs index ab0b7b5..9c95c63 100644 --- a/lib/dtmt-shared/src/log.rs +++ b/lib/dtmt-shared/src/log.rs @@ -84,7 +84,7 @@ pub fn create_tracing_subscriber() { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::try_new("info").unwrap()); let (dev_stdout_layer, prod_stdout_layer, filter_layer) = if cfg!(debug_assertions) { - let fmt_layer = fmt::layer().pretty(); + let fmt_layer = fmt::layer().pretty().with_writer(std::io::stderr); (Some(fmt_layer), None, None) } else { // Creates a layer that @@ -93,6 +93,7 @@ pub fn create_tracing_subscriber() { // - does not print spans/targets // - only prints time, not date let fmt_layer = fmt::layer() + .with_writer(std::io::stderr) .event_format(Formatter) .fmt_fields(debug_fn(format_fields)); From edad0d44930d306c5e6493db920ff97c7160e66c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 30 Aug 2023 18:44:24 +0200 Subject: [PATCH 099/184] Improve file listing output Adds pretty printing for file size and always shows the bundle hash name --- crates/dtmt/src/cmd/bundle/list.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/list.rs b/crates/dtmt/src/cmd/bundle/list.rs index dd72ad2..558126b 100644 --- a/crates/dtmt/src/cmd/bundle/list.rs +++ b/crates/dtmt/src/cmd/bundle/list.rs @@ -36,6 +36,18 @@ enum OutputFormat { Text, } +fn format_byte_size(size: usize) -> String { + if size < 1024 { + format!("{} Bytes", size) + } else if size < 1024 * 1024 { + format!("{} kB", size / 1024) + } else if size < 1024 * 1024 * 1024 { + format!("{} MB", size / (1024 * 1024)) + } else { + format!("{} GB", size / (1024 * 1024 * 1024)) + } +} + #[tracing::instrument(skip(ctx))] async fn print_bundle_contents

(ctx: &sdk::Context, path: P, fmt: OutputFormat) -> Result<()> where @@ -50,7 +62,11 @@ where match fmt { OutputFormat::Text => { - println!("Bundle: {}", bundle.name().display()); + println!( + "Bundle: {} ({:016x})", + bundle.name().display(), + bundle.name() + ); for f in bundle.files().iter() { if f.variants().len() != 1 { @@ -63,9 +79,10 @@ where let v = &f.variants()[0]; println!( - "\t{}.{}: {} bytes", + "\t{}.{}: {} ({})", f.base_name().display(), f.file_type().ext_name(), + format_byte_size(v.size()), v.size() ); } From 08219f05ba81b16bd34afa82ef37f99d32d65e3b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 31 Aug 2023 17:14:54 +0200 Subject: [PATCH 100/184] sdk: Fix reading strings Fatshark has a few weird string fields, where they provide a length field, but then sometimes write a shorter, NUL-terminated string into that same field and adding padding up to the "advertised" length. To properly read those strings, we can't rely on just the length field anymore, but need to check for a NUL, too. --- lib/sdk/src/binary.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 1fcc90e..9348e1b 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -43,6 +43,7 @@ impl FromBinary for Vec { } pub mod sync { + use std::ffi::CStr; use std::io::{self, Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -165,25 +166,13 @@ pub mod sync { } fn read_string_len(&mut self, len: usize) -> Result { - let mut buf = vec![0; len]; - let res = self - .read_exact(&mut buf) - .map_err(Report::new) - .and_then(|_| { - String::from_utf8(buf).map_err(|err| { - let ascii = String::from_utf8_lossy(err.as_bytes()).to_string(); - let bytes = format!("{:?}", err.as_bytes()); - Report::new(err) - .with_section(move || bytes.header("Bytes:")) - .with_section(move || ascii.header("ASCII:")) - }) - }); + let pos = self.stream_position(); + let res = read_string_len(self, len); if res.is_ok() { return res; } - let pos = self.stream_position(); if pos.is_ok() { res.with_section(|| { format!("{pos:#X} ({pos})", pos = pos.unwrap()).header("Position: ") @@ -243,4 +232,22 @@ pub mod sync { Err(err).with_section(|| format!("{pos:#X} ({pos})").header("Position: ")) } + + fn read_string_len(mut r: impl Read, len: usize) -> Result { + let mut buf = vec![0; len]; + r.read_exact(&mut buf) + .wrap_err_with(|| format!("Failed to read {} bytes", len))?; + + let res = match CStr::from_bytes_until_nul(&buf) { + Ok(s) => { + let s = s.to_str()?; + Ok(s.to_string()) + } + Err(_) => String::from_utf8(buf.clone()).map_err(Report::new), + }; + + res.wrap_err("Invalid binary for UTF8 string") + .with_section(|| format!("{}", String::from_utf8_lossy(&buf)).header("ASCI:")) + .with_section(|| format!("{:x?}", buf).header("Bytes:")) + } } From c997489e18de825300a9660f48973ff932663ddc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:35:39 +0200 Subject: [PATCH 101/184] Add some doc comments --- crates/dtmt/src/cmd/build.rs | 7 +++++++ lib/sdk/src/filetype/package.rs | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index fab072b..74a627d 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -55,6 +55,7 @@ pub(crate) fn command_definition() -> Command { ) } +/// Try to find a `dtmt.cfg` in the given directory or traverse up the parents. #[tracing::instrument] async fn find_project_config(dir: Option) -> Result { let (path, mut file) = if let Some(path) = dir { @@ -102,6 +103,8 @@ async fn find_project_config(dir: Option) -> Result { Ok(cfg) } +/// Iterate over the paths in the given `Package` and +/// compile each file by its file type. #[tracing::instrument(skip_all)] async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result> { let root = Arc::new(&cfg.dir); @@ -148,6 +151,8 @@ async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result>(path: P) -> Result { let path = path.as_ref(); diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 6394e42..758f79f 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -14,6 +14,15 @@ use crate::bundle::file::UserFile; use crate::bundle::filetype::BundleFileType; use crate::murmur::{HashGroup, IdString64, Murmur64}; +/// Resolves a relative path that might contain wildcards into a list of +/// paths that exist on disk and match that wildcard. +/// This is similar to globbing in Unix shells, but with much less features. +/// +/// The only wilcard character allowed is `*`, and only at the end of the string, +/// where it matches all files recursively in that directory. +/// +/// `t` is an optional extension name, that may be used to force a wildcard +/// path to only match that file type `t`. #[tracing::instrument] #[async_recursion] async fn resolve_wildcard( From f1f9a818cc4dc006f2d8e8512564b4322d8ef1da Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:37:37 +0200 Subject: [PATCH 102/184] sdk: Allow any byte stream for hashing dictionary entries --- lib/sdk/src/murmur/dictionary.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sdk/src/murmur/dictionary.rs b/lib/sdk/src/murmur/dictionary.rs index 2d51af1..267f0a4 100644 --- a/lib/sdk/src/murmur/dictionary.rs +++ b/lib/sdk/src/murmur/dictionary.rs @@ -147,14 +147,14 @@ impl Dictionary { Ok(()) } - pub fn add(&mut self, value: String, group: HashGroup) { - let long = Murmur64::from(murmurhash64::hash(value.as_bytes(), SEED as u64)); - let short = Murmur32::from(murmurhash64::hash32(value.as_bytes(), SEED)); + pub fn add(&mut self, value: impl AsRef<[u8]>, group: HashGroup) { + let long = Murmur64::from(murmurhash64::hash(value.as_ref(), SEED as u64)); + let short = Murmur32::from(murmurhash64::hash32(value.as_ref(), SEED)); let entry = Entry { long, short, - value, + value: String::from_utf8_lossy(value.as_ref()).to_string(), group, }; From 3a6e954f9a45ac14df3ccb797ac86a797fb22ba4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 19 Jul 2024 11:30:09 +0200 Subject: [PATCH 103/184] sdk: Refactor murmur modules and add IdString32 --- lib/sdk/src/murmur/idstring32.rs | 162 ++++++++++++++++++++++++++++ lib/sdk/src/murmur/idstring64.rs | 175 +++++++++++++++++++++++++++++++ lib/sdk/src/murmur/mod.rs | 4 + lib/sdk/src/murmur/types.rs | 173 ++---------------------------- 4 files changed, 347 insertions(+), 167 deletions(-) create mode 100644 lib/sdk/src/murmur/idstring32.rs create mode 100644 lib/sdk/src/murmur/idstring64.rs diff --git a/lib/sdk/src/murmur/idstring32.rs b/lib/sdk/src/murmur/idstring32.rs new file mode 100644 index 0000000..99ea7aa --- /dev/null +++ b/lib/sdk/src/murmur/idstring32.rs @@ -0,0 +1,162 @@ +use std::fmt; + +use serde::{Deserializer, Serializer}; + +use super::Murmur32; + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString32 { + Hash(Murmur32), + String(String), +} + +impl IdString32 { + pub fn to_murmur32(&self) -> Murmur32 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur32::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString32Display { + let s = match self { + IdString32::Hash(hash) => hash.to_string(), + IdString32::String(s) => s.clone(), + }; + + IdString32Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString32::Hash(_) => false, + IdString32::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString32::Hash(_) => true, + IdString32::String(_) => false, + } + } +} + +impl From for IdString32 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString32 { + fn from(value: u32) -> Self { + Self::Hash(value.into()) + } +} + +impl From for u32 { + fn from(value: IdString32) -> Self { + value.to_murmur32().into() + } +} + +impl From for IdString32 { + fn from(value: Murmur32) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur32 { + fn from(value: IdString32) -> Self { + value.to_murmur32() + } +} + +impl PartialEq for IdString32 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur32() == other.to_murmur32() + } +} + +impl std::hash::Hash for IdString32 { + fn hash(&self, state: &mut H) { + state.write_u32(self.to_murmur32().into()); + } +} + +impl serde::Serialize for IdString32 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.to_murmur32().into()) + } +} + +struct IdString32Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString32Visitor { + type Value = IdString32; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u32 or a string") + } + + fn visit_u32(self, value: u32) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u32(IdString32Visitor) + } +} + +pub struct IdString32Display(String); + +impl std::fmt::Display for IdString32Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur32(), f) + } +} + +impl std::fmt::LowerHex for IdString32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur32(), f) + } +} diff --git a/lib/sdk/src/murmur/idstring64.rs b/lib/sdk/src/murmur/idstring64.rs new file mode 100644 index 0000000..781a0cd --- /dev/null +++ b/lib/sdk/src/murmur/idstring64.rs @@ -0,0 +1,175 @@ +use std::{fmt, path::Path}; + +use path_slash::PathExt as _; +use serde::{Deserializer, Serializer}; + +use super::Murmur64; + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString64 { + Hash(Murmur64), + String(String), +} + +impl IdString64 { + pub fn to_murmur64(&self) -> Murmur64 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur64::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString64Display { + let s = match self { + IdString64::Hash(hash) => hash.to_string(), + IdString64::String(s) => s.clone(), + }; + + IdString64Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString64::Hash(_) => false, + IdString64::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString64::Hash(_) => true, + IdString64::String(_) => false, + } + } + + // Would love to have this as a proper `impl From`, but + // rustc will complain that it overlaps with the `impl From>`. + pub fn from_path(p: impl AsRef) -> Self { + Self::String(p.as_ref().to_slash_lossy().to_string()) + } +} + +impl From for IdString64 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString64 { + fn from(value: u64) -> Self { + Self::Hash(value.into()) + } +} + +impl From for IdString64 { + fn from(value: Murmur64) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur64 { + fn from(value: IdString64) -> Self { + value.to_murmur64() + } +} + +impl From for u64 { + fn from(value: IdString64) -> Self { + value.to_murmur64().into() + } +} + +impl Default for IdString64 { + fn default() -> Self { + Self::Hash(0.into()) + } +} + +impl PartialEq for IdString64 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur64() == other.to_murmur64() + } +} + +impl std::hash::Hash for IdString64 { + fn hash(&self, state: &mut H) { + state.write_u64(self.to_murmur64().into()); + } +} + +impl serde::Serialize for IdString64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.to_murmur64().into()) + } +} + +struct IdString64Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString64Visitor { + type Value = IdString64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u64 or a string") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u64(IdString64Visitor) + } +} + +pub struct IdString64Display(String); + +impl std::fmt::Display for IdString64Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur64(), f) + } +} + +impl std::fmt::LowerHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur64(), f) + } +} diff --git a/lib/sdk/src/murmur/mod.rs b/lib/sdk/src/murmur/mod.rs index 87a8473..6449d38 100644 --- a/lib/sdk/src/murmur/mod.rs +++ b/lib/sdk/src/murmur/mod.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; mod dictionary; // Currently unused // mod murmurhash32; +mod idstring32; +mod idstring64; mod murmurhash64; mod types; mod util; @@ -15,6 +17,8 @@ mod util; pub const SEED: u32 = 0; pub use dictionary::{Dictionary, Entry, HashGroup}; +pub use idstring32::*; +pub use idstring64::*; pub use murmurhash64::hash; pub use murmurhash64::hash32; pub use murmurhash64::hash_inverse as inverse; diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index 20bf46a..c66e2cf 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -1,7 +1,3 @@ -use std::path::Path; - -use path_slash::PathExt; - use self::util::{parse_hex32, parse_hex64}; use super::*; @@ -154,6 +150,12 @@ impl fmt::UpperHex for Murmur32 { } } +impl fmt::LowerHex for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + impl fmt::Display for Murmur32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:08X}", self) @@ -222,166 +224,3 @@ impl<'de> Deserialize<'de> for Murmur32 { deserializer.deserialize_any(Self(0)) } } - -// This type encodes the fact that when reading in a bundle, we don't always have a dictionary -// entry for every hash in there. So we do want to have the real string available when needed, -// but at the same time retain the original hash information for when we don't. -// This is especially important when wanting to write back the read bundle, as the hashes need to -// stay the same. -// The previous system of always turning hashes into strings worked well for the purpose of -// displaying hashes, but would have made it very hard to turn a stringyfied hash back into -// an actual hash. -#[derive(Clone, Debug, Eq)] -pub enum IdString64 { - Hash(Murmur64), - String(String), -} - -impl IdString64 { - pub fn to_murmur64(&self) -> Murmur64 { - match self { - Self::Hash(hash) => *hash, - Self::String(s) => Murmur64::hash(s.as_bytes()), - } - } - - pub fn display(&self) -> IdString64Display { - let s = match self { - IdString64::Hash(hash) => hash.to_string(), - IdString64::String(s) => s.clone(), - }; - - IdString64Display(s) - } - - pub fn is_string(&self) -> bool { - match self { - IdString64::Hash(_) => false, - IdString64::String(_) => true, - } - } - - pub fn is_hash(&self) -> bool { - match self { - IdString64::Hash(_) => true, - IdString64::String(_) => false, - } - } - - // Would love to have this as a proper `impl From`, but - // rustc will complain that it overlaps with the `impl From>`. - pub fn from_path(p: impl AsRef) -> Self { - Self::String(p.as_ref().to_slash_lossy().to_string()) - } -} - -impl From for IdString64 { - fn from(value: String) -> Self { - Self::String(value) - } -} - -impl From for IdString64 { - fn from(value: u64) -> Self { - Self::Hash(value.into()) - } -} - -impl From for IdString64 { - fn from(value: Murmur64) -> Self { - Self::Hash(value) - } -} - -impl From for Murmur64 { - fn from(value: IdString64) -> Self { - value.to_murmur64() - } -} - -impl Default for IdString64 { - fn default() -> Self { - Self::Hash(0.into()) - } -} - -impl PartialEq for IdString64 { - fn eq(&self, other: &Self) -> bool { - self.to_murmur64() == other.to_murmur64() - } -} - -impl std::hash::Hash for IdString64 { - fn hash(&self, state: &mut H) { - state.write_u64(self.to_murmur64().into()); - } -} - -impl serde::Serialize for IdString64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u64(self.to_murmur64().into()) - } -} - -struct IdString64Visitor; - -impl<'de> serde::de::Visitor<'de> for IdString64Visitor { - type Value = IdString64; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an u64 or a string") - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::Hash(value.into())) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v.to_string())) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v)) - } -} - -impl<'de> serde::Deserialize<'de> for IdString64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_u64(IdString64Visitor) - } -} - -pub struct IdString64Display(String); - -impl std::fmt::Display for IdString64Display { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::UpperHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::UpperHex::fmt(&self.to_murmur64(), f) - } -} - -impl std::fmt::LowerHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::LowerHex::fmt(&self.to_murmur64(), f) - } -} From dbf060032b015a9211f475339b8fbfcae234c3d4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 14:46:10 +0200 Subject: [PATCH 104/184] sdk: Implement bundle database resource hashes Algorithm reverse engineered by WhiteGoat. --- lib/sdk/src/bundle/database.rs | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index fce26ee..d9e0f03 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -36,6 +36,25 @@ pub struct BundleDatabase { bundle_contents: HashMap>, } +// Implements the partial Murmur that's used by the engine to compute bundle resource hashes, +// but in a way that the loop can be done outside the function. +#[inline(always)] +fn add_to_resource_hash(mut k: u64, name: impl Into) -> u64 { + const M: u64 = 0xc6a4a7935bd1e995; + const R: u64 = 47; + + let mut h: u64 = name.into(); + + k = k.wrapping_mul(M); + k ^= k >> R; + k = k.wrapping_mul(M); + + h ^= k; + k = M.wrapping_mul(h); + + k +} + impl BundleDatabase { pub fn add_bundle(&mut self, bundle: &Bundle) { let hash = bundle.name().to_murmur64(); @@ -69,20 +88,26 @@ impl BundleDatabase { } } + let mut resource_hash = 0; + for f in bundle.files() { + let name = f.base_name().to_murmur64(); let file_name = FileName { extension: f.file_type(), - name: f.base_name().to_murmur64(), + name, }; - // TODO: Compute actual resource hash - self.resource_hashes.insert(hash, 0); + resource_hash = add_to_resource_hash(resource_hash, name); + // TODO: Make sure each file name only exists once. Probably best to turn + // the `Vec` into a sorted `HashSet`. self.bundle_contents .entry(hash) .or_default() .push(file_name); } + + self.resource_hashes.insert(hash, resource_hash); } } @@ -103,7 +128,7 @@ impl FromBinary for BundleDatabase { let mut stored_files = HashMap::with_capacity(num_entries); for _ in 0..num_entries { - let hash = Murmur64::from(r.read_u64()?); + let hash = r.read_u64().map(Murmur64::from)?; let num_files = r.read_u32()? as usize; let mut files = Vec::with_capacity(num_files); @@ -161,7 +186,7 @@ impl FromBinary for BundleDatabase { let mut resource_hashes = HashMap::with_capacity(num_hashes); for _ in 0..num_hashes { - let name = Murmur64::from(r.read_u64()?); + let name = r.read_u64().map(Murmur64::from)?; let hash = r.read_u64()?; resource_hashes.insert(name, hash); @@ -171,14 +196,14 @@ impl FromBinary for BundleDatabase { let mut bundle_contents = HashMap::with_capacity(num_contents); for _ in 0..num_contents { - let hash = Murmur64::from(r.read_u64()?); + let hash = r.read_u64().map(Murmur64::from)?; let num_files = r.read_u32()? as usize; let mut files = Vec::with_capacity(num_files); for _ in 0..num_files { - let extension = BundleFileType::from(r.read_u64()?); - let name = Murmur64::from(r.read_u64()?); + let extension = r.read_u64().map(BundleFileType::from)?; + let name = r.read_u64().map(Murmur64::from)?; files.push(FileName { extension, name }); } From 7fa08c2efd10ae87f9bcf588d508bf1631eea845 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 17:55:10 +0200 Subject: [PATCH 105/184] dtmt: Implement listing bundle database contents --- CHANGELOG.adoc | 1 + crates/dtmt/src/cmd/bundle/db.rs | 129 ++++++++++++++++++++++++++++++ crates/dtmt/src/cmd/bundle/mod.rs | 3 + lib/sdk/src/bundle/database.rs | 20 +++-- 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 crates/dtmt/src/cmd/bundle/db.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index db30865..c5ba065 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -20,6 +20,7 @@ - dtmm: fetch file version for Nexus mods - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod - dtmm: Add button to open mod on nexusmods.com +- dtmt: Implement commands to list bundles and contents === Fixed diff --git a/crates/dtmt/src/cmd/bundle/db.rs b/crates/dtmt/src/cmd/bundle/db.rs new file mode 100644 index 0000000..6d4da59 --- /dev/null +++ b/crates/dtmt/src/cmd/bundle/db.rs @@ -0,0 +1,129 @@ +use std::{io::Cursor, path::PathBuf}; + +use clap::{value_parser, Arg, ArgMatches, Command}; +use color_eyre::{eyre::Context as _, Result}; +use sdk::murmur::{HashGroup, IdString64, Murmur64}; +use sdk::{BundleDatabase, FromBinary as _}; +use tokio::fs; + +pub(crate) fn command_definition() -> Command { + Command::new("db") + .about("Various operations regarding `bundle_database.data`.") + .subcommand_required(true) + .subcommand( + Command::new("list-files") + .about("List bundle contents") + .arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("bundle") + .help("The bundle name. If omitted, all bundles will be listed.") + .required(false), + ), + ) + .subcommand( + Command::new("list-bundles").about("List bundles").arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ), + ) +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let Some((op, sub_matches)) = matches.subcommand() else { + unreachable!("clap is configured to require a subcommand"); + }; + + let database = { + let path = sub_matches + .get_one::("database") + .expect("argument is required"); + + let binary = fs::read(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + + let mut r = Cursor::new(binary); + + BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")? + }; + + match op { + "list-files" => { + let index = database.files(); + + if let Some(bundle) = sub_matches.get_one::("bundle") { + let hash = u64::from_str_radix(bundle, 16) + .map(Murmur64::from) + .wrap_err("Invalid hex sequence")?; + + if let Some(files) = index.get(&hash) { + for file in files { + let name = ctx.lookup_hash(file.name, HashGroup::Filename); + let extension = file.extension.ext_name(); + println!("{}.{}", name.display(), extension); + } + } else { + tracing::info!("Bundle {} not found in the database", bundle); + } + } else { + for (bundle_hash, files) in index.iter() { + let bundle_name = ctx.lookup_hash(*bundle_hash, HashGroup::Filename); + + match bundle_name { + IdString64::String(name) => { + println!("{:016X} {}", bundle_hash, name); + } + IdString64::Hash(hash) => { + println!("{:016X}", hash); + } + } + + for file in files { + let name = ctx.lookup_hash(file.name, HashGroup::Filename); + let extension = file.extension.ext_name(); + + match name { + IdString64::String(name) => { + println!("\t{:016X}.{:<12} {}", file.name, extension, name); + } + IdString64::Hash(hash) => { + println!("\t{:016X}.{}", hash, extension); + } + } + } + + println!(); + } + } + + Ok(()) + } + "list-bundles" => { + for bundle_hash in database.bundles().keys() { + let bundle_name = ctx.lookup_hash(*bundle_hash, HashGroup::Filename); + + match bundle_name { + IdString64::String(name) => { + println!("{:016X} {}", bundle_hash, name); + } + IdString64::Hash(hash) => { + println!("{:016X}", hash); + } + } + } + + Ok(()) + } + _ => unreachable!( + "clap is configured to require a subcommand, and they're all handled above" + ), + } +} diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 0e7c9f7..c5145e4 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -1,6 +1,7 @@ use clap::{ArgMatches, Command}; use color_eyre::eyre::Result; +mod db; mod decompress; mod extract; mod inject; @@ -14,6 +15,7 @@ pub(crate) fn command_definition() -> Command { .subcommand(extract::command_definition()) .subcommand(inject::command_definition()) .subcommand(list::command_definition()) + .subcommand(db::command_definition()) } #[tracing::instrument(skip_all)] @@ -23,6 +25,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { Some(("extract", sub_matches)) => extract::run(ctx, sub_matches).await, Some(("inject", sub_matches)) => inject::run(ctx, sub_matches).await, Some(("list", sub_matches)) => list::run(ctx, sub_matches).await, + Some(("db", sub_matches)) => db::run(ctx, sub_matches).await, _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" ), diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index d9e0f03..185c62f 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -19,15 +19,15 @@ const DATABASE_VERSION: u32 = 0x6; const FILE_VERSION: u32 = 0x4; pub struct BundleFile { - name: String, - stream: String, - platform_specific: bool, - file_time: u64, + pub name: String, + pub stream: String, + pub platform_specific: bool, + pub file_time: u64, } pub struct FileName { - extension: BundleFileType, - name: Murmur64, + pub extension: BundleFileType, + pub name: Murmur64, } pub struct BundleDatabase { @@ -56,6 +56,14 @@ fn add_to_resource_hash(mut k: u64, name: impl Into) -> u64 { } impl BundleDatabase { + pub fn bundles(&self) -> &HashMap> { + &self.stored_files + } + + pub fn files(&self) -> &HashMap> { + &self.bundle_contents + } + pub fn add_bundle(&mut self, bundle: &Bundle) { let hash = bundle.name().to_murmur64(); let name = hash.to_string(); From d931e6b9cada6fdce92595a27a96b7e7428f7356 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 22:02:01 +0200 Subject: [PATCH 106/184] dtmt: Add command to search for files Not really of much use at the moment, but inspired by the HD2 community. --- CHANGELOG.adoc | 1 + crates/dtmt/src/cmd/bundle/db.rs | 57 ++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c5ba065..1bfb0dd 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -21,6 +21,7 @@ - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod - dtmm: Add button to open mod on nexusmods.com - dtmt: Implement commands to list bundles and contents +- dtmt: Implement command to search for files === Fixed diff --git a/crates/dtmt/src/cmd/bundle/db.rs b/crates/dtmt/src/cmd/bundle/db.rs index 6d4da59..b537991 100644 --- a/crates/dtmt/src/cmd/bundle/db.rs +++ b/crates/dtmt/src/cmd/bundle/db.rs @@ -33,6 +33,21 @@ pub(crate) fn command_definition() -> Command { .value_parser(value_parser!(PathBuf)), ), ) + .subcommand( + Command::new("find-file") + .about("Find the bundle a file belongs to") + .arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("file-name") + .required(true) + .help("Name of the file. May be a hash in hex representation or a string"), + ), + ) } #[tracing::instrument(skip_all)] @@ -79,10 +94,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016X} {}", bundle_hash, name); + println!("{:016x} {}", bundle_hash, name); } IdString64::Hash(hash) => { - println!("{:016X}", hash); + println!("{:016x}", hash); } } @@ -92,10 +107,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match name { IdString64::String(name) => { - println!("\t{:016X}.{:<12} {}", file.name, extension, name); + println!("\t{:016x}.{:<12} {}", file.name, extension, name); } IdString64::Hash(hash) => { - println!("\t{:016X}.{}", hash, extension); + println!("\t{:016x}.{}", hash, extension); } } } @@ -112,16 +127,46 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016X} {}", bundle_hash, name); + println!("{:016x} {}", bundle_hash, name); } IdString64::Hash(hash) => { - println!("{:016X}", hash); + println!("{:016x}", hash); } } } Ok(()) } + "find-file" => { + let name = sub_matches + .get_one::("file-name") + .expect("required argument"); + let name = match u64::from_str_radix(name, 16).map(Murmur64::from) { + Ok(hash) => hash, + Err(_) => Murmur64::hash(name), + }; + + let bundles = database.files().iter().filter_map(|(bundle_hash, files)| { + if files.iter().any(|file| file.name == name) { + Some(bundle_hash) + } else { + None + } + }); + + let mut found = false; + + for bundle in bundles { + found = true; + println!("{:016x}", bundle); + } + + if !found { + std::process::exit(1); + } + + Ok(()) + } _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" ), From 2a1d8d815f3af3cde183183636f55698b8cc5ee2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 14 Aug 2024 09:22:24 +0200 Subject: [PATCH 107/184] Add tests for hash inversion Just a quick round trip test, and an additional assert to demonstrate that byte order does matter. --- lib/sdk/src/murmur/murmurhash64.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/sdk/src/murmur/murmurhash64.rs b/lib/sdk/src/murmur/murmurhash64.rs index f15248c..ca69852 100644 --- a/lib/sdk/src/murmur/murmurhash64.rs +++ b/lib/sdk/src/murmur/murmurhash64.rs @@ -119,4 +119,9 @@ fn test_hash() { } #[test] -fn test_inverse() {} +fn test_inverse() { + let h = hash("lua".as_bytes(), crate::murmur::SEED as u64); + let inv = hash_inverse(h, crate::murmur::SEED as u64); + assert_eq!(h, hash(&inv.to_le_bytes(), crate::murmur::SEED as u64)); + assert_ne!(h, hash(&inv.to_be_bytes(), crate::murmur::SEED as u64)); +} From e3362400943a318db323cf46a8fe9981e8501b99 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 20 Aug 2024 16:28:08 +0200 Subject: [PATCH 108/184] Consilidate template libraries Remove last uses of `string_template` in favor of `minijinja`. Closes #124. --- Cargo.lock | 11 +------- Cargo.toml | 1 + crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/new.rs | 54 ++++++++++++++++++++++---------------- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a251de9..ce13303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "futures-util", "glob", "luajit2-sys", + "minijinja", "nanorand", "notify", "oodle", @@ -948,7 +949,6 @@ dependencies = [ "serde", "serde_sjson", "shlex", - "string_template", "tempfile", "tokio", "tokio-stream", @@ -3307,15 +3307,6 @@ dependencies = [ "float-cmp", ] -[[package]] -name = "string_template" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f2c6b2c3fa950895c9aeb0c3cb9271d7eb580662af9af2b711b593f446320" -dependencies = [ - "regex", -] - [[package]] name = "strip-ansi-escapes" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f38ac22..62d1fd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["lib/color-eyre"] [workspace.dependencies] zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +minijinja = { version = "2.0.1", default-features = false } [patch.crates-io] color-eyre = { path = "lib/color-eyre" } diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index f947fc6..b7bee49 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -27,7 +27,7 @@ futures = "0.3.25" interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -minijinja = { version = "2.0.1", default-features = false } +minijinja = { workspace = true } nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } open = "5.0.1" diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index d836a50..e2e3fb9 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -20,7 +20,7 @@ promptly = "0.3.1" sdk = { path = "../../lib/sdk", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } serde = { version = "1.0.147", features = ["derive"] } -string_template = "0.2.1" +minijinja = { workspace = true } tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } tracing-error = "0.2.0" diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index eb6a4f9..1993ea2 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -1,11 +1,10 @@ -use std::collections::HashMap; use std::path::PathBuf; use clap::{Arg, ArgMatches, Command}; use color_eyre::eyre::{self, Context, Result}; use color_eyre::Help; use futures::{StreamExt, TryStreamExt}; -use string_template::Template; +use minijinja::Environment; use tokio::fs::{self, DirBuilder}; const TEMPLATES: [(&str, &str); 5] = [ @@ -137,34 +136,45 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::debug!(root = %root.display(), name, id); - let mut data = HashMap::new(); - data.insert("name", name.as_str()); - data.insert("id", id.as_str()); + let render_ctx = minijinja::context!(name => name.as_str(), id => id.as_str()); + let env = Environment::new(); let templates = TEMPLATES .iter() .map(|(path_tmpl, content_tmpl)| { - let path = Template::new(path_tmpl).render(&data); - let content = Template::new(content_tmpl).render(&data); - - (root.join(path), content) + env.render_str(path_tmpl, &render_ctx) + .wrap_err_with(|| format!("Failed to render template: {}", path_tmpl)) + .and_then(|path| { + env.render_named_str(&path, content_tmpl, &render_ctx) + .wrap_err_with(|| format!("Failed to render template '{}'", &path)) + .map(|content| (root.join(path), content)) + }) }) - .map(|(path, content)| async move { - let dir = path - .parent() - .ok_or_else(|| eyre::eyre!("invalid root path"))?; + .map(|res| async move { + match res { + Ok((path, content)) => { + let dir = path + .parent() + .ok_or_else(|| eyre::eyre!("invalid root path"))?; - DirBuilder::new() - .recursive(true) - .create(&dir) - .await - .wrap_err_with(|| format!("Failed to create directory {}", dir.display()))?; + DirBuilder::new() + .recursive(true) + .create(&dir) + .await + .wrap_err_with(|| { + format!("Failed to create directory {}", dir.display()) + })?; - tracing::trace!("Writing file {}", path.display()); + tracing::trace!("Writing file {}", path.display()); - fs::write(&path, content.as_bytes()) - .await - .wrap_err_with(|| format!("Failed to write content to path {}", path.display())) + fs::write(&path, content.as_bytes()) + .await + .wrap_err_with(|| { + format!("Failed to write content to path {}", path.display()) + }) + } + Err(e) => Err(e), + } }); futures::stream::iter(templates) From df2992a4768e7c6f582049e45089141cd69ec7bd Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 20 Aug 2024 16:28:56 +0200 Subject: [PATCH 109/184] Improve mod template comments --- crates/dtmt/src/cmd/new.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 1993ea2..571b0cb 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -10,8 +10,21 @@ use tokio::fs::{self, DirBuilder}; const TEMPLATES: [(&str, &str); 5] = [ ( "dtmt.cfg", - r#"id = "{{id}}" + r#"// +// This is your mod's main configuration file. It tells DTMT how to build the mod, +// and DTMM what to display to your users. +// Certain files have been pre-filled by the template, the ones commented out (`//`) +// are optional. +// +// A unique identifier (preferably lower case, alphanumeric) +id = "{{id}}" +// The display name that your users will see. +// This doesn't have to be unique, but you still want to avoid being confused with other +// mods. name = "{{name}}" +// It's good practice to increase this number whenever you publish changes. +// It's up to you if you use SemVer or something simpler like `1970-12-24`. It should sort and +// compare well, though. version = "0.1.0" // author = "" @@ -31,16 +44,25 @@ categories = [ // A list of mod IDs that this mod depends on. You can find // those IDs by downloading the mod and extracting their `dtmt.cfg`. +// To make your fellow modders' lives easier, publish your own mods' IDs +// somewhere visible, such as the Nexusmods page. depends = [ DMF ] +// The primary resources that serve as the entry point to your +// mod's code. Unless for very specific use cases, the generated +// values shouldn't be changed. resources = { init = "scripts/mods/{{id}}/init" data = "scripts/mods/{{id}}/data" localization = "scripts/mods/{{id}}/localization" } +// The list of packages, or bundles, to build. +// Each one corresponds to a package definition in the named folder. +// For mods that contain only code and/or a few small assets, a single +// package will suffice. packages = [ "packages/mods/{{id}}" ] @@ -58,7 +80,6 @@ packages = [ r#"local mod = get_mod("{{id}}") -- Your mod code goes here. --- https://vmf-docs.verminti.de "#, ), ( From a2bbab1398e6f2caecd14a10efc564d0ef95d7c4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 21 Aug 2024 14:25:41 +0200 Subject: [PATCH 110/184] Update dependencies --- Cargo.lock | 870 +++++++++++++++++++++---------------- Cargo.toml | 49 ++- crates/dtmm/Cargo.toml | 60 +-- crates/dtmt/Cargo.toml | 54 +-- lib/color-eyre | 2 +- lib/dtmt-shared/Cargo.toml | 16 +- lib/luajit2-sys | 2 +- lib/oodle/Cargo.toml | 6 +- lib/oodle/src/lib.rs | 1 + lib/sdk/Cargo.toml | 40 +- 10 files changed, 639 insertions(+), 461 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce13303..26ccff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -29,7 +35,8 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.1" -source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#bfcd2689677fa93ce72c55833e891af36c65b2aa" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" dependencies = [ "heapless", "nom", @@ -46,9 +53,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -61,33 +68,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -95,9 +102,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -110,15 +117,15 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "associative-cache" @@ -134,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -161,6 +168,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -169,15 +182,15 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -205,16 +218,14 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools", - "lazy_static", - "lazycell", "log", "prettyplease", "proc-macro2", @@ -222,8 +233,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.63", - "which", + "syn 2.0.75", ] [[package]] @@ -234,9 +244,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmaps" @@ -276,9 +286,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" [[package]] name = "byteorder" @@ -288,9 +298,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" @@ -340,13 +350,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -376,9 +386,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -387,9 +397,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -397,9 +407,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -411,27 +421,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "cli-table" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbb116d9e2c4be7011360d0c0bee565712c11e969c9609b25b619366dc379d" +checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" dependencies = [ "cli-table-derive", "termcolor", @@ -440,9 +450,9 @@ dependencies = [ [[package]] name = "cli-table-derive" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af3bfb9da627b0a6c467624fb7963921433774ed435493b5c08a3053e829ad4" +checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" dependencies = [ "proc-macro2", "quote", @@ -530,9 +540,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "colors-transform" @@ -549,7 +559,7 @@ dependencies = [ "directories", "serde", "thiserror", - "toml 0.8.13", + "toml 0.8.19", ] [[package]] @@ -574,9 +584,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -616,9 +626,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -649,9 +659,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -721,7 +731,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -793,15 +803,21 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "druid" version = "0.8.3" @@ -890,7 +906,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.5.0", + "bitflags 2.6.0", "clap", "color-eyre", "colors-transform", @@ -986,9 +1002,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" @@ -1079,24 +1095,24 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -1147,11 +1163,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" dependencies = [ - "roxmltree 0.19.0", + "roxmltree 0.20.0", ] [[package]] @@ -1261,7 +1277,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -1386,9 +1402,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gio" @@ -1544,15 +1560,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -1604,15 +1620,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "1.1.0" @@ -1626,9 +1633,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -1636,12 +1643,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http", "http-body", "pin-project-lite", @@ -1649,15 +1656,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", @@ -1673,6 +1680,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -1691,9 +1715,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -1756,9 +1780,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -1786,9 +1810,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -1798,10 +1822,11 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" dependencies = [ + "doctest-file", "libc", "recvmsg", "widestring", @@ -1854,15 +1879,15 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -1875,9 +1900,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -1890,9 +1915,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -1969,30 +1994,24 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2001,15 +2020,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lockfree-object-pool" @@ -2019,9 +2039,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "luajit2-sys" @@ -2059,9 +2079,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2097,10 +2117,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minijinja" -version = "2.0.1" +name = "minicov" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7165d0e94806d52ad5295e4b54a95176d831814840bc067298ca647e1c956338" +checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +dependencies = [ + "cc", + "walkdir", +] + +[[package]] +name = "minijinja" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf369fce3289017a63e514dfca10a0a6f1a02216b21b588b79f6a1081eb999f5" dependencies = [ "serde", ] @@ -2113,14 +2143,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -2133,6 +2172,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -2141,11 +2192,10 @@ checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2223,7 +2273,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2231,7 +2281,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 0.8.11", "walkdir", "windows-sys 0.48.0", ] @@ -2261,16 +2311,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.7" @@ -2291,9 +2331,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -2315,9 +2355,9 @@ dependencies = [ [[package]] name = "open" -version = "5.1.2" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" dependencies = [ "is-wsl", "libc", @@ -2326,11 +2366,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -2347,7 +2387,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2358,9 +2398,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -2465,9 +2505,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -2476,9 +2516,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -2486,22 +2526,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "pest_meta" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -2618,7 +2658,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2649,7 +2689,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.7.4", ] [[package]] @@ -2675,7 +2715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2714,9 +2754,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2778,18 +2818,18 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -2798,14 +2838,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2819,13 +2859,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2836,15 +2876,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", @@ -2856,6 +2896,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -2879,7 +2920,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.52.0", + "windows-registry", ] [[package]] @@ -2902,13 +2943,28 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.37" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "roxmltree" version = "0.15.1" @@ -2920,9 +2976,9 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rustc-demangle" @@ -2951,7 +3007,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2959,10 +3015,23 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "2.1.2" +name = "rustls" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -2970,9 +3039,20 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] [[package]] name = "rustybuzz" @@ -3055,7 +3135,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "color-eyre", "csv-async", @@ -3078,11 +3158,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3091,9 +3171,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3122,31 +3202,32 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -3162,9 +3243,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -3272,6 +3353,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3289,7 +3376,7 @@ dependencies = [ "keyvalues-parser", "keyvalues-serde", "serde", - "winreg 0.51.0", + "winreg", ] [[package]] @@ -3322,6 +3409,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "svgfilters" version = "0.4.0" @@ -3354,9 +3447,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -3365,26 +3458,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3399,26 +3495,27 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.13", + "toml 0.8.19", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3432,22 +3529,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3520,18 +3617,18 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -3544,32 +3641,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "num_cpus", + "mio 1.0.2", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3582,6 +3678,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -3617,21 +3724,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -3649,15 +3756,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.18", ] [[package]] @@ -3673,20 +3780,19 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -3694,7 +3800,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3708,7 +3813,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3941,15 +4046,21 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -3991,9 +4102,9 @@ checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -4015,9 +4126,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vte" @@ -4031,9 +4142,9 @@ dependencies = [ [[package]] name = "vte_generate_state_changes" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", @@ -4066,34 +4177,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -4103,9 +4215,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4113,31 +4225,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" dependencies = [ "console_error_panic_hook", "js-sys", + "minicov", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", @@ -4146,20 +4259,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -4171,18 +4284,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "widestring" version = "1.1.0" @@ -4207,11 +4308,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4220,6 +4321,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4235,7 +4366,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -4255,18 +4395,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -4277,9 +4417,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -4289,9 +4429,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -4301,15 +4441,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -4319,9 +4459,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -4331,9 +4471,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -4343,9 +4483,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -4355,9 +4495,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -4370,9 +4510,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4387,16 +4527,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winres" version = "0.1.12" @@ -4440,10 +4570,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] -name = "zip" -version = "2.1.3" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zip" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "arbitrary", "bzip2", @@ -4475,27 +4611,27 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.1.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 62d1fd8..9e08de5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,51 @@ members = [ exclude = ["lib/color-eyre"] [workspace.dependencies] -zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } -minijinja = { version = "2.0.1", default-features = false } - -[patch.crates-io] +ansi-parser = "0.9.1" +ansi_term = "0.12.1" +async-recursion = "1.0.5" +bincode = "1.3.3" +bitflags = "2.5.0" +byteorder = "1.4.3" +clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } +cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } -ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless", version = "0.9.1" } +colors-transform = "0.2.11" +confy = "0.6.1" +csv-async = { version = "1.2.4", features = ["tokio", "serde"] } +druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } +druid-widget-nursery = "0.1" +dtmt-shared = { path = "lib/dtmt-shared" } +fastrand = "2.1.0" +futures = "0.3.25" +futures-util = "0.3.24" +glob = "0.3.0" +interprocess = "2.1.0" +lazy_static = "1.4.0" +luajit2-sys = { path = "lib/luajit2-sys" } +minijinja = { version = "2.0.1", default-features = false } +nanorand = "0.7.0" +nexusmods = { path = "lib/nexusmods" } +notify = "6.1.1" +oodle = { path = "lib/oodle" } +open = "5.0.1" +path-clean = "1.0.1" +path-slash = "0.2.1" +pin-project-lite = "0.2.9" +promptly = "0.3.1" +sdk = { path = "lib/sdk" } +serde = { version = "1.0.152", features = ["derive", "rc"] } +serde_sjson = { path = "lib/serde_sjson" } +steamlocate = "2.0.0-beta.2" +strip-ansi-escapes = "0.2.0" +time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset", "formatting", "macros"] } +tokio = { version = "1.23.0", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } +tokio-stream = { version = "0.1.12", features = ["fs", "io-util"] } +tracing = { version = "0.1.37", features = ["async-await"] } +tracing-error = "0.2.0" +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +usvg = "0.25.0" +zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index b7bee49..52c0522 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -12,37 +12,37 @@ license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi-parser = "0.9.0" -async-recursion = "1.0.5" -bincode = "1.3.3" -bitflags = "2.5.0" -clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } -color-eyre = "0.6.2" -colors-transform = "0.2.11" -confy = "0.6.1" -druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } -druid-widget-nursery = "0.1" -dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } -futures = "0.3.25" -interprocess = "2.1.0" -lazy_static = "1.4.0" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +ansi-parser = { workspace = true } +async-recursion = { workspace = true } +bincode = { workspace = true } +bitflags = { workspace = true } +clap = { workspace = true } +color-eyre = { workspace = true } +colors-transform = { workspace = true } +confy = { workspace = true } +druid = { workspace = true } +druid-widget-nursery = { workspace = true } +dtmt-shared = { workspace = true } +futures = { workspace = true } +interprocess = { workspace = true } +lazy_static = { workspace = true } +luajit2-sys = { workspace = true } minijinja = { workspace = true } -nexusmods = { path = "../../lib/nexusmods", version = "*" } -oodle = { path = "../../lib/oodle", version = "*" } -open = "5.0.1" -path-slash = "0.2.1" -sdk = { path = "../../lib/sdk", version = "*" } -serde = { version = "1.0.152", features = ["derive", "rc"] } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -strip-ansi-escapes = "0.2.0" -time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } -tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } -tokio-stream = { version = "0.1.12", features = ["fs"] } -tracing = "0.1.37" -tracing-error = "0.2.0" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -usvg = "0.25.0" +nexusmods = { workspace = true } +oodle = { workspace = true } +open = { workspace = true } +path-slash = { workspace = true } +sdk = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +strip-ansi-escapes = { workspace = true } +time = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } +usvg = { workspace = true } zip = { workspace = true } [build-dependencies] diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index e2e3fb9..183d6a5 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -4,34 +4,36 @@ version = "0.3.0" edition = "2021" [dependencies] -clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "unicode"] } -cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } -color-eyre = "0.6.2" -confy = "0.6.1" -csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } -futures = "0.3.25" -futures-util = "0.3.24" -glob = "0.3.0" -nanorand = "0.7.0" -oodle = { path = "../../lib/oodle", version = "*" } -pin-project-lite = "0.2.9" -promptly = "0.3.1" -sdk = { path = "../../lib/sdk", version = "*" } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -serde = { version = "1.0.147", features = ["derive"] } +async-recursion = { workspace = true } +clap = { workspace = true } +cli-table = { workspace = true } +color-eyre = { workspace = true } +confy = { workspace = true } +csv-async = { workspace = true } +dtmt-shared = { workspace = true } +futures = { workspace = true } +futures-util = { workspace = true } +glob = { workspace = true } +luajit2-sys = { workspace = true } minijinja = { workspace = true } -tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } -tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } -tracing-error = "0.2.0" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -tracing = { version = "0.1.37", features = ["async-await"] } +nanorand = { workspace = true } +notify = { workspace = true } +oodle = { workspace = true } +path-clean = { workspace = true } +path-slash = { workspace = true } +pin-project-lite = { workspace = true } +promptly = { workspace = true } +sdk = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } zip = { workspace = true } -path-clean = "1.0.1" -path-slash = "0.2.1" -async-recursion = "1.0.2" -notify = "6.1.1" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } + +# Cannot be a workspace dependencies when it's optional shlex = { version = "1.2.0", optional = true } [dev-dependencies] diff --git a/lib/color-eyre b/lib/color-eyre index b40962a..228b8ca 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit b40962a61c748756d7da293d9fff26aca019603e +Subproject commit 228b8ca37ee79ab9afa45c40da415e4dcb029751 diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index b547dbe..26e1b6a 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -6,11 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi_term = "0.12.1" -color-eyre = "0.6.2" -serde = "1.0.152" -steamlocate = "2.0.0-beta.2" -time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } -tracing = "0.1.37" -tracing-error = "0.2.0" -tracing-subscriber = "0.3.16" +ansi_term = { workspace = true } +color-eyre = { workspace = true } +serde = { workspace = true } +steamlocate = { workspace = true } +time = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 5d1a075..6d94a4d 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 5d1a075742395f767c79d9c0d7466c6fb442f106 +Subproject commit 6d94a4dd2c296bf1f044ee4c70fb10dca4c1c241 diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index 6fc5039..feb9951 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -color-eyre = "0.6.2" -tracing = "0.1.37" +color-eyre = { workspace = true } +tracing = { workspace = true } [build-dependencies] -bindgen = "0.69.4" +bindgen = "0.70.1" diff --git a/lib/oodle/src/lib.rs b/lib/oodle/src/lib.rs index 76b1d16..871daab 100644 --- a/lib/oodle/src/lib.rs +++ b/lib/oodle/src/lib.rs @@ -7,6 +7,7 @@ use std::ptr; use color_eyre::{eyre, Result}; #[allow(dead_code)] +#[allow(clippy::identity_op)] mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index b164d47..4667a1c 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -4,23 +4,23 @@ version = "0.3.0" edition = "2021" [dependencies] -bitflags = "2.5.0" -byteorder = "1.4.3" -color-eyre = "0.6.2" -csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -fastrand = "2.1.0" -futures = "0.3.25" -futures-util = "0.3.24" -glob = "0.3.0" -nanorand = "0.7.0" -pin-project-lite = "0.2.9" -serde = { version = "1.0.147", features = ["derive"] } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -oodle = { path = "../../lib/oodle", version = "*" } -tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } -tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } -tracing = { version = "0.1.37", features = ["async-await"] } -tracing-error = "0.2.0" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -async-recursion = "1.0.2" -path-slash = "0.2.1" +async-recursion = { workspace = true } +bitflags = { workspace = true } +byteorder = { workspace = true } +color-eyre = { workspace = true } +csv-async = { workspace = true } +fastrand = { workspace = true } +futures = { workspace = true } +futures-util = { workspace = true } +glob = { workspace = true } +luajit2-sys = { workspace = true } +nanorand = { workspace = true } +oodle = { workspace = true } +path-slash = { workspace = true } +pin-project-lite = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } From 7cb44532b23582e3e37969f1d8337c25ce45b1ae Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 Aug 2024 12:31:14 +0000 Subject: [PATCH 111/184] Add .renovaterc --- .renovaterc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .renovaterc diff --git a/.renovaterc b/.renovaterc new file mode 100644 index 0000000..b36f3b4 --- /dev/null +++ b/.renovaterc @@ -0,0 +1,11 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":combinePatchMinorReleases", + ":enableVulnerabilityAlerts", + ":rebaseStalePrs" + ], + "prConcurrentLimit": 10, + "branchPrefix": "renovate/" +} From 4d665200fa36905fe453c3e312f5abf50633f903 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 23 Aug 2024 21:30:32 +0000 Subject: [PATCH 112/184] fix(deps): update rust crate serde_json to v1.0.127 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ccff7..5b85e79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3222,9 +3222,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", "memchr", From ffd4927d27140365948cddc00cbda2ba398bb022 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 24 Aug 2024 10:02:43 +0000 Subject: [PATCH 113/184] chore(deps): update rust crate fastrand to v2.1.1 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b85e79..4789666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1059,9 +1059,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fd-lock" From 67c64bb3579b31073f38a57d4cd4208d665bb378 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 26 Aug 2024 20:45:40 +0000 Subject: [PATCH 114/184] chore(deps): update rust crate minijinja to v2.2.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4789666..4add151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2128,9 +2128,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf369fce3289017a63e514dfca10a0a6f1a02216b21b588b79f6a1081eb999f5" +checksum = "6d7d3e3a3eece1fa4618237ad41e1de855ced47eab705cec1c9a920e1d1c5aad" dependencies = [ "serde", ] From 659b63bfe998da90fcab4e88d4a94c7b8653bd1b Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 27 Aug 2024 06:30:39 +0000 Subject: [PATCH 115/184] fix(deps): update rust crate serde to v1.0.209 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4add151..e187021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3202,18 +3202,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", From 72ce06b0e5dbe695842340604941bc69b00c8437 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 25 Oct 2024 17:32:26 +0000 Subject: [PATCH 116/184] chore(deps): update rust crate notify to v7 --- Cargo.lock | 47 ++++++++++++++++++----------------------------- Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e187021..7e460c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,15 +657,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1790,9 +1781,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "inotify-sys", @@ -2160,18 +2151,6 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.2" @@ -2180,6 +2159,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", + "log", "wasi", "windows-sys 0.52.0", ] @@ -2269,21 +2249,30 @@ dependencies = [ [[package]] name = "notify" -version = "6.1.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" dependencies = [ "bitflags 2.6.0", - "crossbeam-channel", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", "log", - "mio 0.8.11", + "mio", + "notify-types", "walkdir", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", ] [[package]] @@ -3648,7 +3637,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio", "pin-project-lite", "signal-hook-registry", "socket2", diff --git a/Cargo.toml b/Cargo.toml index 9e08de5..0ff601e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } -notify = "6.1.1" +notify = "7.0.0" oodle = { path = "lib/oodle" } open = "5.0.1" path-clean = "1.0.1" From b219e20f3adb62f0dc8f4717484af6bdbd723416 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 6 Dec 2024 20:32:37 +0000 Subject: [PATCH 117/184] chore(deps): update rust crate bindgen to 0.71.0 --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++------ lib/oodle/Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e460c7..6f6bc4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -231,7 +231,27 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.75", +] + +[[package]] +name = "bindgen" +version = "0.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.0", "shlex", "syn 2.0.75", ] @@ -1122,7 +1142,7 @@ dependencies = [ "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash", + "rustc-hash 1.1.0", "self_cell 0.10.3", "smallvec", "unic-langid", @@ -2038,7 +2058,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "luajit2-sys" version = "0.0.2" dependencies = [ - "bindgen", + "bindgen 0.70.1", "cc", "fs_extra", "libc", @@ -2337,7 +2357,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.71.0", "color-eyre", "tracing", ] @@ -2981,6 +3001,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3883,7 +3909,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index feb9951..e679b5f 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = { workspace = true } tracing = { workspace = true } [build-dependencies] -bindgen = "0.70.1" +bindgen = "0.71.0" From adf9610eccb6d277fd962f4850de78d273502d00 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 10 Jan 2025 14:48:31 +0000 Subject: [PATCH 118/184] chore(deps): update rust crate notify to v8 --- Cargo.lock | 45 +++++++++++++++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f6bc4f..67e6323 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools", @@ -242,7 +242,7 @@ version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools", @@ -264,9 +264,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.6.0", + "bitflags 2.7.0", "clap", "color-eyre", "colors-transform", @@ -1801,11 +1801,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.7.0", "inotify-sys", "libc", ] @@ -2031,7 +2031,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", "redox_syscall", ] @@ -2269,11 +2269,11 @@ dependencies = [ [[package]] name = "notify" -version = "7.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "filetime", "fsevent-sys", "inotify", @@ -2283,17 +2283,14 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "notify-types" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" -dependencies = [ - "instant", -] +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "nu-ansi-term" @@ -2379,7 +2376,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg-if", "foreign-types", "libc", @@ -2831,7 +2828,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -3022,7 +3019,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", "linux-raw-sys", @@ -3150,7 +3147,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.6.0", + "bitflags 2.7.0", "byteorder", "color-eyre", "csv-async", @@ -3177,7 +3174,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "core-foundation-sys", "libc", @@ -3486,7 +3483,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "system-configuration-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 0ff601e..3f00fe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } -notify = "7.0.0" +notify = "8.0.0" oodle = { path = "lib/oodle" } open = "5.0.1" path-clean = "1.0.1" From a3583b4485196e3446ccd51da401b0ebe5860811 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 10 Dec 2024 10:18:35 +0000 Subject: [PATCH 119/184] fix(deps): update rust crate thiserror to v2 --- Cargo.lock | 90 ++++++++++++++++++++++++---------------- lib/nexusmods/Cargo.toml | 2 +- lib/nexusmods/src/lib.rs | 4 +- 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f6bc4f..ccb479f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -253,7 +253,7 @@ dependencies = [ "regex", "rustc-hash 2.1.0", "shlex", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -354,7 +354,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -448,7 +448,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -532,7 +532,7 @@ dependencies = [ "once_cell", "owo-colors", "pretty_assertions", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-error", "tracing-subscriber", @@ -578,7 +578,7 @@ checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" dependencies = [ "directories", "serde", - "thiserror", + "thiserror 1.0.63", "toml 0.8.19", ] @@ -742,7 +742,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -820,7 +820,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -1163,7 +1163,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1288,7 +1288,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -1434,7 +1434,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1469,7 +1469,7 @@ dependencies = [ "libc", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1950,7 +1950,7 @@ checksum = "7e4c8354918309196302015ac9cae43362f1a13d0d5c5539a33b4c2fd2cd6d25" dependencies = [ "pest", "pest_derive", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1961,7 +1961,7 @@ checksum = "0447866c47c00f8bd1949618e8f63017cf93e985b4684dc28d784527e2882390" dependencies = [ "keyvalues-parser", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2217,7 +2217,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 2.0.6", "time", "tokio", "tracing", @@ -2396,7 +2396,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2519,7 +2519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.63", "ucd-trie", ] @@ -2543,7 +2543,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2667,7 +2667,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2724,7 +2724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2763,9 +2763,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2842,7 +2842,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3232,7 +3232,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -3462,9 +3462,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3548,7 +3548,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -3559,7 +3568,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -3680,7 +3700,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -3828,7 +3848,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -4212,7 +4232,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -4246,7 +4266,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4280,7 +4300,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -4604,7 +4624,7 @@ dependencies = [ "flate2", "indexmap", "memchr", - "thiserror", + "thiserror 1.0.63", "time", "zopfli", "zstd", diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index 2f90a46..b9cc879 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -12,7 +12,7 @@ regex = "1.7.1" reqwest = { version = "0.12.4" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" -thiserror = "1.0.39" +thiserror = "2.0.0" time = { version = "0.3.20", features = ["serde"] } tracing = "0.1.37" url = { version = "2.3.1", features = ["serde"] } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 314acb1..cddf6a0 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -28,7 +28,7 @@ pub enum Error { HTTP(#[from] reqwest::Error), #[error("invalid URL: {0:?}")] URLParseError(#[from] url::ParseError), - #[error("failed to deserialize '{error}': {json}")] + #[error("failed to deserialize due to {error}: {json}")] Deserialize { json: String, error: serde_json::Error, @@ -37,7 +37,7 @@ pub enum Error { InvalidHeaderValue(#[from] InvalidHeaderValue), #[error("this error cannot happen")] Infallible(#[from] Infallible), - #[error("invalid NXM URL '{}': {0}", .1.as_str())] + #[error("invalid NXM URL '{url}': {0}", url = .1.as_str())] InvalidNXM(&'static str, Url), #[error("{0}")] Custom(String), From 5612e271fbde6b8808cc8d4c318099bec124f7b1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:26:24 +0100 Subject: [PATCH 120/184] Improve version name for CI artifacts built off master The name from `git describe --tags` is rather confusing to people that aren't familiar with it. Especially in the current situation, where there are no proper versioned releases. A name like `master-123456` should be much clearer. Closes #205. --- .ci/tasks/build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index bb96775..11b0300 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -25,7 +25,8 @@ if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else - ref=$(git describe --tags) + ref=$(cat .git/ref || echo "HEAD") + ref=$(git rev-parse --abbrev-ref $ref)-$(git rev-parse --short $ref) fi title "Version: '$ref'" From beba47f340ea5f1990458029752ca0b138f226a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:33:48 +0100 Subject: [PATCH 121/184] Push a packaged with a fixed version for master To provide something that can easily be linked to, also push packages built from `master` to a version that doesn't contain the SHA. --- .ci/pipelines/base.yml | 38 +++++++++++++++++++++++++++++++------- .ci/tasks/build.yml | 1 - 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 474c090..8d3cc77 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -125,8 +125,6 @@ jobs: vars: pr: "" target: msvc - gitea_url: http://forgejo:3000 - gitea_api_key: ((gitea_api_key)) - load_var: version_number reveal: true @@ -142,10 +140,21 @@ jobs: fail_fast: true override: true globs: - - artifact/dtmt - - artifact/dtmm - artifact/*.exe - - artifact/*.sha256 + - artifact/*.exe.sha256 + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: master + fail_fast: true + override: true + globs: + - artifact/*.exe + - artifact/*.exe.sha256 - name: build-linux on_success: @@ -202,5 +211,20 @@ jobs: globs: - artifact/dtmt - artifact/dtmm - - artifact/*.exe - - artifact/*.sha256 + - artifact/dtmm.sha256 + - artifact/dtmt.sha256 + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: master + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/dtmm.sha256 + - artifact/dtmt.sha256 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index dce44d0..a7f47b4 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -22,7 +22,6 @@ caches: params: CI: "true" TARGET: ((target)) - GITEA_API_KEY: ((gitea_api_key)) PR: ((pr)) OUTPUT: artifact From d15f533e19edbde4062eeb2161418b90fdb859b0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:52:53 +0100 Subject: [PATCH 122/184] Fix branch name in package version --- .ci/tasks/build.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 11b0300..3cb9776 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -26,7 +26,11 @@ if [ -n "$PR" ]; then ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else ref=$(cat .git/ref || echo "HEAD") - ref=$(git rev-parse --abbrev-ref $ref)-$(git rev-parse --short $ref) + branch=$(git rev-parse --abbrev-ref $ref) + if [ -z "$branch" ]; then + branch=$(cat .git/ref) + fi + ref=${branch}-$(git rev-parse --short $ref) fi title "Version: '$ref'" From 71f945a96c95742c4655c5b05ff4b7696905bf3c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 13:11:01 +0100 Subject: [PATCH 123/184] Explicitly define base branches Currently, the dependency dashboard lists a bunch of pending updates under a section called "Other branches". I'm not sure, but this sounds like one of the configs I extend from enables base branches other than only the default. To test, and make it explicit, set only the branches I really want checked. I'm adding the `release/.*` regex for now, even though I don't have any release process yet. --- .renovaterc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.renovaterc b/.renovaterc index b36f3b4..4a2fbf4 100644 --- a/.renovaterc +++ b/.renovaterc @@ -7,5 +7,9 @@ ":rebaseStalePrs" ], "prConcurrentLimit": 10, - "branchPrefix": "renovate/" + "branchPrefix": "renovate/", + "baseBranches": [ + "$default", + "/^release\\/.*/" + ] } From 6ba13ac1ec06c0334cccfda1fd022d7cfd291b7c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 13:24:03 +0100 Subject: [PATCH 124/184] Fix using branch for version number --- .ci/tasks/build.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 3cb9776..0b4f2aa 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -24,13 +24,10 @@ PR=${PR:-} if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" +elif [ -f ".git/branch"]; then + ref=$(cat .git/branch)-$(git rev-parse --short $ref) else - ref=$(cat .git/ref || echo "HEAD") - branch=$(git rev-parse --abbrev-ref $ref) - if [ -z "$branch" ]; then - branch=$(cat .git/ref) - fi - ref=${branch}-$(git rev-parse --short $ref) + ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") fi title "Version: '$ref'" From e61a252ee643d958d4e7eac93aa098e99fce7118 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:18:19 +0200 Subject: [PATCH 125/184] Remove internal URLs from CI Due to using internal URLs, the pipelines demanded a very specific network setup to work. By changing everything to their public-facing URLs, they now become agnostic to internal topology. --- .ci/pipelines/base.yml | 24 ++++++++++++++++-------- .ci/pipelines/pr.yml | 22 +++++++++++++--------- Justfile | 4 +++- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 8d3cc77..79c1e51 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -6,24 +6,30 @@ resource_types: - name: gitea-package type: registry-image source: - repository: registry.local:5000/gitea-package + repository: registry.sclu1034.dev/gitea-package + username: ((registry_user)) + password: ((registry_password)) - name: gitea-status type: registry-image source: - repository: registry.local:5000/gitea-status + repository: registry.sclu1034.dev/gitea-status + username: ((registry_user)) + password: ((registry_password)) - name: gitea-pr type: registry-image source: - repository: registry.local:5000/gitea-pr + repository: registry.sclu1034.dev/gitea-pr + username: ((registry_user)) + password: ((registry_password)) resources: - name: repo type: git source: - uri: http://forgejo:3000/bitsquid_dt/dtmt + uri: http://git.sclu1034.dev/bitsquid_dt/dtmt branch: master - name: repo-pr @@ -38,7 +44,7 @@ resources: type: gitea-package source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -48,7 +54,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -58,7 +64,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -85,6 +91,8 @@ jobs: vars: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) instance_vars: number: ((.:pr.number)) @@ -192,7 +200,7 @@ jobs: vars: pr: "" target: linux - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index bca0ebb..b2f514a 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -6,26 +6,30 @@ resource_types: - name: gitea-package type: registry-image source: - repository: registry.local:5000/gitea-package + repository: registry.sclu1034.dev/gitea-package + username: ((registry_user)) + password: ((registry_password)) - name: gitea-status type: registry-image source: - repository: registry.local:5000/gitea-status + repository: registry.sclu1034.dev/gitea-status + username: ((registry_user)) + password: ((registry_password)) resources: - name: repo type: git source: - uri: http://forgejo:3000/bitsquid_dt/dtmt + uri: http://git.sclu1034.dev/bitsquid_dt/dtmt branch: ((pr.head.ref)) - name: gitea-package type: gitea-package source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -34,7 +38,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: lint/clippy @@ -44,7 +48,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -54,7 +58,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -135,7 +139,7 @@ jobs: vars: target: msvc pr: ((pr)) - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number @@ -193,7 +197,7 @@ jobs: vars: target: linux pr: ((pr)) - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/Justfile b/Justfile index dbecc22..697bdcc 100644 --- a/Justfile +++ b/Justfile @@ -40,6 +40,8 @@ set-base-pipeline: --pipeline dtmt \ --config .ci/pipelines/base.yml \ -v gitea_api_key=${GITEA_API_KEY} \ + -v registry_user=${REGISTRY_USER} \ + -v registry_password=${REGISTRY_PASSWORD} \ -v owner=bitsquid_dt \ -v repo=dtmt @@ -48,7 +50,7 @@ set-pr-pipeline pr: -H "Authorization: ${GITEA_API_KEY}" \ -H 'Accept: application/json' \ 'https://git.sclu1034.dev/api/v1/repos/bitsquid_dt/dtmt/pulls/{{pr}}' \ - | yq -y '.' - > 'pr-{{pr}}.yaml' + | yq -y '.' - > 'pr-{{pr}}.yaml' fly -t main set-pipeline \ --pipeline dtmt-pr \ --config .ci/pipelines/pr.yml \ From 5aa8421f7dcc702f4fdfaef814056ce51c34a201 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:28:27 +0200 Subject: [PATCH 126/184] Fix incorrect URLs --- .ci/pipelines/base.yml | 10 +++++----- .ci/pipelines/pr.yml | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 79c1e51..ce2682c 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -29,7 +29,7 @@ resources: - name: repo type: git source: - uri: http://git.sclu1034.dev/bitsquid_dt/dtmt + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt branch: master - name: repo-pr @@ -44,7 +44,7 @@ resources: type: gitea-package source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -54,7 +54,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -64,7 +64,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -200,7 +200,7 @@ jobs: vars: pr: "" target: linux - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index b2f514a..3198410 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -22,14 +22,14 @@ resources: - name: repo type: git source: - uri: http://git.sclu1034.dev/bitsquid_dt/dtmt + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt branch: ((pr.head.ref)) - name: gitea-package type: gitea-package source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -38,7 +38,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: lint/clippy @@ -48,7 +48,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -58,7 +58,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -139,7 +139,7 @@ jobs: vars: target: msvc pr: ((pr)) - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number @@ -197,7 +197,7 @@ jobs: vars: target: linux pr: ((pr)) - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number From c2207c22ef3c6ea384ab617b4f516e79009cc1b5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:33:33 +0200 Subject: [PATCH 127/184] Fix more URLs in CI pipelines --- .ci/pipelines/base.yml | 4 ++++ .ci/pipelines/check.yml | 4 ++++ .ci/pipelines/pr.yml | 6 ++++++ .ci/tasks/build.yml | 4 +++- .ci/tasks/clippy.yml | 4 +++- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index ce2682c..a5b0b6e 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -133,6 +133,8 @@ jobs: vars: pr: "" target: msvc + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true @@ -202,6 +204,8 @@ jobs: target: linux gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true diff --git a/.ci/pipelines/check.yml b/.ci/pipelines/check.yml index 4d350ac..c8d5d47 100644 --- a/.ci/pipelines/check.yml +++ b/.ci/pipelines/check.yml @@ -18,6 +18,8 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: msvc + registry_user: ((registry_user)) + registry_password: ((registry_password)) - name: build-linux plan: - get: repo @@ -26,3 +28,5 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: linux + registry_user: ((registry_user)) + registry_password: ((registry_password)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 3198410..5c8d7cd 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -101,6 +101,8 @@ jobs: file: repo/.ci/tasks/clippy.yml vars: gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - name: build-msvc @@ -141,6 +143,8 @@ jobs: pr: ((pr)) gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true @@ -199,6 +203,8 @@ jobs: pr: ((pr)) gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index a7f47b4..9b9c094 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -6,7 +6,9 @@ image_resource: name: ctmt-bi-base-((target)) type: registry-image source: - repository: registry.local:5000/dtmt-ci-base-((target)) + repository: registry.sclu1034.dev/dtmt-ci-base-((target)) + username: ((registry_user)) + password: ((registry_password)) tag: latest inputs: diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index 483cb30..806dfd4 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -6,7 +6,9 @@ image_resource: name: dtmt-ci-base-linux type: registry-image source: - repository: registry.local:5000/dtmt-ci-base-linux + repository: registry.sclu1034.dev/dtmt-ci-base-linux + username: ((registry_user)) + password: ((registry_password)) tag: latest inputs: From 1b56acfd6328dc96e74365cd16c838bf95e5f45b Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:19 +0000 Subject: [PATCH 128/184] chore(deps): update rust crate bindgen to v0.71.1 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..6fe2e80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.71.0" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.7.0", "cexpr", @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2354,7 +2354,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen 0.71.0", + "bindgen 0.71.1", "color-eyre", "tracing", ] From 96c0953cf9e1a6610859806e680c3993526e0a5a Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:24 +0000 Subject: [PATCH 129/184] chore(deps): update rust crate clap to v4.5.37 --- Cargo.lock | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..516a0e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,9 +417,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -427,23 +427,23 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "unicase", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -453,9 +453,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cli-table" @@ -465,7 +465,7 @@ checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" dependencies = [ "cli-table-derive", "termcolor", - "unicode-width", + "unicode-width 0.1.13", ] [[package]] @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3101,7 +3101,7 @@ dependencies = [ "scopeguard", "smallvec", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.13", "utf8parse", "winapi", ] @@ -4082,6 +4082,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "untrusted" version = "0.9.0" From 5fa6ecfd0d0faea187fed641b8da2a8225f5170b Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:33 +0000 Subject: [PATCH 130/184] chore(deps): update rust crate interprocess to v2.2.3 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..0def154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1833,9 +1833,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.2.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ "doctest-file", "libc", @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From 9e209add0c46e6f1cc4d33d96a05ae6b8bd23ac0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:36 +0000 Subject: [PATCH 131/184] chore(deps): update rust crate open to v5.3.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..2d95ba6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2361,9 +2361,9 @@ dependencies = [ [[package]] name = "open" -version = "5.3.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", From f1291bdf33cb609ac72f6dec7e88a00dcea87442 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:40 +0000 Subject: [PATCH 132/184] chore(deps): update rust crate pin-project-lite to v0.2.16 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..a2c61f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2669,9 +2669,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" From b19225c75a4b647b9a58f8282c3197a5a4109fbd Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:48 +0000 Subject: [PATCH 133/184] chore(deps): update rust crate strip-ansi-escapes to v0.2.1 --- Cargo.lock | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..695fa27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3408,9 +3408,9 @@ dependencies = [ [[package]] name = "strip-ansi-escapes" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" dependencies = [ "vte", ] @@ -4164,22 +4164,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vte" -version = "0.11.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" -dependencies = [ - "proc-macro2", - "quote", + "memchr", ] [[package]] From 86b8f9e40ff19b9510023c36774c2a24262b09fc Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:06 +0000 Subject: [PATCH 134/184] chore(deps): update rust crate glob to v0.3.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..ffd7666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1499,9 +1499,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gobject-sys" From 64d9aa12aac1bb5594cc93ebd03174875dfe1486 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:16 +0000 Subject: [PATCH 135/184] chore(deps): update rust crate tokio-stream to v0.1.17 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..93832be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3723,9 +3723,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", From f2bf1493e528e887df4545cb0f66f29087b3ad67 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:28 +0000 Subject: [PATCH 136/184] fix(deps): update rust crate reqwest to v0.12.15 --- Cargo.lock | 180 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..6f34a57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1673,9 +1673,9 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -1726,9 +1726,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -1736,10 +1736,10 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1926,10 +1926,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2011,9 +2012,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" @@ -2647,26 +2648,6 @@ dependencies = [ "xi-unicode", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2888,9 +2869,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -2921,6 +2902,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tower", "tower-service", "url", "wasm-bindgen", @@ -3066,6 +3048,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "rustybuzz" version = "0.6.0" @@ -3357,9 +3345,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3801,14 +3789,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -4204,24 +4192,24 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.90", @@ -4242,9 +4230,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4252,9 +4240,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -4265,9 +4253,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" @@ -4339,7 +4330,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -4349,33 +4340,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-link" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -4429,13 +4425,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4448,6 +4460,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4460,6 +4478,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4472,12 +4496,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4490,6 +4526,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4502,6 +4544,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4514,6 +4562,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4526,6 +4580,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" From e6744404a948f211a0dbba2eb8528cfcfc6c6cd8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 17:35:58 +0200 Subject: [PATCH 137/184] Make PR pipelines public --- .ci/pipelines/base.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index a5b0b6e..4e7ed14 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -88,6 +88,7 @@ jobs: values: ((.:prs)) set_pipeline: dtmt-pr file: repo/.ci/pipelines/pr.yml + public: true vars: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) From bbb701176ffe1d2a54978fb77570dba7dfb354c8 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:05 +0000 Subject: [PATCH 138/184] fix(deps): update rust crate serde to v1.0.219 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..4273331 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3202,18 +3202,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", From b119b0d38da4048561f6f3c84a6f1ac22a1f5888 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:14 +0000 Subject: [PATCH 139/184] fix(deps): update rust crate serde_json to v1.0.140 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..c81ed9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3222,9 +3222,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", From ccac8e3859f1e0d634fd5f27928882fb2fdd6e8d Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:21 +0000 Subject: [PATCH 140/184] fix(deps): update rust crate thiserror to v2.0.12 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..03da545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2218,7 +2218,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -3538,11 +3538,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.12", ] [[package]] @@ -3558,9 +3558,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", From 36fba192c0a2bae82509d4ee454265b6ec806db0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:46 +0000 Subject: [PATCH 141/184] fix(deps): update rust-futures monorepo to v0.3.31 --- Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..1eb170d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1234,9 +1234,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1249,9 +1249,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1259,15 +1259,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1276,15 +1276,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1293,21 +1293,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", From bcab6e697f4c3955f96875e7270dbee5bf0b98bd Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:56 +0000 Subject: [PATCH 142/184] fix(deps): update tokio-tracing monorepo --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..65cb15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3816,9 +3816,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3827,9 +3827,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3838,9 +3838,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3848,9 +3848,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -3869,9 +3869,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", From be28b7045cdeefe7f35e17f5935d332cef52acef Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:03 +0000 Subject: [PATCH 143/184] chore(deps): update rust crate bitflags to v2.9.0 --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..fa94879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -242,7 +242,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -264,9 +264,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.7.0", + "bitflags 2.9.0", "clap", "color-eyre", "colors-transform", @@ -1805,7 +1805,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "inotify-sys", "libc", ] @@ -2032,7 +2032,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "libc", "redox_syscall", ] @@ -2274,7 +2274,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "filetime", "fsevent-sys", "inotify", @@ -2377,7 +2377,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -2809,7 +2809,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", ] [[package]] @@ -3001,7 +3001,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -3135,7 +3135,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.7.0", + "bitflags 2.9.0", "byteorder", "color-eyre", "csv-async", @@ -3162,7 +3162,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -3471,7 +3471,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "core-foundation", "system-configuration-sys", ] From c2f02e7ce67fad873db7f09cf86816d4228a0fe7 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:13 +0000 Subject: [PATCH 144/184] chore(deps): update rust crate fastrand to v2.3.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..b475485 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1070,9 +1070,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" From 3f3c4aa3210120448115bdae046e434e10d45a73 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:20 +0000 Subject: [PATCH 145/184] chore(deps): update rust crate minijinja to v2.9.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..198c53c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2140,9 +2140,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.2.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d3e3a3eece1fa4618237ad41e1de855ced47eab705cec1c9a920e1d1c5aad" +checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" dependencies = [ "serde", ] From 221a6b8485508ee758bbb26b63bb0008e3ec898e Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:28 +0000 Subject: [PATCH 146/184] chore(deps): update rust crate tempfile to v3.19.1 --- Cargo.lock | 81 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..5d27dfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,12 +1040,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1081,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix", + "rustix 0.38.34", "windows-sys 0.48.0", ] @@ -1398,7 +1398,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -2043,6 +2055,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -2181,7 +2199,7 @@ dependencies = [ "hermit-abi", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -2766,6 +2784,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radix_trie" version = "0.2.1" @@ -2818,7 +2842,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.63", ] @@ -2946,7 +2970,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -3004,10 +3028,23 @@ dependencies = [ "bitflags 2.7.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.7.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.12" @@ -3507,14 +3544,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -4190,6 +4227,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -4632,6 +4678,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.7.0", +] + [[package]] name = "xi-unicode" version = "0.3.0" From 42e2eb7cc12fbf5192c95e5c846b241580644d54 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:37 +0000 Subject: [PATCH 147/184] chore(deps): update rust crate tokio to v1.44.2 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..e613ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3661,9 +3661,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -3679,9 +3679,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", From bdce3e7787ecef128c3ba329baaf9967673de976 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:48 +0000 Subject: [PATCH 148/184] chore(deps): update rust crate zip to v2.6.1 --- Cargo.lock | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..9470462 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -324,22 +324,20 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -679,9 +677,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -726,9 +724,9 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -736,9 +734,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", @@ -3579,9 +3577,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -3596,15 +3594,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -4664,19 +4662,17 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" dependencies = [ "arbitrary", "bzip2", "crc32fast", "crossbeam-utils", - "displaydoc", "flate2", "indexmap", "memchr", - "thiserror 1.0.63", "time", "zopfli", "zstd", From be7b16c5ef2a88199b6a644313f2a01ada08402d Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:56 +0000 Subject: [PATCH 149/184] fix(deps): update rust crate regex to v1.11.1 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..e3400e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2825,14 +2825,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -2846,13 +2846,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2863,9 +2863,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" From 1a000371faad19de32863a2e7bca270a0db6104f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 18:41:59 +0200 Subject: [PATCH 150/184] Treat lint warnings as errors in CI Warnings will not show up if they don't fail CI. --- .ci/tasks/clippy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/tasks/clippy.sh b/.ci/tasks/clippy.sh index 33901a9..7c27b5f 100755 --- a/.ci/tasks/clippy.sh +++ b/.ci/tasks/clippy.sh @@ -10,6 +10,6 @@ title "Install clippy" rustup component add clippy title "Run clippy" -cargo clippy --color always --no-deps +cargo clippy --color always --no-deps -- -D warnings title "Done" From 9ac13834a1b1adceffdef3d1a9bcdda26b8fc121 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 18:45:18 +0200 Subject: [PATCH 151/184] Commit lock file changes Seems like Renovate missed this. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbc1c09..7417133 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3036,7 +3036,7 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -4682,7 +4682,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", ] [[package]] From 38a023bea638c4d2d555d68f9145db7139dcc0ed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 22:52:54 +0200 Subject: [PATCH 152/184] Move serde_sjson to its own project --- .gitmodules | 3 --- Cargo.lock | 31 +++++++++++++++++++++---------- Cargo.toml | 3 +-- lib/serde_sjson | 1 - 4 files changed, 22 insertions(+), 16 deletions(-) delete mode 160000 lib/serde_sjson diff --git a/.gitmodules b/.gitmodules index a03eebf..e1964a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "lib/serde_sjson"] - path = lib/serde_sjson - url = https://git.sclu1034.dev/lucas/serde_sjson.git [submodule "lib/luajit2-sys"] path = lib/luajit2-sys url = https://github.com/sclu1034/luajit2-sys.git diff --git a/Cargo.lock b/Cargo.lock index 7417133..874331a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" dependencies = [ "heapless", - "nom", + "nom 7.1.3", ] [[package]] @@ -383,7 +383,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -2033,7 +2033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2274,14 +2274,23 @@ dependencies = [ ] [[package]] -name = "nom_locate" -version = "4.2.0" +name = "nom" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom_locate" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" dependencies = [ "bytecount", "memchr", - "nom", + "nom 8.0.0", ] [[package]] @@ -3269,9 +3278,11 @@ dependencies = [ [[package]] name = "serde_sjson" -version = "1.0.0" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c5b0d7af37492e99b8559436ee76b9ec8935c75449b1c2ef08c205a9e92ae6f" dependencies = [ - "nom", + "nom 8.0.0", "nom_locate", "serde", ] @@ -4374,7 +4385,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3f00fe6..31130f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "lib/dtmt-shared", "lib/oodle", "lib/sdk", - "lib/serde_sjson", "lib/luajit2-sys", "lib/color-eyre", ] @@ -47,7 +46,7 @@ pin-project-lite = "0.2.9" promptly = "0.3.1" sdk = { path = "lib/sdk" } serde = { version = "1.0.152", features = ["derive", "rc"] } -serde_sjson = { path = "lib/serde_sjson" } +serde_sjson = "1.2.1" steamlocate = "2.0.0-beta.2" strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset", "formatting", "macros"] } diff --git a/lib/serde_sjson b/lib/serde_sjson deleted file mode 160000 index 73d2b23..0000000 --- a/lib/serde_sjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73d2b23ce50e75b184f5092ad515e97a0adbe6da From 3d05a2395e005de0443afb82a8c90c434f597a83 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:13:36 +0200 Subject: [PATCH 153/184] Fix clippy warnings --- Cargo.toml | 2 +- crates/dtmm/src/controller/deploy.rs | 15 ++++----------- crates/dtmm/src/controller/game.rs | 6 +++--- crates/dtmm/src/controller/import.rs | 2 +- crates/dtmt/src/cmd/bundle/extract.rs | 2 +- crates/dtmt/src/shell_parse.rs | 14 ++++---------- lib/sdk/src/bundle/mod.rs | 2 +- lib/sdk/src/filetype/lua.rs | 8 ++++---- lib/sdk/src/filetype/strings.rs | 8 ++++++-- 9 files changed, 25 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31130f4..23e92ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ glob = "0.3.0" interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "lib/luajit2-sys" } -minijinja = { version = "2.0.1", default-features = false } +minijinja = { version = "2.0.1", default-features = false, features = ["serde"] } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } notify = "8.0.0" diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 02b6860..481b07c 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -116,14 +116,14 @@ async fn patch_game_settings(state: Arc) -> Result<()> { eyre::bail!("couldn't find 'boot_script' field"); }; - f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(&settings.as_bytes()[0..i]).await?; f.write_all(b"boot_script = \"scripts/mod_main\"").await?; let Some(j) = settings[i..].find('\n') else { eyre::bail!("couldn't find end of 'boot_script' field"); }; - f.write_all(settings[(i + j)..].as_bytes()).await?; + f.write_all(&settings.as_bytes()[(i + j)..]).await?; Ok(()) } @@ -453,10 +453,7 @@ async fn build_bundles(state: Arc) -> Result> { } #[tracing::instrument(skip_all)] -async fn patch_boot_bundle( - state: Arc, - deployment_info: &String, -) -> Result> { +async fn patch_boot_bundle(state: Arc, deployment_info: &str) -> Result> { let bundle_dir = Arc::new(state.game_dir.join("bundle")); let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); @@ -590,11 +587,7 @@ fn build_deployment_data( .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) .collect(), // TODO: - mod_folders: mod_folders - .as_ref() - .iter() - .map(|folder| folder.clone()) - .collect(), + mod_folders: mod_folders.as_ref().to_vec(), }; serde_sjson::to_string(&info).wrap_err("Failed to serizalize deployment data") } diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 6b91169..b93d985 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -91,14 +91,14 @@ async fn patch_game_settings(state: Arc) -> Result<()> { eyre::bail!("couldn't find 'boot_script' field"); }; - f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(&settings.as_bytes()[0..i]).await?; f.write_all(b"boot_script = \"scripts/mod_main\"").await?; let Some(j) = settings[i..].find('\n') else { eyre::bail!("couldn't find end of 'boot_script' field"); }; - f.write_all(settings[(i + j)..].as_bytes()).await?; + f.write_all(&settings.as_bytes()[(i + j)..]).await?; Ok(()) } @@ -208,7 +208,7 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { for p in paths { let path = bundle_dir.join(p); - let backup = bundle_dir.join(&format!("{}.bak", p)); + let backup = bundle_dir.join(format!("{}.bak", p)); let res = async { tracing::debug!( diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 2f5f90b..6fc9693 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -397,7 +397,7 @@ fn extract_legacy_mod( tracing::trace!("Writing file '{}'", name.display()); let mut out = std::fs::OpenOptions::new() .write(true) - .create(true) + .truncate(true) .open(&name) .wrap_err_with(|| format!("Failed to open file '{}'", name.display()))?; diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 9a0f1dd..75f1360 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -150,7 +150,7 @@ async fn parse_command_line_template(tmpl: &String) -> Result { String::from_utf8_unchecked(bytes.to_vec()) }); - while let Some(arg) = parsed.next() { + for arg in parsed.by_ref() { // Safety: See above. cmd.arg(unsafe { String::from_utf8_unchecked(arg.to_vec()) }); } diff --git a/crates/dtmt/src/shell_parse.rs b/crates/dtmt/src/shell_parse.rs index 6f35a5f..13b3c4d 100644 --- a/crates/dtmt/src/shell_parse.rs +++ b/crates/dtmt/src/shell_parse.rs @@ -54,17 +54,11 @@ impl<'a> ShellParser<'a> { } _ => {} }, - ParserState::SingleQuote => match c { - b'\'' => { - return Some(&self.bytes[start..(self.offset - 1)]); - } - _ => {} + ParserState::SingleQuote => if c == b'\'' { + return Some(&self.bytes[start..(self.offset - 1)]); }, - ParserState::DoubleQuote => match c { - b'"' => { - return Some(&self.bytes[start..(self.offset - 1)]); - } - _ => {} + ParserState::DoubleQuote => if c == b'"' { + return Some(&self.bytes[start..(self.offset - 1)]); }, } } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index ca36393..075f4d2 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -237,7 +237,7 @@ impl Bundle { // Ceiling division (or division toward infinity) to calculate // the number of chunks required to fit the unpacked data. - let num_chunks = (unpacked_data.len() + CHUNK_SIZE - 1) / CHUNK_SIZE; + let num_chunks = unpacked_data.len().div_ceil(CHUNK_SIZE); tracing::trace!(num_chunks); w.write_u32(num_chunks as u32)?; diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 14f0d6b..dd0494e 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -125,7 +125,7 @@ pub fn compile(name: impl Into, code: impl AsRef) -> Result, code: impl AsRef) -> Result unreachable!(), } - lua::lua_setglobal(state, b"fn\0".as_ptr() as _); + lua::lua_setglobal(state, c"fn".as_ptr()); - let run = b"return string.dump(fn, false)\0"; - match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { + let run = c"return string.dump(fn, false)"; + match lua::luaL_loadstring(state, run.as_ptr()) as u32 { lua::LUA_OK => {} lua::LUA_ERRSYNTAX => { let err = lua::lua_tostring(state, -1); diff --git a/lib/sdk/src/filetype/strings.rs b/lib/sdk/src/filetype/strings.rs index ca2ed8c..8643266 100644 --- a/lib/sdk/src/filetype/strings.rs +++ b/lib/sdk/src/filetype/strings.rs @@ -28,10 +28,14 @@ impl Language { #[derive(serde::Serialize)] pub struct Strings(HashMap>); +#[inline(always)] fn read_string(r: R) -> Result where R: Read, { + // We can safely ignore the warning here, as all data is already in memory, and no additional + // `BufReader` should be needed. + #[allow(clippy::unbuffered_bytes)] r.bytes() .take_while(|b| b.as_ref().map(|b| *b != 0).unwrap_or(false)) .map(|b| b.map_err(Report::new)) @@ -41,7 +45,7 @@ where impl Strings { #[tracing::instrument(skip_all, fields(languages = variants.len()))] - pub fn from_variants(ctx: &crate::Context, variants: &Vec) -> Result { + pub fn from_variants(ctx: &crate::Context, variants: &[BundleFileVariant]) -> Result { let mut map: HashMap> = HashMap::new(); for (i, variant) in variants.iter().enumerate() { @@ -76,7 +80,7 @@ impl Strings { } #[tracing::instrument(skip_all)] -pub fn decompile(ctx: &crate::Context, variants: &Vec) -> Result> { +pub fn decompile(ctx: &crate::Context, variants: &[BundleFileVariant]) -> Result> { let strings = Strings::from_variants(ctx, variants)?; let content = strings.to_sjson()?; From bb6396c9321291a70f90a15116edc3d229bdf5a7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:15:08 +0200 Subject: [PATCH 154/184] Fix build script --- .ci/tasks/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 0b4f2aa..68c61a0 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -24,7 +24,7 @@ PR=${PR:-} if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" -elif [ -f ".git/branch"]; then +elif [ -f ".git/branch" ]; then ref=$(cat .git/branch)-$(git rev-parse --short $ref) else ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") From bf5c21dd03fb9e9ed74b1609399feb41ff05da12 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 21:46:09 +0000 Subject: [PATCH 155/184] chore(deps): update rust crate steamlocate to v2.0.1 --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874331a..f4ff703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -768,15 +768,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1641,6 +1632,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "1.1.0" @@ -3413,12 +3413,12 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "steamlocate" -version = "2.0.0-beta.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b6a4810c4e7fecb0123a9a8ba99b335c17d92e636c265ef99108ee4734c812" +checksum = "a13160bc6ea5cd80cde195ad4a4c629701db2bf397b62c139aa9e739016d2499" dependencies = [ "crc", - "dirs", + "home", "keyvalues-parser", "keyvalues-serde", "serde", @@ -4661,12 +4661,12 @@ dependencies = [ [[package]] name = "winreg" -version = "0.51.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] From 6e58449dac11edf43770971e3f5907e1d4623310 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 21:46:13 +0000 Subject: [PATCH 156/184] fix(deps): update rust crate url to v2.5.4 --- Cargo.lock | 276 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 245 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874331a..a788e09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1755,13 +1755,142 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2059,6 +2188,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -3511,6 +3646,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "system-configuration" version = "0.6.0" @@ -3688,23 +3834,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.44.2" @@ -4083,15 +4215,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-script" version = "0.5.6" @@ -4130,9 +4253,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -4166,12 +4289,24 @@ dependencies = [ "xmlwriter", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf16_lit" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -4696,6 +4831,18 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xi-unicode" version = "0.3.0" @@ -4720,12 +4867,79 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zip" version = "2.6.1" From 83de50409b9a31822508c4fe2b947f8d5443dcd9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:55:55 +0200 Subject: [PATCH 157/184] Fix locating Steam game path --- lib/dtmt-shared/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index d1f69c7..db11579 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -87,7 +87,7 @@ pub fn collect_game_info() -> Result> { .find_app(STEAMAPP_ID) .wrap_err("Failed to look up game by Steam app ID")?; - let Some((app, _)) = found else { + let Some((app, library)) = found else { return Ok(None); }; @@ -96,7 +96,7 @@ pub fn collect_game_info() -> Result> { .ok_or_eyre("Missing field 'last_updated'")?; Ok(Some(GameInfo { - path: app.install_dir.into(), + path: library.path().join(app.install_dir), last_updated: last_updated.into(), })) } From ddc69112bc4f1985f06e8768eb04cd1ec2e22bf3 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 22:01:08 +0000 Subject: [PATCH 158/184] chore(deps): update rust crate cli-table to 0.5.0 --- Cargo.lock | 70 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a788e09..c69417f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -253,7 +253,7 @@ dependencies = [ "regex", "rustc-hash 2.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -446,7 +446,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -457,24 +457,24 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cli-table" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" +checksum = "14da8d951cef7cc4f13ccc9b744d736963d57863c7e6fc33c070ea274546082c" dependencies = [ "cli-table-derive", "termcolor", - "unicode-width 0.1.13", + "unicode-width 0.2.0", ] [[package]] name = "cli-table-derive" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" +checksum = "9f7c1b60bae2c3d45228dfb096046aa51ef6c300de70b658d7a13fcb0c4f832e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -740,7 +740,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -818,7 +818,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -1286,7 +1286,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -1869,7 +1869,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2554,7 +2554,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2701,7 +2701,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2862,7 +2862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2901,9 +2901,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2919,9 +2919,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -3396,7 +3396,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3628,9 +3628,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -3654,7 +3654,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3745,7 +3745,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3756,7 +3756,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3863,7 +3863,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4011,7 +4011,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4402,7 +4402,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -4436,7 +4436,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4473,7 +4473,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4887,7 +4887,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "synstructure", ] @@ -4908,7 +4908,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "synstructure", ] @@ -4937,7 +4937,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 23e92ef..b5fd830 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ bincode = "1.3.3" bitflags = "2.5.0" byteorder = "1.4.3" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } -cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } +cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } colors-transform = "0.2.11" confy = "0.6.1" From afb5bc07954386e1c3bd0a8f2777cffd387220bb Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 22:01:13 +0000 Subject: [PATCH 159/184] chore(deps): update rust crate bincode to v2 --- Cargo.lock | 27 +++++++++++++++++++++++++-- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a788e09..358c5a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,11 +209,22 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bincode" -version = "1.3.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ + "bincode_derive", "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", ] [[package]] @@ -4251,6 +4262,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.4" @@ -4337,6 +4354,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "vte" version = "0.14.1" diff --git a/Cargo.toml b/Cargo.toml index 23e92ef..a4667fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = ["lib/color-eyre"] ansi-parser = "0.9.1" ansi_term = "0.12.1" async-recursion = "1.0.5" -bincode = "1.3.3" +bincode = "2.0.0" bitflags = "2.5.0" byteorder = "1.4.3" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } From 72ab8811c34502fbcfd34db76dd253fd798ef119 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 00:05:29 +0200 Subject: [PATCH 160/184] Ignore broken pipe error when printing dictionary stdout closing prematurely is normal behaviour, e.g. piping into `head`. --- crates/dtmt/src/cmd/dictionary.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 62a5295..4c54c34 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -227,9 +227,12 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( let lookup = &ctx.lookup; let rows: Vec<_> = lookup.entries().iter().map(TableRow::from).collect(); - print_stdout(rows.with_title())?; - - Ok(()) + match print_stdout(rows.with_title()) { + Ok(_) => Ok(()), + // Closing stdout prematurely is normal behavior with things like piping into `head` + Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => Ok(()), + Err(err) => Err(err.into()), + } } _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" From cd4a953a63785cc9bd22fdc549bff11ea02b804c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 00:14:43 +0200 Subject: [PATCH 161/184] Update bincode --- crates/dtmm/src/main.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 6c0bb48..54e101a 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -52,10 +52,14 @@ fn notify_nxm_download( tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); - bincode::serialize_into(&mut stream, uri.as_ref()).wrap_err("Failed to send URI")?; + let bincode_config = bincode::config::standard(); + + bincode::encode_into_std_write(uri.as_ref(), &mut stream, bincode_config) + .wrap_err("Failed to send URI")?; // We don't really care what the message is, we just need an acknowledgement. - let _: String = bincode::deserialize_from(&mut stream).wrap_err("Failed to receive reply")?; + let _: String = bincode::decode_from_std_read(&mut stream, bincode_config) + .wrap_err("Failed to receive reply")?; tracing::info!( "Notified DTMM with uri '{}'. Check the main window.", @@ -160,22 +164,33 @@ fn main() -> Result<()> { match res { Ok(mut stream) => { - let res = bincode::deserialize_from(&mut stream) - .wrap_err("Failed to read message") - .and_then(|uri: String| { - tracing::trace!(uri, "Received NXM uri"); + let res = bincode::decode_from_std_read( + &mut stream, + bincode::config::standard(), + ) + .wrap_err("Failed to read message") + .and_then(|uri: String| { + tracing::trace!(uri, "Received NXM uri"); - event_sink - .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) - .wrap_err("Failed to start NXM download") - }); + event_sink + .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) + .wrap_err("Failed to start NXM download") + }); match res { Ok(()) => { - let _ = bincode::serialize_into(&mut stream, "Ok"); + let _ = bincode::encode_into_std_write( + "Ok", + &mut stream, + bincode::config::standard(), + ); } Err(err) => { tracing::error!("{:?}", err); - let _ = bincode::serialize_into(&mut stream, "Error"); + let _ = bincode::encode_into_std_write( + "Error", + &mut stream, + bincode::config::standard(), + ); } } } From 5bb0f2acf561edca6e358d934c79c9d0a8c9cb44 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 14:50:37 +0200 Subject: [PATCH 162/184] Fix build script --- .ci/tasks/build.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 68c61a0..b362266 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -20,14 +20,15 @@ install_artifact() { cd "repo" PR=${PR:-} +ref=$(cat .git/ref || echo "HEAD") if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$ref" 2>/dev/null || echo 'manual')" elif [ -f ".git/branch" ]; then - ref=$(cat .git/branch)-$(git rev-parse --short $ref) + ref=$(cat .git/branch)-$(git rev-parse --short "$ref") else - ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") + ref=$(git rev-parse --short "$ref") fi title "Version: '$ref'" From 43e3bf7b60de613258f4da7fc116bebaefc9c1f1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 26 Jul 2024 16:03:04 +0200 Subject: [PATCH 163/184] Add cmdline to tracing output Can come in handy when other people report problems and show the error message or full log, but not the command line. Setting that span to `level = "error"` ensures that it won't be disabled by level filters. --- crates/dtmt/src/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index 2e10b17..e41e802 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -36,10 +36,21 @@ struct GlobalConfig { } #[tokio::main] -#[tracing::instrument] +#[tracing::instrument(level = "error", fields(cmd_line = tracing::field::Empty))] async fn main() -> Result<()> { color_eyre::install()?; + { + let span = tracing::Span::current(); + if !span.is_disabled() { + let cmdline: String = std::env::args_os().fold(String::new(), |mut s, arg| { + s.push_str(&arg.to_string_lossy()); + s + }); + span.record("cmd_line", cmdline); + } + } + let matches = command!() .subcommand_required(true) .arg( From 636279edfe376bd9d748347c7195d4e82af90015 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 21 Feb 2025 14:51:42 +0100 Subject: [PATCH 164/184] Use macro to generate file type enum and impls Due to the large amount of variants, and the different kind of values connected to each variant (hash, extension name) being scattered across the various `impl` blocks, the file became rather convoluted. While I don't generally like the indirection of macros or meta programming, it's not that bad with Rust, thanks to Rust Analyzer being able to attach diagnostics to the source inside the macro definition, and the ability to generate the macro's output for validation. Therefore, the new macro allows putting all data used for this enum definition into a single block. --- lib/sdk/src/bundle/filetype.rs | 490 +++++++++------------------------ 1 file changed, 132 insertions(+), 358 deletions(-) diff --git a/lib/sdk/src/bundle/filetype.rs b/lib/sdk/src/bundle/filetype.rs index 0b4f292..68ff6b5 100644 --- a/lib/sdk/src/bundle/filetype.rs +++ b/lib/sdk/src/bundle/filetype.rs @@ -3,232 +3,147 @@ use serde::Serialize; use crate::murmur::Murmur64; -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] -pub enum BundleFileType { - Animation, - AnimationCurves, - Apb, - BakedLighting, - Bik, - BlendSet, - Bones, - Chroma, - CommonPackage, - Config, - Crypto, - Data, - Entity, - Flow, - Font, - Ies, - Ini, - Input, - Ivf, - Keys, - Level, - Lua, - Material, - Mod, - MouseCursor, - NavData, - NetworkConfig, - OddleNet, - Package, - Particles, - PhysicsProperties, - RenderConfig, - RtPipeline, - Scene, - Shader, - ShaderLibrary, - ShaderLibraryGroup, - ShadingEnvionmentMapping, - ShadingEnvironment, - Slug, - SlugAlbum, - SoundEnvironment, - SpuJob, - StateMachine, - StaticPVS, - Strings, - SurfaceProperties, - Texture, - TimpaniBank, - TimpaniMaster, - Tome, - Ugg, - Unit, - Upb, - VectorField, - Wav, - WwiseBank, - WwiseDep, - WwiseEvent, - WwiseMetadata, - WwiseStream, - Xml, +macro_rules! make_enum { + ( + $( $variant:ident, $hash:expr, $ext:expr $(, $decompiled:expr)? ; )+ + ) => { + #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] + pub enum BundleFileType { + $( + $variant, + )+ + Unknown(Murmur64), + } - Unknown(Murmur64), + impl BundleFileType { + pub fn ext_name(&self) -> String { + match self { + $( + Self::$variant => String::from($ext), + )+ + Self::Unknown(s) => format!("{s:016X}"), + } + } + + pub fn decompiled_ext_name(&self) -> String { + match self { + $( + $( Self::$variant => String::from($decompiled), )? + )+ + _ => self.ext_name(), + } + } + } + + impl std::str::FromStr for BundleFileType { + type Err = color_eyre::Report; + fn from_str(s: &str) -> Result { + match s { + $( + $ext => Ok(Self::$variant), + )+ + s => eyre::bail!("Unknown type string '{}'", s), + } + } + } + + impl From for BundleFileType { + fn from(h: u64) -> Self { + match h { + $( + $hash => Self::$variant, + )+ + hash => Self::Unknown(hash.into()), + } + } + } + + impl From for u64 { + fn from(t: BundleFileType) -> u64 { + match t { + $( + BundleFileType::$variant => $hash, + )+ + BundleFileType::Unknown(hash) => hash.into(), + } + } + } + } +} + +make_enum! { + AnimationCurves, 0xdcfb9e18fff13984, "animation_curves"; + Animation, 0x931e336d7646cc26, "animation"; + Apb, 0x3eed05ba83af5090, "apb"; + BakedLighting, 0x7ffdb779b04e4ed1, "baked_lighting"; + Bik, 0xaa5965f03029fa18, "bik"; + BlendSet, 0xe301e8af94e3b5a3, "blend_set"; + Bones, 0x18dead01056b72e9, "bones"; + Chroma, 0xb7893adf7567506a, "chroma"; + CommonPackage, 0xfe9754bd19814a47, "common_package"; + Config, 0x82645835e6b73232, "config"; + Crypto, 0x69108ded1e3e634b, "crypto"; + Data, 0x8fd0d44d20650b68, "data"; + Entity, 0x9831ca893b0d087d, "entity"; + Flow, 0x92d3ee038eeb610d, "flow"; + Font, 0x9efe0a916aae7880, "font"; + Ies, 0x8f7d5a2c0f967655, "ies"; + Ini, 0xd526a27da14f1dc5, "ini"; + Input, 0x2bbcabe5074ade9e, "input"; + Ivf, 0xfa4a8e091a91201e, "ivf"; + Keys, 0xa62f9297dc969e85, "keys"; + Level, 0x2a690fd348fe9ac5, "level"; + Lua, 0xa14e8dfa2cd117e2, "lua"; + Material, 0xeac0b497876adedf, "material"; + Mod, 0x3fcdd69156a46417, "mod"; + MouseCursor, 0xb277b11fe4a61d37, "mouse_cursor"; + NavData, 0x169de9566953d264, "nav_data"; + NetworkConfig, 0x3b1fa9e8f6bac374, "network_config"; + OddleNet, 0xb0f2c12eb107f4d8, "oodle_net"; + Package, 0xad9c6d9ed1e5e77a, "package"; + Particles, 0xa8193123526fad64, "particles"; + PhysicsProperties, 0xbf21403a3ab0bbb1, "physics_properties"; + RenderConfig, 0x27862fe24795319c, "render_config"; + RtPipeline, 0x9ca183c2d0e76dee, "rt_pipeline"; + Scene, 0x9d0a795bfe818d19, "scene"; + Shader, 0xcce8d5b5f5ae333f, "shader"; + ShaderLibrary, 0xe5ee32a477239a93, "shader_library"; + ShaderLibraryGroup, 0x9e5c3cc74575aeb5, "shader_library_group"; + ShadingEnvionmentMapping, 0x250e0a11ac8e26f8, "shading_envionment_mapping"; + ShadingEnvironment, 0xfe73c7dcff8a7ca5, "shading_environment"; + Slug, 0xa27b4d04a9ba6f9e, "slug"; + SlugAlbum, 0xe9fc9ea7042e5ec0, "slug_album"; + SoundEnvironment, 0xd8b27864a97ffdd7, "sound_environment"; + SpuJob, 0xf97af9983c05b950, "spu_job"; + StateMachine, 0xa486d4045106165c, "state_machine"; + StaticPVS, 0xe3f0baa17d620321, "static_pvs"; + Strings, 0x0d972bab10b40fd3, "strings"; + SurfaceProperties, 0xad2d3fa30d9ab394, "surface_properties"; + Texture, 0xcd4238c6a0c69e32, "texture", "dds"; + TimpaniBank, 0x99736be1fff739a4, "timpani_bank"; + TimpaniMaster, 0x00a3e6c59a2b9c6c, "timpani_master"; + Tome, 0x19c792357c99f49b, "tome"; + Ugg, 0x712d6e3dd1024c9c, "ugg"; + Unit, 0xe0a48d0be9a7453f, "unit"; + Upb, 0xa99510c6e86dd3c2, "upb"; + VectorField, 0xf7505933166d6755, "vector_field"; + Wav, 0x786f65c00a816b19, "wav"; + WwiseBank, 0x535a7bd3e650d799, "wwise_bank", "bnk"; + WwiseDep, 0xaf32095c82f2b070, "wwise_dep"; + WwiseEvent, 0xaabdd317b58dfc8a, "wwise_event"; + WwiseMetadata, 0xd50a8b7e1c82b110, "wwise_metadata"; + WwiseStream, 0x504b55235d21440e, "wwise_stream", "ogg"; + Xml, 0x76015845a6003765, "xml"; + Theme, 0x38BB9442048A7FBD, "theme"; + MissionThemes, 0x80F2DE893657F83A, "mission_themes"; } impl BundleFileType { - pub fn ext_name(&self) -> String { - match self { - BundleFileType::AnimationCurves => String::from("animation_curves"), - BundleFileType::Animation => String::from("animation"), - BundleFileType::Apb => String::from("apb"), - BundleFileType::BakedLighting => String::from("baked_lighting"), - BundleFileType::Bik => String::from("bik"), - BundleFileType::BlendSet => String::from("blend_set"), - BundleFileType::Bones => String::from("bones"), - BundleFileType::Chroma => String::from("chroma"), - BundleFileType::CommonPackage => String::from("common_package"), - BundleFileType::Config => String::from("config"), - BundleFileType::Crypto => String::from("crypto"), - BundleFileType::Data => String::from("data"), - BundleFileType::Entity => String::from("entity"), - BundleFileType::Flow => String::from("flow"), - BundleFileType::Font => String::from("font"), - BundleFileType::Ies => String::from("ies"), - BundleFileType::Ini => String::from("ini"), - BundleFileType::Input => String::from("input"), - BundleFileType::Ivf => String::from("ivf"), - BundleFileType::Keys => String::from("keys"), - BundleFileType::Level => String::from("level"), - BundleFileType::Lua => String::from("lua"), - BundleFileType::Material => String::from("material"), - BundleFileType::Mod => String::from("mod"), - BundleFileType::MouseCursor => String::from("mouse_cursor"), - BundleFileType::NavData => String::from("nav_data"), - BundleFileType::NetworkConfig => String::from("network_config"), - BundleFileType::OddleNet => String::from("oodle_net"), - BundleFileType::Package => String::from("package"), - BundleFileType::Particles => String::from("particles"), - BundleFileType::PhysicsProperties => String::from("physics_properties"), - BundleFileType::RenderConfig => String::from("render_config"), - BundleFileType::RtPipeline => String::from("rt_pipeline"), - BundleFileType::Scene => String::from("scene"), - BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), - BundleFileType::ShaderLibrary => String::from("shader_library"), - BundleFileType::Shader => String::from("shader"), - BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), - BundleFileType::ShadingEnvironment => String::from("shading_environment"), - BundleFileType::SlugAlbum => String::from("slug_album"), - BundleFileType::Slug => String::from("slug"), - BundleFileType::SoundEnvironment => String::from("sound_environment"), - BundleFileType::SpuJob => String::from("spu_job"), - BundleFileType::StateMachine => String::from("state_machine"), - BundleFileType::StaticPVS => String::from("static_pvs"), - BundleFileType::Strings => String::from("strings"), - BundleFileType::SurfaceProperties => String::from("surface_properties"), - BundleFileType::Texture => String::from("texture"), - BundleFileType::TimpaniBank => String::from("timpani_bank"), - BundleFileType::TimpaniMaster => String::from("timpani_master"), - BundleFileType::Tome => String::from("tome"), - BundleFileType::Ugg => String::from("ugg"), - BundleFileType::Unit => String::from("unit"), - BundleFileType::Upb => String::from("upb"), - BundleFileType::VectorField => String::from("vector_field"), - BundleFileType::Wav => String::from("wav"), - BundleFileType::WwiseBank => String::from("wwise_bank"), - BundleFileType::WwiseDep => String::from("wwise_dep"), - BundleFileType::WwiseEvent => String::from("wwise_event"), - BundleFileType::WwiseMetadata => String::from("wwise_metadata"), - BundleFileType::WwiseStream => String::from("wwise_stream"), - BundleFileType::Xml => String::from("xml"), - - BundleFileType::Unknown(s) => format!("{s:016X}"), - } - } - - pub fn decompiled_ext_name(&self) -> String { - match self { - BundleFileType::Texture => String::from("dds"), - BundleFileType::WwiseBank => String::from("bnk"), - BundleFileType::WwiseStream => String::from("ogg"), - _ => self.ext_name(), - } - } - pub fn hash(&self) -> Murmur64 { Murmur64::from(*self) } } -impl std::str::FromStr for BundleFileType { - type Err = color_eyre::Report; - - fn from_str(s: &str) -> Result { - let val = match s { - "animation_curves" => BundleFileType::AnimationCurves, - "animation" => BundleFileType::Animation, - "apb" => BundleFileType::Apb, - "baked_lighting" => BundleFileType::BakedLighting, - "bik" => BundleFileType::Bik, - "blend_set" => BundleFileType::BlendSet, - "bones" => BundleFileType::Bones, - "chroma" => BundleFileType::Chroma, - "common_package" => BundleFileType::CommonPackage, - "config" => BundleFileType::Config, - "crypto" => BundleFileType::Crypto, - "data" => BundleFileType::Data, - "entity" => BundleFileType::Entity, - "flow" => BundleFileType::Flow, - "font" => BundleFileType::Font, - "ies" => BundleFileType::Ies, - "ini" => BundleFileType::Ini, - "input" => BundleFileType::Input, - "ivf" => BundleFileType::Ivf, - "keys" => BundleFileType::Keys, - "level" => BundleFileType::Level, - "lua" => BundleFileType::Lua, - "material" => BundleFileType::Material, - "mod" => BundleFileType::Mod, - "mouse_cursor" => BundleFileType::MouseCursor, - "nav_data" => BundleFileType::NavData, - "network_config" => BundleFileType::NetworkConfig, - "oodle_net" => BundleFileType::OddleNet, - "package" => BundleFileType::Package, - "particles" => BundleFileType::Particles, - "physics_properties" => BundleFileType::PhysicsProperties, - "render_config" => BundleFileType::RenderConfig, - "rt_pipeline" => BundleFileType::RtPipeline, - "scene" => BundleFileType::Scene, - "shader_library_group" => BundleFileType::ShaderLibraryGroup, - "shader_library" => BundleFileType::ShaderLibrary, - "shader" => BundleFileType::Shader, - "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, - "shading_environment" => BundleFileType::ShadingEnvironment, - "slug_album" => BundleFileType::SlugAlbum, - "slug" => BundleFileType::Slug, - "sound_environment" => BundleFileType::SoundEnvironment, - "spu_job" => BundleFileType::SpuJob, - "state_machine" => BundleFileType::StateMachine, - "static_pvs" => BundleFileType::StaticPVS, - "strings" => BundleFileType::Strings, - "surface_properties" => BundleFileType::SurfaceProperties, - "texture" => BundleFileType::Texture, - "timpani_bank" => BundleFileType::TimpaniBank, - "timpani_master" => BundleFileType::TimpaniMaster, - "tome" => BundleFileType::Tome, - "ugg" => BundleFileType::Ugg, - "unit" => BundleFileType::Unit, - "upb" => BundleFileType::Upb, - "vector_field" => BundleFileType::VectorField, - "wav" => BundleFileType::Wav, - "wwise_bank" => BundleFileType::WwiseBank, - "wwise_dep" => BundleFileType::WwiseDep, - "wwise_event" => BundleFileType::WwiseEvent, - "wwise_metadata" => BundleFileType::WwiseMetadata, - "wwise_stream" => BundleFileType::WwiseStream, - "xml" => BundleFileType::Xml, - s => eyre::bail!("Unknown type string '{}'", s), - }; - - Ok(val) - } -} - impl Serialize for BundleFileType { fn serialize(&self, serializer: S) -> Result where @@ -245,147 +160,6 @@ impl From for BundleFileType { } } -impl From for BundleFileType { - fn from(hash: u64) -> BundleFileType { - match hash { - 0x931e336d7646cc26 => BundleFileType::Animation, - 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, - 0x3eed05ba83af5090 => BundleFileType::Apb, - 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, - 0xaa5965f03029fa18 => BundleFileType::Bik, - 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, - 0x18dead01056b72e9 => BundleFileType::Bones, - 0xb7893adf7567506a => BundleFileType::Chroma, - 0xfe9754bd19814a47 => BundleFileType::CommonPackage, - 0x82645835e6b73232 => BundleFileType::Config, - 0x69108ded1e3e634b => BundleFileType::Crypto, - 0x8fd0d44d20650b68 => BundleFileType::Data, - 0x9831ca893b0d087d => BundleFileType::Entity, - 0x92d3ee038eeb610d => BundleFileType::Flow, - 0x9efe0a916aae7880 => BundleFileType::Font, - 0x8f7d5a2c0f967655 => BundleFileType::Ies, - 0xd526a27da14f1dc5 => BundleFileType::Ini, - 0x2bbcabe5074ade9e => BundleFileType::Input, - 0xfa4a8e091a91201e => BundleFileType::Ivf, - 0xa62f9297dc969e85 => BundleFileType::Keys, - 0x2a690fd348fe9ac5 => BundleFileType::Level, - 0xa14e8dfa2cd117e2 => BundleFileType::Lua, - 0xeac0b497876adedf => BundleFileType::Material, - 0x3fcdd69156a46417 => BundleFileType::Mod, - 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, - 0x169de9566953d264 => BundleFileType::NavData, - 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, - 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, - 0xad9c6d9ed1e5e77a => BundleFileType::Package, - 0xa8193123526fad64 => BundleFileType::Particles, - 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, - 0x27862fe24795319c => BundleFileType::RenderConfig, - 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, - 0x9d0a795bfe818d19 => BundleFileType::Scene, - 0xcce8d5b5f5ae333f => BundleFileType::Shader, - 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, - 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, - 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, - 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, - 0xa27b4d04a9ba6f9e => BundleFileType::Slug, - 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, - 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, - 0xf97af9983c05b950 => BundleFileType::SpuJob, - 0xa486d4045106165c => BundleFileType::StateMachine, - 0xe3f0baa17d620321 => BundleFileType::StaticPVS, - 0x0d972bab10b40fd3 => BundleFileType::Strings, - 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, - 0xcd4238c6a0c69e32 => BundleFileType::Texture, - 0x99736be1fff739a4 => BundleFileType::TimpaniBank, - 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, - 0x19c792357c99f49b => BundleFileType::Tome, - 0x712d6e3dd1024c9c => BundleFileType::Ugg, - 0xe0a48d0be9a7453f => BundleFileType::Unit, - 0xa99510c6e86dd3c2 => BundleFileType::Upb, - 0xf7505933166d6755 => BundleFileType::VectorField, - 0x786f65c00a816b19 => BundleFileType::Wav, - 0x535a7bd3e650d799 => BundleFileType::WwiseBank, - 0xaf32095c82f2b070 => BundleFileType::WwiseDep, - 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, - 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, - 0x504b55235d21440e => BundleFileType::WwiseStream, - 0x76015845a6003765 => BundleFileType::Xml, - - _ => BundleFileType::Unknown(Murmur64::from(hash)), - } - } -} - -impl From for u64 { - fn from(t: BundleFileType) -> u64 { - match t { - BundleFileType::Animation => 0x931e336d7646cc26, - BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, - BundleFileType::Apb => 0x3eed05ba83af5090, - BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, - BundleFileType::Bik => 0xaa5965f03029fa18, - BundleFileType::BlendSet => 0xe301e8af94e3b5a3, - BundleFileType::Bones => 0x18dead01056b72e9, - BundleFileType::Chroma => 0xb7893adf7567506a, - BundleFileType::CommonPackage => 0xfe9754bd19814a47, - BundleFileType::Config => 0x82645835e6b73232, - BundleFileType::Crypto => 0x69108ded1e3e634b, - BundleFileType::Data => 0x8fd0d44d20650b68, - BundleFileType::Entity => 0x9831ca893b0d087d, - BundleFileType::Flow => 0x92d3ee038eeb610d, - BundleFileType::Font => 0x9efe0a916aae7880, - BundleFileType::Ies => 0x8f7d5a2c0f967655, - BundleFileType::Ini => 0xd526a27da14f1dc5, - BundleFileType::Input => 0x2bbcabe5074ade9e, - BundleFileType::Ivf => 0xfa4a8e091a91201e, - BundleFileType::Keys => 0xa62f9297dc969e85, - BundleFileType::Level => 0x2a690fd348fe9ac5, - BundleFileType::Lua => 0xa14e8dfa2cd117e2, - BundleFileType::Material => 0xeac0b497876adedf, - BundleFileType::Mod => 0x3fcdd69156a46417, - BundleFileType::MouseCursor => 0xb277b11fe4a61d37, - BundleFileType::NavData => 0x169de9566953d264, - BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, - BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, - BundleFileType::Package => 0xad9c6d9ed1e5e77a, - BundleFileType::Particles => 0xa8193123526fad64, - BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, - BundleFileType::RenderConfig => 0x27862fe24795319c, - BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, - BundleFileType::Scene => 0x9d0a795bfe818d19, - BundleFileType::Shader => 0xcce8d5b5f5ae333f, - BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, - BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, - BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, - BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, - BundleFileType::Slug => 0xa27b4d04a9ba6f9e, - BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, - BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, - BundleFileType::SpuJob => 0xf97af9983c05b950, - BundleFileType::StateMachine => 0xa486d4045106165c, - BundleFileType::StaticPVS => 0xe3f0baa17d620321, - BundleFileType::Strings => 0x0d972bab10b40fd3, - BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, - BundleFileType::Texture => 0xcd4238c6a0c69e32, - BundleFileType::TimpaniBank => 0x99736be1fff739a4, - BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, - BundleFileType::Tome => 0x19c792357c99f49b, - BundleFileType::Ugg => 0x712d6e3dd1024c9c, - BundleFileType::Unit => 0xe0a48d0be9a7453f, - BundleFileType::Upb => 0xa99510c6e86dd3c2, - BundleFileType::VectorField => 0xf7505933166d6755, - BundleFileType::Wav => 0x786f65c00a816b19, - BundleFileType::WwiseBank => 0x535a7bd3e650d799, - BundleFileType::WwiseDep => 0xaf32095c82f2b070, - BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, - BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, - BundleFileType::WwiseStream => 0x504b55235d21440e, - BundleFileType::Xml => 0x76015845a6003765, - - BundleFileType::Unknown(hash) => hash.into(), - } - } -} impl From for Murmur64 { fn from(t: BundleFileType) -> Murmur64 { let hash: u64 = t.into(); From 7b95918000430088139af81b9aea2d540e25f98b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:42:16 +0200 Subject: [PATCH 165/184] Refactor code for file injection I ended up wrapping the raw data in a `BundleFile` twice. I also made '--compile' the default, as it should be much less often that raw data needs to be inserted. Even files that are essentially raw binary blobs, like `.wwise_event`, still have some custom fields that need to be accounted for. --- crates/dtmt/src/cmd/bundle/inject.rs | 327 +++++++++++++++++++++------ lib/sdk/src/bundle/file.rs | 23 +- lib/sdk/src/bundle/mod.rs | 3 +- lib/sdk/src/lib.rs | 2 +- 4 files changed, 277 insertions(+), 78 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/inject.rs b/crates/dtmt/src/cmd/bundle/inject.rs index 2b86691..21f4a91 100644 --- a/crates/dtmt/src/cmd/bundle/inject.rs +++ b/crates/dtmt/src/cmd/bundle/inject.rs @@ -1,112 +1,297 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::str::FromStr as _; -use clap::{value_parser, Arg, ArgMatches, Command}; -use color_eyre::eyre::{self, Context, Result}; +use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; +use color_eyre::eyre::{self, Context, OptionExt, Result}; use color_eyre::Help; -use sdk::Bundle; -use tokio::fs::{self, File}; -use tokio::io::AsyncReadExt; +use path_slash::PathBufExt as _; +use sdk::murmur::IdString64; +use sdk::{Bundle, BundleFile, BundleFileType}; +use tokio::fs; pub(crate) fn command_definition() -> Command { Command::new("inject") - .about("Inject a file into a bundle.") - .arg( - Arg::new("replace") - .help("The name of a file in the bundle whos content should be replaced.") - .short('r') - .long("replace"), - ) + .subcommand_required(true) + .about("Inject a file into a bundle.\n\ + Raw binary data can be used to directly replace the file's variant data blob without affecting the metadata.\n\ + Alternatively, a compiler format may be specified, and a complete bundle file is created.") .arg( Arg::new("output") .help( "The path to write the changed bundle to. \ - If omitted, the input bundle will be overwritten.", + If omitted, the input bundle will be overwritten.\n\ + Remember to add a `.patch_` suffix if you also use '--patch'.", ) .short('o') .long("output") .value_parser(value_parser!(PathBuf)), ) .arg( - Arg::new("bundle") - .help("Path to the bundle to inject the file into.") - .required(true) - .value_parser(value_parser!(PathBuf)), + Arg::new("patch") + .help("Create a patch bundle. Optionally, a patch NUMBER may be specified as \ + '--patch=123'.\nThe maximum number is 999, the default is 1.\n\ + If `--output` is not specified, the `.patch_` suffix is added to \ + the given bundle name.") + .short('p') + .long("patch") + .num_args(0..=1) + .require_equals(true) + .default_missing_value("1") + .value_name("NUMBER") + .value_parser(value_parser!(u16)) ) .arg( - Arg::new("file") - .help("Path to the file to inject.") - .required(true) - .value_parser(value_parser!(PathBuf)), + Arg::new("type") + .help("Compile the new file as the given TYPE. If omitted, the file type is \ + is guessed from the file extension.") + .value_name("TYPE") ) + .subcommand( + Command::new("replace") + .about("Replace an existing file in the bundle") + .arg( + Arg::new("variant") + .help("In combination with '--raw', specify the variant index to replace.") + .long("variant") + .default_value("0") + .value_parser(value_parser!(u8)) + ) + .arg( + Arg::new("raw") + .help("Insert the given file as raw binary data.\n\ + Cannot be used with '--patch'.") + .long("raw") + .action(ArgAction::SetTrue) + ) + .arg( + Arg::new("bundle") + .help("Path to the bundle to inject the file into.") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("bundle-file") + .help("The name of a file in the bundle whose content should be replaced.") + .required(true), + ) + .arg( + Arg::new("new-file") + .help("Path to the file to inject.") + .required(true) + .value_parser(value_parser!(PathBuf)), + ), + ) + // .subcommand( + // Command::new("add") + // .about("Add a new file to the bundle") + // .arg( + // Arg::new("new-file") + // .help("Path to the file to inject.") + // .required(true) + // .value_parser(value_parser!(PathBuf)), + // ) + // .arg( + // Arg::new("bundle") + // .help("Path to the bundle to inject the file into.") + // .required(true) + // .value_parser(value_parser!(PathBuf)), + // ), + // ) } -#[tracing::instrument(skip_all)] +#[tracing::instrument] +async fn compile_file( + path: impl AsRef + std::fmt::Debug, + name: impl Into + std::fmt::Debug, + file_type: BundleFileType, +) -> Result { + let path = path.as_ref(); + + let file_data = fs::read(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + let _sjson = String::from_utf8(file_data) + .wrap_err_with(|| format!("Invalid UTF8 data in '{}'", path.display()))?; + + let _root = path.parent().ok_or_eyre("File path has no parent")?; + + eyre::bail!( + "Compilation for type '{}' is not implemented, yet", + file_type + ) +} + +#[tracing::instrument( + skip_all, + fields( + bundle_path = tracing::field::Empty, + in_file_path = tracing::field::Empty, + output_path = tracing::field::Empty, + target_name = tracing::field::Empty, + file_type = tracing::field::Empty, + raw = tracing::field::Empty, + ) +)] pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - let bundle_path = matches + let Some((op, sub_matches)) = matches.subcommand() else { + unreachable!("clap is configured to require a subcommand, and they're all handled above"); + }; + + let bundle_path = sub_matches .get_one::("bundle") .expect("required parameter not found"); - let file_path = matches - .get_one::("file") + let in_file_path = sub_matches + .get_one::("new-file") .expect("required parameter not found"); - tracing::trace!(bundle_path = %bundle_path.display(), file_path = %file_path.display()); + let patch_number = matches + .get_one::("patch") + .map(|num| format!("{:03}", num)); - let mut bundle = { - let binary = fs::read(bundle_path).await?; - let name = Bundle::get_name_from_path(&ctx, bundle_path); - Bundle::from_binary(&ctx, name, binary).wrap_err("Failed to open bundle file")? + let output_path = matches + .get_one::("output") + .cloned() + .unwrap_or_else(|| { + let mut output_path = bundle_path.clone(); + + if let Some(patch_number) = patch_number.as_ref() { + output_path.set_extension(format!("patch_{:03}", patch_number)); + } + + output_path + }); + + let target_name = if op == "replace" { + sub_matches + .get_one::("bundle-file") + .map(|name| match u64::from_str_radix(name, 16) { + Ok(id) => IdString64::from(id), + Err(_) => IdString64::String(name.clone()), + }) + .expect("argument is required") + } else { + let mut path = PathBuf::from(in_file_path); + path.set_extension(""); + IdString64::from(path.to_slash_lossy().to_string()) }; - if let Some(name) = matches.get_one::("replace") { - let mut file = File::open(&file_path) - .await - .wrap_err_with(|| format!("Failed to open '{}'", file_path.display()))?; + let file_type = if let Some(forced_type) = matches.get_one::("type") { + BundleFileType::from_str(forced_type.as_str()).wrap_err("Unknown file type")? + } else { + in_file_path + .extension() + .and_then(|s| s.to_str()) + .ok_or_eyre("File extension missing") + .and_then(BundleFileType::from_str) + .wrap_err("Unknown file type") + .with_suggestion(|| "Use '--type TYPE' to specify the file type")? + }; - if let Some(variant) = bundle - .files_mut() - .filter(|file| file.matches_name(name.clone())) - // TODO: Handle file variants - .find_map(|file| file.variants_mut().next()) - { - let mut data = Vec::new(); - file.read_to_end(&mut data) - .await - .wrap_err("Failed to read input file")?; - variant.set_data(data); - } else { - let err = eyre::eyre!("No file '{}' in this bundle.", name) - .with_suggestion(|| { + { + let span = tracing::Span::current(); + if !span.is_disabled() { + span.record("bundle_path", bundle_path.display().to_string()); + span.record("in_file_path", in_file_path.display().to_string()); + span.record("output_path", output_path.display().to_string()); + span.record("raw", sub_matches.get_flag("raw")); + span.record("target_name", target_name.display().to_string()); + span.record("file_type", format!("{:?}", file_type)); + } + } + + let bundle_name = Bundle::get_name_from_path(&ctx, bundle_path); + let mut bundle = { + let binary = fs::read(bundle_path).await?; + Bundle::from_binary(&ctx, bundle_name.clone(), binary) + .wrap_err_with(|| format!("Failed to open bundle '{}'", bundle_path.display()))? + }; + + if op == "copy" { + unimplemented!("Implement copying a file from one bundle to the other."); + } + + let output_bundle = match op { + "replace" => { + let Some(file) = bundle + .files_mut() + .find(|file| *file.base_name() == target_name) + else { + let err = eyre::eyre!( + "No file with name '{}' in bundle '{}'", + target_name.display(), + bundle_path.display() + ); + + return Err(err).with_suggestion(|| { format!( - "Run '{} bundle list {}' to list the files in this bundle.", + "Run '{} bundle list \"{}\"' to list the files in this bundle.", clap::crate_name!(), bundle_path.display() ) - }) - .with_suggestion(|| { - format!( - "Use '{} bundle inject --add {} {} {}' to add it as a new file", - clap::crate_name!(), - name, - bundle_path.display(), - file_path.display() - ) }); + }; - return Err(err); + if sub_matches.get_flag("raw") { + let variant_index = sub_matches + .get_one::("variant") + .expect("argument with default missing"); + + let Some(variant) = file.variants_mut().nth(*variant_index as usize) else { + let err = eyre::eyre!( + "Variant index '{}' does not exist in '{}'", + variant_index, + target_name.display() + ); + + return Err(err).with_suggestion(|| { + format!( + "See '{} bundle inject add --help' if you want to add it as a new file", + clap::crate_name!(), + ) + }); + }; + + let data = tokio::fs::read(&in_file_path).await.wrap_err_with(|| { + format!("Failed to read file '{}'", in_file_path.display()) + })?; + variant.set_data(data); + file.set_modded(true); + bundle + } else { + let mut bundle_file = compile_file(in_file_path, target_name.clone(), file_type) + .await + .wrap_err("Failed to compile")?; + + bundle_file.set_modded(true); + + if patch_number.is_some() { + let mut output_bundle = Bundle::new(bundle_name); + output_bundle.add_file(bundle_file); + output_bundle + } else { + *file = bundle_file; + + dbg!(&file); + bundle + } + } } + "add" => { + unimplemented!("Implement adding a new file to the bundle."); + } + _ => unreachable!("no other operations exist"), + }; - let out_path = matches.get_one::("output").unwrap_or(bundle_path); - let data = bundle - .to_binary() - .wrap_err("Failed to write changed bundle to output")?; + let data = output_bundle + .to_binary() + .wrap_err("Failed to write changed bundle to output")?; - fs::write(out_path, &data) - .await - .wrap_err("Failed to write data to output file")?; + fs::write(&output_path, &data) + .await + .wrap_err_with(|| format!("Failed to write data to '{}'", output_path.display()))?; - Ok(()) - } else { - eyre::bail!("Currently, only the '--replace' operation is supported."); - } + tracing::info!("Modified bundle written to '{}'", output_path.display()); + + Ok(()) } diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index f387409..6d49821 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -20,6 +20,7 @@ struct BundleFileHeader { len_data_file_name: usize, } +#[derive(Clone, Debug)] pub struct BundleFileVariant { property: u32, data: Vec, @@ -109,9 +110,12 @@ bitflags! { #[derive(Default, Clone, Copy, Debug)] pub struct Properties: u32 { const DATA = 0b100; + // A custom flag used by DTMT to signify a file altered by mods. + const MODDED = 1 << 31; } } +#[derive(Clone, Debug)] pub struct BundleFile { file_type: BundleFileType, name: IdString64, @@ -133,6 +137,18 @@ impl BundleFile { self.variants.push(variant) } + pub fn set_variants(&mut self, variants: Vec) { + self.variants = variants; + } + + pub fn set_props(&mut self, props: Properties) { + self.props = props; + } + + pub fn set_modded(&mut self, is_modded: bool) { + self.props.set(Properties::MODDED, is_modded); + } + #[tracing::instrument(name = "File::read", skip(ctx, r))] pub fn from_reader(ctx: &crate::Context, r: &mut R, props: Properties) -> Result where @@ -299,14 +315,13 @@ impl BundleFile { s } - pub fn matches_name(&self, name: impl Into) -> bool { - let name = name.into(); - if self.name == name { + pub fn matches_name(&self, name: &IdString64) -> bool { + if self.name == *name { return true; } if let IdString64::String(name) = name { - self.name(false, None) == name || self.name(true, None) == name + self.name(false, None) == *name || self.name(true, None) == *name } else { false } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 075f4d2..edb71bb 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -7,14 +7,13 @@ use color_eyre::{Help, Report, SectionExt}; use oodle::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; use crate::binary::sync::*; -use crate::bundle::file::Properties; use crate::murmur::{HashGroup, IdString64, Murmur64}; pub(crate) mod database; pub(crate) mod file; pub(crate) mod filetype; -pub use file::{BundleFile, BundleFileVariant}; +pub use file::{BundleFile, BundleFileVariant, Properties}; pub use filetype::BundleFileType; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index a24b3bd..9b1806b 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -9,5 +9,5 @@ pub mod murmur; pub use binary::{FromBinary, ToBinary}; pub use bundle::database::BundleDatabase; pub use bundle::decompress; -pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant}; +pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant, Properties}; pub use context::{CmdLine, Context}; From f521e20f2b61aef35315081c7f6c78746acda679 Mon Sep 17 00:00:00 2001 From: Renovate Date: Thu, 15 May 2025 22:16:23 +0000 Subject: [PATCH 166/184] chore(deps): update rust crate csv-async to v1.3.1 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4ec109..88a9bb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,9 +704,9 @@ dependencies = [ [[package]] name = "csv-async" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37fe5b0d07f4a8260ce1e9a81413e88f459af0f2dfc55c15e96868a2f99c0f0" +checksum = "888dbb0f640d2c4c04e50f933885c7e9c95995d93cec90aba8735b4c610f26f1" dependencies = [ "cfg-if", "csv-core", @@ -2173,7 +2173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From 164cb7bc131199a5089e9a2cb8dffe89fabbc9ff Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 12:46:31 +0000 Subject: [PATCH 167/184] chore(deps): update rust crate tempfile to v3.20.0 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88a9bb9..bc37db3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3710,15 +3710,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4543,7 +4543,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From ca677606fa25fd62466963e3e65efc7db82bb5f9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:25 +0000 Subject: [PATCH 168/184] chore(deps): update rust crate bitflags to v2.9.1 --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..aa01c87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,7 +233,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", @@ -253,7 +253,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", @@ -275,9 +275,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.9.0", + "bitflags 2.9.1", "clap", "color-eyre", "colors-transform", @@ -1955,7 +1955,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -2182,7 +2182,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] @@ -2445,7 +2445,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "filetime", "fsevent-sys", "inotify", @@ -2548,7 +2548,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -2986,7 +2986,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -3178,7 +3178,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.4.14", @@ -3191,7 +3191,7 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", @@ -3325,7 +3325,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "color-eyre", "csv-async", @@ -3352,7 +3352,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -3674,7 +3674,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "system-configuration-sys", ] @@ -4851,7 +4851,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] From 1975435805f5aded7a9eaf3cb9c0a6eefd1c9ff0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:29 +0000 Subject: [PATCH 169/184] chore(deps): update rust crate clap to v4.5.38 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..ed45902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3718,7 +3718,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From 1e9738c953051cd678000ab78e2cd01b2428e30c Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:37 +0000 Subject: [PATCH 170/184] chore(deps): update rust crate tokio to v1.45.0 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..29df0e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3718,7 +3718,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3850,9 +3850,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", From 5e1581b428f028ef637c05b2d10598d6bbb148fc Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 08:31:36 +0000 Subject: [PATCH 171/184] chore(deps): update rust crate minijinja to v2.10.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eb31bc..7fb7b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2302,9 +2302,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.9.0" +version = "2.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" dependencies = [ "serde", ] From 14eded5b7e1d3b43bcbd6b5d04860fe69ee4ea42 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 08:31:45 +0000 Subject: [PATCH 172/184] chore(deps): update rust crate zip to v3 --- Cargo.lock | 41 +++++++++++++++++++++++++---------------- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eb31bc..212dc5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,12 +686,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crypto-common" version = "0.1.6" @@ -1118,12 +1112,13 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.32" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "libz-rs-sys", + "miniz_oxide 0.8.8", ] [[package]] @@ -2173,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2187,6 +2182,15 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2327,9 +2331,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -4543,7 +4547,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -4965,14 +4969,13 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" dependencies = [ "arbitrary", "bzip2", "crc32fast", - "crossbeam-utils", "flate2", "indexmap", "memchr", @@ -4981,6 +4984,12 @@ dependencies = [ "zstd", ] +[[package]] +name = "zlib-rs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" + [[package]] name = "zopfli" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 87d9ea6..4b083a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "3.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 From 6d576be4ae62d95e4e827cda3a1123430cf31120 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 09:16:27 +0000 Subject: [PATCH 173/184] chore(deps): update rust crate confy to v1 --- Cargo.lock | 31 +++++++++++++++++++++---------- Cargo.toml | 2 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..f4ee177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,13 +581,13 @@ checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" [[package]] name = "confy" -version = "0.6.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" +checksum = "f29222b549d4e3ded127989d523da9e928918d0d0d7f7c1690b439d0d538bae9" dependencies = [ "directories", "serde", - "thiserror 1.0.63", + "thiserror 2.0.12", "toml 0.8.19", ] @@ -766,9 +766,9 @@ dependencies = [ [[package]] name = "directories" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ "dirs-sys", ] @@ -785,14 +785,14 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -802,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -3004,6 +3004,17 @@ dependencies = [ "thiserror 1.0.63", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", +] + [[package]] name = "regex" version = "1.11.1" diff --git a/Cargo.toml b/Cargo.toml index 4b083a9..3619e5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "str cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } colors-transform = "0.2.11" -confy = "0.6.1" +confy = "1.0.0" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } druid-widget-nursery = "0.1" From 27062d22040199a00db630623f4e76d4c0b907e9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 22:01:36 +0000 Subject: [PATCH 174/184] chore(deps): update rust crate zip to v4 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..831d598 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4969,9 +4969,9 @@ dependencies = [ [[package]] name = "zip" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" +checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd" dependencies = [ "arbitrary", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index 4b083a9..eb0dceb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = { version = "3.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "4.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 From 220f37c7288c16e84c7a24a0c985b0d73cf99dc1 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 24 May 2025 15:01:28 +0000 Subject: [PATCH 175/184] chore(deps): update rust crate tokio to v1.45.1 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..555a11a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3854,9 +3854,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", From 3901355f9d89104d5c9f461609852702ba4462f7 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 27 May 2025 18:16:25 +0000 Subject: [PATCH 176/184] chore(deps): update rust crate clap to v4.5.39 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..09c09c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", @@ -2168,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From d66dcb5cfd9a83f376c389add3eca4d71a575fd6 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 28 May 2025 16:31:22 +0000 Subject: [PATCH 177/184] fix(deps): update rust crate reqwest to v0.12.18 --- Cargo.lock | 73 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..e9263f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1742,22 +1742,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2014,6 +2020,16 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -2168,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3050,15 +3066,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -3075,21 +3090,20 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -3215,21 +3229,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" @@ -3674,9 +3681,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", "core-foundation", @@ -3995,6 +4002,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4547,7 +4572,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 138cb79ff6285b6ea504d287e2c4509275cdeaad Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 30 May 2025 11:52:44 +0200 Subject: [PATCH 178/184] Disable excessive rebase for Renovate --- .renovaterc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.renovaterc b/.renovaterc index 4a2fbf4..7ad59cd 100644 --- a/.renovaterc +++ b/.renovaterc @@ -3,8 +3,7 @@ "extends": [ "config:recommended", ":combinePatchMinorReleases", - ":enableVulnerabilityAlerts", - ":rebaseStalePrs" + ":enableVulnerabilityAlerts" ], "prConcurrentLimit": 10, "branchPrefix": "renovate/", From bb9223a43eaf2aba6cbfc38660bd9c5769e3dfe6 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 2 Jun 2025 12:16:19 +0000 Subject: [PATCH 179/184] fix(deps): update rust crate reqwest to v0.12.19 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f1039a..87a86de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.18" +version = "0.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" +checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" dependencies = [ "base64 0.22.1", "bytes", @@ -4015,9 +4015,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "5cc2d9e086a412a451384326f521c8123a99a466b329941a9403696bff9b0da2" dependencies = [ "bitflags 2.9.1", "bytes", From 8749d4e6be6974bf91050d702b759a32f5081516 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sun, 8 Jun 2025 11:46:19 +0000 Subject: [PATCH 180/184] chore(deps): update rust crate bindgen to 0.72.0 --- Cargo.lock | 10 +++++----- lib/oodle/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..89bb171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ "bitflags 2.9.1", "cexpr", @@ -2184,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2546,7 +2546,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen 0.71.1", + "bindgen 0.72.0", "color-eyre", "tracing", ] @@ -4583,7 +4583,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index e679b5f..4a6fe2f 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = { workspace = true } tracing = { workspace = true } [build-dependencies] -bindgen = "0.71.0" +bindgen = "0.72.0" From 4fd17a2d0d42673c626b46bd378e53de2076d2ee Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 9 Jun 2025 18:16:20 +0000 Subject: [PATCH 181/184] chore(deps): update rust crate clap to v4.5.40 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..5e49e15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -450,9 +450,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck 0.5.0", "proc-macro2", From 6b7d5265ad64c76d5babf8fb33c7109a165c9b29 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 10 Jun 2025 19:01:29 +0000 Subject: [PATCH 182/184] fix(deps): update rust crate reqwest to v0.12.20 --- Cargo.lock | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..4136148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", @@ -3093,12 +3093,10 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "rustls-pki-types", From fb072e1fba9f5e21670628c0c01a21ff33826f4e Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 11 Jun 2025 09:31:38 +0000 Subject: [PATCH 183/184] chore(deps): update rust crate nanorand to 0.8.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe6f1cd..44197f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2369,9 +2369,9 @@ dependencies = [ [[package]] name = "nanorand" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4" [[package]] name = "native-tls" diff --git a/Cargo.toml b/Cargo.toml index 8d51a45..99ce493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false, features = ["serde"] } -nanorand = "0.7.0" +nanorand = "0.8.0" nexusmods = { path = "lib/nexusmods" } notify = "8.0.0" oodle = { path = "lib/oodle" } From 03a11269a244f0d2b088d28daed5368f7ccb2a12 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sun, 15 Jun 2025 04:01:33 +0000 Subject: [PATCH 184/184] chore(deps): update rust crate zip to v4.1.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44197f8..609555e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5003,9 +5003,9 @@ dependencies = [ [[package]] name = "zip" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd" +checksum = "af7dcdb4229c0e79c2531a24de7726a0e980417a74fb4d030a35f535665439a0" dependencies = [ "arbitrary", "bzip2",