Compare commits

...
14 Commits
Author SHA1 Message Date
GitHub Action 0b4abe07ac chore(build): sync gow3-launcher-win.exe to repo root [skip ci] 2026-10-17 15:21:09 +00:00
ScreamAngel 6d7a6fad1b Merge branch 'origin/main' and resolve binary conflicts 2026-10-17 18:12:16 +03:00
ScreamAngel 6d8770767c feat(engine): add cross-platform auto engine downloader & standalone deployment support 2026-10-17 16:13:11 +03:00
GitHub Action f2d09ade09 chore(build): sync gow3-launcher-win.exe to repo root [skip ci] 2026-10-17 12:08:20 +00:00
EgEhE 7f6505c5bc Delete gow3_launcher_win.exe
This file should be in relases
2026-10-17 15:00:55 +03:00
EgEhE 1b7dd8aeb1 Delete gow3-launcher
This file should be in relases
2026-10-17 14:59:02 +03:00
EgEhE d03ce5b8ab Delete gow3-launcher-win.exe
This file should be in relases
2026-10-17 14:58:19 +03:00
ScreamAngel fe1874c93c fix(launcher): resolve Win32 os error 193 & UI stutter, add Windows engine downloader 2026-10-17 14:41:00 +03:00
GitHub Action fcee9a136c chore(build): sync gow3-launcher-win.exe to repo root [skip ci] 2026-10-17 10:41:23 +00:00
ScreamAngel b548073f5c fix(windows): resolve VRAM detection cap and add portable Python dependency downloader 2026-10-17 13:34:54 +03:00
GitHub Action f5e9a0f10e chore(build): sync gow3-launcher-win.exe to repo root [skip ci] 2026-10-17 09:45:12 +00:00
ScreamAngel 17daa20a36 README.md 2026-10-17 12:39:50 +03:00
ScreamAngel f6a1ade73d README.md 2026-10-17 12:38:20 +03:00
GitHub Action abd4c153a4 chore(build): sync gow3-launcher-win.exe to repo root [skip ci] 2026-10-17 09:34:26 +00:00
9 changed files with 608 additions and 29 deletions

No files matched your search

