929 lines
31 KiB
Go
929 lines
31 KiB
Go
package patching
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"turtlesilicon/pkg/debug"
|
|
"turtlesilicon/pkg/paths" // Corrected import path
|
|
"turtlesilicon/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)
|
|
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")
|
|
filesToCopy := map[string]string{
|
|
"winerosetta/winerosetta.dll": targetWinerosettaDll,
|
|
"winerosetta/d3d9.dll": targetD3d9Dll,
|
|
"winerosetta/libSiliconPatch.dll": targetLibSiliconPatchDll,
|
|
}
|
|
|
|
for resourceName, destPath := range filesToCopy {
|
|
debug.Printf("Processing resource: %s to %s", resourceName, destPath)
|
|
|
|
// Check if file already exists and has correct size
|
|
if utils.PathExists(destPath) && utils.CompareFileWithBundledResource(destPath, resourceName) {
|
|
debug.Printf("File %s already exists with correct size, skipping copy", destPath)
|
|
continue
|
|
}
|
|
|
|
if utils.PathExists(destPath) {
|
|
debug.Printf("File %s exists but has incorrect size, updating...", destPath)
|
|
} else {
|
|
debug.Printf("File %s does not exist, creating...", destPath)
|
|
}
|
|
|
|
resource, err := fyne.LoadResourceFromPath(resourceName)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedTurtleWoW = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
destinationFile, err := os.Create(destPath)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedTurtleWoW = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
defer destinationFile.Close()
|
|
|
|
_, err = io.Copy(destinationFile, bytes.NewReader(resource.Content()))
|
|
if err != nil {
|
|
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
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
debug.Printf("Successfully copied %s to %s", resourceName, destPath)
|
|
}
|
|
|
|
debug.Printf("Preparing rosettax87 directory at: %s", targetRosettaX87Dir)
|
|
if err := os.RemoveAll(targetRosettaX87Dir); err != nil {
|
|
debug.Printf("Warning: could not remove existing rosettax87 folder '%s': %v", targetRosettaX87Dir, err)
|
|
}
|
|
if err := os.MkdirAll(targetRosettaX87Dir, 0755); err != nil {
|
|
errMsg := fmt.Sprintf("failed to create directory %s: %v", targetRosettaX87Dir, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedTurtleWoW = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
rosettaFilesToCopy := map[string]string{
|
|
"rosettax87/rosettax87": filepath.Join(targetRosettaX87Dir, "rosettax87"),
|
|
"rosettax87/libRuntimeRosettax87": filepath.Join(targetRosettaX87Dir, "libRuntimeRosettax87"),
|
|
}
|
|
|
|
for resourceName, destPath := range rosettaFilesToCopy {
|
|
debug.Printf("Processing rosetta resource: %s to %s", resourceName, destPath)
|
|
resource, err := fyne.LoadResourceFromPath(resourceName)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to open bundled resource %s: %v", resourceName, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedTurtleWoW = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
destinationFile, err := os.Create(destPath)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to create destination file %s: %v", destPath, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedTurtleWoW = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
_, err = io.Copy(destinationFile, bytes.NewReader(resource.Content()))
|
|
if err != nil {
|
|
destinationFile.Close()
|
|
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
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
destinationFile.Close()
|
|
|
|
if filepath.Base(destPath) == "rosettax87" {
|
|
debug.Printf("Setting execute permission for %s", destPath)
|
|
if err := os.Chmod(destPath, 0755); err != nil {
|
|
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
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
}
|
|
debug.Printf("Successfully copied %s to %s", resourceName, destPath)
|
|
}
|
|
|
|
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)
|
|
if strings.Contains(fileContent, winerosettaEntry) {
|
|
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 {
|
|
var fileContentBytes []byte
|
|
var err error
|
|
if utils.PathExists(dllsTextFile) {
|
|
fileContentBytes, err = os.ReadFile(dllsTextFile)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to read dlls.txt for update: %v", err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
}
|
|
}
|
|
|
|
currentContent := string(fileContentBytes)
|
|
updatedContent := currentContent
|
|
|
|
if len(updatedContent) > 0 && !strings.HasSuffix(updatedContent, "\n") {
|
|
updatedContent += "\n"
|
|
}
|
|
|
|
if needsWinerosettaUpdate {
|
|
if !strings.Contains(updatedContent, winerosettaEntry+"\n") {
|
|
updatedContent += winerosettaEntry + "\n"
|
|
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)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
} else {
|
|
debug.Printf("Successfully updated dlls.txt")
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
} 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)
|
|
updateAllStatuses()
|
|
}
|
|
|
|
func PatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
|
|
debug.Println("Patch CrossOver clicked")
|
|
if paths.CrossoverPath == "" {
|
|
dialog.ShowError(fmt.Errorf("CrossOver path not set. Please set it first."), myWindow)
|
|
return
|
|
}
|
|
|
|
wineloaderBasePath := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application")
|
|
wineloaderOrig := filepath.Join(wineloaderBasePath, "wineloader")
|
|
wineloaderCopy := filepath.Join(wineloaderBasePath, "wineloader2")
|
|
|
|
if !utils.PathExists(wineloaderOrig) {
|
|
dialog.ShowError(fmt.Errorf("original wineloader not found at %s", wineloaderOrig), myWindow)
|
|
paths.PatchesAppliedCrossOver = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
debug.Printf("Copying %s to %s", wineloaderOrig, wineloaderCopy)
|
|
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."
|
|
}
|
|
dialog.ShowError(fmt.Errorf(errMsg), myWindow)
|
|
paths.PatchesAppliedCrossOver = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
debug.Printf("Executing: codesign --remove-signature %s", wineloaderCopy)
|
|
cmd := exec.Command("codesign", "--remove-signature", wineloaderCopy)
|
|
combinedOutput, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
derrMsg := fmt.Sprintf("failed to remove signature from %s: %v\nOutput: %s", wineloaderCopy, err, string(combinedOutput))
|
|
dialog.ShowError(errors.New(derrMsg), myWindow)
|
|
debug.Println(derrMsg)
|
|
paths.PatchesAppliedCrossOver = false
|
|
if err := os.Remove(wineloaderCopy); err != nil {
|
|
debug.Printf("Warning: failed to cleanup wineloader2 after codesign failure: %v", err)
|
|
}
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
debug.Printf("codesign output: %s", string(combinedOutput))
|
|
|
|
debug.Printf("Setting execute permissions for %s", wineloaderCopy)
|
|
if err := os.Chmod(wineloaderCopy, 0755); err != nil {
|
|
errMsg := fmt.Sprintf("failed to set executable permissions for %s: %v", wineloaderCopy, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
paths.PatchesAppliedCrossOver = false
|
|
updateAllStatuses()
|
|
return
|
|
}
|
|
|
|
debug.Println("CrossOver patching completed successfully.")
|
|
paths.PatchesAppliedCrossOver = true
|
|
dialog.ShowInformation("Success", "CrossOver patching process completed.", myWindow)
|
|
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)
|
|
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")
|
|
|
|
// Remove the rosettaX87 directory
|
|
if utils.DirExists(rosettaX87DirPath) {
|
|
debug.Printf("Removing directory: %s", rosettaX87DirPath)
|
|
if err := os.RemoveAll(rosettaX87DirPath); err != nil {
|
|
errMsg := fmt.Sprintf("failed to remove directory %s: %v", rosettaX87DirPath, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
} else {
|
|
debug.Printf("Successfully removed directory: %s", rosettaX87DirPath)
|
|
}
|
|
}
|
|
|
|
// Remove DLL files
|
|
filesToRemove := []string{winerosettaDllPath, d3d9DllPath, libSiliconPatchDllPath}
|
|
for _, file := range filesToRemove {
|
|
if utils.PathExists(file) {
|
|
debug.Printf("Removing file: %s", file)
|
|
if err := os.Remove(file); err != nil {
|
|
errMsg := fmt.Sprintf("failed to remove file %s: %v", file, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
} else {
|
|
debug.Printf("Successfully removed file: %s", file)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update dlls.txt file - remove winerosetta.dll and libSiliconPatch.dll entries
|
|
if utils.PathExists(dllsTextFile) {
|
|
debug.Printf("Updating dlls.txt file: %s", dllsTextFile)
|
|
content, err := os.ReadFile(dllsTextFile)
|
|
if err != nil {
|
|
errMsg := fmt.Sprintf("failed to read dlls.txt file: %v", err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
} else {
|
|
lines := strings.Split(string(content), "\n")
|
|
filteredLines := make([]string, 0, len(lines))
|
|
|
|
for _, line := range lines {
|
|
trimmedLine := strings.TrimSpace(line)
|
|
if trimmedLine != "winerosetta.dll" && trimmedLine != "libSiliconPatch.dll" {
|
|
filteredLines = append(filteredLines, line)
|
|
}
|
|
}
|
|
|
|
updatedContent := strings.Join(filteredLines, "\n")
|
|
if err := os.WriteFile(dllsTextFile, []byte(updatedContent), 0644); err != nil {
|
|
errMsg := fmt.Sprintf("failed to update dlls.txt file: %v", err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
} else {
|
|
debug.Printf("Successfully updated dlls.txt file")
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
updateAllStatuses()
|
|
}
|
|
|
|
func UnpatchCrossOver(myWindow fyne.Window, updateAllStatuses func()) {
|
|
debug.Println("Unpatch CrossOver clicked")
|
|
if paths.CrossoverPath == "" {
|
|
dialog.ShowError(fmt.Errorf("CrossOver path not set. Please set it first."), myWindow)
|
|
return
|
|
}
|
|
|
|
wineloaderCopy := filepath.Join(paths.CrossoverPath, "Contents", "SharedSupport", "CrossOver", "CrossOver-Hosted Application", "wineloader2")
|
|
|
|
if utils.PathExists(wineloaderCopy) {
|
|
debug.Printf("Removing file: %s", wineloaderCopy)
|
|
if err := os.Remove(wineloaderCopy); err != nil {
|
|
errMsg := fmt.Sprintf("failed to remove file %s: %v", wineloaderCopy, err)
|
|
dialog.ShowError(errors.New(errMsg), myWindow)
|
|
debug.Println(errMsg)
|
|
updateAllStatuses()
|
|
return
|
|
} else {
|
|
debug.Printf("Successfully removed file: %s", wineloaderCopy)
|
|
}
|
|
} else {
|
|
debug.Printf("File not found to remove: %s", wineloaderCopy)
|
|
}
|
|
|
|
debug.Println("CrossOver unpatching completed successfully.")
|
|
paths.PatchesAppliedCrossOver = false
|
|
dialog.ShowInformation("Success", "CrossOver unpatching process completed.", myWindow)
|
|
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
|
|
pattern := fmt.Sprintf(`SET\s+%s\s+"[^"]*"`, regexp.QuoteMeta(setting))
|
|
re := regexp.MustCompile(pattern)
|
|
|
|
newSetting := fmt.Sprintf(`SET %s "%s"`, setting, value)
|
|
|
|
if re.MatchString(configText) {
|
|
// Replace existing setting
|
|
configText = re.ReplaceAllString(configText, newSetting)
|
|
debug.Printf("Updated setting %s to %s", setting, value)
|
|
} else {
|
|
// Add new setting
|
|
if configText != "" && !strings.HasSuffix(configText, "\n") {
|
|
configText += "\n"
|
|
}
|
|
configText += newSetting + "\n"
|
|
debug.Printf("Added new setting %s with value %s", setting, value)
|
|
}
|
|
|
|
return configText
|
|
}
|
|
|
|
// removeConfigSetting removes a setting from the config text
|
|
func removeConfigSetting(configText, setting string) string {
|
|
// Create regex pattern to match the setting
|
|
pattern := fmt.Sprintf(`SET\s+%s\s+"[^"]*"[\r\n]*`, regexp.QuoteMeta(setting))
|
|
re := regexp.MustCompile(pattern)
|
|
|
|
if re.MatchString(configText) {
|
|
configText = re.ReplaceAllString(configText, "")
|
|
debug.Printf("Removed setting %s from config", setting)
|
|
}
|
|
|
|
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
|
|
pattern := fmt.Sprintf(`SET\s+%s\s+"([^"]*)"`, regexp.QuoteMeta(setting))
|
|
re := regexp.MustCompile(pattern)
|
|
|
|
matches := re.FindStringSubmatch(configText)
|
|
if len(matches) < 2 {
|
|
return false
|
|
}
|
|
|
|
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
|
|
}
|