Skip to content

Commit

Permalink
Add Header.Version field
Browse files Browse the repository at this point in the history
  • Loading branch information
icza committed Sep 30, 2022
1 parent 1aff508 commit 8455dab
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 26 deletions.
4 changes: 1 addition & 3 deletions cmd/screp/screp.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/*
A simple CLI app to parse and display information about
a StarCraft: Brood War replay passed as a CLI argument.
*/
package main

Expand All @@ -26,7 +24,7 @@ import (

const (
appName = "screp"
appVersion = "v1.7.2"
appVersion = "v1.7.3"
appAuthor = "Andras Belicza"
appHome = "https://github.com/icza/screp"
)
Expand Down
8 changes: 8 additions & 0 deletions rep/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ type Header struct {
// Engine used to play the game and save the replay
Engine *repcore.Engine

// Version contains information about the replay version.
// Since version is not stored in replays, this only designates certain version ranges deducted from replay format.
// Current possible values are:
// - "-1.16": version is 1.16 or older
// - "1.18-1.20": version is 1.18..1.20
// - "1.21+": version is 1.21 or newer
Version string

// Frames is the number of frames. There are approximately ~23.81 frames in
// a second. (1 frame = 0.042 second to be exact).
Frames repcore.Frame
Expand Down
43 changes: 25 additions & 18 deletions repparser/repdecoder/repdecoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ var (

// Decoder wraps a Section method for decoding a section of a given size.
type Decoder interface {
// RepFormat returns the replay format
RepFormat() RepFormat

// NewSection must be called between sections.
// ErrNoMoreSections is returned if the replay has no more sections.
NewSection() error
Expand Down Expand Up @@ -63,7 +66,7 @@ func NewFromFile(name string) (d Decoder, err error) {
return nil, fmt.Errorf("not a file: %s", name)
}

var rf repFormat
var rf RepFormat
if stat.Size() >= 30 {
fileHeader := make([]byte, 30)
if _, err = io.ReadFull(f, fileHeader); err != nil {
Expand All @@ -81,23 +84,23 @@ func NewFromFile(name string) (d Decoder, err error) {
// New creates a new Decoder that reads and decompresses data from the
// given byte slice.
func New(repData []byte) Decoder {
rf := repFormatUnknown
rf := RepFormatUnknown
if len(repData) >= 30 {
rf = detectRepFormat(repData[:30])
}

return newDecoder(bytes.NewBuffer(repData), rf)
}

// repFormat identifies the replay format
type repFormat int
// RepFormat identifies the replay format
type RepFormat int

// Possible values of repFormat
const (
repFormatUnknown repFormat = iota // Unknown replay format
repFormatLegacy // Legacy replay format (pre 1.18)
repFormatModern // Modern replay format (1.18 - 1.20)
repFormatModern121 // Modern 1.21 replay format (starting from 1.21)
RepFormatUnknown RepFormat = iota // Unknown replay format
RepFormatLegacy // Legacy replay format (pre 1.18)
RepFormatModern // Modern replay format (1.18 - 1.20)
RepFormatModern121 // Modern 1.21 replay format (starting from 1.21)
)

// detectRepFormat detects the replay format based on the file header
Expand All @@ -107,15 +110,15 @@ const (
// data block of the Header section (which starts at offset 28).
// If the compressed data block starts with the magic of the valid zlib header,
// it is modern. If it is modern, the replay ID data decides which version.
func detectRepFormat(fileHeader []byte) repFormat {
func detectRepFormat(fileHeader []byte) RepFormat {
if len(fileHeader) < 30 {
return repFormatUnknown
return RepFormatUnknown
}

// legacy and pre 1.21 modern replays have replay ID data "reRS".
// Starting from 1.21, replay ID data is "seRS".
if fileHeader[12] == 's' {
return repFormatModern121
return RepFormatModern121
}

// It's pre 1.21, check if legacy:
Expand All @@ -127,16 +130,16 @@ func detectRepFormat(fileHeader []byte) repFormat {
// 0x9C level 6 (default compression?)
// 0xDA level 7..9
if fileHeader[28] != 0x78 {
return repFormatLegacy
return RepFormatLegacy
}

return repFormatModern
return RepFormatModern
}

// newDecoder creates a new Decoder that reads and decompresses data from the given Reader.
// The source is treated as a modern replay if modern is true, else as a
// legacy replay.
func newDecoder(r io.Reader, rf repFormat) Decoder {
func newDecoder(r io.Reader, rf RepFormat) Decoder {
dec := decoder{
r: r,
rf: rf,
Expand All @@ -145,7 +148,7 @@ func newDecoder(r io.Reader, rf repFormat) Decoder {
}

switch rf {
case repFormatModern, repFormatModern121:
case RepFormatModern, RepFormatModern121:
return &modernDecoder{
decoder: dec,
}
Expand All @@ -163,7 +166,7 @@ type decoder struct {
r io.Reader

// rf identifiers the rep format
rf repFormat
rf RepFormat

// sectionsCounter tells how many sections have been read
sectionsCounter int
Expand All @@ -175,6 +178,10 @@ type decoder struct {
buf []byte
}

func (d *decoder) RepFormat() RepFormat {
return d.rf
}

// readInt32 reads an int32 from the underlying Reader.
func (d *decoder) readInt32() (n int32, err error) {
if _, err = io.ReadFull(d.r, d.int32Buf); err != nil {
Expand All @@ -191,11 +198,11 @@ func (d *decoder) NewSection() (err error) {
d.sectionsCounter++

switch d.rf {
case repFormatLegacy:
case RepFormatLegacy:
if d.sectionsCounter == 5 {
return ErrNoMoreSections // Legacy replays only have 4 sections
}
case repFormatModern121:
case RepFormatModern121:
// There is a 4-byte encoded length between sections:
if d.sectionsCounter == 2 {
if _, err = d.readInt32(); err != nil {
Expand Down
18 changes: 13 additions & 5 deletions repparser/repparser.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/*
Package repparser implements StarCraft: Brood War replay parsing.
The package is safe for concurrent use.
Expand All @@ -24,7 +23,6 @@ https://github.com/icza/bwhf/blob/master/src/hu/belicza/andras/bwhf/model/Action
https://github.com/bwapi/bwapi/tree/master/bwapi/libReplayTool
jssuh replay parser:
https://github.com/neivv/jssuh
Expand All @@ -36,7 +34,6 @@ https://www.starcraftai.com/wiki/CHK_Format
http://www.staredit.net/wiki/index.php/Scenario.chk
http://blog.naver.com/PostView.nhn?blogId=wisdomswrap&logNo=60119755717&parentCategoryNo=&categoryNo=19&viewDate=&isShowPopularPosts=false&from=postView
*/
package repparser

Expand All @@ -61,7 +58,7 @@ import (

const (
// Version is a Semver2 compatible version of the parser.
Version = "v1.8.2"
Version = "v1.8.3"
)

var (
Expand Down Expand Up @@ -240,6 +237,17 @@ func parse(dec repdecoder.Decoder, cfg Config) (*rep.Replay, error) {
if err = s.ParseFunc(data, r, cfg); err != nil {
return nil, fmt.Errorf("ParseFunc() error (sectionID: %d): %v", s.ID, err)
}
if s == SectionHeader {
// Fill Version:
switch dec.RepFormat() {
case repdecoder.RepFormatModern121:
r.Header.Version = "1.21+"
case repdecoder.RepFormatLegacy:
r.Header.Version = "-1.16"
case repdecoder.RepFormatModern:
r.Header.Version = "1.18-1.20"
}
}
}
}

Expand All @@ -249,7 +257,7 @@ func parse(dec repdecoder.Decoder, cfg Config) (*rep.Replay, error) {
// repIDs is the possible valid content of the Replay ID section
var repIDs = [][]byte{
[]byte("seRS"), // Starting from 1.21
[]byte("reRS"), // Up until 1.20. Abbreviation for replay ReSource?
[]byte("reRS"), // Up until 1.20.
}

// parseReplayID processes the replay ID data.
Expand Down

0 comments on commit 8455dab

Please sign in to comment.