Epochify (#1)

Make it work for Epoch!

Reviewed-on: #1
This commit is contained in:
2025-07-22 20:47:04 +00:00
parent 488d10cb8b
commit 0967834f6b
30 changed files with 412 additions and 1446 deletions

View File

@@ -8,9 +8,9 @@ import (
"path/filepath"
"sync"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/paths" // Corrected import path
"turtlesilicon/pkg/utils" // Corrected import path
"epochsilicon/pkg/debug"
"epochsilicon/pkg/paths" // Corrected import path
"epochsilicon/pkg/utils" // Corrected import path
"fyne.io/fyne/v2"
"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)
return
}
if paths.TurtlewowPath == "" {
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it in the patcher."), myWindow)
if paths.EpochPath == "" {
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it in the patcher."), myWindow)
return
}
if !paths.PatchesAppliedTurtleWoW || !paths.PatchesAppliedCrossOver {
if !paths.PatchesAppliedEpoch || !paths.PatchesAppliedCrossOver {
confirmed := false
dialog.ShowConfirm("Warning", "Not all patches confirmed applied. Continue with launch?", func(c bool) {
confirmed = c
@@ -132,28 +132,9 @@ func LaunchGame(myWindow fyne.Window) {
}
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
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")
}
wowExePath := filepath.Join(paths.EpochPath, "Project-Epoch.exe")
// Continue with normal launch process
continueLaunch(myWindow, wowExePath)
@@ -161,12 +142,12 @@ func LaunchGame(myWindow fyne.Window) {
// continueLaunch continues the game launch process with the specified executable
func continueLaunch(myWindow fyne.Window, wowExePath string) {
rosettaInTurtlePath := filepath.Join(paths.TurtlewowPath, "rosettax87")
rosettaExecutable := filepath.Join(rosettaInTurtlePath, "rosettax87")
rosettaInEpochPath := filepath.Join(paths.EpochPath, "rosettax87")
rosettaExecutable := filepath.Join(rosettaInEpochPath, "rosettax87")
wineloader2Path := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application", "wineloader2")
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
}
if !utils.PathExists(wineloader2Path) {
@@ -174,13 +155,13 @@ func continueLaunch(myWindow fyne.Window, wowExePath string) {
return
}
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
}
// Auto-delete WDB directory if enabled
if AutoDeleteWdb {
wdbPath := filepath.Join(paths.TurtlewowPath, "WDB")
wdbPath := filepath.Join(paths.EpochPath, "WDB")
if utils.DirExists(wdbPath) {
debug.Printf("Auto-deleting WDB directory: %s", wdbPath)
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
debug.Println("RosettaX87 service is running. Proceeding to launch WoW.")
if paths.CrossoverPath == "" || paths.TurtlewowPath == "" {
dialog.ShowError(fmt.Errorf("CrossOver path or TurtleWoW path is not set. Cannot launch WoW."), myWindow)
if paths.CrossoverPath == "" || paths.EpochPath == "" {
dialog.ShowError(fmt.Errorf("CrossOver path or Epoch path is not set. Cannot launch WoW."), myWindow)
return
}
@@ -214,7 +195,7 @@ func continueLaunch(myWindow fyne.Window, wowExePath string) {
}
shellCmd := fmt.Sprintf(`cd %s && %s %s %s %s`,
utils.QuotePathForShell(paths.TurtlewowPath),
utils.QuotePathForShell(paths.EpochPath),
envVars,
utils.QuotePathForShell(rosettaExecutable),
utils.QuotePathForShell(wineloader2Path),

View File

@@ -7,8 +7,8 @@ import (
"regexp"
"strings"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/paths"
"epochsilicon/pkg/debug"
"epochsilicon/pkg/paths"
)
// 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
// Returns true if all settings are correctly applied, false otherwise
func CheckRecommendedSettings() bool {
if paths.TurtlewowPath == "" {
debug.Printf("TurtleWoW path not set, cannot check Config.wtf")
if paths.EpochPath == "" {
debug.Printf("Epoch path not set, cannot check Config.wtf")
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) {
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
func ApplyRecommendedSettings() error {
if paths.TurtlewowPath == "" {
return fmt.Errorf("TurtleWoW path not set")
if paths.EpochPath == "" {
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
wtfDir := filepath.Dir(configPath)

View File

@@ -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)
}

View File

@@ -4,37 +4,37 @@ import (
"bytes"
"errors"
"fmt"
"git.burkey.co/eburk/epochcli/pkg/epoch"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/paths" // Corrected import path
"turtlesilicon/pkg/utils" // Corrected import path
"epochsilicon/pkg/debug"
"epochsilicon/pkg/paths" // Corrected import path
"epochsilicon/pkg/utils" // Corrected import path
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
)
func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
debug.Println("Patch TurtleWoW clicked")
if paths.TurtlewowPath == "" {
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first."), myWindow)
func PatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
debug.Println("Patch Epoch clicked")
if paths.EpochPath == "" {
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first."), myWindow)
return
}
targetWinerosettaDll := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
targetD3d9Dll := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
targetLibSiliconPatchDll := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
targetRosettaX87Dir := filepath.Join(paths.TurtlewowPath, "rosettax87")
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
targetWinerosettaDll := filepath.Join(paths.EpochPath, "winerosetta.dll")
targetD3d9Dll := filepath.Join(paths.EpochPath, "d3d9.dll")
targetRosettaX87Dir := filepath.Join(paths.EpochPath, "rosettax87")
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
filesToCopy := map[string]string{
"winerosetta/winerosetta.dll": targetWinerosettaDll,
"winerosetta/d3d9.dll": targetD3d9Dll,
"winerosetta/libSiliconPatch.dll": targetLibSiliconPatchDll,
"winerosetta/winerosetta.dll": targetWinerosettaDll,
"winerosetta/d3d9.dll": targetD3d9Dll,
}
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)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
@@ -67,7 +67,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
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)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
@@ -93,7 +93,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
@@ -110,7 +110,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
@@ -120,7 +120,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
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)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
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)
dialog.ShowError(errors.New(errMsg), myWindow)
debug.Println(errMsg)
paths.PatchesAppliedTurtleWoW = false
paths.PatchesAppliedEpoch = false
updateAllStatuses()
return
}
@@ -153,36 +153,7 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
debug.Printf("Checking dlls.txt file at: %s", dllsTextFile)
winerosettaEntry := "winerosetta.dll"
libSiliconPatchEntry := "libSiliconPatch.dll"
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 {
fileContent := string(fileContentBytes)
@@ -190,21 +161,11 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
debug.Printf("dlls.txt already contains %s", winerosettaEntry)
needsWinerosettaUpdate = false
}
if strings.Contains(fileContent, libSiliconPatchEntry) {
debug.Printf("dlls.txt already contains %s", libSiliconPatchEntry)
needsLibSiliconPatchUpdate = false
}
} else {
debug.Printf("dlls.txt not found, will create a new one")
}
// Only add libSiliconPatch if user wants it enabled
if !shouldEnableLibSiliconPatch {
needsLibSiliconPatchUpdate = false
debug.Printf("libSiliconPatch disabled by user preference, will not add to dlls.txt")
}
if needsWinerosettaUpdate || needsLibSiliconPatchUpdate {
if needsWinerosettaUpdate {
var fileContentBytes []byte
var err error
if utils.PathExists(dllsTextFile) {
@@ -229,12 +190,6 @@ func PatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
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 {
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
if !shouldEnableLibSiliconPatch {
if err := disableLibSiliconPatchInDlls(); err != nil {
debug.Printf("Warning: failed to remove libSiliconPatch from dlls.txt: %v", err)
}
}
debug.Println("Downloading updates from Project Epoch servers.")
// Always apply vertex animation shaders setting to Config.wtf
if err := applyVertexAnimShadersSetting(); err != nil {
debug.Printf("Warning: failed to apply vertex animation shaders setting to Config.wtf: %v", err)
// Continue with patching even if Config.wtf update fails
}
// Apply shadowLOD setting to Config.wtf for FPS optimization
// Use shouldEnableShadowLOD which accounts for first-time patching
if shouldEnableShadowLOD {
if err := applyShadowLODSetting(); err != nil {
debug.Printf("Warning: failed to apply shadowLOD setting to Config.wtf: %v", err)
// Continue with patching even if Config.wtf update fails
// TODO: Change from dialog to pulsing animation
dialog.ShowInformation("Downloading patches", "Downloading patches for Project Epoch, this will take some time. Please wait until the status changes to \"Patched\"", myWindow)
paths.DownloadingPatches = true
go func() {
stats, err := epoch.Update(paths.EpochPath, true, true, false)
if err != nil {
errMsg := fmt.Sprintf("failed to update Epoch files: %v", err)
fyne.Do(func() {
dialog.ShowError(errors.New(errMsg), myWindow)
})
paths.DownloadingPatches = false
log.Println(errMsg)
}
} else {
// If user has disabled shadowLOD, make sure it's removed from Config.wtf
if err := removeShadowLODSetting(); err != nil {
debug.Printf("Warning: failed to remove shadowLOD setting from Config.wtf: %v", err)
}
}
debug.Println("TurtleWoW patching with bundled resources completed successfully.")
dialog.ShowInformation("Success", "TurtleWoW patching process completed using bundled resources.", myWindow)
log.Printf("Successfully updated %d Epoch files", stats.Updated)
debug.Println("Epoch patching with bundled resources completed successfully.")
fyne.Do(func() {
dialog.ShowInformation("Success", "Epoch patching process completed.", myWindow)
})
fyne.DoAndWait(func() {
paths.DownloadingPatches = false
updateAllStatuses()
})
}()
updateAllStatuses()
}
@@ -299,7 +250,7 @@ func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
if err := utils.CopyFile(wineloaderOrig, wineloaderCopy); err != nil {
errMsg := fmt.Sprintf("failed to copy wineloader: %v", err)
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)
paths.PatchesAppliedCrossOver = false
@@ -339,19 +290,18 @@ func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
updateAllStatuses()
}
func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
debug.Println("Unpatch TurtleWoW clicked")
if paths.TurtlewowPath == "" {
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first."), myWindow)
func UnpatchEpoch(myWindow fyne.Window, updateAllStatuses func()) {
debug.Println("Unpatch Epoch clicked")
if paths.EpochPath == "" {
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first."), myWindow)
return
}
// Files to remove
winerosettaDllPath := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
d3d9DllPath := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
libSiliconPatchDllPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
rosettaX87DirPath := filepath.Join(paths.TurtlewowPath, "rosettax87")
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
winerosettaDllPath := filepath.Join(paths.EpochPath, "winerosetta.dll")
d3d9DllPath := filepath.Join(paths.EpochPath, "d3d9.dll")
rosettaX87DirPath := filepath.Join(paths.EpochPath, "rosettax87")
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
// Remove the rosettaX87 directory
if utils.DirExists(rosettaX87DirPath) {
@@ -366,7 +316,7 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
}
// Remove DLL files
filesToRemove := []string{winerosettaDllPath, d3d9DllPath, libSiliconPatchDllPath}
filesToRemove := []string{winerosettaDllPath, d3d9DllPath}
for _, file := range filesToRemove {
if utils.PathExists(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) {
debug.Printf("Updating dlls.txt file: %s", dllsTextFile)
content, err := os.ReadFile(dllsTextFile)
@@ -394,7 +344,7 @@ func UnpatchTurtleWoW(myWindow fyne.Window, updateAllStatuses func()) {
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
if trimmedLine != "winerosetta.dll" && trimmedLine != "libSiliconPatch.dll" {
if trimmedLine != "winerosetta.dll" {
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
prefs, _ := utils.LoadPrefs()
if prefs.SetShadowLOD0 {
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)
debug.Println("Epoch unpatching completed successfully.")
paths.PatchesAppliedEpoch = false
dialog.ShowInformation("Success", "Epoch unpatching process completed.", myWindow)
updateAllStatuses()
}
@@ -455,42 +396,6 @@ func UnpatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
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
func updateOrAddConfigSetting(configText, setting, value string) string {
// Create regex pattern to match the setting
@@ -529,27 +434,6 @@ func removeConfigSetting(configText, setting string) string {
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
func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
// Create regex pattern to match the setting
@@ -564,365 +448,3 @@ func isConfigSettingCorrect(configText, setting, expectedValue string) bool {
currentValue := matches[1]
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
}

View File

@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
@@ -18,11 +18,12 @@ const DefaultCrossOverPath = "/Applications/CrossOver.app"
var (
CrossoverPath string
TurtlewowPath string
PatchesAppliedTurtleWoW = false
EpochPath string
PatchesAppliedEpoch = false
PatchesAppliedCrossOver = false
RosettaX87ServiceRunning = false
ServiceStarting = false
DownloadingPatches = false
)
func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichText, updateAllStatuses func()) {
@@ -53,35 +54,35 @@ func SelectCrossOverPath(myWindow fyne.Window, crossoverPathLabel *widget.RichTe
}, 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) {
if err != nil {
dialog.ShowError(err, myWindow)
return
}
if uri == nil {
log.Println("TurtleWoW path selection cancelled.")
log.Println("Epoch path selection cancelled.")
updateAllStatuses()
return
}
selectedPath := uri.Path()
if utils.DirExists(selectedPath) {
TurtlewowPath = selectedPath
PatchesAppliedTurtleWoW = false
log.Println("TurtleWoW path set to:", TurtlewowPath)
EpochPath = selectedPath
PatchesAppliedEpoch = false
log.Println("Epoch path set to:", EpochPath)
// Save to prefs
prefs, _ := utils.LoadPrefs()
prefs.TurtleWoWPath = selectedPath
prefs.EpochPath = selectedPath
utils.SavePrefs(prefs)
} else {
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()
}, myWindow)
}
func UpdatePathLabels(crossoverPathLabel, turtlewowPathLabel *widget.RichText) {
func UpdatePathLabels(crossoverPathLabel, EpochPathLabel *widget.RichText) {
if CrossoverPath == "" {
crossoverPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
} else {
@@ -89,12 +90,12 @@ func UpdatePathLabels(crossoverPathLabel, turtlewowPathLabel *widget.RichText) {
}
crossoverPathLabel.Refresh()
if TurtlewowPath == "" {
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
if EpochPath == "" {
EpochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
} 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() {

View File

@@ -10,8 +10,8 @@ import (
"syscall"
"time"
"turtlesilicon/pkg/paths"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/paths"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
@@ -79,16 +79,16 @@ func isRosettaSocketActive() bool {
func StartRosettaX87Service(myWindow fyne.Window, updateAllStatuses func()) {
log.Println("Starting RosettaX87 service...")
if paths.TurtlewowPath == "" {
dialog.ShowError(fmt.Errorf("TurtleWoW path not set. Please set it first"), myWindow)
if paths.EpochPath == "" {
dialog.ShowError(fmt.Errorf("Epoch path not set. Please set it first"), myWindow)
return
}
rosettaX87Dir := filepath.Join(paths.TurtlewowPath, "rosettax87")
rosettaX87Dir := filepath.Join(paths.EpochPath, "rosettax87")
rosettaX87Exe := filepath.Join(rosettaX87Dir, "rosettax87")
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
}

View File

@@ -1,20 +1,18 @@
package ui
import (
"fmt"
"net/url"
"strings"
"time"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/launcher"
"turtlesilicon/pkg/patching"
"turtlesilicon/pkg/service"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/debug"
"epochsilicon/pkg/launcher"
"epochsilicon/pkg/patching"
"epochsilicon/pkg/service"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
)
@@ -43,17 +41,6 @@ func createOptionsComponents() {
})
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) {
launcher.AutoDeleteWdb = checked
// Save to preferences
@@ -65,41 +52,9 @@ func createOptionsComponents() {
autoDeleteWdbCheckbox.SetChecked(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
createWineRegistryComponents()
// Create graphics settings components
createGraphicsSettingsComponents()
// Load environment variables from preferences
if prefs.EnvironmentVariables != "" {
launcher.CustomEnvVars = prefs.EnvironmentVariables
@@ -120,11 +75,11 @@ func createOptionsComponents() {
// createPatchingButtons creates all patching-related buttons
func createPatchingButtons(myWindow fyne.Window) {
patchTurtleWoWButton = widget.NewButton("Patch TurtleWoW", func() {
patching.PatchTurtleWoW(myWindow, UpdateAllStatuses)
patchEpochButton = widget.NewButton("Patch Epoch", func() {
patching.PatchEpoch(myWindow, UpdateAllStatuses)
})
unpatchTurtleWoWButton = widget.NewButton("Unpatch TurtleWoW", func() {
patching.UnpatchTurtleWoW(myWindow, UpdateAllStatuses)
unpatchEpochButton = widget.NewButton("Unpatch Epoch", func() {
patching.UnpatchEpoch(myWindow, UpdateAllStatuses)
})
patchCrossOverButton = widget.NewButton("Patch CrossOver", func() {
patching.PatchCrossOver(myWindow, UpdateAllStatuses)
@@ -166,12 +121,12 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
showTroubleshootingPopup()
})
// GitHub button
githubButton := widget.NewButton("GitHub", func() {
githubURL := "https://github.com/tairasu/TurtleSilicon"
// Git button
gitButton := widget.NewButton("Source Code", func() {
githubURL := "https://git.burkey.co/eburk/epochsilicon"
parsedURL, err := url.Parse(githubURL)
if err != nil {
debug.Printf("Error parsing GitHub URL: %v", err)
debug.Printf("Error parsing git URL: %v", err)
return
}
fyne.CurrentApp().OpenURL(parsedURL)
@@ -193,7 +148,7 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
leftButtons := container.NewHBox(
optionsButton,
troubleshootingButton,
githubButton,
gitButton,
)
// Create the large play button with fixed size
@@ -484,187 +439,3 @@ func showRecommendedSettingsHelpPopup() {
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()
}

View File

@@ -1,8 +1,8 @@
package ui
import (
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/paths"
"epochsilicon/pkg/debug"
"epochsilicon/pkg/paths"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
@@ -13,11 +13,11 @@ import (
// createHeaderContainer creates the header with title and subtitle
func createHeaderContainer() fyne.CanvasObject {
// Main title
titleText := widget.NewRichTextFromMarkdown("# TurtleSilicon")
titleText := widget.NewRichTextFromMarkdown("# EpochSilicon")
titleText.Wrapping = fyne.TextWrapOff
// 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
// Create header container
@@ -57,15 +57,15 @@ func createLogoContainer() fyne.CanvasObject {
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 {
pathSelectionForm := widget.NewForm(
widget.NewFormItem("CrossOver Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
paths.SelectCrossOverPath(myWindow, crossoverPathLabel, UpdateAllStatuses)
}), crossoverPathLabel)),
widget.NewFormItem("TurtleWoW Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
paths.SelectTurtleWoWPath(myWindow, turtlewowPathLabel, UpdateAllStatuses)
}), turtlewowPathLabel)),
widget.NewFormItem("Warcraft Path:", container.NewBorder(nil, nil, nil, widget.NewButton("Set/Change", func() {
paths.SelectEpochPath(myWindow, epochPathLabel, UpdateAllStatuses)
}), epochPathLabel)),
)
return pathSelectionForm
@@ -76,7 +76,7 @@ func createPatchOperationsLayout() fyne.CanvasObject {
patchOperationsLayout := container.NewVBox(
widget.NewSeparator(),
container.NewGridWithColumns(4,
widget.NewLabel("TurtleWoW Patch:"), turtlewowStatusLabel, patchTurtleWoWButton, unpatchTurtleWoWButton,
widget.NewLabel("Epoch Patch:"), epochStatusLabel, patchEpochButton, unpatchEpochButton,
),
container.NewGridWithColumns(4,
widget.NewLabel("CrossOver Patch:"), crossoverStatusLabel, patchCrossOverButton, unpatchCrossOverButton,

View File

@@ -13,10 +13,8 @@ import (
"howett.net/plist"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/patching"
"turtlesilicon/pkg/paths"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/paths"
"epochsilicon/pkg/utils"
)
// showOptionsPopup creates and shows an integrated popup window for options
@@ -25,17 +23,6 @@ func showOptionsPopup() {
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
generalTitle := widget.NewLabel("General Settings")
generalTitle.TextStyle = fyne.TextStyle{Bold: true}
@@ -45,63 +32,11 @@ func showOptionsPopup() {
widget.NewSeparator(),
metalHudCheckbox,
showTerminalCheckbox,
vanillaTweaksCheckbox,
autoDeleteWdbCheckbox,
widget.NewSeparator(),
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
envVarsTitle := widget.NewLabel("Environment Variables")
envVarsTitle.TextStyle = fyne.TextStyle{Bold: true}
@@ -114,7 +49,6 @@ func showOptionsPopup() {
// Create tabs
tabs := container.NewAppTabs(
container.NewTabItem("General", container.NewScroll(generalContainer)),
container.NewTabItem("Graphics", container.NewScroll(graphicsContainer)),
container.NewTabItem("Environment", container.NewScroll(envVarsContainer)),
)
@@ -223,9 +157,9 @@ func showTroubleshootingPopup() {
// --- Delete WDB Directory ---
wdbDeleteButton = widget.NewButton("Delete", func() {
wdbPath := filepath.Join(paths.TurtlewowPath, "WDB")
wdbPath := filepath.Join(paths.EpochPath, "WDB")
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
}
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() {
homeDir, _ := os.UserHomeDir()
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."
dialog.NewConfirm("Delete Wine Prefixes", msg, func(confirm bool) {
if confirm {
@@ -255,7 +189,7 @@ func showTroubleshootingPopup() {
return
}
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
}
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)
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)
appMgmtNote := widget.NewLabel("Please ensure TurtleSilicon is enabled in System Settings > Privacy & Security > App Management.")
rowWine := container.NewBorder(nil, nil, widget.NewLabel("Delete Wine prefixes (~/.wine & Epoch/.wine):"), wineDeleteButton, nil)
appMgmtNote := widget.NewLabel("Please ensure EpochSilicon is enabled in System Settings > Privacy & Security > App Management.")
appMgmtNote.Wrapping = fyne.TextWrapWord
appMgmtNote.TextStyle = fyne.TextStyle{Italic: true}
@@ -288,11 +222,11 @@ func showTroubleshootingPopup() {
troubleshootingCloseButton = widget.NewButton("Close", func() {})
popupContent := container.NewBorder(
nil, // top
nil, // top
container.NewCenter(troubleshootingCloseButton), // bottom
nil, // left
nil, // right
container.NewPadded(scrollContainer), // center
nil, // left
nil, // right
container.NewPadded(scrollContainer), // center
)
windowSize := currentWindow.Content().Size()

View File

@@ -1,15 +1,16 @@
package ui
import (
"epochsilicon/pkg/debug"
"git.burkey.co/eburk/epochcli/pkg/epoch"
"os"
"path/filepath"
"strings"
"time"
"turtlesilicon/pkg/patching"
"turtlesilicon/pkg/paths"
"turtlesilicon/pkg/service"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/paths"
"epochsilicon/pkg/service"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
@@ -23,7 +24,7 @@ var (
// UpdateAllStatuses updates all UI components based on current application state
func UpdateAllStatuses() {
updateCrossoverStatus()
updateTurtleWoWStatus()
updateEpochStatus()
updatePlayButtonState()
updateServiceStatus()
@@ -76,20 +77,19 @@ func updateCrossoverStatus() {
crossoverStatusLabel.Refresh()
}
// updateTurtleWoWStatus updates TurtleWoW path and patch status
func updateTurtleWoWStatus() {
if paths.TurtlewowPath == "" {
turtlewowPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
paths.PatchesAppliedTurtleWoW = false // Reset if path is cleared
// updateEpochStatus updates Epoch path and patch status
func updateEpochStatus() {
if paths.EpochPath == "" {
epochPathLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not set", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
paths.PatchesAppliedEpoch = false // Reset if path is cleared
} 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
winerosettaDllPath := filepath.Join(paths.TurtlewowPath, "winerosetta.dll")
d3d9DllPath := filepath.Join(paths.TurtlewowPath, "d3d9.dll")
libSiliconPatchDllPath := filepath.Join(paths.TurtlewowPath, "libSiliconPatch.dll")
rosettaX87DirPath := filepath.Join(paths.TurtlewowPath, "rosettax87")
dllsTextFile := filepath.Join(paths.TurtlewowPath, "dlls.txt")
winerosettaDllPath := filepath.Join(paths.EpochPath, "winerosetta.dll")
d3d9DllPath := filepath.Join(paths.EpochPath, "d3d9.dll")
rosettaX87DirPath := filepath.Join(paths.EpochPath, "rosettax87")
dllsTextFile := filepath.Join(paths.EpochPath, "dlls.txt")
rosettaX87ExePath := filepath.Join(rosettaX87DirPath, "rosettax87")
libRuntimeRosettaX87Path := filepath.Join(rosettaX87DirPath, "libRuntimeRosettax87")
@@ -99,70 +99,77 @@ func updateTurtleWoWStatus() {
contentStr := string(fileContent)
winerosettaPresent := strings.Contains(contentStr, "winerosetta.dll")
// Check if libSiliconPatch should be present based on user preference
prefs, _ := utils.LoadPrefs()
libSiliconPatchRequired := prefs.EnableLibSiliconPatch
libSiliconPatchPresent := strings.Contains(contentStr, "libSiliconPatch.dll")
// Validate dlls.txt: winerosetta must be present, libSiliconPatch based on setting
if winerosettaPresent && (!libSiliconPatchRequired || libSiliconPatchPresent) {
// Validate dlls.txt: winerosetta must be present
if winerosettaPresent {
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)
winerosettaDllCorrectSize := utils.CompareFileWithBundledResource(winerosettaDllPath, "winerosetta/winerosetta.dll")
d3d9DllCorrectSize := utils.CompareFileWithBundledResource(d3d9DllPath, "winerosetta/d3d9.dll")
libSiliconPatchCorrectSize := utils.CompareFileWithBundledResource(libSiliconPatchDllPath, "winerosetta/libSiliconPatch.dll")
rosettaX87CorrectSize := utils.CompareFileWithBundledResource(rosettaX87ExePath, "rosettax87/rosettax87")
libRuntimeRosettaX87CorrectSize := utils.CompareFileWithBundledResource(libRuntimeRosettaX87Path, "rosettax87/libRuntimeRosettax87")
// Check if shadowLOD setting is applied (only if user has enabled it in graphics settings)
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) &&
if utils.PathExists(winerosettaDllPath) && utils.PathExists(d3d9DllPath) &&
utils.DirExists(rosettaX87DirPath) && utils.PathExists(rosettaX87ExePath) &&
utils.PathExists(libRuntimeRosettaX87Path) && dllsFileValid &&
winerosettaDllCorrectSize && d3d9DllCorrectSize && libSiliconPatchCorrectSize &&
rosettaX87CorrectSize && libRuntimeRosettaX87CorrectSize && shadowLODRequiredAndApplied {
paths.PatchesAppliedTurtleWoW = true
winerosettaDllCorrectSize && d3d9DllCorrectSize &&
rosettaX87CorrectSize && libRuntimeRosettaX87CorrectSize && epochPatchesApplied {
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 {
patchTurtleWoWButton.Disable()
}
epochPathLabel.Refresh()
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 {
unpatchTurtleWoWButton.Enable()
if unpatchEpochButton != nil {
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 {
turtlewowStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
if patchTurtleWoWButton != nil {
if paths.TurtlewowPath != "" {
patchTurtleWoWButton.Enable()
epochStatusLabel.Segments = []widget.RichTextSegment{&widget.TextSegment{Text: "Not patched", Style: widget.RichTextStyle{ColorName: theme.ColorNameError}}}
if patchEpochButton != nil {
if paths.EpochPath != "" {
patchEpochButton.Enable()
} else {
patchTurtleWoWButton.Disable()
patchEpochButton.Disable()
}
}
if unpatchTurtleWoWButton != nil {
unpatchTurtleWoWButton.Disable()
if unpatchEpochButton != nil {
unpatchEpochButton.Disable()
}
}
turtlewowStatusLabel.Refresh()
epochStatusLabel.Refresh()
}
// updatePlayButtonState enables/disables play and launch buttons based on current state
func updatePlayButtonState() {
launchEnabled := paths.PatchesAppliedTurtleWoW && paths.PatchesAppliedCrossOver &&
paths.TurtlewowPath != "" && paths.CrossoverPath != "" && service.IsServiceRunning()
launchEnabled := paths.PatchesAppliedEpoch && paths.PatchesAppliedCrossOver &&
paths.EpochPath != "" && paths.CrossoverPath != "" && service.IsServiceRunning()
if launchButton != nil {
if launchEnabled {
@@ -239,7 +246,7 @@ func updateServiceStatus() {
serviceStatusLabel.Refresh()
}
if startServiceButton != nil {
if paths.TurtlewowPath != "" && paths.PatchesAppliedTurtleWoW {
if paths.EpochPath != "" && paths.PatchesAppliedEpoch {
startServiceButton.Enable()
} else {
startServiceButton.Disable()

View File

@@ -1,10 +1,8 @@
package ui
import (
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/patching"
"turtlesilicon/pkg/paths"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/paths"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
@@ -14,15 +12,15 @@ import (
func CreateUI(myWindow fyne.Window) fyne.CanvasObject {
// Initialize UI component variables
crossoverPathLabel = widget.NewRichText()
turtlewowPathLabel = widget.NewRichText()
turtlewowStatusLabel = widget.NewRichText()
epochPathLabel = widget.NewRichText()
epochStatusLabel = widget.NewRichText()
crossoverStatusLabel = widget.NewRichText()
serviceStatusLabel = widget.NewRichText()
// Load saved paths from prefs
prefs, _ := utils.LoadPrefs()
if prefs.TurtleWoWPath != "" {
paths.TurtlewowPath = prefs.TurtleWoWPath
if prefs.EpochPath != "" {
paths.EpochPath = prefs.EpochPath
}
if prefs.CrossOverPath != "" {
paths.CrossoverPath = prefs.CrossOverPath
@@ -37,18 +35,6 @@ func CreateUI(myWindow fyne.Window) fyne.CanvasObject {
// Check default CrossOver path
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
headerContent := createHeaderContainer()
mainContent := createMainContent(myWindow)

View File

@@ -5,8 +5,8 @@ import (
"strings"
"time"
"turtlesilicon/pkg/debug"
"turtlesilicon/pkg/utils"
"epochsilicon/pkg/debug"
"epochsilicon/pkg/utils"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"

View File

@@ -11,8 +11,8 @@ import (
var (
// Status labels
crossoverPathLabel *widget.RichText
turtlewowPathLabel *widget.RichText
turtlewowStatusLabel *widget.RichText
epochPathLabel *widget.RichText
epochStatusLabel *widget.RichText
crossoverStatusLabel *widget.RichText
serviceStatusLabel *widget.RichText
@@ -20,9 +20,9 @@ var (
launchButton *widget.Button
playButton *widget.Button
playButtonText *widget.RichText
patchTurtleWoWButton *widget.Button
patchEpochButton *widget.Button
patchCrossOverButton *widget.Button
unpatchTurtleWoWButton *widget.Button
unpatchEpochButton *widget.Button
unpatchCrossOverButton *widget.Button
startServiceButton *widget.Button
stopServiceButton *widget.Button
@@ -30,7 +30,6 @@ var (
// Option checkboxes
metalHudCheckbox *widget.Check
showTerminalCheckbox *widget.Check
vanillaTweaksCheckbox *widget.Check
autoDeleteWdbCheckbox *widget.Check
// Recommended settings button
@@ -45,19 +44,6 @@ var (
// Environment variables 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
currentWindow fyne.Window

View File

@@ -8,7 +8,7 @@ import (
)
const (
serviceName = "TurtleSilicon"
serviceName = "EpochSilicon"
accountName = "sudo_password"
)

View File

@@ -8,25 +8,14 @@ import (
type UserPrefs struct {
SuppressedUpdateVersion string `json:"suppressed_update_version"`
TurtleWoWPath string `json:"turtlewow_path"`
EpochPath string `json:"epoch_path"`
CrossOverPath string `json:"crossover_path"`
EnvironmentVariables string `json:"environment_variables"`
SaveSudoPassword bool `json:"save_sudo_password"`
ShowTerminalNormally bool `json:"show_terminal_normally"`
EnableVanillaTweaks bool `json:"enable_vanilla_tweaks"`
RemapOptionAsAlt bool `json:"remap_option_as_alt"`
AutoDeleteWdb bool `json:"auto_delete_wdb"`
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) {
@@ -34,7 +23,7 @@ func getPrefsPath() (string, error) {
if err != nil {
return "", err
}
return filepath.Join(dir, "TurtleSilicon", "prefs.json"), nil
return filepath.Join(dir, "EpochSilicon", "prefs.json"), nil
}
func LoadPrefs() (*UserPrefs, error) {

View File

@@ -11,7 +11,7 @@ import (
"path/filepath"
"strings"
"turtlesilicon/pkg/debug"
"epochsilicon/pkg/debug"
"fyne.io/fyne/v2"
"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) {
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 {
return "", "", false, err
}
@@ -163,7 +163,7 @@ type Asset struct {
// CheckForUpdateWithAssets returns update information including download assets
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 {
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
func DownloadUpdate(downloadURL string, progressCallback func(downloaded, total int64)) (string, error) {
// Create temporary file
tempFile, err := os.CreateTemp("", "TurtleSilicon-update-*.dmg")
tempFile, err := os.CreateTemp("", "EpochSilicon-update-*.dmg")
if err != nil {
return "", fmt.Errorf("failed to create temp file: %v", err)
}
@@ -349,12 +349,12 @@ func InstallUpdate(dmgPath string) error {
var newAppPath string
// First, try the exact name
exactPath := filepath.Join(mountPoint, "TurtleSilicon.app")
exactPath := filepath.Join(mountPoint, "EpochSilicon.app")
if PathExists(exactPath) {
newAppPath = exactPath
} else {
// 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)
if err != nil {
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
executablePath := filepath.Join(currentAppPath, "Contents", "MacOS", "turtlesilicon")
executablePath := filepath.Join(currentAppPath, "Contents", "MacOS", "epochsilicon")
if PathExists(executablePath) {
debug.Printf("Setting executable permissions for: %s", executablePath)
if err := os.Chmod(executablePath, 0755); err != nil {
@@ -466,7 +466,7 @@ func TestDMGMount(dmgPath string) (string, string, error) {
if infoErr == nil {
infoLines := strings.Split(string(infoOutput), "\n")
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)
if strings.HasPrefix(line, "/Volumes/") {
mountPoint = line
@@ -487,12 +487,12 @@ func TestDMGMount(dmgPath string) (string, string, error) {
var newAppPath string
// First, try the exact name
exactPath := filepath.Join(mountPoint, "TurtleSilicon.app")
exactPath := filepath.Join(mountPoint, "EpochSilicon.app")
if PathExists(exactPath) {
newAppPath = exactPath
} else {
// 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)
if err != nil {
// Unmount before returning error