Skip to content

Latest commit

 

History

63 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Epic Games Manifest Parser

CI Go Reference Go Report Card

A Go library and CLI tool for parsing, inspecting, downloading, and verifying Epic Games binary manifest files.

Features

  • 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

Installation

CLI (Binary)

Download a pre-built binary from GitHub Releases.

Or install with Go:

go install github.com/meszmate/manifest/cmd/manifest@latest

Library

go get github.com/meszmate/manifest

CLI Usage

Inspect a manifest

manifest info path/to/file.manifest
manifest info https://example.com/file.manifest

List files

manifest list file.manifest
manifest list file.manifest --sort size --long
manifest list file.manifest --tag core --filter "*.pak"

Download game files

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

Verify an installation

manifest verify file.manifest --path ./game

Compare two manifests

manifest delta old.manifest new.manifest
manifest delta old.manifest new.manifest --format json

List chunks

manifest chunks file.manifest --sort size

Export manifest data

manifest export file.manifest --format json --output manifest.json
manifest export file.manifest --format csv --output files.csv

Library Usage

Parsing a manifest

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))

Inspecting metadata

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

Iterating files

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))

Inspecting chunks

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)

Inspecting chunk parts (file → chunk mapping)

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)
}

Reading custom fields

for key, value := range m.CustomFields.Fields {
    fmt.Printf("%s = %s\n", key, value)
}

Downloading and decompressing a single chunk

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))

Manual file assembly from chunks

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()
}

Downloading files (high-level)

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)

Delta patching

// 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

Verifying an installation

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)
    }
}

CDN Base URLs

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.

License

MIT

About

Epic Games Manifest Parser

Resources

Stars

11 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages