mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-15 00:24:45 +01:00
Merge branch 'mod_management'
This commit is contained in:
88
src-tauri/src/gamebanana.rs
Normal file
88
src-tauri/src/gamebanana.rs
Normal 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
|
||||
}
|
||||
@@ -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!())
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user