package main import ( "crypto/md5" "encoding/hex" "flag" "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "runtime" "strings" ) const ( manifestUrl = "https://updater.project-epoch.net/api/manifest" defaultWowPath = "/path/to/wow" defaultLaunchCmd = "not configured" ) func main() { var ( helpFlag bool updateOnlyFlag bool ) flag.BoolVar(&helpFlag, "h", false, "Print help") flag.BoolVar(&updateOnlyFlag, "u", false, "Only update the client, do not launch") flag.Parse() if helpFlag { flag.CommandLine.SetOutput(os.Stdout) flag.PrintDefaults() os.Exit(0) } config, err := setupConfig() if err != nil { log.Fatal(err) } if config.WowDir == defaultWowPath { log.Fatalf("WowDir in %s is still the default setting, exiting", cfgPath) } count, err := downloadUpdate(config) if err != nil { log.Fatal(err) } fmt.Printf("Updated %d files\n", count) if updateOnlyFlag { os.Exit(0) } if config.LaunchCmd == defaultLaunchCmd { log.Fatalf("LaunchCmd in %s is still the default setting, exiting\n", cfgPath) } fmt.Printf("Starting Epoch....\n", count) switch runtime.GOOS { case "darwin": exec.Command("open", config.LaunchCmd).Run() case "linux": exec.Command(config.LaunchCmd).Run() } } func downloadUpdate(config *Config) (int, error) { var c int manifest, err := getManifest() if err != nil { log.Fatalf("Failed to get manifest: %v\n", err) } for _, file := range manifest.Files { path := strings.ReplaceAll(file.Path, `\`, `/`) path = strings.TrimLeft(path, `\`) localPath := filepath.Join(config.WowDir, path) localDir := filepath.Dir(localPath) if _, err = os.Stat(localDir); os.IsNotExist(err) { os.MkdirAll(localDir, 0755) } if _, err = os.Stat(localPath); err == nil { data, err := os.ReadFile(localPath) if err != nil { return c, err } hashBytes := md5.Sum(data) hash := hex.EncodeToString(hashBytes[:]) if hash == file.Hash { continue } } fmt.Printf("Updating %s...\n", file.Path) outFile, err := os.Create(localPath) if err != nil { return c, err } defer outFile.Close() resp, err := http.Get(file.URL) if err != nil { return c, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return c, fmt.Errorf("failed to download update from %s, status code: %d", file.URL, resp.StatusCode) } _, err = io.Copy(outFile, resp.Body) if err != nil { return c, err } c += 1 } return c, nil }