Merge branch 'mod_management'

This commit is contained in:
SpikeHD
2022-07-26 20:39:54 -07:00
43 changed files with 2100 additions and 275 deletions

View File

@@ -0,0 +1,88 @@
use crate::file_helpers;
use crate::web;
use std::collections::HashMap;
use std::fs::read_dir;
use std::io::Read;
use std::path::PathBuf;
static SITE_URL: &str = "https://gamebanana.com";
#[tauri::command]
pub async fn get_download_links(mod_id: String) -> String {
let res = web::query(format!("{}/apiv9/Mod/{}/DownloadPage", SITE_URL, mod_id).as_str()).await;
res
}
#[tauri::command]
pub async fn list_submissions(mode: String) -> String {
let res = web::query(
format!(
"{}/apiv9/Util/Game/Submissions?_idGameRow=8552&_nPage=1&_nPerpage=50&_sMode={}",
SITE_URL, mode
)
.as_str(),
)
.await;
res
}
#[tauri::command]
pub async fn list_mods(path: String) -> HashMap<String, String> {
let mut path_buf = PathBuf::from(path);
// If the path includes a file, remove it
if path_buf.file_name().is_some() {
path_buf.pop();
}
// Ensure we are in the Mods folder
path_buf.push("Mods");
// Check if dir is empty
if file_helpers::dir_is_empty(path_buf.to_str().unwrap()) {
return HashMap::new();
}
let mut mod_info_files = vec![];
let mut mod_info_strings = HashMap::new();
for entry in read_dir(path_buf).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
// Check each dir for a modinfo.json file
if path.is_dir() {
let mut mod_info_path = path.clone();
mod_info_path.push("modinfo.json");
if mod_info_path.exists() {
// Push path AND file contents into the hashmap using path as key
mod_info_files.push(mod_info_path.to_str().unwrap().to_string());
} else {
// No modinfo, but we can still push a JSON obj with the folder name
mod_info_strings.insert(
path.to_str().unwrap().to_string(),
format!(
"{{ \"name\": \"{}\" }}",
path.file_name().unwrap().to_str().unwrap()
),
);
}
}
}
// Read each modinfo.json file
for mod_info_file in mod_info_files {
let mut mod_info_string = String::new();
// It is safe to unwrap here since we *know* that the file exists
let mut file = std::fs::File::open(&mod_info_file).unwrap();
file.read_to_string(&mut mod_info_string).unwrap();
// Push into hashmap using path as key
mod_info_strings.insert(mod_info_file, mod_info_string);
}
mod_info_strings
}

View File

@@ -12,6 +12,7 @@ use sysinfo::{System, SystemExt};
mod downloader;
mod file_helpers;
mod gamebanana;
mod lang;
mod metadata_patcher;
mod proxy;
@@ -59,6 +60,9 @@ fn main() {
lang::get_languages,
web::valid_url,
web::web_get,
gamebanana::get_download_links,
gamebanana::list_submissions,
gamebanana::list_mods,
metadata_patcher::patch_metadata
])
.run(tauri::generate_context!())

View File

