func DecodeHeader(contents []byte) Struct {
	p := GetProtocol(MaxBaseBuild)
	if p == nil {
		panic("Default protocol is not available!")
	}
	contents = contents[4:]  
	d := newVersionedDec(contents, p.typeInfos)
	v, ok := d.instance(p.replayHeaderTypeid).(Struct)
	if !ok {
		return nil
	}
	return v
}
DecodeHeader decodes and returns the replay header.
Panics if decoding fails.
package s2prot
import (
	"bufio"
	"fmt"
	"log"
	"strconv"
	"strings"
	"sync"
	"github.com/icza/s2prot/build"
)
var (
	MinBaseBuild int
	MaxBaseBuild int
)
func init() {
	for k := range build.Builds {
		MinBaseBuild, MaxBaseBuild = k, k
		break
	}
	for k := range build.Builds {
		if MaxBaseBuild < k {
			MaxBaseBuild = k
		}
		if MinBaseBuild > k {
			MinBaseBuild = k
		}
	}
}
type EvtType struct {
	ID     int     
	Name   string  
	typeid int     
}
type Protocol struct {
	baseBuild int  
	typeInfos []typeInfo  
	hasTrackerEvents bool  
	gameEvtTypes         []EvtType  
	gameEventidTypeid    int        
	messageEvtTypes      []EvtType  
	messageEventidTypeid int        
	trackerEvtTypes      []EvtType  
	trackerEventidTypeid int        
	svaruint32Typeid int  
	replayUseridTypeid int  
	replayHeaderTypeid   int  
	gameDetailsTypeid    int  
	replayInitdataTypeid int  
}
var (
	protocols = make(map[int]*Protocol)
	protMux = &sync.Mutex{}
)
func GetProtocol(baseBuild int) *Protocol {
	protMux.Lock()
	defer protMux.Unlock()
	return getProtocol(baseBuild)
}
func getProtocol(baseBuild int) *Protocol {
	p, ok := protocols[baseBuild]
	if ok {
		return p
	}
	src, ok := build.Builds[baseBuild]
	if ok {
		p = parseProtocol(src, baseBuild)
		protocols[baseBuild] = p
		return p
	}
	origBaseBuild, ok := build.Duplicates[baseBuild]
	if ok {
		if op := getProtocol(origBaseBuild); op != nil {
			p = new(Protocol)
			*p = *op
			p.baseBuild = baseBuild
		}
	}
	protocols[baseBuild] = p
	return p
}
func parseProtocol(src string, baseBuild int) *Protocol {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("Failed to parse protocol source %d: %v\n", baseBuild, r)
		}
	}()
	p := Protocol{baseBuild: baseBuild, hasTrackerEvents: baseBuild >= 24944}
	scanner := bufio.NewScanner(strings.NewReader(src))
	var line string
	seek := func(prefix string) {
		for scanner.Scan() {
			line = scanner.Text()
			if strings.HasPrefix(line, prefix) {
				return
			}
		}
		panic(fmt.Sprintf(`Couldn't find "%s"`, prefix))
	}
	// Helper function to parse the last integer number from the current line with form: "some_name = int_value"
	parseInt := func() int {
		i := strings.LastIndex(line, "=")
		if i < 0 {
			panic("Can't find '=' in line")
		}
		n, err := strconv.Atoi(strings.TrimSpace(line[i+1:]))
		if err != nil {
			panic(err)
		}
		return n
	}
	// Helper function to parse an event types slice
	parseEvtTypes := func(stripPref, stripPost string) []EvtType {
		var err error
		em := make(map[int]EvtType) // First build it in a map
		maxEid := -1                // Max Event id
		for scanner.Scan() {
			line = scanner.Text()
			if line == "}" {
				break
			}
			e := EvtType{}
			i := strings.IndexByte(line, ':')
			e.ID, err = strconv.Atoi(strings.TrimSpace(line[:i]))
			if err != nil {
				panic(err)
			}
			line = line[i+1:]
			i = strings.IndexByte(line, '(') + 1
			j := strings.IndexByte(line, ',')
			e.typeid, err = strconv.Atoi(strings.TrimSpace(line[i:j]))
			if err != nil {
				panic(err)
			}
			i = strings.IndexByte(line, '\'') + 1
			line = line[i:]
			i = strings.IndexByte(line, '\'')
			e.Name = line[len(stripPref) : i-len(stripPost)]
			em[e.ID] = e
			if e.ID > maxEid {
				maxEid = e.ID
			}
		}
		// And now create a slice from the map:
		es := make([]EvtType, maxEid+1)
		for k, v := range em {
			es[k] = v
		}
		return es
	}
	_ = parseEvtTypes
	// Decode typeinfos
	seek("typeinfos")
	// Use a large local variable
	typeInfos := make([]typeInfo, 0, 256)
	for scanner.Scan() {
		line = scanner.Text()
		if line == "]" {
			break
		}
		typeInfos = append(typeInfos, parseTypeInfo(line))
	}
	// And now copy a trimmed version of this to Protocol (typeInfo is a relatively large struct):
	p.typeInfos = make([]typeInfo, len(typeInfos))
	copy(p.typeInfos, typeInfos)
	// Decode game event types
	seek("game_event_types")
	p.gameEvtTypes = parseEvtTypes("NNet.Game.S", "Event")
	seek("game_eventid_typeid")
	p.gameEventidTypeid = parseInt()
	// Decode message event types
	seek("message_event_types")
	p.messageEvtTypes = parseEvtTypes("NNet.Game.S", "Message")
	seek("message_eventid_typeid")
	p.messageEventidTypeid = parseInt()
	if p.hasTrackerEvents {
		// Decode track event types
		seek("tracker_event_types")
		p.trackerEvtTypes = parseEvtTypes("NNet.Replay.Tracker.S", "Event")
		seek("tracker_eventid_typeid")
		p.trackerEventidTypeid = parseInt()
	}
	seek("svaruint32_typeid")
	p.svaruint32Typeid = parseInt()
	// From basebuild 24764 user id is present, before that player id
	if baseBuild >= 24764 {
		seek("replay_userid_typeid")
	} else {
		seek("replay_playerid_typeid")
	}
	p.replayUseridTypeid = parseInt()
	seek("replay_header_typeid")
	p.replayHeaderTypeid = parseInt()
	seek("game_details_typeid")
	p.gameDetailsTypeid = parseInt()
	seek("replay_initdata_typeid")
	p.replayInitdataTypeid = parseInt()
	return &p
}