+8 -8
View File
@@ -1,11 +1,11 @@
# gow3_pclauncher
Launcher for God Of War 3 PC on Linux
Launcher for God Of War 3 PC on Linux and Windows
# ⚡ God of War® III Remastered - Linux Port & Native Launcher
# ⚡ God of War® III Remastered - Linux-Windows Port & Native Launcher
An optimized Linux port and native graphical launcher for **God of War® III Remastered** (*CUSA01623 / CUSA01715*), featuring hardware auto-tuning, 144Hz Delta-Time synchronization, crash diagnostics, and upstream GitHub integration.
An optimized Linux-Windows port and native graphical launcher for **God of War® III Remastered** (*CUSA01623 / CUSA01715*), featuring hardware auto-tuning, 144Hz Delta-Time synchronization, crash diagnostics, and upstream GitHub integration.
[![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20Steam%20Deck-orange.svg)](https://kernel.org)
[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20Steam%20Deck-orange.svg)](https://kernel.org)
[![Launcher](https://img.shields.io/badge/Launcher-Rust%20%2F%20egui-blue.svg)](https://www.rust-lang.org)
[![Vulkan](https://img.shields.io/badge/Graphics-Vulkan%201.3-red.svg)](https://www.vulkan.org)
[![License](https://img.shields.io/badge/License-GPLv2-green.svg)](LICENSE)
@@ -32,7 +32,7 @@ Launcher for God Of War 3 PC on Linux
| Component | Minimum | Recommended |
|---|---|---|
| **OS** | Linux (Arch, Ubuntu, Fedora, SteamOS, Debian) | Any modern 64-bit Linux distro |
| **OS** | Linux (Arch, Ubuntu, Fedora, SteamOS, Debian) and Windows 10-11 | Any modern 64-bit Linux distro |
| **CPU** | 6-core x86_64 processor | 8-core / 16-thread modern CPU |
| **GPU** | Vulkan 1.3 compatible (4 GB VRAM) | Vulkan 1.3+ (6 GB+ VRAM, Nvidia / AMD) |
| **RAM** | 8 GB | 16 GB+ |
@@ -55,7 +55,7 @@ Launcher for God Of War 3 PC on Linux
**Option B: Download Pre-compiled Binaries (GitHub Releases)**
Download the executables directly from the GitHub Releases tab:
- **Linux**: `gow3-launcher`
- **Windows / Dual-Boot**: `gow3-launcher-win.exe`
- **Windows**: `gow3-launcher-win.exe`
The launcher is completely standalone: it automatically detects your hardware, verifies the game dump, configures settings files, and applies all required patches without requiring external bash scripts.
@@ -115,12 +115,12 @@ Launcher for God Of War 3 PC on Linux
## 📜 Credits & Acknowledgments
- **Original Project:** https://github.com/FelipePrado0/gow3_pc
- **Original Project:** [FelipePrado0](https://github.com/FelipePrado0/gow3_pc)
- **Upstream Linux Port & Probe:** Developed by [R-V-L](https://github.com/R-V-L/gow3_pc) and community contributors.
- **Launcher & Hardware Tuner:** Built with Rust, [egui/eframe](https://github.com/emilk/egui).
> **Disclaimer:** God of War is a registered trademark of Sony Interactive Entertainment LLC and Santa Monica Studio. This repository **does not contain any copyrighted game assets, ROMs, or binaries**. Users must supply their own legally obtained game files.
#Licence
## License
GNU GPL v2 or later ([LICENSE](https://github.com/R-V-L/gow3_pc/blob/main/LICENSE)). Third-party components keep their own licenses.
BIN
View File
Binary file not shown.
Executable → Regular
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.
+171
View File
@@ -77,6 +77,16 @@ pub struct LauncherApp {
is_game_running: bool,
game_exit_rx: Option<Receiver<Option<i32>>>,
// Python dependency state
has_python: bool,
is_downloading_python: bool,
python_dl_rx: Option<Receiver<Result<String, String>>>,
// Engine dependency state
has_engine: bool,
is_downloading_engine: bool,
engine_dl_rx: Option<Receiver<Result<String, String>>>,
// Modals
show_first_launch_modal: bool,
active_crash_diagnosis: Option<CrashDiagnosis>,
@@ -104,6 +114,8 @@ impl LauncherApp {
let is_first = !config.first_launch_done;
let game_valid = is_valid_game_dir(Path::new(&config.game_dir));
let gamemode_avail = is_gamemode_available();
let has_python = crate::runner::find_python(&base_dir).is_ok();
let has_engine = crate::runner::find_game_engine(&base_dir).is_some();
let mut app = Self {
base_dir,
@@ -120,6 +132,12 @@ impl LauncherApp {
update_modal: None,
is_game_running: false,
game_exit_rx: None,
has_python,
is_downloading_python: false,
python_dl_rx: None,
has_engine,
is_downloading_engine: false,
engine_dl_rx: None,
show_first_launch_modal: is_first,
active_crash_diagnosis: None,
show_log_modal: false,
@@ -245,6 +263,37 @@ impl LauncherApp {
});
}
fn start_python_download(&mut self) {
if self.is_downloading_python {
return;
}
self.is_downloading_python = true;
let (tx, rx) = channel();
self.python_dl_rx = Some(rx);
let base_dir_clone = self.base_dir.clone();
thread::spawn(move || {
let res = crate::runner::download_portable_python(&base_dir_clone);
let _ = tx.send(res);
});
}
#[allow(dead_code)]
fn start_engine_download(&mut self) {
if self.is_downloading_engine {
return;
}
self.is_downloading_engine = true;
let (tx, rx) = channel();
self.engine_dl_rx = Some(rx);
let base_dir_clone = self.base_dir.clone();
thread::spawn(move || {
let res = crate::runner::download_engine(&base_dir_clone);
let _ = tx.send(res);
});
}
fn launch_game(&mut self) {
// Apply settings before launching
self.apply_all_optimizations();
@@ -328,6 +377,41 @@ impl eframe::App for LauncherApp {
}
}
// 4. Check background python downloader thread
if let Some(ref rx) = self.python_dl_rx {
if let Ok(res) = rx.try_recv() {
self.is_downloading_python = false;
self.python_dl_rx = None;
match res {
Ok(_) => {
self.has_python = true;
self.status_message = Some((self.t("python_download_success").to_string(), false));
}
Err(e) => {
self.status_message = Some((format!("{}{}", self.t("python_download_err"), e), true));
}
}
}
}
// 5. Check background engine downloader thread
if let Some(ref rx) = self.engine_dl_rx {
if let Ok(res) = rx.try_recv() {
self.is_downloading_engine = false;
self.engine_dl_rx = None;
match res {
Ok(_) => {
self.has_engine = true;
self.has_python = crate::runner::find_python(&self.base_dir).is_ok();
self.status_message = Some((self.t("engine_download_success").to_string(), false));
}
Err(e) => {
self.status_message = Some((format!("❌ {}", e), true));
}
}
}
}
// --- FULL SCREEN BACKGROUND WALLPAPER ---
if let Some(ref tex) = self.bg_texture {
let painter = ui.painter();
@@ -489,6 +573,93 @@ impl eframe::App for LauncherApp {
ui.label(egui::RichText::new(format!("{} Hz", hw.refresh_rate_hz)).color(egui::Color32::from_rgb(130, 205, 135)).strong());
});
});
ui.add_space(8.0);
ui.separator();
ui.horizontal(|ui| {
ui.label(egui::RichText::new(self.t("python_dep")).color(egui::Color32::LIGHT_GRAY).size(12.0));
if self.has_python {
ui.label(
egui::RichText::new(self.t("python_ready"))
.color(egui::Color32::from_rgb(102, 225, 106))
.strong()
.size(12.0),
);
} else {
ui.label(
egui::RichText::new(self.t("python_missing"))
.color(egui::Color32::from_rgb(239, 83, 80))
.strong()
.size(12.0),
);
ui.add_space(8.0);
if self.is_downloading_python {
ui.spinner();
ui.label(
egui::RichText::new(self.t("python_downloading"))
.color(egui::Color32::from_rgb(255, 213, 79))
.size(12.0),
);
} else {
let dl_btn = egui::Button::new(
egui::RichText::new(self.t("python_download"))
.size(11.0)
.color(egui::Color32::WHITE)
.strong(),
)
.fill(egui::Color32::from_rgb(45, 120, 185))
.corner_radius(egui::CornerRadius::same(5));
if ui.add(dl_btn).clicked() {
self.start_python_download();
}
}
}
});
ui.add_space(6.0);
ui.horizontal(|ui| {
ui.label(egui::RichText::new(self.t("engine_label")).color(egui::Color32::LIGHT_GRAY).size(12.0));
if self.has_engine {
ui.label(
egui::RichText::new(self.t("engine_ready"))
.color(egui::Color32::from_rgb(102, 225, 106))
.strong()
.size(12.0),
);
} else {
ui.label(
egui::RichText::new(self.t("engine_missing"))
.color(egui::Color32::from_rgb(239, 83, 80))
.strong()
.size(12.0),
);
ui.add_space(8.0);
if self.is_downloading_engine {
ui.spinner();
ui.label(
egui::RichText::new(self.t("engine_downloading"))
.color(egui::Color32::from_rgb(255, 213, 79))
.size(12.0),
);
} else {
let dl_btn = egui::Button::new(
egui::RichText::new(self.t("engine_download"))
.size(11.0)
.color(egui::Color32::WHITE)
.strong(),
)
.fill(egui::Color32::from_rgb(180, 80, 45))
.corner_radius(egui::CornerRadius::same(5));
if ui.add(dl_btn).clicked() {
self.start_engine_download();
}
}
}
});
});
// --- 2. GAME DIRECTORY CARD ---
+52
View File
@@ -105,6 +105,19 @@ pub fn text(lang: Language, key: &str) -> &'static str {
(Language::Turkish, "latest_commit") => "Son Değişiklik:",
(Language::Turkish, "commit_date") => "Tarih:",
(Language::Turkish, "view_on_github") => "🌐 GitHub'da Görüntüle",
(Language::Turkish, "python_dep") => "Python 3 Bağımlılığı:",
(Language::Turkish, "python_ready") => "Kurulu / Hazır",
(Language::Turkish, "python_missing") => "Bulunamadı",
(Language::Turkish, "python_download") => "📥 Taşınabilir Python 3 İndir (~10 MB, Kurulumsuz)",
(Language::Turkish, "python_downloading") => "⏳ Python 3 indiriliyor ve hazırlanıyor...",
(Language::Turkish, "python_download_success") => "✅ Taşınabilir Python 3 başarıyla kuruldu ve hazırlandı!",
(Language::Turkish, "python_download_err") => "❌ Python 3 indirilemedi: ",
(Language::Turkish, "engine_label") => "⚙️ Oyun Motoru:",
(Language::Turkish, "engine_ready") => "Kurulu / Hazır (Vulkan Native)",
(Language::Turkish, "engine_missing") => "Bulunamadı / Eksik!",
(Language::Turkish, "engine_download") => "📥 Motoru Otomatik İndir (~74 MB)",
(Language::Turkish, "engine_downloading") => "⏳ Oyun motoru paketi indiriliyor ve kuruluyor...",
(Language::Turkish, "engine_download_success") => "✅ Oyun motoru başarıyla kuruldu!",
// English
(Language::English, "app_title") => "GOD OF WAR® III REMASTERED",
@@ -167,6 +180,19 @@ pub fn text(lang: Language, key: &str) -> &'static str {
(Language::English, "latest_commit") => "Latest Commit:",
(Language::English, "commit_date") => "Date:",
(Language::English, "view_on_github") => "🌐 View on GitHub",
(Language::English, "python_dep") => "Python 3 Dependency:",
(Language::English, "python_ready") => "Installed / Ready",
(Language::English, "python_missing") => "Not Found",
(Language::English, "python_download") => "📥 Download Portable Python 3 (~10 MB, No Install)",
(Language::English, "python_downloading") => "⏳ Downloading & configuring Python 3...",
(Language::English, "python_download_success") => "✅ Portable Python 3 installed and configured successfully!",
(Language::English, "python_download_err") => "❌ Failed to download Python 3: ",
(Language::English, "engine_label") => "⚙️ Game Engine:",
(Language::English, "engine_ready") => "Installed / Ready (Vulkan Native)",
(Language::English, "engine_missing") => "Not Found / Missing!",
(Language::English, "engine_download") => "📥 Download Engine (~74 MB)",
(Language::English, "engine_downloading") => "⏳ Downloading and configuring game engine...",
(Language::English, "engine_download_success") => "✅ Game engine installed successfully!",
// German
(Language::German, "app_title") => "GOD OF WAR® III REMASTERED",
@@ -229,6 +255,19 @@ pub fn text(lang: Language, key: &str) -> &'static str {
(Language::German, "latest_commit") => "Letzter Commit:",
(Language::German, "commit_date") => "Datum:",
(Language::German, "view_on_github") => "🌐 Auf GitHub ansehen",
(Language::German, "python_dep") => "Python 3 Abhängigkeit:",
(Language::German, "python_ready") => "Installiert / Bereit",
(Language::German, "python_missing") => "Nicht gefunden",
(Language::German, "python_download") => "📥 Portables Python 3 herunterladen (~10 MB, Ohne Installation)",
(Language::German, "python_downloading") => "⏳ Python 3 wird heruntergeladen und eingerichtet...",
(Language::German, "python_download_success") => "✅ Portables Python 3 erfolgreich eingerichtet!",
(Language::German, "python_download_err") => "❌ Fehler beim Herunterladen von Python 3: ",
(Language::German, "engine_label") => "⚙️ Spiel-Engine:",
(Language::German, "engine_ready") => "Installiert / Bereit (Vulkan Native)",
(Language::German, "engine_missing") => "Nicht gefunden / Fehlt!",
(Language::German, "engine_download") => "📥 Engine herunterladen (~74 MB)",
(Language::German, "engine_downloading") => "⏳ Spiel-Engine wird heruntergeladen und eingerichtet...",
(Language::German, "engine_download_success") => "✅ Spiel-Engine erfolgreich installiert!",
// Spanish
(Language::Spanish, "app_title") => "GOD OF WAR® III REMASTERED",
@@ -291,6 +330,19 @@ pub fn text(lang: Language, key: &str) -> &'static str {
(Language::Spanish, "latest_commit") => "Último Commit:",
(Language::Spanish, "commit_date") => "Fecha:",
(Language::Spanish, "view_on_github") => "🌐 Ver en GitHub",
(Language::Spanish, "python_dep") => "Dependencia Python 3:",
(Language::Spanish, "python_ready") => "Instalado / Listo",
(Language::Spanish, "python_missing") => "No encontrado",
(Language::Spanish, "python_download") => "📥 Descargar Python 3 portable (~10 MB, Sin instalación)",
(Language::Spanish, "python_downloading") => "⏳ Descargando y configurando Python 3...",
(Language::Spanish, "python_download_success") => "✅ ¡Python 3 portable configurado con éxito!",
(Language::Spanish, "python_download_err") => "❌ Error al descargar Python 3: ",
(Language::Spanish, "engine_label") => "⚙️ Motor del Juego:",
(Language::Spanish, "engine_ready") => "Instalado / Listo (Vulkan Native)",
(Language::Spanish, "engine_missing") => "¡No encontrado / Falta!",
(Language::Spanish, "engine_download") => "📥 Descargar Motor (~74 MB)",
(Language::Spanish, "engine_downloading") => "⏳ Descargando y configurando motor de juego...",
(Language::Spanish, "engine_download_success") => "✅ ¡Motor de juego instalado con éxito!",
_ => "",
}
+323 -19
View File
@@ -71,15 +71,315 @@ pub fn ensure_runtime_assets(base_dir: &Path) -> Result<(PathBuf, PathBuf), Stri
Ok((scripts_dir, patches_dir))
}
pub fn find_python() -> Result<String, String> {
for cmd in &["python3", "python", "py"] {
if let Ok(out) = Command::new(cmd).arg("--version").output() {
if out.status.success() {
return Ok(cmd.to_string());
/// Finds an available Python 3 interpreter (checking local runtime bundle first, then system PATH)
pub fn find_python(base_dir: &Path) -> Result<String, String> {
// 1. Check local runtime python folder (.runtime/python/python.exe or bin/python3)
let candidates = [
base_dir.join(".runtime/python/python.exe"),
base_dir.join(".runtime/python/python"),
base_dir.join(".runtime/python/bin/python3"),
base_dir.join(".runtime/python/bin/python"),
base_dir.join("python/python.exe"),
];
for p in &candidates {
if p.is_file() {
if let Ok(out) = Command::new(p).arg("--version").output() {
if out.status.success() {
return Ok(p.to_string_lossy().to_string());
}
}
}
}
Err("Python 3 bulunamadı. Lütfen sisteminize 'python' kurun.".to_string())
// 2. Check system-wide Python
for cmd in &["python3", "python", "py"] {
if let Ok(out) = Command::new(cmd).arg("--version").output() {
if out.status.success() {
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
let combined = format!("{} {}", stdout, stderr);
if combined.contains("Python 3.") {
return Ok(cmd.to_string());
}
}
}
}
Err("Python 3 bulunamadı. Lütfen taşınabilir Python paketini indirin veya sisteminize kurun.".to_string())
}
/// Automatically downloads and configures official portable Python 3 runtime into `.runtime/python`
pub fn download_portable_python(base_dir: &Path) -> Result<String, String> {
let runtime_dir = base_dir.join(".runtime");
let python_dir = runtime_dir.join("python");
let _ = fs::create_dir_all(&python_dir);
#[cfg(target_os = "windows")]
{
let ps_code = format!(
r#"
$ProgressPreference = 'SilentlyContinue';
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
$url = 'https://www.python.org/ftp/python/3.11.9/python-3.11.9-embed-amd64.zip';
$zip = Join-Path $env:TEMP 'gow3_python_embed.zip';
$dest = '{}';
if (Test-Path $zip) {{ Remove-Item $zip -Force }};
Write-Host 'Downloading Python 3.11 embed package...';
(New-Object System.Net.WebClient).DownloadFile($url, $zip);
if (!(Test-Path $dest)) {{ New-Item -ItemType Directory -Path $dest -Force | Out-Null }};
Write-Host 'Extracting archive...';
Expand-Archive -Path $zip -DestinationPath $dest -Force;
Remove-Item $zip -Force;
"#,
python_dir.to_string_lossy().replace('\'', "''")
);
let status = Command::new("powershell")
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps_code])
.status()
.map_err(|e| format!("PowerShell ile Python indirme komutu çalıştırılamadı: {}", e))?;
if !status.success() {
return Err("PowerShell üzerinden Python 3 indirilemedi.".to_string());
}
// Configure python311._pth to enable import site and recognize scripts directory
let pth_file = python_dir.join("python311._pth");
let pth_content = "python311.zip\n.\n..\n..\\scripts\n..\\..\\scripts\nimport site\n";
let _ = fs::write(pth_file, pth_content);
}
#[cfg(not(target_os = "windows"))]
{
// On Linux / macOS fallback: if curl and unzip exist, download embed (usable with wine or testing)
let zip_path = runtime_dir.join("python_embed.zip");
let curl_res = Command::new("curl")
.args([
"-sL",
"https://www.python.org/ftp/python/3.11.9/python-3.11.9-embed-amd64.zip",
"-o",
&zip_path.to_string_lossy(),
])
.status();
if let Ok(st) = curl_res {
if st.success() {
let _ = Command::new("unzip")
.args(["-o", "-q", &zip_path.to_string_lossy(), "-d", &python_dir.to_string_lossy()])
.status();
let _ = fs::remove_file(&zip_path);
let pth_file = python_dir.join("python311._pth");
let pth_content = "python311.zip\n.\n..\nimport site\n";
let _ = fs::write(pth_file, pth_content);
}
}
}
find_python(base_dir).map_err(|_| "Python 3 indirildi ancak doğrulanamadı.".to_string())
}
/// Ensures Python 3 is available, automatically downloading portable package if missing
pub fn ensure_python(base_dir: &Path) -> Result<String, String> {
if let Ok(py) = find_python(base_dir) {
return Ok(py);
}
download_portable_python(base_dir)
}
pub fn find_game_engine(base_dir: &Path) -> Option<PathBuf> {
#[cfg(target_os = "windows")]
let probe_candidates = [
base_dir.join("bin/gow3-probe.exe"),
base_dir.join("out/gow3-probe.exe"),
base_dir.join("gow3-probe.exe"),
base_dir.join("bin/gow3.exe"),
base_dir.join("gow3.exe"),
base_dir.join("God of War III.exe"),
];
#[cfg(not(target_os = "windows"))]
let probe_candidates = [
base_dir.join("bin/gow3-probe"),
base_dir.join("out/gow3-probe"),
base_dir.join("gow3-probe"),
];
probe_candidates.into_iter().find(|p| p.is_file())
}
#[allow(dead_code)]
pub fn download_windows_engine(base_dir: &Path) -> Result<String, String> {
let dest_bin = base_dir.join("bin");
let _ = fs::create_dir_all(&dest_bin);
#[cfg(target_os = "windows")]
{
let ps_code = format!(
r#"
$ProgressPreference = 'SilentlyContinue';
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
$url = 'https://github.com/FelipePrado0/gow3_pc/releases/download/windows-v2.4.1/gow3-windows.zip';
$zip = Join-Path $env:TEMP 'gow3_win_download.zip';
$tempDir = Join-Path $env:TEMP 'gow3_win_extracted';
if (Test-Path $tempDir) {{ Remove-Item $tempDir -Recurse -Force }};
if (Test-Path $zip) {{ Remove-Item $zip -Force }};
Write-Host 'Downloading Windows engine package (~74MB)...';
(New-Object System.Net.WebClient).DownloadFile($url, $zip);
Write-Host 'Extracting Windows package...';
Expand-Archive -Path $zip -DestinationPath $tempDir -Force;
$probe = Get-ChildItem -Path $tempDir -Recurse -Filter "gow3-probe.exe" | Select-Object -First 1;
if ($probe) {{
$sourceBin = $probe.DirectoryName;
$destBin = '{dest_bin}';
if (!(Test-Path $destBin)) {{ New-Item -ItemType Directory -Path $destBin -Force | Out-Null }};
Copy-Item "$sourceBin\*" -Destination $destBin -Recurse -Force;
}} else {{
throw "gow3-probe.exe extracted archive icinde bulunamadi!";
}}
$base = '{base_dir}';
$scripts = Get-ChildItem -Path $tempDir -Recurse -Directory -Filter "scripts" | Select-Object -First 1;
if ($scripts -and !(Test-Path (Join-Path $base 'scripts'))) {{
Copy-Item $scripts.FullName -Destination $base -Recurse -Force;
}}
$patches = Get-ChildItem -Path $tempDir -Recurse -Directory -Filter "patches" | Select-Object -First 1;
if ($patches -and !(Test-Path (Join-Path $base 'patches'))) {{
Copy-Item $patches.FullName -Destination $base -Recurse -Force;
}}
Remove-Item $tempDir -Recurse -Force;
Remove-Item $zip -Force;
"#,
dest_bin = dest_bin.to_string_lossy().replace('\'', "''"),
base_dir = base_dir.to_string_lossy().replace('\'', "''")
);
let status = Command::new("powershell")
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps_code])
.status()
.map_err(|e| format!("PowerShell Windows motoru indirme komutu çalıştırılamadı: {}", e))?;
if !status.success() {
return Err("PowerShell üzerinden Windows motor paketi indirilemedi veya açılamadı.".to_string());
}
if find_game_engine(base_dir).is_some() {
return Ok("Windows oyun motoru başarıyla kuruldu!".to_string());
}
Err("Windows oyun motoru ('bin/gow3-probe.exe') arşivden çıkarılamadı.".to_string())
}
#[cfg(not(target_os = "windows"))]
{
Err("Bu fonksiyon yalnızca Windows üzerinde çalışır.".to_string())
}
}
#[allow(dead_code)]
pub fn download_linux_engine(base_dir: &Path) -> Result<String, String> {
let dest_bin = base_dir.join("bin");
let _ = fs::create_dir_all(&dest_bin);
#[cfg(not(target_os = "windows"))]
{
let url = "https://github.com/FelipePrado0/gow3_pc/releases/download/windows-v2.4.1/gow3-x86_64.AppImage";
let temp_dir = std::env::temp_dir().join("gow3_linux_extracted");
let _ = fs::remove_dir_all(&temp_dir);
let _ = fs::create_dir_all(&temp_dir);
let appimage_path = temp_dir.join("gow3-x86_64.AppImage");
let dl_status = Command::new("curl")
.args(["-sL", url, "-o", &appimage_path.to_string_lossy()])
.status();
let success = match dl_status {
Ok(s) if s.success() => true,
_ => {
Command::new("wget")
.args(["-q", url, "-O", &appimage_path.to_string_lossy()])
.status()
.map(|s| s.success())
.unwrap_or(false)
}
};
if !success {
let _ = fs::remove_dir_all(&temp_dir);
return Err("Linux AppImage paketi indirilemedi (curl/wget başarısız oldu).".to_string());
}
let _ = Command::new("chmod")
.args(["+x", &appimage_path.to_string_lossy()])
.status();
let ext_status = Command::new(&appimage_path)
.arg("--appimage-extract")
.current_dir(&temp_dir)
.status();
if let Ok(st) = ext_status {
if st.success() {
let squash_bin = temp_dir.join("squashfs-root/bin");
if squash_bin.is_dir() {
let _ = Command::new("cp")
.args(["-r", &format!("{}/.", squash_bin.to_string_lossy()), &dest_bin.to_string_lossy()])
.status();
let _ = Command::new("chmod")
.args(["+x", &dest_bin.join("gow3-probe").to_string_lossy()])
.status();
}
}
}
let _ = fs::remove_dir_all(&temp_dir);
if find_game_engine(base_dir).is_some() {
return Ok("Linux oyun motoru başarıyla kuruldu!".to_string());
}
Err("Linux oyun motoru paketten çıkarılamadı.".to_string())
}
#[cfg(target_os = "windows")]
{
Err("Bu fonksiyon yalnızca Linux üzerinde çalışır.".to_string())
}
}
pub fn download_engine(base_dir: &Path) -> Result<String, String> {
#[cfg(target_os = "windows")]
{
download_windows_engine(base_dir)
}
#[cfg(not(target_os = "windows"))]
{
download_linux_engine(base_dir)
}
}
pub fn ensure_game_engine(base_dir: &Path) -> Result<PathBuf, String> {
if let Some(p) = find_game_engine(base_dir) {
return Ok(p);
}
// Try automatic download if missing
let _ = download_engine(base_dir);
if let Some(p) = find_game_engine(base_dir) {
return Ok(p);
}
#[cfg(target_os = "windows")]
{
Err("Windows için oyun motoru ('bin/gow3-probe.exe') bulunamadı! Lütfen arayüzdeki '📥 Motoru İndir' butonuna basarak indirin.".to_string())
}
#[cfg(not(target_os = "windows"))]
{
Err("Linux için oyun motoru ('bin/gow3-probe') bulunamadı! Lütfen arayüzdeki '📥 Motoru İndir' butonuna basarak indirin.".to_string())
}
}
/// Executes the full God of War III preparation and probe execution natively from Rust.
@@ -93,17 +393,7 @@ pub fn launch_gow3(
return Err("Oyun klasöründe 'eboot.bin' bulunamadı!".to_string());
}
let probe_candidates = [
base_dir.join("bin/gow3-probe"),
base_dir.join("bin/gow3-probe.exe"),
base_dir.join("out/gow3-probe"),
base_dir.join("out/gow3-probe.exe"),
base_dir.join("gow3-probe.exe"),
];
let probe_bin = probe_candidates
.into_iter()
.find(|p| p.is_file())
.ok_or_else(|| "Oyun motoru ('gow3-probe' / 'gow3-probe.exe') bulunamadı!".to_string())?;
let probe_bin = ensure_game_engine(base_dir)?;
let out_dir = base_dir.join("out");
fs::create_dir_all(&out_dir).map_err(|e| format!("'out' klasörü oluşturulamadı: {}", e))?;
@@ -127,7 +417,7 @@ pub fn launch_gow3(
.try_clone()
.map_err(|e| format!("Log dosyası çoğaltılamadı: {}", e))?;
let python = find_python()?;
let python = ensure_python(base_dir)?;
// 1. Run mods.py
let _ = Command::new(&python)
@@ -317,6 +607,20 @@ pub fn launch_gow3(
cmd.env("GOW3_CONFIG", &config_ini);
cmd.env("GOW3_GAME_DIR", game_dir);
// Prepend bin directory to PATH so Windows/Linux loaders find bundled libraries
let bin_dir = base_dir.join("bin");
if bin_dir.is_dir() {
if let Some(existing_path) = std::env::var_os("PATH") {
let mut paths = vec![bin_dir.clone()];
paths.extend(std::env::split_paths(&existing_path));
if let Ok(new_path) = std::env::join_paths(paths) {
cmd.env("PATH", new_path);
}
} else {
cmd.env("PATH", &bin_dir);
}
}
// Pipe stdout & stderr to gow3_log.txt
cmd.stdout(Stdio::from(log_file));
cmd.stderr(Stdio::from(log_file_err));
@@ -333,7 +637,7 @@ mod tests {
#[test]
fn test_find_python() {
let py = find_python();
let py = find_python(Path::new("."));
assert!(py.is_ok(), "Python 3 should be detected on the system");
}
+54 -2
View File
@@ -179,17 +179,68 @@ fn detect_refresh_rate() -> u32 {
#[cfg(target_os = "windows")]
fn detect_gpu() -> (String, u64) {
// 1. Try nvidia-smi first (exact VRAM for NVIDIA GPUs, e.g. RTX 4070 Laptop 8GB)
if let Ok(output) = Command::new("nvidia-smi")
.args(["--query-gpu=name,memory.total", "--format=csv,noheader,nounits"])
.output()
{
if output.status.success() {
let text = String::from_utf8_lossy(&output.stdout);
if let Some(line) = text.lines().next() {
let parts: Vec<&str> = line.split(',').collect();
if parts.len() >= 2 {
let name = parts[0].trim().to_string();
let vram = parts[1].trim().parse::<u64>().unwrap_or(0);
if vram > 0 {
return (name, vram);
}
}
}
}
}
// 2. Try Windows Registry for AMD / Intel / NVIDIA (64-bit QWORD qwMemorySize)
let ps_script = r#"
$g = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\00*' -ErrorAction SilentlyContinue |
Where-Object { $_.'HardwareInformation.qwMemorySize' -gt 0 } |
Sort-Object 'HardwareInformation.qwMemorySize' -Descending |
Select-Object -First 1;
if ($g) {
$name = if ($g.DriverDesc) { $g.DriverDesc } else { $g.'Device Description' };
$vram = [int64]$g.'HardwareInformation.qwMemorySize';
[PSCustomObject]@{ Name = $name; VRAM = $vram } | ConvertTo-Json -Compress
}
"#;
if let Ok(output) = Command::new("powershell")
.args(["-NoProfile", "-Command", ps_script])
.output()
{
if output.status.success() {
let text = String::from_utf8_lossy(&output.stdout);
if let Ok(val) = serde_json::from_str::<serde_json::Value>(text.trim()) {
let name = val["Name"].as_str().unwrap_or("Windows DirectX/Vulkan GPU").to_string();
if let Some(vram_bytes) = val["VRAM"].as_u64() {
let vram_mb = vram_bytes / (1024 * 1024);
if vram_mb >= 1024 {
return (name, vram_mb);
}
}
}
}
}
// 3. Fallback to Win32_VideoController
if let Ok(output) = Command::new("powershell")
.args([
"-NoProfile",
"-Command",
"Get-CimInstance Win32_VideoController | Select-Object -First 1 -Property Name,AdapterRAM | ConvertTo-Json",
"Get-CimInstance Win32_VideoController | Sort-Object AdapterRAM -Descending | Select-Object -First 1 -Property Name,AdapterRAM | ConvertTo-Json -Compress",
])
.output()
{
if output.status.success() {
let text = String::from_utf8_lossy(&output.stdout);
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&text) {
if let Ok(val) = serde_json::from_str::<serde_json::Value>(text.trim()) {
let name = val["Name"].as_str().unwrap_or("Windows DirectX/Vulkan GPU").to_string();
let vram_bytes = val["AdapterRAM"].as_u64().unwrap_or(4 * 1024 * 1024 * 1024);
let vram_mb = vram_bytes / (1024 * 1024);
@@ -197,6 +248,7 @@ fn detect_gpu() -> (String, u64) {
}
}
}
("Windows DirectX/Vulkan GPU".to_string(), 4096)
}