@@ -1,12 +1,30 @@
use duct::cmd;
#[tauri::command]
pub fn run_program(path: String) {
// Open in new thread to prevent blocking.
std::thread::spawn(move || {
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(&path).unwrap_or(());
});
pub fn run_program(path: String, args: Option<String>) {
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, &args.unwrap_or("".into()))).unwrap_or(());
}
#[tauri::command]
pub fn run_program_relative(path: String, args: Option<String>) {
// Save the current working directory
let cwd = std::env::current_dir().unwrap();
// Set the new working directory to the path before the executable
let mut path_buf = std::path::PathBuf::from(&path);
path_buf.pop();
// Set new working directory
std::env::set_current_dir(&path_buf).unwrap();
println!("Opening {} {}", &path, args.clone().unwrap_or("".into()));
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, args.unwrap_or("".into()))).unwrap_or(());
// Restore the original working directory
std::env::set_current_dir(&cwd).unwrap();
}
#[tauri::command]
@@ -30,7 +48,13 @@ pub fn run_program_relative(path: String) {
#[tauri::command]
pub fn run_command(program: &str, args: Vec<&str>) {
cmd(program, args).run().expect("Failed to run command");
let prog = program.to_string();
let args = args.iter().map(|s| s.to_string()).collect::<Vec<String>>();
// Commands should not block (this is for the reshade injector mostly)
std::thread::spawn(move || {
cmd(prog, args).run().unwrap();
});
}
#[tauri::command]

View File

@@ -1,9 +1,16 @@
use std::fs::File;
use std::fs::{read_dir, File};
use std::path;
use std::thread;
use unrar::archive::{Archive, OpenArchive};
#[tauri::command]
pub fn unzip(window: tauri::Window, zipfile: String, destpath: String) {
pub fn unzip(
window: tauri::Window,
zipfile: String,
destpath: String,
top_level: Option<bool>,
folder_if_loose: Option<bool>,
) {
// Read file TODO: replace test file
let f = match File::open(&zipfile) {
Ok(f) => f,
@@ -15,40 +22,80 @@ pub fn unzip(window: tauri::Window, zipfile: String, destpath: String) {
let write_path = path::PathBuf::from(&destpath);
// Get a list of all current directories
let mut dirs = vec![];
for entry in read_dir(&write_path).unwrap() {
let entry = entry.unwrap();
let entry_path = entry.path();
if entry_path.is_dir() {
dirs.push(entry_path);
}
}
// Run extraction in seperate thread
thread::spawn(move || {
let full_path = write_path;
let mut full_path = write_path.clone();
window.emit("extract_start", &zipfile).unwrap();
match zip_extract::extract(&f, &full_path, true) {
Ok(_) => {
println!(
"Extracted zip file to: {}",
full_path.to_str().unwrap_or("Error")
);
}
Err(e) => {
println!("Failed to extract zip file: {}", e);
let mut res_hash = std::collections::HashMap::new();
if folder_if_loose.unwrap_or(false) {
// Create a new folder with the same name as the zip file
let mut file_name = path::Path::new(&zipfile)
.file_name()
.unwrap()
.to_str()
.unwrap();
res_hash.insert("error".to_string(), e.to_string());
// remove ".zip" from the end of the file name
file_name = &file_name[..file_name.len() - 4];
res_hash.insert("path".to_string(), zipfile.to_string());
let new_path = full_path.join(file_name);
match std::fs::create_dir_all(&new_path) {
Ok(_) => {}
Err(e) => {
println!("Failed to create directory: {}", e);
return;
}
};
window.emit("download_error", &res_hash).unwrap();
}
};
full_path = new_path.clone();
}
// Get the name of the inenr file in the zip file
let mut zip = zip::ZipArchive::new(&f).unwrap();
let file = zip.by_index(0).unwrap();
let name = file.name();
println!("Is rar file? {}", zipfile.ends_with(".rar"));
let mut name = "".into();
// If file ends in zip, OR is unknown, extract as zip, otherwise extract as rar
if zipfile.ends_with(".rar") {
extract_rar(
&window,
&zipfile,
&f,
&full_path,
top_level.unwrap_or(false),
);
let archive = Archive::new(zipfile.clone());
name = archive.list().unwrap().next().unwrap().unwrap().filename;
} else {
extract_zip(
&window,
&zipfile,
&f,
&full_path,
top_level.unwrap_or(false),
);
// Get the name of the inenr file in the zip file
let mut zip = zip::ZipArchive::new(&f).unwrap();
let file = zip.by_index(0).unwrap();
name = file.name().to_string().clone();
}
// If the contents is a jar file, emit that we have extracted a new jar file
if name.ends_with(".jar") {
window
.emit("jar_extracted", destpath.to_string() + name)
.emit("jar_extracted", destpath.to_string() + name.as_str())
.unwrap();
}
@@ -62,6 +109,80 @@ pub fn unzip(window: tauri::Window, zipfile: String, destpath: String) {
}
};
window.emit("extract_end", &zipfile).unwrap();
// Get any new directory that could have been created
let mut new_dir: String = String::new();
for entry in read_dir(&write_path).unwrap() {
let entry = entry.unwrap();
let entry_path = entry.path();
if entry_path.is_dir() {
if !dirs.contains(&entry_path) {
new_dir = entry_path.to_str().unwrap().to_string();
}
}
}
let mut res_hash = std::collections::HashMap::new();
res_hash.insert("file", zipfile.to_string());
res_hash.insert("new_folder", new_dir.to_string());
window.emit("extract_end", &res_hash).unwrap();
});
}
fn extract_rar(
window: &tauri::Window,
rarfile: &String,
f: &File,
full_path: &path::PathBuf,
top_level: bool,
) {
let archive = Archive::new(rarfile.clone());
let mut open_archive = archive
.extract_to(full_path.to_str().unwrap().to_string())
.unwrap();
match open_archive.process() {
Ok(_) => {
println!(
"Extracted rar file to: {}",
full_path.to_str().unwrap_or("Error")
);
}
Err(e) => {
println!("Failed to extract rar file: {}", e);
let mut res_hash = std::collections::HashMap::new();
res_hash.insert("error".to_string(), e.to_string());
res_hash.insert("path".to_string(), rarfile.to_string());
window.emit("download_error", &res_hash).unwrap();
}
}
}
fn extract_zip(
window: &tauri::Window,
zipfile: &String,
f: &File,
full_path: &path::PathBuf,
top_level: bool,
) {
match zip_extract::extract(f, full_path, top_level) {
Ok(_) => {
println!(
"Extracted zip file to: {}",
full_path.to_str().unwrap_or("Error")
);
}
Err(e) => {
println!("Failed to extract zip file: {}", e);
let mut res_hash = std::collections::HashMap::new();
res_hash.insert("error".to_string(), e.to_string());
res_hash.insert("path".to_string(), zipfile.to_string());
window.emit("download_error", &res_hash).unwrap();
}
};
}

View File

@@ -1,4 +1,4 @@
use reqwest::header::USER_AGENT;
use reqwest::header::{CONTENT_TYPE, USER_AGENT};
pub(crate) async fn query(site: &str) -> String {
let client = reqwest::Client::new();
@@ -6,6 +6,7 @@ pub(crate) async fn query(site: &str) -> String {
let response = client
.get(site)
.header(USER_AGENT, "cultivation")
.header(CONTENT_TYPE, "application/json")
.send()
.await
.unwrap();