mirror of
https://github.com/ScreamAngel/gow3_launcher.git
synced 2026-11-09 17:52:11 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56e77b172b | ||
|
|
bfaf219d8d | ||
|
|
b8423a0760 | ||
|
|
de79b29de5 | ||
|
|
90cae4b6f7 | ||
|
|
88ad672c6e | ||
|
|
521e87e8c4 | ||
|
|
bb76fa8621 | ||
|
|
6c4e36ec08 | ||
|
|
34387f6c87 | ||
|
|
634571103b | ||
|
|
a709098214 | ||
|
|
b63a55cf02 | ||
|
|
5be05f0024 | ||
|
|
779fce78ab | ||
|
|
d7aafd49e9 | ||
|
|
93e2d8d1fb | ||
|
|
57698ae376 | ||
|
|
50285f0ff8 | ||
|
|
b58b282a35 | ||
|
|
72ec274d33 | ||
|
|
a69d9090c9 | ||
|
|
5b74ff4a6a | ||
|
|
7b2160537f | ||
|
|
2bc81ff974 | ||
|
|
151c976598 | ||
|
|
f92f09da53 | ||
|
|
764b5541d7 | ||
|
|
5f7e3bb850 | ||
|
|
67bc336216 | ||
|
|
8e4ebbbcb9 | ||
|
|
1b2e0b566a | ||
|
|
10a9a95862 | ||
|
|
fa50be4b24 | ||
|
|
4093ee43c1 | ||
|
|
4f6c6788d0 | ||
|
|
47437d280b | ||
|
|
e82257aa27 | ||
|
|
5991b03cd0 | ||
|
|
dc3df87384 | ||
|
|
a9e5233ae7 | ||
|
|
410c5fc0b7 | ||
|
|
e6cec74c96 | ||
|
|
c65f1acb1b | ||
|
|
f1b3cfae29 | ||
|
|
0614038175 | ||
|
|
66efe4ec0e | ||
|
|
0a7fb20aa9 | ||
|
|
709845f472 | ||
|
|
f90f25f033 | ||
|
|
08e46e6675 | ||
|
|
29e57c38e5 | ||
|
|
1ade24e88c | ||
|
|
b7e366e893 | ||
|
|
1179c01ddf | ||
|
|
765fa79ecd | ||
|
|
01771af1d7 | ||
|
|
5d1de303af | ||
|
|
4326563b9d | ||
|
|
bc48a858f0 | ||
|
|
d96f683944 |
No files matched your search
@@ -1,122 +0,0 @@
|
||||
name: Build Cross-Platform Launchers
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: Build Linux Launcher
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install Linux build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev libx11-dev libxcursor-dev libxi-dev libxrandr-dev libgl1-mesa-dev
|
||||
|
||||
- name: Build Linux Launcher (Release)
|
||||
working-directory: launcher
|
||||
run: cargo build --release
|
||||
|
||||
- name: Prepare binary
|
||||
run: |
|
||||
cp launcher/target/release/launcher ./gow3-launcher
|
||||
chmod +x ./gow3-launcher
|
||||
|
||||
- name: Upload Linux Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gow3-launcher-linux
|
||||
path: ./gow3-launcher
|
||||
|
||||
build-windows:
|
||||
name: Build Windows Launcher
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Build Windows Launcher (Release)
|
||||
working-directory: launcher
|
||||
run: cargo build --release
|
||||
|
||||
- name: Prepare binary
|
||||
run: |
|
||||
Copy-Item launcher/target/release/launcher.exe -Destination ./gow3-launcher-win.exe
|
||||
|
||||
- name: Upload Windows Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gow3-launcher-windows
|
||||
path: ./gow3-launcher-win.exe
|
||||
|
||||
publish-release:
|
||||
name: Publish Release & Sync Windows Binary
|
||||
needs: [build-linux, build-windows]
|
||||
if: github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Linux binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: gow3-launcher-linux
|
||||
path: ./artifacts/
|
||||
|
||||
- name: Download Windows binary
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: gow3-launcher-windows
|
||||
path: ./artifacts/
|
||||
|
||||
- name: Commit Windows launcher to repo root
|
||||
run: |
|
||||
cp ./artifacts/gow3-launcher-win.exe ./gow3-launcher-win.exe
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add gow3-launcher-win.exe
|
||||
git diff --quiet && git diff --staged --quiet || (git commit -m "chore(build): sync gow3-launcher-win.exe to repo root [skip ci]" && git push origin main)
|
||||
|
||||
- name: Create or Update GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: latest
|
||||
name: "God of War III Native Launcher (Dual-Boot & Cross-Platform)"
|
||||
body: |
|
||||
## 🚀 God of War III Remastered - Native Launcher (Linux & Windows)
|
||||
|
||||
Aynı klasörde yan yana çalışabilen yerel ikili dosyalar:
|
||||
- 🐧 **Linux**: `gow3-launcher`
|
||||
- 🪟 **Windows / Dual-Boot**: `gow3-launcher-win.exe`
|
||||
|
||||
### ✨ Özellikler
|
||||
- **Dual-Boot Desteği:** Aynı klasörde hem Windows hem Linux için yan yana bağımsız launcher'lar!
|
||||
- **Modern Kayan Anahtarlar:** Klasik kutucuklar yerine animasyonlu Switch teması
|
||||
- **Kararlı 60 FPS / 120 FPS / 144 FPS / 240 FPS Pacing:** Fizik hızlanma sorunu olmadan akıcı oynanış
|
||||
- **Sinematikleri 'X' Tuşuyla Atlama Yaması:** Ara sahneleri kumandanızın X tuşuyla geçebilme seçeneği
|
||||
- **Sarı Doku Hatası Önleme Yaması (Instant Readbacks):** Karlı ve parlak sahnelerdeki renk bozulmasını engeller
|
||||
- **Dahili Donanım ve Ekran Hz Tespiti:** GPU, VRAM, RAM ve ekran yenileme hızına göre otomatik yapılandırma
|
||||
- **4 Farklı Dil Desteği:** Türkçe 🇹🇷, English 🇬🇧, Deutsch 🇩🇪, Español 🇪🇸
|
||||
- **%100 Rust Uyumlu Self-Updater:** Doğrudan GitHub üzerinden en son sürüme tek tıkla güncelleme
|
||||
files: |
|
||||
./artifacts/gow3-launcher
|
||||
./artifacts/gow3-launcher-win.exe
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,154 @@
|
||||
name: Build & Release Single Executables (AppImage & Portable Exe)
|
||||
|
||||
on:
|
||||
# Otomatik release/tag build tetikleyicisi geçici olarak devre dışı bırakıldı
|
||||
# push:
|
||||
# tags:
|
||||
# - 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: Build Linux AppImage & Binary
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install Linux build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev libx11-dev libxcursor-dev libxi-dev libxrandr-dev libgl1-mesa-dev wget
|
||||
|
||||
- name: Build Linux Launcher (Release)
|
||||
working-directory: launcher
|
||||
run: cargo build --release
|
||||
|
||||
- name: Build AppImage
|
||||
run: |
|
||||
mkdir -p AppDir/usr/bin
|
||||
mkdir -p AppDir/usr/share/applications
|
||||
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps
|
||||
cp launcher/target/release/launcher AppDir/usr/bin/gow3-launcher
|
||||
cp gow3.png AppDir/usr/share/icons/hicolor/256x256/apps/gow3.png
|
||||
cp gow3.png AppDir/gow3.png
|
||||
|
||||
cat << 'EOF' > AppDir/gow3-launcher.desktop
|
||||
[Desktop Entry]
|
||||
Name=God of War III Launcher
|
||||
Exec=gow3-launcher
|
||||
Icon=gow3
|
||||
Type=Application
|
||||
Categories=Game;
|
||||
Terminal=false
|
||||
EOF
|
||||
cp AppDir/gow3-launcher.desktop AppDir/usr/share/applications/
|
||||
|
||||
cat << 'EOF' > AppDir/AppRun
|
||||
#!/bin/bash
|
||||
HERE="$(dirname "$(readlink -f "${0}")")"
|
||||
export PATH="${HERE}/usr/bin:${PATH}"
|
||||
exec "${HERE}/usr/bin/gow3-launcher" "$@"
|
||||
EOF
|
||||
chmod +x AppDir/AppRun
|
||||
|
||||
wget -q https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool
|
||||
chmod +x appimagetool
|
||||
./appimagetool --appimage-extract
|
||||
ARCH=x86_64 ./squashfs-root/AppRun AppDir God_of_War_III_Launcher-x86_64.AppImage
|
||||
|
||||
cp launcher/target/release/launcher ./gow3-launcher
|
||||
|
||||
- name: Upload Linux Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: |
|
||||
God_of_War_III_Launcher-x86_64.AppImage
|
||||
gow3-launcher
|
||||
|
||||
build-windows:
|
||||
name: Build Windows Portable Exe
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Build Windows Launcher (Release)
|
||||
working-directory: launcher
|
||||
run: cargo build --release
|
||||
|
||||
- name: Prepare Windows Executables
|
||||
run: |
|
||||
Copy-Item launcher/target/release/launcher.exe -Destination ./gow3-launcher-win.exe
|
||||
Copy-Item launcher/target/release/launcher.exe -Destination ./God_of_War_III_Launcher-Windows.exe
|
||||
|
||||
- name: Upload Windows Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: |
|
||||
gow3-launcher-win.exe
|
||||
God_of_War_III_Launcher-Windows.exe
|
||||
|
||||
publish-release:
|
||||
name: Publish Release with Assets
|
||||
needs: [build-linux, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download Linux artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: ./dist/
|
||||
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: ./dist/
|
||||
|
||||
- name: Create or Update GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
name: "God of War III Launcher ${{ github.ref_name }}"
|
||||
draft: false
|
||||
prerelease: false
|
||||
make_latest: true
|
||||
body: |
|
||||
## 🚀 God of War III Remastered - Single Executable Release
|
||||
|
||||
Tek tıkla çalışan taşınabilir yürütülebilir dosyalar (Single Executables):
|
||||
|
||||
### 📦 İndirme Seçenekleri:
|
||||
- 🐧 **Linux (AppImage - Tek Dosya Kurulumsuz):** [`God_of_War_III_Launcher-x86_64.AppImage`](./God_of_War_III_Launcher-x86_64.AppImage)
|
||||
- *Kullanım:* İndirin, sağ tıklayıp özelliklerden "Çalıştırılabilir yap" (veya `chmod +x`) deyin ve çift tıklayın!
|
||||
- 🪟 **Windows (Portable Exe - Kurulumsuz):** [`God_of_War_III_Launcher-Windows.exe`](./God_of_War_III_Launcher-Windows.exe) / [`gow3-launcher-win.exe`](./gow3-launcher-win.exe)
|
||||
- 🐧 **Linux (Native ELF):** [`gow3-launcher`](./gow3-launcher)
|
||||
|
||||
### ✨ Yenilikler (${{ github.ref_name }})
|
||||
- **AppImage Desteği:** Linux üzerinde bağımlılık derdi olmadan tek tıkla çalışan resmi AppImage paketi.
|
||||
- **Windows Portable Exe:** Harici dosyalara ihtiyaç duymadan kendi simgesi ve arayüzüyle açılan taşınabilir Windows sürümü.
|
||||
- **Akıllı Konum & Donanım Tespiti:** Launcher artık doğrudan kendi bulunduğu dizini tanır, taşınabilir çalışır.
|
||||
- **Gelişmiş Oyun Arama:** Disklerde oyun bulunamadığında açıklayıcı modal uyarı penceresi ve anlık doğrulama.
|
||||
- **Otomatik Yama & FPS Ayarları:** 60/120/144/240 Hz pacing, sarı doku önleme ve sinematik atlama.
|
||||
files: |
|
||||
./dist/God_of_War_III_Launcher-x86_64.AppImage
|
||||
./dist/God_of_War_III_Launcher-Windows.exe
|
||||
./dist/gow3-launcher-win.exe
|
||||
./dist/gow3-launcher
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,21 +1,21 @@
|
||||
# gow3_pclauncher
|
||||
Launcher for God Of War 3 PC on Linux
|
||||
# gow3_launcher
|
||||
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.
|
||||
|
||||
[](https://kernel.org)
|
||||
[](https://kernel.org)
|
||||
[](https://www.rust-lang.org)
|
||||
[](https://www.vulkan.org)
|
||||
[](LICENSE)
|
||||
[](LICENSE)
|
||||
Website: [https://screamangel.github.io/gow3-site/](https://screamangel.github.io/gow3-site/)
|
||||
|
||||
|
||||
|
||||
## 🌟 Key Features
|
||||
|
||||
- 🦀 **Native Rust GUI Launcher (`gow3-launcher`):** Built with `eframe`/`egui`. Ultra-fast, lightweight (~17 MB standalone), with a sleek game-art interface and no heavy
|
||||
dependencies.
|
||||
- 🦀 **Native Rust GUI Launcher (`gow3-launcher`):** Built with `eframe`/`egui`. Ultra-fast, lightweight, with a sleek game-art interface and no heavy dependencies.
|
||||
- 🖥️ **Hardware Auto-Detection:** Automatically inspects GPU model, dedicated VRAM, system RAM, and display refresh rate (Hz) on launch.
|
||||
- ⏱️ **144Hz / High-Hz Delta-Time Sync:** Eliminates the infamous 2x speed bug on 144Hz+ gaming displays by syncing engine delta-time with target framerates.
|
||||
- 🎨 **Texture Glitch Fix (Instant Readbacks):** Prevents snowy, bright, or reflection textures from corrupting into golden-yellow artifacts.
|
||||
@@ -23,20 +23,29 @@ Launcher for God Of War 3 PC on Linux
|
||||
- 🔍 **Smart Game Scanner & Completeness Check:** Scans drives to locate `eboot.bin` dumps, validates file integrity (`param.sfo`, elf size), and prevents launching corrupted
|
||||
directories.
|
||||
- 🚨 **Crash & Error Diagnostic Assistant:** Automatically analyzes `gow3_log.txt` on exit. If an unexpected crash occurs, it displays a plain-language diagnosis and recommended fix.
|
||||
- 🔄 **GitHub Upstream Sync & Auto-Updater:** Checks upstream ([R-V-L/gow3_pc](https://github.com/R-V-L/gow3_pc)) for updates and re-applies all your custom directory paths, 144Hz patches, and VRAM budgets seamlessly.
|
||||
- 🌐 **Multi-Language Support:** Available in **Turkish 🇹🇷**, **English 🇬🇧**, **German 🇩🇪**, and **Spanish 🇪🇸**.
|
||||
|
||||
- 🔄 **GitHub Upstream Sync & Auto-Updater:** Checks for updates in Upstream ([ScreamAngel/gow3_launcher](https://github.com/ScreamAngel/gow3_launcher)) and updates the launcher to the latest version.
|
||||
- 🌐 **Multi-Language Support:** Available in **Turkish 🇹🇷**, **English 🇬🇧**, **German 🇩🇪**, and **Spanish 🇪🇸**.
|
||||
|
||||
|
||||
|
||||
## 📋 System Requirements
|
||||
|
||||
| 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 OS |
|
||||
| **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+ |
|
||||
| **Dependencies** | Python 3, Vulkan drivers | `gamemode` (optional) |
|
||||
| **Dependencies** | Python 3, Vulkan drivers | `gamemode` (optional) |
|
||||
|
||||
## Video
|
||||
|
||||
You can watch the video for details.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.youtube.com/watch?v=-yGAEjdQGzE"><img width="1920" height="1080" alt="vlc_ie5PhePn2o" src="https://github.com/user-attachments/assets/190f531e-55b0-4a4a-b883-440cd615a18a" /></a>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -46,18 +55,33 @@ Launcher for God Of War 3 PC on Linux
|
||||
|
||||
**Option A: Clone with Git**
|
||||
```bash
|
||||
git clone https://github.com/ScreamAngel/gow3_pclauncher.git
|
||||
cd gow3_pclauncher
|
||||
git clone https://github.com/ScreamAngel/gow3_launcher.git
|
||||
cd gow3_launcher
|
||||
```
|
||||
- 🐧 **On Linux**: `./gow3-launcher`
|
||||
- 🪟 **On Windows**: `gow3-launcher-win.exe`
|
||||
|
||||
**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`
|
||||
**Option B: Download from release tab or website**
|
||||
- Extract the downloaded file from the archive. Then, depending on your operating system, select and run the appropriate file ( “gow3-launcher1” for Linux or “gow3-launcher-win.exe” for Windows).
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Sections of the launcher
|
||||
|
||||
**Home Screen**
|
||||
<img width="2384" height="1301" alt="image" src="https://github.com/user-attachments/assets/1e285415-92ce-48c4-a1b1-b8bcebde2cc5" />
|
||||
|
||||
|
||||
|
||||
|
||||
**Keyboard Configuration**
|
||||
<img width="3183" height="1742" alt="image" src="https://github.com/user-attachments/assets/6785f030-0bbb-4d56-a541-e8d2bdbe4684" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 2. Add Your Game Dump
|
||||
@@ -75,6 +99,9 @@ Launcher for God Of War 3 PC on Linux
|
||||
.\gow3-launcher-win.exe --play
|
||||
```
|
||||
|
||||
<img width="1920" height="1080" alt="gow3-probe_Hz51yYBACH" src="https://github.com/user-attachments/assets/651de8e7-15b9-454c-ab6d-a09dbd71f44e" />
|
||||
|
||||
|
||||
### 🔄 Dual-Boot Ready
|
||||
Both `gow3-launcher` and `gow3-launcher-win.exe` live side-by-side in the same directory. If you dual-boot between Linux and Windows, you can play on either operating system seamlessly while sharing the same game dumps and configurations!
|
||||
|
||||
@@ -95,6 +122,8 @@ Launcher for God Of War 3 PC on Linux
|
||||
Ensure **"Refresh Rate (Hz) Sync Patch"** is enabled and your target FPS matches your display.
|
||||
- **Game crashes during heavy combat or asset streaming?**
|
||||
Check if your GPU has at least 6GB VRAM. You can adjust the VRAM Garbage Collection Budget directly in the launcher.
|
||||
- **Does the Launcher have a keyboard configuration?**
|
||||
On the “Configuration & Controls” tab, you can assign any button to a function of your choice. DualShock 4 support is available.
|
||||
|
||||
|
||||
|
||||
@@ -110,17 +139,18 @@ Launcher for God Of War 3 PC on Linux
|
||||
5. **Feral GameMode:** Tek tıkla `gamemoderun` performans modu desteği.
|
||||
6. **Akıllı Hata Teşhisi:** Oyun kapanırsa log dosyasını analiz edip hatanın sebebini ve çözümünü arayüzde bildirir.
|
||||
7. **Steam / Steam Deck Desteği:** `--play` parametresi ile arayüze girmeden doğrudan oyunu başlatabilme.
|
||||
8. **Klavye Konfigürasyonu:** "Yapılandırma & Kontroller" sekmesinden tüm tuşlara klavyeden bir tuş atayabilirsiniz. Ayrıca Dualshock 4 desteği mevcuttur.
|
||||
|
||||
|
||||
|
||||
## 📜 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.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 449 KiB |
Binary file not shown.
Generated
+20
-1
@@ -1784,7 +1784,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "launcher"
|
||||
version = "0.1.0"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"eframe",
|
||||
"egui_extras",
|
||||
@@ -1792,6 +1792,7 @@ dependencies = [
|
||||
"rfd",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"winres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3450,6 +3451,15 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
@@ -4414,6 +4424,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winres"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
|
||||
dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.57.1"
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "launcher"
|
||||
version = "0.1.0"
|
||||
version = "1.7.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,6 +11,9 @@ rfd = "0.17.2"
|
||||
serde = { version = "1.0.229", features = ["derive"] }
|
||||
serde_json = "1.0.154"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winres = "0.1"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fn main() {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let mut res = winres::WindowsResource::new();
|
||||
res.set_icon("gow3.ico");
|
||||
res.set("ProductName", "God of War III Remastered Launcher");
|
||||
res.set("FileDescription", "God of War III Remastered PC Launcher");
|
||||
res.set("LegalCopyright", "God of War III Remastered");
|
||||
if let Err(e) = res.compile() {
|
||||
eprintln!("Warning: failed to compile Windows icon resource: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
+2313
-375
File diff suppressed because it is too large.
Load diff
@@ -2,12 +2,186 @@ use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub enum GraphicPreset {
|
||||
#[default]
|
||||
Balanced, // Standart / Orijinal (1080p)
|
||||
UltraLow, // Ultra Düşük Donanım (480p)
|
||||
Low, // Düşük Sistem / Steam Deck (720p)
|
||||
SteamDeck,// Steam Deck 16:10 (800p)
|
||||
Ultra, // Yüksek / 2K - 4K
|
||||
Custom, // Özel (Kullanıcı Ayarlı)
|
||||
}
|
||||
|
||||
impl GraphicPreset {
|
||||
#[allow(dead_code)]
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
GraphicPreset::Balanced => "balanced",
|
||||
GraphicPreset::UltraLow => "ultra_low",
|
||||
GraphicPreset::Low => "low",
|
||||
GraphicPreset::SteamDeck => "steam_deck",
|
||||
GraphicPreset::Ultra => "ultra",
|
||||
GraphicPreset::Custom => "custom",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub enum DisplayMode {
|
||||
#[default]
|
||||
Borderless,
|
||||
Fullscreen,
|
||||
Windowed,
|
||||
}
|
||||
|
||||
impl DisplayMode {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
DisplayMode::Borderless => "borderless",
|
||||
DisplayMode::Fullscreen => "fullscreen",
|
||||
DisplayMode::Windowed => "windowed",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_toml_mode(&self) -> (&'static str, bool) {
|
||||
match self {
|
||||
DisplayMode::Borderless => ("Borderless", true),
|
||||
DisplayMode::Fullscreen => ("Fullscreen", true),
|
||||
DisplayMode::Windowed => ("Windowed", false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct KeyboardBindings {
|
||||
// Face buttons
|
||||
pub triangle: String,
|
||||
pub circle: String,
|
||||
pub cross: String,
|
||||
pub square: String,
|
||||
|
||||
// Shoulder buttons
|
||||
pub l1: String,
|
||||
pub r1: String,
|
||||
pub l2: String,
|
||||
pub r2: String,
|
||||
pub l3: String,
|
||||
pub r3: String,
|
||||
|
||||
// System
|
||||
pub options: String,
|
||||
pub touchpad_center: String,
|
||||
|
||||
// D-Pad
|
||||
pub pad_up: String,
|
||||
pub pad_down: String,
|
||||
pub pad_left: String,
|
||||
pub pad_right: String,
|
||||
|
||||
// Left Stick (Movement)
|
||||
pub axis_left_x_minus: String, // Sol (A)
|
||||
pub axis_left_x_plus: String, // Sağ (D)
|
||||
pub axis_left_y_minus: String, // İleri (W)
|
||||
pub axis_left_y_plus: String, // Geri (S)
|
||||
|
||||
// Right Stick (Camera)
|
||||
pub axis_right_x_minus: String, // Sol (J)
|
||||
pub axis_right_x_plus: String, // Sağ (L)
|
||||
pub axis_right_y_minus: String, // Yukarı (I)
|
||||
pub axis_right_y_plus: String, // Aşağı (K)
|
||||
}
|
||||
|
||||
impl Default for KeyboardBindings {
|
||||
fn default() -> Self {
|
||||
Self::wasd_cvbn()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardBindings {
|
||||
pub fn wasd_cvbn() -> Self {
|
||||
Self {
|
||||
triangle: "c".to_string(),
|
||||
circle: "b".to_string(),
|
||||
cross: "n".to_string(),
|
||||
square: "v".to_string(),
|
||||
l1: "q".to_string(),
|
||||
r1: "u".to_string(),
|
||||
l2: "e".to_string(),
|
||||
r2: "o".to_string(),
|
||||
l3: "x".to_string(),
|
||||
r3: "m".to_string(),
|
||||
options: "enter".to_string(),
|
||||
touchpad_center: "space".to_string(),
|
||||
pad_up: "up".to_string(),
|
||||
pad_down: "down".to_string(),
|
||||
pad_left: "left".to_string(),
|
||||
pad_right: "right".to_string(),
|
||||
axis_left_x_minus: "a".to_string(),
|
||||
axis_left_x_plus: "d".to_string(),
|
||||
axis_left_y_minus: "w".to_string(),
|
||||
axis_left_y_plus: "s".to_string(),
|
||||
axis_right_x_minus: "j".to_string(),
|
||||
axis_right_x_plus: "l".to_string(),
|
||||
axis_right_y_minus: "i".to_string(),
|
||||
axis_right_y_plus: "k".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wasd_numpad() -> Self {
|
||||
Self {
|
||||
triangle: "kp8".to_string(),
|
||||
circle: "kp6".to_string(),
|
||||
cross: "kp2".to_string(),
|
||||
square: "kp4".to_string(),
|
||||
l1: "q".to_string(),
|
||||
r1: "u".to_string(),
|
||||
l2: "e".to_string(),
|
||||
r2: "o".to_string(),
|
||||
l3: "x".to_string(),
|
||||
r3: "m".to_string(),
|
||||
options: "enter".to_string(),
|
||||
touchpad_center: "space".to_string(),
|
||||
pad_up: "up".to_string(),
|
||||
pad_down: "down".to_string(),
|
||||
pad_left: "left".to_string(),
|
||||
pad_right: "right".to_string(),
|
||||
axis_left_x_minus: "a".to_string(),
|
||||
axis_left_x_plus: "d".to_string(),
|
||||
axis_left_y_minus: "w".to_string(),
|
||||
axis_left_y_plus: "s".to_string(),
|
||||
axis_right_x_minus: "j".to_string(),
|
||||
axis_right_x_plus: "l".to_string(),
|
||||
axis_right_y_minus: "i".to_string(),
|
||||
axis_right_y_plus: "k".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CachedHardware {
|
||||
pub gpu_name: String,
|
||||
pub vram_mb: u64,
|
||||
pub ram_mb: u64,
|
||||
pub refresh_rate_hz: u32,
|
||||
#[serde(default = "default_cpu_name")]
|
||||
pub cpu_name: String,
|
||||
#[serde(default = "default_cpu_cores")]
|
||||
pub cpu_cores: usize,
|
||||
#[serde(default = "default_cpu_threads")]
|
||||
pub cpu_threads: usize,
|
||||
}
|
||||
|
||||
fn default_cpu_name() -> String {
|
||||
"Bilinmeyen CPU".to_string()
|
||||
}
|
||||
|
||||
fn default_cpu_cores() -> usize {
|
||||
4
|
||||
}
|
||||
|
||||
fn default_cpu_threads() -> usize {
|
||||
4
|
||||
}
|
||||
|
||||
impl Default for CachedHardware {
|
||||
@@ -17,6 +191,9 @@ impl Default for CachedHardware {
|
||||
vram_mb: 4096,
|
||||
ram_mb: 8192,
|
||||
refresh_rate_hz: 60,
|
||||
cpu_name: "Bilinmeyen CPU".to_string(),
|
||||
cpu_cores: 4,
|
||||
cpu_threads: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +213,8 @@ pub struct LauncherConfig {
|
||||
#[serde(default = "default_true")]
|
||||
pub patch_readbacks: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub async_readbacks: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub skip_cinematics: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub use_gamemode: bool,
|
||||
@@ -47,6 +226,40 @@ pub struct LauncherConfig {
|
||||
pub cached_hardware: CachedHardware,
|
||||
#[serde(default = "default_last_commit")]
|
||||
pub last_git_commit: String,
|
||||
#[serde(default = "default_launcher_version")]
|
||||
pub launcher_version: String,
|
||||
|
||||
// Graphics Settings
|
||||
#[serde(default = "default_render_resolution")]
|
||||
pub render_resolution: String,
|
||||
#[serde(default = "default_true")]
|
||||
pub fsr_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub disable_motion_blur: bool,
|
||||
#[serde(default)]
|
||||
pub disable_dof: bool,
|
||||
#[serde(default)]
|
||||
pub graphic_preset: GraphicPreset,
|
||||
|
||||
// Display settings
|
||||
#[serde(default)]
|
||||
pub display_mode: DisplayMode,
|
||||
#[serde(default = "default_true")]
|
||||
pub vsync: bool,
|
||||
|
||||
// Controller settings
|
||||
#[serde(default)]
|
||||
pub background_controller_input: bool,
|
||||
#[serde(default = "default_true")]
|
||||
pub is_motion_controls_enabled: bool,
|
||||
#[serde(default = "default_deadzone")]
|
||||
pub deadzone_left: u32,
|
||||
#[serde(default = "default_deadzone")]
|
||||
pub deadzone_right: u32,
|
||||
|
||||
// Keyboard bindings
|
||||
#[serde(default)]
|
||||
pub keyboard_bindings: KeyboardBindings,
|
||||
}
|
||||
|
||||
fn default_language() -> String {
|
||||
@@ -58,6 +271,9 @@ fn default_target_fps() -> u32 {
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
fn default_deadzone() -> u32 {
|
||||
2
|
||||
}
|
||||
fn default_vram_budget() -> u64 {
|
||||
6144
|
||||
}
|
||||
@@ -67,27 +283,157 @@ fn default_dmem() -> u64 {
|
||||
fn default_last_commit() -> String {
|
||||
"c5638d9956002b8a9f09a240028f6793ddf7b2f8".to_string()
|
||||
}
|
||||
fn default_launcher_version() -> String {
|
||||
"v1.7".to_string()
|
||||
}
|
||||
fn default_render_resolution() -> String {
|
||||
"native".to_string()
|
||||
}
|
||||
|
||||
impl Default for LauncherConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first_launch_done: false,
|
||||
language: default_language(),
|
||||
launcher_version: default_launcher_version(),
|
||||
game_dir: String::new(),
|
||||
target_fps: default_target_fps(),
|
||||
patch_delta_time: true,
|
||||
patch_readbacks: true,
|
||||
async_readbacks: true,
|
||||
skip_cinematics: true,
|
||||
use_gamemode: true,
|
||||
vram_gc_budget_mb: default_vram_budget(),
|
||||
dmem_mb: default_dmem(),
|
||||
cached_hardware: CachedHardware::default(),
|
||||
last_git_commit: default_last_commit(),
|
||||
render_resolution: default_render_resolution(),
|
||||
fsr_enabled: false,
|
||||
disable_motion_blur: false,
|
||||
disable_dof: false,
|
||||
graphic_preset: GraphicPreset::Balanced,
|
||||
display_mode: DisplayMode::default(),
|
||||
vsync: true,
|
||||
background_controller_input: false,
|
||||
is_motion_controls_enabled: true,
|
||||
deadzone_left: default_deadzone(),
|
||||
deadzone_right: default_deadzone(),
|
||||
keyboard_bindings: KeyboardBindings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LauncherConfig {
|
||||
pub fn apply_preset(&mut self, preset: GraphicPreset) {
|
||||
self.graphic_preset = preset;
|
||||
match preset {
|
||||
GraphicPreset::UltraLow => {
|
||||
self.render_resolution = "480p".to_string();
|
||||
self.fsr_enabled = false;
|
||||
self.disable_motion_blur = true;
|
||||
self.disable_dof = true;
|
||||
self.vram_gc_budget_mb = 1280;
|
||||
self.target_fps = 60;
|
||||
}
|
||||
GraphicPreset::Low => {
|
||||
self.render_resolution = "720p".to_string();
|
||||
self.fsr_enabled = true;
|
||||
self.disable_motion_blur = true;
|
||||
self.disable_dof = true;
|
||||
self.vram_gc_budget_mb = 2048;
|
||||
self.target_fps = 60;
|
||||
}
|
||||
GraphicPreset::SteamDeck => {
|
||||
self.render_resolution = "800p".to_string();
|
||||
self.fsr_enabled = true;
|
||||
self.disable_motion_blur = true;
|
||||
self.disable_dof = true;
|
||||
self.vram_gc_budget_mb = 2048;
|
||||
self.target_fps = 40;
|
||||
}
|
||||
GraphicPreset::Balanced => {
|
||||
self.render_resolution = "native".to_string();
|
||||
self.fsr_enabled = false;
|
||||
self.disable_motion_blur = false;
|
||||
self.disable_dof = false;
|
||||
self.vram_gc_budget_mb = 6144;
|
||||
self.target_fps = 60;
|
||||
}
|
||||
GraphicPreset::Ultra => {
|
||||
self.render_resolution = "1440p".to_string();
|
||||
self.fsr_enabled = false;
|
||||
self.disable_motion_blur = false;
|
||||
self.disable_dof = false;
|
||||
self.vram_gc_budget_mb = 8192;
|
||||
self.target_fps = 120;
|
||||
}
|
||||
GraphicPreset::Custom => {
|
||||
// Keep customized values
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_preset_match(&mut self) {
|
||||
if self.render_resolution == "480p" && !self.fsr_enabled && self.disable_motion_blur && self.disable_dof {
|
||||
self.graphic_preset = GraphicPreset::UltraLow;
|
||||
} else if self.render_resolution == "720p" && self.fsr_enabled && self.disable_motion_blur && self.disable_dof {
|
||||
self.graphic_preset = GraphicPreset::Low;
|
||||
} else if self.render_resolution == "800p" && self.fsr_enabled && self.disable_motion_blur && self.disable_dof {
|
||||
self.graphic_preset = GraphicPreset::SteamDeck;
|
||||
} else if self.render_resolution == "native" && !self.fsr_enabled && !self.disable_motion_blur && !self.disable_dof {
|
||||
self.graphic_preset = GraphicPreset::Balanced;
|
||||
} else if (self.render_resolution == "1440p" || self.render_resolution == "4K") && !self.fsr_enabled && !self.disable_motion_blur && !self.disable_dof {
|
||||
self.graphic_preset = GraphicPreset::Ultra;
|
||||
} else {
|
||||
self.graphic_preset = GraphicPreset::Custom;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_resolution_dimensions(&self) -> (u32, u32) {
|
||||
match self.render_resolution.as_str() {
|
||||
"480p" => (854, 480),
|
||||
"720p" => (1280, 720),
|
||||
"800p" => (1280, 800),
|
||||
"1080p" => (1920, 1080),
|
||||
"1440p" => (2560, 1440),
|
||||
"4K" | "2160p" => (3840, 2160),
|
||||
_ => (1920, 1080),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_render_res_env_value(&self) -> Option<String> {
|
||||
if self.render_resolution == "native" {
|
||||
None
|
||||
} else {
|
||||
let (w, h) = self.get_resolution_dimensions();
|
||||
Some(format!("{}x{}", w, h))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_effective_vram_budget(&self) -> u64 {
|
||||
let vram = self.cached_hardware.vram_mb;
|
||||
if vram > 0 {
|
||||
// Guard against PCIe memory thrashing / swapping to system RAM over slow bus:
|
||||
// Desktop environment, window compositor and GPU drivers consume ~350-512MB of VRAM.
|
||||
// Texture GC budget must stay within available physical VRAM.
|
||||
let safe_ceiling = if vram <= 2048 {
|
||||
1280
|
||||
} else if vram <= 3072 {
|
||||
2048
|
||||
} else if vram <= 4096 {
|
||||
3072
|
||||
} else if vram <= 6144 {
|
||||
4608
|
||||
} else if vram <= 8192 {
|
||||
6144
|
||||
} else {
|
||||
vram.saturating_sub(1024)
|
||||
};
|
||||
self.vram_gc_budget_mb.min(safe_ceiling)
|
||||
} else {
|
||||
self.vram_gc_budget_mb
|
||||
}
|
||||
}
|
||||
pub fn config_path(base_dir: &Path) -> PathBuf {
|
||||
base_dir.join("launcher_config.json")
|
||||
}
|
||||
@@ -111,4 +457,165 @@ impl LauncherConfig {
|
||||
let content = serde_json::to_string_pretty(self)?;
|
||||
fs::write(path, content)
|
||||
}
|
||||
|
||||
pub fn build_gow3_key_map(&self) -> String {
|
||||
let b = &self.keyboard_bindings;
|
||||
let to_sdl = |k: &str| -> String {
|
||||
let k_low = k.trim().to_lowercase();
|
||||
match k_low.as_str() {
|
||||
"enter" | "return" => "Return".to_string(),
|
||||
"space" => "Space".to_string(),
|
||||
"escape" | "esc" => "Escape".to_string(),
|
||||
"backspace" => "Backspace".to_string(),
|
||||
"tab" => "Tab".to_string(),
|
||||
"up" => "Up".to_string(),
|
||||
"down" => "Down".to_string(),
|
||||
"left" => "Left".to_string(),
|
||||
"right" => "Right".to_string(),
|
||||
s if s.starts_with("kp") && s.len() == 3 && s[2..].chars().all(|c| c.is_ascii_digit()) => {
|
||||
format!("Keypad {}", &s[2..])
|
||||
}
|
||||
"shift" | "lshift" => "Left Shift".to_string(),
|
||||
"rshift" => "Right Shift".to_string(),
|
||||
"ctrl" | "lctrl" => "Left Ctrl".to_string(),
|
||||
"rctrl" => "Right Ctrl".to_string(),
|
||||
"alt" | "lalt" => "Left Alt".to_string(),
|
||||
"ralt" => "Right Alt".to_string(),
|
||||
s if s.len() == 1 => s.to_uppercase(),
|
||||
s => s.to_string(),
|
||||
}
|
||||
};
|
||||
|
||||
format!(
|
||||
"triangle={},square={},circle={},cross={},l1={},r1={},l2={},r2={},l3={},r3={},options={},touchpad={},up={},down={},left={},right={}",
|
||||
to_sdl(&b.triangle),
|
||||
to_sdl(&b.square),
|
||||
to_sdl(&b.circle),
|
||||
to_sdl(&b.cross),
|
||||
to_sdl(&b.l1),
|
||||
to_sdl(&b.r1),
|
||||
to_sdl(&b.l2),
|
||||
to_sdl(&b.r2),
|
||||
to_sdl(&b.l3),
|
||||
to_sdl(&b.r3),
|
||||
to_sdl(&b.options),
|
||||
to_sdl(&b.touchpad_center),
|
||||
to_sdl(&b.pad_up),
|
||||
to_sdl(&b.pad_down),
|
||||
to_sdl(&b.pad_left),
|
||||
to_sdl(&b.pad_right),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_gow3_pad_map(&self) -> &'static str {
|
||||
"cross=a,circle=b,square=x,triangle=y,l1=leftshoulder,r1=rightshoulder,l3=leftstick,r3=rightstick,options=start,touchpad=back,up=dpup,down=dpdown,left=dpleft,right=dpright"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_to_sdl_scancode(key: &str) -> Option<u8> {
|
||||
let lower = key.trim().to_lowercase();
|
||||
match lower.as_str() {
|
||||
// Letters (SDL_SCANCODE_A = 4 .. SDL_SCANCODE_Z = 29)
|
||||
"a" => Some(4),
|
||||
"b" => Some(5),
|
||||
"c" => Some(6),
|
||||
"d" => Some(7),
|
||||
"e" => Some(8),
|
||||
"f" => Some(9),
|
||||
"g" => Some(10),
|
||||
"h" => Some(11),
|
||||
"i" => Some(12),
|
||||
"j" => Some(13),
|
||||
"k" => Some(14),
|
||||
"l" => Some(15),
|
||||
"m" => Some(16),
|
||||
"n" => Some(17),
|
||||
"o" => Some(18),
|
||||
"p" => Some(19),
|
||||
"q" => Some(20),
|
||||
"r" => Some(21),
|
||||
"s" => Some(22),
|
||||
"t" => Some(23),
|
||||
"u" => Some(24),
|
||||
"v" => Some(25),
|
||||
"w" => Some(26),
|
||||
"x" => Some(27),
|
||||
"y" => Some(28),
|
||||
"z" => Some(29),
|
||||
|
||||
// Number row (SDL_SCANCODE_1 = 30 .. SDL_SCANCODE_0 = 39)
|
||||
"1" => Some(30),
|
||||
"2" => Some(31),
|
||||
"3" => Some(32),
|
||||
"4" => Some(33),
|
||||
"5" => Some(34),
|
||||
"6" => Some(35),
|
||||
"7" => Some(36),
|
||||
"8" => Some(37),
|
||||
"9" => Some(38),
|
||||
"0" => Some(39),
|
||||
|
||||
// Controls
|
||||
"enter" | "return" => Some(40),
|
||||
"escape" | "esc" => Some(41),
|
||||
"backspace" => Some(42),
|
||||
"tab" => Some(43),
|
||||
"space" => Some(44),
|
||||
|
||||
// Function keys (SDL_SCANCODE_F1 = 58 ... F12 = 69)
|
||||
"f1" => Some(58),
|
||||
"f2" => Some(59),
|
||||
"f3" => Some(60),
|
||||
"f4" => Some(61),
|
||||
"f5" => Some(62),
|
||||
"f6" => Some(63),
|
||||
"f7" => Some(64),
|
||||
"f8" => Some(65),
|
||||
"f9" => Some(66),
|
||||
"f10" => Some(67),
|
||||
"f11" => Some(68),
|
||||
"f12" => Some(69),
|
||||
|
||||
// Navigation
|
||||
"insert" => Some(73),
|
||||
"home" => Some(74),
|
||||
"pageup" | "prior" => Some(75),
|
||||
"delete" => Some(76),
|
||||
"end" => Some(77),
|
||||
"pagedown" | "next" => Some(78),
|
||||
|
||||
// Arrow keys
|
||||
"right" => Some(79),
|
||||
"left" => Some(80),
|
||||
"down" => Some(81),
|
||||
"up" => Some(82),
|
||||
|
||||
// Numpad (SDL_SCANCODE_KP_1 = 89 .. SDL_SCANCODE_KP_0 = 98)
|
||||
"kp_divide" | "kpdivide" | "numdivide" => Some(84),
|
||||
"kp_multiply" | "kpmultiply" | "nummultiply" => Some(85),
|
||||
"kp_minus" | "kpminus" | "numminus" => Some(86),
|
||||
"kp_plus" | "kpplus" | "numplus" => Some(87),
|
||||
"kp_enter" | "kpenter" | "numenter" => Some(88),
|
||||
"kp1" | "num1" | "numpad1" | "numpad 1" => Some(89),
|
||||
"kp2" | "num2" | "numpad2" | "numpad 2" => Some(90),
|
||||
"kp3" | "num3" | "numpad3" | "numpad 3" => Some(91),
|
||||
"kp4" | "num4" | "numpad4" | "numpad 4" => Some(92),
|
||||
"kp5" | "num5" | "numpad5" | "numpad 5" => Some(93),
|
||||
"kp6" | "num6" | "numpad6" | "numpad 6" => Some(94),
|
||||
"kp7" | "num7" | "numpad7" | "numpad 7" => Some(95),
|
||||
"kp8" | "num8" | "numpad8" | "numpad 8" => Some(96),
|
||||
"kp9" | "num9" | "numpad9" | "numpad 9" => Some(97),
|
||||
"kp0" | "num0" | "numpad0" | "numpad 0" => Some(98),
|
||||
"kp_period" | "kpperiod" | "kp_decimal" | "kpdecimal" => Some(99),
|
||||
|
||||
// Modifiers
|
||||
"ctrl" | "lctrl" => Some(224),
|
||||
"shift" | "lshift" => Some(225),
|
||||
"alt" | "lalt" => Some(226),
|
||||
"rctrl" => Some(228),
|
||||
"rshift" => Some(229),
|
||||
"ralt" => Some(230),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,77 @@ pub fn analyze_post_game(base_dir: &Path, exit_code: Option<i32>) -> Option<Cras
|
||||
let tail_lines: Vec<&str> = log_content.lines().rev().take(100).collect();
|
||||
let tail_str = tail_lines.iter().rev().cloned().collect::<Vec<&str>>().join("\n");
|
||||
|
||||
// 1. Check for Out of Memory
|
||||
// 1. Check for Broken Vulkan Layers (lsfg-vk / Lossless Scaling)
|
||||
if tail_str.contains("lsfg-vk") && (tail_str.contains("shader DLL does not exist") || tail_str.contains("Failed to create swapchain") || tail_str.contains("ErrorInitializationFailed")) {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Hatalı Vulkan Katmanı Algılandı (lsfg-vk / Lossless Scaling)".to_string(),
|
||||
description: "Sisteminizde kurulu harici 'lsfg-vk' Vulkan katmanı, Lossless Scaling shader DLL dosyasını bulamadığı için ekran penceresini (swapchain) kilitledi ve oyunu çökertti.".to_string(),
|
||||
recommendation: "Launcher bu hatalı lsfg-vk katmanını otomatik engeller. Manuel olarak 'VK_LOADER_LAYERS_DISABLE=\"*lsfg*\"' ortam değişkenini kullanabilir veya '~/.config/lsfg-vk/conf.toml' içindeki Lossless.dll dosya yolunu düzeltebilirsiniz.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "lsfg-vk"),
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Check for Incompatible XML Resolution Patch Segfault (0x1f0b3a)
|
||||
if tail_str.contains("0x1f0b3a") || tail_str.contains("0x2000003") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Uyumsuz EBOOT Çözünürlük Yaması Algılandı".to_string(),
|
||||
description: "Oyunun eski XML tabanlı çözünürlük yaması eboot ikili kodunu bozduğu için motor başlatılırken çöktü.".to_string(),
|
||||
recommendation: "Launcher'ın yeni sürümü çözünürlük ölçeklemeyi GPU kütüphanesi (FSR 1.0) üzerinden donanımsal yapar. Launcher'ı güncelleyin.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "0x1f0b3a"),
|
||||
});
|
||||
}
|
||||
|
||||
// Check for Magic / Combat Pool Crash (0x2de2d2)
|
||||
if tail_str.contains("0x2de2d2") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Büyü / Savaş Olay Havuzu Çökmesi Algılandı (0x2de2d2)".to_string(),
|
||||
description: "Büyü kullanırken veya yoğun savaş sahnelerinde oyun motorunun olay düğüm havuzu (128 eleman) tükendiği için boş bellek adresi (0x10) referans alınarak çöktü.".to_string(),
|
||||
recommendation: "v1.7 sürümündeki evrensel bellek havuzu genişletmesi ve NULL koruması bu sorunu çözer. Launcher'ı güncelleyip oyunu tekrar başlatın.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "0x2de2d2"),
|
||||
});
|
||||
}
|
||||
|
||||
// Check for Intro Transition / Memory Pool Crash (0x2c54b2)
|
||||
if tail_str.contains("0x2c54b2") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Giriş İntrosu / Sahne Geçiş Çökmesi Algılandı (0x2c54b2)".to_string(),
|
||||
description: "Oyun motorunun olay havuzu (128 eleman) yüksek kare hızında veya introyu geçerken tükendiği için boş bellek adresi (0x10) referans alınarak çöktü.".to_string(),
|
||||
recommendation: "v1.7 sürümündeki 'Bug Fix - Intro Crash Fix' yaması havuzu 2048'e çıkarıp otomatik genişletme ve güvenli NULL kontrolü ekler. Launcher'ı güncelleyip oyunu tekrar başlatın.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "0x2c54b2"),
|
||||
});
|
||||
}
|
||||
|
||||
// Check for Vulkan Scheduler Device Lost (SubmitExecution / Assertion Failed)
|
||||
if tail_str.contains("vk_scheduler.cpp:465 SubmitExecution") || tail_str.contains("Device lost during submit") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Vulkan Grafik Teslimat Hatası (Device Lost)".to_string(),
|
||||
description: "Vulkan grafik komutları GPU'ya teslim edilirken sürücü zaman aşımına uğradı veya ölçekleyici (Upscaler/FSR) çakışması yaşandı.".to_string(),
|
||||
recommendation: "v1.7 sürümünde Native 1080p ve çözünürlük ölçekleyici ortam değişkenleri düzeltilmiştir. Launcher'ı güncelleyip grafik profilini 'Dengeli (Native 1080p)' seçeneğine ayarlayarak oyunu başlatın.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "SubmitExecution"),
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Check for Incomplete Haswell/Intel iGPU on hybrid laptops
|
||||
if tail_str.contains("Haswell Vulkan support is incomplete") || (tail_str.contains("MESA-INTEL") && tail_str.contains("DepthStencilAttachment")) {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "⚠️ Uyumsuz Dahili Intel GPU (Haswell / Eski iGPU)".to_string(),
|
||||
description: "Oyun, modern Vulkan özelliklerini desteklemeyen eski Intel Haswell dahili ekran kartında çalıştırılmış. Harici 2GB ekran kartınız (NVIDIA/AMD) devreye girmemiş.".to_string(),
|
||||
recommendation: "Launcher harici GPU'yu otomatik seçmeye çalışır. Manuel başlatmak için NVIDIA'da 'prime-run ./gow3-launcher' veya '__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia ./gow3-launcher', AMD'de 'DRI_PRIME=1 ./gow3-launcher' komutunu kullanın.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "Haswell"),
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Check for Swapchain Creation Failure
|
||||
if tail_str.contains("Failed to create swapchain") || tail_str.contains("vk_swapchain.cpp") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Vulkan Swapchain (Görüntü Çıkışı) Başlatılamadı".to_string(),
|
||||
description: "Grafik kartı sürücüsü veya araya giren harici bir Vulkan katmanı oyunun pencere/ekran yüzeyini oluşturmasını engelledi.".to_string(),
|
||||
recommendation: "Harici Vulkan katmanlarını kapatın, GPU sürücülerinizi güncelleyin ve tam ekran modunu pencereli moda alarak tekrar deneyin.".to_string(),
|
||||
log_snippet: extract_relevant_snippet(&tail_str, "swapchain"),
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Check for Out of Memory
|
||||
if tail_str.contains("VK_ERROR_OUT_OF_DEVICE_MEMORY") || tail_str.contains("Out of memory") || tail_str.contains("dmem out of memory") {
|
||||
return Some(CrashDiagnosis {
|
||||
title: "💥 Bellek / VRAM Yetersizliği Algılandı".to_string(),
|
||||
|
||||
+624
-168
@@ -16,10 +16,10 @@ impl Language {
|
||||
|
||||
pub fn display_name(&self) -> &'static str {
|
||||
match self {
|
||||
Language::Turkish => "🇹🇷 Türkçe",
|
||||
Language::English => "🇬🇧 English",
|
||||
Language::German => "🇩🇪 Deutsch",
|
||||
Language::Spanish => "🇪🇸 Español",
|
||||
Language::Turkish => "Türkçe",
|
||||
Language::English => "English",
|
||||
Language::German => "Deutsch",
|
||||
Language::Spanish => "Español",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,48 @@ impl Language {
|
||||
pub fn text(lang: Language, key: &str) -> &'static str {
|
||||
match (lang, key) {
|
||||
// Turkish
|
||||
(Language::Turkish, "hero_badge") => "🔥 Açık Kaynaklı PC Başlatıcı",
|
||||
(Language::Turkish, "hero_desc") => "Windows, Linux ve Steam Deck üzerinde God of War III Remastered deneyimini kusursuzlaştırmak için tasarlandı.",
|
||||
(Language::Turkish, "badge_v13") => "v1.4",
|
||||
(Language::Turkish, "badge_windows") => "Windows",
|
||||
(Language::Turkish, "badge_linux") => "Linux",
|
||||
(Language::Turkish, "badge_steam_deck") => "Steam Deck",
|
||||
(Language::Turkish, "badge_free_oss") => "%100 Ücretsiz & Açık Kaynak",
|
||||
(Language::Turkish, "tag_hardware") => "D O N A N I M V E S İ S T E M",
|
||||
(Language::Turkish, "tag_game_location") => "O Y U N K O N U M U",
|
||||
(Language::Turkish, "tag_patches") => "P E R F O R M A N S V E Y A M A L A R",
|
||||
(Language::Turkish, "tag_display") => "E K R A N V E G R A F İ K",
|
||||
(Language::Turkish, "tag_controller") => "K O N T R O L C Ü V E G A M E P A D",
|
||||
(Language::Turkish, "tag_keyboard") => "K L A V Y E A T A M A L A R I",
|
||||
(Language::Turkish, "app_title") => "GOD OF WAR® III REMASTERED",
|
||||
(Language::Turkish, "app_subtitle") => "Linux & Windows Port Launcher & Donanım Yöneticisi",
|
||||
(Language::Turkish, "sys_hardware") => "🖥️ Sistem Donanımı",
|
||||
(Language::Turkish, "locked_auto") => "🔒 Kilitli (Otomatik Algılandı)",
|
||||
(Language::Turkish, "tab_game_perf") => "Oyun & Başlatma",
|
||||
(Language::Turkish, "tab_game") => "Oyun & Başlatma",
|
||||
(Language::Turkish, "tab_graphics") => "Grafik Ayarları",
|
||||
(Language::Turkish, "tab_config_controls") => "Yapılandırma & Kontroller",
|
||||
(Language::Turkish, "graphics_header") => "Grafik & Performans Ayarları",
|
||||
(Language::Turkish, "graphics_subtitle") => "Düşük sistem optimizasyonu, çözünürlük ölçekleme ve görsel efekt ayarları",
|
||||
(Language::Turkish, "graphic_preset") => "Hazır Grafik Profili:",
|
||||
(Language::Turkish, "preset_ultra_low") => "🚀 Ultra Düşük Donanım (480p + FSR + Efekt Kısma)",
|
||||
(Language::Turkish, "preset_low") => "⚡ Düşük Sistem / Steam Deck (720p + FSR + Efekt Kısma)",
|
||||
(Language::Turkish, "preset_steam_deck") => "🎮 Steam Deck Özel (800p + FSR + 40/90Hz)",
|
||||
(Language::Turkish, "preset_balanced") => "⚖️ Standart / Orijinal (1080p Native + Orijinal Efektler)",
|
||||
(Language::Turkish, "preset_ultra") => "💎 Yüksek / 2K - 4K (1440p / 4K + 120 FPS)",
|
||||
(Language::Turkish, "preset_custom") => "🛠️ Özel (Kullanıcı Ayarlı)",
|
||||
(Language::Turkish, "preset_desc") => "Hazır profil seçerek tüm ayarları tek tıkla optimize edebilirsiniz. Herhangi bir ayarı değiştirdiğinizde profil otomatik olarak 'Özel' olur.",
|
||||
(Language::Turkish, "render_resolution") => "Dahili Çözünürlük (Resolution):",
|
||||
(Language::Turkish, "res_desc") => "Oyunun dahili çizim çözünürlüğü. Düşük sistemlerde 720p veya 480p seçmek FPS'i 2-3 kat artırır.",
|
||||
(Language::Turkish, "fsr_toggle") => "AMD FSR 1.0 & RCAS Keskinleştirme",
|
||||
(Language::Turkish, "fsr_desc") => "Düşük çözünürlükleri (720p/480p) pencere boyutuna net ve keskin şekilde ölçekler; bulanıklığı ve çamurlaşmayı önler.",
|
||||
(Language::Turkish, "disable_motion_blur") => "Hareket Bulanıklığını Kapat (Disable Motion Blur)",
|
||||
(Language::Turkish, "disable_motion_blur_desc") => "Kamera ve nesne hareket bulanıklığını kaldırarak ekrana kristal netlik kazandırır ve GPU yükünü hafifletir.",
|
||||
(Language::Turkish, "disable_dof") => "Alan Derinliğini Kapat (Disable Depth of Field)",
|
||||
(Language::Turkish, "disable_dof_desc") => "Arka planı bulanıklaştıran ağır post-pass filtresini devre dışı bırakır; arka planı netleştirir ve FPS artırır.",
|
||||
(Language::Turkish, "vram_budget") => "VRAM Bellek Temizleme Bütçesi:",
|
||||
(Language::Turkish, "vram_budget_desc") => "Ekran kartınızın VRAM kapasitesine göre ayarlayın. Düşük VRAM'li sistemlerde takılma ve bellek taşmalarını önler.",
|
||||
(Language::Turkish, "sys_hardware") => "Sistem Donanımı",
|
||||
(Language::Turkish, "locked_auto") => "Kilitli (Otomatik Algılandı)",
|
||||
(Language::Turkish, "cpu") => "İşlemci (CPU)",
|
||||
(Language::Turkish, "gpu") => "Ekran Kartı (GPU)",
|
||||
(Language::Turkish, "vram") => "VRAM Kapasitesi",
|
||||
(Language::Turkish, "vram_safe") => "Doku bozulmasına karşı güvenli",
|
||||
@@ -57,240 +95,658 @@ pub fn text(lang: Language, key: &str) -> &'static str {
|
||||
(Language::Turkish, "ram_safe") => "Yeterli Bellek",
|
||||
(Language::Turkish, "ram_warning") => "Düşük RAM",
|
||||
(Language::Turkish, "refresh_rate") => "Monitör Yenileme",
|
||||
(Language::Turkish, "game_location") => "📂 Oyun Konumu",
|
||||
(Language::Turkish, "game_location") => "Oyun Konumu",
|
||||
(Language::Turkish, "no_folder_selected") => "Henüz oyun klasörü belirlenmedi",
|
||||
(Language::Turkish, "manual_select") => "📁 Manuel Seç",
|
||||
(Language::Turkish, "auto_scan") => "🔍 Otomatik Tara",
|
||||
(Language::Turkish, "manual_select") => "Manuel Seç",
|
||||
(Language::Turkish, "auto_scan") => "Otomatik Tara",
|
||||
(Language::Turkish, "scanning") => "Taranıyor...",
|
||||
(Language::Turkish, "game_valid") => "✅ Geçerli ve eksiksiz God of War III klasörü doğrulandı",
|
||||
(Language::Turkish, "game_invalid") => "❌ Oyun klasörü eksik, bozuk veya 'eboot.bin' bulunamadı!",
|
||||
(Language::Turkish, "patches_header") => "⚙️ Yamalar & Performans Ayarları",
|
||||
(Language::Turkish, "hz_patch") => "Ekran Hızı (Hz) Senkronizasyon Yaması",
|
||||
(Language::Turkish, "hz_desc") => "60 FPS orijinal oyun hızını korur. 120/144Hz ekranlarda akıcılık için ekranı 120Hz yapmak veya FreeSync/VRR açmak önerilir.",
|
||||
(Language::Turkish, "game_valid") => "Geçerli ve eksiksiz God of War III klasörü doğrulandı",
|
||||
(Language::Turkish, "game_invalid") => "Oyun klasörü eksik, bozuk veya 'eboot.bin' bulunamadı!",
|
||||
(Language::Turkish, "patches_header") => "Yamalar & Performans Ayarları",
|
||||
(Language::Turkish, "hz_patch") => "Ekran Hızı (Hz) ve Dinamik FPS Senkronizasyonu",
|
||||
(Language::Turkish, "hz_desc") => "Oyun motorunu seçilen FPS'e kilitlerken 60Hz fizik ve salınım simülasyonunu korur. Oyun asla hızlanmaz veya sapıtmaz; 60, 120, 144 vb. tüm modlarda pürüzsüz çalışır.",
|
||||
(Language::Turkish, "target_fps") => "Hedef FPS:",
|
||||
(Language::Turkish, "readback_patch") => "Sarı Doku Hatası Önleme (Instant Readbacks)",
|
||||
(Language::Turkish, "readback_desc") => "Karlı ve parlak sahnelerde dokuların altın sarısına dönüşmesini önler.",
|
||||
(Language::Turkish, "readback_patch") => "Doku Bozulması ve Sararma Önleme (Instant Readbacks)",
|
||||
(Language::Turkish, "readback_desc") => "Gecikmeli geri okumaları kapatıp anlık doğrusal bellek okuması sağlayarak karlı ve parlak sahnelerdeki altın sarısı ve doku bozulmalarını tamamen çözer.",
|
||||
(Language::Turkish, "async_readbacks") => "Asenkron Geri Okuma (Anti-Stutter & Drop Önleme)",
|
||||
(Language::Turkish, "async_readbacks_desc") => "CPU'nun GPU çizimlerini beklemeden önceki kareyi okumasını ve yazmaların takılmadan sürmesini sağlar. Ani FPS düşüşlerini ve mikro takılmaları tamamen engeller.",
|
||||
(Language::Turkish, "skip_cinematics") => "Sinematikleri 'X' Tuşuyla Atlama Yaması",
|
||||
(Language::Turkish, "skip_cinematics_desc") => "Ara sahneleri kumandanızın X tuşuna basarak anında geçmenizi sağlar.",
|
||||
(Language::Turkish, "gamemode_toggle") => "🎮 Feral GameMode (gamemoderun)",
|
||||
(Language::Turkish, "gamemode_toggle") => "Feral GameMode (gamemoderun)",
|
||||
(Language::Turkish, "gamemode_desc") => "CPU zamanlayıcısını ve GPU performansını oyun için en yüksek seviyeye ayarlar.",
|
||||
(Language::Turkish, "save_apply") => "💾 Ayarları Kaydet ve Uygula",
|
||||
(Language::Turkish, "view_log") => "📜 Log Dosyasını Görüntüle",
|
||||
(Language::Turkish, "save_apply") => "Ayarları Kaydet ve Uygula",
|
||||
(Language::Turkish, "view_log") => "Log Dosyasını Görüntüle",
|
||||
(Language::Turkish, "play_ready") => "OYUNU BAŞLAT",
|
||||
(Language::Turkish, "play_running") => "⏳ OYUN ÇALIŞIYOR...",
|
||||
(Language::Turkish, "play_disabled_warn") => "⚠️ Buton Devre Dışı: Eksiksiz God of War III klasörü bulunamadı!",
|
||||
(Language::Turkish, "play_running") => "OYUN ÇALIŞIYOR...",
|
||||
(Language::Turkish, "force_quit_btn") => "Zorla Kapat",
|
||||
(Language::Turkish, "force_quit_tooltip") => "Oyun kilitlendiğinde veya kapanmadığında süreci anında zorla sonlandırır.",
|
||||
(Language::Turkish, "force_quit_status") => "Oyun süreci zorla sonlandırıldı.",
|
||||
(Language::Turkish, "kill_leftover_btn") => "Süreci Sonlandır",
|
||||
(Language::Turkish, "kill_leftover_tooltip") => "Arka planda takılı kalmış oyun süreçlerini (gow3-probe) temizler.",
|
||||
(Language::Turkish, "play_disabled_warn") => "Buton Devre Dışı: Eksiksiz God of War III klasörü bulunamadı!",
|
||||
(Language::Turkish, "play_disabled_help") => "Lütfen yukarıdaki 'Otomatik Tara' veya 'Manuel Seç' butonunu kullanarak oyunu ekleyin.",
|
||||
(Language::Turkish, "play_subtitle") => "Tüm optimizasyonlar devrede • İyi oyunlar!",
|
||||
(Language::Turkish, "first_launch_title") => "👋 Hoş Geldiniz - God of War III Kurulumu",
|
||||
(Language::Turkish, "first_launch_title") => "Hoş Geldiniz - God of War III Kurulumu",
|
||||
(Language::Turkish, "first_launch_prompt") => "God of War III Launcher ilk kez çalıştırılıyor. Sistem donanımınızı ve disklerdeki oyun klasörünü otomatik olarak tarayıp 144 Hz ve doku yamalarını uygulayalım mı?",
|
||||
(Language::Turkish, "first_launch_auto") => "⚡ Otomatik Tara ve Yapılandır",
|
||||
(Language::Turkish, "first_launch_manual") => "🔧 Manuel Olarak Kendim Ayarlayacağım",
|
||||
(Language::Turkish, "candidates_modal_title") => "🎮 Bulunan God of War III Klasörleri",
|
||||
(Language::Turkish, "first_launch_auto") => "Otomatik Tara ve Yapılandır",
|
||||
(Language::Turkish, "first_launch_manual") => "Manuel Olarak Kendim Ayarlayacağım",
|
||||
(Language::Turkish, "candidates_modal_title") => "Bulunan God of War III Klasörleri",
|
||||
(Language::Turkish, "game_not_found_modal_title") => "Oyun Bulunamadı",
|
||||
(Language::Turkish, "game_not_found_title") => "Sistemde God of War III Yüklü Değil!",
|
||||
(Language::Turkish, "game_not_found_desc") => "Diskleriniz tarandı ancak geçerli bir God of War III Remastered (CUSA01623 / CUSA01715) kurulumu tespit edilemedi.\n\n• Oyun henüz sisteminizde yüklü olmayabilir.\n• Oyun harici bir diskte veya özel bir klasördeyse, lütfen aşağıdaki 'Manuel Seç' butonunu kullanarak 'eboot.bin' dosyasının bulunduğu ana oyun klasörünü seçin.",
|
||||
(Language::Turkish, "scan_not_found_inline") => "Sistemde oyun yüklü değil veya klasör bulunamadı! Lütfen oyunu kurun veya 'Manuel Seç' butonunu kullanın.",
|
||||
(Language::Turkish, "select_this") => "Bu Klasörü Kullan",
|
||||
(Language::Turkish, "close") => "Kapat",
|
||||
(Language::Turkish, "diagnosis_title") => "🚨 Oyun Kapanma & Hata Teşhis Raporu",
|
||||
(Language::Turkish, "diagnosis_title") => "Oyun Kapanma & Hata Teşhis Raporu",
|
||||
(Language::Turkish, "diagnosis_desc") => "Açıklama:",
|
||||
(Language::Turkish, "diagnosis_solution") => "Önerilen Çözüm:",
|
||||
(Language::Turkish, "diagnosis_log") => "📋 İlgili Log Çıktısı (gow3_log.txt)",
|
||||
(Language::Turkish, "diagnosis_log") => "İlgili Log Çıktısı (gow3_log.txt)",
|
||||
(Language::Turkish, "diagnosis_ack") => "Anladım, Kapat",
|
||||
(Language::Turkish, "check_updates") => "🔄 Güncellemeleri Kontrol Et",
|
||||
(Language::Turkish, "checking_updates") => "⏳ Kontrol ediliyor...",
|
||||
(Language::Turkish, "update_modal_title") => "🚀 God of War III Güncellemesi",
|
||||
(Language::Turkish, "up_to_date_title") => "✅ Sisteminiz Güncel",
|
||||
(Language::Turkish, "update_available_notice") => "GitHub deposunda (ScreamAngel/gow3_pclauncher) yeni bir sürüm bulundu!",
|
||||
(Language::Turkish, "up_to_date_notice") => "Tüm yerel dosyalarınız ve launcher en son sürüm ile birebir eşleşiyor.",
|
||||
(Language::Turkish, "update_backup_info") => "Güncelleme uygulandığında launcher ve yama dosyaları en son sürüme güncellenir. Tüm kişisel ayarlarınız korunur.",
|
||||
(Language::Turkish, "apply_update") => "⚡ Güncellemeyi İndir ve Entegre Et",
|
||||
(Language::Turkish, "force_sync") => "🔄 Dosyaları Yeniden Eşitle & Entegre Et",
|
||||
(Language::Turkish, "latest_commit") => "Son Değişiklik:",
|
||||
(Language::Turkish, "check_updates") => "Güncellemeleri Kontrol Et",
|
||||
(Language::Turkish, "checking_updates") => "Kontrol Ediliyor...",
|
||||
(Language::Turkish, "update_modal_title") => "God of War III Güncellemesi",
|
||||
(Language::Turkish, "up_to_date_title") => "Sistem Güncel",
|
||||
(Language::Turkish, "update_available_notice") => "GitHub üzerinde yeni bir güncelleme tespit edildi (ScreamAngel/gow3_launcher)!",
|
||||
(Language::Turkish, "up_to_date_notice") => "Yerel dosyalarınız ve launcher en son sürümle tamamen senkronize.",
|
||||
(Language::Turkish, "update_backup_info") => "Güncelleme sırasında launcher ve yama dosyalarınız otomatik olarak en son sürüme yükseltilir. Ayarlarınız ve kayıtlarınız korunur.",
|
||||
(Language::Turkish, "apply_update") => "Güncellemeyi İndir ve Entegre Et",
|
||||
(Language::Turkish, "force_sync") => "Yeniden Senkronize Et ve Entegre Et",
|
||||
(Language::Turkish, "latest_commit") => "Son Commit:",
|
||||
(Language::Turkish, "commit_date") => "Tarih:",
|
||||
(Language::Turkish, "view_on_github") => "🌐 GitHub'da Görüntüle",
|
||||
(Language::Turkish, "view_on_github") => "GitHub'da Görüntüle",
|
||||
(Language::Turkish, "python_dep") => "Python 3 Bağımlılığı:",
|
||||
(Language::Turkish, "python_ready") => "Yüklü / 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 ayarlanıyor...",
|
||||
(Language::Turkish, "python_download_success") => "Taşınabilir Python 3 başarıyla kuruldu!",
|
||||
(Language::Turkish, "python_download_err") => "Python 3 indirme hatası: ",
|
||||
(Language::Turkish, "engine_label") => "Oyun Motoru:",
|
||||
(Language::Turkish, "engine_ready") => "Yüklü / Hazır (Vulkan Native)",
|
||||
(Language::Turkish, "engine_missing") => "Bulunamadı / Eksik!",
|
||||
(Language::Turkish, "engine_download") => "Oyun Motorunu İndir (~74 MB)",
|
||||
(Language::Turkish, "engine_downloading") => "Motor indiriliyor ve ayarlanıyor...",
|
||||
(Language::Turkish, "engine_download_success") => "Oyun motoru başarıyla kuruldu!",
|
||||
// Turkish Config Tab
|
||||
(Language::Turkish, "display_header") => "Görüntü Modu & Ekran Ayarları",
|
||||
(Language::Turkish, "display_mode") => "Görüntüleme Modu:",
|
||||
(Language::Turkish, "display_borderless") => "Kenarlıksız Pencereli (Borderless - Önerilen)",
|
||||
(Language::Turkish, "display_fullscreen") => "Tam Ekran (Exclusive Fullscreen)",
|
||||
(Language::Turkish, "display_windowed") => "Pencereli (Windowed)",
|
||||
(Language::Turkish, "display_desc") => "Kenarlıksız Pencereli modu çoklu monitörler ve Alt+Tab geçişleri için en kararlı deneyimi sunar.",
|
||||
(Language::Turkish, "vsync") => "Dikey Senkronizasyon (V-Sync)",
|
||||
(Language::Turkish, "vsync_desc") => "Ekran yırtılmalarını önlemek için kare hızını monitör tazeleme hızına kilitler.",
|
||||
(Language::Turkish, "controller_header") => "Kumanda / Gamepad Ayarları",
|
||||
(Language::Turkish, "bg_input") => "Arka Planda Kumanda Girişi (Background Input)",
|
||||
(Language::Turkish, "bg_input_desc") => "Oyun penceresi odakta değilken bile kumanda girdilerini algılamaya devam eder.",
|
||||
(Language::Turkish, "motion_controls") => "Hareket / Jiroskop Sensörleri (Gyro / Motion)",
|
||||
(Language::Turkish, "motion_controls_desc") => "DualShock 4 / DualSense dahili hareket algılama sensörlerini etkinleştirir.",
|
||||
(Language::Turkish, "deadzone_left") => "Sol Analog Ölü Bölge (Left Stick Deadzone):",
|
||||
(Language::Turkish, "deadzone_right") => "Sağ Analog Ölü Bölge (Right Stick Deadzone):",
|
||||
(Language::Turkish, "deadzone_desc") => "Düşük değer daha hassas kontrol sağlar; analog çubuğunuzda kendi kendine kayma (drift) varsa değeri artırın.",
|
||||
(Language::Turkish, "keyboard_header") => "Klavye Tuş Atamaları (Keyboard Bindings)",
|
||||
(Language::Turkish, "keyboard_desc") => "Değiştirmek istediğiniz tuşa tıklayın ve yeni tuşa basın. İptal etmek için ESC tuşuna basın.",
|
||||
(Language::Turkish, "preset_label") => "Hızlı Tuş Düzeni (Preset):",
|
||||
(Language::Turkish, "preset_standard") => "Standart WASD + CVBN (Önerilen)",
|
||||
(Language::Turkish, "preset_numpad") => "WASD + Numpad (8462)",
|
||||
(Language::Turkish, "reset_defaults") => "Varsayılanlara Sıfırla",
|
||||
(Language::Turkish, "press_any_key") => "[ Bir tuşa basın... / ESC ]",
|
||||
(Language::Turkish, "sec_face_buttons") => "Eylem Tuşları (Saldırı & Zıplama)",
|
||||
(Language::Turkish, "sec_triggers") => "Tetik & Omuz Tuşları (L1/R1/L2/R2/L3/R3)",
|
||||
(Language::Turkish, "sec_dpad_menu") => "Yön Tuşları & Menü (D-Pad / Options / Touchpad)",
|
||||
(Language::Turkish, "sec_analog_sticks") => "Analog Çubuklar (Hareket & Kamera)",
|
||||
(Language::Turkish, "act_triangle") => "Üçgen (Ağır Saldırı)",
|
||||
(Language::Turkish, "act_square") => "Kare (Hafif Saldırı)",
|
||||
(Language::Turkish, "act_circle") => "Daire (Yakalama)",
|
||||
(Language::Turkish, "act_cross") => "Çarpı (Zıplama)",
|
||||
(Language::Turkish, "act_l1") => "L1 (Savunma / Blok)",
|
||||
(Language::Turkish, "act_r1") => "R1 (Kaçınma / Etkileşim)",
|
||||
(Language::Turkish, "act_l2") => "L2 (Büyü Saldırısı)",
|
||||
(Language::Turkish, "act_r2") => "R2 (Özel Eşya)",
|
||||
(Language::Turkish, "act_l3") => "L3 (Sol Stick Tıklama)",
|
||||
(Language::Turkish, "act_r3") => "R3 (Sağ Stick Tıklama)",
|
||||
(Language::Turkish, "act_options") => "Options (Menü / Duraklat)",
|
||||
(Language::Turkish, "act_touchpad") => "Touchpad (Harita / Seçim)",
|
||||
(Language::Turkish, "act_dpad_up") => "D-Pad Yukarı",
|
||||
(Language::Turkish, "act_dpad_down") => "D-Pad Aşağı",
|
||||
(Language::Turkish, "act_dpad_left") => "D-Pad Sol",
|
||||
(Language::Turkish, "act_dpad_right") => "D-Pad Sağ",
|
||||
(Language::Turkish, "act_move_up") => "Sol Analog: İleri Git",
|
||||
(Language::Turkish, "act_move_down") => "Sol Analog: Geri Git",
|
||||
(Language::Turkish, "act_move_left") => "Sol Analog: Sola Git",
|
||||
(Language::Turkish, "act_move_right") => "Sol Analog: Sağa Git",
|
||||
(Language::Turkish, "act_cam_up") => "Sağ Analog: Yukarı",
|
||||
(Language::Turkish, "act_cam_down") => "Sağ Analog: Aşağı",
|
||||
(Language::Turkish, "act_cam_left") => "Sağ Analog: Sol",
|
||||
(Language::Turkish, "act_cam_right") => "Sağ Analog: Sağ",
|
||||
|
||||
// English
|
||||
(Language::English, "hero_badge") => "🔥 Open Source PC Launcher",
|
||||
(Language::English, "hero_desc") => "Designed to perfect the God of War III Remastered experience across Windows, Linux, and Steam Deck.",
|
||||
(Language::English, "badge_v13") => "v1.4",
|
||||
(Language::English, "badge_windows") => "Windows",
|
||||
(Language::English, "badge_linux") => "Linux",
|
||||
(Language::English, "badge_steam_deck") => "Steam Deck",
|
||||
(Language::English, "badge_free_oss") => "100% Free & Open Source",
|
||||
(Language::English, "tag_hardware") => "H A R D W A R E & S Y S T E M",
|
||||
(Language::English, "tag_game_location") => "G A M E L O C A T I O N",
|
||||
(Language::English, "tag_patches") => "P E R F O R M A N C E & P A T C H E S",
|
||||
(Language::English, "tag_display") => "D I S P L A Y & G R A P H I C S",
|
||||
(Language::English, "tag_controller") => "C O N T R O L L E R & G A M E P A D",
|
||||
(Language::English, "tag_keyboard") => "K E Y B O A R D B I N D I N G S",
|
||||
(Language::English, "app_title") => "GOD OF WAR® III REMASTERED",
|
||||
(Language::English, "app_subtitle") => "Linux & Windows Native Port Launcher & Hardware Optimizer",
|
||||
(Language::English, "sys_hardware") => "🖥️ System Hardware",
|
||||
(Language::English, "locked_auto") => "🔒 Locked (Auto-detected)",
|
||||
(Language::English, "app_subtitle") => "Linux & Windows Port Launcher & Hardware Manager",
|
||||
(Language::English, "tab_game_perf") => "Game & Launch",
|
||||
(Language::English, "tab_game") => "Game & Launch",
|
||||
(Language::English, "tab_graphics") => "Graphics Settings",
|
||||
(Language::English, "tab_config_controls") => "Settings & Controls",
|
||||
(Language::English, "graphics_header") => "Graphics & Performance Settings",
|
||||
(Language::English, "graphics_subtitle") => "Low-end system optimizations, resolution scaling, and visual effect controls",
|
||||
(Language::English, "graphic_preset") => "Graphics Preset:",
|
||||
(Language::English, "preset_ultra_low") => "🚀 Ultra Low-End (480p + FSR + Reduced Effects)",
|
||||
(Language::English, "preset_low") => "⚡ Low-End / Steam Deck (720p + FSR + Reduced Effects)",
|
||||
(Language::English, "preset_steam_deck") => "🎮 Steam Deck Native (800p + FSR + 40/90Hz)",
|
||||
(Language::English, "preset_balanced") => "⚖️ Standard / Native (1080p Native + Default Effects)",
|
||||
(Language::English, "preset_ultra") => "💎 High Quality (1440p / 4K + 120 FPS)",
|
||||
(Language::English, "preset_custom") => "🛠️ Custom (User Defined)",
|
||||
(Language::English, "preset_desc") => "Select a preset to optimize all settings with one click. Any manual change will automatically switch to Custom.",
|
||||
(Language::English, "render_resolution") => "Internal Resolution:",
|
||||
(Language::English, "res_desc") => "Internal rendering resolution. Selecting 720p or 480p massively increases FPS on low-end systems.",
|
||||
(Language::English, "fsr_toggle") => "AMD FSR 1.0 & RCAS Sharpening",
|
||||
(Language::English, "fsr_desc") => "Sharpens and scales lower resolutions (720p/480p) to your display without blur.",
|
||||
(Language::English, "disable_motion_blur") => "Disable Motion Blur",
|
||||
(Language::English, "disable_motion_blur_desc") => "Removes camera and object motion smearing, improving visual clarity and reducing GPU load.",
|
||||
(Language::English, "disable_dof") => "Disable Depth of Field (DoF)",
|
||||
(Language::English, "disable_dof_desc") => "Disables heavy background blur pass, keeping background sharp and boosting FPS.",
|
||||
(Language::English, "vram_budget") => "VRAM Garbage Collection Budget:",
|
||||
(Language::English, "vram_budget_desc") => "Adjust according to your GPU VRAM capacity. Prevents memory overflow and stutter on budget cards.",
|
||||
(Language::English, "sys_hardware") => "System Hardware",
|
||||
(Language::English, "locked_auto") => "Locked (Auto Detected)",
|
||||
(Language::English, "cpu") => "Processor (CPU)",
|
||||
(Language::English, "gpu") => "Graphics Card (GPU)",
|
||||
(Language::English, "vram") => "VRAM Capacity",
|
||||
(Language::English, "vram_safe") => "Safe from texture corruption",
|
||||
(Language::English, "vram_warning") => "Low VRAM (Glitch risk)",
|
||||
(Language::English, "vram_safe") => "Safe against texture corruption",
|
||||
(Language::English, "vram_warning") => "Low VRAM (Corruption risk)",
|
||||
(Language::English, "ram") => "System Memory (RAM)",
|
||||
(Language::English, "ram_safe") => "Sufficient RAM",
|
||||
(Language::English, "ram_safe") => "Sufficient Memory",
|
||||
(Language::English, "ram_warning") => "Low RAM",
|
||||
(Language::English, "refresh_rate") => "Monitor Refresh",
|
||||
(Language::English, "game_location") => "📂 Game Location",
|
||||
(Language::English, "no_folder_selected") => "No game directory selected yet",
|
||||
(Language::English, "manual_select") => "📁 Browse Folder",
|
||||
(Language::English, "auto_scan") => "🔍 Auto Scan",
|
||||
(Language::English, "refresh_rate") => "Display Refresh",
|
||||
(Language::English, "game_location") => "Game Location",
|
||||
(Language::English, "no_folder_selected") => "No game directory configured yet",
|
||||
(Language::English, "manual_select") => "Browse",
|
||||
(Language::English, "auto_scan") => "Auto Scan",
|
||||
(Language::English, "scanning") => "Scanning...",
|
||||
(Language::English, "game_valid") => "✅ Complete and valid God of War III directory verified",
|
||||
(Language::English, "game_invalid") => "❌ Game directory incomplete, corrupted, or 'eboot.bin' missing!",
|
||||
(Language::English, "patches_header") => "⚙️ Patches & Performance Settings",
|
||||
(Language::English, "hz_patch") => "Refresh Rate (Hz) Sync Patch",
|
||||
(Language::English, "hz_desc") => "60 FPS maintains intended game speed. For 144Hz smoothness, set monitor to 120Hz or enable FreeSync/VRR.",
|
||||
(Language::English, "game_valid") => "Valid and complete God of War III directory verified",
|
||||
(Language::English, "game_invalid") => "Missing game folder or 'eboot.bin' not found!",
|
||||
(Language::English, "patches_header") => "Patches & Optimizations",
|
||||
(Language::English, "hz_patch") => "Display Rate (Hz) & Dynamic FPS Synchronization",
|
||||
(Language::English, "hz_desc") => "Locks the game engine to target FPS while preserving the 60Hz physics and swing energy simulation. Eliminates high-speed glitches across 60, 120, 144+ FPS.",
|
||||
(Language::English, "target_fps") => "Target FPS:",
|
||||
(Language::English, "readback_patch") => "Yellow Texture Glitch Fix (Instant Readbacks)",
|
||||
(Language::English, "readback_desc") => "Prevents textures from turning golden-yellow in snowy scenes.",
|
||||
(Language::English, "skip_cinematics") => "Skip Cinematics with 'X' Button",
|
||||
(Language::English, "skip_cinematics_desc") => "Allows skipping in-game cutscenes instantly by pressing the X button.",
|
||||
(Language::English, "gamemode_toggle") => "🎮 Feral GameMode (gamemoderun)",
|
||||
(Language::English, "gamemode_desc") => "Optimizes CPU governor and GPU clocks for peak gaming performance.",
|
||||
(Language::English, "save_apply") => "💾 Save & Apply Settings",
|
||||
(Language::English, "view_log") => "📜 View Log File",
|
||||
(Language::English, "play_ready") => "PLAY GAME",
|
||||
(Language::English, "play_running") => "⏳ GAME RUNNING...",
|
||||
(Language::English, "play_disabled_warn") => "⚠️ Button Disabled: Complete God of War III directory not found!",
|
||||
(Language::English, "play_disabled_help") => "Please use 'Auto Scan' or 'Browse Folder' to locate your game files.",
|
||||
(Language::English, "play_subtitle") => "All optimizations enabled • Enjoy the game!",
|
||||
(Language::English, "first_launch_title") => "👋 Welcome - God of War III Setup",
|
||||
(Language::English, "first_launch_prompt") => "God of War III Launcher opened for the first time. Would you like to automatically scan hardware and game files to apply optimal 144Hz and texture patches?",
|
||||
(Language::English, "first_launch_auto") => "⚡ Auto Scan & Configure",
|
||||
(Language::English, "first_launch_manual") => "🔧 I will configure manually",
|
||||
(Language::English, "candidates_modal_title") => "🎮 Found God of War III Directories",
|
||||
(Language::English, "readback_patch") => "Texture Corruption & Yellow Tint Fix (Instant Readbacks)",
|
||||
(Language::English, "readback_desc") => "Forces immediate linear readbacks and disables stale buffers, completely eliminating yellow tint and corrupted textures in bright or snow scenes.",
|
||||
(Language::English, "async_readbacks") => "Asynchronous Readbacks (Anti-Stutter & Drop Prevention)",
|
||||
(Language::English, "async_readbacks_desc") => "Allows CPU writes and texture reads to proceed asynchronously without stalling on GPU fences. Eliminates periodic FPS drops and stuttering.",
|
||||
(Language::English, "skip_cinematics") => "Skip Cutscenes with 'X' Button",
|
||||
(Language::English, "skip_cinematics_desc") => "Allows skipping any cutscene immediately by pressing the X button.",
|
||||
(Language::English, "gamemode_toggle") => "Feral GameMode (gamemoderun)",
|
||||
(Language::English, "gamemode_desc") => "Configures CPU governor and GPU clocks for optimal gaming performance.",
|
||||
(Language::English, "save_apply") => "Save & Apply Settings",
|
||||
(Language::English, "view_log") => "View Log File",
|
||||
(Language::English, "play_ready") => "LAUNCH GAME",
|
||||
(Language::English, "play_running") => "GAME IS RUNNING...",
|
||||
(Language::English, "force_quit_btn") => "Force Quit",
|
||||
(Language::English, "force_quit_tooltip") => "Immediately kills the game process if it hangs or freezes.",
|
||||
(Language::English, "force_quit_status") => "Game process was forcefully terminated.",
|
||||
(Language::English, "kill_leftover_btn") => "Kill Process",
|
||||
(Language::English, "kill_leftover_tooltip") => "Kills any hanging or leftover background game processes (gow3-probe).",
|
||||
(Language::English, "play_disabled_warn") => "Button Disabled: Valid God of War III folder not found!",
|
||||
(Language::English, "play_disabled_help") => "Please use 'Auto Scan' or 'Browse' button above to select your game folder.",
|
||||
(Language::English, "play_subtitle") => "All optimizations active • Enjoy playing!",
|
||||
(Language::English, "first_launch_title") => "Welcome - God of War III Setup",
|
||||
(Language::English, "first_launch_prompt") => "God of War III Launcher is running for the first time. Would you like to automatically scan for the game and configure optimal patches?",
|
||||
(Language::English, "first_launch_auto") => "Auto Scan & Configure",
|
||||
(Language::English, "first_launch_manual") => "I Will Configure Manually",
|
||||
(Language::English, "candidates_modal_title") => "Found God of War III Directories",
|
||||
(Language::English, "game_not_found_modal_title") => "Game Not Found",
|
||||
(Language::English, "game_not_found_title") => "God of War III is not installed on this system!",
|
||||
(Language::English, "game_not_found_desc") => "Drives were scanned but no valid God of War III Remastered (CUSA01623 / CUSA01715) installation was detected.\n\n• The game might not be installed on your computer yet.\n• If the game is on an external drive or custom path, please click 'Browse' below and choose the folder containing 'eboot.bin'.",
|
||||
(Language::English, "scan_not_found_inline") => "Game is not installed or folder not found! Please install the game or use 'Browse'.",
|
||||
(Language::English, "select_this") => "Use This Folder",
|
||||
(Language::English, "close") => "Close",
|
||||
(Language::English, "diagnosis_title") => "🚨 Game Crash & Error Diagnosis Report",
|
||||
(Language::English, "diagnosis_title") => "Crash & Error Diagnosis Report",
|
||||
(Language::English, "diagnosis_desc") => "Description:",
|
||||
(Language::English, "diagnosis_solution") => "Recommended Fix:",
|
||||
(Language::English, "diagnosis_log") => "📋 Relevant Log Snippet (gow3_log.txt)",
|
||||
(Language::English, "diagnosis_solution") => "Recommended Solution:",
|
||||
(Language::English, "diagnosis_log") => "Relevant Log Snippet (gow3_log.txt)",
|
||||
(Language::English, "diagnosis_ack") => "Understood, Close",
|
||||
(Language::English, "check_updates") => "🔄 Check for Updates",
|
||||
(Language::English, "checking_updates") => "⏳ Checking...",
|
||||
(Language::English, "update_modal_title") => "🚀 God of War III Update",
|
||||
(Language::English, "up_to_date_title") => "✅ System Up to Date",
|
||||
(Language::English, "update_available_notice") => "A new version was found on GitHub (ScreamAngel/gow3_pclauncher)!",
|
||||
(Language::English, "up_to_date_notice") => "Your local launcher and files match the latest release.",
|
||||
(Language::English, "update_backup_info") => "Applying the update will upgrade the launcher and patch files to the latest version. All your settings are preserved.",
|
||||
(Language::English, "apply_update") => "⚡ Download & Integrate Update",
|
||||
(Language::English, "force_sync") => "🔄 Re-sync & Re-integrate Files",
|
||||
(Language::English, "check_updates") => "Check for Updates",
|
||||
(Language::English, "checking_updates") => "Checking...",
|
||||
(Language::English, "update_modal_title") => "God of War III Update",
|
||||
(Language::English, "up_to_date_title") => "System Up to Date",
|
||||
(Language::English, "update_available_notice") => "A new update was found on GitHub (ScreamAngel/gow3_launcher)!",
|
||||
(Language::English, "up_to_date_notice") => "Your local files and launcher are in sync with the latest release.",
|
||||
(Language::English, "update_backup_info") => "Updating will download and apply the newest launcher and patch scripts. Your settings remain untouched.",
|
||||
(Language::English, "apply_update") => "Download & Integrate",
|
||||
(Language::English, "force_sync") => "Re-sync & Integrate",
|
||||
(Language::English, "latest_commit") => "Latest Commit:",
|
||||
(Language::English, "commit_date") => "Date:",
|
||||
(Language::English, "view_on_github") => "🌐 View on GitHub",
|
||||
(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") => "Missing",
|
||||
(Language::English, "python_download") => "Download Portable Python 3 (~10 MB, No install needed)",
|
||||
(Language::English, "python_downloading") => "Downloading & configuring Python 3...",
|
||||
(Language::English, "python_download_success") => "Portable Python 3 configured successfully!",
|
||||
(Language::English, "python_download_err") => "Python 3 download error: ",
|
||||
(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 & configuring game engine...",
|
||||
(Language::English, "engine_download_success") => "Game engine installed successfully!",
|
||||
// English Config Tab
|
||||
(Language::English, "display_header") => "Display Mode & Screen Settings",
|
||||
(Language::English, "display_mode") => "Display Mode:",
|
||||
(Language::English, "display_borderless") => "Borderless Window (Recommended)",
|
||||
(Language::English, "display_fullscreen") => "Exclusive Fullscreen",
|
||||
(Language::English, "display_windowed") => "Windowed",
|
||||
(Language::English, "display_desc") => "Borderless window mode provides the most stable experience for multi-monitor setups and Alt+Tab switching.",
|
||||
(Language::English, "vsync") => "Vertical Sync (V-Sync)",
|
||||
(Language::English, "vsync_desc") => "Synchronizes frame rate with monitor refresh rate to prevent screen tearing.",
|
||||
(Language::English, "controller_header") => "Gamepad & Controller Settings",
|
||||
(Language::English, "bg_input") => "Background Controller Input",
|
||||
(Language::English, "bg_input_desc") => "Allows gamepad inputs even when the game window is not actively focused.",
|
||||
(Language::English, "motion_controls") => "Motion & Gyroscope Controls",
|
||||
(Language::English, "motion_controls_desc") => "Enables DualShock 4 / DualSense internal accelerometer and gyroscope features.",
|
||||
(Language::English, "deadzone_left") => "Left Stick Deadzone:",
|
||||
(Language::English, "deadzone_right") => "Right Stick Deadzone:",
|
||||
(Language::English, "deadzone_desc") => "Lower values provide more responsiveness; increase if your stick suffers from drift.",
|
||||
(Language::English, "keyboard_header") => "Keyboard Bindings",
|
||||
(Language::English, "keyboard_desc") => "Click any button to assign a new key. Press ESC to cancel.",
|
||||
(Language::English, "preset_label") => "Quick Preset:",
|
||||
(Language::English, "preset_standard") => "Standard WASD + CVBN (Recommended)",
|
||||
(Language::English, "preset_numpad") => "WASD + Numpad (8462)",
|
||||
(Language::English, "reset_defaults") => "Reset to Defaults",
|
||||
(Language::English, "press_any_key") => "[ Press key... / ESC ]",
|
||||
(Language::English, "sec_face_buttons") => "Action Buttons (Attacks & Jump)",
|
||||
(Language::English, "sec_triggers") => "Triggers & Shoulder Buttons (L1/R1/L2/R2/L3/R3)",
|
||||
(Language::English, "sec_dpad_menu") => "D-Pad & Menu (Options / Touchpad)",
|
||||
(Language::English, "sec_analog_sticks") => "Analog Sticks (Movement & Camera)",
|
||||
(Language::English, "act_triangle") => "Triangle (Heavy Attack)",
|
||||
(Language::English, "act_square") => "Square (Light Attack)",
|
||||
(Language::English, "act_circle") => "Circle (Grab)",
|
||||
(Language::English, "act_cross") => "Cross (Jump)",
|
||||
(Language::English, "act_l1") => "L1 (Block / Guard)",
|
||||
(Language::English, "act_r1") => "R1 (Evade / Interact)",
|
||||
(Language::English, "act_l2") => "L2 (Magic)",
|
||||
(Language::English, "act_r2") => "R2 (Item)",
|
||||
(Language::English, "act_l3") => "L3 (Left Stick Click)",
|
||||
(Language::English, "act_r3") => "R3 (Right Stick Click)",
|
||||
(Language::English, "act_options") => "Options (Pause / Menu)",
|
||||
(Language::English, "act_touchpad") => "Touchpad (Map / Menu)",
|
||||
(Language::English, "act_dpad_up") => "D-Pad Up",
|
||||
(Language::English, "act_dpad_down") => "D-Pad Down",
|
||||
(Language::English, "act_dpad_left") => "D-Pad Left",
|
||||
(Language::English, "act_dpad_right") => "D-Pad Right",
|
||||
(Language::English, "act_move_up") => "Left Stick: Move Forward",
|
||||
(Language::English, "act_move_down") => "Left Stick: Move Backward",
|
||||
(Language::English, "act_move_left") => "Left Stick: Move Left",
|
||||
(Language::English, "act_move_right") => "Left Stick: Move Right",
|
||||
(Language::English, "act_cam_up") => "Right Stick: Up",
|
||||
(Language::English, "act_cam_down") => "Right Stick: Down",
|
||||
(Language::English, "act_cam_left") => "Right Stick: Left",
|
||||
(Language::English, "act_cam_right") => "Right Stick: Right",
|
||||
|
||||
// German
|
||||
(Language::German, "hero_badge") => "🔥 Open-Source-PC-Launcher",
|
||||
(Language::German, "hero_desc") => "Entwickelt zur Perfektionierung von God of War III Remastered auf Windows, Linux und Steam Deck.",
|
||||
(Language::German, "badge_v13") => "v1.4",
|
||||
(Language::German, "badge_windows") => "Windows",
|
||||
(Language::German, "badge_linux") => "Linux",
|
||||
(Language::German, "badge_steam_deck") => "Steam Deck",
|
||||
(Language::German, "badge_free_oss") => "100% Kostenlos & Open Source",
|
||||
(Language::German, "tag_hardware") => "H A R D W A R E & S Y S T E M",
|
||||
(Language::German, "tag_game_location") => "S P I E L P F A D",
|
||||
(Language::German, "tag_patches") => "L E I S T U N G & P A T C H E S",
|
||||
(Language::German, "tag_display") => "A N Z E I G E & G R A F I K",
|
||||
(Language::German, "tag_controller") => "C O N T R O L L E R & G A M E P A D",
|
||||
(Language::German, "tag_keyboard") => "T A S T A T U R B E L E G U N G",
|
||||
(Language::German, "app_title") => "GOD OF WAR® III REMASTERED",
|
||||
(Language::German, "app_subtitle") => "Linux & Windows Nativer Launcher & Hardware-Optimierer",
|
||||
(Language::German, "sys_hardware") => "🖥️ System-Hardware",
|
||||
(Language::German, "locked_auto") => "🔒 Gesperrt (Automatisch erkannt)",
|
||||
(Language::German, "app_subtitle") => "Linux & Windows Port Launcher & Hardware-Manager",
|
||||
(Language::German, "tab_game_perf") => "Spiel & Start",
|
||||
(Language::German, "tab_game") => "Spiel & Start",
|
||||
(Language::German, "tab_graphics") => "Grafikeinstellungen",
|
||||
(Language::German, "tab_config_controls") => "Konfiguration & Steuerung",
|
||||
(Language::German, "graphics_header") => "Grafik- & Leistungseinstellungen",
|
||||
(Language::German, "graphics_subtitle") => "Optimierungen für Low-End-Systeme, Auflösungsskalierung und visuelle Effekte",
|
||||
(Language::German, "graphic_preset") => "Grafikprofil:",
|
||||
(Language::German, "preset_ultra_low") => "🚀 Ultra Low-End (480p + FSR + Reduzierte Effekte)",
|
||||
(Language::German, "preset_low") => "⚡ Low-End / Steam Deck (720p + FSR + Reduzierte Effekte)",
|
||||
(Language::German, "preset_steam_deck") => "🎮 Steam Deck (800p + FSR + 40/90Hz)",
|
||||
(Language::German, "preset_balanced") => "⚖️ Standard (1080p Nativ + Standard-Effekte)",
|
||||
(Language::German, "preset_ultra") => "💎 Hohe Qualität (1440p / 4K + 120 FPS)",
|
||||
(Language::German, "preset_custom") => "🛠️ Benutzerdefiniert",
|
||||
(Language::German, "preset_desc") => "Wählen Sie ein Profil, um alle Einstellungen mit einem Klick zu optimieren.",
|
||||
(Language::German, "render_resolution") => "Interne Auflösung:",
|
||||
(Language::German, "res_desc") => "Interne Render-Auflösung. 720p oder 480p erhöht die FPS auf schwächeren Systemen deutlich.",
|
||||
(Language::German, "fsr_toggle") => "AMD FSR 1.0 & RCAS Schärfung",
|
||||
(Language::German, "fsr_desc") => "Skaliert und schärft niedrigere Auflösungen (720p/480p) sauber auf den Bildschirm.",
|
||||
(Language::German, "disable_motion_blur") => "Bewegungsunschärfe deaktivieren",
|
||||
(Language::German, "disable_motion_blur_desc") => "Entfernt Unschärfe bei Kamerabewegungen, verbessert die Bildklarheit und senkt die GPU-Last.",
|
||||
(Language::German, "disable_dof") => "Tiefenschärfe (DoF) deaktivieren",
|
||||
(Language::German, "disable_dof_desc") => "Deaktiviert die Hintergrundunschärfe, sorgt für ein schärferes Bild und mehr FPS.",
|
||||
(Language::German, "vram_budget") => "VRAM-Bereinigungsbudget:",
|
||||
(Language::German, "vram_budget_desc") => "Passen Sie das Budget an Ihre GPU an, um Ruckler bei wenig VRAM zu vermeiden.",
|
||||
(Language::German, "sys_hardware") => "System-Hardware",
|
||||
(Language::German, "locked_auto") => "Gesperrt (Automatisch erkannt)",
|
||||
(Language::German, "cpu") => "Prozessor (CPU)",
|
||||
(Language::German, "gpu") => "Grafikkarte (GPU)",
|
||||
(Language::German, "vram") => "VRAM-Kapazität",
|
||||
(Language::German, "vram_safe") => "Sicher vor Texturfehlern",
|
||||
(Language::German, "vram_warning") => "Niedriger VRAM (Fehlerrisiko)",
|
||||
(Language::German, "vram_safe") => "Sicher gegen Texturfehler",
|
||||
(Language::German, "vram_warning") => "Wenig VRAM (Fehlerrisiko)",
|
||||
(Language::German, "ram") => "Arbeitsspeicher (RAM)",
|
||||
(Language::German, "ram_safe") => "Ausreichend RAM",
|
||||
(Language::German, "ram_warning") => "Niedriger RAM",
|
||||
(Language::German, "ram_safe") => "Ausreichender Speicher",
|
||||
(Language::German, "ram_warning") => "Wenig RAM",
|
||||
(Language::German, "refresh_rate") => "Bildwiederholrate",
|
||||
(Language::German, "game_location") => "📂 Spielverzeichnis",
|
||||
(Language::German, "no_folder_selected") => "Noch kein Spielordner ausgewählt",
|
||||
(Language::German, "manual_select") => "📁 Durchsuchen",
|
||||
(Language::German, "auto_scan") => "🔍 Automatisch scannen",
|
||||
(Language::German, "scanning") => "Wird gescannt...",
|
||||
(Language::German, "game_valid") => "✅ Vollständiger God of War III Ordner bestätigt",
|
||||
(Language::German, "game_invalid") => "❌ Spielordner unvollständig oder 'eboot.bin' fehlt!",
|
||||
(Language::German, "patches_header") => "⚙️ Patches & Leistung",
|
||||
(Language::German, "hz_patch") => "Bildwiederholrate (Hz) Sync-Patch",
|
||||
(Language::German, "hz_desc") => "Verhindert 2x Spielbeschleunigung auf 144Hz+ Monitoren.",
|
||||
(Language::German, "game_location") => "Spielverzeichnis",
|
||||
(Language::German, "no_folder_selected") => "Noch kein Spielverzeichnis gewählt",
|
||||
(Language::German, "manual_select") => "Durchsuchen",
|
||||
(Language::German, "auto_scan") => "Auto-Scan",
|
||||
(Language::German, "scanning") => "Scannen...",
|
||||
(Language::German, "game_valid") => "Gültiges God of War III Verzeichnis bestätigt",
|
||||
(Language::German, "game_invalid") => "Spielordner unvollständig oder 'eboot.bin' fehlt!",
|
||||
(Language::German, "patches_header") => "Patches & Optimierungen",
|
||||
(Language::German, "hz_patch") => "Bildwiederholrate (Hz) & Dynamische FPS-Synchronisation",
|
||||
(Language::German, "hz_desc") => "Sperrt die Engine auf Ziel-FPS unter Beibehaltung der 60Hz-Physik. Verhindert Geschwindigkeitsfehler und Glitches bei 60, 120, 144+ FPS.",
|
||||
(Language::German, "target_fps") => "Ziel-FPS:",
|
||||
(Language::German, "readback_patch") => "Gelb-Texturfehler-Korrektur (Instant Readbacks)",
|
||||
(Language::German, "readback_desc") => "Verhindert Gelbfärbung von Texturen in Schneeszenen.",
|
||||
(Language::German, "skip_cinematics") => "Zwischensequenzen mit 'X' überspringen",
|
||||
(Language::German, "readback_patch") => "Texturfehler- & Gelbstich-Behebung (Instant Readbacks)",
|
||||
(Language::German, "readback_desc") => "Erzwingt sofortige lineare Readbacks und verhindert goldgelbe Texturen und Artefakte in Schneeszenen.",
|
||||
(Language::German, "async_readbacks") => "Asynchrone Readbacks (Anti-Ruckler & Drop-Schutz)",
|
||||
(Language::German, "async_readbacks_desc") => "Entkoppelt CPU- und GPU-Wartezeiten für Texturoperationen. Verhindert plötzliche FPS-Drops und Ruckler.",
|
||||
(Language::German, "skip_cinematics") => "Zwischensequenzen mit 'X'-Taste überspringen",
|
||||
(Language::German, "skip_cinematics_desc") => "Ermöglicht das sofortige Überspringen von Zwischensequenzen mit der X-Taste.",
|
||||
(Language::German, "gamemode_toggle") => "🎮 Feral GameMode (gamemoderun)",
|
||||
(Language::German, "gamemode_desc") => "Optimiert CPU-Taktung und GPU für maximale Gaming-Leistung.",
|
||||
(Language::German, "save_apply") => "💾 Einstellungen speichern & anwenden",
|
||||
(Language::German, "view_log") => "📜 Protokolldatei anzeigen",
|
||||
(Language::German, "gamemode_toggle") => "Feral GameMode (gamemoderun)",
|
||||
(Language::German, "gamemode_desc") => "Optimiert CPU-Governor und GPU für maximale Gaming-Leistung.",
|
||||
(Language::German, "save_apply") => "Einstellungen speichern & anwenden",
|
||||
(Language::German, "view_log") => "Logdatei ansehen",
|
||||
(Language::German, "play_ready") => "SPIEL STARTEN",
|
||||
(Language::German, "play_running") => "⏳ SPIEL LÄUFT...",
|
||||
(Language::German, "play_disabled_warn") => "⚠️ Deaktiviert: Vollständiger Ordner nicht gefunden!",
|
||||
(Language::German, "play_disabled_help") => "Bitte scannen Sie oder wählen Sie den Ordner manuell aus.",
|
||||
(Language::German, "play_running") => "SPIEL LÄUFT...",
|
||||
(Language::German, "force_quit_btn") => "Beenden erzwingen",
|
||||
(Language::German, "force_quit_tooltip") => "Beendet den Spielprozess sofort, falls er einfriert oder abstürzt.",
|
||||
(Language::German, "force_quit_status") => "Spielprozess wurde zwangsweise beendet.",
|
||||
(Language::German, "kill_leftover_btn") => "Prozess beenden",
|
||||
(Language::German, "kill_leftover_tooltip") => "Beendet hängende Hintergrundprozesse (gow3-probe).",
|
||||
(Language::German, "play_disabled_warn") => "Button deaktiviert: God of War III nicht gefunden!",
|
||||
(Language::German, "play_disabled_help") => "Bitte nutzen Sie 'Auto-Scan' oder 'Durchsuchen' zum Hinzufügen.",
|
||||
(Language::German, "play_subtitle") => "Alle Optimierungen aktiv • Viel Spaß!",
|
||||
(Language::German, "first_launch_title") => "👋 Willkommen - God of War III Einrichtung",
|
||||
(Language::German, "first_launch_prompt") => "Erster Start. Möchten Sie Hardware und Spielordner automatisch scannen und optimieren?",
|
||||
(Language::German, "first_launch_auto") => "⚡ Automatisch scannen & konfigurieren",
|
||||
(Language::German, "first_launch_manual") => "🔧 Manuell konfigurieren",
|
||||
(Language::German, "candidates_modal_title") => "🎮 Gefundene Spielordner",
|
||||
(Language::German, "first_launch_title") => "Willkommen - God of War III Einrichtung",
|
||||
(Language::German, "first_launch_prompt") => "Erster Start. Möchten Sie das Spiel automatisch scannen und konfigurieren?",
|
||||
(Language::German, "first_launch_auto") => "Automatisch scannen & einrichten",
|
||||
(Language::German, "first_launch_manual") => "Manuell konfigurieren",
|
||||
(Language::German, "candidates_modal_title") => "Gefundene Spielordner",
|
||||
(Language::German, "game_not_found_modal_title") => "Spiel nicht gefunden",
|
||||
(Language::German, "game_not_found_title") => "God of War III ist nicht installiert!",
|
||||
(Language::German, "game_not_found_desc") => "Laufwerke wurden durchsucht, aber keine gültige God of War III Installation gefunden.\n\n• Das Spiel ist eventuell noch nicht installiert.\n• Falls es sich auf einem externen Laufwerk befindet, wählen Sie es bitte über 'Durchsuchen' aus.",
|
||||
(Language::German, "scan_not_found_inline") => "Spiel nicht installiert oder Ordner nicht gefunden! Bitte installieren oder 'Durchsuchen' nutzen.",
|
||||
(Language::German, "select_this") => "Diesen Ordner wählen",
|
||||
(Language::German, "close") => "Schließen",
|
||||
(Language::German, "diagnosis_title") => "🚨 Fehlerdiagnose-Bericht",
|
||||
(Language::German, "diagnosis_title") => "Fehlerdiagnose-Bericht",
|
||||
(Language::German, "diagnosis_desc") => "Beschreibung:",
|
||||
(Language::German, "diagnosis_solution") => "Empfohlene Lösung:",
|
||||
(Language::German, "diagnosis_log") => "📋 Relevanter Protokollauszug (gow3_log.txt)",
|
||||
(Language::German, "diagnosis_log") => "Relevanter Logauszug (gow3_log.txt)",
|
||||
(Language::German, "diagnosis_ack") => "Verstanden, Schließen",
|
||||
(Language::German, "check_updates") => "🔄 Nach Updates suchen",
|
||||
(Language::German, "checking_updates") => "⏳ Wird geprüft...",
|
||||
(Language::German, "update_modal_title") => "🚀 God of War III Aktualisierung",
|
||||
(Language::German, "up_to_date_title") => "✅ Auf dem neuesten Stand",
|
||||
(Language::German, "update_available_notice") => "Eine neue Version wurde auf GitHub gefunden (ScreamAngel/gow3_pclauncher)!",
|
||||
(Language::German, "up_to_date_notice") => "Ihr lokaler Launcher und die Dateien entsprechen der neuesten Version.",
|
||||
(Language::German, "update_backup_info") => "Bei der Aktualisierung werden Launcher und Patch-Dateien auf die neueste Version aktualisiert. Alle Einstellungen bleiben erhalten.",
|
||||
(Language::German, "apply_update") => "⚡ Aktualisieren & Integrieren",
|
||||
(Language::German, "force_sync") => "🔄 Neu synchronisieren",
|
||||
(Language::German, "latest_commit") => "Letzter Commit:",
|
||||
(Language::German, "check_updates") => "Nach Updates suchen",
|
||||
(Language::German, "checking_updates") => "Prüfe...",
|
||||
(Language::German, "update_modal_title") => "God of War III Update",
|
||||
(Language::German, "up_to_date_title") => "System ist aktuell",
|
||||
(Language::German, "update_available_notice") => "Neues Update auf GitHub gefunden (ScreamAngel/gow3_launcher)!",
|
||||
(Language::German, "up_to_date_notice") => "Ihre lokalen Dateien entsprechen der neuesten Version.",
|
||||
(Language::German, "update_backup_info") => "Beim Update werden Launcher und Patches aktualisiert. Ihre Einstellungen bleiben erhalten.",
|
||||
(Language::German, "apply_update") => "Herunterladen & Integrieren",
|
||||
(Language::German, "force_sync") => "Neu synchronisieren & Integrieren",
|
||||
(Language::German, "latest_commit") => "Neuester Commit:",
|
||||
(Language::German, "commit_date") => "Datum:",
|
||||
(Language::German, "view_on_github") => "🌐 Auf GitHub ansehen",
|
||||
(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)",
|
||||
(Language::German, "python_downloading") => "Lade Python 3 herunter...",
|
||||
(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") => "Lade Spiel-Engine herunter...",
|
||||
(Language::German, "engine_download_success") => "Spiel-Engine erfolgreich installiert!",
|
||||
// German Config Tab
|
||||
(Language::German, "display_header") => "Anzeigemodus & Bildschirmeinstellungen",
|
||||
(Language::German, "display_mode") => "Anzeigemodus:",
|
||||
(Language::German, "display_borderless") => "Randloses Fenster (Empfohlen)",
|
||||
(Language::German, "display_fullscreen") => "Vollbild",
|
||||
(Language::German, "display_windowed") => "Fenster",
|
||||
(Language::German, "display_desc") => "Der randlose Fenstermodus bietet das stabilste Erlebnis für Multi-Monitor-Setups und Alt+Tab.",
|
||||
(Language::German, "vsync") => "Vertikale Synchronisation (V-Sync)",
|
||||
(Language::German, "vsync_desc") => "Synchronisiert die Bildrate mit der Monitor-Bildwiederholfrequenz zur Vermeidung von Tearing.",
|
||||
(Language::German, "controller_header") => "Gamepad- & Controller-Einstellungen",
|
||||
(Language::German, "bg_input") => "Hintergrund-Controller-Eingabe",
|
||||
(Language::German, "bg_input_desc") => "Ermöglicht Gamepad-Eingaben auch wenn das Spielfenster nicht fokussiert ist.",
|
||||
(Language::German, "motion_controls") => "Bewegungs- & Gyrosensoren",
|
||||
(Language::German, "motion_controls_desc") => "Aktiviert interne Beschleunigungsmesser und Gyroskop der DualShock 4 / DualSense Controller.",
|
||||
(Language::German, "deadzone_left") => "Linker Stick Totzone:",
|
||||
(Language::German, "deadzone_right") => "Rechter Stick Totzone:",
|
||||
(Language::German, "deadzone_desc") => "Niedrigere Werte bieten mehr Reaktionsfähigkeit; erhöhen Sie den Wert bei Stick-Drift.",
|
||||
(Language::German, "keyboard_header") => "Tastaturbelegung",
|
||||
(Language::German, "keyboard_desc") => "Klicken Sie auf eine Taste und drücken Sie eine Taste auf der Tastatur. ESC zum Abbrechen.",
|
||||
(Language::German, "preset_label") => "Schnell-Voreinstellung:",
|
||||
(Language::German, "preset_standard") => "Standard WASD + CVBN (Empfohlen)",
|
||||
(Language::German, "preset_numpad") => "WASD + Numpad (8462)",
|
||||
(Language::German, "reset_defaults") => "Auf Standard zurücksetzen",
|
||||
(Language::German, "press_any_key") => "[ Taste drücken... / ESC ]",
|
||||
(Language::German, "sec_face_buttons") => "Aktionstasten (Angriff & Sprung)",
|
||||
(Language::German, "sec_triggers") => "Schultertasten & Trigger (L1/R1/L2/R2/L3/R3)",
|
||||
(Language::German, "sec_dpad_menu") => "Steuerkreuz & Menü (D-Pad / Options / Touchpad)",
|
||||
(Language::German, "sec_analog_sticks") => "Analogsticks (Bewegung & Kamera)",
|
||||
(Language::German, "act_triangle") => "Dreieck (Schwerer Angriff)",
|
||||
(Language::German, "act_square") => "Viereck (Leichter Angriff)",
|
||||
(Language::German, "act_circle") => "Kreis (Greifen)",
|
||||
(Language::German, "act_cross") => "Kreuz (Springen)",
|
||||
(Language::German, "act_l1") => "L1 (Blocken / Parieren)",
|
||||
(Language::German, "act_r1") => "R1 (Ausweichen / Interaktion)",
|
||||
(Language::German, "act_l2") => "L2 (Magie)",
|
||||
(Language::German, "act_r2") => "R2 (Gegenstand)",
|
||||
(Language::German, "act_l3") => "L3 (Linker Stick Klick)",
|
||||
(Language::German, "act_r3") => "R3 (Rechter Stick Klick)",
|
||||
(Language::German, "act_options") => "Options (Pause / Menü)",
|
||||
(Language::German, "act_touchpad") => "Touchpad (Karte / Menü)",
|
||||
(Language::German, "act_dpad_up") => "Steuerkreuz Oben",
|
||||
(Language::German, "act_dpad_down") => "Steuerkreuz Unten",
|
||||
(Language::German, "act_dpad_left") => "Steuerkreuz Links",
|
||||
(Language::German, "act_dpad_right") => "Steuerkreuz Rechts",
|
||||
(Language::German, "act_move_up") => "Linker Stick: Vorwärts",
|
||||
(Language::German, "act_move_down") => "Linker Stick: Rückwärts",
|
||||
(Language::German, "act_move_left") => "Linker Stick: Links",
|
||||
(Language::German, "act_move_right") => "Linker Stick: Rechts",
|
||||
(Language::German, "act_cam_up") => "Rechter Stick: Hoch",
|
||||
(Language::German, "act_cam_down") => "Rechter Stick: Runter",
|
||||
(Language::German, "act_cam_left") => "Rechter Stick: Links",
|
||||
(Language::German, "act_cam_right") => "Rechter Stick: Rechts",
|
||||
|
||||
// Spanish
|
||||
(Language::Spanish, "hero_badge") => "🔥 Lanzador de PC de Código Abierto",
|
||||
(Language::Spanish, "hero_desc") => "Diseñado para perfeccionar la experiencia de God of War III Remastered en Windows, Linux y Steam Deck.",
|
||||
(Language::Spanish, "badge_v13") => "v1.4",
|
||||
(Language::Spanish, "badge_windows") => "Windows",
|
||||
(Language::Spanish, "badge_linux") => "Linux",
|
||||
(Language::Spanish, "badge_steam_deck") => "Steam Deck",
|
||||
(Language::Spanish, "badge_free_oss") => "100% Gratis y Código Abierto",
|
||||
(Language::Spanish, "tag_hardware") => "H A R D W A R E Y S I S T E M A",
|
||||
(Language::Spanish, "tag_game_location") => "U B I C A C I Ó N D E L J U E G O",
|
||||
(Language::Spanish, "tag_patches") => "R E N D I M I E N T O Y P A R C H E S",
|
||||
(Language::Spanish, "tag_display") => "P A N T A L L A Y G R Á F I C O S",
|
||||
(Language::Spanish, "tag_controller") => "M A N D O Y G A M E P A D",
|
||||
(Language::Spanish, "tag_keyboard") => "A S I G N A C I Ó N D E T E C L A S",
|
||||
(Language::Spanish, "app_title") => "GOD OF WAR® III REMASTERED",
|
||||
(Language::Spanish, "app_subtitle") => "Lanzador y Optimizador para Linux y Windows",
|
||||
(Language::Spanish, "sys_hardware") => "🖥️ Hardware del Sistema",
|
||||
(Language::Spanish, "locked_auto") => "🔒 Bloqueado (Detectado)",
|
||||
(Language::Spanish, "app_subtitle") => "Lanzador de Port Linux y Windows y Gestor de Hardware",
|
||||
(Language::Spanish, "tab_game_perf") => "Juego e Inicio",
|
||||
(Language::Spanish, "tab_game") => "Juego e Inicio",
|
||||
(Language::Spanish, "tab_graphics") => "Ajustes Gráficos",
|
||||
(Language::Spanish, "tab_config_controls") => "Configuración y Controles",
|
||||
(Language::Spanish, "graphics_header") => "Ajustes Gráficos y Rendimiento",
|
||||
(Language::Spanish, "graphics_subtitle") => "Optimizaciones para gama baja, escalado de resolución y efectos visuales",
|
||||
(Language::Spanish, "graphic_preset") => "Perfil Gráfico:",
|
||||
(Language::Spanish, "preset_ultra_low") => "🚀 Gama Ultra Baja (480p + FSR + Efectos Reducidos)",
|
||||
(Language::Spanish, "preset_low") => "⚡ Gama Baja / Steam Deck (720p + FSR + Efectos Reducidos)",
|
||||
(Language::Spanish, "preset_steam_deck") => "🎮 Steam Deck (800p + FSR + 40/90Hz)",
|
||||
(Language::Spanish, "preset_balanced") => "⚖️ Estándar / Nativo (1080p Nativo + Efectos por Defecto)",
|
||||
(Language::Spanish, "preset_ultra") => "💎 Calidad Alta (1440p / 4K + 120 FPS)",
|
||||
(Language::Spanish, "preset_custom") => "🛠️ Personalizado",
|
||||
(Language::Spanish, "preset_desc") => "Seleccione un perfil para optimizar con un clic. Cualquier cambio manual cambiará el perfil a Personalizado.",
|
||||
(Language::Spanish, "render_resolution") => "Resolución Interna:",
|
||||
(Language::Spanish, "res_desc") => "Resolución de renderizado interno. Seleccionar 720p o 480p aumenta enormemente los FPS en PCs modestos.",
|
||||
(Language::Spanish, "fsr_toggle") => "AMD FSR 1.0 y Nitidez RCAS",
|
||||
(Language::Spanish, "fsr_desc") => "Escala y enfoca resoluciones menores (720p/480p) a la pantalla sin desenfoque.",
|
||||
(Language::Spanish, "disable_motion_blur") => "Desactivar Desenfoque de Movimiento",
|
||||
(Language::Spanish, "disable_motion_blur_desc") => "Elimina el desenfoque de cámara y objetos, mejorando la claridad y reduciendo la carga de GPU.",
|
||||
(Language::Spanish, "disable_dof") => "Desactivar Profundidad de Campo (DoF)",
|
||||
(Language::Spanish, "disable_dof_desc") => "Desactiva el filtro pesado de fondo, manteniendo nitidez en la distancia y ganando FPS.",
|
||||
(Language::Spanish, "vram_budget") => "Presupuesto de Limpieza VRAM:",
|
||||
(Language::Spanish, "vram_budget_desc") => "Ajuste según la memoria de su GPU para evitar tirones en tarjetas con poca VRAM.",
|
||||
(Language::Spanish, "sys_hardware") => "Hardware del Sistema",
|
||||
(Language::Spanish, "locked_auto") => "Bloqueado (Detectado Automáticamente)",
|
||||
(Language::Spanish, "cpu") => "Procesador (CPU)",
|
||||
(Language::Spanish, "gpu") => "Tarjeta Gráfica (GPU)",
|
||||
(Language::Spanish, "vram") => "Capacidad VRAM",
|
||||
(Language::Spanish, "vram_safe") => "Seguro contra fallos de textura",
|
||||
(Language::Spanish, "vram_warning") => "VRAM baja (Riesgo de errores)",
|
||||
(Language::Spanish, "vram_safe") => "Protegido contra corrupción de texturas",
|
||||
(Language::Spanish, "vram_warning") => "VRAM baja (Riesgo de corrupción)",
|
||||
(Language::Spanish, "ram") => "Memoria del Sistema (RAM)",
|
||||
(Language::Spanish, "ram_safe") => "RAM Suficiente",
|
||||
(Language::Spanish, "ram_warning") => "RAM Baja",
|
||||
(Language::Spanish, "refresh_rate") => "Frecuencia Monitor",
|
||||
(Language::Spanish, "game_location") => "📂 Ubicación del Juego",
|
||||
(Language::Spanish, "no_folder_selected") => "Aún no se ha seleccionado carpeta",
|
||||
(Language::Spanish, "manual_select") => "📁 Explorar",
|
||||
(Language::Spanish, "auto_scan") => "🔍 Escaneo Automático",
|
||||
(Language::Spanish, "ram_safe") => "Memoria suficiente",
|
||||
(Language::Spanish, "ram_warning") => "Memoria RAM baja",
|
||||
(Language::Spanish, "refresh_rate") => "Frecuencia del Monitor",
|
||||
(Language::Spanish, "game_location") => "Ubicación del Juego",
|
||||
(Language::Spanish, "no_folder_selected") => "No se ha seleccionado ninguna carpeta",
|
||||
(Language::Spanish, "manual_select") => "Explorar",
|
||||
(Language::Spanish, "auto_scan") => "Escaneo Automático",
|
||||
(Language::Spanish, "scanning") => "Escaneando...",
|
||||
(Language::Spanish, "game_valid") => "✅ Carpeta de God of War III verificada y completa",
|
||||
(Language::Spanish, "game_invalid") => "❌ ¡Carpeta incompleta o falta 'eboot.bin'!",
|
||||
(Language::Spanish, "patches_header") => "⚙️ Parches y Rendimiento",
|
||||
(Language::Spanish, "hz_patch") => "Parche de Sincronización Hz",
|
||||
(Language::Spanish, "hz_desc") => "Evita la aceleración 2x en pantallas de 144Hz+.",
|
||||
(Language::Spanish, "game_valid") => "Carpeta de God of War III verificada y completa",
|
||||
(Language::Spanish, "game_invalid") => "¡Carpeta incompleta o falta 'eboot.bin'!",
|
||||
(Language::Spanish, "patches_header") => "Parches y Rendimiento",
|
||||
(Language::Spanish, "hz_patch") => "Tasa de Refresco (Hz) y Sincronización FPS Dinámica",
|
||||
(Language::Spanish, "hz_desc") => "Bloquea el motor a los FPS objetivo preservando la física de 60Hz. Elimina fallos de aceleración en 60, 120, 144+ FPS.",
|
||||
(Language::Spanish, "target_fps") => "FPS Objetivo:",
|
||||
(Language::Spanish, "readback_patch") => "Corrección de Textura Amarilla (Instant Readbacks)",
|
||||
(Language::Spanish, "readback_desc") => "Evita que las texturas se vuelvan amarillas en escenas de nieve.",
|
||||
(Language::Spanish, "readback_patch") => "Corrección de Texturas y Tinte Amarillo (Instant Readbacks)",
|
||||
(Language::Spanish, "readback_desc") => "Fuerza lecturas lineales inmediatas, eliminando por completo el tinte amarillo y texturas corruptas en escenas de nieve o alto brillo.",
|
||||
(Language::Spanish, "async_readbacks") => "Lecturas Asíncronas (Anti-Tirones y Prevención de Caídas de FPS)",
|
||||
(Language::Spanish, "async_readbacks_desc") => "Desacopla la CPU de las esperas de la GPU para operaciones de textura. Elimina tirones y caídas repentinas de FPS.",
|
||||
(Language::Spanish, "skip_cinematics") => "Omitir Cinemáticas con el Botón 'X'",
|
||||
(Language::Spanish, "skip_cinematics_desc") => "Permite saltar cualquier cinemática o vídeo pulsando el botón X del mando.",
|
||||
(Language::Spanish, "gamemode_toggle") => "🎮 Feral GameMode (gamemoderun)",
|
||||
(Language::Spanish, "gamemode_toggle") => "Feral GameMode (gamemoderun)",
|
||||
(Language::Spanish, "gamemode_desc") => "Optimiza la CPU y GPU para máximo rendimiento en juegos.",
|
||||
(Language::Spanish, "save_apply") => "💾 Guardar y Aplicar",
|
||||
(Language::Spanish, "view_log") => "📜 Ver Archivo de Registro",
|
||||
(Language::Spanish, "save_apply") => "Guardar y Aplicar",
|
||||
(Language::Spanish, "view_log") => "Ver Archivo de Registro",
|
||||
(Language::Spanish, "play_ready") => "INICIAR JUEGO",
|
||||
(Language::Spanish, "play_running") => "⏳ JUEGO EN EJECUCIÓN...",
|
||||
(Language::Spanish, "play_disabled_warn") => "⚠️ Botón Deshabilitado: ¡Carpeta incompleta!",
|
||||
(Language::Spanish, "play_running") => "JUEGO EN EJECUCIÓN...",
|
||||
(Language::Spanish, "force_quit_btn") => "Forzar Cierre",
|
||||
(Language::Spanish, "force_quit_tooltip") => "Fuerza el cierre inmediato del proceso del juego si se congela o bloquea.",
|
||||
(Language::Spanish, "force_quit_status") => "El proceso del juego fue finalizado a la fuerza.",
|
||||
(Language::Spanish, "kill_leftover_btn") => "Matar Proceso",
|
||||
(Language::Spanish, "kill_leftover_tooltip") => "Elimina procesos colgados en segundo plano (gow3-probe).",
|
||||
(Language::Spanish, "play_disabled_warn") => "Botón Deshabilitado: ¡Carpeta incompleta!",
|
||||
(Language::Spanish, "play_disabled_help") => "Use 'Escaneo Automático' o 'Explorar' para seleccionar el juego.",
|
||||
(Language::Spanish, "play_subtitle") => "¡Todas las optimizaciones activas • A disfrutar!",
|
||||
(Language::Spanish, "first_launch_title") => "👋 Bienvenido - Configuración de God of War III",
|
||||
(Language::Spanish, "first_launch_title") => "Bienvenido - Configuración de God of War III",
|
||||
(Language::Spanish, "first_launch_prompt") => "Primer inicio. ¿Desea escanear y configurar automáticamente?",
|
||||
(Language::Spanish, "first_launch_auto") => "⚡ Escanear y Configurar",
|
||||
(Language::Spanish, "first_launch_manual") => "🔧 Configurar Manualmente",
|
||||
(Language::Spanish, "candidates_modal_title") => "🎮 Carpetas Encontradas",
|
||||
(Language::Spanish, "first_launch_auto") => "Escanear y Configurar",
|
||||
(Language::Spanish, "first_launch_manual") => "Configurar Manualmente",
|
||||
(Language::Spanish, "candidates_modal_title") => "Carpetas Encontradas",
|
||||
(Language::Spanish, "game_not_found_modal_title") => "Juego no encontrado",
|
||||
(Language::Spanish, "game_not_found_title") => "¡God of War III no está instalado!",
|
||||
(Language::Spanish, "game_not_found_desc") => "Se escanearon todas las unidades pero no se encontró una instalación válida de God of War III Remastered (CUSA01623 / CUSA01715).\n\n• Es posible que el juego aún no esté instalado en su sistema.\n• Si el juego está en una unidad externa o carpeta personalizada, use el botón 'Explorar' para seleccionar la carpeta que contiene 'eboot.bin'.",
|
||||
(Language::Spanish, "scan_not_found_inline") => "¡El juego no está instalado en el sistema o no se encontró la carpeta! Por favor instale el juego o use 'Explorar'.",
|
||||
(Language::Spanish, "select_this") => "Usar Esta Carpeta",
|
||||
(Language::Spanish, "close") => "Cerrar",
|
||||
(Language::Spanish, "diagnosis_title") => "🚨 Informe de Diagnóstico de Error",
|
||||
(Language::Spanish, "diagnosis_title") => "Informe de Diagnóstico de Error",
|
||||
(Language::Spanish, "diagnosis_desc") => "Descripción:",
|
||||
(Language::Spanish, "diagnosis_solution") => "Solución Recomendada:",
|
||||
(Language::Spanish, "diagnosis_log") => "📋 Registro Relevante (gow3_log.txt)",
|
||||
(Language::Spanish, "diagnosis_log") => "Registro Relevante (gow3_log.txt)",
|
||||
(Language::Spanish, "diagnosis_ack") => "Entendido, Cerrar",
|
||||
(Language::Spanish, "check_updates") => "🔄 Buscar Actualizaciones",
|
||||
(Language::Spanish, "checking_updates") => "⏳ Comprobando...",
|
||||
(Language::Spanish, "update_modal_title") => "🚀 Actualización de God of War III",
|
||||
(Language::Spanish, "up_to_date_title") => "✅ Sistema Actualizado",
|
||||
(Language::Spanish, "update_available_notice") => "¡Se encontró una nueva versión en GitHub (ScreamAngel/gow3_pclauncher)!",
|
||||
(Language::Spanish, "check_updates") => "Buscar Actualizaciones",
|
||||
(Language::Spanish, "checking_updates") => "Comprobando...",
|
||||
(Language::Spanish, "update_modal_title") => "Actualización de God of War III",
|
||||
(Language::Spanish, "up_to_date_title") => "Sistema Actualizado",
|
||||
(Language::Spanish, "update_available_notice") => "¡Se encontró una nueva versión en GitHub (ScreamAngel/gow3_launcher)!",
|
||||
(Language::Spanish, "up_to_date_notice") => "Sus archivos locales y el lanzador coinciden con la última versión.",
|
||||
(Language::Spanish, "update_backup_info") => "Al actualizar, el lanzador y los archivos de parches se actualizarán a la última versión. Se conservan todas sus configuraciones.",
|
||||
(Language::Spanish, "apply_update") => "⚡ Descargar e Integrar",
|
||||
(Language::Spanish, "force_sync") => "🔄 Re-sincronizar e Integrar",
|
||||
(Language::Spanish, "apply_update") => "Descargar e Integrar",
|
||||
(Language::Spanish, "force_sync") => "Re-sincronizar e Integrar",
|
||||
(Language::Spanish, "latest_commit") => "Último Commit:",
|
||||
(Language::Spanish, "commit_date") => "Fecha:",
|
||||
(Language::Spanish, "view_on_github") => "🌐 Ver en GitHub",
|
||||
(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!",
|
||||
// Spanish Config Tab
|
||||
(Language::Spanish, "display_header") => "Modo de Pantalla y Configuración",
|
||||
(Language::Spanish, "display_mode") => "Modo de pantalla:",
|
||||
(Language::Spanish, "display_borderless") => "Ventana sin bordes (Recomendado)",
|
||||
(Language::Spanish, "display_fullscreen") => "Pantalla completa",
|
||||
(Language::Spanish, "display_windowed") => "Modo ventana",
|
||||
(Language::Spanish, "display_desc") => "El modo sin bordes ofrece la experiencia más estable para varios monitores y Alt+Tab.",
|
||||
(Language::Spanish, "vsync") => "Sincronización vertical (V-Sync)",
|
||||
(Language::Spanish, "vsync_desc") => "Sincroniza la tasa de cuadros con la tasa de refresco del monitor para evitar cortes de imagen.",
|
||||
(Language::Spanish, "controller_header") => "Ajustes de Mando / Gamepad",
|
||||
(Language::Spanish, "bg_input") => "Entrada de mando en segundo plano",
|
||||
(Language::Spanish, "bg_input_desc") => "Permite entradas del mando incluso cuando la ventana del juego no está activa.",
|
||||
(Language::Spanish, "motion_controls") => "Controles de movimiento y giroscopio",
|
||||
(Language::Spanish, "motion_controls_desc") => "Activa el acelerómetro y giroscopio internos de mandos DualShock 4 / DualSense.",
|
||||
(Language::Spanish, "deadzone_left") => "Zona muerta palanca izquierda:",
|
||||
(Language::Spanish, "deadzone_right") => "Zona muerta palanca derecha:",
|
||||
(Language::Spanish, "deadzone_desc") => "Valores más bajos ofrecen mayor sensibilidad; auméntelo si su palanca sufre de deriva (drift).",
|
||||
(Language::Spanish, "keyboard_header") => "Asignación de Teclas (Keyboard Bindings)",
|
||||
(Language::Spanish, "keyboard_desc") => "Haz clic en cualquier botón y pulsa la nueva tecla. Pulsa ESC para cancelar.",
|
||||
(Language::Spanish, "preset_label") => "Ajuste rápido:",
|
||||
(Language::Spanish, "preset_standard") => "Estándar WASD + CVBN (Recomendado)",
|
||||
(Language::Spanish, "preset_numpad") => "WASD + Teclado Numérico (8462)",
|
||||
(Language::Spanish, "reset_defaults") => "Restablecer por defecto",
|
||||
(Language::Spanish, "press_any_key") => "[ Presiona una tecla... / ESC ]",
|
||||
(Language::Spanish, "sec_face_buttons") => "Botones de acción (Ataques y Salto)",
|
||||
(Language::Spanish, "sec_triggers") => "Gatillos y botones laterales (L1/R1/L2/R2/L3/R3)",
|
||||
(Language::Spanish, "sec_dpad_menu") => "Cruceta y Menú (D-Pad / Opciones / Touchpad)",
|
||||
(Language::Spanish, "sec_analog_sticks") => "Palancas analógicas (Movimiento y Cámara)",
|
||||
(Language::Spanish, "act_triangle") => "Triángulo (Ataque pesado)",
|
||||
(Language::Spanish, "act_square") => "Cuadrado (Ataque ligero)",
|
||||
(Language::Spanish, "act_circle") => "Círculo (Agarrar)",
|
||||
(Language::Spanish, "act_cross") => "Cruz (Saltar)",
|
||||
(Language::Spanish, "act_l1") => "L1 (Bloqueo / Guardia)",
|
||||
(Language::Spanish, "act_r1") => "R1 (Esquivar / Interactuar)",
|
||||
(Language::Spanish, "act_l2") => "L2 (Magia)",
|
||||
(Language::Spanish, "act_r2") => "R2 (Objeto)",
|
||||
(Language::Spanish, "act_l3") => "L3 (Click palanca izquierda)",
|
||||
(Language::Spanish, "act_r3") => "R3 (Click palanca derecha)",
|
||||
(Language::Spanish, "act_options") => "Options (Pausa / Menú)",
|
||||
(Language::Spanish, "act_touchpad") => "Touchpad (Mapa / Menú)",
|
||||
(Language::Spanish, "act_dpad_up") => "Cruceta Arriba",
|
||||
(Language::Spanish, "act_dpad_down") => "Cruceta Abajo",
|
||||
(Language::Spanish, "act_dpad_left") => "Cruceta Izquierda",
|
||||
(Language::Spanish, "act_dpad_right") => "Cruceta Derecha",
|
||||
(Language::Spanish, "act_move_up") => "Palanca Izq: Adelante",
|
||||
(Language::Spanish, "act_move_down") => "Palanca Izq: Atrás",
|
||||
(Language::Spanish, "act_move_left") => "Palanca Izq: Izquierda",
|
||||
(Language::Spanish, "act_move_right") => "Palanca Izq: Derecha",
|
||||
(Language::Spanish, "act_cam_up") => "Palanca Der: Arriba",
|
||||
(Language::Spanish, "act_cam_down") => "Palanca Der: Abajo",
|
||||
(Language::Spanish, "act_cam_left") => "Palanca Der: Izquierda",
|
||||
(Language::Spanish, "act_cam_right") => "Palanca Der: Derecha",
|
||||
|
||||
_ => "",
|
||||
}
|
||||
|
||||
+61
-27
@@ -13,12 +13,24 @@ use eframe::egui;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
// Determine base_dir: If running inside 'launcher' folder, go up to parent, else use current dir
|
||||
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
let base_dir = if current_dir.ends_with("launcher") {
|
||||
current_dir.parent().unwrap_or(¤t_dir).to_path_buf()
|
||||
// Determine base_dir:
|
||||
// 1. If running as an AppImage, use the directory where the .AppImage file is located
|
||||
// 2. If running as standalone executable, use the folder containing the binary
|
||||
// 3. Fallback to current working directory
|
||||
let base_dir = if let Ok(appimage) = std::env::var("APPIMAGE") {
|
||||
PathBuf::from(appimage).parent().unwrap_or(&PathBuf::from(".")).to_path_buf()
|
||||
} else if let Ok(exe_path) = std::env::current_exe() {
|
||||
let exe_dir = exe_path.parent().unwrap_or(&PathBuf::from(".")).to_path_buf();
|
||||
let cur = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
|
||||
if cur.ends_with("launcher") {
|
||||
cur.parent().unwrap_or(&cur).to_path_buf()
|
||||
} else if exe_dir.ends_with("target/release") || exe_dir.ends_with("target/debug") {
|
||||
cur
|
||||
} else {
|
||||
exe_dir
|
||||
}
|
||||
} else {
|
||||
current_dir
|
||||
std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
|
||||
};
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
@@ -26,15 +38,8 @@ fn main() -> eframe::Result<()> {
|
||||
let config = config::LauncherConfig::load(&base_dir);
|
||||
let gamemode_avail = system::is_gamemode_available();
|
||||
let use_gamemode = gamemode_avail && config.use_gamemode;
|
||||
let _ = tuner::apply_settings(
|
||||
&base_dir,
|
||||
config.target_fps,
|
||||
config.patch_delta_time,
|
||||
config.patch_readbacks,
|
||||
config.vram_gc_budget_mb,
|
||||
config.dmem_mb,
|
||||
);
|
||||
match runner::launch_gow3(&base_dir, &config, use_gamemode) {
|
||||
let _ = tuner::apply_settings(&base_dir, &config);
|
||||
match runner::launch_gow3(&base_dir, &config, use_gamemode, None) {
|
||||
Ok(code) => std::process::exit(code),
|
||||
Err(e) => {
|
||||
eprintln!("Error launching God of War III: {}", e);
|
||||
@@ -49,19 +54,8 @@ fn main() -> eframe::Result<()> {
|
||||
.with_min_inner_size([650.0, 550.0])
|
||||
.with_resizable(true);
|
||||
|
||||
// Try load window icon
|
||||
let icon_path = base_dir.join("gow3.png");
|
||||
if icon_path.is_file() {
|
||||
if let Ok(img) = image::open(&icon_path) {
|
||||
let width = img.width();
|
||||
let height = img.height();
|
||||
let rgba = img.to_rgba8().into_raw();
|
||||
viewport = viewport.with_icon(egui::IconData {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
}
|
||||
if let Some(icon) = load_window_icon(&base_dir) {
|
||||
viewport = viewport.with_icon(icon);
|
||||
}
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
@@ -79,3 +73,43 @@ fn main() -> eframe::Result<()> {
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_window_icon(base_dir: &std::path::Path) -> Option<egui::IconData> {
|
||||
// 1. Try to load gow3.png from base_dir, current_dir, exe_dir, or fallback to embedded asset
|
||||
let img = if let Ok(img) = image::open(base_dir.join("gow3.png")) {
|
||||
Some(img)
|
||||
} else if let Ok(cur) = std::env::current_dir() {
|
||||
if let Ok(img) = image::open(cur.join("gow3.png")) {
|
||||
Some(img)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.or_else(|| {
|
||||
if let Ok(exe_p) = std::env::current_exe() {
|
||||
if let Some(parent) = exe_p.parent() {
|
||||
if let Ok(img) = image::open(parent.join("gow3.png")) {
|
||||
return Some(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
image::load_from_memory(include_bytes!("../../gow3.png")).ok()
|
||||
})?;
|
||||
|
||||
// Downscale to 256x256 if larger:
|
||||
// Windows Win32 User32 CreateIcon fails on dimensions > 256, causing taskbar icon to revert to generic.
|
||||
// Scaling to 256x256 ensures smooth High-DPI rendering and 100% Win32 / Wayland / X11 taskbar compatibility.
|
||||
let (w, h) = (img.width(), img.height());
|
||||
let resized = if w > 256 || h > 256 || w != h {
|
||||
img.resize_exact(256, 256, image::imageops::FilterType::Lanczos3)
|
||||
} else {
|
||||
img
|
||||
};
|
||||
|
||||
let width = resized.width();
|
||||
let height = resized.height();
|
||||
let rgba = resized.to_rgba8().into_raw();
|
||||
Some(egui::IconData { rgba, width, height })
|
||||
}
|
||||
+657
-24
@@ -1,6 +1,8 @@
|
||||
use std::fs::{self, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::LauncherConfig;
|
||||
|
||||
@@ -71,15 +73,475 @@ 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())
|
||||
}
|
||||
}
|
||||
|
||||
/// Force terminates any running game processes (probe / wine / gamemoderun) on the system.
|
||||
pub fn kill_game_processes(pid: u32) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
if pid > 0 {
|
||||
let _ = Command::new("taskkill")
|
||||
.args(["/F", "/T", "/PID", &pid.to_string()])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output();
|
||||
}
|
||||
let _ = Command::new("taskkill")
|
||||
.args(["/F", "/T", "/IM", "gow3-probe.exe"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output();
|
||||
let _ = Command::new("taskkill")
|
||||
.args(["/F", "/T", "/IM", "gow3.exe"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let my_pid = std::process::id();
|
||||
if pid > 0 && pid != my_pid {
|
||||
let _ = Command::new("kill")
|
||||
.args(["-9", &pid.to_string()])
|
||||
.output();
|
||||
let _ = Command::new("pkill")
|
||||
.args(["-9", "-P", &pid.to_string()])
|
||||
.output();
|
||||
}
|
||||
let _ = Command::new("pkill")
|
||||
.args(["-9", "-x", "gow3-probe"])
|
||||
.output();
|
||||
let _ = Command::new("pkill")
|
||||
.args(["-9", "-f", "gow3-probe"])
|
||||
.output();
|
||||
let _ = Command::new("pkill")
|
||||
.args(["-9", "-f", "gow3-probe.exe"])
|
||||
.output();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn adjust_windows_windowed_mode(target_pid: u32) {
|
||||
std::thread::spawn(move || {
|
||||
use std::ffi::c_void;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[repr(C)]
|
||||
struct RECT {
|
||||
left: i32,
|
||||
top: i32,
|
||||
right: i32,
|
||||
bottom: i32,
|
||||
}
|
||||
|
||||
struct EnumContext {
|
||||
pid: u32,
|
||||
hwnd: *mut c_void,
|
||||
}
|
||||
|
||||
unsafe extern "system" {
|
||||
fn GetWindowThreadProcessId(hwnd: *mut c_void, lpdwProcessId: *mut u32) -> u32;
|
||||
fn EnumWindows(lpEnumFunc: unsafe extern "system" fn(*mut c_void, isize) -> i32, lParam: isize) -> i32;
|
||||
fn IsWindowVisible(hwnd: *mut c_void) -> i32;
|
||||
fn GetWindowLongPtrA(hWnd: *mut c_void, nIndex: i32) -> isize;
|
||||
fn SetWindowLongPtrA(hWnd: *mut c_void, nIndex: i32, dwNewLong: isize) -> isize;
|
||||
fn SetWindowPos(hWnd: *mut c_void, hWndInsertAfter: *mut c_void, X: i32, Y: i32, cx: i32, cy: i32, uFlags: u32) -> i32;
|
||||
fn ShowWindow(hWnd: *mut c_void, nCmdShow: i32) -> i32;
|
||||
fn SystemParametersInfoA(uiAction: u32, uiParam: u32, pvParam: *mut c_void, fWinIni: u32) -> i32;
|
||||
fn GetSystemMetrics(nIndex: i32) -> i32;
|
||||
}
|
||||
|
||||
unsafe extern "system" fn enum_proc(hwnd: *mut c_void, lparam: isize) -> i32 {
|
||||
unsafe {
|
||||
let ctx = &mut *(lparam as *mut EnumContext);
|
||||
let mut proc_id = 0u32;
|
||||
GetWindowThreadProcessId(hwnd, &mut proc_id);
|
||||
if proc_id == ctx.pid && IsWindowVisible(hwnd) != 0 {
|
||||
ctx.hwnd = hwnd;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let mut target_hwnd: *mut c_void = std::ptr::null_mut();
|
||||
|
||||
while start.elapsed() < Duration::from_secs(15) {
|
||||
let mut ctx = EnumContext {
|
||||
pid: target_pid,
|
||||
hwnd: std::ptr::null_mut(),
|
||||
};
|
||||
unsafe {
|
||||
EnumWindows(enum_proc, &mut ctx as *mut _ as isize);
|
||||
}
|
||||
if !ctx.hwnd.is_null() {
|
||||
target_hwnd = ctx.hwnd;
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(150));
|
||||
}
|
||||
|
||||
if target_hwnd.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(600));
|
||||
|
||||
unsafe {
|
||||
let mut work_area: RECT = std::mem::zeroed();
|
||||
const SPI_GETWORKAREA: u32 = 0x0030;
|
||||
let ok = SystemParametersInfoA(SPI_GETWORKAREA, 0, &mut work_area as *mut _ as *mut c_void, 0);
|
||||
|
||||
let screen_w = if ok != 0 { work_area.right - work_area.left } else { GetSystemMetrics(0) };
|
||||
let screen_h = if ok != 0 { work_area.bottom - work_area.top } else { GetSystemMetrics(1) };
|
||||
|
||||
let (win_w, win_h) = if screen_w >= 2560 && screen_h >= 1440 {
|
||||
(1920, 1080)
|
||||
} else if screen_w > 1680 && screen_h > 940 {
|
||||
(1600, 900)
|
||||
} else if screen_w > 1360 && screen_h > 760 {
|
||||
(1280, 720)
|
||||
} else {
|
||||
((screen_w * 85 / 100).max(800), (screen_h * 85 / 100).max(600))
|
||||
};
|
||||
|
||||
let pos_x = work_area.left + ((screen_w - win_w) / 2).max(10);
|
||||
let pos_y = work_area.top + ((screen_h - win_h) / 2).max(10);
|
||||
|
||||
const GWL_STYLE: i32 = -16;
|
||||
const WS_OVERLAPPEDWINDOW: isize = 0x00CF0000;
|
||||
const WS_VISIBLE: isize = 0x10000000;
|
||||
let current_style = GetWindowLongPtrA(target_hwnd, GWL_STYLE);
|
||||
SetWindowLongPtrA(target_hwnd, GWL_STYLE, current_style | WS_OVERLAPPEDWINDOW | WS_VISIBLE);
|
||||
|
||||
const SWP_FRAMECHANGED: u32 = 0x0020;
|
||||
const SWP_SHOWWINDOW: u32 = 0x0040;
|
||||
const SWP_NOZORDER: u32 = 0x0004;
|
||||
const SW_RESTORE: i32 = 9;
|
||||
|
||||
ShowWindow(target_hwnd, SW_RESTORE);
|
||||
SetWindowPos(
|
||||
target_hwnd,
|
||||
std::ptr::null_mut(),
|
||||
pos_x,
|
||||
pos_y,
|
||||
win_w,
|
||||
win_h,
|
||||
SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Executes the full God of War III preparation and probe execution natively from Rust.
|
||||
@@ -87,23 +549,14 @@ pub fn launch_gow3(
|
||||
base_dir: &Path,
|
||||
config: &LauncherConfig,
|
||||
use_gamemode: bool,
|
||||
running_pid: Option<Arc<AtomicU32>>,
|
||||
) -> Result<i32, String> {
|
||||
let game_dir = Path::new(&config.game_dir);
|
||||
if !game_dir.join("eboot.bin").is_file() {
|
||||
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,14 +580,20 @@ 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 mods_dir = base_dir.join("mods");
|
||||
if !mods_dir.exists() {
|
||||
let _ = std::fs::create_dir_all(&mods_dir);
|
||||
}
|
||||
let _ = Command::new(&python)
|
||||
.arg(scripts_path.join("mods.py"))
|
||||
.arg(game_dir)
|
||||
.arg("--out")
|
||||
.arg(&out_dir)
|
||||
.arg("--mods-dir")
|
||||
.arg(&mods_dir)
|
||||
.arg("--enabled")
|
||||
.arg("1")
|
||||
.env("PYTHONPATH", &scripts_path)
|
||||
@@ -238,15 +697,38 @@ pub fn launch_gow3(
|
||||
|
||||
// 7. Run patches.py
|
||||
let mut patch_list = Vec::new();
|
||||
if config.patch_readbacks {
|
||||
let res_patch = match config.render_resolution.as_str() {
|
||||
"480p" => Some("Resolution Patch - 480p"),
|
||||
"720p" => Some("Resolution Patch - 720p"),
|
||||
"800p" => Some("Resolution Patch - 800p"),
|
||||
"1440p" => Some("Resolution Patch - 1440p"),
|
||||
"1800p" => Some("Resolution Patch - 1800p"),
|
||||
"4K" => Some("Resolution Patch - 4K"),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(p) = res_patch {
|
||||
patch_list.push(p.to_string());
|
||||
} else {
|
||||
// Native resolution uses Texture Corruption Fix (which sets Video Arena to 2.25 GiB)
|
||||
patch_list.push("Bug Fix - Texture Corruption Fix".to_string());
|
||||
}
|
||||
|
||||
patch_list.push("Bug Fix - Startup Black Screen".to_string());
|
||||
patch_list.push("Bug Fix - Intro Crash Fix".to_string());
|
||||
|
||||
if config.skip_cinematics {
|
||||
patch_list.push("Skip Any Video With X Button".to_string());
|
||||
}
|
||||
if config.target_fps != 60 && config.patch_delta_time {
|
||||
if config.patch_delta_time {
|
||||
patch_list.push(format!("Frame Rate Patch - {} FPS", config.target_fps));
|
||||
}
|
||||
if config.disable_motion_blur {
|
||||
patch_list.push("Graphics - Disable Motion Blur".to_string());
|
||||
}
|
||||
if config.disable_dof {
|
||||
patch_list.push("Graphics - Disable Depth of Field".to_string());
|
||||
}
|
||||
let patches_str = patch_list.join(";");
|
||||
|
||||
let _ = Command::new(&python)
|
||||
@@ -299,15 +781,112 @@ pub fn launch_gow3(
|
||||
cmd.env(k, v);
|
||||
}
|
||||
|
||||
// Vulkan Layer Crash Prevention:
|
||||
// If third-party broken layers (such as lsfg-vk when missing its shader DLL) are registered as
|
||||
// implicit Vulkan layers on the system, vkCreateSwapchainKHR will crash with ErrorInitializationFailed.
|
||||
// By default, prevent lsfg from hooking and crashing GOW3 unless explicitly overridden.
|
||||
if std::env::var("VK_LOADER_LAYERS_DISABLE").is_err() {
|
||||
cmd.env("VK_LOADER_LAYERS_DISABLE", "*lsfg*");
|
||||
}
|
||||
|
||||
// Hybrid GPU (PRIME Offload) Automatic Handling on Linux:
|
||||
// When running on dual GPU laptops (Intel iGPU + NVIDIA or AMD 2GB VRAM), Linux defaults to the
|
||||
// integrated GPU (which on older laptops like Haswell lacks modern Vulkan features and fails).
|
||||
// Automatically route rendering to the discrete GPU if detected and not overridden.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if crate::system::detect_has_discrete_nvidia() {
|
||||
if std::env::var("__NV_PRIME_RENDER_OFFLOAD").is_err() {
|
||||
cmd.env("__NV_PRIME_RENDER_OFFLOAD", "1");
|
||||
}
|
||||
if std::env::var("__GLX_VENDOR_LIBRARY_NAME").is_err() {
|
||||
cmd.env("__GLX_VENDOR_LIBRARY_NAME", "nvidia");
|
||||
}
|
||||
if std::env::var("__VK_LAYER_NV_optimus").is_err() {
|
||||
cmd.env("__VK_LAYER_NV_optimus", "NVIDIA_only");
|
||||
}
|
||||
} else if crate::system::detect_has_discrete_amd() {
|
||||
if std::env::var("DRI_PRIME").is_err() {
|
||||
cmd.env("DRI_PRIME", "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply launcher optimization overrides
|
||||
cmd.env("GOW3_PREBUILT", "1");
|
||||
cmd.env("GOW3_FORCE_PATCHES", "1");
|
||||
cmd.env("GOW3_STREAM_MEMORY", "cached");
|
||||
cmd.env("GOW3_FULL_BARRIERS", "1");
|
||||
// Resolution & Upscaler configuration:
|
||||
// When native resolution is selected, GOW3_RENDER_RES and internal dimensions MUST NOT be set
|
||||
// in order to avoid an illegal 1:1 upscale pass and device loss ("TAA: remove GOW3_RENDER_RES to use native-resolution TAA").
|
||||
if let Some(res_env) = config.get_render_res_env_value() {
|
||||
let (internal_w, internal_h) = config.get_resolution_dimensions();
|
||||
cmd.env("GOW3_RENDER_RES", &res_env);
|
||||
cmd.env("GOW3_INTERNAL_WIDTH", internal_w.to_string());
|
||||
cmd.env("GOW3_INTERNAL_HEIGHT", internal_h.to_string());
|
||||
} else {
|
||||
cmd.env_remove("GOW3_RENDER_RES");
|
||||
cmd.env_remove("GOW3_INTERNAL_WIDTH");
|
||||
cmd.env_remove("GOW3_INTERNAL_HEIGHT");
|
||||
}
|
||||
|
||||
// Upscaler configuration:
|
||||
// libgow3gpu explicitly tests `strcmp(getenv("GOW3_UPSCALER"), "none") == 0` to disable the upscaler.
|
||||
// Setting "off" or leaving it unset incorrectly enables the temporal upscaler.
|
||||
// Upscaler is only active when enabled AND resolution is scaled.
|
||||
if config.fsr_enabled && config.render_resolution != "native" {
|
||||
cmd.env("GOW3_UPSCALER", "fsr");
|
||||
cmd.env("GOW3_FSR1", "1");
|
||||
cmd.env("GOW3_RCAS", "1");
|
||||
cmd.env("GOW3_FSR_SHARPNESS", "0.8");
|
||||
} else {
|
||||
cmd.env("GOW3_UPSCALER", "none");
|
||||
cmd.env("GOW3_FSR1", "0");
|
||||
cmd.env("GOW3_RCAS", "0");
|
||||
cmd.env("GOW3_FSR_SHARPNESS", "0.0");
|
||||
}
|
||||
cmd.env("GOW3_OBJECT_MOTION", if config.disable_motion_blur { "0" } else { "1" });
|
||||
cmd.env("GOW3_READBACKS", if config.patch_readbacks { "1" } else { "0" });
|
||||
cmd.env("GOW3_READBACK_LINEAR", if config.patch_readbacks { "1" } else { "0" });
|
||||
cmd.env("GOW3_FORCE_READBACK", if config.patch_readbacks { "1" } else { "0" });
|
||||
cmd.env("GOW3_GC_BUDGET_MB", config.vram_gc_budget_mb.to_string());
|
||||
|
||||
// Asynchronous & Stale Readbacks:
|
||||
// Decouples CPU from GPU fences. CPU reads use the previous frame's copy and CPU writes proceed
|
||||
// without stalling the render thread. Completely prevents sudden FPS drops and micro-stutters.
|
||||
cmd.env("GOW3_DEFERRED_READBACK", if config.async_readbacks { "1" } else { "0" });
|
||||
cmd.env("GOW3_STALE_READBACK", if config.async_readbacks { "1" } else { "0" });
|
||||
|
||||
// CPU Thread Tuning:
|
||||
// Scale draw preparation workers and copy threads to CPU topology to prevent thread contention.
|
||||
let cpu_threads = if config.cached_hardware.cpu_threads > 0 {
|
||||
config.cached_hardware.cpu_threads
|
||||
} else {
|
||||
crate::system::detect_cpu_threads()
|
||||
};
|
||||
let (prep_workers, copy_threads) = if cpu_threads <= 4 {
|
||||
(2, 1)
|
||||
} else if cpu_threads <= 8 {
|
||||
(4, 2)
|
||||
} else {
|
||||
(6, 4)
|
||||
};
|
||||
cmd.env("GOW3_PREP_WORKERS", prep_workers.to_string());
|
||||
cmd.env("GOW3_COPY_THREADS", copy_threads.to_string());
|
||||
|
||||
cmd.env("GOW3_INTERP_INT_FIX", "1");
|
||||
cmd.env("GOW3_DIRECT_MEMORY_ACCESS", "1");
|
||||
cmd.env("GOW3_PIPELINE_CACHE", "1");
|
||||
cmd.env("GOW3_PARALLEL_WARMUP", "1");
|
||||
cmd.env("GOW3_BACKGROUND_WARMUP", "1");
|
||||
cmd.env("GOW3_VK_RECORD_THREAD", "1");
|
||||
cmd.env("GOW3_PRESENT_THREAD", "1");
|
||||
cmd.env("GOW3_ASYNC_FENCES", "1");
|
||||
cmd.env("GOW3_ASYNC_SHADERS", "1");
|
||||
cmd.env("GOW3_FPS_LIMIT", config.target_fps.to_string());
|
||||
|
||||
let effective_budget = config.get_effective_vram_budget();
|
||||
cmd.env("GOW3_GC_BUDGET_MB", effective_budget.to_string());
|
||||
cmd.env("GOW3_DMEM_POOL_MB", config.dmem_mb.to_string());
|
||||
cmd.env("GOW3_DMEM_MB", config.dmem_mb.to_string());
|
||||
cmd.env("GOW3_DEFAULT_ENGINE_FPS", config.target_fps.to_string());
|
||||
@@ -316,15 +895,69 @@ pub fn launch_gow3(
|
||||
cmd.env("GOW3_PATCHES_ONLY", "1");
|
||||
cmd.env("GOW3_CONFIG", &config_ini);
|
||||
cmd.env("GOW3_GAME_DIR", game_dir);
|
||||
cmd.env(
|
||||
"GOW3_FULLSCREEN",
|
||||
if config.display_mode != crate::config::DisplayMode::Windowed {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
|
||||
// Apply Controller and Keyboard mapping overrides
|
||||
let key_map = config.build_gow3_key_map();
|
||||
let pad_map = config.build_gow3_pad_map();
|
||||
cmd.env("GOW3_KEY_MAP", &key_map);
|
||||
cmd.env("GOW3_PAD_MAP", pad_map);
|
||||
cmd.env(
|
||||
"SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS",
|
||||
if config.background_controller_input {
|
||||
"1"
|
||||
} else {
|
||||
"0"
|
||||
},
|
||||
);
|
||||
if config.deadzone_left > 0 {
|
||||
let dz = (config.deadzone_left as f32 / 100.0 * 32767.0) as u32;
|
||||
cmd.env("SDL_LINUX_JOYSTICK_DEADZONE", dz.to_string());
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
let mut child = cmd.spawn().map_err(|e| format!("Oyun başlatılamadı: {}", e))?;
|
||||
let exit_status = child.wait().map_err(|e| format!("Oyun beklenirken hata oluştu: {}", e))?;
|
||||
if let Some(ref pid_holder) = running_pid {
|
||||
pid_holder.store(child.id(), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
Ok(exit_status.code().unwrap_or(-1))
|
||||
#[cfg(target_os = "windows")]
|
||||
if config.display_mode == crate::config::DisplayMode::Windowed {
|
||||
adjust_windows_windowed_mode(child.id());
|
||||
}
|
||||
|
||||
let exit_status = child.wait().map_err(|e| format!("Oyun beklenirken hata oluştu: {}", e));
|
||||
|
||||
if let Some(ref pid_holder) = running_pid {
|
||||
pid_holder.store(0, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let status = exit_status?;
|
||||
Ok(status.code().unwrap_or(-1))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -333,7 +966,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");
|
||||
}
|
||||
|
||||
|
||||
+23
-9
@@ -15,10 +15,13 @@ pub fn is_valid_game_dir(path: &Path) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
let eboot = path.join("eboot.bin");
|
||||
if !eboot.is_file() {
|
||||
let eboot = if path.join("eboot.bin").is_file() {
|
||||
path.join("eboot.bin")
|
||||
} else if path.join("EBOOT.BIN").is_file() {
|
||||
path.join("EBOOT.BIN")
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 1. Verify eboot.bin size (genuine God of War III eboot is > 1MB, typically ~4.5MB)
|
||||
if let Ok(meta) = fs::metadata(&eboot) {
|
||||
@@ -30,10 +33,17 @@ pub fn is_valid_game_dir(path: &Path) -> bool {
|
||||
}
|
||||
|
||||
// 2. Verify sce_sys/param.sfo exists and has data
|
||||
let sfo = path.join("sce_sys/param.sfo");
|
||||
if !sfo.is_file() {
|
||||
let sfo = if path.join("sce_sys/param.sfo").is_file() {
|
||||
path.join("sce_sys/param.sfo")
|
||||
} else if path.join("sce_sys/PARAM.SFO").is_file() {
|
||||
path.join("sce_sys/PARAM.SFO")
|
||||
} else if path.join("SCE_SYS/param.sfo").is_file() {
|
||||
path.join("SCE_SYS/param.sfo")
|
||||
} else if path.join("SCE_SYS/PARAM.SFO").is_file() {
|
||||
path.join("SCE_SYS/PARAM.SFO")
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if let Ok(meta) = fs::metadata(&sfo) {
|
||||
if meta.len() < 100 {
|
||||
return false;
|
||||
@@ -43,13 +53,13 @@ pub fn is_valid_game_dir(path: &Path) -> bool {
|
||||
}
|
||||
|
||||
// 3. Verify at least one essential game directory exists (sce_module, EXEC, or language packs)
|
||||
let has_modules = path.join("sce_module").is_dir();
|
||||
let has_exec = path.join("EXEC").is_dir();
|
||||
let has_modules = path.join("sce_module").is_dir() || path.join("SCE_MODULE").is_dir();
|
||||
let has_exec = path.join("EXEC").is_dir() || path.join("exec").is_dir();
|
||||
let has_lang = [
|
||||
"ENGLISH", "DUTCH", "FRENCH", "GERMAN", "ITALIAN", "POLISH", "PORTUGUESE", "RUSSIAN", "SPANISH", "data",
|
||||
]
|
||||
.iter()
|
||||
.any(|lang| path.join(lang).is_dir());
|
||||
.any(|lang| path.join(lang).is_dir() || path.join(lang.to_lowercase()).is_dir());
|
||||
|
||||
has_modules || has_exec || has_lang
|
||||
}
|
||||
@@ -58,6 +68,10 @@ pub fn scan_for_game() -> Vec<GameCandidate> {
|
||||
let mut candidates = Vec::new();
|
||||
let mut scan_roots = Vec::new();
|
||||
|
||||
if let Ok(cur) = std::env::current_dir() {
|
||||
scan_roots.push(cur);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Ok(userprofile) = std::env::var("USERPROFILE") {
|
||||
|
||||
+356
-2
@@ -3,19 +3,135 @@ use std::fs;
|
||||
use std::process::Command;
|
||||
use crate::config::CachedHardware;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CpuGrade {
|
||||
Weak, // Zayıf / Darboğaz Riski (Kırmızı)
|
||||
Medium, // Orta Seviye (40-60 FPS) (Sarı/Amber)
|
||||
Good, // Güçlü (60 FPS İdeal) (Açık Yeşil)
|
||||
Ultra, // Üst Seviye (60-144 FPS) (Parlak Yeşil)
|
||||
}
|
||||
|
||||
impl CpuGrade {
|
||||
pub fn badge_text(&self, lang: &str) -> &'static str {
|
||||
match self {
|
||||
CpuGrade::Weak => match lang {
|
||||
"tr" => "• Zayıf / Darboğaz Riski",
|
||||
"de" => "• Schwach / Bottleneck-Risiko",
|
||||
"es" => "• Débil / Riesgo Cuello de Botella",
|
||||
_ => "• Weak / Bottleneck Risk",
|
||||
},
|
||||
CpuGrade::Medium => match lang {
|
||||
"tr" => "• Orta Seviye (40-60 FPS)",
|
||||
"de" => "• Mittelstufe (40-60 FPS)",
|
||||
"es" => "• Nivel Medio (40-60 FPS)",
|
||||
_ => "• Mid-Range (40-60 FPS)",
|
||||
},
|
||||
CpuGrade::Good => match lang {
|
||||
"tr" => "• Güçlü (60 FPS İdeal)",
|
||||
"de" => "• Stark (60 FPS Ideal)",
|
||||
"es" => "• Fuerte (Ideal 60 FPS)",
|
||||
_ => "• Strong (60 FPS Ideal)",
|
||||
},
|
||||
CpuGrade::Ultra => match lang {
|
||||
"tr" => "• Üst Seviye (Kusursuz)",
|
||||
"de" => "• Spitzenklasse (Perfekt)",
|
||||
"es" => "• Gama Alta (Perfecto)",
|
||||
_ => "• High-End (Flawless)",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rgb(&self) -> (u8, u8, u8) {
|
||||
match self {
|
||||
CpuGrade::Weak => (239, 83, 80),
|
||||
CpuGrade::Medium => (255, 213, 79),
|
||||
CpuGrade::Good => (130, 225, 140),
|
||||
CpuGrade::Ultra => (0, 230, 118),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_cpu(name: &str, cores: usize, threads: usize) -> CpuGrade {
|
||||
let lower = name.to_lowercase();
|
||||
|
||||
// 1. Obsolete or severely underpowered architectures
|
||||
if lower.contains("celeron")
|
||||
|| lower.contains("pentium")
|
||||
|| lower.contains("core 2")
|
||||
|| lower.contains("core(tm)2")
|
||||
|| lower.contains("athlon")
|
||||
|| lower.contains("sempron")
|
||||
|| lower.contains("phenom")
|
||||
|| lower.contains("fx-")
|
||||
|| lower.contains("a10-")
|
||||
|| lower.contains("a8-")
|
||||
|| lower.contains("a6-")
|
||||
|| lower.contains("a4-")
|
||||
{
|
||||
return CpuGrade::Weak;
|
||||
}
|
||||
|
||||
// 2. Thread count constraints
|
||||
if threads <= 4 {
|
||||
// Any 2-core or 4-core/4-thread CPU (e.g. i5-4460, i5-6500, older i3) struggles with PS4 emulation threads
|
||||
return CpuGrade::Weak;
|
||||
}
|
||||
|
||||
// 3. Medium tier (4 cores / 8 threads or 6 cores / 6 threads)
|
||||
// Or older 4-core HEDT / 6-core Zen 1
|
||||
if threads <= 8 || cores <= 4 {
|
||||
return CpuGrade::Medium;
|
||||
}
|
||||
|
||||
// 4. Good vs Ultra
|
||||
if threads >= 16 || cores >= 8 {
|
||||
CpuGrade::Ultra
|
||||
} else if threads >= 12 || cores >= 6 {
|
||||
CpuGrade::Good
|
||||
} else {
|
||||
CpuGrade::Medium
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clean_cpu_name(name: &str) -> String {
|
||||
let mut cleaned = name
|
||||
.replace("(R)", "")
|
||||
.replace("(TM)", "")
|
||||
.replace(" CPU", "");
|
||||
|
||||
if let Some(pos) = cleaned.find("-Core") {
|
||||
if let Some(start) = cleaned[..pos].rfind(' ') {
|
||||
cleaned.drain(start..);
|
||||
}
|
||||
}
|
||||
|
||||
let parts: Vec<&str> = cleaned.split_whitespace().collect();
|
||||
parts.join(" ")
|
||||
}
|
||||
|
||||
pub fn detect_hardware() -> CachedHardware {
|
||||
let (gpu_name, vram_mb) = detect_gpu();
|
||||
let ram_mb = detect_ram();
|
||||
let refresh_rate_hz = detect_refresh_rate();
|
||||
let (cpu_name, cpu_cores, cpu_threads) = detect_cpu();
|
||||
|
||||
CachedHardware {
|
||||
gpu_name,
|
||||
vram_mb,
|
||||
ram_mb,
|
||||
refresh_rate_hz,
|
||||
cpu_name,
|
||||
cpu_cores,
|
||||
cpu_threads,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_cpu_threads() -> usize {
|
||||
std::thread::available_parallelism()
|
||||
.map(|n| n.get())
|
||||
.unwrap_or(4)
|
||||
}
|
||||
|
||||
pub fn is_gamemode_available() -> bool {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -76,6 +192,47 @@ fn detect_gpu() -> (String, u64) {
|
||||
("Bilinmeyen GPU (Genel)".to_string(), 4096)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn detect_has_discrete_nvidia() -> bool {
|
||||
if let Ok(output) = Command::new("lspci").output() {
|
||||
if output.status.success() {
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
for line in text.lines() {
|
||||
if (line.contains("VGA compatible controller") || line.contains("3D controller"))
|
||||
&& line.to_lowercase().contains("nvidia")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn detect_has_discrete_amd() -> bool {
|
||||
if let Ok(output) = Command::new("lspci").output() {
|
||||
if output.status.success() {
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
let mut has_intel = false;
|
||||
let mut has_amd = false;
|
||||
for line in text.lines() {
|
||||
if line.contains("VGA compatible controller") || line.contains("3D controller") {
|
||||
let lower = line.to_lowercase();
|
||||
if lower.contains("intel") {
|
||||
has_intel = true;
|
||||
}
|
||||
if lower.contains("amd") || lower.contains("advanced micro devices") || lower.contains("ati") {
|
||||
has_amd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return has_intel && has_amd;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn glob_vram() -> Result<Option<(String, u64)>, std::io::Error> {
|
||||
for card_idx in 0..8 {
|
||||
@@ -177,19 +334,123 @@ fn detect_refresh_rate() -> u32 {
|
||||
rates.into_iter().max().unwrap_or(60)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn detect_cpu() -> (String, usize, usize) {
|
||||
let threads = detect_cpu_threads();
|
||||
let mut name = String::new();
|
||||
let mut cores = 0;
|
||||
|
||||
if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
|
||||
for line in content.lines() {
|
||||
if name.is_empty() && (line.starts_with("model name") || line.starts_with("Hardware")) {
|
||||
if let Some((_, n)) = line.split_once(':') {
|
||||
name = n.trim().to_string();
|
||||
}
|
||||
}
|
||||
if cores == 0 && line.starts_with("cpu cores") {
|
||||
if let Some((_, c)) = line.split_once(':') {
|
||||
if let Ok(val) = c.trim().parse::<usize>() {
|
||||
cores = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !name.is_empty() && cores > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name.is_empty() {
|
||||
if let Ok(output) = Command::new("lscpu").output() {
|
||||
if output.status.success() {
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
for line in text.lines() {
|
||||
if line.starts_with("Model name:") {
|
||||
if let Some((_, n)) = line.split_once(':') {
|
||||
name = n.trim().to_string();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if name.is_empty() {
|
||||
name = "Linux x86_64 İşlemci".to_string();
|
||||
}
|
||||
|
||||
if cores == 0 {
|
||||
cores = if threads > 1 { threads / 2 } else { 1 };
|
||||
}
|
||||
|
||||
(name, cores, threads)
|
||||
}
|
||||
|
||||
#[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 +458,7 @@ fn detect_gpu() -> (String, u64) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
("Windows DirectX/Vulkan GPU".to_string(), 4096)
|
||||
}
|
||||
|
||||
@@ -241,3 +503,95 @@ fn detect_refresh_rate() -> u32 {
|
||||
}
|
||||
60
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[allow(dead_code)]
|
||||
pub fn detect_has_discrete_nvidia() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[allow(dead_code)]
|
||||
pub fn detect_has_discrete_amd() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn detect_cpu() -> (String, usize, usize) {
|
||||
let threads = detect_cpu_threads();
|
||||
let mut name = "Windows x86_64 İşlemci".to_string();
|
||||
let mut cores = if threads > 1 { threads / 2 } else { 1 };
|
||||
|
||||
let ps_cmd = "Get-CimInstance Win32_Processor | Select-Object -First 1 -Property Name,NumberOfCores,NumberOfLogicalProcessors | ConvertTo-Json -Compress";
|
||||
if let Ok(output) = Command::new("powershell")
|
||||
.args(["-NoProfile", "-Command", ps_cmd])
|
||||
.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()) {
|
||||
if let Some(n) = val["Name"].as_str() {
|
||||
name = n.trim().to_string();
|
||||
}
|
||||
if let Some(c) = val["NumberOfCores"].as_u64() {
|
||||
cores = c as usize;
|
||||
}
|
||||
let detected_threads = val["NumberOfLogicalProcessors"].as_u64().unwrap_or(threads as u64) as usize;
|
||||
return (name, cores, detected_threads.max(threads));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(name, cores, threads)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_clean_cpu_name() {
|
||||
assert_eq!(
|
||||
clean_cpu_name("12th Gen Intel(R) Core(TM) i7-12700H"),
|
||||
"12th Gen Intel Core i7-12700H"
|
||||
);
|
||||
assert_eq!(
|
||||
clean_cpu_name("AMD Ryzen 7 5800X 8-Core Processor"),
|
||||
"AMD Ryzen 7 5800X"
|
||||
);
|
||||
assert_eq!(
|
||||
clean_cpu_name("Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz"),
|
||||
"Intel Core i5-4460 @ 3.20GHz"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_evaluate_cpu() {
|
||||
// Low-end / Bottleneck CPUs
|
||||
assert_eq!(evaluate_cpu("Intel Core i5-4460", 4, 4), CpuGrade::Weak);
|
||||
assert_eq!(evaluate_cpu("Intel Core i3-4130", 2, 4), CpuGrade::Weak);
|
||||
assert_eq!(evaluate_cpu("AMD FX-8350 Eight-Core", 8, 8), CpuGrade::Weak);
|
||||
assert_eq!(evaluate_cpu("Intel Pentium G4560", 2, 4), CpuGrade::Weak);
|
||||
|
||||
// Medium tier CPUs
|
||||
assert_eq!(evaluate_cpu("Intel Core i7-4790K", 4, 8), CpuGrade::Medium);
|
||||
assert_eq!(evaluate_cpu("AMD Ryzen 5 1600", 6, 6), CpuGrade::Medium);
|
||||
|
||||
// Good tier CPUs
|
||||
assert_eq!(evaluate_cpu("AMD Ryzen 5 5600", 6, 12), CpuGrade::Good);
|
||||
assert_eq!(evaluate_cpu("Intel Core i5-12400", 6, 12), CpuGrade::Good);
|
||||
|
||||
// Ultra tier CPUs
|
||||
assert_eq!(evaluate_cpu("12th Gen Intel Core i7-12700H", 14, 20), CpuGrade::Ultra);
|
||||
assert_eq!(evaluate_cpu("AMD Ryzen 7 7800X3D", 8, 16), CpuGrade::Ultra);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detect_cpu_host() {
|
||||
let (name, cores, threads) = detect_cpu();
|
||||
assert!(!name.is_empty());
|
||||
assert!(cores > 0);
|
||||
assert!(threads > 0);
|
||||
println!("Detected CPU: {} ({}C / {}T)", name, cores, threads);
|
||||
}
|
||||
}
|
||||
+693
-72
@@ -1,20 +1,17 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub fn apply_settings(
|
||||
base_dir: &Path,
|
||||
target_hz: u32,
|
||||
patch_delta_time: bool,
|
||||
patch_readbacks: bool,
|
||||
vram_gc_budget_mb: u64,
|
||||
dmem_mb: u64,
|
||||
) -> Result<(), String> {
|
||||
if patch_delta_time {
|
||||
update_xml_patches(base_dir, target_hz)?;
|
||||
use crate::config::{key_to_sdl_scancode, DisplayMode, LauncherConfig};
|
||||
|
||||
pub fn apply_settings(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
if config.patch_delta_time {
|
||||
update_xml_patches(base_dir, config.target_fps)?;
|
||||
}
|
||||
update_run_sh(base_dir, target_hz, patch_readbacks, vram_gc_budget_mb, dmem_mb)?;
|
||||
update_gow3_ini(base_dir, target_hz, patch_readbacks, vram_gc_budget_mb)?;
|
||||
update_user_config_toml(base_dir, vram_gc_budget_mb)?;
|
||||
update_run_sh(base_dir, config)?;
|
||||
update_gow3_ini(base_dir, config)?;
|
||||
update_user_config_toml(base_dir, config)?;
|
||||
update_user_input_config(base_dir, config)?;
|
||||
let _ = patch_engine_analog_scancodes(base_dir, config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -35,10 +32,12 @@ fn update_xml_patches(base_dir: &Path, target_hz: u32) -> Result<(), String> {
|
||||
for path in &patch_files {
|
||||
if path.is_file() {
|
||||
if let Ok(content) = fs::read_to_string(path) {
|
||||
// Update lines with Delta-Time or 120/144 FPS
|
||||
let mut new_lines = Vec::new();
|
||||
for line in content.lines() {
|
||||
if (line.contains("Delta-Time") || line.contains("120 FPS") || line.contains("144 FPS") || line.contains("60 FPS"))
|
||||
if (line.contains("Delta-Time")
|
||||
|| line.contains("120 FPS")
|
||||
|| line.contains("144 FPS")
|
||||
|| line.contains("60 FPS"))
|
||||
&& line.contains("value=\"")
|
||||
{
|
||||
if let Some(start) = line.find("value=\"") {
|
||||
@@ -60,13 +59,7 @@ fn update_xml_patches(base_dir: &Path, target_hz: u32) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_run_sh(
|
||||
base_dir: &Path,
|
||||
target_hz: u32,
|
||||
patch_readbacks: bool,
|
||||
vram_gc_budget_mb: u64,
|
||||
dmem_mb: u64,
|
||||
) -> Result<(), String> {
|
||||
fn update_run_sh(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
let run_sh = base_dir.join("run.sh");
|
||||
if !run_sh.is_file() {
|
||||
return Ok(());
|
||||
@@ -74,40 +67,115 @@ fn update_run_sh(
|
||||
|
||||
let mut content = fs::read_to_string(&run_sh).map_err(|e| e.to_string())?;
|
||||
|
||||
let patches_str = if target_hz != 60 {
|
||||
format!("Bug Fix - Texture Corruption Fix;Skip Any Video With X Button;Frame Rate Patch - {} FPS", target_hz)
|
||||
} else {
|
||||
"Bug Fix - Texture Corruption Fix;Skip Any Video With X Button".to_string()
|
||||
let mut patches = Vec::new();
|
||||
let res_patch = match config.render_resolution.as_str() {
|
||||
"480p" => Some("Resolution Patch - 480p"),
|
||||
"720p" => Some("Resolution Patch - 720p"),
|
||||
"800p" => Some("Resolution Patch - 800p"),
|
||||
"1440p" => Some("Resolution Patch - 1440p"),
|
||||
"1800p" => Some("Resolution Patch - 1800p"),
|
||||
"4K" => Some("Resolution Patch - 4K"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(p) = res_patch {
|
||||
patches.push(p.to_string());
|
||||
} else {
|
||||
patches.push("Bug Fix - Texture Corruption Fix".to_string());
|
||||
}
|
||||
patches.push("Bug Fix - Startup Black Screen".to_string());
|
||||
patches.push("Bug Fix - Intro Crash Fix".to_string());
|
||||
patches.push("Skip Any Video With X Button".to_string());
|
||||
if config.patch_delta_time {
|
||||
patches.push(format!("Frame Rate Patch - {} FPS", config.target_fps));
|
||||
}
|
||||
if config.disable_motion_blur {
|
||||
patches.push("Graphics - Disable Motion Blur".to_string());
|
||||
}
|
||||
if config.disable_dof {
|
||||
patches.push("Graphics - Disable Depth of Field".to_string());
|
||||
}
|
||||
let patches_str = patches.join(";");
|
||||
|
||||
// Update GOW3_GC_BUDGET_MB
|
||||
replace_or_insert_env(&mut content, "GOW3_GC_BUDGET_MB", &vram_gc_budget_mb.to_string());
|
||||
// Update GOW3_DMEM_POOL_MB
|
||||
replace_or_insert_env(&mut content, "GOW3_DMEM_POOL_MB", &dmem_mb.to_string());
|
||||
// Update GOW3_DEFAULT_ENGINE_FPS
|
||||
replace_or_insert_env(&mut content, "GOW3_DEFAULT_ENGINE_FPS", &target_hz.to_string());
|
||||
// Update GOW3_FORCE_READBACK
|
||||
replace_or_insert_env(&mut content, "GOW3_FORCE_READBACK", if patch_readbacks { "1" } else { "0" });
|
||||
// Update GOW3_READBACKS
|
||||
replace_or_insert_env(&mut content, "GOW3_READBACKS", if patch_readbacks { "1" } else { "0" });
|
||||
// Update GOW3_READBACK_LINEAR
|
||||
replace_or_insert_env(&mut content, "GOW3_READBACK_LINEAR", if patch_readbacks { "1" } else { "0" });
|
||||
// Update GOW3_FULL_BARRIERS
|
||||
if let Some(res_env) = config.get_render_res_env_value() {
|
||||
let (internal_w, internal_h) = config.get_resolution_dimensions();
|
||||
replace_or_insert_env(&mut content, "GOW3_RENDER_RES", &res_env);
|
||||
replace_or_insert_env(&mut content, "GOW3_INTERNAL_WIDTH", &internal_w.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_INTERNAL_HEIGHT", &internal_h.to_string());
|
||||
} else {
|
||||
remove_env_if_present(&mut content, "GOW3_RENDER_RES");
|
||||
remove_env_if_present(&mut content, "GOW3_INTERNAL_WIDTH");
|
||||
remove_env_if_present(&mut content, "GOW3_INTERNAL_HEIGHT");
|
||||
}
|
||||
|
||||
if config.fsr_enabled && config.render_resolution != "native" {
|
||||
replace_or_insert_env(&mut content, "GOW3_UPSCALER", "fsr");
|
||||
replace_or_insert_env(&mut content, "GOW3_FSR1", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_RCAS", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_FSR_SHARPNESS", "0.8");
|
||||
} else {
|
||||
replace_or_insert_env(&mut content, "GOW3_UPSCALER", "none");
|
||||
replace_or_insert_env(&mut content, "GOW3_FSR1", "0");
|
||||
replace_or_insert_env(&mut content, "GOW3_RCAS", "0");
|
||||
replace_or_insert_env(&mut content, "GOW3_FSR_SHARPNESS", "0.0");
|
||||
}
|
||||
replace_or_insert_env(&mut content, "GOW3_OBJECT_MOTION", if config.disable_motion_blur { "0" } else { "1" });
|
||||
|
||||
let effective_budget = config.get_effective_vram_budget();
|
||||
replace_or_insert_env(&mut content, "GOW3_GC_BUDGET_MB", &effective_budget.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_DMEM_POOL_MB", &config.dmem_mb.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_DEFAULT_ENGINE_FPS", &config.target_fps.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_FORCE_READBACK", if config.patch_readbacks { "1" } else { "0" });
|
||||
replace_or_insert_env(&mut content, "GOW3_READBACKS", if config.patch_readbacks { "1" } else { "0" });
|
||||
replace_or_insert_env(&mut content, "GOW3_READBACK_LINEAR", if config.patch_readbacks { "1" } else { "0" });
|
||||
replace_or_insert_env(&mut content, "GOW3_DEFERRED_READBACK", if config.async_readbacks { "1" } else { "0" });
|
||||
replace_or_insert_env(&mut content, "GOW3_STALE_READBACK", if config.async_readbacks { "1" } else { "0" });
|
||||
|
||||
let cpu_threads = if config.cached_hardware.cpu_threads > 0 {
|
||||
config.cached_hardware.cpu_threads
|
||||
} else {
|
||||
crate::system::detect_cpu_threads()
|
||||
};
|
||||
let (prep_workers, copy_threads) = if cpu_threads <= 4 {
|
||||
(2, 1)
|
||||
} else if cpu_threads <= 8 {
|
||||
(4, 2)
|
||||
} else {
|
||||
(6, 4)
|
||||
};
|
||||
replace_or_insert_env(&mut content, "GOW3_PREP_WORKERS", &prep_workers.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_COPY_THREADS", ©_threads.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_INTERP_INT_FIX", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_DIRECT_MEMORY_ACCESS", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_PIPELINE_CACHE", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_PARALLEL_WARMUP", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_BACKGROUND_WARMUP", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_VK_RECORD_THREAD", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_PRESENT_THREAD", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_ASYNC_FENCES", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_ASYNC_SHADERS", "1");
|
||||
replace_or_insert_env(&mut content, "GOW3_FPS_LIMIT", &config.target_fps.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_FULL_BARRIERS", "1");
|
||||
// Update GOW3_FORCE_PATCHES
|
||||
replace_or_insert_env(&mut content, "GOW3_FORCE_PATCHES", "1");
|
||||
// Update GOW3_PREBUILT
|
||||
replace_or_insert_env(&mut content, "GOW3_PREBUILT", "1");
|
||||
// Update GOW3_STREAM_MEMORY
|
||||
replace_or_insert_env(&mut content, "GOW3_STREAM_MEMORY", "cached");
|
||||
// Update GOW3_PATCHES
|
||||
replace_or_insert_env(&mut content, "GOW3_PATCHES", &patches_str);
|
||||
// Update GOW3_PATCHES_ONLY
|
||||
replace_or_insert_env(&mut content, "GOW3_PATCHES_ONLY", "1");
|
||||
// Update GOW3_DMEM_MB
|
||||
replace_or_insert_env(&mut content, "GOW3_DMEM_MB", &dmem_mb.to_string());
|
||||
// Update GOW3_VBLANK_HZ
|
||||
replace_or_insert_env(&mut content, "GOW3_VBLANK_HZ", &target_hz.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_DMEM_MB", &config.dmem_mb.to_string());
|
||||
replace_or_insert_env(&mut content, "GOW3_VBLANK_HZ", &config.target_fps.to_string());
|
||||
replace_or_insert_env(
|
||||
&mut content,
|
||||
"GOW3_FULLSCREEN",
|
||||
if config.display_mode != DisplayMode::Windowed { "1" } else { "0" },
|
||||
);
|
||||
let key_map = config.build_gow3_key_map();
|
||||
let pad_map = config.build_gow3_pad_map();
|
||||
replace_or_insert_env(&mut content, "GOW3_KEY_MAP", &key_map);
|
||||
replace_or_insert_env(&mut content, "GOW3_PAD_MAP", pad_map);
|
||||
replace_or_insert_env(
|
||||
&mut content,
|
||||
"SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS",
|
||||
if config.background_controller_input { "1" } else { "0" },
|
||||
);
|
||||
|
||||
fs::write(&run_sh, content).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
@@ -128,7 +196,6 @@ fn replace_or_insert_env(content: &mut String, var_name: &str, value: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
// Insert before the last launch command if not present
|
||||
let insertion = format!("export {}=\"{}\"\n", var_name, value);
|
||||
if let Some(idx) = content.find("exec ") {
|
||||
content.insert_str(idx, &insertion);
|
||||
@@ -137,28 +204,79 @@ fn replace_or_insert_env(content: &mut String, var_name: &str, value: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_gow3_ini(
|
||||
base_dir: &Path,
|
||||
target_hz: u32,
|
||||
patch_readbacks: bool,
|
||||
vram_gc_budget_mb: u64,
|
||||
) -> Result<(), String> {
|
||||
let ini_path = base_dir.join("gow3.ini");
|
||||
if !ini_path.is_file() {
|
||||
return Ok(());
|
||||
fn remove_env_if_present(content: &mut String, var_name: &str) {
|
||||
let pattern_quote = format!("export {}=\"", var_name);
|
||||
let pattern_noquote = format!("export {}=", var_name);
|
||||
|
||||
while let Some(idx) = content.find(&pattern_quote).or_else(|| content.find(&pattern_noquote)) {
|
||||
if let Some(end) = content[idx..].find('\n') {
|
||||
content.drain(idx..idx + end + 1);
|
||||
} else {
|
||||
content.truncate(idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut content = fs::read_to_string(&ini_path).map_err(|e| e.to_string())?;
|
||||
fn update_gow3_ini(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
let ini_path = base_dir.join("gow3.ini");
|
||||
let mut content = if ini_path.is_file() {
|
||||
fs::read_to_string(&ini_path).unwrap_or_default()
|
||||
} else {
|
||||
"# God of War III Remastered settings (auto-tuned)\n\
|
||||
render_resolution=native\n\
|
||||
engine_fps=60\n\
|
||||
fps_limit=60\n\
|
||||
upscaler=off\n\
|
||||
fsr1=0\n\
|
||||
rcas=0\n\
|
||||
display_mode=windowed\n\
|
||||
vsync=1\n\
|
||||
deferred_readback=0\n\
|
||||
stale_readback=0\n\
|
||||
async_shaders=1\n\
|
||||
frames_queued=1\n\
|
||||
readbacks=true\n\
|
||||
readbackLinearImages=true\n\
|
||||
budget=6144\n".to_string()
|
||||
};
|
||||
|
||||
let readbacks_val = if patch_readbacks { "true" } else { "false" };
|
||||
let readbacks_val = if config.patch_readbacks { "true" } else { "false" };
|
||||
replace_ini_key(&mut content, "readbacks", readbacks_val);
|
||||
replace_ini_key(&mut content, "readbackLinearImages", readbacks_val);
|
||||
replace_ini_key(&mut content, "stale_readback", "0");
|
||||
replace_ini_key(&mut content, "budget", &vram_gc_budget_mb.to_string());
|
||||
replace_ini_key(&mut content, "engine_fps", &target_hz.to_string());
|
||||
replace_ini_key(&mut content, "fps_limit", &target_hz.to_string());
|
||||
replace_ini_key(&mut content, "deferred_readback", if config.async_readbacks { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "stale_readback", if config.async_readbacks { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "async_shaders", "1");
|
||||
if config.render_resolution == "native" {
|
||||
replace_ini_key(&mut content, "render_resolution", "native");
|
||||
replace_ini_key(&mut content, "internal_width", "1920");
|
||||
replace_ini_key(&mut content, "internal_height", "1080");
|
||||
replace_ini_key(&mut content, "upscaler", "off");
|
||||
replace_ini_key(&mut content, "fsr1", "0");
|
||||
replace_ini_key(&mut content, "rcas", "0");
|
||||
replace_ini_key(&mut content, "sharpen", "0");
|
||||
replace_ini_key(&mut content, "sharpness", "0.00");
|
||||
} else {
|
||||
let (internal_w, internal_h) = config.get_resolution_dimensions();
|
||||
replace_ini_key(&mut content, "render_resolution", &config.render_resolution);
|
||||
replace_ini_key(&mut content, "internal_width", &internal_w.to_string());
|
||||
replace_ini_key(&mut content, "internal_height", &internal_h.to_string());
|
||||
replace_ini_key(&mut content, "upscaler", if config.fsr_enabled { "fsr" } else { "off" });
|
||||
replace_ini_key(&mut content, "fsr1", if config.fsr_enabled { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "rcas", if config.fsr_enabled { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "sharpen", if config.fsr_enabled { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "sharpness", if config.fsr_enabled { "0.80" } else { "0.00" });
|
||||
}
|
||||
replace_ini_key(&mut content, "rcas_strength", "250");
|
||||
replace_ini_key(&mut content, "object_motion", if config.disable_motion_blur { "0" } else { "1" });
|
||||
|
||||
let effective_budget = config.get_effective_vram_budget();
|
||||
replace_ini_key(&mut content, "budget", &effective_budget.to_string());
|
||||
replace_ini_key(&mut content, "engine_fps", &config.target_fps.to_string());
|
||||
replace_ini_key(&mut content, "fps_limit", &config.target_fps.to_string());
|
||||
replace_ini_key(&mut content, "frames_queued", "1");
|
||||
replace_ini_key(&mut content, "vsync", "1");
|
||||
replace_ini_key(&mut content, "vsync", if config.vsync { "1" } else { "0" });
|
||||
replace_ini_key(&mut content, "display_mode", config.display_mode.as_str());
|
||||
|
||||
fs::write(&ini_path, content).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
@@ -187,21 +305,524 @@ fn replace_ini_key(content: &mut String, key: &str, val: &str) {
|
||||
*content = lines.join("\n");
|
||||
}
|
||||
|
||||
fn update_user_config_toml(base_dir: &Path, vram_gc_budget_mb: u64) -> Result<(), String> {
|
||||
let toml_path = base_dir.join("user/config.toml");
|
||||
if !toml_path.is_file() {
|
||||
return Ok(());
|
||||
fn update_toml_key_in_section(content: &mut String, section: &str, key: &str, val: &str) {
|
||||
let section_header = format!("[{}]", section);
|
||||
let mut in_section = false;
|
||||
let mut key_replaced = false;
|
||||
let mut new_lines = Vec::new();
|
||||
|
||||
for line in content.lines() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with('[') && trimmed.ends_with(']') {
|
||||
if in_section && !key_replaced {
|
||||
new_lines.push(format!("{} = {}", key, val));
|
||||
key_replaced = true;
|
||||
}
|
||||
in_section = trimmed == section_header;
|
||||
new_lines.push(line.to_string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_section {
|
||||
if let Some((k, _)) = trimmed.split_once('=') {
|
||||
if k.trim() == key {
|
||||
new_lines.push(format!("{} = {}", key, val));
|
||||
key_replaced = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_lines.push(line.to_string());
|
||||
}
|
||||
|
||||
let mut content = fs::read_to_string(&toml_path).map_err(|e| e.to_string())?;
|
||||
if in_section && !key_replaced {
|
||||
new_lines.push(format!("{} = {}", key, val));
|
||||
} else if !key_replaced {
|
||||
new_lines.push(String::new());
|
||||
new_lines.push(section_header);
|
||||
new_lines.push(format!("{} = {}", key, val));
|
||||
}
|
||||
|
||||
*content = new_lines.join("\n");
|
||||
}
|
||||
|
||||
fn update_user_config_toml(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
let toml_path = base_dir.join("user/config.toml");
|
||||
let mut content = if toml_path.is_file() {
|
||||
fs::read_to_string(&toml_path).unwrap_or_default()
|
||||
} else {
|
||||
let user_dir = base_dir.join("user");
|
||||
let _ = fs::create_dir_all(&user_dir);
|
||||
"[General]\n\
|
||||
[GPU]\n\
|
||||
Fullscreen = false\n\
|
||||
FullscreenMode = \"Windowed\"\n\
|
||||
[Input]\n".to_string()
|
||||
};
|
||||
|
||||
// Update vulkan_gc_budget if present
|
||||
if let Some(idx) = content.find("vulkan_gc_budget =") {
|
||||
if let Some(end) = content[idx..].find('\n') {
|
||||
let prefix = &content[..idx];
|
||||
let suffix = &content[idx + end..];
|
||||
content = format!("{}vulkan_gc_budget = {}{}", prefix, vram_gc_budget_mb, suffix);
|
||||
content = format!("{}vulkan_gc_budget = {}{}", prefix, config.vram_gc_budget_mb, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Display / GPU settings
|
||||
let (fullscreen_mode_str, fullscreen_bool) = config.display_mode.to_toml_mode();
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"Fullscreen",
|
||||
if fullscreen_bool { "true" } else { "false" },
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"FullscreenMode",
|
||||
&format!("\"{}\"", fullscreen_mode_str),
|
||||
);
|
||||
let (internal_w, internal_h) = match config.render_resolution.as_str() {
|
||||
"480p" => (854, 480),
|
||||
"720p" => (1280, 720),
|
||||
"800p" => (1280, 800),
|
||||
"1440p" => (2560, 1440),
|
||||
"1800p" => (3200, 1800),
|
||||
"4K" => (3840, 2160),
|
||||
_ => (1920, 1080),
|
||||
};
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"internalScreenWidth",
|
||||
&internal_w.to_string(),
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"internalScreenHeight",
|
||||
&internal_h.to_string(),
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"fsrEnabled",
|
||||
if config.fsr_enabled { "true" } else { "false" },
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"rcasEnabled",
|
||||
if config.fsr_enabled { "true" } else { "false" },
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"vblankFrequency",
|
||||
&config.target_fps.to_string(),
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"directMemoryAccess",
|
||||
"true",
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"readbacks",
|
||||
if config.patch_readbacks { "true" } else { "false" },
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"GPU",
|
||||
"readbackLinearImages",
|
||||
if config.patch_readbacks { "true" } else { "false" },
|
||||
);
|
||||
|
||||
// Update Vulkan settings
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"Vulkan",
|
||||
"pipelineCacheEnable",
|
||||
"true",
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"Vulkan",
|
||||
"pipelineCacheArchive",
|
||||
"true",
|
||||
);
|
||||
|
||||
// Update Input settings
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"Input",
|
||||
"backgroundControllerInput",
|
||||
if config.background_controller_input { "true" } else { "false" },
|
||||
);
|
||||
update_toml_key_in_section(
|
||||
&mut content,
|
||||
"Input",
|
||||
"isMotionControlsEnabled",
|
||||
if config.is_motion_controls_enabled { "true" } else { "false" },
|
||||
);
|
||||
|
||||
fs::write(&toml_path, content).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_user_input_config(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
let input_dir = base_dir.join("user/input_config");
|
||||
let _ = fs::create_dir_all(&input_dir);
|
||||
let ini_path = input_dir.join("default.ini");
|
||||
|
||||
let b = &config.keyboard_bindings;
|
||||
let content = format!(
|
||||
"# God of War III Remastered - Input Configuration\n\
|
||||
# Auto-generated by Launcher\n\n\
|
||||
# Keyboard bindings\n\n\
|
||||
triangle = {triangle}\n\
|
||||
circle = {circle}\n\
|
||||
cross = {cross}\n\
|
||||
square = {square}\n\n\
|
||||
l1 = {l1}\n\
|
||||
r1 = {r1}\n\
|
||||
l2 = {l2}\n\
|
||||
r2 = {r2}\n\
|
||||
l3 = {l3}\n\
|
||||
r3 = {r3}\n\n\
|
||||
options = {options}\n\
|
||||
touchpad_center = {touchpad}\n\n\
|
||||
pad_up = {pad_up}\n\
|
||||
pad_down = {pad_down}\n\
|
||||
pad_left = {pad_left}\n\
|
||||
pad_right = {pad_right}\n\n\
|
||||
axis_left_x_minus = {ax_lx_min}\n\
|
||||
axis_left_x_plus = {ax_lx_plus}\n\
|
||||
axis_left_y_minus = {ax_ly_min}\n\
|
||||
axis_left_y_plus = {ax_ly_plus}\n\n\
|
||||
axis_right_x_minus = {ax_rx_min}\n\
|
||||
axis_right_x_plus = {ax_rx_plus}\n\
|
||||
axis_right_y_minus = {ax_ry_min}\n\
|
||||
axis_right_y_plus = {ax_ry_plus}\n\n\
|
||||
# Controller bindings\n\n\
|
||||
triangle = triangle\n\
|
||||
cross = cross\n\
|
||||
square = square\n\
|
||||
circle = circle\n\n\
|
||||
l1 = l1\n\
|
||||
l2 = l2\n\
|
||||
l3 = l3\n\
|
||||
r1 = r1\n\
|
||||
r2 = r2\n\
|
||||
r3 = r3\n\n\
|
||||
options = options\n\
|
||||
touchpad_center = back\n\n\
|
||||
pad_up = pad_up\n\
|
||||
pad_down = pad_down\n\
|
||||
pad_left = pad_left\n\
|
||||
pad_right = pad_right\n\n\
|
||||
axis_left_x = axis_left_x\n\
|
||||
axis_left_y = axis_left_y\n\
|
||||
axis_right_x = axis_right_x\n\
|
||||
axis_right_y = axis_right_y\n\n\
|
||||
# Range of deadzones: 1 (almost none) to 127 (max)\n\
|
||||
analog_deadzone = leftjoystick, {deadzone_l}, 127\n\
|
||||
analog_deadzone = rightjoystick, {deadzone_r}, 127\n\n\
|
||||
override_controller_color = false, 0, 0, 255\n",
|
||||
triangle = b.triangle,
|
||||
circle = b.circle,
|
||||
cross = b.cross,
|
||||
square = b.square,
|
||||
l1 = b.l1,
|
||||
r1 = b.r1,
|
||||
l2 = b.l2,
|
||||
r2 = b.r2,
|
||||
l3 = b.l3,
|
||||
r3 = b.r3,
|
||||
options = b.options,
|
||||
touchpad = b.touchpad_center,
|
||||
pad_up = b.pad_up,
|
||||
pad_down = b.pad_down,
|
||||
pad_left = b.pad_left,
|
||||
pad_right = b.pad_right,
|
||||
ax_lx_min = b.axis_left_x_minus,
|
||||
ax_lx_plus = b.axis_left_x_plus,
|
||||
ax_ly_min = b.axis_left_y_minus,
|
||||
ax_ly_plus = b.axis_left_y_plus,
|
||||
ax_rx_min = b.axis_right_x_minus,
|
||||
ax_rx_plus = b.axis_right_x_plus,
|
||||
ax_ry_min = b.axis_right_y_minus,
|
||||
ax_ry_plus = b.axis_right_y_plus,
|
||||
deadzone_l = config.deadzone_left,
|
||||
deadzone_r = config.deadzone_right,
|
||||
);
|
||||
|
||||
fs::write(&ini_path, content).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Patches the 8 hardcoded analog-stick scancodes in gow3-probe (Linux) and gow3-probe.exe (Windows).
|
||||
///
|
||||
/// The engine binary uses SDL_GetKeyboardState and checks 8 hardcoded 1-byte scancodes:
|
||||
/// Left Stick: +X (right), -X (left), +Y (down), -Y (up)
|
||||
/// Right Stick: +X (right), -X (left), +Y (down), -Y (up)
|
||||
///
|
||||
/// These scancodes are found immediately after a known unique anchor byte sequence
|
||||
/// that appears exactly once in each binary.
|
||||
fn patch_engine_analog_scancodes(base_dir: &Path, config: &LauncherConfig) -> Result<(), String> {
|
||||
let b = &config.keyboard_bindings;
|
||||
|
||||
// Convert user key strings to SDL scancodes
|
||||
let sc_lx_plus = key_to_sdl_scancode(&b.axis_left_x_plus); // Left +X (move right → D default)
|
||||
let sc_lx_minus = key_to_sdl_scancode(&b.axis_left_x_minus); // Left -X (move left → A default)
|
||||
let sc_ly_plus = key_to_sdl_scancode(&b.axis_left_y_plus); // Left +Y (move down → S default)
|
||||
let sc_ly_minus = key_to_sdl_scancode(&b.axis_left_y_minus); // Left -Y (move up → W default)
|
||||
let sc_rx_plus = key_to_sdl_scancode(&b.axis_right_x_plus); // Right +X (cam right → Right Arrow default)
|
||||
let sc_rx_minus = key_to_sdl_scancode(&b.axis_right_x_minus); // Right -X (cam left → Left Arrow default)
|
||||
let sc_ry_plus = key_to_sdl_scancode(&b.axis_right_y_plus); // Right +Y (cam down → Down Arrow default)
|
||||
let sc_ry_minus = key_to_sdl_scancode(&b.axis_right_y_minus); // Right -Y (cam up → Up Arrow default)
|
||||
|
||||
// ─── Linux binary ──────────────────────────────────────────────────────────
|
||||
// Anchor (unique prefix just before the stick scancode block):
|
||||
// f6 c4 01 74 06 41 c6 44 24 08 ff f6 c4 02 74 06 41 c6 44 24 09 ff
|
||||
// Layout after anchor (offsets relative to first byte after anchor):
|
||||
// +0 : 0f b6 55 [LX+] +4 : 0f b6 45 [LX-]
|
||||
// +24: 0f b6 55 [LY+] +28: 0f b6 45 [LY-]
|
||||
// +48: 0f b6 55 [RX+] +52: 0f b6 45 [RX-]
|
||||
// +72: 0f b6 55 [RY+] +76: 0f b6 45 [RY-]
|
||||
let mut probe_candidates: Vec<PathBuf> = vec![
|
||||
base_dir.join("bin/gow3-probe"),
|
||||
base_dir.join("out/gow3-probe"),
|
||||
base_dir.join("gow3-probe"),
|
||||
];
|
||||
if !config.game_dir.is_empty() {
|
||||
let gd = Path::new(&config.game_dir);
|
||||
probe_candidates.push(gd.join("bin/gow3-probe"));
|
||||
probe_candidates.push(gd.join("out/gow3-probe"));
|
||||
probe_candidates.push(gd.join("gow3-probe"));
|
||||
}
|
||||
for probe_path in &probe_candidates {
|
||||
if probe_path.is_file() {
|
||||
patch_binary_linux(
|
||||
probe_path,
|
||||
sc_lx_plus, sc_lx_minus, sc_ly_plus, sc_ly_minus,
|
||||
sc_rx_plus, sc_rx_minus, sc_ry_plus, sc_ry_minus,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Windows binary ────────────────────────────────────────────────────────
|
||||
// Anchor:
|
||||
// a9 00 01 00 00 74 04 c6 46 08 ff a9 00 02 00 00 74 04 c6 46 09 ff
|
||||
// Layout after anchor:
|
||||
// +0 : 0f b6 47 [LX-] +4 : 0f b6 4f [LX+]
|
||||
// +36: 0f b6 4f [LY+] +40: 0f b6 47 [LY-]
|
||||
// +72: 0f b6 4f [RX+] +76: 0f b6 47 [RX-]
|
||||
// +108:0f b6 4f [RY+] +112: 0f b6 47 [RY-]
|
||||
let mut probe_exe_candidates: Vec<PathBuf> = vec![
|
||||
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"),
|
||||
];
|
||||
if !config.game_dir.is_empty() {
|
||||
let gd = Path::new(&config.game_dir);
|
||||
probe_exe_candidates.push(gd.join("bin/gow3-probe.exe"));
|
||||
probe_exe_candidates.push(gd.join("out/gow3-probe.exe"));
|
||||
probe_exe_candidates.push(gd.join("gow3-probe.exe"));
|
||||
probe_exe_candidates.push(gd.join("bin/gow3.exe"));
|
||||
probe_exe_candidates.push(gd.join("gow3.exe"));
|
||||
probe_exe_candidates.push(gd.join("God of War III.exe"));
|
||||
}
|
||||
for probe_path in &probe_exe_candidates {
|
||||
if probe_path.is_file() {
|
||||
patch_binary_windows(
|
||||
probe_path,
|
||||
sc_lx_plus, sc_lx_minus, sc_ly_plus, sc_ly_minus,
|
||||
sc_rx_plus, sc_rx_minus, sc_ry_plus, sc_ry_minus,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn patch_binary_linux(
|
||||
path: &Path,
|
||||
sc_lx_plus: Option<u8>, sc_lx_minus: Option<u8>,
|
||||
sc_ly_plus: Option<u8>, sc_ly_minus: Option<u8>,
|
||||
sc_rx_plus: Option<u8>, sc_rx_minus: Option<u8>,
|
||||
sc_ry_plus: Option<u8>, sc_ry_minus: Option<u8>,
|
||||
) {
|
||||
let anchor: &[u8] = &[
|
||||
0xf6, 0xc4, 0x01, 0x74, 0x06, 0x41, 0xc6, 0x44, 0x24, 0x08, 0xff,
|
||||
0xf6, 0xc4, 0x02, 0x74, 0x06, 0x41, 0xc6, 0x44, 0x24, 0x09, 0xff,
|
||||
];
|
||||
let Ok(mut data) = fs::read(path) else { return };
|
||||
let Some(pos) = find_bytes(&data, anchor) else { return };
|
||||
let base = pos + anchor.len();
|
||||
|
||||
// Verify expected opcodes at each offset before patching
|
||||
if !check_opcode(&data, base, 0, &[0x0f, 0xb6, 0x55]) { return }
|
||||
if !check_opcode(&data, base, 4, &[0x0f, 0xb6, 0x45]) { return }
|
||||
if !check_opcode(&data, base, 24, &[0x0f, 0xb6, 0x55]) { return }
|
||||
if !check_opcode(&data, base, 28, &[0x0f, 0xb6, 0x45]) { return }
|
||||
if !check_opcode(&data, base, 48, &[0x0f, 0xb6, 0x55]) { return }
|
||||
if !check_opcode(&data, base, 52, &[0x0f, 0xb6, 0x45]) { return }
|
||||
if !check_opcode(&data, base, 72, &[0x0f, 0xb6, 0x55]) { return }
|
||||
if !check_opcode(&data, base, 76, &[0x0f, 0xb6, 0x45]) { return }
|
||||
|
||||
if let Some(v) = sc_lx_plus { data[base + 3] = v; }
|
||||
if let Some(v) = sc_lx_minus { data[base + 7] = v; }
|
||||
if let Some(v) = sc_ly_plus { data[base + 27] = v; }
|
||||
if let Some(v) = sc_ly_minus { data[base + 31] = v; }
|
||||
if let Some(v) = sc_rx_plus { data[base + 51] = v; }
|
||||
if let Some(v) = sc_rx_minus { data[base + 55] = v; }
|
||||
if let Some(v) = sc_ry_plus { data[base + 75] = v; }
|
||||
if let Some(v) = sc_ry_minus { data[base + 79] = v; }
|
||||
|
||||
if let Err(_) = fs::write(path, &data) {
|
||||
let tmp = path.with_extension("tmp_probe_patch");
|
||||
if fs::write(&tmp, &data).is_ok() {
|
||||
let _ = fs::rename(&tmp, path);
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o755));
|
||||
}
|
||||
println!("[Input Patch] Analog stick keys patched in {:?}", path);
|
||||
}
|
||||
|
||||
fn patch_binary_windows(
|
||||
path: &Path,
|
||||
sc_lx_plus: Option<u8>, sc_lx_minus: Option<u8>,
|
||||
sc_ly_plus: Option<u8>, sc_ly_minus: Option<u8>,
|
||||
sc_rx_plus: Option<u8>, sc_rx_minus: Option<u8>,
|
||||
sc_ry_plus: Option<u8>, sc_ry_minus: Option<u8>,
|
||||
) {
|
||||
let anchor: &[u8] = &[
|
||||
0xa9, 0x00, 0x01, 0x00, 0x00, 0x74, 0x04, 0xc6, 0x46, 0x08, 0xff,
|
||||
0xa9, 0x00, 0x02, 0x00, 0x00, 0x74, 0x04, 0xc6, 0x46, 0x09, 0xff,
|
||||
];
|
||||
let Ok(mut data) = fs::read(path) else { return };
|
||||
let Some(pos) = find_bytes(&data, anchor) else { return };
|
||||
let base = pos + anchor.len();
|
||||
|
||||
// Verify expected opcodes at each offset before patching
|
||||
if !check_opcode(&data, base, 0, &[0x0f, 0xb6, 0x47]) { return }
|
||||
if !check_opcode(&data, base, 4, &[0x0f, 0xb6, 0x4f]) { return }
|
||||
if !check_opcode(&data, base, 36, &[0x0f, 0xb6, 0x4f]) { return }
|
||||
if !check_opcode(&data, base, 40, &[0x0f, 0xb6, 0x47]) { return }
|
||||
if !check_opcode(&data, base, 72, &[0x0f, 0xb6, 0x4f]) { return }
|
||||
if !check_opcode(&data, base, 76, &[0x0f, 0xb6, 0x47]) { return }
|
||||
if !check_opcode(&data, base, 108, &[0x0f, 0xb6, 0x4f]) { return }
|
||||
if !check_opcode(&data, base, 112, &[0x0f, 0xb6, 0x47]) { return }
|
||||
|
||||
if let Some(v) = sc_lx_minus { data[base + 3] = v; }
|
||||
if let Some(v) = sc_lx_plus { data[base + 7] = v; }
|
||||
if let Some(v) = sc_ly_plus { data[base + 39] = v; }
|
||||
if let Some(v) = sc_ly_minus { data[base + 43] = v; }
|
||||
if let Some(v) = sc_rx_plus { data[base + 75] = v; }
|
||||
if let Some(v) = sc_rx_minus { data[base + 79] = v; }
|
||||
if let Some(v) = sc_ry_plus { data[base + 111] = v; }
|
||||
if let Some(v) = sc_ry_minus { data[base + 115] = v; }
|
||||
|
||||
if let Err(_) = fs::write(path, &data) {
|
||||
let tmp = path.with_extension("tmp_probe_patch");
|
||||
if fs::write(&tmp, &data).is_ok() {
|
||||
let _ = fs::rename(&tmp, path);
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o755));
|
||||
}
|
||||
println!("[Input Patch] Analog stick keys patched in {:?}", path);
|
||||
}
|
||||
|
||||
fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
|
||||
haystack.windows(needle.len()).position(|w| w == needle)
|
||||
}
|
||||
|
||||
fn check_opcode(data: &[u8], base: usize, offset: usize, expected: &[u8]) -> bool {
|
||||
let start = base + offset;
|
||||
if start + expected.len() > data.len() {
|
||||
return false;
|
||||
}
|
||||
&data[start..start + expected.len()] == expected
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_patch_analog_scancodes_synthetic() {
|
||||
// Construct a synthetic buffer with the Linux anchor and opcodes
|
||||
let anchor = [
|
||||
0xf6, 0xc4, 0x01, 0x74, 0x06, 0x41, 0xc6, 0x44, 0x24, 0x08, 0xff,
|
||||
0xf6, 0xc4, 0x02, 0x74, 0x06, 0x41, 0xc6, 0x44, 0x24, 0x09, 0xff,
|
||||
];
|
||||
let mut buf = Vec::new();
|
||||
buf.extend_from_slice(&[0x90; 16]); // nop padding
|
||||
buf.extend_from_slice(&anchor);
|
||||
let base = buf.len();
|
||||
|
||||
// 8 instructions:
|
||||
// +0: 0f b6 55 07 (D)
|
||||
// +4: 0f b6 45 04 (A)
|
||||
// +8..+23: 16 dummy bytes
|
||||
// +24: 0f b6 55 16 (S)
|
||||
// +28: 0f b6 45 1a (W)
|
||||
// +32..+47: 16 dummy bytes
|
||||
// +48: 0f b6 55 4f (Right)
|
||||
// +52: 0f b6 45 50 (Left)
|
||||
// +56..+71: 16 dummy bytes
|
||||
// +72: 0f b6 55 51 (Down)
|
||||
// +76: 0f b6 45 52 (Up)
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x55, 0x07]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x45, 0x04]);
|
||||
buf.extend_from_slice(&[0x00; 16]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x55, 0x16]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x45, 0x1a]);
|
||||
buf.extend_from_slice(&[0x00; 16]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x55, 0x4f]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x45, 0x50]);
|
||||
buf.extend_from_slice(&[0x00; 16]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x55, 0x51]);
|
||||
buf.extend_from_slice(&[0x0f, 0xb6, 0x45, 0x52]);
|
||||
buf.extend_from_slice(&[0x90; 32]);
|
||||
|
||||
let tmp_file = std::env::temp_dir().join("test_gow3_probe_synthetic");
|
||||
fs::write(&tmp_file, &buf).unwrap();
|
||||
|
||||
// Test patching: Left stick unchanged, Right stick -> I(12), K(14), J(13), L(15)
|
||||
patch_binary_linux(
|
||||
&tmp_file,
|
||||
Some(7), Some(4), Some(22), Some(26),
|
||||
Some(15), // RX+ = L
|
||||
Some(13), // RX- = J
|
||||
Some(14), // RY+ = K
|
||||
Some(12), // RY- = I
|
||||
);
|
||||
|
||||
let patched = fs::read(&tmp_file).unwrap();
|
||||
let _ = fs::remove_file(&tmp_file);
|
||||
|
||||
assert_eq!(patched[base + 51], 15); // RX+ = L
|
||||
assert_eq!(patched[base + 55], 13); // RX- = J
|
||||
assert_eq!(patched[base + 75], 14); // RY+ = K
|
||||
assert_eq!(patched[base + 79], 12); // RY- = I
|
||||
}
|
||||
}
|
||||
+313
-69
@@ -5,8 +5,9 @@ use std::process::Command;
|
||||
use crate::config::LauncherConfig;
|
||||
use crate::tuner::apply_settings;
|
||||
|
||||
pub const GITHUB_REPO: &str = "ScreamAngel/gow3_pclauncher";
|
||||
pub const GITHUB_REPO_URL: &str = "https://github.com/ScreamAngel/gow3_pclauncher.git";
|
||||
pub const BUILTIN_VERSION: &str = "v1.7";
|
||||
pub const GITHUB_REPO: &str = "ScreamAngel/gow3_launcher";
|
||||
pub const GITHUB_REPO_URL: &str = "https://github.com/ScreamAngel/gow3_launcher.git";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UpdateInfo {
|
||||
@@ -14,61 +15,299 @@ pub struct UpdateInfo {
|
||||
pub commit_message: String,
|
||||
pub commit_date: String,
|
||||
pub is_new: bool,
|
||||
pub version_tag: Option<String>,
|
||||
pub has_missing_files: bool,
|
||||
pub missing_files: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn check_github_update(current_commit: &str) -> Result<UpdateInfo, String> {
|
||||
// 1. Fetch remote commit SHA using git ls-remote (fast, no GitHub API rate limit)
|
||||
let output = Command::new("git")
|
||||
.args(["ls-remote", GITHUB_REPO_URL, "HEAD"])
|
||||
.output()
|
||||
.map_err(|e| format!("git ls-remote failed: {}", e))?;
|
||||
pub fn parse_version_numbers(v: &str) -> Vec<u32> {
|
||||
v.trim_start_matches(|c: char| c == 'v' || c == 'V')
|
||||
.split('.')
|
||||
.filter_map(|s| {
|
||||
let num_str: String = s.chars().take_while(|c| c.is_ascii_digit()).collect();
|
||||
num_str.parse::<u32>().ok()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("GitHub deposuna ulaşılamadı veya internet bağlantısı yok.".to_string());
|
||||
pub fn compare_version_strings(v1: &str, v2: &str) -> std::cmp::Ordering {
|
||||
let p1 = parse_version_numbers(v1);
|
||||
let p2 = parse_version_numbers(v2);
|
||||
let max_len = p1.len().max(p2.len());
|
||||
for i in 0..max_len {
|
||||
let n1 = p1.get(i).copied().unwrap_or(0);
|
||||
let n2 = p2.get(i).copied().unwrap_or(0);
|
||||
match n1.cmp(&n2) {
|
||||
std::cmp::Ordering::Equal => continue,
|
||||
other => return other,
|
||||
}
|
||||
}
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
|
||||
pub fn detect_launcher_version(base_dir: &Path, config: &LauncherConfig) -> String {
|
||||
// 1. If in a git repository and git is installed, ask git for the exact tag
|
||||
if base_dir.join(".git").is_dir() && is_git_available() {
|
||||
if let Ok(output) = Command::new("git")
|
||||
.args(["describe", "--tags", "--abbrev=0"])
|
||||
.current_dir(base_dir)
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
let tag = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if !tag.is_empty() {
|
||||
let formatted = if tag.starts_with('v') || tag.starts_with('V') {
|
||||
tag
|
||||
} else {
|
||||
format!("v{}", tag)
|
||||
};
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
let remote_sha = text
|
||||
.lines()
|
||||
.next()
|
||||
.and_then(|ln| ln.split_whitespace().next())
|
||||
.ok_or_else(|| "Uzak commit SHA değeri okunamadı.".to_string())?
|
||||
.to_string();
|
||||
// 2. If config has a recorded launcher_version and it's valid
|
||||
if !config.launcher_version.trim().is_empty() {
|
||||
let cv = config.launcher_version.trim();
|
||||
let formatted = if cv.starts_with('v') || cv.starts_with('V') {
|
||||
cv.to_string()
|
||||
} else {
|
||||
format!("v{}", cv)
|
||||
};
|
||||
if compare_version_strings(&formatted, BUILTIN_VERSION) >= std::cmp::Ordering::Equal {
|
||||
return formatted;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fetch commit details (message and date) via curl from GitHub API if available
|
||||
let mut commit_message = "En son optimizasyonlar ve güncellemeler".to_string();
|
||||
let mut commit_date = String::new();
|
||||
// 3. Fallback to built-in compiled version
|
||||
BUILTIN_VERSION.to_string()
|
||||
}
|
||||
|
||||
let api_url = format!("https://api.github.com/repos/{}/commits/HEAD", GITHUB_REPO);
|
||||
if let Ok(api_out) = Command::new("curl")
|
||||
.args([
|
||||
"-s",
|
||||
"-H",
|
||||
"User-Agent: gow3-launcher",
|
||||
&api_url,
|
||||
])
|
||||
fn is_git_available() -> bool {
|
||||
Command::new("git").arg("--version").output().map(|o| o.status.success()).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn http_get_string(url: &str) -> Option<String> {
|
||||
// 1. Try curl (installed on Linux, macOS, and modern Windows 10+)
|
||||
if let Ok(out) = Command::new("curl")
|
||||
.args(["-fsSL", "-H", "User-Agent: gow3-launcher", url])
|
||||
.output()
|
||||
{
|
||||
if api_out.status.success() {
|
||||
let json_str = String::from_utf8_lossy(&api_out.stdout);
|
||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||
if let Some(msg) = val["commit"]["message"].as_str() {
|
||||
if out.status.success() {
|
||||
let body = String::from_utf8_lossy(&out.stdout).to_string();
|
||||
if !body.trim().is_empty() {
|
||||
return Some(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback to PowerShell (native on all Windows installations)
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let ps_code = format!(
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
|
||||
$wc = New-Object System.Net.WebClient; \
|
||||
$wc.Headers.Add('User-Agent', 'gow3-launcher'); \
|
||||
$wc.DownloadString('{}')",
|
||||
url.replace('\'', "''")
|
||||
);
|
||||
if let Ok(out) = Command::new("powershell")
|
||||
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps_code])
|
||||
.output()
|
||||
{
|
||||
if out.status.success() {
|
||||
let body = String::from_utf8_lossy(&out.stdout).to_string();
|
||||
if !body.trim().is_empty() {
|
||||
return Some(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn http_download_file(url: &str, dest: &Path) -> Result<(), String> {
|
||||
// 1. Try curl
|
||||
if let Ok(out) = Command::new("curl")
|
||||
.args(["-fsSL", "-H", "User-Agent: gow3-launcher", url, "-o", dest.to_str().unwrap_or("")])
|
||||
.output()
|
||||
{
|
||||
if out.status.success() && dest.is_file() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback to PowerShell on Windows
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let ps_code = format!(
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \
|
||||
$wc = New-Object System.Net.WebClient; \
|
||||
$wc.Headers.Add('User-Agent', 'gow3-launcher'); \
|
||||
$wc.DownloadFile('{}', '{}')",
|
||||
url.replace('\'', "''"),
|
||||
dest.to_string_lossy().replace('\'', "''")
|
||||
);
|
||||
if let Ok(out) = Command::new("powershell")
|
||||
.args(["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", &ps_code])
|
||||
.output()
|
||||
{
|
||||
if out.status.success() && dest.is_file() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err("Dosya indirilemedi.".to_string())
|
||||
}
|
||||
|
||||
pub fn check_installation_integrity(base_dir: &Path) -> Vec<String> {
|
||||
let mut missing = Vec::new();
|
||||
|
||||
// 1. Icon file
|
||||
if !base_dir.join("gow3.png").is_file() {
|
||||
missing.push("gow3.png".to_string());
|
||||
}
|
||||
|
||||
// 2. Patches XML
|
||||
let has_patch = base_dir.join("patches/God_of_War_III_Remastered.xml").is_file()
|
||||
|| base_dir.join(".runtime/patches/God_of_War_III_Remastered.xml").is_file();
|
||||
if !has_patch {
|
||||
missing.push("patches/God_of_War_III_Remastered.xml".to_string());
|
||||
}
|
||||
|
||||
// 3. Auto tune script
|
||||
let has_script = base_dir.join("scripts/auto_tune.py").is_file()
|
||||
|| base_dir.join(".runtime/scripts/auto_tune.py").is_file();
|
||||
if !has_script {
|
||||
missing.push("scripts/auto_tune.py".to_string());
|
||||
}
|
||||
|
||||
missing
|
||||
}
|
||||
|
||||
pub fn check_github_update(
|
||||
base_dir: &Path,
|
||||
_current_commit: &str,
|
||||
current_version: &str,
|
||||
) -> Result<UpdateInfo, String> {
|
||||
let release_api_url = format!("https://api.github.com/repos/{}/releases/latest", GITHUB_REPO);
|
||||
let commit_api_url = format!("https://api.github.com/repos/{}/commits/HEAD", GITHUB_REPO);
|
||||
|
||||
let mut remote_sha = String::new();
|
||||
let mut commit_message = "En son optimizasyonlar ve güncellemeler".to_string();
|
||||
let mut commit_date = String::new();
|
||||
let mut version_tag: Option<String> = None;
|
||||
|
||||
// 1. Fetch release tag and notes via GitHub Releases API
|
||||
if let Some(json_str) = http_get_string(&release_api_url) {
|
||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||
if let Some(tag) = val["tag_name"].as_str() {
|
||||
let trimmed = tag.trim().to_string();
|
||||
if !trimmed.is_empty() {
|
||||
version_tag = Some(trimmed);
|
||||
}
|
||||
}
|
||||
if let Some(body) = val["body"].as_str().filter(|b| !b.trim().is_empty()) {
|
||||
commit_message = body.lines().next().unwrap_or(body).to_string();
|
||||
}
|
||||
if let Some(d) = val["published_at"].as_str() {
|
||||
commit_date = d.split('T').next().unwrap_or(d).to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fetch commit info via GitHub Commits API
|
||||
if let Some(json_str) = http_get_string(&commit_api_url) {
|
||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
||||
if let Some(sha) = val["sha"].as_str() {
|
||||
remote_sha = sha.to_string();
|
||||
}
|
||||
if let Some(msg) = val["commit"]["message"].as_str() {
|
||||
if commit_message == "En son optimizasyonlar ve güncellemeler" {
|
||||
commit_message = msg.lines().next().unwrap_or(msg).to_string();
|
||||
}
|
||||
if let Some(d) = val["commit"]["committer"]["date"].as_str() {
|
||||
}
|
||||
if let Some(d) = val["commit"]["committer"]["date"].as_str() {
|
||||
if commit_date.is_empty() {
|
||||
commit_date = d.split('T').next().unwrap_or(d).to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let is_new = current_commit.trim().is_empty() || current_commit.trim() != remote_sha.trim();
|
||||
// 3. Fallback: only if GitHub API failed AND git is installed on system
|
||||
if remote_sha.is_empty() && is_git_available() {
|
||||
if let Ok(output) = Command::new("git")
|
||||
.args(["ls-remote", GITHUB_REPO_URL, "HEAD"])
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
if let Some(sha) = text.lines().next().and_then(|ln| ln.split_whitespace().next()) {
|
||||
remote_sha = sha.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if version_tag.is_none() {
|
||||
if let Ok(output) = Command::new("git")
|
||||
.args(["ls-remote", "--tags", GITHUB_REPO_URL])
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
let text = String::from_utf8_lossy(&output.stdout);
|
||||
let mut highest_tag: Option<String> = None;
|
||||
for line in text.lines() {
|
||||
if let Some(ref_part) = line.split_whitespace().nth(1) {
|
||||
if let Some(tag) = ref_part.strip_prefix("refs/tags/") {
|
||||
if !tag.ends_with("^{}") && (tag.starts_with('v') || tag.starts_with('V')) {
|
||||
if let Some(ref h) = highest_tag {
|
||||
if compare_version_strings(tag, h) > std::cmp::Ordering::Equal {
|
||||
highest_tag = Some(tag.to_string());
|
||||
}
|
||||
} else {
|
||||
highest_tag = Some(tag.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
version_tag = highest_tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if remote_sha.is_empty() && version_tag.is_none() {
|
||||
return Err("GitHub deposuna ulaşılamadı. İnternet bağlantınızı kontrol edin veya daha sonra tekrar deneyin.".to_string());
|
||||
}
|
||||
|
||||
// Version Check:
|
||||
// Only true if the remote version tag is STRICTLY HIGHER than current running launcher version.
|
||||
let is_newer_version = if let Some(ref tag) = version_tag {
|
||||
compare_version_strings(tag, current_version) > std::cmp::Ordering::Equal
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let missing_files = check_installation_integrity(base_dir);
|
||||
let has_missing_files = !missing_files.is_empty();
|
||||
|
||||
// Do NOT flag update as needed if current launcher is already at or above the remote release tag!
|
||||
// Prevents redundant re-downloads when already running v1.7.
|
||||
let is_new = is_newer_version || has_missing_files;
|
||||
|
||||
Ok(UpdateInfo {
|
||||
commit_sha: remote_sha,
|
||||
commit_message,
|
||||
commit_date,
|
||||
is_new,
|
||||
version_tag,
|
||||
has_missing_files,
|
||||
missing_files,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -78,21 +317,17 @@ pub fn apply_github_update(
|
||||
config: &mut LauncherConfig,
|
||||
) -> Result<String, String> {
|
||||
let binary_name = if cfg!(target_os = "windows") {
|
||||
if base_dir.join("gow3_launcher_win.exe").is_file() {
|
||||
"gow3_launcher_win.exe"
|
||||
} else {
|
||||
"gow3-launcher-win.exe"
|
||||
}
|
||||
"gow3-launcher-win.exe"
|
||||
} else {
|
||||
"gow3-launcher"
|
||||
};
|
||||
|
||||
let is_git_repo = base_dir.join(".git").is_dir();
|
||||
let is_git_repo = base_dir.join(".git").is_dir() && is_git_available();
|
||||
|
||||
if is_git_repo {
|
||||
// Path A: User cloned via git, perform git pull
|
||||
// Path A: User cloned via git and git is available on system
|
||||
let pull_output = Command::new("git")
|
||||
.args(["pull", "origin", "main"])
|
||||
.args(["pull", "--tags", "origin", "main"])
|
||||
.current_dir(base_dir)
|
||||
.output()
|
||||
.map_err(|e| format!("'git pull' çalıştırılamadı: {}", e))?;
|
||||
@@ -102,7 +337,6 @@ pub fn apply_github_update(
|
||||
return Err(format!("Git güncellemesi başarısız oldu: {}", err));
|
||||
}
|
||||
|
||||
// Ensure launcher binary is executable
|
||||
let launcher_bin = base_dir.join(binary_name);
|
||||
if launcher_bin.is_file() {
|
||||
#[cfg(unix)]
|
||||
@@ -112,7 +346,7 @@ pub fn apply_github_update(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Path B: Standalone folder without .git; download latest launcher binary and patch files
|
||||
// Path B: Standalone folder or git not installed; download latest launcher binary and patch files
|
||||
let raw_launcher_url = format!(
|
||||
"https://raw.githubusercontent.com/{}/main/{}",
|
||||
GITHUB_REPO, binary_name
|
||||
@@ -120,15 +354,8 @@ pub fn apply_github_update(
|
||||
let launcher_bin = base_dir.join(binary_name);
|
||||
let launcher_tmp = base_dir.join(format!("{}.new", binary_name));
|
||||
|
||||
let download = Command::new("curl")
|
||||
.args(["-fsSL", &raw_launcher_url, "-o", launcher_tmp.to_str().unwrap()])
|
||||
.output()
|
||||
.map_err(|e| format!("Yeni launcher indirilemedi: {}", e))?;
|
||||
|
||||
if !download.status.success() {
|
||||
let _ = fs::remove_file(&launcher_tmp);
|
||||
return Err("GitHub üzerinden yeni launcher ikili dosyası indirilemedi.".to_string());
|
||||
}
|
||||
http_download_file(&raw_launcher_url, &launcher_tmp)
|
||||
.map_err(|e| format!("Yeni launcher indirilemedi ({}): {}", raw_launcher_url, e))?;
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
@@ -143,7 +370,6 @@ pub fn apply_github_update(
|
||||
let _ = fs::rename(&launcher_bin, &launcher_old);
|
||||
}
|
||||
|
||||
// Atomically replace running binary
|
||||
fs::rename(&launcher_tmp, &launcher_bin)
|
||||
.map_err(|e| format!("Launcher güncellenemedi: {}", e))?;
|
||||
|
||||
@@ -155,23 +381,25 @@ pub fn apply_github_update(
|
||||
GITHUB_REPO
|
||||
);
|
||||
let patch_dest = patches_dir.join("God_of_War_III_Remastered.xml");
|
||||
let _ = Command::new("curl")
|
||||
.args(["-fsSL", &raw_patch_url, "-o", patch_dest.to_str().unwrap()])
|
||||
.output();
|
||||
let _ = http_download_file(&raw_patch_url, &patch_dest);
|
||||
|
||||
// Also fetch gow3.png if missing
|
||||
let icon_file = base_dir.join("gow3.png");
|
||||
if !icon_file.is_file() {
|
||||
let raw_icon_url = format!("https://raw.githubusercontent.com/{}/main/gow3.png", GITHUB_REPO);
|
||||
let _ = http_download_file(&raw_icon_url, &icon_file);
|
||||
}
|
||||
|
||||
// Also ensure runtime assets are unpacked
|
||||
let _ = crate::runner::ensure_runtime_assets(base_dir);
|
||||
}
|
||||
|
||||
// Re-apply optimizations and settings
|
||||
let _ = apply_settings(
|
||||
base_dir,
|
||||
config.target_fps,
|
||||
config.patch_delta_time,
|
||||
config.patch_readbacks,
|
||||
config.vram_gc_budget_mb,
|
||||
config.dmem_mb,
|
||||
);
|
||||
let _ = apply_settings(base_dir, config);
|
||||
|
||||
// Update last_git_commit in config
|
||||
config.last_git_commit = update.commit_sha.clone();
|
||||
if let Some(ref tag) = update.version_tag {
|
||||
config.launcher_version = tag.clone();
|
||||
}
|
||||
let _ = config.save(base_dir);
|
||||
|
||||
Ok("Launcher ve optimizasyonlar başarıyla güncellendi! Yeni sürümün geçerli olması için lütfen launcher'ı yeniden başlatın.".to_string())
|
||||
@@ -183,10 +411,26 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_check_github_update() {
|
||||
let res = check_github_update("test_dummy_sha");
|
||||
let res = check_github_update(Path::new(".."), "test_dummy_sha", "v0.1");
|
||||
if let Ok(info) = res {
|
||||
assert!(!info.commit_sha.is_empty());
|
||||
assert!(!info.commit_sha.is_empty() || info.version_tag.is_some());
|
||||
assert!(info.is_new);
|
||||
}
|
||||
|
||||
// Test with current version v1.7: should NOT report as an update
|
||||
let res_current = check_github_update(Path::new(".."), "test_dummy_sha", "v1.7");
|
||||
if let Ok(info) = res_current {
|
||||
// Because remote release on GitHub is v1.6.1.6, and current is v1.7, and files are present, is_new must be false!
|
||||
assert!(!info.is_new);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_compare() {
|
||||
assert_eq!(compare_version_strings("v1.3", "v1.4"), std::cmp::Ordering::Less);
|
||||
assert_eq!(compare_version_strings("v1.4", "v1.4"), std::cmp::Ordering::Equal);
|
||||
assert_eq!(compare_version_strings("v1.5", "v1.4"), std::cmp::Ordering::Greater);
|
||||
assert_eq!(compare_version_strings("v1.10", "v1.9"), std::cmp::Ordering::Greater);
|
||||
assert_eq!(compare_version_strings("1.4", "v1.4"), std::cmp::Ordering::Equal);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 250 B |
+34
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"first_launch_done": true,
|
||||
"language": "tr",
|
||||
"game_dir": "/home/screamangel/İndirilenler/GOW3 Emu Linux/CUSA01623",
|
||||
"game_dir": "/home/screamangel/İndirilenler/GOW3 Emu Linux/CUSA01715",
|
||||
"target_fps": 60,
|
||||
"patch_delta_time": true,
|
||||
"patch_readbacks": true,
|
||||
@@ -15,5 +15,37 @@
|
||||
"ram_mb": 15603,
|
||||
"refresh_rate_hz": 144
|
||||
},
|
||||
"last_git_commit": "c5638d9956002b8a9f09a240028f6793ddf7b2f8"
|
||||
"last_git_commit": "0b4abe07acb711848d1b5d72402a9de8c0ad9844",
|
||||
"display_mode": "Borderless",
|
||||
"vsync": true,
|
||||
"background_controller_input": false,
|
||||
"is_motion_controls_enabled": true,
|
||||
"deadzone_left": 2,
|
||||
"deadzone_right": 2,
|
||||
"keyboard_bindings": {
|
||||
"triangle": "c",
|
||||
"circle": "b",
|
||||
"cross": "n",
|
||||
"square": "v",
|
||||
"l1": "q",
|
||||
"r1": "u",
|
||||
"l2": "e",
|
||||
"r2": "o",
|
||||
"l3": "x",
|
||||
"r3": "m",
|
||||
"options": "enter",
|
||||
"touchpad_center": "space",
|
||||
"pad_up": "up",
|
||||
"pad_down": "down",
|
||||
"pad_left": "left",
|
||||
"pad_right": "right",
|
||||
"axis_left_x_minus": "a",
|
||||
"axis_left_x_plus": "d",
|
||||
"axis_left_y_minus": "w",
|
||||
"axis_left_y_plus": "s",
|
||||
"axis_right_x_minus": "j",
|
||||
"axis_right_x_plus": "l",
|
||||
"axis_right_y_minus": "i",
|
||||
"axis_right_y_plus": "k"
|
||||
}
|
||||
}
|
||||
@@ -17,59 +17,73 @@
|
||||
<Line Type="bytes" Address="0x007cd918" Value="00000090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Bug Fix - Startup Black Screen" Note="Fixes the black screen at startup with the FPS counter running: the frame pacing index starts at 2 instead of 0, so the second frame no longer waits for a flip that only it can trigger." Author="SpartanPort" PatchVer="1.0" AppVer="01.02" AppElf="eboot.bin" isEnabled="true">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x018ba600" Value="02000000"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Bug Fix - Intro Crash Fix" Note="Universal fix for engine pool memory exhaustion crashes (such as intro transition crash at 0x2c54b2 and magic/combat crash at 0x2de2d2). Enables automatic slab growth for all engine pool allocators and adds defensive null checks." Author="gow3-launcher" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="true">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x006b36ae" Value="6a305eba00080000b9800000006a04415890"/>
|
||||
<Line Type="bytes" Address="0x006c54a7" Value="0f84d600000090"/>
|
||||
<Line Type="bytes" Address="0x006c6387" Value="0f84d600000090"/>
|
||||
<Line Type="bytes" Address="0x0058dca0" Value="4585ff750441b78090"/>
|
||||
<Line Type="bytes" Address="0x006de2c7" Value="0f84d600000090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 480p" Note="Decreases game output resolution to 854x480 and increases Video Arena to 2.25 GiB. Requires setting DMEM to 2048 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="5603"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="e001"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="5603"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="e001"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="56030000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="e0010000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="00000090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 720p" Note="Decreases game output resolution to 1280x720 and increases Video Arena to 2.25 GiB. Requires setting DMEM to 2048 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="0005"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="d002"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="0005"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="d002"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="00000090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 800p" Note="Sets the game output resolution to 1280x800 (16:10, the Steam Deck panel) and increases Video Arena to 2.25 GiB. Requires setting DMEM to 2048 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4 (1280x800 adapted for the Steam Deck)" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="0005"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="2003"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="0005"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="2003"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="20030000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="00000090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 1440p" Note="Increases game output resolution to 2560x1440 and Video Arena to 2.75 GiB. Requires setting DMEM to 3072 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="000a"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="a005"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="000a"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="a005"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x007c46b4" Value="000a0000"/>
|
||||
<Line Type="bytes" Address="0x007c46b8" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x0056bb82" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x0060953d" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x0056bb72" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x0060965d" Value="a0050000"/>
|
||||
<Line Type="bytes" Address="0x007f2a3c" Value="00050000"/>
|
||||
<Line Type="bytes" Address="0x007f2a40" Value="d0020000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="000000b0"/>
|
||||
@@ -77,18 +91,18 @@
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 1800p" Note="Increases game output resolution to 3200x1800 and Video Arena to 3.25 GiB. Requires setting DMEM to 4608 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="800c"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="0807"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="800c"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="0807"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x007c46b4" Value="800c0000"/>
|
||||
<Line Type="bytes" Address="0x007c46b8" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x0056bb82" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x0060953d" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x0056bb72" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x0060965d" Value="08070000"/>
|
||||
<Line Type="bytes" Address="0x007f2a3c" Value="40060000"/>
|
||||
<Line Type="bytes" Address="0x007f2a40" Value="84030000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="000000d0"/>
|
||||
@@ -96,39 +110,77 @@
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Resolution Patch - 4K" Note="Increases game output resolution to 3840x2160 and Video Arena to approximately 4 GB (3.75 GiB). Requires setting DMEM to 6144 MB or more. Only leave one resolution patch enabled; do not enable it with Bug Fix - Texture Corruption Fix." Author="kvicken + cuesta4" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x005f0b3b" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005f0b43" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x005f0b4c" Value="000f"/>
|
||||
<Line Type="bytes" Address="0x005f0b53" Value="7008"/>
|
||||
<Line Type="bytes" Address="0x005643f3" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x00564400" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005ff769" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005ff76f" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x005f0c5b" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005f0c63" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x005f0c6c" Value="000f"/>
|
||||
<Line Type="bytes" Address="0x005f0c73" Value="7008"/>
|
||||
<Line Type="bytes" Address="0x005643e3" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x005643f0" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005ff889" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x005ff88f" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x007c46b4" Value="000f0000"/>
|
||||
<Line Type="bytes" Address="0x007c46b8" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x0056bb82" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x0060953d" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x0056bb72" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x0060965d" Value="70080000"/>
|
||||
<Line Type="bytes" Address="0x007f2a3c" Value="80070000"/>
|
||||
<Line Type="bytes" Address="0x007f2a40" Value="38040000"/>
|
||||
<Line Type="bytes" Address="0x007cd918" Value="000000f0"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 120 FPS" Note="Raises the engine maximum framerate from 59.94 to 120 FPS so the frametime clamp produces a 1/120 second gameplay delta. Corrects swing energy calculations to match the original 60 Hz simulation. Requires setting VBlank to 120 FPS." Author="kvicken" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 30 FPS" Note="Sets the engine maximum framerate to 30 FPS with correct 60 Hz swing energy simulation. Requires setting VBlank to 30 FPS." Author="gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05a7c12600"/>
|
||||
<Line Type="bytes" Address="0x0075dbc4" Value="0000f042"/>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="0000f041"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="0000f041"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 144 FPS" Note="Raises the engine maximum framerate from 59.94 to 144 FPS so the frametime clamp produces a 1/144 second gameplay delta. Corrects swing energy calculations to match the original 60 Hz simulation. Requires setting VBlank to 144 FPS." Author="kvicken" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 40 FPS" Note="Sets the engine maximum framerate to 40 FPS (ideal for Steam Deck 40 Hz) with correct 60 Hz swing energy simulation. Requires setting VBlank to 40 FPS." Author="gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05a7c12600"/>
|
||||
<Line Type="bytes" Address="0x0075dbc4" Value="00001043"/>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="00002042"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="00002042"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 240 FPS" Note="The 120 FPS patch with the engine maximum framerate at 240 FPS (frametime clamp of 1/240 second), adapted by Felipe Prado. Same swing energy correction. Requires setting VBlank to 240 FPS." Author="kvicken, gow3_pc" PatchVer="1.0" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 60 FPS" Note="Sets the engine maximum framerate to 60 FPS with correct 60 Hz swing energy simulation. Requires setting VBlank to 60 FPS." Author="gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05a7c12600"/>
|
||||
<Line Type="bytes" Address="0x0075dbc4" Value="00007043"/>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="00007042"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="00007042"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 90 FPS" Note="Sets the engine maximum framerate to 90 FPS (ideal for Steam Deck OLED 90 Hz) with correct 60 Hz swing energy simulation. Requires setting VBlank to 90 FPS." Author="gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="0000b442"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="0000b442"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 120 FPS" Note="Raises the engine maximum framerate from 59.94 to 120 FPS. Corrects swing energy calculations to match original 60 Hz simulation so game speed and physics stay normal. Requires setting VBlank to 120 FPS." Author="kvicken, gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="0000f042"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="0000f042"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 144 FPS" Note="Raises the engine maximum framerate from 59.94 to 144 FPS. Corrects swing energy calculations to match original 60 Hz simulation. Requires setting VBlank to 144 FPS." Author="kvicken, gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="00001043"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="00001043"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 165 FPS" Note="Raises the engine maximum framerate from 59.94 to 165 FPS. Corrects swing energy calculations to match original 60 Hz simulation. Requires setting VBlank to 165 FPS." Author="gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="00002543"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="00002543"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Frame Rate Patch - 240 FPS" Note="The 120 FPS patch with the engine maximum framerate at 240 FPS, with swing energy correction. Requires setting VBlank to 240 FPS." Author="kvicken, Felipe Prado, gow3-launcher" PatchVer="1.2" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0048ff0a" Value="488d05e7c22600"/>
|
||||
<Line Type="bytes" Address="0x0075dd00" Value="00007043"/>
|
||||
<Line Type="bytes" Address="0x0075dd04" Value="00007043"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Skip Any Video With X Button" Note="Allows manual skipping of cinematics any cinematic with the X button. Thanks to illusion0001 aka illusionyy for the PS3 patch which made this very easy." Author="cuesta4" PatchVer="1.0" AppVer="01.02" AppElf="eboot.bin" isEnabled="true">
|
||||
@@ -137,4 +189,16 @@
|
||||
<Line Type="bytes" Address="0x0041b2e7" Value="b901000000909090"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Graphics - Disable Motion Blur" Note="Disables the camera and object motion blur render pass, improving performance on low-end systems and removing motion smearing." Author="gow3-launcher" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x0056ae80" Value="c3"/>
|
||||
<Line Type="bytes" Address="0x0056b0e0" Value="c3"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
<Metadata Title="God of War III Remastered" Name="Graphics - Disable Depth of Field" Note="Disables the Depth of Field (DoF) post-processing blur pass, increasing FPS on budget GPUs and keeping background details sharp." Author="gow3-launcher" PatchVer="1.1" AppVer="01.02" AppElf="eboot.bin" isEnabled="false">
|
||||
<PatchList>
|
||||
<Line Type="bytes" Address="0x00565ac0" Value="c3"/>
|
||||
<Line Type="bytes" Address="0x00566090" Value="c3"/>
|
||||
</PatchList>
|
||||
</Metadata>
|
||||
</Patch>
|
||||
@@ -118,8 +118,10 @@ def calculate_settings(specs, override_hz=None):
|
||||
gc_budget_mb = 4608
|
||||
elif vram_mb >= 3500:
|
||||
gc_budget_mb = 3072
|
||||
else:
|
||||
elif vram_mb >= 2500:
|
||||
gc_budget_mb = 2048
|
||||
else:
|
||||
gc_budget_mb = 1280
|
||||
|
||||
# Guest DMEM Pool (based on RAM)
|
||||
if ram_mb >= 24000:
|
||||
@@ -198,10 +200,7 @@ def update_run_sh(settings):
|
||||
dmem = settings["dmem_mb"]
|
||||
|
||||
# Target patches string
|
||||
if target_hz != 60:
|
||||
patches_str = f"Bug Fix - Texture Corruption Fix;Skip Any Video With X Button;Frame Rate Patch - {target_hz} FPS"
|
||||
else:
|
||||
patches_str = "Bug Fix - Texture Corruption Fix;Skip Any Video With X Button"
|
||||
patches_str = f"Bug Fix - Texture Corruption Fix;Bug Fix - Startup Black Screen;Bug Fix - Intro Crash Fix;Skip Any Video With X Button;Frame Rate Patch - {target_hz} FPS"
|
||||
|
||||
# Update or add GOW3_GC_BUDGET_MB near the top
|
||||
if re.search(r"^export GOW3_GC_BUDGET_MB=.*$", content, re.M):
|
||||
@@ -291,6 +290,9 @@ def update_user_config_toml(settings):
|
||||
content = re.sub(r"^readbacks\s*=\s*\w+", "readbacks = true", content, flags=re.M)
|
||||
content = re.sub(r"^readbackLinearImages\s*=\s*\w+", "readbackLinearImages = true", content, flags=re.M)
|
||||
content = re.sub(r"^copyGPUBuffers\s*=\s*\w+", "copyGPUBuffers = true", content, flags=re.M)
|
||||
content = re.sub(r"^directMemoryAccess\s*=\s*\w+", "directMemoryAccess = true", content, flags=re.M)
|
||||
content = re.sub(r"^pipelineCacheEnable\s*=\s*\w+", "pipelineCacheEnable = true", content, flags=re.M)
|
||||
content = re.sub(r"^pipelineCacheArchive\s*=\s*\w+", "pipelineCacheArchive = true", content, flags=re.M)
|
||||
|
||||
toml_path.write_text(content, encoding="utf-8")
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import json
|
||||
from pathlib import Path
|
||||
|
||||
RESOLUTIONS = ('native', '480p', '720p', '800p', '1440p', '1800p', '4K')
|
||||
ENGINE_FPS = (60, 120, 144, 240)
|
||||
DEFAULTS = {'render_resolution': 'native', 'engine_fps': '60', 'deferred_readback': '1',
|
||||
'stale_readback': '1', 'async_shaders': '1'}
|
||||
ENGINE_FPS = (30, 40, 60, 90, 120, 144, 165, 240)
|
||||
DEFAULTS = {'render_resolution': 'native', 'engine_fps': '60', 'deferred_readback': '0',
|
||||
'stale_readback': '0', 'async_shaders': '1'}
|
||||
RESOLUTION_PREFIX = 'Resolution Patch'
|
||||
FPS_PREFIX = 'Frame Rate Patch'
|
||||
TEXTURE_FIX = 'Bug Fix - Texture Corruption Fix'
|
||||
@@ -96,12 +96,16 @@ def patch_names(names, options, texture_fix_available=True):
|
||||
names = [n for n in names if n != TEXTURE_FIX] + [f'{RESOLUTION_PREFIX} - {resolution}']
|
||||
elif texture_fix_available and TEXTURE_FIX not in names:
|
||||
names.insert(0, TEXTURE_FIX)
|
||||
if options['engine_fps'] != '60':
|
||||
names.append(f'{FPS_PREFIX} - {options["engine_fps"]} FPS')
|
||||
fps = options.get('engine_fps', '60')
|
||||
names.append(f'{FPS_PREFIX} - {fps} FPS')
|
||||
return names
|
||||
|
||||
|
||||
def environment(options):
|
||||
return {'GOW3_DEFERRED_READBACK': options['deferred_readback'],
|
||||
'GOW3_STALE_READBACK': options['stale_readback'],
|
||||
'GOW3_ASYNC_SHADERS': options['async_shaders']}
|
||||
return {'GOW3_DEFERRED_READBACK': options.get('deferred_readback', '0'),
|
||||
'GOW3_STALE_READBACK': options.get('stale_readback', '0'),
|
||||
'GOW3_ASYNC_SHADERS': options.get('async_shaders', '1'),
|
||||
'GOW3_READBACKS': '1',
|
||||
'GOW3_READBACK_LINEAR': '1',
|
||||
'GOW3_FORCE_READBACK': '1',
|
||||
'GOW3_FPS_LIMIT': options.get('engine_fps', '60')}
|
||||
Reference in new issue
Block a user