76
.github/workflows/build.yml
vendored
76
.github/workflows/build.yml
vendored
@@ -1,76 +0,0 @@
|
|||||||
name: Build Go/Fyne App for macOS ARM64
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main, master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
|
|
||||||
- name: Install Fyne dependencies
|
|
||||||
run: |
|
|
||||||
# Install required system dependencies for Fyne
|
|
||||||
brew install pkg-config
|
|
||||||
|
|
||||||
- name: Install Fyne CLI
|
|
||||||
run: go install fyne.io/fyne/v2/cmd/fyne@latest
|
|
||||||
|
|
||||||
- name: Set environment variables
|
|
||||||
run: |
|
|
||||||
echo "CGO_ENABLED=1" >> $GITHUB_ENV
|
|
||||||
echo "GOOS=darwin" >> $GITHUB_ENV
|
|
||||||
echo "GOARCH=arm64" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Cache Go modules
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go-
|
|
||||||
|
|
||||||
- name: Download dependencies
|
|
||||||
run: go mod download
|
|
||||||
|
|
||||||
- name: Verify dependencies
|
|
||||||
run: go mod verify
|
|
||||||
|
|
||||||
- name: Check code formatting
|
|
||||||
run: |
|
|
||||||
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
|
|
||||||
echo "Code is not properly formatted. Please run 'gofmt -s -w .'"
|
|
||||||
gofmt -s -l .
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run go vet
|
|
||||||
run: go vet ./...
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: go test -v ./...
|
|
||||||
|
|
||||||
- name: Build application
|
|
||||||
run: make build
|
|
||||||
|
|
||||||
- name: Verify build output
|
|
||||||
run: |
|
|
||||||
ls -la TurtleSilicon.app/
|
|
||||||
ls -la TurtleSilicon.app/Contents/Resources/
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: TurtleSilicon-macos
|
|
||||||
path: TurtleSilicon.app/
|
|
||||||
retention-days: 30
|
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
TurtleSilicon.*
|
EpochSilicon.*
|
||||||
TurtleSilicon.app/Contents/MacOS/turtlesilicon
|
EpochSilicon.app/Contents/MacOS/epochsilicon
|
||||||
*.dmg
|
*.dmg
|
||||||
turtlesilicon
|
epochsilicon
|
||||||
|
.idea
|
34
EpochSilicon.app/Contents/Info.plist
Normal file
34
EpochSilicon.app/Contents/Info.plist
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>EpochSilicon</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>epochsilicon</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.burkey.epochsilicon</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>icon.icns</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.2.4</string>
|
||||||
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
|
<array>
|
||||||
|
<string>MacOSX</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>133</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
|
<true/>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.11</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
BIN
EpochSilicon.app/Contents/Resources/icon.icns
Normal file
BIN
EpochSilicon.app/Contents/Resources/icon.icns
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
[Details]
|
[Details]
|
||||||
Icon = "Icon.png"
|
Icon = "Icon.png"
|
||||||
Name = "TurtleSilicon"
|
Name = "EpochSilicon"
|
||||||
ID = "com.tairasu.turtlesilicon"
|
ID = "com.burkey.epochsilicon"
|
||||||
Version = "1.2.4"
|
Version = "1.2.4"
|
||||||
Build = 78
|
Build = 134
|
||||||
|
BIN
Icon.png
BIN
Icon.png
Binary file not shown.
Before Width: | Height: | Size: 313 KiB After Width: | Height: | Size: 14 KiB |
1
LICENSE
1
LICENSE
@@ -1,5 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Evan Burkey <evan@burkey.co>
|
||||||
Copyright (c) 2025 tairasu
|
Copyright (c) 2025 tairasu
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
59
Makefile
59
Makefile
@@ -7,48 +7,59 @@ all: build-release
|
|||||||
build-dev:
|
build-dev:
|
||||||
GOOS=darwin GOARCH=arm64 fyne package
|
GOOS=darwin GOARCH=arm64 fyne package
|
||||||
@echo "Copying additional resources to app bundle..."
|
@echo "Copying additional resources to app bundle..."
|
||||||
@mkdir -p TurtleSilicon.app/Contents/Resources/rosettax87
|
@mkdir -p EpochSilicon.app/Contents/Resources/rosettax87
|
||||||
@mkdir -p TurtleSilicon.app/Contents/Resources/winerosetta
|
@mkdir -p EpochSilicon.app/Contents/Resources/winerosetta
|
||||||
@cp -R rosettax87/* TurtleSilicon.app/Contents/Resources/rosettax87/
|
@cp -R rosettax87/* EpochSilicon.app/Contents/Resources/rosettax87/
|
||||||
@cp -R winerosetta/* TurtleSilicon.app/Contents/Resources/winerosetta/
|
@cp -R winerosetta/* EpochSilicon.app/Contents/Resources/winerosetta/
|
||||||
@cp -R Icon.png TurtleSilicon.app/Contents/Resources/
|
@cp -R Icon.png EpochSilicon.app/Contents/Resources/
|
||||||
@echo "Development build complete!"
|
@echo "Development build complete!"
|
||||||
|
|
||||||
build: build-dev
|
build: build-dev
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
|
@rm -rf ./EpochSilicon.app
|
||||||
@echo "Building optimized release version..."
|
@echo "Building optimized release version..."
|
||||||
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build \
|
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build \
|
||||||
-ldflags="-s -w -X main.appVersion=$$(grep Version FyneApp.toml | cut -d'"' -f2)" \
|
-ldflags="-s -w -X main.appVersion=$$(grep Version FyneApp.toml | cut -d'"' -f2)" \
|
||||||
-trimpath \
|
-trimpath \
|
||||||
-tags=release \
|
-tags=release \
|
||||||
-o turtlesilicon .
|
-o epochsilicon .
|
||||||
@echo "Packaging with fyne..."
|
@echo "Packaging with fyne..."
|
||||||
GOOS=darwin GOARCH=arm64 fyne package --release --executable turtlesilicon
|
GOOS=darwin GOARCH=arm64 fyne package --release --executable epochsilicon
|
||||||
@echo "Copying additional resources to app bundle..."
|
@echo "Copying additional resources to app bundle..."
|
||||||
@mkdir -p TurtleSilicon.app/Contents/Resources/rosettax87
|
@mkdir -p EpochSilicon.app/Contents/Resources/rosettax87
|
||||||
@mkdir -p TurtleSilicon.app/Contents/Resources/winerosetta
|
@mkdir -p EpochSilicon.app/Contents/Resources/winerosetta
|
||||||
@cp -R rosettax87/* TurtleSilicon.app/Contents/Resources/rosettax87/
|
@cp -R rosettax87/* EpochSilicon.app/Contents/Resources/rosettax87/
|
||||||
@cp -R winerosetta/* TurtleSilicon.app/Contents/Resources/winerosetta/
|
@cp -R winerosetta/* EpochSilicon.app/Contents/Resources/winerosetta/
|
||||||
@cp -R Icon.png TurtleSilicon.app/Contents/Resources/
|
@cp -R Icon.png EpochSilicon.app/Contents/Resources/
|
||||||
@echo "Stripping additional symbols..."
|
@echo "Stripping additional symbols..."
|
||||||
strip -x TurtleSilicon.app/Contents/MacOS/turtlesilicon
|
strip -x EpochSilicon.app/Contents/MacOS/epochsilicon
|
||||||
@echo "Optimized release build complete!"
|
@echo "Optimized release build complete!"
|
||||||
@echo "Binary size: $$(ls -lah TurtleSilicon.app/Contents/MacOS/turtlesilicon | awk '{print $$5}')"
|
@echo "Binary size: $$(ls -lah EpochSilicon.app/Contents/MacOS/epochsilicon | awk '{print $$5}')"
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
clean:
|
clean:
|
||||||
rm -rf TurtleSilicon.app
|
rm -rf EpochSilicon.app
|
||||||
rm -f TurtleSilicon.dmg
|
rm -f EpochSilicon.dmg
|
||||||
rm -f turtlesilicon
|
rm -f EpochSilicon.zip
|
||||||
|
rm -f epochsilicon
|
||||||
|
|
||||||
dmg: build-release
|
sign: build-release
|
||||||
@echo "Preparing DMG staging directory..."
|
codesign --timestamp --options runtime -s "Developer ID Application: Evan Burkey (2TXG4AP658)" ./EpochSilicon.app/Contents/Resources/rosettax87/rosettax87
|
||||||
|
codesign --timestamp --options runtime -s "Developer ID Application: Evan Burkey (2TXG4AP658)" ./EpochSilicon.app/Contents/Resources/rosettax87/libRuntimeRosettax87
|
||||||
|
codesign --timestamp --options runtime -s "Developer ID Application: Evan Burkey (2TXG4AP658)" ./EpochSilicon.app
|
||||||
|
@/usr/bin/ditto -c -k -rsrc --sequesterRsrc --keepParent ./EpochSilicon.app EpochSilicon.zip
|
||||||
|
xcrun notarytool submit --keychain-profile "EpochSilicon" --wait ./EpochSilicon.zip
|
||||||
|
xcrun stapler staple ./EpochSilicon.app
|
||||||
|
|
||||||
|
dmg: build-release sign
|
||||||
|
echo "Preparing DMG staging directory..."
|
||||||
@rm -rf dmg-staging
|
@rm -rf dmg-staging
|
||||||
@mkdir dmg-staging
|
@mkdir dmg-staging
|
||||||
@cp -R TurtleSilicon.app dmg-staging/
|
@/usr/bin/ditto --rsrc ./EpochSilicon.app dmg-staging/EpochSilicon.app
|
||||||
@ln -s /Applications dmg-staging/Applications
|
@ln -s /Applications dmg-staging/Applications
|
||||||
@echo "Creating DMG file..."
|
hdiutil create -volname EpochSilicon -srcfolder dmg-staging -ov -format UDZO EpochSilicon.dmg
|
||||||
@hdiutil create -volname TurtleSilicon -srcfolder dmg-staging -ov -format UDZO TurtleSilicon.dmg
|
@rm -rf dmg-staging
|
||||||
@echo "DMG created: TurtleSilicon.dmg"
|
codesign --timestamp -i com.burkey.epochsilicon -s "Developer ID Application: Evan Burkey (2TXG4AP658)" ./EpochSilicon.dmg
|
||||||
@rm -rf dmg-staging
|
xcrun notarytool submit --keychain-profile "EpochSilicon" --wait ./EpochSilicon.dmg
|
||||||
|
xcrun stapler staple ./EpochSilicon.dmg
|
||||||
|
69
README.md
69
README.md
@@ -1,29 +1,13 @@
|
|||||||
<h1 align="center"><img src="Icon.png" alt="TurtleSilicon Logo" width="50" height="50" align="center"> TurtleSilicon</h1>
|
A user-friendly launcher for Project Epoch on Apple Silicon Macs, with one-click patching of winerosetta, rosettax87 and d9vk.
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
<a href="">[](https://github.com/tairasu/TurtleSilicon/actions/workflows/build.yml)</a>
|
|
||||||
<a href="">[]()</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<img src="img/turtlesilicon-fps_v2.png" alt="Turtle WoW FPS on Apple Silicon" width="49%" />
|
|
||||||
<img src="img/turtlesilicon-app_v4.png" alt="TurtleSilicon Application" width="49%" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
A user-friendly launcher for Turtle WoW on Apple Silicon Macs, with one-click patching of winerosetta, rosettax87 and d9vk.
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Before you begin, ensure you have the following:
|
Before you begin, ensure you have the following:
|
||||||
|
|
||||||
* A working version of **CrossOver** installed (the trial version is sufficient and can still be used after expiration).
|
* A working version of **CrossOver** installed (the trial version is sufficient and can still be used after expiration).
|
||||||
- I recommend using CrossOver v25.0.1 or later. Older versions will cause issues.
|
- You must use CrossOver v25.0.1 or later. Older versions will cause issues.
|
||||||
* The **Turtle WoW Client** downloaded from the official website.
|
* A 3.3.5a World of Warcraft client. You can easily get the client from [Project Epoch's download page](https://project-epoch.com/play)
|
||||||
|
* Place the client in an folder you have rights to. Your user directory (example: `/Users/username/Epoch`) is a good default choice. Avoid spaces in your path!
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@@ -33,30 +17,34 @@ All credit for the core translation layer `winerosetta` and `rosettax87` goes to
|
|||||||
|
|
||||||
[https://github.com/Lifeisawful/rosettax87](https://github.com/Lifeisawful/rosettax87)
|
[https://github.com/Lifeisawful/rosettax87](https://github.com/Lifeisawful/rosettax87)
|
||||||
|
|
||||||
|
EpochSilicon is a fork of [https://turtlesilicon.github.io/](TurtleSilicon), credit for the base of the application goes to tairasu.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Apple Silicon Compatibility:** Runs 32-bit DirectX9 World of Warcraft (v1.12) on M1/M2/M3/M4 Macs without "illegal instruction" errors.
|
* **Apple Silicon Compatibility:** Runs 32-bit DirectX9 World of Warcraft (v3.3.5a) on M1/M2/M3/M4 Macs without "illegal instruction" errors.
|
||||||
* **Performance Optimization:**
|
* **Performance Optimization:**
|
||||||
* Integrates `rosettax87` for accelerated x87 FPU instructions
|
* Integrates `rosettax87` for accelerated x87 FPU instructions
|
||||||
* Uses `d9vk` for efficient DirectX9 via Vulkan/Metal translation
|
* Uses `d9vk` for efficient DirectX9 via Vulkan/Metal translation
|
||||||
* Achieves significant FPS improvements (20 FPS → 200+ FPS in many scenarios)
|
* Achieves significant FPS improvements
|
||||||
* **Vanilla-Tweaks Support:** Optional integration with automatic application.
|
* **Automated Setup:** One-click patching for both CrossOver and Epoch installations.
|
||||||
* **Automated Setup:** One-click patching for both CrossOver and Turtle WoW installations.
|
|
||||||
* **Simple Interface:** Easy to use GUI with status indicators and configuration options.
|
* **Simple Interface:** Easy to use GUI with status indicators and configuration options.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Method 1: Using the Pre-built Application
|
### Method 1: Using the Pre-built Application
|
||||||
|
|
||||||
1. Launch `TurtleSilicon.app`.
|
1. Launch `EpochSilicon.app`.
|
||||||
* (The app is not signed, so you will get a "this app is damaged" message. Open Terminal and enter `xattr -cr /Applications/TurtleSilicon.app` to bypass it)
|
2. **Set CrossOver Path**:
|
||||||
3. **Set CrossOver Path**:
|
|
||||||
* If CrossOver is installed in the default location (`/Applications/CrossOver.app`), this path will be pre-filled.
|
* If CrossOver is installed in the default location (`/Applications/CrossOver.app`), this path will be pre-filled.
|
||||||
* Otherwise, click "Set/Change" and navigate to your `CrossOver.app` bundle.
|
* Otherwise, click "Set/Change" and navigate to your `CrossOver.app` bundle.
|
||||||
4. **Set TurtleWoW Directory Path**:
|
3. **Set Warcraft Directory Path**:
|
||||||
* Click "Set/Change" and select the folder where you have your Turtle WoW client files.
|
* Click "Set/Change" and select the folder where you have your WoW 3.3.5a client files.
|
||||||
5. **Apply Patches**:
|
4. **Apply Patches**:
|
||||||
* Click "Patch TurtleWoW".
|
* Click "Patch Epoch".
|
||||||
* Click "Patch CrossOver".
|
* Click "Patch CrossOver".
|
||||||
* Status indicators will turn green once patching is successful for each.
|
* Status indicators will turn green once patching is successful for each.
|
||||||
6. **Start RosettaX87 Service**:
|
6. **Start RosettaX87 Service**:
|
||||||
@@ -64,13 +52,11 @@ All credit for the core translation layer `winerosetta` and `rosettax87` goes to
|
|||||||
* This will run the RosettaX87 service in the background and is required for launching the game.
|
* This will run the RosettaX87 service in the background and is required for launching the game.
|
||||||
* The service will automatically stop when you close the launcher.
|
* The service will automatically stop when you close the launcher.
|
||||||
7. **Configure Options (Optional)**:
|
7. **Configure Options (Optional)**:
|
||||||
* **Enable vanilla-tweaks**: Check this box to use vanilla-tweaks, which provides various game improvements and fixes.
|
|
||||||
* If vanilla-tweaks hasn't been applied yet, TurtleSilicon will automatically offer to apply it when you launch the game.
|
|
||||||
* **Enable Metal Hud**: Shows FPS counter in-game.
|
* **Enable Metal Hud**: Shows FPS counter in-game.
|
||||||
* **Show Terminal**: Displays terminal output during game launch for debugging.
|
* **Show Terminal**: Displays terminal output during game launch for debugging.
|
||||||
8. **Launch Game**:
|
8. **Launch Game**:
|
||||||
* Once both paths are set, both components are patched, and the RosettaX87 service is running, the "Launch Game" button will become active. Click it.
|
* Once both paths are set, both components are patched, and the RosettaX87 service is running, the "Launch Game" button will become active. Click it.
|
||||||
9. **Enjoy**: Experience a significantly smoother Turtle WoW on your Apple Silicon Mac!
|
9. **Enjoy**: Experience a VM free, smoother Project Epoch on your Apple Silicon Mac!
|
||||||
|
|
||||||
### Method 2: Running from Source Code
|
### Method 2: Running from Source Code
|
||||||
|
|
||||||
@@ -78,12 +64,12 @@ If you prefer to run the application directly from source code:
|
|||||||
|
|
||||||
1. **Clone the repository**:
|
1. **Clone the repository**:
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/tairasu/TurtleSilicon.git
|
git clone https://git.burkey.co/eburk/EpochSilicon
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Navigate to the directory**:
|
2. **Navigate to the directory**:
|
||||||
```sh
|
```sh
|
||||||
cd TurtleSilicon
|
cd EpochSilicon
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Run the application**:
|
3. **Run the application**:
|
||||||
@@ -97,10 +83,7 @@ If you prefer to run the application directly from source code:
|
|||||||
|
|
||||||
## Recommended settings
|
## Recommended settings
|
||||||
|
|
||||||
1. Set "Terrain distance" as low as possible. This reduces the overhead stress on the CPU
|
TBD
|
||||||
2. Turn Vertex Animation Shaders on. Otherwise you get graphic glitches on custom models.
|
|
||||||
3. Set Multisampling to 24-bit color 24-bit depth **2x** to make portraits load
|
|
||||||
4. Add `SET shadowLOD "0"` to your Config.wtf inside the WTF directory
|
|
||||||
|
|
||||||
## Build Instructions
|
## Build Instructions
|
||||||
|
|
||||||
@@ -130,10 +113,10 @@ If you prefer to build manually:
|
|||||||
```sh
|
```sh
|
||||||
GOOS=darwin GOARCH=arm64 fyne package
|
GOOS=darwin GOARCH=arm64 fyne package
|
||||||
# Then manually copy the resource directories
|
# Then manually copy the resource directories
|
||||||
cp -R rosettax87 winerosetta TurtleSilicon.app/Contents/Resources/
|
cp -R rosettax87 winerosetta EpochSilicon.app/Contents/Resources/
|
||||||
```
|
```
|
||||||
|
|
||||||
In either case, this will create a `TurtleSilicon.app` file in the project directory, which you can then run.
|
In either case, this will create a `EpochSilicon.app` file in the project directory, which you can then run.
|
||||||
|
|
||||||
Make sure you have an `Icon.png` file in the root of the project directory before building.
|
Make sure you have an `Icon.png` file in the root of the project directory before building.
|
||||||
|
|
||||||
|
41
go.mod
41
go.mod
@@ -1,46 +1,49 @@
|
|||||||
module turtlesilicon
|
module epochsilicon
|
||||||
|
|
||||||
go 1.21
|
go 1.24.3
|
||||||
|
|
||||||
|
toolchain go1.24.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
fyne.io/fyne/v2 v2.6.1
|
fyne.io/fyne/v2 v2.6.1
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721041733-516a1f9b570e
|
||||||
github.com/zalando/go-keyring v0.2.6
|
github.com/zalando/go-keyring v0.2.6
|
||||||
|
howett.net/plist v1.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
al.essio.dev/pkg/shellescape v1.5.1 // indirect
|
al.essio.dev/pkg/shellescape v1.6.0 // indirect
|
||||||
fyne.io/systray v1.11.0 // indirect
|
fyne.io/systray v1.11.0 // indirect
|
||||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||||
github.com/danieljoos/wincred v1.2.2 // indirect
|
github.com/danieljoos/wincred v1.2.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fredbi/uri v1.1.0 // indirect
|
github.com/fredbi/uri v1.1.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/fyne-io/gl-js v0.1.0 // indirect
|
github.com/fyne-io/gl-js v0.2.0 // indirect
|
||||||
github.com/fyne-io/glfw-js v0.2.0 // indirect
|
github.com/fyne-io/glfw-js v0.3.0 // indirect
|
||||||
github.com/fyne-io/image v0.1.1 // indirect
|
github.com/fyne-io/image v0.1.1 // indirect
|
||||||
github.com/fyne-io/oksvg v0.1.0 // indirect
|
github.com/fyne-io/oksvg v0.1.0 // indirect
|
||||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
|
||||||
github.com/go-text/render v0.2.0 // indirect
|
github.com/go-text/render v0.2.0 // indirect
|
||||||
github.com/go-text/typesetting v0.2.1 // indirect
|
github.com/go-text/typesetting v0.3.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
|
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
|
||||||
github.com/hack-pad/safejs v0.1.0 // indirect
|
github.com/hack-pad/safejs v0.1.1 // indirect
|
||||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 // indirect
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
|
||||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rymdport/portal v0.4.1 // indirect
|
github.com/rymdport/portal v0.4.2 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/yuin/goldmark v1.7.8 // indirect
|
github.com/yuin/goldmark v1.7.12 // indirect
|
||||||
golang.org/x/image v0.24.0 // indirect
|
golang.org/x/image v0.29.0 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.42.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.34.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
howett.net/plist v1.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
38
go.sum
38
go.sum
@@ -1,11 +1,21 @@
|
|||||||
al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
|
al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
|
||||||
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
|
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
|
||||||
|
al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=
|
||||||
|
al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
|
||||||
fyne.io/fyne/v2 v2.6.1 h1:kjPJD4/rBS9m2nHJp+npPSuaK79yj6ObMTuzR6VQ1Is=
|
fyne.io/fyne/v2 v2.6.1 h1:kjPJD4/rBS9m2nHJp+npPSuaK79yj6ObMTuzR6VQ1Is=
|
||||||
fyne.io/fyne/v2 v2.6.1/go.mod h1:YZt7SksjvrSNJCwbWFV32WON3mE1Sr7L41D29qMZ/lU=
|
fyne.io/fyne/v2 v2.6.1/go.mod h1:YZt7SksjvrSNJCwbWFV32WON3mE1Sr7L41D29qMZ/lU=
|
||||||
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
||||||
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721033741-54cc1d9a552e h1:TiiZEnc7E3/wJ86qHckGoNht0snSUpQyY2cjxRUSs3Q=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721033741-54cc1d9a552e/go.mod h1:DgybCn9/LpJwvkrsyea9N2nWy/wuDgo6jkpOWYkTH3c=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721041340-6b6dbdda99e2 h1:89rNJ5p6gYJPZkeJM8nyRHMMgHErfVz8SdLcGiqEyBM=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721041340-6b6dbdda99e2/go.mod h1:DgybCn9/LpJwvkrsyea9N2nWy/wuDgo6jkpOWYkTH3c=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721041733-516a1f9b570e h1:WN1jlnC3sc/2ix70bzMPpzsPdsE/pqUIR/Vw8cVEkw8=
|
||||||
|
git.burkey.co/eburk/epochcli v0.0.0-20250721041733-516a1f9b570e/go.mod h1:DgybCn9/LpJwvkrsyea9N2nWy/wuDgo6jkpOWYkTH3c=
|
||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
|
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
|
||||||
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
|
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
|
||||||
@@ -17,10 +27,16 @@ github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
|
|||||||
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
|
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/fyne-io/gl-js v0.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
|
github.com/fyne-io/gl-js v0.1.0 h1:8luJzNs0ntEAJo+8x8kfUOXujUlP8gB3QMOxO2mUdpM=
|
||||||
github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
github.com/fyne-io/gl-js v0.1.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
||||||
|
github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
|
||||||
|
github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
|
||||||
github.com/fyne-io/glfw-js v0.2.0 h1:8GUZtN2aCoTPNqgRDxK5+kn9OURINhBEBc7M4O1KrmM=
|
github.com/fyne-io/glfw-js v0.2.0 h1:8GUZtN2aCoTPNqgRDxK5+kn9OURINhBEBc7M4O1KrmM=
|
||||||
github.com/fyne-io/glfw-js v0.2.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
|
github.com/fyne-io/glfw-js v0.2.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
|
||||||
|
github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
|
||||||
|
github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
|
||||||
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
|
github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
|
||||||
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
|
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
|
||||||
github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
|
github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
|
||||||
@@ -29,10 +45,14 @@ github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw
|
|||||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
|
||||||
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
|
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
|
||||||
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
|
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
|
||||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||||
|
github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
|
||||||
|
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
@@ -45,8 +65,12 @@ github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQb
|
|||||||
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
|
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
|
||||||
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
|
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
|
||||||
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||||
|
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
||||||
|
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
|
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
|
||||||
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
|
||||||
|
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
@@ -56,6 +80,8 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6
|
|||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||||
@@ -64,6 +90,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
github.com/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
||||||
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
|
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
||||||
|
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||||
@@ -74,16 +102,26 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
|
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||||
|
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
|
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
|
||||||
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
|
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
|
||||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||||
|
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
|
||||||
|
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
41
main.go
41
main.go
@@ -1,26 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/service"
|
"epochsilicon/pkg/service"
|
||||||
"turtlesilicon/pkg/ui"
|
"epochsilicon/pkg/ui"
|
||||||
"turtlesilicon/pkg/utils"
|
|
||||||
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
const appVersion = "1.2.4"
|
const appVersion = "0.1.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
TSApp := app.NewWithID("com.tairasu.turtlesilicon")
|
PEApp := app.NewWithID("com.burkey.epochsilicon")
|
||||||
TSWindow := TSApp.NewWindow("TurtleSilicon v" + appVersion)
|
PEWindow := PEApp.NewWindow("EpochSilicon v" + appVersion)
|
||||||
TSWindow.Resize(fyne.NewSize(650, 500))
|
PEWindow.Resize(fyne.NewSize(650, 500))
|
||||||
TSWindow.SetFixedSize(true)
|
PEWindow.SetFixedSize(true)
|
||||||
|
|
||||||
// Check for updates
|
/* Check for updates
|
||||||
|
// TODO: Fix updating
|
||||||
go func() {
|
go func() {
|
||||||
prefs, _ := utils.LoadPrefs()
|
prefs, _ := utils.LoadPrefs()
|
||||||
updateInfo, updateAvailable, err := utils.CheckForUpdateWithAssets(appVersion)
|
updateInfo, updateAvailable, err := utils.CheckForUpdateWithAssets(appVersion)
|
||||||
@@ -43,19 +40,21 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show enhanced update dialog
|
|
||||||
ui.ShowUpdateDialog(updateInfo, appVersion, TSWindow)
|
|
||||||
}()
|
|
||||||
|
|
||||||
content := ui.CreateUI(TSWindow)
|
// Show enhanced update dialog
|
||||||
TSWindow.SetContent(content)
|
ui.ShowUpdateDialog(updateInfo, appVersion, PEWindow)
|
||||||
|
}()
|
||||||
|
*/
|
||||||
|
|
||||||
|
content := ui.CreateUI(PEWindow)
|
||||||
|
PEWindow.SetContent(content)
|
||||||
|
|
||||||
// Set up cleanup when window closes
|
// Set up cleanup when window closes
|
||||||
TSWindow.SetCloseIntercept(func() {
|
PEWindow.SetCloseIntercept(func() {
|
||||||
debug.Println("Application closing, cleaning up RosettaX87 service...")
|
debug.Println("Application closing, cleaning up RosettaX87 service...")
|
||||||
service.CleanupService()
|
service.CleanupService()
|
||||||
TSApp.Quit()
|
PEApp.Quit()
|
||||||
})
|
})
|
||||||
|
|
||||||
TSWindow.ShowAndRun()
|
PEWindow.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
@@ -8,9 +8,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/paths" // Corrected import path
|
"epochsilicon/pkg/paths" // Corrected import path
|
||||||
"turtlesilicon/pkg/utils" // Corrected import path
|
"epochsilicon/pkg/utils" // Corrected import path
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
@@ -109,11 +109,11 @@ func LaunchGame(myWindow fyne.Window) {
|
|||||||
dialog.ShowError(fmt.Errorf("CrossOver path not set. Please set it in the patcher."), myWindow)
|
dialog.ShowError(fmt.Errorf("CrossOver path not set. Please set it in the patcher."), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it in the patcher."), myWindow)
|
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it in the patcher."), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !paths.PatchesAppliedTurtleWoW || !paths.PatchesAppliedCrossOver {
|
if !paths.PatchesAppliedEpoch || !paths.PatchesAppliedCrossOver {
|
||||||
confirmed := false
|
confirmed := false
|
||||||
dialog.ShowConfirm("Warning", "Not all patches confirmed applied. Continue with launch?", func(c bool) {
|
dialog.ShowConfirm("Warning", "Not all patches confirmed applied. Continue with launch?", func(c bool) {
|
||||||
confirmed = c
|
confirmed = c
|
||||||
@@ -132,28 +132,9 @@ func LaunchGame(myWindow fyne.Window) {
|
|||||||
}
|
}
|
||||||
gameMutex.Unlock()
|
gameMutex.Unlock()
|
||||||
|
|
||||||
debug.Println("Preparing to launch TurtleSilicon...")
|
debug.Println("Preparing to launch EpochSilicon...")
|
||||||
|
|
||||||
// Determine which WoW executable to use based on vanilla-tweaks preference
|
wowExePath := filepath.Join(paths.EpochPath, "Project-Epoch.exe")
|
||||||
var wowExePath string
|
|
||||||
if EnableVanillaTweaks {
|
|
||||||
if !CheckForWoWTweakedExecutable() {
|
|
||||||
// Show dialog asking if user wants us to apply vanilla-tweaks
|
|
||||||
HandleVanillaTweaksRequest(myWindow, func() {
|
|
||||||
// After successful patching, continue with launch using the tweaked executable
|
|
||||||
wowTweakedExePath := GetWoWTweakedExecutablePath()
|
|
||||||
if wowTweakedExePath != "" {
|
|
||||||
continueLaunch(myWindow, wowTweakedExePath)
|
|
||||||
} else {
|
|
||||||
dialog.ShowError(fmt.Errorf("failed to find WoW-tweaked.exe after patching"), myWindow)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return // Exit early since dialog will handle the continuation
|
|
||||||
}
|
|
||||||
wowExePath = GetWoWTweakedExecutablePath()
|
|
||||||
} else {
|
|
||||||
wowExePath = filepath.Join(paths.TurtlewowPath, "WoW.exe")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continue with normal launch process
|
// Continue with normal launch process
|
||||||
continueLaunch(myWindow, wowExePath)
|
continueLaunch(myWindow, wowExePath)
|
||||||
@@ -161,12 +142,12 @@ func LaunchGame(myWindow fyne.Window) {
|
|||||||
|
|
||||||
// continueLaunch continues the game launch process with the specified executable
|
// continueLaunch continues the game launch process with the specified executable
|
||||||
func continueLaunch(myWindow fyne.Window, wowExePath string) {
|
func continueLaunch(myWindow fyne.Window, wowExePath string) {
|
||||||
rosettaInTurtlePath := filepath.Join(paths.TurtlewowPath, "rosettax87")
|
rosettaInEpochPath := filepath.Join(paths.EpochPath, "rosettax87")
|
||||||
rosettaExecutable := filepath.Join(rosettaInTurtlePath, "rosettax87")
|
rosettaExecutable := filepath.Join(rosettaInEpochPath, "rosettax87")
|
||||||
wineloader2Path := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application", "wineloader2")
|
wineloader2Path := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application", "wineloader2")
|
||||||
|
|
||||||
if !utils.PathExists(rosettaExecutable) {
|
if !utils.PathExists(rosettaExecutable) {
|
||||||
dialog.ShowError(fmt.Errorf("rosetta executable not found at %s. Ensure TurtleWoW patching was successful", rosettaExecutable), myWindow)
|
dialog.ShowError(fmt.Errorf("rosetta executable not found at %s. Ensure Epoch patching was successful", rosettaExecutable), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !utils.PathExists(wineloader2Path) {
|
if !utils.PathExists(wineloader2Path) {
|
||||||
@@ -174,13 +155,13 @@ func continueLaunch(myWindow fyne.Window, wowExePath string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !utils.PathExists(wowExePath) {
|
if !utils.PathExists(wowExePath) {
|
||||||
dialog.ShowError(fmt.Errorf("WoW executable not found at %s. Ensure your TurtleWoW directory is correct", wowExePath), myWindow)
|
dialog.ShowError(fmt.Errorf("WoW executable not found at %s. Ensure your Epoch directory is correct", wowExePath), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-delete WDB directory if enabled
|
// Auto-delete WDB directory if enabled
|
||||||
if AutoDeleteWdb {
|
if AutoDeleteWdb {
|
||||||
wdbPath := filepath.Join(paths.TurtlewowPath, "WDB")
|
wdbPath := filepath.Join(paths.EpochPath, "WDB")
|
||||||
if utils.DirExists(wdbPath) {
|
if utils.DirExists(wdbPath) {
|
||||||
debug.Printf("Auto-deleting WDB directory: %s", wdbPath)
|
debug.Printf("Auto-deleting WDB directory: %s", wdbPath)
|
||||||
if err := os.RemoveAll(wdbPath); err != nil {
|
if err := os.RemoveAll(wdbPath); err != nil {
|
||||||
@@ -197,8 +178,8 @@ func continueLaunch(myWindow fyne.Window, wowExePath string) {
|
|||||||
// Since RosettaX87 service is already running, we can directly launch WoW
|
// Since RosettaX87 service is already running, we can directly launch WoW
|
||||||
debug.Println("RosettaX87 service is running. Proceeding to launch WoW.")
|
debug.Println("RosettaX87 service is running. Proceeding to launch WoW.")
|
||||||
|
|
||||||
if paths.CrossoverPath == "" || paths.TurtlewowPath == "" {
|
if paths.CrossoverPath == "" || paths.EpochPath == "" {
|
||||||
dialog.ShowError(fmt.Errorf("CrossOver path or TurtleWoW path is not set. Cannot launch WoW."), myWindow)
|
dialog.ShowError(fmt.Errorf("CrossOver path or Epoch path is not set. Cannot launch WoW."), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +195,7 @@ func continueLaunch(myWindow fyne.Window, wowExePath string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shellCmd := fmt.Sprintf(`cd %s && %s %s %s %s`,
|
shellCmd := fmt.Sprintf(`cd %s && %s %s %s %s`,
|
||||||
utils.QuotePathForShell(paths.TurtlewowPath),
|
utils.QuotePathForShell(paths.EpochPath),
|
||||||
envVars,
|
envVars,
|
||||||
utils.QuotePathForShell(rosettaExecutable),
|
utils.QuotePathForShell(rosettaExecutable),
|
||||||
utils.QuotePathForShell(wineloader2Path),
|
utils.QuotePathForShell(wineloader2Path),
|
||||||
|
@@ -7,8 +7,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/paths"
|
"epochsilicon/pkg/paths"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RecommendedSettings contains the recommended graphics settings for optimal performance
|
// RecommendedSettings contains the recommended graphics settings for optimal performance
|
||||||
@@ -24,12 +24,12 @@ var RecommendedSettings = map[string]string{
|
|||||||
// CheckRecommendedSettings reads the Config.wtf file and checks if all recommended settings are applied
|
// CheckRecommendedSettings reads the Config.wtf file and checks if all recommended settings are applied
|
||||||
// Returns true if all settings are correctly applied, false otherwise
|
// Returns true if all settings are correctly applied, false otherwise
|
||||||
func CheckRecommendedSettings() bool {
|
func CheckRecommendedSettings() bool {
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
debug.Printf("TurtleWoW path not set, cannot check Config.wtf")
|
debug.Printf("Epoch path not set, cannot check Config.wtf")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
configPath := filepath.Join(paths.EpochPath, "WTF", "Config.wtf")
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||||
debug.Printf("Config.wtf not found at %s", configPath)
|
debug.Printf("Config.wtf not found at %s", configPath)
|
||||||
@@ -73,11 +73,11 @@ func isSettingCorrect(configText, setting, expectedValue string) bool {
|
|||||||
|
|
||||||
// ApplyRecommendedSettings applies all recommended graphics settings to Config.wtf
|
// ApplyRecommendedSettings applies all recommended graphics settings to Config.wtf
|
||||||
func ApplyRecommendedSettings() error {
|
func ApplyRecommendedSettings() error {
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
return fmt.Errorf("Epoch path not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
configPath := filepath.Join(paths.EpochPath, "WTF", "Config.wtf")
|
||||||
|
|
||||||
// Create WTF directory if it doesn't exist
|
// Create WTF directory if it doesn't exist
|
||||||
wtfDir := filepath.Dir(configPath)
|
wtfDir := filepath.Dir(configPath)
|
||||||
|
@@ -1,205 +0,0 @@
|
|||||||
package launcher
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
|
||||||
"turtlesilicon/pkg/paths"
|
|
||||||
"turtlesilicon/pkg/utils"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
|
||||||
"fyne.io/fyne/v2/dialog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ApplyVanillaTweaks applies vanilla-tweaks to WoW.exe to create WoW-tweaked.exe
|
|
||||||
func ApplyVanillaTweaks(myWindow fyne.Window) error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
if paths.CrossoverPath == "" {
|
|
||||||
return fmt.Errorf("CrossOver path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current working directory (where the app executable is located)
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get executable path: %v", err)
|
|
||||||
}
|
|
||||||
appDir := filepath.Dir(execPath)
|
|
||||||
|
|
||||||
// Check if we're in development mode
|
|
||||||
vanillaTweaksPath := filepath.Join(appDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
if !utils.PathExists(vanillaTweaksPath) {
|
|
||||||
// Try relative path from current working directory (for development)
|
|
||||||
workingDir, _ := os.Getwd()
|
|
||||||
vanillaTweaksPath = filepath.Join(workingDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
if !utils.PathExists(vanillaTweaksPath) {
|
|
||||||
return fmt.Errorf("vanilla-tweaks.exe not found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wowExePath := filepath.Join(paths.TurtlewowPath, "WoW.exe")
|
|
||||||
wineloader2Path := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application", "wineloader2")
|
|
||||||
|
|
||||||
if !utils.PathExists(wowExePath) {
|
|
||||||
return fmt.Errorf("WoW.exe not found at %s", wowExePath)
|
|
||||||
}
|
|
||||||
if !utils.PathExists(wineloader2Path) {
|
|
||||||
return fmt.Errorf("wineloader2 not found at %s", wineloader2Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, copy vanilla-tweaks.exe to the TurtleWoW directory temporarily
|
|
||||||
tempVanillaTweaksPath := filepath.Join(paths.TurtlewowPath, "vanilla-tweaks.exe")
|
|
||||||
|
|
||||||
// Copy vanilla-tweaks.exe to TurtleWoW directory
|
|
||||||
debug.Printf("Copying vanilla-tweaks.exe from %s to %s", vanillaTweaksPath, tempVanillaTweaksPath)
|
|
||||||
sourceFile, err := os.Open(vanillaTweaksPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open vanilla-tweaks.exe: %v", err)
|
|
||||||
}
|
|
||||||
defer sourceFile.Close()
|
|
||||||
|
|
||||||
destFile, err := os.Create(tempVanillaTweaksPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create temporary vanilla-tweaks.exe: %v", err)
|
|
||||||
}
|
|
||||||
defer destFile.Close()
|
|
||||||
|
|
||||||
_, err = destFile.ReadFrom(sourceFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to copy vanilla-tweaks.exe: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the copied file is executable
|
|
||||||
if err := os.Chmod(tempVanillaTweaksPath, 0755); err != nil {
|
|
||||||
debug.Printf("Warning: failed to set executable permission on vanilla-tweaks.exe: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the command to apply vanilla-tweaks using the correct format:
|
|
||||||
// cd "path" && "wineloader2" ./vanilla-tweaks.exe --no-frilldistance -no-farclip ./WoW.exe
|
|
||||||
shellCmd := fmt.Sprintf(`cd %s && %s ./vanilla-tweaks.exe --no-frilldistance --no-farclip ./WoW.exe`,
|
|
||||||
utils.QuotePathForShell(paths.TurtlewowPath),
|
|
||||||
utils.QuotePathForShell(wineloader2Path))
|
|
||||||
|
|
||||||
debug.Printf("Applying vanilla-tweaks with command: %s", shellCmd)
|
|
||||||
|
|
||||||
// Execute the command
|
|
||||||
cmd := exec.Command("sh", "-c", shellCmd)
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
|
|
||||||
debug.Printf("vanilla-tweaks command output: %s", string(output))
|
|
||||||
|
|
||||||
// Clean up the temporary vanilla-tweaks.exe file
|
|
||||||
if cleanupErr := os.Remove(tempVanillaTweaksPath); cleanupErr != nil {
|
|
||||||
debug.Printf("Warning: failed to clean up temporary vanilla-tweaks.exe: %v", cleanupErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always check if the output file was created, regardless of exit code
|
|
||||||
// Some Wine programs report error exit codes even when they succeed
|
|
||||||
foundPath := GetWoWTweakedExecutablePath()
|
|
||||||
if foundPath == "" {
|
|
||||||
// Only report error if no output file was created
|
|
||||||
if err != nil {
|
|
||||||
debug.Printf("vanilla-tweaks command failed: %v", err)
|
|
||||||
return fmt.Errorf("failed to apply vanilla-tweaks: %v\nOutput: %s", err, string(output))
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("vanilla-tweaks completed but WoW-tweaked.exe was not created\nOutput: %s", string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we found the file but there was an error code, log it as a warning
|
|
||||||
if err != nil {
|
|
||||||
debug.Printf("vanilla-tweaks reported error but output file was created: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Println("vanilla-tweaks applied successfully")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckForVanillaTweaksExecutable checks if vanilla-tweaks.exe exists and is accessible
|
|
||||||
func CheckForVanillaTweaksExecutable() bool {
|
|
||||||
// Get the current working directory (where the app executable is located)
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
appDir := filepath.Dir(execPath)
|
|
||||||
|
|
||||||
// Check if we're in development mode (running from VSCode)
|
|
||||||
vanillaTweaksPath := filepath.Join(appDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
if utils.PathExists(vanillaTweaksPath) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try relative path from current working directory (for development)
|
|
||||||
workingDir, _ := os.Getwd()
|
|
||||||
vanillaTweaksPath = filepath.Join(workingDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
return utils.PathExists(vanillaTweaksPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVanillaTweaksExecutablePath returns the path to vanilla-tweaks.exe if it exists
|
|
||||||
func GetVanillaTweaksExecutablePath() (string, error) {
|
|
||||||
// Get the current working directory (where the app executable is located)
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to get executable path: %v", err)
|
|
||||||
}
|
|
||||||
appDir := filepath.Dir(execPath)
|
|
||||||
|
|
||||||
// Check if we're in development mode (running from VSCode)
|
|
||||||
vanillaTweaksPath := filepath.Join(appDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
if utils.PathExists(vanillaTweaksPath) {
|
|
||||||
return vanillaTweaksPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try relative path from current working directory (for development)
|
|
||||||
workingDir, _ := os.Getwd()
|
|
||||||
vanillaTweaksPath = filepath.Join(workingDir, "winerosetta", "vanilla-tweaks.exe")
|
|
||||||
if utils.PathExists(vanillaTweaksPath) {
|
|
||||||
return vanillaTweaksPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", fmt.Errorf("vanilla-tweaks.exe not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckForWoWTweakedExecutable checks if WoW_tweaked.exe exists in the TurtleWoW directory
|
|
||||||
func CheckForWoWTweakedExecutable() bool {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
testPath := filepath.Join(paths.TurtlewowPath, "WoW_tweaked.exe")
|
|
||||||
return utils.PathExists(testPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWoWTweakedExecutablePath returns the path to the WoW_tweaked.exe file if it exists
|
|
||||||
func GetWoWTweakedExecutablePath() string {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
testPath := filepath.Join(paths.TurtlewowPath, "WoW_tweaked.exe")
|
|
||||||
if utils.PathExists(testPath) {
|
|
||||||
return testPath
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleVanillaTweaksRequest handles the case when vanilla-tweaks is enabled but WoW-tweaked.exe doesn't exist
|
|
||||||
func HandleVanillaTweaksRequest(myWindow fyne.Window, callback func()) {
|
|
||||||
dialog.ShowConfirm("Vanilla-tweaks not found",
|
|
||||||
"WoW-tweaked.exe was not found in your TurtleWoW directory.\n\nWould you like TurtleSilicon to automatically apply vanilla-tweaks for you?",
|
|
||||||
func(confirmed bool) {
|
|
||||||
if confirmed {
|
|
||||||
if err := ApplyVanillaTweaks(myWindow); err != nil {
|
|
||||||
dialog.ShowError(fmt.Errorf("failed to apply vanilla-tweaks: %v", err), myWindow)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// After successful patching, execute the callback
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}, myWindow)
|
|
||||||
}
|
|
@@ -4,37 +4,37 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.burkey.co/eburk/epochcli/pkg/epoch"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/paths" // Corrected import path
|
"epochsilicon/pkg/paths" // Corrected import path
|
||||||
"turtlesilicon/pkg/utils" // Corrected import path
|
"epochsilicon/pkg/utils" // Corrected import path
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
func PatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
|
||||||
debug.Println("Patch TurtleWoW clicked")
|
debug.Println("Patch Epoch clicked")
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first."), myWindow)
|
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first."), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetWinerosettaDll := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
|
targetWinerosettaDll := filepath.Join(paths.EpochPath, "winerosetta.dll")
|
||||||
targetD3d9Dll := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
|
targetD3d9Dll := filepath.Join(paths.EpochPath, "d3d9.dll")
|
||||||
targetLibSiliconPatchDll := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
targetRosettaX87Dir := filepath.Join(paths.EpochPath, "rosettax87")
|
||||||
targetRosettaX87Dir := filepath.Join(paths.TurtlewowPath, "rosettax87")
|
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
filesToCopy := map[string]string{
|
filesToCopy := map[string]string{
|
||||||
"winerosetta/winerosetta.dll": targetWinerosettaDll,
|
"winerosetta/winerosetta.dll": targetWinerosettaDll,
|
||||||
"winerosetta/d3d9.dll": targetD3d9Dll,
|
"winerosetta/d3d9.dll": targetD3d9Dll,
|
||||||
"winerosetta/libSiliconPatch.dll": targetLibSiliconPatchDll,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for resourceName, destPath := range filesToCopy {
|
for resourceName, destPath := range filesToCopy {
|
||||||
@@ -57,7 +57,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
|
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
|
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -131,7 +131,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
|
errMsg := fmt.Sprintf("failed to copy bundled resource %s to %s: %v", resourceName, destPath, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
errMsg := fmt.Sprintf("failed to set execute permission for %s: %v", destPath, err)
|
errMsg := fmt.Sprintf("failed to set execute permission for %s: %v", destPath, err)
|
||||||
dialog.ShowError(errors.New(errMsg), myWindow)
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
debug.Println(errMsg)
|
debug.Println(errMsg)
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
paths.PatchesAppliedEpoch = false
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -153,36 +153,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
|
|
||||||
debug.Printf("Checking dlls.txt file at: %s", dllsTextFile)
|
debug.Printf("Checking dlls.txt file at: %s", dllsTextFile)
|
||||||
winerosettaEntry := "winerosetta.dll"
|
winerosettaEntry := "winerosetta.dll"
|
||||||
libSiliconPatchEntry := "libSiliconPatch.dll"
|
|
||||||
needsWinerosettaUpdate := true
|
needsWinerosettaUpdate := true
|
||||||
needsLibSiliconPatchUpdate := true
|
|
||||||
|
|
||||||
// Check user's preference for libSiliconPatch and shadowLOD
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
|
|
||||||
// Enable by default unless user has explicitly disabled them
|
|
||||||
shouldEnableLibSiliconPatch := true
|
|
||||||
shouldEnableShadowLOD := true
|
|
||||||
|
|
||||||
// If user has manually disabled these settings, respect their choice
|
|
||||||
if prefs.UserDisabledLibSiliconPatch {
|
|
||||||
shouldEnableLibSiliconPatch = false
|
|
||||||
debug.Printf("libSiliconPatch disabled by user choice")
|
|
||||||
} else {
|
|
||||||
// Enable by default and update preferences
|
|
||||||
prefs.EnableLibSiliconPatch = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if prefs.UserDisabledShadowLOD {
|
|
||||||
shouldEnableShadowLOD = false
|
|
||||||
debug.Printf("shadowLOD disabled by user choice")
|
|
||||||
} else {
|
|
||||||
// Enable by default and update preferences
|
|
||||||
prefs.SetShadowLOD0 = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save updated preferences
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
|
|
||||||
if fileContentBytes, err := os.ReadFile(dllsTextFile); err == nil {
|
if fileContentBytes, err := os.ReadFile(dllsTextFile); err == nil {
|
||||||
fileContent := string(fileContentBytes)
|
fileContent := string(fileContentBytes)
|
||||||
@@ -190,21 +161,11 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
debug.Printf("dlls.txt already contains %s", winerosettaEntry)
|
debug.Printf("dlls.txt already contains %s", winerosettaEntry)
|
||||||
needsWinerosettaUpdate = false
|
needsWinerosettaUpdate = false
|
||||||
}
|
}
|
||||||
if strings.Contains(fileContent, libSiliconPatchEntry) {
|
|
||||||
debug.Printf("dlls.txt already contains %s", libSiliconPatchEntry)
|
|
||||||
needsLibSiliconPatchUpdate = false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
debug.Printf("dlls.txt not found, will create a new one")
|
debug.Printf("dlls.txt not found, will create a new one")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add libSiliconPatch if user wants it enabled
|
if needsWinerosettaUpdate {
|
||||||
if !shouldEnableLibSiliconPatch {
|
|
||||||
needsLibSiliconPatchUpdate = false
|
|
||||||
debug.Printf("libSiliconPatch disabled by user preference, will not add to dlls.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
if needsWinerosettaUpdate || needsLibSiliconPatchUpdate {
|
|
||||||
var fileContentBytes []byte
|
var fileContentBytes []byte
|
||||||
var err error
|
var err error
|
||||||
if utils.PathExists(dllsTextFile) {
|
if utils.PathExists(dllsTextFile) {
|
||||||
@@ -229,12 +190,6 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
debug.Printf("Adding %s to dlls.txt", winerosettaEntry)
|
debug.Printf("Adding %s to dlls.txt", winerosettaEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if needsLibSiliconPatchUpdate && shouldEnableLibSiliconPatch {
|
|
||||||
if !strings.Contains(updatedContent, libSiliconPatchEntry+"\n") {
|
|
||||||
updatedContent += libSiliconPatchEntry + "\n"
|
|
||||||
debug.Printf("Adding %s to dlls.txt", libSiliconPatchEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.WriteFile(dllsTextFile, []byte(updatedContent), 0644); err != nil {
|
if err := os.WriteFile(dllsTextFile, []byte(updatedContent), 0644); err != nil {
|
||||||
errMsg := fmt.Sprintf("failed to update dlls.txt: %v", err)
|
errMsg := fmt.Sprintf("failed to update dlls.txt: %v", err)
|
||||||
@@ -245,35 +200,31 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user has disabled libSiliconPatch, make sure it's removed from dlls.txt
|
debug.Println("Downloading updates from Project Epoch servers.")
|
||||||
if !shouldEnableLibSiliconPatch {
|
|
||||||
if err := disableLibSiliconPatchInDlls(); err != nil {
|
|
||||||
debug.Printf("Warning: failed to remove libSiliconPatch from dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always apply vertex animation shaders setting to Config.wtf
|
// TODO: Change from dialog to pulsing animation
|
||||||
if err := applyVertexAnimShadersSetting(); err != nil {
|
dialog.ShowInformation("Downloading patches", "Downloading patches for Project Epoch, this will take some time. Please wait until the status changes to \"Patched\"", myWindow)
|
||||||
debug.Printf("Warning: failed to apply vertex animation shaders setting to Config.wtf: %v", err)
|
paths.DownloadingPatches = true
|
||||||
// Continue with patching even if Config.wtf update fails
|
go func() {
|
||||||
}
|
stats, err := epoch.Update(paths.EpochPath, true, true, false)
|
||||||
|
if err != nil {
|
||||||
// Apply shadowLOD setting to Config.wtf for FPS optimization
|
errMsg := fmt.Sprintf("failed to update Epoch files: %v", err)
|
||||||
// Use shouldEnableShadowLOD which accounts for first-time patching
|
fyne.Do(func() {
|
||||||
if shouldEnableShadowLOD {
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
||||||
if err := applyShadowLODSetting(); err != nil {
|
})
|
||||||
debug.Printf("Warning: failed to apply shadowLOD setting to Config.wtf: %v", err)
|
paths.DownloadingPatches = false
|
||||||
// Continue with patching even if Config.wtf update fails
|
log.Println(errMsg)
|
||||||
}
|
}
|
||||||
} else {
|
log.Printf("Successfully updated %d Epoch files", stats.Updated)
|
||||||
// If user has disabled shadowLOD, make sure it's removed from Config.wtf
|
debug.Println("Epoch patching with bundled resources completed successfully.")
|
||||||
if err := removeShadowLODSetting(); err != nil {
|
fyne.Do(func() {
|
||||||
debug.Printf("Warning: failed to remove shadowLOD setting from Config.wtf: %v", err)
|
dialog.ShowInformation("Success", "Epoch patching process completed.", myWindow)
|
||||||
}
|
})
|
||||||
}
|
fyne.DoAndWait(func() {
|
||||||
|
paths.DownloadingPatches = false
|
||||||
debug.Println("TurtleWoW patching with bundled resources completed successfully.")
|
updateAllStatuses()
|
||||||
dialog.ShowInformation("Success", "TurtleWoW patching process completed using bundled resources.", myWindow)
|
})
|
||||||
|
}()
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +250,7 @@ func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
if err := utils.CopyFile(wineloaderOrig, wineloaderCopy); err != nil {
|
if err := utils.CopyFile(wineloaderOrig, wineloaderCopy); err != nil {
|
||||||
errMsg := fmt.Sprintf("failed to copy wineloader: %v", err)
|
errMsg := fmt.Sprintf("failed to copy wineloader: %v", err)
|
||||||
if strings.Contains(err.Error(), "operation not permitted") {
|
if strings.Contains(err.Error(), "operation not permitted") {
|
||||||
errMsg += "\n\nSolution: Open System Settings, go to Privacy & Security > App Management, and enable TurtleSilicon."
|
errMsg += "\n\nSolution: Open System Settings, go to Privacy & Security > App Management, and enable EpochSilicon."
|
||||||
}
|
}
|
||||||
dialog.ShowError(fmt.Errorf(errMsg), myWindow)
|
dialog.ShowError(fmt.Errorf(errMsg), myWindow)
|
||||||
paths.PatchesAppliedCrossOver = false
|
paths.PatchesAppliedCrossOver = false
|
||||||
@@ -339,19 +290,18 @@ func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
func UnpatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
|
||||||
debug.Println("Unpatch TurtleWoW clicked")
|
debug.Println("Unpatch Epoch clicked")
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first."), myWindow)
|
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first."), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Files to remove
|
// Files to remove
|
||||||
winerosettaDllPath := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
|
winerosettaDllPath := filepath.Join(paths.EpochPath, "winerosetta.dll")
|
||||||
d3d9DllPath := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
|
d3d9DllPath := filepath.Join(paths.EpochPath, "d3d9.dll")
|
||||||
libSiliconPatchDllPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
rosettaX87DirPath := filepath.Join(paths.EpochPath, "rosettax87")
|
||||||
rosettaX87DirPath := filepath.Join(paths.TurtlewowPath, "rosettax87")
|
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
|
|
||||||
// Remove the rosettaX87 directory
|
// Remove the rosettaX87 directory
|
||||||
if utils.DirExists(rosettaX87DirPath) {
|
if utils.DirExists(rosettaX87DirPath) {
|
||||||
@@ -366,7 +316,7 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove DLL files
|
// Remove DLL files
|
||||||
filesToRemove := []string{winerosettaDllPath, d3d9DllPath, libSiliconPatchDllPath}
|
filesToRemove := []string{winerosettaDllPath, d3d9DllPath}
|
||||||
for _, file := range filesToRemove {
|
for _, file := range filesToRemove {
|
||||||
if utils.PathExists(file) {
|
if utils.PathExists(file) {
|
||||||
debug.Printf("Removing file: %s", file)
|
debug.Printf("Removing file: %s", file)
|
||||||
@@ -380,7 +330,7 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update dlls.txt file - remove winerosetta.dll and libSiliconPatch.dll entries
|
// Update dlls.txt file - remove winerosetta.dll
|
||||||
if utils.PathExists(dllsTextFile) {
|
if utils.PathExists(dllsTextFile) {
|
||||||
debug.Printf("Updating dlls.txt file: %s", dllsTextFile)
|
debug.Printf("Updating dlls.txt file: %s", dllsTextFile)
|
||||||
content, err := os.ReadFile(dllsTextFile)
|
content, err := os.ReadFile(dllsTextFile)
|
||||||
@@ -394,7 +344,7 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
trimmedLine := strings.TrimSpace(line)
|
trimmedLine := strings.TrimSpace(line)
|
||||||
if trimmedLine != "winerosetta.dll" && trimmedLine != "libSiliconPatch.dll" {
|
if trimmedLine != "winerosetta.dll" {
|
||||||
filteredLines = append(filteredLines, line)
|
filteredLines = append(filteredLines, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,18 +360,9 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove shadowLOD setting from Config.wtf - only if it was applied via graphics settings
|
debug.Println("Epoch unpatching completed successfully.")
|
||||||
prefs, _ := utils.LoadPrefs()
|
paths.PatchesAppliedEpoch = false
|
||||||
if prefs.SetShadowLOD0 {
|
dialog.ShowInformation("Success", "Epoch unpatching process completed.", myWindow)
|
||||||
if err := removeShadowLODSetting(); err != nil {
|
|
||||||
debug.Printf("Warning: failed to remove shadowLOD setting from Config.wtf: %v", err)
|
|
||||||
// Continue with unpatching even if Config.wtf update fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Println("TurtleWoW unpatching completed successfully.")
|
|
||||||
paths.PatchesAppliedTurtleWoW = false
|
|
||||||
dialog.ShowInformation("Success", "TurtleWoW unpatching process completed.", myWindow)
|
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,42 +396,6 @@ func UnpatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
|
|||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyShadowLODSetting applies the shadowLOD setting to Config.wtf for FPS optimization
|
|
||||||
func applyShadowLODSetting() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
// Create WTF directory if it doesn't exist
|
|
||||||
wtfDir := filepath.Dir(configPath)
|
|
||||||
if err := os.MkdirAll(wtfDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("failed to create WTF directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var configText string
|
|
||||||
|
|
||||||
// Read existing config if it exists
|
|
||||||
if content, err := os.ReadFile(configPath); err == nil {
|
|
||||||
configText = string(content)
|
|
||||||
} else {
|
|
||||||
debug.Printf("Config.wtf not found, creating new file")
|
|
||||||
configText = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply shadowLOD setting
|
|
||||||
configText = updateOrAddConfigSetting(configText, "shadowLOD", "0")
|
|
||||||
|
|
||||||
// Write the updated config back to file
|
|
||||||
if err := os.WriteFile(configPath, []byte(configText), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Successfully applied shadowLOD setting to Config.wtf")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateOrAddConfigSetting updates an existing setting or adds a new one if it doesn't exist
|
// updateOrAddConfigSetting updates an existing setting or adds a new one if it doesn't exist
|
||||||
func updateOrAddConfigSetting(configText, setting, value string) string {
|
func updateOrAddConfigSetting(configText, setting, value string) string {
|
||||||
// Create regex pattern to match the setting
|
// Create regex pattern to match the setting
|
||||||
@@ -529,27 +434,6 @@ func removeConfigSetting(configText, setting string) string {
|
|||||||
return configText
|
return configText
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckShadowLODSetting checks if the shadowLOD setting is correctly applied in Config.wtf
|
|
||||||
func CheckShadowLODSetting() bool {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
configText := string(content)
|
|
||||||
return isConfigSettingCorrect(configText, "shadowLOD", "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// isConfigSettingCorrect checks if a specific setting has the correct value in the config text
|
// isConfigSettingCorrect checks if a specific setting has the correct value in the config text
|
||||||
func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
|
func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
|
||||||
// Create regex pattern to match the setting
|
// Create regex pattern to match the setting
|
||||||
@@ -564,365 +448,3 @@ func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
|
|||||||
currentValue := matches[1]
|
currentValue := matches[1]
|
||||||
return currentValue == expectedValue
|
return currentValue == expectedValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeShadowLODSetting removes the shadowLOD setting from Config.wtf
|
|
||||||
func removeShadowLODSetting() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
||||||
debug.Printf("Config.wtf not found, nothing to remove")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
configText := string(content)
|
|
||||||
|
|
||||||
// Remove shadowLOD setting if it exists
|
|
||||||
pattern := fmt.Sprintf(`SET\s+%s\s+"[^"]*"[\r\n]*`, regexp.QuoteMeta("shadowLOD"))
|
|
||||||
re := regexp.MustCompile(pattern)
|
|
||||||
|
|
||||||
if re.MatchString(configText) {
|
|
||||||
configText = re.ReplaceAllString(configText, "")
|
|
||||||
debug.Printf("Removed shadowLOD setting from Config.wtf")
|
|
||||||
|
|
||||||
// Write the updated config back to file
|
|
||||||
if err := os.WriteFile(configPath, []byte(configText), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
debug.Printf("Successfully updated Config.wtf")
|
|
||||||
} else {
|
|
||||||
debug.Printf("shadowLOD setting not found in Config.wtf, nothing to remove")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// applyVertexAnimShadersSetting applies the vertex animation shaders setting to Config.wtf
|
|
||||||
func applyVertexAnimShadersSetting() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
// Create WTF directory if it doesn't exist
|
|
||||||
wtfDir := filepath.Dir(configPath)
|
|
||||||
if err := os.MkdirAll(wtfDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("failed to create WTF directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var configText string
|
|
||||||
|
|
||||||
// Read existing config if it exists
|
|
||||||
if content, err := os.ReadFile(configPath); err == nil {
|
|
||||||
configText = string(content)
|
|
||||||
} else {
|
|
||||||
debug.Printf("Config.wtf not found, creating new file")
|
|
||||||
configText = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply vertex animation shaders setting
|
|
||||||
configText = updateOrAddConfigSetting(configText, "M2UseShaders", "1")
|
|
||||||
|
|
||||||
// Write the updated config back to file
|
|
||||||
if err := os.WriteFile(configPath, []byte(configText), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Successfully applied vertex animation shaders setting to Config.wtf")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyGraphicsSettings applies the selected graphics settings to Config.wtf
|
|
||||||
func ApplyGraphicsSettings(myWindow fyne.Window) error {
|
|
||||||
prefs, err := utils.LoadPrefs()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load preferences: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
// Create WTF directory if it doesn't exist
|
|
||||||
wtfDir := filepath.Dir(configPath)
|
|
||||||
if err := os.MkdirAll(wtfDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("failed to create WTF directory: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var configText string
|
|
||||||
|
|
||||||
// Read existing config if it exists
|
|
||||||
if content, err := os.ReadFile(configPath); err == nil {
|
|
||||||
configText = string(content)
|
|
||||||
} else {
|
|
||||||
debug.Printf("Config.wtf not found, creating new file")
|
|
||||||
configText = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply or remove graphics settings based on user preferences
|
|
||||||
if prefs.ReduceTerrainDistance {
|
|
||||||
configText = updateOrAddConfigSetting(configText, "farclip", "177")
|
|
||||||
} else {
|
|
||||||
configText = removeConfigSetting(configText, "farclip")
|
|
||||||
}
|
|
||||||
|
|
||||||
if prefs.SetMultisampleTo2x {
|
|
||||||
configText = updateOrAddConfigSetting(configText, "gxMultisample", "2")
|
|
||||||
} else {
|
|
||||||
configText = removeConfigSetting(configText, "gxMultisample")
|
|
||||||
}
|
|
||||||
|
|
||||||
if prefs.SetShadowLOD0 {
|
|
||||||
configText = updateOrAddConfigSetting(configText, "shadowLOD", "0")
|
|
||||||
} else {
|
|
||||||
configText = removeConfigSetting(configText, "shadowLOD")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle libSiliconPatch.dll in dlls.txt (only if DLL exists)
|
|
||||||
libSiliconPatchPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
|
||||||
if utils.PathExists(libSiliconPatchPath) {
|
|
||||||
if prefs.EnableLibSiliconPatch {
|
|
||||||
if err := enableLibSiliconPatchInDlls(); err != nil {
|
|
||||||
debug.Printf("Warning: failed to enable libSiliconPatch in dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := disableLibSiliconPatchInDlls(); err != nil {
|
|
||||||
debug.Printf("Warning: failed to disable libSiliconPatch in dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the updated config back to file
|
|
||||||
if err := os.WriteFile(configPath, []byte(configText), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Successfully applied graphics settings to Config.wtf")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckGraphicsSettings checks if the graphics settings are correctly applied in Config.wtf
|
|
||||||
func CheckGraphicsSettings() (bool, bool, bool) {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return false, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
||||||
return false, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return false, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
configText := string(content)
|
|
||||||
|
|
||||||
terrainCorrect := !prefs.ReduceTerrainDistance || isConfigSettingCorrect(configText, "farclip", "177")
|
|
||||||
multisampleCorrect := !prefs.SetMultisampleTo2x || isConfigSettingCorrect(configText, "gxMultisample", "2")
|
|
||||||
shadowCorrect := !prefs.SetShadowLOD0 || isConfigSettingCorrect(configText, "shadowLOD", "0")
|
|
||||||
|
|
||||||
return terrainCorrect, multisampleCorrect, shadowCorrect
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadGraphicsSettingsFromConfig reads Config.wtf and updates preferences with current settings
|
|
||||||
func LoadGraphicsSettingsFromConfig() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
configPath := filepath.Join(paths.TurtlewowPath, "WTF", "Config.wtf")
|
|
||||||
|
|
||||||
// If Config.wtf doesn't exist, nothing to load
|
|
||||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
|
||||||
debug.Printf("Config.wtf not found, using default graphics settings")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(configPath)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
configText := string(content)
|
|
||||||
|
|
||||||
// Load current preferences
|
|
||||||
prefs, err := utils.LoadPrefs()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load preferences: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each graphics setting and update preferences
|
|
||||||
prefs.ReduceTerrainDistance = isConfigSettingCorrect(configText, "farclip", "177")
|
|
||||||
prefs.SetMultisampleTo2x = isConfigSettingCorrect(configText, "gxMultisample", "2")
|
|
||||||
prefs.SetShadowLOD0 = isConfigSettingCorrect(configText, "shadowLOD", "0")
|
|
||||||
|
|
||||||
// Check libSiliconPatch status (DLL exists and enabled in dlls.txt)
|
|
||||||
libSiliconPatchPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
libSiliconPatchExists := utils.PathExists(libSiliconPatchPath)
|
|
||||||
libSiliconPatchEnabled := false
|
|
||||||
|
|
||||||
if libSiliconPatchExists && utils.PathExists(dllsTextFile) {
|
|
||||||
if dllsContent, err := os.ReadFile(dllsTextFile); err == nil {
|
|
||||||
libSiliconPatchEnabled = strings.Contains(string(dllsContent), "libSiliconPatch.dll")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prefs.EnableLibSiliconPatch = libSiliconPatchExists && libSiliconPatchEnabled
|
|
||||||
|
|
||||||
// Save updated preferences
|
|
||||||
if err := utils.SavePrefs(prefs); err != nil {
|
|
||||||
return fmt.Errorf("failed to save preferences: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Loaded graphics settings from Config.wtf: terrain=%v, multisample=%v, shadow=%v, libSiliconPatch=%v",
|
|
||||||
prefs.ReduceTerrainDistance, prefs.SetMultisampleTo2x, prefs.SetShadowLOD0, prefs.EnableLibSiliconPatch)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckGraphicsSettingsPresence checks if libSiliconPatch.dll exists and shadowLOD is applied, updates preferences accordingly
|
|
||||||
func CheckGraphicsSettingsPresence() {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
libSiliconPatchPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
|
|
||||||
// Check if libSiliconPatch.dll exists
|
|
||||||
libSiliconPatchExists := utils.PathExists(libSiliconPatchPath)
|
|
||||||
|
|
||||||
// Check if it's enabled in dlls.txt
|
|
||||||
libSiliconPatchEnabled := false
|
|
||||||
if utils.PathExists(dllsTextFile) {
|
|
||||||
if fileContentBytes, err := os.ReadFile(dllsTextFile); err == nil {
|
|
||||||
fileContent := string(fileContentBytes)
|
|
||||||
libSiliconPatchEnabled = strings.Contains(fileContent, "libSiliconPatch.dll")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if shadowLOD is currently applied
|
|
||||||
shadowLODApplied := CheckShadowLODSetting()
|
|
||||||
|
|
||||||
// Load current preferences
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
|
|
||||||
// Handle libSiliconPatch preference detection
|
|
||||||
if libSiliconPatchExists {
|
|
||||||
if libSiliconPatchEnabled && !prefs.EnableLibSiliconPatch {
|
|
||||||
// DLL is currently enabled but user preference says disabled - likely first run detection
|
|
||||||
prefs.EnableLibSiliconPatch = true
|
|
||||||
debug.Printf("libSiliconPatch detected as enabled, setting user preference to enabled")
|
|
||||||
} else if !libSiliconPatchEnabled && prefs.EnableLibSiliconPatch {
|
|
||||||
// DLL exists but not enabled, user preference says enabled - respect user choice
|
|
||||||
debug.Printf("libSiliconPatch disabled in dlls.txt but user preference is enabled - keeping user preference")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle shadowLOD preference detection - enable by default if currently applied
|
|
||||||
if shadowLODApplied && !prefs.SetShadowLOD0 {
|
|
||||||
// shadowLOD is currently applied but user preference says disabled - likely first run detection
|
|
||||||
prefs.SetShadowLOD0 = true
|
|
||||||
debug.Printf("shadowLOD detected as applied, setting user preference to enabled")
|
|
||||||
} else if !shadowLODApplied && prefs.SetShadowLOD0 {
|
|
||||||
// shadowLOD not applied but user preference says enabled - respect user choice
|
|
||||||
debug.Printf("shadowLOD not applied but user preference is enabled - keeping user preference")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save any changes
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
|
|
||||||
debug.Printf("Graphics settings detection: libSiliconPatch exists=%v, enabled_in_dlls=%v, user_setting=%v; shadowLOD applied=%v, user_setting=%v",
|
|
||||||
libSiliconPatchExists, libSiliconPatchEnabled, prefs.EnableLibSiliconPatch, shadowLODApplied, prefs.SetShadowLOD0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// enableLibSiliconPatchInDlls adds libSiliconPatch.dll to dlls.txt if not present
|
|
||||||
func enableLibSiliconPatchInDlls() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
libSiliconPatchEntry := "libSiliconPatch.dll"
|
|
||||||
|
|
||||||
var fileContentBytes []byte
|
|
||||||
var err error
|
|
||||||
if utils.PathExists(dllsTextFile) {
|
|
||||||
fileContentBytes, err = os.ReadFile(dllsTextFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentContent := string(fileContentBytes)
|
|
||||||
if strings.Contains(currentContent, libSiliconPatchEntry) {
|
|
||||||
debug.Printf("libSiliconPatch.dll already present in dlls.txt")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add libSiliconPatch.dll to dlls.txt
|
|
||||||
if len(currentContent) > 0 && !strings.HasSuffix(currentContent, "\n") {
|
|
||||||
currentContent += "\n"
|
|
||||||
}
|
|
||||||
currentContent += libSiliconPatchEntry + "\n"
|
|
||||||
|
|
||||||
if err := os.WriteFile(dllsTextFile, []byte(currentContent), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to update dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Added libSiliconPatch.dll to dlls.txt")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// disableLibSiliconPatchInDlls removes libSiliconPatch.dll from dlls.txt
|
|
||||||
func disableLibSiliconPatchInDlls() error {
|
|
||||||
if paths.TurtlewowPath == "" {
|
|
||||||
return fmt.Errorf("TurtleWoW path not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
|
|
||||||
if !utils.PathExists(dllsTextFile) {
|
|
||||||
debug.Printf("dlls.txt not found, nothing to remove")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := os.ReadFile(dllsTextFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(string(content), "\n")
|
|
||||||
filteredLines := make([]string, 0, len(lines))
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
trimmedLine := strings.TrimSpace(line)
|
|
||||||
if trimmedLine != "libSiliconPatch.dll" {
|
|
||||||
filteredLines = append(filteredLines, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedContent := strings.Join(filteredLines, "\n")
|
|
||||||
if err := os.WriteFile(dllsTextFile, []byte(updatedContent), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to update dlls.txt: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.Printf("Removed libSiliconPatch.dll from dlls.txt")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"turtlesilicon/pkg/utils"
|
"epochsilicon/pkg/utils"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
@@ -18,11 +18,12 @@ const DefaultCrossOverPath = "/Applications/CrossOver.app"
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
CrossoverPath string
|
CrossoverPath string
|
||||||
TurtlewowPath string
|
EpochPath string
|
||||||
PatchesAppliedTurtleWoW = false
|
PatchesAppliedEpoch = false
|
||||||
PatchesAppliedCrossOver = false
|
PatchesAppliedCrossOver = false
|
||||||
RosettaX87ServiceRunning = false
|
RosettaX87ServiceRunning = false
|
||||||
ServiceStarting = false
|
ServiceStarting = false
|
||||||
|
DownloadingPatches = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichText, updateAllStatuses func()) {
|
func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichText, updateAllStatuses func()) {
|
||||||
@@ -53,35 +54,35 @@ func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichTe
|
|||||||
}, myWindow)
|
}, myWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectTurtleWoWPath(myWindow fyne.Window, turtlewowPathLabel *widget.RichText, updateAllStatuses func()) {
|
func SelectEpochPath(myWindow fyne.Window, epochPathLabel *widget.RichText, updateAllStatuses func()) {
|
||||||
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
|
dialog.ShowFolderOpen(func(uri fyne.ListableURI, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dialog.ShowError(err, myWindow)
|
dialog.ShowError(err, myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if uri == nil {
|
if uri == nil {
|
||||||
log.Println("TurtleWoW path selection cancelled.")
|
log.Println("Epoch path selection cancelled.")
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectedPath := uri.Path()
|
selectedPath := uri.Path()
|
||||||
if utils.DirExists(selectedPath) {
|
if utils.DirExists(selectedPath) {
|
||||||
TurtlewowPath = selectedPath
|
EpochPath = selectedPath
|
||||||
PatchesAppliedTurtleWoW = false
|
PatchesAppliedEpoch = false
|
||||||
log.Println("TurtleWoW path set to:", TurtlewowPath)
|
log.Println("Epoch path set to:", EpochPath)
|
||||||
// Save to prefs
|
// Save to prefs
|
||||||
prefs, _ := utils.LoadPrefs()
|
prefs, _ := utils.LoadPrefs()
|
||||||
prefs.TurtleWoWPath = selectedPath
|
prefs.EpochPath = selectedPath
|
||||||
utils.SavePrefs(prefs)
|
utils.SavePrefs(prefs)
|
||||||
} else {
|
} else {
|
||||||
dialog.ShowError(fmt.Errorf("invalid selection: '%s' is not a valid directory", selectedPath), myWindow)
|
dialog.ShowError(fmt.Errorf("invalid selection: '%s' is not a valid directory", selectedPath), myWindow)
|
||||||
log.Println("Invalid TurtleWoW path selected:", selectedPath)
|
log.Println("Invalid Epoch path selected:", selectedPath)
|
||||||
}
|
}
|
||||||
updateAllStatuses()
|
updateAllStatuses()
|
||||||
}, myWindow)
|
}, myWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdatePathLabels(crossoverPathLabel, turtlewowPathLabel *widget.RichText) {
|
func UpdatePathLabels(crossoverPathLabel, EpochPathLabel *widget.RichText) {
|
||||||
if CrossoverPath == "" {
|
if CrossoverPath == "" {
|
||||||
crossoverPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
crossoverPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
||||||
} else {
|
} else {
|
||||||
@@ -89,12 +90,12 @@ func UpdatePathLabels(crossoverPathLabel, turtlewowPathLabel *widget.RichText) {
|
|||||||
}
|
}
|
||||||
crossoverPathLabel.Refresh()
|
crossoverPathLabel.Refresh()
|
||||||
|
|
||||||
if TurtlewowPath == "" {
|
if EpochPath == "" {
|
||||||
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
EpochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
||||||
} else {
|
} else {
|
||||||
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: TurtlewowPath, Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
EpochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: EpochPath, Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
||||||
}
|
}
|
||||||
turtlewowPathLabel.Refresh()
|
EpochPathLabel.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckDefaultCrossOverPath() {
|
func CheckDefaultCrossOverPath() {
|
||||||
|
@@ -10,8 +10,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"turtlesilicon/pkg/paths"
|
"epochsilicon/pkg/paths"
|
||||||
"turtlesilicon/pkg/utils"
|
"epochsilicon/pkg/utils"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -79,16 +79,16 @@ func isRosettaSocketActive() bool {
|
|||||||
func StartRosettaX87Service(myWindow fyne.Window, updateAllStatuses func()) {
|
func StartRosettaX87Service(myWindow fyne.Window, updateAllStatuses func()) {
|
||||||
log.Println("Starting RosettaX87 service...")
|
log.Println("Starting RosettaX87 service...")
|
||||||
|
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first"), myWindow)
|
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first"), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rosettaX87Dir := filepath.Join(paths.TurtlewowPath, "rosettax87")
|
rosettaX87Dir := filepath.Join(paths.EpochPath, "rosettax87")
|
||||||
rosettaX87Exe := filepath.Join(rosettaX87Dir, "rosettax87")
|
rosettaX87Exe := filepath.Join(rosettaX87Dir, "rosettax87")
|
||||||
|
|
||||||
if !utils.PathExists(rosettaX87Exe) {
|
if !utils.PathExists(rosettaX87Exe) {
|
||||||
dialog.ShowError(fmt.Errorf("rosettax87 executable not found at %s. Please apply TurtleWoW patches first", rosettaX87Exe), myWindow)
|
dialog.ShowError(fmt.Errorf("rosettax87 executable not found at %s. Please apply Epoch patches first", rosettaX87Exe), myWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,20 +1,18 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/launcher"
|
"epochsilicon/pkg/launcher"
|
||||||
"turtlesilicon/pkg/patching"
|
"epochsilicon/pkg/patching"
|
||||||
"turtlesilicon/pkg/service"
|
"epochsilicon/pkg/service"
|
||||||
"turtlesilicon/pkg/utils"
|
"epochsilicon/pkg/utils"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/dialog"
|
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,17 +41,6 @@ func createOptionsComponents() {
|
|||||||
})
|
})
|
||||||
showTerminalCheckbox.SetChecked(prefs.ShowTerminalNormally)
|
showTerminalCheckbox.SetChecked(prefs.ShowTerminalNormally)
|
||||||
|
|
||||||
vanillaTweaksCheckbox = widget.NewCheck("Enable vanilla-tweaks", func(checked bool) {
|
|
||||||
launcher.EnableVanillaTweaks = checked
|
|
||||||
// Save to preferences
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
prefs.EnableVanillaTweaks = checked
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
debug.Printf("Vanilla-tweaks enabled: %v", launcher.EnableVanillaTweaks)
|
|
||||||
})
|
|
||||||
vanillaTweaksCheckbox.SetChecked(prefs.EnableVanillaTweaks)
|
|
||||||
launcher.EnableVanillaTweaks = prefs.EnableVanillaTweaks
|
|
||||||
|
|
||||||
autoDeleteWdbCheckbox = widget.NewCheck("Auto-delete WDB directory on launch", func(checked bool) {
|
autoDeleteWdbCheckbox = widget.NewCheck("Auto-delete WDB directory on launch", func(checked bool) {
|
||||||
launcher.AutoDeleteWdb = checked
|
launcher.AutoDeleteWdb = checked
|
||||||
// Save to preferences
|
// Save to preferences
|
||||||
@@ -65,41 +52,9 @@ func createOptionsComponents() {
|
|||||||
autoDeleteWdbCheckbox.SetChecked(prefs.AutoDeleteWdb)
|
autoDeleteWdbCheckbox.SetChecked(prefs.AutoDeleteWdb)
|
||||||
launcher.AutoDeleteWdb = prefs.AutoDeleteWdb
|
launcher.AutoDeleteWdb = prefs.AutoDeleteWdb
|
||||||
|
|
||||||
// Create recommended settings button with help icon
|
|
||||||
applyRecommendedSettingsButton = widget.NewButton("Apply recommended settings", func() {
|
|
||||||
err := launcher.ApplyRecommendedSettings()
|
|
||||||
if err != nil {
|
|
||||||
debug.Printf("Failed to apply recommended settings: %v", err)
|
|
||||||
// Show error dialog if we have a window reference
|
|
||||||
if currentWindow != nil {
|
|
||||||
dialog.ShowError(fmt.Errorf("failed to apply recommended settings: %v", err), currentWindow)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug.Printf("Successfully applied recommended settings")
|
|
||||||
// Show success dialog if we have a window reference
|
|
||||||
if currentWindow != nil {
|
|
||||||
dialog.ShowInformation("Success", "Recommended graphics settings have been applied", currentWindow)
|
|
||||||
}
|
|
||||||
// Update button state
|
|
||||||
updateRecommendedSettingsButton()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
applyRecommendedSettingsButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Create help button for recommended settings
|
|
||||||
recommendedSettingsHelpButton = widget.NewButton("?", func() {
|
|
||||||
showRecommendedSettingsHelpPopup()
|
|
||||||
})
|
|
||||||
recommendedSettingsHelpButton.Importance = widget.MediumImportance
|
|
||||||
// Initialize button state
|
|
||||||
updateRecommendedSettingsButton()
|
|
||||||
|
|
||||||
// Create Wine registry Option-as-Alt buttons and status
|
// Create Wine registry Option-as-Alt buttons and status
|
||||||
createWineRegistryComponents()
|
createWineRegistryComponents()
|
||||||
|
|
||||||
// Create graphics settings components
|
|
||||||
createGraphicsSettingsComponents()
|
|
||||||
|
|
||||||
// Load environment variables from preferences
|
// Load environment variables from preferences
|
||||||
if prefs.EnvironmentVariables != "" {
|
if prefs.EnvironmentVariables != "" {
|
||||||
launcher.CustomEnvVars = prefs.EnvironmentVariables
|
launcher.CustomEnvVars = prefs.EnvironmentVariables
|
||||||
@@ -120,11 +75,11 @@ func createOptionsComponents() {
|
|||||||
|
|
||||||
// createPatchingButtons creates all patching-related buttons
|
// createPatchingButtons creates all patching-related buttons
|
||||||
func createPatchingButtons(myWindow fyne.Window) {
|
func createPatchingButtons(myWindow fyne.Window) {
|
||||||
patchTurtleWoWButton = widget.NewButton("Patch TurtleWoW", func() {
|
patchEpochButton = widget.NewButton("Patch Epoch", func() {
|
||||||
patching.PatchTurtleWoW(myWindow, UpdateAllStatuses)
|
patching.PatchEpoch(myWindow, UpdateAllStatuses)
|
||||||
})
|
})
|
||||||
unpatchTurtleWoWButton = widget.NewButton("Unpatch TurtleWoW", func() {
|
unpatchEpochButton = widget.NewButton("Unpatch Epoch", func() {
|
||||||
patching.UnpatchTurtleWoW(myWindow, UpdateAllStatuses)
|
patching.UnpatchEpoch(myWindow, UpdateAllStatuses)
|
||||||
})
|
})
|
||||||
patchCrossOverButton = widget.NewButton("Patch CrossOver", func() {
|
patchCrossOverButton = widget.NewButton("Patch CrossOver", func() {
|
||||||
patching.PatchCrossOver(myWindow, UpdateAllStatuses)
|
patching.PatchCrossOver(myWindow, UpdateAllStatuses)
|
||||||
@@ -166,12 +121,12 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
|
|||||||
showTroubleshootingPopup()
|
showTroubleshootingPopup()
|
||||||
})
|
})
|
||||||
|
|
||||||
// GitHub button
|
// Git button
|
||||||
githubButton := widget.NewButton("GitHub", func() {
|
gitButton := widget.NewButton("Source Code", func() {
|
||||||
githubURL := "https://github.com/tairasu/TurtleSilicon"
|
githubURL := "https://git.burkey.co/eburk/epochsilicon"
|
||||||
parsedURL, err := url.Parse(githubURL)
|
parsedURL, err := url.Parse(githubURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Printf("Error parsing GitHub URL: %v", err)
|
debug.Printf("Error parsing git URL: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fyne.CurrentApp().OpenURL(parsedURL)
|
fyne.CurrentApp().OpenURL(parsedURL)
|
||||||
@@ -193,7 +148,7 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
|
|||||||
leftButtons := container.NewHBox(
|
leftButtons := container.NewHBox(
|
||||||
optionsButton,
|
optionsButton,
|
||||||
troubleshootingButton,
|
troubleshootingButton,
|
||||||
githubButton,
|
gitButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the large play button with fixed size
|
// Create the large play button with fixed size
|
||||||
@@ -484,187 +439,3 @@ func showRecommendedSettingsHelpPopup() {
|
|||||||
|
|
||||||
helpPopup.Show()
|
helpPopup.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createGraphicsSettingsComponents creates all graphics settings checkboxes and buttons
|
|
||||||
func createGraphicsSettingsComponents() {
|
|
||||||
// Load preferences for initial values
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
|
|
||||||
// Create Reduce Terrain Distance setting with help button
|
|
||||||
reduceTerrainDistanceCheckbox = widget.NewCheck("", func(checked bool) {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
prefs.ReduceTerrainDistance = checked
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
debug.Printf("Reduce terrain distance: %v", checked)
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
})
|
|
||||||
reduceTerrainDistanceCheckbox.SetChecked(prefs.ReduceTerrainDistance)
|
|
||||||
|
|
||||||
reduceTerrainDistanceHelpButton = widget.NewButton("?", func() {
|
|
||||||
showGraphicsSettingHelpPopup("Reduce Terrain Distance", "Sets the draw distance to the lowest setting. This will drastically increase your FPS", "High Performance Impact")
|
|
||||||
})
|
|
||||||
reduceTerrainDistanceHelpButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Create Set Multisample to 2x setting with help button
|
|
||||||
setMultisampleTo2xCheckbox = widget.NewCheck("", func(checked bool) {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
prefs.SetMultisampleTo2x = checked
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
debug.Printf("Set multisample to 2x: %v", checked)
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
})
|
|
||||||
setMultisampleTo2xCheckbox.SetChecked(prefs.SetMultisampleTo2x)
|
|
||||||
|
|
||||||
setMultisampleTo2xHelpButton = widget.NewButton("?", func() {
|
|
||||||
showGraphicsSettingHelpPopup("Set Multisample to 2x", "Might reduce your FPS slightly on lower end machines, but makes sure the portraits load properly.", "Medium Performance Impact")
|
|
||||||
})
|
|
||||||
setMultisampleTo2xHelpButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Create Set Shadow LOD to 0 setting with help button
|
|
||||||
setShadowLOD0Checkbox = widget.NewCheck("", func(checked bool) {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
prefs.SetShadowLOD0 = checked
|
|
||||||
|
|
||||||
// Track if user manually disabled this setting
|
|
||||||
if !checked {
|
|
||||||
prefs.UserDisabledShadowLOD = true
|
|
||||||
} else {
|
|
||||||
prefs.UserDisabledShadowLOD = false
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
debug.Printf("Set shadow LOD to 0: %v (user manually changed)", checked)
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
})
|
|
||||||
setShadowLOD0Checkbox.SetChecked(prefs.SetShadowLOD0)
|
|
||||||
|
|
||||||
setShadowLOD0HelpButton = widget.NewButton("?", func() {
|
|
||||||
showGraphicsSettingHelpPopup("Set Shadow LOD to 0", "Turns off all shadows. This will give you ~10% more FPS.", "High Performance Impact")
|
|
||||||
})
|
|
||||||
setShadowLOD0HelpButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Create Enable libSiliconPatch setting with help button
|
|
||||||
libSiliconPatchCheckbox = widget.NewCheck("", func(checked bool) {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
prefs.EnableLibSiliconPatch = checked
|
|
||||||
|
|
||||||
// Track if user manually disabled this setting
|
|
||||||
if !checked {
|
|
||||||
prefs.UserDisabledLibSiliconPatch = true
|
|
||||||
} else {
|
|
||||||
prefs.UserDisabledLibSiliconPatch = false
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SavePrefs(prefs)
|
|
||||||
debug.Printf("Enable libSiliconPatch: %v (user manually changed)", checked)
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
})
|
|
||||||
libSiliconPatchCheckbox.SetChecked(prefs.EnableLibSiliconPatch)
|
|
||||||
|
|
||||||
libSiliconPatchHelpButton = widget.NewButton("?", func() {
|
|
||||||
showGraphicsSettingHelpPopup("Enable libSiliconPatch", "Hooks into the WoW process and replaces slow X87 instructions with SSE2 instructions that Rosetta can translate much quicker, resulting in an increase in FPS (2x or more). May potentially cause graphical bugs.", "Very High Performance Impact")
|
|
||||||
})
|
|
||||||
libSiliconPatchHelpButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
applyGraphicsSettingsButton = widget.NewButton("Apply Graphics Settings", func() {
|
|
||||||
err := patching.ApplyGraphicsSettings(currentWindow)
|
|
||||||
if err != nil {
|
|
||||||
debug.Printf("Failed to apply graphics settings: %v", err)
|
|
||||||
if currentWindow != nil {
|
|
||||||
dialog.ShowError(fmt.Errorf("failed to apply graphics settings: %v", err), currentWindow)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug.Printf("Successfully applied graphics settings")
|
|
||||||
if currentWindow != nil {
|
|
||||||
dialog.ShowInformation("Success", "Graphics settings have been applied", currentWindow)
|
|
||||||
}
|
|
||||||
// Refresh checkboxes to reflect current state
|
|
||||||
refreshGraphicsSettingsCheckboxes()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
applyGraphicsSettingsButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Initialize button state
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateApplyGraphicsSettingsButton updates the state of the apply graphics settings button
|
|
||||||
func updateApplyGraphicsSettingsButton() {
|
|
||||||
if applyGraphicsSettingsButton == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always enable the button since we need to handle both adding and removing settings
|
|
||||||
applyGraphicsSettingsButton.Enable()
|
|
||||||
applyGraphicsSettingsButton.SetText("Apply Changes")
|
|
||||||
}
|
|
||||||
|
|
||||||
// refreshGraphicsSettingsCheckboxes updates the checkbox states from current preferences
|
|
||||||
func refreshGraphicsSettingsCheckboxes() {
|
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
|
|
||||||
if reduceTerrainDistanceCheckbox != nil {
|
|
||||||
reduceTerrainDistanceCheckbox.SetChecked(prefs.ReduceTerrainDistance)
|
|
||||||
}
|
|
||||||
if setMultisampleTo2xCheckbox != nil {
|
|
||||||
setMultisampleTo2xCheckbox.SetChecked(prefs.SetMultisampleTo2x)
|
|
||||||
}
|
|
||||||
if setShadowLOD0Checkbox != nil {
|
|
||||||
setShadowLOD0Checkbox.SetChecked(prefs.SetShadowLOD0)
|
|
||||||
}
|
|
||||||
if libSiliconPatchCheckbox != nil {
|
|
||||||
libSiliconPatchCheckbox.SetChecked(prefs.EnableLibSiliconPatch)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the apply button state
|
|
||||||
updateApplyGraphicsSettingsButton()
|
|
||||||
}
|
|
||||||
|
|
||||||
// showGraphicsSettingHelpPopup shows a help popup for a specific graphics setting
|
|
||||||
func showGraphicsSettingHelpPopup(title, description, impact string) {
|
|
||||||
if currentWindow == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create help content
|
|
||||||
helpTitle := widget.NewRichTextFromMarkdown("# " + title)
|
|
||||||
|
|
||||||
descriptionLabel := widget.NewLabel(description)
|
|
||||||
descriptionLabel.Wrapping = fyne.TextWrapWord
|
|
||||||
|
|
||||||
impactLabel := widget.NewLabel(impact)
|
|
||||||
impactLabel.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
// Create OK button
|
|
||||||
okButton := widget.NewButton("OK", func() {
|
|
||||||
// This will be set when the popup is created
|
|
||||||
})
|
|
||||||
okButton.Importance = widget.MediumImportance
|
|
||||||
|
|
||||||
// Create help content container
|
|
||||||
helpContentContainer := container.NewVBox(
|
|
||||||
container.NewCenter(helpTitle),
|
|
||||||
widget.NewSeparator(),
|
|
||||||
descriptionLabel,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
impactLabel,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
container.NewCenter(okButton),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Calculate popup size
|
|
||||||
windowSize := currentWindow.Content().Size()
|
|
||||||
popupWidth := windowSize.Width * 2 / 3
|
|
||||||
popupHeight := windowSize.Height / 2
|
|
||||||
|
|
||||||
// Create the help popup
|
|
||||||
helpPopup := widget.NewModalPopUp(container.NewPadded(helpContentContainer), currentWindow.Canvas())
|
|
||||||
helpPopup.Resize(fyne.NewSize(popupWidth, popupHeight))
|
|
||||||
|
|
||||||
// Set the OK button action to hide the help popup
|
|
||||||
okButton.OnTapped = func() {
|
|
||||||
helpPopup.Hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
helpPopup.Show()
|
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/paths"
|
"epochsilicon/pkg/paths"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
@@ -13,11 +13,11 @@ import (
|
|||||||
// createHeaderContainer creates the header with title and subtitle
|
// createHeaderContainer creates the header with title and subtitle
|
||||||
func createHeaderContainer() fyne.CanvasObject {
|
func createHeaderContainer() fyne.CanvasObject {
|
||||||
// Main title
|
// Main title
|
||||||
titleText := widget.NewRichTextFromMarkdown("# TurtleSilicon")
|
titleText := widget.NewRichTextFromMarkdown("# EpochSilicon")
|
||||||
titleText.Wrapping = fyne.TextWrapOff
|
titleText.Wrapping = fyne.TextWrapOff
|
||||||
|
|
||||||
// Subtitle
|
// Subtitle
|
||||||
subtitleText := widget.NewLabel("A TurtleWoW launcher for Apple Silicon Macs")
|
subtitleText := widget.NewLabel("An Epoch launcher for Apple Silicon Macs")
|
||||||
subtitleText.Alignment = fyne.TextAlignCenter
|
subtitleText.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
// Create header container
|
// Create header container
|
||||||
@@ -57,15 +57,15 @@ func createLogoContainer() fyne.CanvasObject {
|
|||||||
return logoContainer
|
return logoContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
// createPathSelectionForm creates the form for selecting CrossOver and TurtleWoW paths
|
// createPathSelectionForm creates the form for selecting CrossOver and Epoch paths
|
||||||
func createPathSelectionForm(myWindow fyne.Window) *widget.Form {
|
func createPathSelectionForm(myWindow fyne.Window) *widget.Form {
|
||||||
pathSelectionForm := widget.NewForm(
|
pathSelectionForm := widget.NewForm(
|
||||||
widget.NewFormItem("CrossOver Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
|
widget.NewFormItem("CrossOver Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
|
||||||
paths.SelectCrossOverPath(myWindow, crossoverPathLabel, UpdateAllStatuses)
|
paths.SelectCrossOverPath(myWindow, crossoverPathLabel, UpdateAllStatuses)
|
||||||
}), crossoverPathLabel)),
|
}), crossoverPathLabel)),
|
||||||
widget.NewFormItem("TurtleWoW Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
|
widget.NewFormItem("Warcraft Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
|
||||||
paths.SelectTurtleWoWPath(myWindow, turtlewowPathLabel, UpdateAllStatuses)
|
paths.SelectEpochPath(myWindow, epochPathLabel, UpdateAllStatuses)
|
||||||
}), turtlewowPathLabel)),
|
}), epochPathLabel)),
|
||||||
)
|
)
|
||||||
|
|
||||||
return pathSelectionForm
|
return pathSelectionForm
|
||||||
@@ -76,7 +76,7 @@ func createPatchOperationsLayout() fyne.CanvasObject {
|
|||||||
patchOperationsLayout := container.NewVBox(
|
patchOperationsLayout := container.NewVBox(
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
container.NewGridWithColumns(4,
|
container.NewGridWithColumns(4,
|
||||||
widget.NewLabel("TurtleWoW Patch:"), turtlewowStatusLabel, patchTurtleWoWButton, unpatchTurtleWoWButton,
|
widget.NewLabel("Epoch Patch:"), epochStatusLabel, patchEpochButton, unpatchEpochButton,
|
||||||
),
|
),
|
||||||
container.NewGridWithColumns(4,
|
container.NewGridWithColumns(4,
|
||||||
widget.NewLabel("CrossOver Patch:"), crossoverStatusLabel, patchCrossOverButton, unpatchCrossOverButton,
|
widget.NewLabel("CrossOver Patch:"), crossoverStatusLabel, patchCrossOverButton, unpatchCrossOverButton,
|
||||||
|
@@ -13,10 +13,8 @@ import (
|
|||||||
|
|
||||||
"howett.net/plist"
|
"howett.net/plist"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/paths"
|
||||||
"turtlesilicon/pkg/patching"
|
"epochsilicon/pkg/utils"
|
||||||
"turtlesilicon/pkg/paths"
|
|
||||||
"turtlesilicon/pkg/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// showOptionsPopup creates and shows an integrated popup window for options
|
// showOptionsPopup creates and shows an integrated popup window for options
|
||||||
@@ -25,17 +23,6 @@ func showOptionsPopup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check graphics settings presence and update preferences before showing UI
|
|
||||||
patching.CheckGraphicsSettingsPresence()
|
|
||||||
|
|
||||||
// Load graphics settings from Config.wtf and update preferences
|
|
||||||
if err := patching.LoadGraphicsSettingsFromConfig(); err != nil {
|
|
||||||
debug.Printf("Warning: failed to load graphics settings from Config.wtf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh checkbox states to reflect current settings
|
|
||||||
refreshGraphicsSettingsCheckboxes()
|
|
||||||
|
|
||||||
// Create General tab content
|
// Create General tab content
|
||||||
generalTitle := widget.NewLabel("General Settings")
|
generalTitle := widget.NewLabel("General Settings")
|
||||||
generalTitle.TextStyle = fyne.TextStyle{Bold: true}
|
generalTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
@@ -45,63 +32,11 @@ func showOptionsPopup() {
|
|||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
metalHudCheckbox,
|
metalHudCheckbox,
|
||||||
showTerminalCheckbox,
|
showTerminalCheckbox,
|
||||||
vanillaTweaksCheckbox,
|
|
||||||
autoDeleteWdbCheckbox,
|
autoDeleteWdbCheckbox,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
container.NewBorder(nil, nil, nil, container.NewHBox(enableOptionAsAltButton, disableOptionAsAltButton), optionAsAltStatusLabel),
|
container.NewBorder(nil, nil, nil, container.NewHBox(enableOptionAsAltButton, disableOptionAsAltButton), optionAsAltStatusLabel),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create Graphics tab content
|
|
||||||
graphicsTitle := widget.NewLabel("Graphics Settings")
|
|
||||||
graphicsTitle.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
graphicsDescription := widget.NewLabel("Select graphics settings to apply to Config.wtf:")
|
|
||||||
graphicsDescription.TextStyle = fyne.TextStyle{Italic: true}
|
|
||||||
|
|
||||||
// Create bold text labels for each setting
|
|
||||||
terrainLabel := widget.NewLabel("Reduce Terrain Distance")
|
|
||||||
terrainLabel.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
multisampleLabel := widget.NewLabel("Set Multisample to 2x")
|
|
||||||
multisampleLabel.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
shadowLabel := widget.NewLabel("Set Shadow LOD to 0")
|
|
||||||
shadowLabel.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
libSiliconPatchLabel := widget.NewLabel("Enable libSiliconPatch")
|
|
||||||
libSiliconPatchLabel.TextStyle = fyne.TextStyle{Bold: true}
|
|
||||||
|
|
||||||
// Create setting rows with help buttons between checkbox and label
|
|
||||||
terrainRow := container.NewHBox(
|
|
||||||
reduceTerrainDistanceCheckbox,
|
|
||||||
reduceTerrainDistanceHelpButton,
|
|
||||||
terrainLabel)
|
|
||||||
multisampleRow := container.NewHBox(
|
|
||||||
setMultisampleTo2xCheckbox,
|
|
||||||
setMultisampleTo2xHelpButton,
|
|
||||||
multisampleLabel)
|
|
||||||
shadowRow := container.NewHBox(
|
|
||||||
setShadowLOD0Checkbox,
|
|
||||||
setShadowLOD0HelpButton,
|
|
||||||
shadowLabel)
|
|
||||||
libSiliconPatchRow := container.NewHBox(
|
|
||||||
libSiliconPatchCheckbox,
|
|
||||||
libSiliconPatchHelpButton,
|
|
||||||
libSiliconPatchLabel)
|
|
||||||
|
|
||||||
graphicsContainer := container.NewVBox(
|
|
||||||
graphicsTitle,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
graphicsDescription,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
terrainRow,
|
|
||||||
multisampleRow,
|
|
||||||
shadowRow,
|
|
||||||
libSiliconPatchRow,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
container.NewCenter(applyGraphicsSettingsButton),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create Environment Variables tab content
|
// Create Environment Variables tab content
|
||||||
envVarsTitle := widget.NewLabel("Environment Variables")
|
envVarsTitle := widget.NewLabel("Environment Variables")
|
||||||
envVarsTitle.TextStyle = fyne.TextStyle{Bold: true}
|
envVarsTitle.TextStyle = fyne.TextStyle{Bold: true}
|
||||||
@@ -114,7 +49,6 @@ func showOptionsPopup() {
|
|||||||
// Create tabs
|
// Create tabs
|
||||||
tabs := container.NewAppTabs(
|
tabs := container.NewAppTabs(
|
||||||
container.NewTabItem("General", container.NewScroll(generalContainer)),
|
container.NewTabItem("General", container.NewScroll(generalContainer)),
|
||||||
container.NewTabItem("Graphics", container.NewScroll(graphicsContainer)),
|
|
||||||
container.NewTabItem("Environment", container.NewScroll(envVarsContainer)),
|
container.NewTabItem("Environment", container.NewScroll(envVarsContainer)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -223,9 +157,9 @@ func showTroubleshootingPopup() {
|
|||||||
|
|
||||||
// --- Delete WDB Directory ---
|
// --- Delete WDB Directory ---
|
||||||
wdbDeleteButton = widget.NewButton("Delete", func() {
|
wdbDeleteButton = widget.NewButton("Delete", func() {
|
||||||
wdbPath := filepath.Join(paths.TurtlewowPath, "WDB")
|
wdbPath := filepath.Join(paths.EpochPath, "WDB")
|
||||||
if !utils.DirExists(wdbPath) {
|
if !utils.DirExists(wdbPath) {
|
||||||
dialog.ShowInformation("WDB Not Found", "No WDB directory found in your TurtleWoW folder.", currentWindow)
|
dialog.ShowInformation("WDB Not Found", "No WDB directory found in your Epoch folder.", currentWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dialog.NewConfirm("Delete WDB Directory", "Are you sure you want to delete the WDB directory? This will remove all cached data. No important data will be lost.", func(confirm bool) {
|
dialog.NewConfirm("Delete WDB Directory", "Are you sure you want to delete the WDB directory? This will remove all cached data. No important data will be lost.", func(confirm bool) {
|
||||||
@@ -244,7 +178,7 @@ func showTroubleshootingPopup() {
|
|||||||
wineDeleteButton = widget.NewButton("Delete", func() {
|
wineDeleteButton = widget.NewButton("Delete", func() {
|
||||||
homeDir, _ := os.UserHomeDir()
|
homeDir, _ := os.UserHomeDir()
|
||||||
userWine := filepath.Join(homeDir, ".wine")
|
userWine := filepath.Join(homeDir, ".wine")
|
||||||
turtleWine := filepath.Join(paths.TurtlewowPath, ".wine")
|
turtleWine := filepath.Join(paths.EpochPath, ".wine")
|
||||||
msg := "Are you sure you want to delete the following Wine prefixes?\n\n- " + userWine + "\n- " + turtleWine + "\n\nThis cannot be undone."
|
msg := "Are you sure you want to delete the following Wine prefixes?\n\n- " + userWine + "\n- " + turtleWine + "\n\nThis cannot be undone."
|
||||||
dialog.NewConfirm("Delete Wine Prefixes", msg, func(confirm bool) {
|
dialog.NewConfirm("Delete Wine Prefixes", msg, func(confirm bool) {
|
||||||
if confirm {
|
if confirm {
|
||||||
@@ -255,7 +189,7 @@ func showTroubleshootingPopup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err2 != nil && !os.IsNotExist(err2) {
|
if err2 != nil && !os.IsNotExist(err2) {
|
||||||
dialog.ShowError(fmt.Errorf("Failed to delete TurtleWoW/.wine: %v", err2), currentWindow)
|
dialog.ShowError(fmt.Errorf("Failed to delete Epoch/.wine: %v", err2), currentWindow)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dialog.ShowInformation("Wine Prefixes Deleted", "Wine prefixes deleted successfully.", currentWindow)
|
dialog.ShowInformation("Wine Prefixes Deleted", "Wine prefixes deleted successfully.", currentWindow)
|
||||||
@@ -268,8 +202,8 @@ func showTroubleshootingPopup() {
|
|||||||
|
|
||||||
rowCrossover := container.NewBorder(nil, nil, widget.NewLabel("CrossOver version:"), crossoverStatusShort, nil)
|
rowCrossover := container.NewBorder(nil, nil, widget.NewLabel("CrossOver version:"), crossoverStatusShort, nil)
|
||||||
rowWDB := container.NewBorder(nil, nil, widget.NewLabel("Delete WDB directory (cache):"), wdbDeleteButton, nil)
|
rowWDB := container.NewBorder(nil, nil, widget.NewLabel("Delete WDB directory (cache):"), wdbDeleteButton, nil)
|
||||||
rowWine := container.NewBorder(nil, nil, widget.NewLabel("Delete Wine prefixes (~/.wine & TurtleWoW/.wine):"), wineDeleteButton, nil)
|
rowWine := container.NewBorder(nil, nil, widget.NewLabel("Delete Wine prefixes (~/.wine & Epoch/.wine):"), wineDeleteButton, nil)
|
||||||
appMgmtNote := widget.NewLabel("Please ensure TurtleSilicon is enabled in System Settings > Privacy & Security > App Management.")
|
appMgmtNote := widget.NewLabel("Please ensure EpochSilicon is enabled in System Settings > Privacy & Security > App Management.")
|
||||||
appMgmtNote.Wrapping = fyne.TextWrapWord
|
appMgmtNote.Wrapping = fyne.TextWrapWord
|
||||||
appMgmtNote.TextStyle = fyne.TextStyle{Italic: true}
|
appMgmtNote.TextStyle = fyne.TextStyle{Italic: true}
|
||||||
|
|
||||||
@@ -288,11 +222,11 @@ func showTroubleshootingPopup() {
|
|||||||
troubleshootingCloseButton = widget.NewButton("Close", func() {})
|
troubleshootingCloseButton = widget.NewButton("Close", func() {})
|
||||||
|
|
||||||
popupContent := container.NewBorder(
|
popupContent := container.NewBorder(
|
||||||
nil, // top
|
nil, // top
|
||||||
container.NewCenter(troubleshootingCloseButton), // bottom
|
container.NewCenter(troubleshootingCloseButton), // bottom
|
||||||
nil, // left
|
nil, // left
|
||||||
nil, // right
|
nil, // right
|
||||||
container.NewPadded(scrollContainer), // center
|
container.NewPadded(scrollContainer), // center
|
||||||
)
|
)
|
||||||
|
|
||||||
windowSize := currentWindow.Content().Size()
|
windowSize := currentWindow.Content().Size()
|
||||||
|
115
pkg/ui/status.go
115
pkg/ui/status.go
@@ -1,15 +1,16 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"epochsilicon/pkg/debug"
|
||||||
|
"git.burkey.co/eburk/epochcli/pkg/epoch"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"turtlesilicon/pkg/patching"
|
"epochsilicon/pkg/paths"
|
||||||
"turtlesilicon/pkg/paths"
|
"epochsilicon/pkg/service"
|
||||||
"turtlesilicon/pkg/service"
|
"epochsilicon/pkg/utils"
|
||||||
"turtlesilicon/pkg/utils"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
@@ -23,7 +24,7 @@ var (
|
|||||||
// UpdateAllStatuses updates all UI components based on current application state
|
// UpdateAllStatuses updates all UI components based on current application state
|
||||||
func UpdateAllStatuses() {
|
func UpdateAllStatuses() {
|
||||||
updateCrossoverStatus()
|
updateCrossoverStatus()
|
||||||
updateTurtleWoWStatus()
|
updateEpochStatus()
|
||||||
updatePlayButtonState()
|
updatePlayButtonState()
|
||||||
updateServiceStatus()
|
updateServiceStatus()
|
||||||
|
|
||||||
@@ -76,20 +77,19 @@ func updateCrossoverStatus() {
|
|||||||
crossoverStatusLabel.Refresh()
|
crossoverStatusLabel.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateTurtleWoWStatus updates TurtleWoW path and patch status
|
// updateEpochStatus updates Epoch path and patch status
|
||||||
func updateTurtleWoWStatus() {
|
func updateEpochStatus() {
|
||||||
if paths.TurtlewowPath == "" {
|
if paths.EpochPath == "" {
|
||||||
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
epochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
||||||
paths.PatchesAppliedTurtleWoW = false // Reset if path is cleared
|
paths.PatchesAppliedEpoch = false // Reset if path is cleared
|
||||||
} else {
|
} else {
|
||||||
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: paths.TurtlewowPath, Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
epochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: paths.EpochPath, Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
||||||
|
|
||||||
// Check if all required files exist
|
// Check if all required files exist
|
||||||
winerosettaDllPath := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
|
winerosettaDllPath := filepath.Join(paths.EpochPath, "winerosetta.dll")
|
||||||
d3d9DllPath := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
|
d3d9DllPath := filepath.Join(paths.EpochPath, "d3d9.dll")
|
||||||
libSiliconPatchDllPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
|
rosettaX87DirPath := filepath.Join(paths.EpochPath, "rosettax87")
|
||||||
rosettaX87DirPath := filepath.Join(paths.TurtlewowPath, "rosettax87")
|
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
|
||||||
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
|
|
||||||
rosettaX87ExePath := filepath.Join(rosettaX87DirPath, "rosettax87")
|
rosettaX87ExePath := filepath.Join(rosettaX87DirPath, "rosettax87")
|
||||||
libRuntimeRosettaX87Path := filepath.Join(rosettaX87DirPath, "libRuntimeRosettax87")
|
libRuntimeRosettaX87Path := filepath.Join(rosettaX87DirPath, "libRuntimeRosettax87")
|
||||||
|
|
||||||
@@ -99,70 +99,77 @@ func updateTurtleWoWStatus() {
|
|||||||
contentStr := string(fileContent)
|
contentStr := string(fileContent)
|
||||||
winerosettaPresent := strings.Contains(contentStr, "winerosetta.dll")
|
winerosettaPresent := strings.Contains(contentStr, "winerosetta.dll")
|
||||||
|
|
||||||
// Check if libSiliconPatch should be present based on user preference
|
// Validate dlls.txt: winerosetta must be present
|
||||||
prefs, _ := utils.LoadPrefs()
|
if winerosettaPresent {
|
||||||
libSiliconPatchRequired := prefs.EnableLibSiliconPatch
|
|
||||||
libSiliconPatchPresent := strings.Contains(contentStr, "libSiliconPatch.dll")
|
|
||||||
|
|
||||||
// Validate dlls.txt: winerosetta must be present, libSiliconPatch based on setting
|
|
||||||
if winerosettaPresent && (!libSiliconPatchRequired || libSiliconPatchPresent) {
|
|
||||||
dllsFileValid = true
|
dllsFileValid = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for Epoch-specific files
|
||||||
|
epochPatchesApplied := false
|
||||||
|
stats, err := epoch.Update(paths.EpochPath, false, true, true)
|
||||||
|
if err != nil {
|
||||||
|
debug.Printf("Failed to get download Epoch patches: %v", err)
|
||||||
|
}
|
||||||
|
if stats.Outdated == 0 {
|
||||||
|
debug.Println("Nothing is outdated")
|
||||||
|
epochPatchesApplied = true
|
||||||
|
}
|
||||||
|
|
||||||
// Check if patched files have the correct size (matches bundled versions)
|
// Check if patched files have the correct size (matches bundled versions)
|
||||||
winerosettaDllCorrectSize := utils.CompareFileWithBundledResource(winerosettaDllPath, "winerosetta/winerosetta.dll")
|
winerosettaDllCorrectSize := utils.CompareFileWithBundledResource(winerosettaDllPath, "winerosetta/winerosetta.dll")
|
||||||
d3d9DllCorrectSize := utils.CompareFileWithBundledResource(d3d9DllPath, "winerosetta/d3d9.dll")
|
d3d9DllCorrectSize := utils.CompareFileWithBundledResource(d3d9DllPath, "winerosetta/d3d9.dll")
|
||||||
libSiliconPatchCorrectSize := utils.CompareFileWithBundledResource(libSiliconPatchDllPath, "winerosetta/libSiliconPatch.dll")
|
|
||||||
rosettaX87CorrectSize := utils.CompareFileWithBundledResource(rosettaX87ExePath, "rosettax87/rosettax87")
|
rosettaX87CorrectSize := utils.CompareFileWithBundledResource(rosettaX87ExePath, "rosettax87/rosettax87")
|
||||||
libRuntimeRosettaX87CorrectSize := utils.CompareFileWithBundledResource(libRuntimeRosettaX87Path, "rosettax87/libRuntimeRosettax87")
|
libRuntimeRosettaX87CorrectSize := utils.CompareFileWithBundledResource(libRuntimeRosettaX87Path, "rosettax87/libRuntimeRosettax87")
|
||||||
|
|
||||||
// Check if shadowLOD setting is applied (only if user has enabled it in graphics settings)
|
if utils.PathExists(winerosettaDllPath) && utils.PathExists(d3d9DllPath) &&
|
||||||
prefs, _ := utils.LoadPrefs()
|
|
||||||
shadowLODRequiredAndApplied := true // Default to true if not required
|
|
||||||
if prefs.SetShadowLOD0 {
|
|
||||||
shadowLODRequiredAndApplied = patching.CheckShadowLODSetting()
|
|
||||||
}
|
|
||||||
|
|
||||||
if utils.PathExists(winerosettaDllPath) && utils.PathExists(d3d9DllPath) && utils.PathExists(libSiliconPatchDllPath) &&
|
|
||||||
utils.DirExists(rosettaX87DirPath) && utils.PathExists(rosettaX87ExePath) &&
|
utils.DirExists(rosettaX87DirPath) && utils.PathExists(rosettaX87ExePath) &&
|
||||||
utils.PathExists(libRuntimeRosettaX87Path) && dllsFileValid &&
|
utils.PathExists(libRuntimeRosettaX87Path) && dllsFileValid &&
|
||||||
winerosettaDllCorrectSize && d3d9DllCorrectSize && libSiliconPatchCorrectSize &&
|
winerosettaDllCorrectSize && d3d9DllCorrectSize &&
|
||||||
rosettaX87CorrectSize && libRuntimeRosettaX87CorrectSize && shadowLODRequiredAndApplied {
|
rosettaX87CorrectSize && libRuntimeRosettaX87CorrectSize && epochPatchesApplied {
|
||||||
paths.PatchesAppliedTurtleWoW = true
|
paths.PatchesAppliedEpoch = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
turtlewowPathLabel.Refresh()
|
|
||||||
|
|
||||||
if paths.PatchesAppliedTurtleWoW {
|
}
|
||||||
turtlewowStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
|
||||||
if patchTurtleWoWButton != nil {
|
epochPathLabel.Refresh()
|
||||||
patchTurtleWoWButton.Disable()
|
if paths.DownloadingPatches {
|
||||||
|
epochStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Downloading...", Style: widget.RichTextStyle{ColorName: theme.ColorNamePrimary}}}
|
||||||
|
if patchEpochButton != nil {
|
||||||
|
patchEpochButton.Disable()
|
||||||
}
|
}
|
||||||
if unpatchTurtleWoWButton != nil {
|
if unpatchEpochButton != nil {
|
||||||
unpatchTurtleWoWButton.Enable()
|
unpatchEpochButton.Disable()
|
||||||
|
}
|
||||||
|
} else if paths.PatchesAppliedEpoch {
|
||||||
|
epochStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameSuccess}}}
|
||||||
|
if patchEpochButton != nil {
|
||||||
|
patchEpochButton.Disable()
|
||||||
|
}
|
||||||
|
if unpatchEpochButton != nil {
|
||||||
|
unpatchEpochButton.Enable()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
turtlewowStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
epochStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
|
||||||
if patchTurtleWoWButton != nil {
|
if patchEpochButton != nil {
|
||||||
if paths.TurtlewowPath != "" {
|
if paths.EpochPath != "" {
|
||||||
patchTurtleWoWButton.Enable()
|
patchEpochButton.Enable()
|
||||||
} else {
|
} else {
|
||||||
patchTurtleWoWButton.Disable()
|
patchEpochButton.Disable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if unpatchTurtleWoWButton != nil {
|
if unpatchEpochButton != nil {
|
||||||
unpatchTurtleWoWButton.Disable()
|
unpatchEpochButton.Disable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
turtlewowStatusLabel.Refresh()
|
epochStatusLabel.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
// updatePlayButtonState enables/disables play and launch buttons based on current state
|
// updatePlayButtonState enables/disables play and launch buttons based on current state
|
||||||
func updatePlayButtonState() {
|
func updatePlayButtonState() {
|
||||||
launchEnabled := paths.PatchesAppliedTurtleWoW && paths.PatchesAppliedCrossOver &&
|
launchEnabled := paths.PatchesAppliedEpoch && paths.PatchesAppliedCrossOver &&
|
||||||
paths.TurtlewowPath != "" && paths.CrossoverPath != "" && service.IsServiceRunning()
|
paths.EpochPath != "" && paths.CrossoverPath != "" && service.IsServiceRunning()
|
||||||
|
|
||||||
if launchButton != nil {
|
if launchButton != nil {
|
||||||
if launchEnabled {
|
if launchEnabled {
|
||||||
@@ -239,7 +246,7 @@ func updateServiceStatus() {
|
|||||||
serviceStatusLabel.Refresh()
|
serviceStatusLabel.Refresh()
|
||||||
}
|
}
|
||||||
if startServiceButton != nil {
|
if startServiceButton != nil {
|
||||||
if paths.TurtlewowPath != "" && paths.PatchesAppliedTurtleWoW {
|
if paths.EpochPath != "" && paths.PatchesAppliedEpoch {
|
||||||
startServiceButton.Enable()
|
startServiceButton.Enable()
|
||||||
} else {
|
} else {
|
||||||
startServiceButton.Disable()
|
startServiceButton.Disable()
|
||||||
|
26
pkg/ui/ui.go
26
pkg/ui/ui.go
@@ -1,10 +1,8 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/paths"
|
||||||
"turtlesilicon/pkg/patching"
|
"epochsilicon/pkg/utils"
|
||||||
"turtlesilicon/pkg/paths"
|
|
||||||
"turtlesilicon/pkg/utils"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
@@ -14,15 +12,15 @@ import (
|
|||||||
func CreateUI(myWindow fyne.Window) fyne.CanvasObject {
|
func CreateUI(myWindow fyne.Window) fyne.CanvasObject {
|
||||||
// Initialize UI component variables
|
// Initialize UI component variables
|
||||||
crossoverPathLabel = widget.NewRichText()
|
crossoverPathLabel = widget.NewRichText()
|
||||||
turtlewowPathLabel = widget.NewRichText()
|
epochPathLabel = widget.NewRichText()
|
||||||
turtlewowStatusLabel = widget.NewRichText()
|
epochStatusLabel = widget.NewRichText()
|
||||||
crossoverStatusLabel = widget.NewRichText()
|
crossoverStatusLabel = widget.NewRichText()
|
||||||
serviceStatusLabel = widget.NewRichText()
|
serviceStatusLabel = widget.NewRichText()
|
||||||
|
|
||||||
// Load saved paths from prefs
|
// Load saved paths from prefs
|
||||||
prefs, _ := utils.LoadPrefs()
|
prefs, _ := utils.LoadPrefs()
|
||||||
if prefs.TurtleWoWPath != "" {
|
if prefs.EpochPath != "" {
|
||||||
paths.TurtlewowPath = prefs.TurtleWoWPath
|
paths.EpochPath = prefs.EpochPath
|
||||||
}
|
}
|
||||||
if prefs.CrossOverPath != "" {
|
if prefs.CrossOverPath != "" {
|
||||||
paths.CrossoverPath = prefs.CrossOverPath
|
paths.CrossoverPath = prefs.CrossOverPath
|
||||||
@@ -37,18 +35,6 @@ func CreateUI(myWindow fyne.Window) fyne.CanvasObject {
|
|||||||
// Check default CrossOver path
|
// Check default CrossOver path
|
||||||
paths.CheckDefaultCrossOverPath()
|
paths.CheckDefaultCrossOverPath()
|
||||||
|
|
||||||
// Check graphics settings presence and set default state
|
|
||||||
patching.CheckGraphicsSettingsPresence()
|
|
||||||
|
|
||||||
// Load graphics settings from Config.wtf and update UI
|
|
||||||
if err := patching.LoadGraphicsSettingsFromConfig(); err != nil {
|
|
||||||
// Log error but continue - this is not critical for app startup
|
|
||||||
debug.Printf("Warning: failed to load graphics settings from Config.wtf: %v", err)
|
|
||||||
} else {
|
|
||||||
// Refresh checkbox states to reflect loaded settings
|
|
||||||
refreshGraphicsSettingsCheckboxes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create header, main content and bottom bar
|
// Create header, main content and bottom bar
|
||||||
headerContent := createHeaderContainer()
|
headerContent := createHeaderContainer()
|
||||||
mainContent := createMainContent(myWindow)
|
mainContent := createMainContent(myWindow)
|
||||||
|
@@ -5,8 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
"turtlesilicon/pkg/utils"
|
"epochsilicon/pkg/utils"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
@@ -11,8 +11,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
// Status labels
|
// Status labels
|
||||||
crossoverPathLabel *widget.RichText
|
crossoverPathLabel *widget.RichText
|
||||||
turtlewowPathLabel *widget.RichText
|
epochPathLabel *widget.RichText
|
||||||
turtlewowStatusLabel *widget.RichText
|
epochStatusLabel *widget.RichText
|
||||||
crossoverStatusLabel *widget.RichText
|
crossoverStatusLabel *widget.RichText
|
||||||
serviceStatusLabel *widget.RichText
|
serviceStatusLabel *widget.RichText
|
||||||
|
|
||||||
@@ -20,9 +20,9 @@ var (
|
|||||||
launchButton *widget.Button
|
launchButton *widget.Button
|
||||||
playButton *widget.Button
|
playButton *widget.Button
|
||||||
playButtonText *widget.RichText
|
playButtonText *widget.RichText
|
||||||
patchTurtleWoWButton *widget.Button
|
patchEpochButton *widget.Button
|
||||||
patchCrossOverButton *widget.Button
|
patchCrossOverButton *widget.Button
|
||||||
unpatchTurtleWoWButton *widget.Button
|
unpatchEpochButton *widget.Button
|
||||||
unpatchCrossOverButton *widget.Button
|
unpatchCrossOverButton *widget.Button
|
||||||
startServiceButton *widget.Button
|
startServiceButton *widget.Button
|
||||||
stopServiceButton *widget.Button
|
stopServiceButton *widget.Button
|
||||||
@@ -30,7 +30,6 @@ var (
|
|||||||
// Option checkboxes
|
// Option checkboxes
|
||||||
metalHudCheckbox *widget.Check
|
metalHudCheckbox *widget.Check
|
||||||
showTerminalCheckbox *widget.Check
|
showTerminalCheckbox *widget.Check
|
||||||
vanillaTweaksCheckbox *widget.Check
|
|
||||||
autoDeleteWdbCheckbox *widget.Check
|
autoDeleteWdbCheckbox *widget.Check
|
||||||
|
|
||||||
// Recommended settings button
|
// Recommended settings button
|
||||||
@@ -45,19 +44,6 @@ var (
|
|||||||
// Environment variables entry
|
// Environment variables entry
|
||||||
envVarsEntry *widget.Entry
|
envVarsEntry *widget.Entry
|
||||||
|
|
||||||
// Graphics settings checkboxes
|
|
||||||
reduceTerrainDistanceCheckbox *widget.Check
|
|
||||||
setMultisampleTo2xCheckbox *widget.Check
|
|
||||||
setShadowLOD0Checkbox *widget.Check
|
|
||||||
libSiliconPatchCheckbox *widget.Check
|
|
||||||
applyGraphicsSettingsButton *widget.Button
|
|
||||||
|
|
||||||
// Graphics settings help buttons
|
|
||||||
reduceTerrainDistanceHelpButton *widget.Button
|
|
||||||
setMultisampleTo2xHelpButton *widget.Button
|
|
||||||
setShadowLOD0HelpButton *widget.Button
|
|
||||||
libSiliconPatchHelpButton *widget.Button
|
|
||||||
|
|
||||||
// Window reference for popup functionality
|
// Window reference for popup functionality
|
||||||
currentWindow fyne.Window
|
currentWindow fyne.Window
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
serviceName = "TurtleSilicon"
|
serviceName = "EpochSilicon"
|
||||||
accountName = "sudo_password"
|
accountName = "sudo_password"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -8,25 +8,14 @@ import (
|
|||||||
|
|
||||||
type UserPrefs struct {
|
type UserPrefs struct {
|
||||||
SuppressedUpdateVersion string `json:"suppressed_update_version"`
|
SuppressedUpdateVersion string `json:"suppressed_update_version"`
|
||||||
TurtleWoWPath string `json:"turtlewow_path"`
|
EpochPath string `json:"epoch_path"`
|
||||||
CrossOverPath string `json:"crossover_path"`
|
CrossOverPath string `json:"crossover_path"`
|
||||||
EnvironmentVariables string `json:"environment_variables"`
|
EnvironmentVariables string `json:"environment_variables"`
|
||||||
SaveSudoPassword bool `json:"save_sudo_password"`
|
SaveSudoPassword bool `json:"save_sudo_password"`
|
||||||
ShowTerminalNormally bool `json:"show_terminal_normally"`
|
ShowTerminalNormally bool `json:"show_terminal_normally"`
|
||||||
EnableVanillaTweaks bool `json:"enable_vanilla_tweaks"`
|
|
||||||
RemapOptionAsAlt bool `json:"remap_option_as_alt"`
|
RemapOptionAsAlt bool `json:"remap_option_as_alt"`
|
||||||
AutoDeleteWdb bool `json:"auto_delete_wdb"`
|
AutoDeleteWdb bool `json:"auto_delete_wdb"`
|
||||||
EnableMetalHud bool `json:"enable_metal_hud"`
|
EnableMetalHud bool `json:"enable_metal_hud"`
|
||||||
|
|
||||||
// Graphics settings
|
|
||||||
ReduceTerrainDistance bool `json:"reduce_terrain_distance"`
|
|
||||||
SetMultisampleTo2x bool `json:"set_multisample_to_2x"`
|
|
||||||
SetShadowLOD0 bool `json:"set_shadow_lod_0"`
|
|
||||||
EnableLibSiliconPatch bool `json:"enable_lib_silicon_patch"`
|
|
||||||
|
|
||||||
// Tracking whether user has manually disabled these settings
|
|
||||||
UserDisabledShadowLOD bool `json:"user_disabled_shadow_lod"`
|
|
||||||
UserDisabledLibSiliconPatch bool `json:"user_disabled_lib_silicon_patch"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPrefsPath() (string, error) {
|
func getPrefsPath() (string, error) {
|
||||||
@@ -34,7 +23,7 @@ func getPrefsPath() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return filepath.Join(dir, "TurtleSilicon", "prefs.json"), nil
|
return filepath.Join(dir, "EpochSilicon", "prefs.json"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadPrefs() (*UserPrefs, error) {
|
func LoadPrefs() (*UserPrefs, error) {
|
||||||
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"turtlesilicon/pkg/debug"
|
"epochsilicon/pkg/debug"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
@@ -130,7 +130,7 @@ func QuotePathForShell(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckForUpdate(currentVersion string) (latestVersion, releaseNotes string, updateAvailable bool, err error) {
|
func CheckForUpdate(currentVersion string) (latestVersion, releaseNotes string, updateAvailable bool, err error) {
|
||||||
resp, err := http.Get("https://api.github.com/repos/tairasu/TurtleSilicon/releases/latest")
|
resp, err := http.Get("https://api.github.com/repos/tairasu/EpochSilicon/releases/latest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", false, err
|
return "", "", false, err
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ type Asset struct {
|
|||||||
|
|
||||||
// CheckForUpdateWithAssets returns update information including download assets
|
// CheckForUpdateWithAssets returns update information including download assets
|
||||||
func CheckForUpdateWithAssets(currentVersion string) (*UpdateInfo, bool, error) {
|
func CheckForUpdateWithAssets(currentVersion string) (*UpdateInfo, bool, error) {
|
||||||
resp, err := http.Get("https://api.github.com/repos/tairasu/TurtleSilicon/releases/latest")
|
resp, err := http.Get("https://api.github.com/repos/tairasu/EpochSilicon/releases/latest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ func CheckForUpdateWithAssets(currentVersion string) (*UpdateInfo, bool, error)
|
|||||||
// DownloadUpdate downloads the latest release and returns the path to the downloaded file
|
// DownloadUpdate downloads the latest release and returns the path to the downloaded file
|
||||||
func DownloadUpdate(downloadURL string, progressCallback func(downloaded, total int64)) (string, error) {
|
func DownloadUpdate(downloadURL string, progressCallback func(downloaded, total int64)) (string, error) {
|
||||||
// Create temporary file
|
// Create temporary file
|
||||||
tempFile, err := os.CreateTemp("", "TurtleSilicon-update-*.dmg")
|
tempFile, err := os.CreateTemp("", "EpochSilicon-update-*.dmg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to create temp file: %v", err)
|
return "", fmt.Errorf("failed to create temp file: %v", err)
|
||||||
}
|
}
|
||||||
@@ -349,12 +349,12 @@ func InstallUpdate(dmgPath string) error {
|
|||||||
var newAppPath string
|
var newAppPath string
|
||||||
|
|
||||||
// First, try the exact name
|
// First, try the exact name
|
||||||
exactPath := filepath.Join(mountPoint, "TurtleSilicon.app")
|
exactPath := filepath.Join(mountPoint, "EpochSilicon.app")
|
||||||
if PathExists(exactPath) {
|
if PathExists(exactPath) {
|
||||||
newAppPath = exactPath
|
newAppPath = exactPath
|
||||||
} else {
|
} else {
|
||||||
// Search for any .app bundle in the mount point
|
// Search for any .app bundle in the mount point
|
||||||
debug.Printf("TurtleSilicon.app not found at exact path, searching for .app bundles")
|
debug.Printf("EpochSilicon.app not found at exact path, searching for .app bundles")
|
||||||
entries, err := os.ReadDir(mountPoint)
|
entries, err := os.ReadDir(mountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read DMG contents: %v", err)
|
return fmt.Errorf("failed to read DMG contents: %v", err)
|
||||||
@@ -406,7 +406,7 @@ func InstallUpdate(dmgPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fix executable permissions for the main binary
|
// Fix executable permissions for the main binary
|
||||||
executablePath := filepath.Join(currentAppPath, "Contents", "MacOS", "turtlesilicon")
|
executablePath := filepath.Join(currentAppPath, "Contents", "MacOS", "epochsilicon")
|
||||||
if PathExists(executablePath) {
|
if PathExists(executablePath) {
|
||||||
debug.Printf("Setting executable permissions for: %s", executablePath)
|
debug.Printf("Setting executable permissions for: %s", executablePath)
|
||||||
if err := os.Chmod(executablePath, 0755); err != nil {
|
if err := os.Chmod(executablePath, 0755); err != nil {
|
||||||
@@ -466,7 +466,7 @@ func TestDMGMount(dmgPath string) (string, string, error) {
|
|||||||
if infoErr == nil {
|
if infoErr == nil {
|
||||||
infoLines := strings.Split(string(infoOutput), "\n")
|
infoLines := strings.Split(string(infoOutput), "\n")
|
||||||
for _, line := range infoLines {
|
for _, line := range infoLines {
|
||||||
if strings.Contains(line, "/Volumes/") && strings.Contains(line, "TurtleSilicon") {
|
if strings.Contains(line, "/Volumes/") && strings.Contains(line, "EpochSilicon") {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
if strings.HasPrefix(line, "/Volumes/") {
|
if strings.HasPrefix(line, "/Volumes/") {
|
||||||
mountPoint = line
|
mountPoint = line
|
||||||
@@ -487,12 +487,12 @@ func TestDMGMount(dmgPath string) (string, string, error) {
|
|||||||
var newAppPath string
|
var newAppPath string
|
||||||
|
|
||||||
// First, try the exact name
|
// First, try the exact name
|
||||||
exactPath := filepath.Join(mountPoint, "TurtleSilicon.app")
|
exactPath := filepath.Join(mountPoint, "EpochSilicon.app")
|
||||||
if PathExists(exactPath) {
|
if PathExists(exactPath) {
|
||||||
newAppPath = exactPath
|
newAppPath = exactPath
|
||||||
} else {
|
} else {
|
||||||
// Search for any .app bundle in the mount point
|
// Search for any .app bundle in the mount point
|
||||||
debug.Printf("TurtleSilicon.app not found at exact path, searching for .app bundles")
|
debug.Printf("EpochSilicon.app not found at exact path, searching for .app bundles")
|
||||||
entries, err := os.ReadDir(mountPoint)
|
entries, err := os.ReadDir(mountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unmount before returning error
|
// Unmount before returning error
|
||||||
|
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user