package epoch import ( "crypto/md5" "encoding/hex" "fmt" "io" "io/fs" "log" "net/http" "os" "path/filepath" "regexp" "strings" ) type DownloadStats struct { Updated int Current int Outdated int } func Update(wowdir string, force bool, removeUnknown bool, skipDownload bool) (DownloadStats, error) { var stats DownloadStats 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(wowdir, path) localDir := filepath.Dir(localPath) if _, err = os.Stat(localDir); os.IsNotExist(err) { err = os.MkdirAll(localDir, 0755) if err != nil { return stats, fmt.Errorf("failed to create directory %s: %v", localDir, err) } } if !force { if _, err = os.Stat(localPath); err == nil { data, err := os.ReadFile(localPath) if err != nil { return stats, err } hashBytes := md5.Sum(data) hash := hex.EncodeToString(hashBytes[:]) if hash == file.Hash { fmt.Printf("File %s is up to date\n", localPath) stats.Current += 1 continue } else { stats.Outdated += 1 } } } if !skipDownload { fmt.Printf("Updating %s...\n", localPath) outFile, err := os.Create(localPath) if err != nil { return stats, err } for _, url := range []string{file.Urls.Cloudflare, file.Urls.Digitalocean, file.Urls.None} { resp, err := http.Get(url) if err != nil { outFile.Close() return stats, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { outFile.Close() return stats, fmt.Errorf("failed to download update from %s, status code: %d", url, resp.StatusCode) } _, err = io.Copy(outFile, resp.Body) if err != nil { outFile.Close() return stats, err } break } outFile.Close() stats.Updated += 1 } } if removeUnknown { patches := make([]string, 0) patchreg := regexp.MustCompile(`patch-[A-Za-z].MPQ`) for _, file := range manifest.Files { if patchreg.MatchString(file.Path) { patches = append(patches, strings.Split(file.Path, "Data\\")[1]) } } err = filepath.WalkDir(filepath.Join(wowdir, "Data"), func(path string, d fs.DirEntry, err error) error { if !d.IsDir() && patchreg.MatchString(d.Name()) { del := true for _, patch := range patches { if patch == d.Name() { del = false break } } if del { err = os.Remove(path) if err != nil { return err } fmt.Println("Removed unknown patch", d.Name()) } } return nil }) if err != nil { log.Fatalf("failed to delete unknown patches: %s", err) } } return stats, nil }