Darktide Mod Manager #39

Merged
lucas merged 91 commits from feat/dtmm into master 2023-03-01 22:27:42 +01:00
6 changed files with 91 additions and 15 deletions
Showing only changes of commit 55a1fc9723 - Show all commits

7
Cargo.lock generated
View file

@ -702,6 +702,7 @@ dependencies = [
"libloading", "libloading",
"nanorand", "nanorand",
"oodle-sys", "oodle-sys",
"path-clean",
"pin-project-lite", "pin-project-lite",
"promptly", "promptly",
"sdk", "sdk",
@ -1698,6 +1699,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "path-clean"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef"
[[package]] [[package]]
name = "pbkdf2" name = "pbkdf2"
version = "0.11.0" version = "0.11.0"

View file

@ -163,22 +163,22 @@ fn build_mod_data_lua(state: Arc<State>) -> String {
lua.push_str(" new_mod(\""); lua.push_str(" new_mod(\"");
lua.push_str(mod_info.get_id()); lua.push_str(mod_info.get_id());
lua.push_str("\", {\n init = \""); lua.push_str("\", {\n init = \"");
lua.push_str(resources.get_init()); lua.push_str(&resources.get_init().to_string_lossy());
if let Some(data) = resources.get_data() { if let Some(data) = resources.get_data() {
lua.push_str("\",\n data = \""); lua.push_str("\",\n data = \"");
lua.push_str(data); lua.push_str(&data.to_string_lossy());
} }
if let Some(localization) = resources.get_localization() { if let Some(localization) = resources.get_localization() {
lua.push_str("\",\n localization = \""); lua.push_str("\",\n localization = \"");
lua.push_str(localization); lua.push_str(&localization.to_string_lossy());
} }
lua.push_str("\",\n })\n"); lua.push_str("\",\n })\n");
} else { } else {
lua.push_str(" return dofile(\""); lua.push_str(" return dofile(\"");
lua.push_str(resources.get_init()); lua.push_str(&resources.get_init().to_string_lossy());
lua.push_str("\")"); lua.push_str("\")");
} }

View file

@ -61,23 +61,23 @@ impl PackageInfo {
} }
} }
#[derive(Clone, Data, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ModResourceInfo { pub(crate) struct ModResourceInfo {
init: String, init: PathBuf,
data: Option<String>, data: Option<PathBuf>,
localization: Option<String>, localization: Option<PathBuf>,
} }
impl ModResourceInfo { impl ModResourceInfo {
pub(crate) fn get_init(&self) -> &String { pub(crate) fn get_init(&self) -> &PathBuf {
&self.init &self.init
} }
pub(crate) fn get_data(&self) -> Option<&String> { pub(crate) fn get_data(&self) -> Option<&PathBuf> {
self.data.as_ref() self.data.as_ref()
} }
pub(crate) fn get_localization(&self) -> Option<&String> { pub(crate) fn get_localization(&self) -> Option<&PathBuf> {
self.localization.as_ref() self.localization.as_ref()
} }
} }
@ -89,8 +89,10 @@ pub(crate) struct ModInfo {
description: Arc<String>, description: Arc<String>,
enabled: bool, enabled: bool,
#[lens(ignore)] #[lens(ignore)]
#[data(ignore)]
packages: Vector<PackageInfo>, packages: Vector<PackageInfo>,
#[lens(ignore)] #[lens(ignore)]
#[data(ignore)]
resources: ModResourceInfo, resources: ModResourceInfo,
} }

View file

@ -28,6 +28,7 @@ tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
tracing = { version = "0.1.37", features = ["async-await"] } tracing = { version = "0.1.37", features = ["async-await"] }
zip = "0.6.3" zip = "0.6.3"
path-clean = "1.0.1"
[dev-dependencies] [dev-dependencies]
tempfile = "3.3.0" tempfile = "3.3.0"

View file

@ -164,6 +164,23 @@ where
.wrap_err("failed to build bundle") .wrap_err("failed to build bundle")
} }
fn normalize_file_path<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
let path = path.as_ref();
if path.is_absolute() || path.has_root() {
let err = eyre::eyre!("path is absolute: {}", path.display());
return Err(err).with_suggestion(|| "Specify a relative file path.".to_string());
}
let path = path_clean::clean(path);
if path.starts_with("..") {
eyre::bail!("path starts with a parent component: {}", path.display());
}
Ok(path)
}
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> {
unsafe { unsafe {
@ -172,7 +189,54 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()>
let cfg = { let cfg = {
let dir = matches.get_one::<PathBuf>("directory").cloned(); let dir = matches.get_one::<PathBuf>("directory").cloned();
find_project_config(dir).await? let mut cfg = find_project_config(dir).await?;
cfg.resources.init = normalize_file_path(cfg.resources.init)
.wrap_err("invalid config field 'resources.init'")
.with_suggestion(|| {
"Specify a file path relative to and child path of the \
directory where 'dtmt.cfg' is."
.to_string()
})
.with_suggestion(|| {
"Use 'dtmt new' in a separate directory to generate \
a valid mod template."
.to_string()
})?;
if let Some(path) = cfg.resources.data {
let path = normalize_file_path(path)
.wrap_err("invalid config field 'resources.data'")
.with_suggestion(|| {
"Specify a file path relative to and child path of the \
directory where 'dtmt.cfg' is."
.to_string()
})
.with_suggestion(|| {
"Use 'dtmt new' in a separate directory to generate \
a valid mod template."
.to_string()
})?;
cfg.resources.data = Some(path);
}
if let Some(path) = cfg.resources.localization {
let path = normalize_file_path(path)
.wrap_err("invalid config field 'resources.localization'")
.with_suggestion(|| {
"Specify a file path relative to and child path of the \
directory where 'dtmt.cfg' is."
.to_string()
})
.with_suggestion(|| {
"Use 'dtmt new' in a separate directory to generate \
a valid mod template."
.to_string()
})?;
cfg.resources.localization = Some(path);
}
cfg
}; };
let dest = { let dest = {

View file

@ -1,14 +1,16 @@
mod log; mod log;
use std::path::PathBuf;
pub use log::*; pub use log::*;
#[derive(Clone, Debug, Default, serde::Deserialize)] #[derive(Clone, Debug, Default, serde::Deserialize)]
pub struct ModConfigResources { pub struct ModConfigResources {
pub init: String, pub init: PathBuf,
#[serde(default)] #[serde(default)]
pub data: Option<String>, pub data: Option<PathBuf>,
#[serde(default)] #[serde(default)]
pub localization: Option<String>, pub localization: Option<PathBuf>,
} }
#[derive(Clone, Debug, Default, serde::Deserialize)] #[derive(Clone, Debug, Default, serde::Deserialize)]