CI Go Reference Go Report Card
A Go library and CLI tool for parsing, inspecting, downloading, and verifying Epic Games binary manifest files.
- Parse binary manifest files from disk or URL
- Inspect metadata, file lists, chunks, and custom fields
- Download game files with parallel chunk downloading, resume, and SHA1 verification
- Verify installed files against a manifest
- Compare two manifests to see what changed (delta)
- Apply delta manifests for efficient patching
- Export manifest data to JSON or CSV
- Cross-platform CLI with progress bars
Download a pre-built binary from GitHub Releases.
Or install with Go:
go install github.com/meszmate/manifest/cmd/manifest@latest
go get github.com/meszmate/manifest
manifest info path/to/file.manifest manifest info https://example.com/file.manifest
manifest list file.manifest
manifest list file.manifest --sort size --long
manifest list file.manifest --tag core --filter "*.pak"manifest download file.manifest \ --base-url http://download.epicgames.com/Builds/Fortnite/CloudDir/ \ --output ./game \ --concurrency 16
Use --dry-run to see what would be downloaded without actually downloading:
manifest download file.manifest --base-url http://example.com --dry-run
manifest verify file.manifest --path ./game
manifest delta old.manifest new.manifest manifest delta old.manifest new.manifest --format json
manifest chunks file.manifest --sort size
manifest export file.manifest --format json --output manifest.json manifest export file.manifest --format csv --output files.csv
Load from a file, URL, or any io.ReadSeeker:
// From a file m, err := manifest.ParseManifestFile("path/to/file.manifest") // From a URL m, err := manifest.ParseManifestURL(context.Background(), "https://example.com/file.manifest") // From raw bytes (e.g. already in memory) data, _ := os.ReadFile("file.manifest") m, err := manifest.ParseManifest(bytes.NewReader(data))
m, _ := manifest.ParseManifestFile("file.manifest") // Basic info fmt.Println(m.Metadata.AppName) // e.g. "Fortnite" fmt.Println(m.Metadata.BuildVersion) // e.g. "++Fortnite+Release-29.10-CL-33169942" fmt.Println(m.Metadata.BuildId) // unique build identifier fmt.Println(m.Metadata.AppID) // numeric app ID fmt.Println(m.Metadata.FeatureLevel) // e.g. EFeatureLevelStoresUniqueBuildId // Launch info fmt.Println(m.Metadata.LaunchExe) // e.g. "FortniteClient-Win64-Shipping.exe" fmt.Println(m.Metadata.LaunchCommand) // Prerequisites fmt.Println(m.Metadata.PrereqName) fmt.Println(m.Metadata.PrereqPath) fmt.Println(m.Metadata.PrereqArgs) fmt.Println(m.Metadata.PrereqIds) // Header details fmt.Println(m.Header.Version) // feature level fmt.Println(m.Header.StoredAs) // 0x01 = compressed, 0x02 = encrypted fmt.Printf("SHA: %x\n", m.Header.SHAHash) // manifest body hash // Aggregate sizes fmt.Printf("Download: %d bytes\n", m.TotalDownloadSize()) // sum of chunk file sizes fmt.Printf("Install: %d bytes\n", m.TotalInstallSize()) // sum of file sizes
for _, f := range m.FileManifestList.FileManifestList { fmt.Printf("%-60s %10d bytes %d chunks", f.FileName, f.FileSize, len(f.ChunkParts)) if len(f.InstallTags) > 0 { fmt.Printf(" tags: %v", f.InstallTags) } fmt.Println() } // Look up a specific file by path f := m.FileManifestList.GetFileByPath("FortniteGame/Content/Paks/pakchunk0-WindowsClient.pak") if f != nil { fmt.Printf("%s: %d bytes, SHA: %x\n", f.FileName, f.FileSize, f.SHAHash) } // Get all files matching an install tag coreFiles := m.FileManifestList.GetFilesByTag("core") fmt.Printf("%d files with 'core' tag\n", len(coreFiles))
for _, c := range m.ChunkDataList.Chunks { fmt.Printf("GUID: %s group: %02d size: %d hash: %016X sha: %x\n", c.GUID, c.Group, c.FileSize, c.Hash, c.SHAHash) } // Build a CDN URL for a chunk baseURL := "http://download.epicgames.com/Builds/Fortnite/CloudDir/" chunk := m.ChunkDataList.Chunks[0] url := m.ChunkURL(baseURL, chunk) fmt.Println(url) // full CDN URL including version subdirectory (ChunksV2/V3/V4) // Or build the URL directly from the chunk subDir := m.Metadata.FeatureLevel.ChunkSubDir() // "ChunksV4" url = chunk.GetURL(baseURL + subDir)
Each file is composed of one or more chunk parts. A chunk part is a byte range within a decompressed chunk:
f := m.FileManifestList.GetFileByPath("game/data.pak") for i, cp := range f.ChunkParts { fmt.Printf(" part %d: chunk %s offset: %d size: %d (chunk file size: %d)\n", i, cp.Chunk.GUID, cp.Offset, cp.Size, cp.Chunk.FileSize) }
for key, value := range m.CustomFields.Fields { fmt.Printf("%s = %s\n", key, value) }
import "github.com/meszmate/manifest/chunks" // Download raw chunk bytes from CDN url := m.ChunkURL(baseURL, chunk) rawBytes, err := manifest.LoadURLBytes(context.Background(), url) // Decompress (handles zlib + offset detection) decompressed, err := chunks.Decompress(rawBytes) // Or parse the full chunk (header + body) from a reader reader, err := chunks.ParseChunk(bytes.NewReader(rawBytes))
For full control over the download process:
import "github.com/meszmate/manifest/chunks" baseURL := "http://download.epicgames.com/Builds/Fortnite/CloudDir/" for _, f := range m.FileManifestList.FileManifestList { outFile, _ := os.Create(f.FileName) for _, cp := range f.ChunkParts { // Download and decompress the chunk url := m.ChunkURL(baseURL, cp.Chunk) raw, _ := manifest.LoadURLBytes(context.Background(), url) data, _ := chunks.Decompress(raw) // Write only the relevant byte range from this chunk outFile.Write(data[cp.Offset : cp.Offset+cp.Size]) } outFile.Close() }
The downloader package handles parallelism, retries, resume, deduplication, and verification:
import "github.com/meszmate/manifest/downloader" d := downloader.New(m, downloader.Config{ OutputDir: "./game", BaseURL: "http://download.epicgames.com/Builds/Fortnite/CloudDir/", Concurrency: 16, // parallel chunk downloads MaxRetries: 5, // retries per chunk with exponential backoff RetryBaseDelay: time.Second, VerifyChunks: true, // SHA1 verify each chunk Resume: true, // skip already-downloaded chunks Tags: []string{}, // empty = all files ExcludeTags: []string{"highres", "ondemand"}, // skip optional content }) result, err := d.Download(context.Background()) fmt.Printf("Downloaded %d files (%d chunks, %d skipped) in %s\n", result.TotalFiles, result.TotalChunks, result.SkippedChunks, result.Duration)
// Load the new and old manifests newManifest, _ := manifest.ParseManifestFile("new.manifest") oldManifest, _ := manifest.ParseManifestFile("old.manifest") // Download the delta manifest deltaBytes, err := manifest.GetDeltaManifest( context.Background(), "https://cdn.example.com/base", newManifest.Metadata.BuildId, oldManifest.Metadata.BuildId, ) // Parse and apply the delta deltaManifest, _ := manifest.ParseManifest(bytes.NewReader(deltaBytes)) newManifest.ApplyDelta(deltaManifest) // newManifest now contains updated file entries and new chunks
result, err := downloader.VerifyInstallation(m, "./game") if err != nil { log.Fatal(err) } fmt.Printf("Valid: %d, Missing: %d, Invalid: %d\n", result.ValidFiles, result.MissingFiles, result.InvalidFiles) for _, d := range result.Details { if d.Status != downloader.StatusOK { fmt.Printf(" %s: %s\n", d.Status, d.Path) } }
Performance benchmarks for common CDN endpoints:
http://download.epicgames.com/Builds/Fortnite/CloudDir/ 20-21 ms
http://fastly-download.epicgames.com/Builds/Fortnite/CloudDir/ 19-20 ms
http://epicgames-download1.akamaized.net/Builds/Fortnite/CloudDir/ 27-28 ms
http://cloudflare.epicgamescdn.com/Builds/Fortnite/CloudDir/ 34-36 ms
The --base-url flag is required because the CDN URL is game-specific and comes from the Epic Games API (which requires authentication). Auto-discovery is out of scope for this tool.
MIT