package main import ( "bufio" "flag" "fmt" "log" "os" "path/filepath" "strings" ) func main() { // Define the --path flag rootPath := flag.String("path", ".", "Root path for patch application") flag.Parse() // Convert root path to an absolute path absoluteRootPath, err := filepath.Abs(*rootPath) if err != nil { log.Fatalf("Failed to resolve absolute path for root: %v", err) } fmt.Printf("Starting custom patcher...\nRoot path: %s\n", absoluteRootPath) patchDir := "./patches" var successfulPatches, failedPatches []string err = filepath.Walk(patchDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && strings.HasSuffix(info.Name(), ".patch") { fmt.Printf("Applying patch: %s\n", path) err := applyPatch(path, absoluteRootPath) if err != nil { fmt.Printf("Failed to apply patch %s: %v\n", path, err) failedPatches = append(failedPatches, path) } else { fmt.Printf("Successfully applied patch: %s\n", path) successfulPatches = append(successfulPatches, path) } } return nil }) if err != nil { log.Fatalf("Error reading patches: %v", err) } // Print the summary fmt.Println("\nSummary:") fmt.Printf("Successful patches (%d):\n", len(successfulPatches)) for _, patch := range successfulPatches { fmt.Printf(" - %s\n", patch) } fmt.Printf("Failed patches (%d):\n", len(failedPatches)) for _, patch := range failedPatches { fmt.Printf(" - %s\n", patch) } // Exit with appropriate status if len(failedPatches) > 0 { fmt.Println("\nPatch process completed with failures.") os.Exit(1) } else { fmt.Println("\nAll patches applied successfully.") os.Exit(0) } } // applyPatch processes a single patch file func applyPatch(patchPath, rootPath string) error { file, err := os.Open(patchPath) if err != nil { return fmt.Errorf("failed to open patch file: %v", err) } defer file.Close() scanner := bufio.NewScanner(file) var inputFilePath, outputFilePath string var modifications []string var patchType string // Parse the patch file for scanner.Scan() { line := scanner.Text() // Check for header type if strings.HasPrefix(line, "t:") && patchType == "" { patchType = strings.TrimSpace(strings.TrimPrefix(line, "t:")) fmt.Printf("Detected patch type: %s\n", patchType) continue } // Detect the input file if strings.HasPrefix(line, "i:") { relativePath := strings.TrimSpace(strings.TrimPrefix(line, "i:")) inputFilePath = filepath.Join(rootPath, relativePath) continue } // Detect the output file if strings.HasPrefix(line, "o:") { relativePath := strings.TrimSpace(strings.TrimPrefix(line, "o:")) outputFilePath = filepath.Join(rootPath, relativePath) continue } // Ignore comment lines if strings.HasPrefix(strings.TrimSpace(line), "//") { continue } // Collect modifications modifications = append(modifications, line) } if err := scanner.Err(); err != nil { return fmt.Errorf("failed to read patch file: %v", err) } if inputFilePath == "" || outputFilePath == "" { return fmt.Errorf("patch file must specify both input (i:) and output (o:) files") } // Replace the original file with the temporary file err = os.Rename(inputFilePath, outputFilePath) if err != nil { return fmt.Errorf("failed to replace output file: %v", err) } // Process based on patch type switch patchType { case "pref": return applyPrefModifications(outputFilePath, modifications) case "standard": return applyStandardModifications(outputFilePath, modifications) default: fmt.Printf("Type not specified defaulting to standard") return applyStandardModifications(outputFilePath, modifications) } }