package main import ( "crypto/md5" "encoding/hex" "fmt" "github.com/BurntSushi/toml" "io" "log" "net/http" "os" "path/filepath" "strings" ) const ( manifestUrl = "https://updater.project-epoch.net/api/manifest" configDirName = "epoch-linux" configName = "config.toml" ) type Config struct { WowDir string } var ( config Config ) func setupConfig() { home := os.Getenv("HOME") if home == "" { log.Fatal("$HOME environment variable not set") } cfgPath := filepath.Join(home, ".config", configDirName, configName) if _, statErr := os.Stat(cfgPath); os.IsNotExist(statErr) { os.MkdirAll(filepath.Join(home, ".config", configDirName), 0755) newConfig := &Config{ WowDir: "/path/to/wow", } file, err := os.Create(cfgPath) if err != nil { log.Fatal(err) } defer file.Close() encoder := toml.NewEncoder(file) if err = encoder.Encode(newConfig); err != nil { log.Fatal(err) } fmt.Printf("Created new config at %s, edit it before running the launcher again\n", cfgPath) os.Exit(0) } _, err := toml.DecodeFile(cfgPath, &config) if err != nil { log.Fatal(err) } } func main() { setupConfig() count, err := downloadUpdate() if err != nil { log.Fatal(err) } fmt.Printf("Updated %d files\n", count) } func downloadUpdate() (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 }