aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/mod_management
diff options
context:
space:
mode:
Diffstat (limited to 'src-tauri/src/mod_management')
-rw-r--r--src-tauri/src/mod_management/mod.rs200
1 files changed, 196 insertions, 4 deletions
diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs
index a2aca85a..2ca613ca 100644
--- a/src-tauri/src/mod_management/mod.rs
+++ b/src-tauri/src/mod_management/mod.rs
@@ -4,15 +4,17 @@ use crate::constants::{BLACKLISTED_MODS, CORE_MODS};
use async_recursion::async_recursion;
use crate::NorthstarMod;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
+use std::str::FromStr;
+use std::string::ToString;
use std::{fs, path::PathBuf};
mod legacy;
use crate::GameInstall;
#[derive(Debug, Clone)]
-struct ParsedThunderstoreModString {
+pub struct ParsedThunderstoreModString {
author_name: String,
mod_name: String,
version: String,
@@ -22,6 +24,12 @@ impl std::str::FromStr for ParsedThunderstoreModString {
type Err = &'static str; // todo use an better error management
fn from_str(s: &str) -> Result<Self, Self::Err> {
+ // Check whether Thunderstore string passse reges
+ let re = regex::Regex::new(r"^[a-zA-Z0-9_]+-[a-zA-Z0-9_]+-\d+\.\d+\.\d++$").unwrap();
+ if !re.is_match(s) {
+ return Err("Incorrect format");
+ }
+
let mut parts = s.split('-');
let author_name = parts.next().ok_or("None value on author_name")?.to_string();
@@ -36,6 +44,12 @@ impl std::str::FromStr for ParsedThunderstoreModString {
}
}
+impl ToString for ParsedThunderstoreModString {
+ fn to_string(&self) -> String {
+ format!("{}-{}-{}", self.author_name, self.mod_name, self.version)
+ }
+}
+
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ThunderstoreManifest {
name: String,
@@ -170,6 +184,175 @@ pub fn set_mod_enabled_status(
Ok(())
}
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct ModJson {
+ #[serde(rename = "Name")]
+ name: String,
+ #[serde(rename = "Version")]
+ version: Option<String>,
+}
+
+/// Parse `mods` folder for installed mods.
+pub fn parse_mods_in_package(
+ package_mods_path: PathBuf,
+ thunderstore_mod_string: ParsedThunderstoreModString,
+) -> Result<Vec<NorthstarMod>, anyhow::Error> {
+ dbg!(package_mods_path.clone());
+ let paths = match std::fs::read_dir(package_mods_path) {
+ Ok(paths) => paths,
+ Err(_err) => return Err(anyhow!("No mods folder found")),
+ };
+
+ let mut directories: Vec<PathBuf> = Vec::new();
+ let mut mods: Vec<NorthstarMod> = Vec::new();
+
+ log::info!("----------- {paths:?}");
+
+ // Get list of folders in `mods` directory
+ for path in paths {
+ // dbg!(path);
+ log::info!("{path:?}");
+ let my_path = path.unwrap().path();
+ log::info!("{my_path:?}");
+
+ let md = std::fs::metadata(my_path.clone()).unwrap();
+ if md.is_dir() {
+ directories.push(my_path);
+ }
+ }
+
+ dbg!(directories.clone());
+ // todo!();
+ // Iterate over folders and check if they are Northstar mods
+ for directory in directories {
+ let directory_str = directory.to_str().unwrap().to_string();
+ // Check if mod.json exists
+ let mod_json_path = format!("{}/mod.json", directory_str);
+ if !std::path::Path::new(&mod_json_path).exists() {
+ continue;
+ }
+
+ // Parse mod.json and get mod name
+
+ // Read file into string and parse it
+ let data = std::fs::read_to_string(mod_json_path.clone())?;
+ let parsed_mod_json: ModJson = match json5::from_str(&data) {
+ Ok(parsed_json) => parsed_json,
+ Err(err) => {
+ log::warn!("Failed parsing {} with {}", mod_json_path, err.to_string());
+ continue;
+ }
+ };
+
+ // Get directory path
+ let mod_directory = directory.to_str().unwrap().to_string();
+
+ let ns_mod = NorthstarMod {
+ name: parsed_mod_json.name,
+ version: parsed_mod_json.version,
+ thunderstore_mod_string: Some(thunderstore_mod_string.to_string()),
+ enabled: false, // Placeholder
+ directory: mod_directory,
+ };
+
+ mods.push(ns_mod);
+ }
+
+ // Return found mod names
+ Ok(mods)
+}
+
+/// Parse `packages` folder for installed mods.
+pub fn parse_installed_package_mods(
+ game_install: &GameInstall,
+) -> Result<Vec<NorthstarMod>, anyhow::Error> {
+ let mut collected_mods: Vec<NorthstarMod> = Vec::new();
+
+ let packages_folder = format!("{}/R2Northstar/packages/", game_install.game_path);
+
+ let packages_dir = match fs::read_dir(packages_folder) {
+ Ok(res) => res,
+ Err(err) => {
+ // We couldn't read directory, probably cause it doesn't exist yet.
+ // In that case we just say no package mods installed.
+ log::warn!("{err}");
+ return Ok(vec![]);
+ }
+ };
+
+ // Iteratore over folders in `packages` dir
+ for entry in packages_dir {
+ let entry = entry?;
+ let entry_path = entry.path();
+ let entry_str = entry_path.file_name().unwrap().to_str().unwrap();
+ log::warn!("Trying: {entry_str}");
+
+ // Use the struct's from_str function to verify format
+ if entry_path.is_dir() {
+ let package_thunderstore_string = match ParsedThunderstoreModString::from_str(entry_str)
+ {
+ Ok(res) => res,
+ Err(err) => {
+ log::warn!(
+ "Not a Thunderstore mod string \"{}\" cause: {}",
+ entry_path.display(),
+ err
+ );
+ continue;
+ }
+ };
+ let manifest_path = entry_path.join("manifest.json");
+ let mods_path = entry_path.join("mods");
+
+ // Ensure `manifest.json` and `mods/` dir exist
+ if manifest_path.exists() && mods_path.is_dir() {
+ dbg!(mods_path.clone());
+
+ // Do something with mod path here
+ log::warn!("{}", mods_path.to_string_lossy());
+
+ // let paths = match std::fs::read_dir(mods_path) {
+ // Ok(paths) => paths,
+ // Err(_err) => todo!(),
+ // };
+
+ // // Get list of folders in `mods` directory
+ // for path in paths {
+ // log::info!("{path:?}");
+ // let my_path = path.unwrap().path();
+ // log::info!("{my_path:?}");
+
+ // let md = std::fs::metadata(my_path.clone()).unwrap();
+ // if !md.is_dir() {
+ // continue;
+ // }
+ log::warn!("Found: {}", mods_path.display());
+ let mods =
+ match parse_mods_in_package(mods_path, package_thunderstore_string.clone()) {
+ Ok(res) => res,
+ Err(err) => {
+ log::warn!("Failed parsing cause: {err}");
+ continue;
+ }
+ };
+ dbg!(mods.clone());
+ collected_mods.extend(mods);
+ // }
+
+ // let ns_mod = NorthstarMod {
+ // name: parsed_mod_json.name,
+ // version: parsed_mod_json.version,
+ // thunderstore_mod_string,
+ // enabled: false, // Placeholder
+ // directory: mod_directory,
+ // };
+ }
+ }
+ }
+
+ Ok(collected_mods)
+}
+
/// Gets list of installed mods and their properties
/// - name
/// - is enabled?
@@ -177,12 +360,21 @@ pub fn set_mod_enabled_status(
pub fn get_installed_mods_and_properties(
game_install: GameInstall,
) -> Result<Vec<NorthstarMod>, String> {
- // Get actually installed mods
- let found_installed_mods = match legacy::parse_installed_mods(&game_install) {
+ log::info!("{game_install:?}");
+ // Get installed mods from packages
+ let mut found_installed_mods = match parse_installed_package_mods(&game_install) {
+ Ok(res) => res,
+ Err(err) => return Err(err.to_string()),
+ };
+ // Get installed legacy mods
+ let found_installed_legacy_mods = match legacy::parse_installed_mods(&game_install) {
Ok(res) => res,
Err(err) => return Err(err.to_string()),
};
+ // Combine list of package and legacy mods
+ found_installed_mods.extend(found_installed_legacy_mods);
+
// Get enabled mods as JSON
let enabled_mods: serde_json::Value = match get_enabled_mods(&game_install) {
Ok(enabled_mods) => enabled_mods,