use crate::path;
use clap::Parser;
use std::fs;
use std::path::Path;
use std::process::Command;
use toml_edit::{DocumentMut, value};
use which::which;
use rust_i18n;
#[derive(Parser)]
pub struct Args {
#[arg(long, default_value = "master")]
version: String,
}
pub fn run(args: &Args) {
let version: &str = &args.version;
if which("git").is_err() {
eprintln!("{}", rust_i18n::t!("git_not_found"));
std::process::exit(1);
}
let target_dir = path::config_dir().join("SGDK");
if target_dir.exists() {
println!("{}", rust_i18n::t!("sgdk_exists_overwrite_prompt"));
use std::io::{self, Write};
print!("{}", rust_i18n::t!("prompt"));
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
let input = input.trim().to_lowercase();
if input != "y" {
println!("{}", rust_i18n::t!("sgdk_overwrite_cancelled"));
std::process::exit(0);
}
reset_sgdk_worktree(&target_dir);
if target_dir.join(".git").exists() {
let fetch_status = Command::new("git")
.arg("fetch")
.current_dir(&target_dir)
.status()
.expect(&rust_i18n::t!("sgdk_git_fetch_failed"));
if !fetch_status.success() {
eprintln!("{}", rust_i18n::t!("sgdk_git_fetch_failed"));
std::process::exit(1);
}
let checkout_status = Command::new("git")
.arg("checkout")
.arg(version)
.current_dir(&target_dir)
.status()
.expect(&rust_i18n::t!("sgdk_git_checkout_failed"));
if !checkout_status.success() {
eprintln!("{}", rust_i18n::t!("sgdk_git_checkout_failed"));
std::process::exit(1);
}
} else {
eprintln!("{}", rust_i18n::t!("sgdk_git_missing"));
std::process::exit(1);
}
println!("{}", rust_i18n::t!("sgdk_config_updating"));
let config_dir = path::config_dir();
fs::create_dir_all(&config_dir).expect("Failed to create config directory");
let config_path = config_dir.join("config.toml");
let mut doc = if config_path.exists() {
let text =
fs::read_to_string(&config_path).expect(&rust_i18n::t!("config_read_failed"));
text.parse::<DocumentMut>()
.expect(&rust_i18n::t!("toml_parse_failed"))
} else {
DocumentMut::new()
};
let abs_path = target_dir
.canonicalize()
.expect("Failed to get absolute path");
doc["sgdk"]["path"] = value(abs_path.to_str().unwrap().replace(r"\\?\", ""));
doc["sgdk"]["version"] = value(version);
fs::write(&config_path, doc.to_string()).expect("Failed to write config.toml");
println!("{}", rust_i18n::t!("sgdk_config_updated"));
#[cfg(not(target_os = "windows"))]
{
run_generate_wine(&target_dir);
}
generate_sgdk_doc(&target_dir);
return;
}
println!("{}", rust_i18n::t!("cloning_sgdk"));
if let Some(parent) = target_dir.parent() {
fs::create_dir_all(parent).expect("Failed to create parent directory");
}
let is_commit_id = {
let v = version;
let len = v.len();
len >= 7 && len <= 40 && v.chars().all(|c| c.is_ascii_hexdigit())
};
let clone_status = if is_commit_id {
Command::new("git")
.args([
"clone",
"https://github.com/Stephane-D/SGDK",
target_dir.to_str().unwrap(),
])
.status()
.expect("git clone failed")
} else {
Command::new("git")
.args([
"clone",
"--branch",
version,
"https://github.com/Stephane-D/SGDK",
target_dir.to_str().unwrap(),
])
.status()
.expect("git clone failed")
};
if !clone_status.success() {
eprintln!("{}", rust_i18n::t!("git_clone_failed"));
std::process::exit(1);
}
if is_commit_id {
let checkout_status = Command::new("git")
.arg("checkout")
.arg(version)
.current_dir(&target_dir)
.status()
.expect("git checkout failed");
if !checkout_status.success() {
eprintln!("git checkout failed");
std::process::exit(1);
}
}
println!("{}", rust_i18n::t!("saving_config"));
let config_dir = path::config_dir();
fs::create_dir_all(&config_dir).expect("Failed to create config directory");
let config_path = config_dir.join("config.toml");
let mut doc = if config_path.exists() {
let text = fs::read_to_string(&config_path).expect(&rust_i18n::t!("config_read_failed"));
text.parse::<DocumentMut>()
.expect(&rust_i18n::t!("toml_parse_failed"))
} else {
DocumentMut::new()
};
let abs_path = target_dir
.canonicalize()
.expect("Failed to get absolute path");
doc["sgdk"]["path"] = value(abs_path.to_str().unwrap().replace(r"\\?\", ""));
doc["sgdk"]["version"] = value(version);
fs::write(&config_path, doc.to_string()).expect("Failed to write config.toml");
#[cfg(not(target_os = "windows"))]
{
run_generate_wine(&target_dir);
}
generate_sgdk_doc(&target_dir);
println!(
"{}",
rust_i18n::t!("sgdk_setup_complete", path = target_dir.display())
);
}
fn reset_sgdk_worktree(target_dir: &Path) {
use std::fs;
let reset_status = std::process::Command::new("git")
.args(&["reset", "--hard"])
.current_dir(target_dir)
.status();
match reset_status {
Ok(s) if s.success() => {
println!("git reset --hard executed successfully.");
}
Ok(s) => {
eprintln!("git reset --hard exited with code {:?}", s.code());
}
Err(e) => {
eprintln!("failed to execute git reset --hard: {}", e);
}
}
for i in 1..=2 {
let clean_status = std::process::Command::new("git")
.args(&["clean", "-dfx", "."])
.current_dir(target_dir)
.status();
match clean_status {
Ok(s) if s.success() => {
println!("git clean -dfx . executed successfully (pass {}).", i);
}
Ok(s) => {
eprintln!("git clean exited with code {:?} (pass {}).", s.code(), i);
}
Err(e) => {
eprintln!("failed to execute git clean (pass {}): {}", i, e);
}
}
}
let sjasm_dir = target_dir.join("tools").join("sjasm");
if sjasm_dir.exists() {
println!("Removing problematic directory: {}", sjasm_dir.display());
let _ = fs::remove_dir_all(&sjasm_dir);
}
}
#[cfg(not(target_os = "windows"))]
fn run_generate_wine(sgdk_path: &Path) {
let sgdk_bin = sgdk_path.join("bin");
let script_url =
"https://raw.githubusercontent.com/Franticware/SGDK_wine/master/generate_wine.sh";
let local_script = sgdk_bin.join("generate_wine.sh");
println!("{}", rust_i18n::t!("wine_downloading"));
let response = reqwest::blocking::get(script_url)
.expect("Script download failed")
.text()
.expect("Text retrieval failed");
fs::write(&local_script, response).expect("Failed to write generate_wine.sh");
println!("{}", rust_i18n::t!("wine_generating"));
let status = Command::new("sh")
.arg("generate_wine.sh")
.current_dir(sgdk_path.join("bin"))
.status()
.expect("Failed to execute generate_wine.sh");
if !status.success() {
eprintln!("{}", rust_i18n::t!("wine_script_failed"));
std::process::exit(1);
}
println!("{}", rust_i18n::t!("wine_wrapper_complete"));
}
fn generate_sgdk_doc(target_dir: &Path) {
if which("doxygen").is_ok() {
use regex::Regex;
let doc_dir = target_dir.join("doc");
let html_index = doc_dir.join("html").join("index.html");
let doxyconfig = doc_dir.join("doxyconfig");
let temp_path = doc_dir.join("temp_doxyconfig");
if html_index.exists() {
return;
}
if doxyconfig.exists() {
if let Err(e) = fs::copy(&doxyconfig, &temp_path) {
eprintln!("Failed to copy doxygen config: {}", e);
return;
}
if let Ok(content) = fs::read_to_string(&temp_path) {
let re = Regex::new(r"(?m)^OUTPUT_DIRECTORY\s*=.*$").unwrap();
let new_content = re.replace_all(&content, "OUTPUT_DIRECTORY = ./SGDK/doc");
if let Err(e) = fs::write(&temp_path, new_content.as_ref()) {
eprintln!("Failed to write temp_doxyconfig: {}", e);
} else {
let parent_dir = target_dir.parent().unwrap_or(target_dir);
let status = Command::new("doxygen")
.arg(&temp_path)
.current_dir(parent_dir)
.status();
match status {
Ok(s) if s.success() => {
println!("{}", rust_i18n::t!("sgdk_doc_generated"));
}
Ok(s) => {
eprintln!("doxygen exited with code {:?}", s.code());
}
Err(e) => {
eprintln!("failed to execute doxygen: {}", e);
}
}
}
}
}
}
}