269 lines
8.4 KiB
Rust
269 lines
8.4 KiB
Rust
use copypasta::{ClipboardContext, ClipboardProvider};
|
|
use isahc::{prelude::*, ReadResponseExt, Request};
|
|
use native_dialog::{FileDialog, MessageDialog, MessageType};
|
|
use serde::Deserialize;
|
|
use std::{io::Cursor, path::PathBuf};
|
|
use zip::ZipArchive;
|
|
|
|
#[derive(Deserialize, Debug, Clone)]
|
|
struct GeezShadeConfig {
|
|
reshade_url: String,
|
|
gshade_shaders_url: String,
|
|
gshade_presets_url: String,
|
|
|
|
geezshade_version: String,
|
|
geezshade_user_agent: String,
|
|
}
|
|
|
|
fn get_geezshade_config() -> anyhow::Result<GeezShadeConfig> {
|
|
let req = Request::get("https://notnite.com/geezshade.json").header(
|
|
"User-Agent",
|
|
format!("GeezShade/{}", env!("CARGO_PKG_VERSION")),
|
|
);
|
|
|
|
let mut resp = req.body(())?.send()?;
|
|
let text = resp.text()?;
|
|
|
|
let config: GeezShadeConfig = serde_json::from_str(&text)?;
|
|
Ok(config)
|
|
}
|
|
|
|
fn get_file(path: &str, user_agent: String) -> 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, config: GeezShadeConfig) -> anyhow::Result<()> {
|
|
println!("fetching reshade");
|
|
let reshade_installer = get_file(&config.reshade_url, config.geezshade_user_agent)?;
|
|
|
|
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, config: GeezShadeConfig) -> anyhow::Result<()> {
|
|
println!("fetching presets");
|
|
let gshade_shaders = get_file(
|
|
&config.gshade_shaders_url,
|
|
config.geezshade_user_agent.clone(),
|
|
)?;
|
|
let gshade_presets = get_file(&config.gshade_presets_url, config.geezshade_user_agent)?;
|
|
|
|
println!("opening presets");
|
|
let shaders_zip = zip::ZipArchive::new(Cursor::new(gshade_shaders))?;
|
|
let presets_zip = zip::ZipArchive::new(Cursor::new(gshade_presets))?;
|
|
|
|
let shaders_folders = vec![
|
|
"GShade-master/Textures",
|
|
"GShade-master/Shaders",
|
|
"GShade-master/ComputeShaders",
|
|
];
|
|
let presets_folders = vec!["GShade-Presets-master/FFXIV"];
|
|
|
|
let dir = xiv_install.join("reshade-shaders");
|
|
|
|
println!("extracting presets");
|
|
extract(dir.clone(), shaders_zip, shaders_folders.clone())?;
|
|
extract(dir, presets_zip, 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 show_error(message: &str, error: String) {
|
|
MessageDialog::new()
|
|
.set_title("GeezShade")
|
|
.set_text(&format!("{}\n\nAn error message will be copied to your clipboard. Please report this to geezshade@notnite.com.", message))
|
|
.set_type(MessageType::Error)
|
|
.show_alert().unwrap();
|
|
|
|
let mut clipboard = ClipboardContext::new().unwrap();
|
|
clipboard.set_contents(error).unwrap();
|
|
}
|
|
|
|
fn do_the_thing(xiv_install: PathBuf, config: GeezShadeConfig) -> anyhow::Result<()> {
|
|
if let Err(e) = get_reshade(xiv_install.clone(), config.clone()) {
|
|
show_error(
|
|
"Failed to download ReShade. You will have to manually install ReShade yourself.",
|
|
format!("{:?}", e),
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
if let Err(e) = get_gshade_presets(xiv_install.clone(), config) {
|
|
show_error(
|
|
"Failed to download GShade shaders & presets. Please try again later.",
|
|
format!("{:?}", e),
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
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();
|
|
|
|
if !xiv_install.join("ReShade.ini").exists() {
|
|
println!("writing config");
|
|
std::fs::write(xiv_install.join("ReShade.ini"), config)?;
|
|
} else {
|
|
println!("config already exists");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let config = get_geezshade_config();
|
|
|
|
let config = match config {
|
|
Ok(config) => config,
|
|
Err(e) => {
|
|
show_error(
|
|
"An error occured trying to fetch information required for GeezShade.",
|
|
format!("{:?}", e),
|
|
);
|
|
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let semver_current = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
|
let semver_latest = semver::Version::parse(&config.geezshade_version).unwrap();
|
|
|
|
if semver_latest > semver_current {
|
|
let choice = MessageDialog::new()
|
|
.set_title("GeezShade")
|
|
.set_text("A new version of GeezShade is available to download.\nPlease go to https://notnite.com/geezshade for the latest version.\nNot updating may break the installation process, but it is not required.\n\nContinue anyways?")
|
|
.set_type(MessageType::Warning)
|
|
.show_confirm()?;
|
|
|
|
if !choice {
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
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(), config) {
|
|
show_error("An error occured.", format!("{:?}", e));
|
|
|
|
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(())
|
|
}
|