geezshade/src/main.rs

204 lines
6.5 KiB
Rust

use copypasta::{ClipboardContext, ClipboardProvider};
use isahc::{prelude::*, ReadResponseExt, Request};
use native_dialog::{FileDialog, MessageDialog, MessageType};
use std::{io::Cursor, path::PathBuf};
use zip::ZipArchive;
const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0";
const RESHADE_URL: &str = "https://reshade.me/downloads/ReShade_Setup_5.6.0_Addon.exe";
const GSHADE_PRESETS_URL: &str =
"https://github.com/Mortalitas/GShade/archive/refs/heads/master.zip";
const GSHADE_COMMUNITY_PRESETS_URL: &str =
"https://github.com/Mortalitas/GShade-Presets/archive/refs/heads/master.zip";
fn get_file(path: &str) -> anyhow::Result<Vec<u8>> {
let req = Request::get(path)
.header("User-Agent", USER_AGENT)
.redirect_policy(isahc::config::RedirectPolicy::Follow);
let mut resp = req.body(())?.send()?;
Ok(resp.bytes()?)
}
fn get_reshade(xiv_install: PathBuf) -> anyhow::Result<()> {
println!("fetching reshade");
let reshade_installer = get_file(RESHADE_URL)?;
println!("extracting dll");
let mut zip = zip::ZipArchive::new(Cursor::new(reshade_installer))?;
let mut dll = zip.by_name("ReShade64.dll")?;
println!("writing dll");
let mut dll_file = std::fs::File::create(xiv_install.join("dxgi.dll"))?;
std::io::copy(&mut dll, &mut dll_file)?;
Ok(())
}
fn get_gshade_presets(xiv_install: PathBuf) -> anyhow::Result<()> {
println!("fetching presets");
let gshade_presets = get_file(GSHADE_PRESETS_URL)?;
let gshade_community_presets = get_file(GSHADE_COMMUNITY_PRESETS_URL)?;
println!("opening presets");
let presets_zip = zip::ZipArchive::new(Cursor::new(gshade_presets))?;
let community_presets_zip = zip::ZipArchive::new(Cursor::new(gshade_community_presets))?;
let presets_folders = vec![
"GShade-master/Textures",
"GShade-master/Shaders",
"GShade-master/ComputeShaders",
];
let community_presets_folders = vec!["GShade-Presets-master/FFXIV"];
let dir = xiv_install.join("reshade-shaders");
println!("extracting presets");
extract(dir.clone(), presets_zip, presets_folders.clone())?;
extract(
dir,
community_presets_zip,
community_presets_folders.clone(),
)?;
Ok(())
}
fn extract(
dir: PathBuf,
presets_zip: ZipArchive<Cursor<Vec<u8>>>,
presets_folders: Vec<&str>,
) -> anyhow::Result<()> {
for filename in presets_zip.file_names() {
for folder in &presets_folders {
if filename.starts_with(folder) {
let mut presets_zip = presets_zip.clone();
let mut file = presets_zip.by_name(filename)?;
let path = filename.split('/').collect::<Vec<&str>>();
let path = path[1..].join("/");
let path = dir.join(path);
if path.is_dir() || path.display().to_string().ends_with('/') {
continue;
}
let dir = path.parent().unwrap().to_path_buf();
if !dir.exists() {
std::fs::create_dir_all(dir)?;
}
let mut out = std::fs::File::create(path)?;
std::io::copy(&mut file, &mut out)?;
}
}
}
Ok(())
}
fn get_game_exe() -> anyhow::Result<PathBuf> {
let shared = "Check the install path in the Settings app in Windows, Steam, or XIVLauncher if you don't know where you installed FFXIV.";
MessageDialog::new()
.set_title("GeezShade")
.set_text(&format!(
"You will now be asked to select ffxiv_dx11.exe from your game install.\n{}",
shared
))
.set_type(MessageType::Info)
.show_alert()?;
loop {
let choice = FileDialog::new()
.add_filter("Executable", &["exe"])
.show_open_single_file()?;
if let Some(path) = choice {
if path.file_name().map(|x| x.to_str().unwrap()) == Some("ffxiv_dx11.exe") {
return Ok(path);
} else {
MessageDialog::new()
.set_title("GeezShade")
.set_text(&format!(
"This is not the right file. Please select ffxiv_dx11.exe.\n{}",
shared
))
.set_type(MessageType::Error)
.show_alert()?;
}
} else {
MessageDialog::new()
.set_title("GeezShade")
.set_text(&format!(
"This is not the right file. Please select ffxiv_dx11.exe.\n{}",
shared
))
.set_type(MessageType::Error)
.show_alert()?;
}
}
}
fn do_the_thing(xiv_install: PathBuf) -> anyhow::Result<()> {
get_reshade(xiv_install.clone())?;
get_gshade_presets(xiv_install.clone())?;
println!("making screenshot folders");
std::fs::create_dir_all(xiv_install.join("reshade-screenshots"))?;
// write config file
let config = r#"
[GENERAL]
EffectSearchPaths=.\reshade-shaders\Shaders\**
TextureSearchPaths=.\reshade-shaders\Textures\**
[SCREENSHOT]
SavePath=.\reshade-screenshots
"#
.trim();
println!("writing config");
std::fs::write(xiv_install.join("ReShade.ini"), config)?;
Ok(())
}
fn main() -> anyhow::Result<()> {
let game_exe = get_game_exe()?;
let xiv_install = game_exe.parent().unwrap();
let choice = MessageDialog::new()
.set_title("GeezShade")
.set_text("GeezShade will now download ReShade and the GShade presets.\nMake a backup before of your presets and settings before continuing.\n\nContinue?")
.set_type(MessageType::Info)
.show_confirm()?;
if !choice {
return Ok(());
}
if let Err(e) = do_the_thing(xiv_install.to_path_buf()) {
MessageDialog::new()
.set_title("GeezShade")
.set_text("An error occurred installing ReShade.\nThis error will be copied to your clipboard.\nPlease report this error to geezshade@notnite.com.")
.set_type(MessageType::Error)
.show_alert()?;
let mut clipboard = ClipboardContext::new().unwrap();
clipboard.set_contents(e.to_string()).unwrap();
return Err(e);
}
MessageDialog::new()
.set_title("GeezShade")
.set_text("GeezShade has finished installing.\nYou can now launch the game and use the ReShade menu to enable your presets.\nEnjoy!")
.set_type(MessageType::Info)
.show_alert()?;
Ok(())
}