Documentation ¶
Overview ¶
Package w3g implements a decoder and encoder for w3g files.
Each record type is mapped to a struct type that implements the Record interface. To open a file, use w3g.Open().
Format:
size/type | Description -----------+----------------------------------------------------------- 28 chars | zero terminated string "Warcraft III recorded game\0x1A\0" 1 dword | fileoffset of first compressed data block (header size) | 0x40 for WarCraft III with patch <= v1.06 | 0x44 for WarCraft III patch >= 1.07 and TFT replays 1 dword | overall size of compressed file 1 dword | replay header version: | 0x00 for WarCraft III with patch <= 1.06 | 0x01 for WarCraft III patch >= 1.07 and TFT replays 1 dword | overall size of decompressed data (excluding header) 1 dword | number of compressed data blocks in file * replay header version 0x00: 1 word | unknown (always zero so far) 1 word | version number (corresponds to patch 1.xx) * replay header version 0x01: 1 dword | version identifier string reading: | 'WAR3' for WarCraft III Classic | 'W3XP' for WarCraft III Expansion Set 'The Frozen Throne' 1 dword | version number (corresponds to patch 1.xx so far) 1 word | build number 1 word | flags | 0x0000 for single player games | 0x8000 for multiplayer games (LAN or Battle.net) 1 dword | replay length in msec 1 dword | CRC32 checksum for the header | (the checksum is calculated for the complete header | including this field which is set to zero) For each data block: 1 word | size n of compressed data block (excluding header) 1 word | size of decompressed data block (currently 8k) 1 word | CRC checksum for the header 1 word | CRC checksum for the compressed block n bytes | compressed data (using zlib)
Example ¶
package main import ( "fmt" "github.com/nielsAD/gowarcraft3/file/w3g" ) func main() { replay, err := w3g.Open("./test_130.w3g") if err != nil { fmt.Println(err) return } fmt.Println(replay.HostPlayer.Name) }
Output: niels
Index ¶
- Constants
- Variables
- func DecodeHeader(r io.Reader, f RecordFactory) (*Header, *Decompressor, int, error)
- func FindHeader(r Peeker) (int, error)
- func SerializeRecord(r Record, e Encoding) ([]byte, error)
- func WriteRecord(w io.Writer, r Record, e Encoding) (int, error)
- type BlockCompressor
- type CacheFactory
- type ChatMessage
- type Compressor
- type CountDownEnd
- type CountDownStart
- type Decompressor
- type Desync
- type Encoder
- type Encoding
- type EndTimer
- type FactoryFunc
- type GameInfo
- type GameStart
- type Header
- type MapFactory
- type Peeker
- type PlayerExtra
- type PlayerInfo
- func (rec *PlayerInfo) Deserialize(buf *protocol.Buffer, enc *Encoding) error
- func (rec *PlayerInfo) DeserializeContent(buf *protocol.Buffer, enc *Encoding) error
- func (rec *PlayerInfo) Serialize(buf *protocol.Buffer, enc *Encoding) error
- func (rec *PlayerInfo) SerializeContent(buf *protocol.Buffer, enc *Encoding)
- type PlayerLeft
- type Record
- type RecordDecoder
- type RecordEncoder
- type RecordFactory
- type Replay
- type SlotInfo
- type TimeSlot
- type TimeSlotAck
Examples ¶
Constants ¶
const ( RidGameInfo = 0x10 RidPlayerInfo = 0x16 // w3gs.PlayerInfo RidPlayerLeft = 0x17 // w3gs.Leave RidSlotInfo = 0x19 // w3gs.SlotInfo RidCountDownStart = 0x1A // w3gs.CountDownStart RidCountDownEnd = 0x1B // w3gs.CountDownEnd RidGameStart = 0x1C RidTimeSlot2 = 0x1E // w3gs.TimeSlot2 (fragment) RidTimeSlot = 0x1F // w3gs.TimeSlot2 RidChatMessage = 0x20 // w3gs.MessageRelay RidTimeSlotAck = 0x22 // w3gs.TimeSlotAck RidDesync = 0x23 RidEndTimer = 0x2F RidPlayerExtra = 0x39 )
Record type identifiers
Variables ¶
var ( ErrBadFormat = errors.New("w3g: Invalid file format") ErrInvalidChecksum = errors.New("w3g: Checksum invalid") ErrUnexpectedConst = errors.New("w3g: Unexpected constant value") ErrUnknownRecord = errors.New("w3g: Unknown record ID") )
Errors
var DefaultFactory = MapFactory{ RidGameInfo: func(_ *Encoding) Record { return &GameInfo{} }, RidPlayerInfo: func(_ *Encoding) Record { return &PlayerInfo{} }, RidPlayerLeft: func(_ *Encoding) Record { return &PlayerLeft{} }, RidSlotInfo: func(_ *Encoding) Record { return &SlotInfo{} }, RidCountDownStart: func(_ *Encoding) Record { return &CountDownStart{} }, RidCountDownEnd: func(_ *Encoding) Record { return &CountDownEnd{} }, RidGameStart: func(_ *Encoding) Record { return &GameStart{} }, RidTimeSlot2: func(_ *Encoding) Record { return &TimeSlot{} }, RidTimeSlot: func(_ *Encoding) Record { return &TimeSlot{} }, RidChatMessage: func(e *Encoding) Record { if e.GameVersion == 0 || e.GameVersion > 2 { return &ChatMessage{} } return &TimeSlotAck{} }, RidTimeSlotAck: func(_ *Encoding) Record { return &TimeSlotAck{} }, RidDesync: func(_ *Encoding) Record { return &Desync{} }, RidEndTimer: func(_ *Encoding) Record { return &EndTimer{} }, RidPlayerExtra: func(_ *Encoding) Record { return &PlayerExtra{} }, }
DefaultFactory maps record ID to matching type
var Signature = "Warcraft III recorded game\x1A"
Signature constant for w3g files
Functions ¶
func DecodeHeader ¶
func DecodeHeader(r io.Reader, f RecordFactory) (*Header, *Decompressor, int, error)
DecodeHeader a w3g file, returns header and a Decompressor to read compressed records
func SerializeRecord ¶
SerializeRecord serializes r and returns its byte representation.
Types ¶
type BlockCompressor ¶
type BlockCompressor struct { Encoding SizeWritten uint32 // Compressed size written in total SizeTotal uint32 // Decompressed size written in total NumBlocks uint32 // Blocks written in total // contains filtered or unexported fields }
BlockCompressor is an io.Writer that compresses data blocks
func NewBlockCompressor ¶
func NewBlockCompressor(w io.Writer, e Encoding) *BlockCompressor
NewBlockCompressor for compressed w3g data
type CacheFactory ¶
type CacheFactory struct {
// contains filtered or unexported fields
}
CacheFactory implements a RecordFactory that will only create a type once
type ChatMessage ¶
ChatMessage record [0x20]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 byte | PlayerID (message sender) 1 word | n = number of bytes that follow 1 byte | flags | 0x10 for delayed startup screen messages | 0x20 for normal messages 1 dword | chat mode (not present if flag = 0x10): | 0x00 for messages to all players | 0x01 for messages to allies | 0x02 for messages to observers or referees | 0x03+N for messages to specific player N (with N = slotnumber) n bytes | zero terminated string containing the text message
func (*ChatMessage) Deserialize ¶
func (rec *ChatMessage) Deserialize(buf *protocol.Buffer, enc *Encoding) error
Deserialize decodes the binary data generated by Serialize.
type Compressor ¶
type Compressor struct { RecordEncoder *BlockCompressor *bufio.Writer }
Compressor is an io.Writer that compresses buffered data blocks
func NewCompressor ¶
func NewCompressor(w io.Writer, e Encoding) *Compressor
NewCompressor for compressed w3g with default buffer size
func NewCompressorSize ¶
func NewCompressorSize(w io.Writer, e Encoding, size int) *Compressor
NewCompressorSize for compressed w3g with specified buffer size
func (*Compressor) Close ¶
func (d *Compressor) Close() error
Close adds padding to fill last block and flushes any buffered data
func (*Compressor) Write ¶
func (d *Compressor) Write(p []byte) (int, error)
Write implements the io.Writer interface.
func (*Compressor) WriteRecord ¶
func (d *Compressor) WriteRecord(r Record) (int, error)
WriteRecord serializes r and writes it to d
func (*Compressor) WriteRecords ¶
func (d *Compressor) WriteRecords(r ...Record) (int, error)
WriteRecords serializes r and writes to d
type CountDownEnd ¶
type CountDownEnd struct {
GameStart
}
CountDownEnd record [0x1B]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | unknown (always 0x01 so far)
type CountDownStart ¶
type CountDownStart struct {
GameStart
}
CountDownStart record [0x1A]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | unknown (always 0x01 so far)
type Decompressor ¶
type Decompressor struct { RecordDecoder SizeRead uint32 // Compressed size read in total SizeTotal uint32 // Decompressed size left to read in total SizeBlock uint32 // Decompressed size left to read current block NumBlocks uint32 // Blocks left to read // contains filtered or unexported fields }
Decompressor is an io.Reader that decompresses data blocks
func NewDecompressor ¶
func NewDecompressor(r io.Reader, e Encoding, f RecordFactory, numBlocks uint32, sizeTotal uint32) *Decompressor
NewDecompressor for compressed w3g data
type Desync ¶
Desync record [0x23]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | unknown 1 byte | unknown (always 4?) 1 dword | unknown (random?) 1 byte | unknown (always 0?)
func (*Desync) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type Encoder ¶
type Encoder struct { Header *Compressor // contains filtered or unexported fields }
Encoder compresses records and updates header on close
func NewEncoder ¶
NewEncoder for replay file
type EndTimer ¶
EndTimer record [0x2F]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | mode: | 0x00 countdown is running | 0x01 countdown is over (end is forced *now*) 1 dword | countdown time in sec
func (*EndTimer) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type GameInfo ¶
type GameInfo struct { HostPlayer PlayerInfo GameName string GameSettings w3gs.GameSettings GameFlags w3gs.GameFlags NumSlots uint32 LanguageID uint32 }
GameInfo record [0x10]
Format:
Size | Name -----------+-------------------------- 4 byte | Number of host records variable | PlayerInfo for host variable | GameName (null terminated string) 1 byte | Nullbyte variable | Encoded String (null terminated) | - GameSettings | - Map&CreatorName 4 byte | PlayerCount 4 byte | GameType 4 byte | LanguageID
func (*GameInfo) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type GameStart ¶
type GameStart struct{}
GameStart record [0x1C]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | unknown (always 0x01 so far)
func (*GameStart) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type Header ¶
type Header struct { GameVersion w3gs.GameVersion BuildNumber uint16 DurationMS uint32 SinglePlayer bool }
Header for a Warcraft III recorded game file
type MapFactory ¶
type MapFactory map[uint8]FactoryFunc
MapFactory implements RecordFactory using a map
type Peeker ¶
Peeker is the interface that wraps basic Peek and Discard methods.
There is no way to determine record size without reading the full record, so it's impossible to know how many bytes should be read from an io.Reader. Instead, we peek a certain amount of bytes, deserialize the record, and then discard the actual amount of bytes read.
This interface is implemented by bufio.Reader, for example.
type PlayerExtra ¶ added in v1.6.0
type PlayerExtra struct {
w3gs.PlayerExtra
}
PlayerExtra record [0x39]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 byte | sub type (0x03) | 0x03 battle.net profile data | 0x04 in-game skins 1 dword | number of bytes following n bytes | protobuf encoded struct For each battle.net profile (sub type 0x03, encoded with protobuf): 1 byte | player ID string | battletag string | clan string | portrait 1 byte | team string | unknown For each player (sub type 0x04, encoded with protobuf): 1 byte | player ID For each in-game skin: qword | unit ID qword | skin ID string | skin collection
func (*PlayerExtra) Deserialize ¶ added in v1.6.0
func (rec *PlayerExtra) Deserialize(buf *protocol.Buffer, enc *Encoding) error
Deserialize decodes the binary data generated by Serialize.
type PlayerInfo ¶
PlayerInfo record [0x16]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 byte | PlayerID n bytes | PlayerName (null terminated string) 1 byte | size of additional data: | 0x01 = custom | 0x08 = ladder * If custom (0x01): 1 byte | null byte (1 byte) * If ladder (0x08): 4 bytes | runtime of player's Warcraft.exe in milliseconds 4 bytes | player race flags: | 0x01=human | 0x02=orc | 0x04=nightelf | 0x08=undead | (0x10=daemon) | 0x20=random | 0x40=race selectable/fixed
func (*PlayerInfo) Deserialize ¶
func (rec *PlayerInfo) Deserialize(buf *protocol.Buffer, enc *Encoding) error
Deserialize decodes the binary data generated by Serialize.
func (*PlayerInfo) DeserializeContent ¶
func (rec *PlayerInfo) DeserializeContent(buf *protocol.Buffer, enc *Encoding) error
DeserializeContent decodes the binary data generated by SerializeContent.
func (*PlayerInfo) Serialize ¶
func (rec *PlayerInfo) Serialize(buf *protocol.Buffer, enc *Encoding) error
Serialize encodes the struct into its binary form.
func (*PlayerInfo) SerializeContent ¶
func (rec *PlayerInfo) SerializeContent(buf *protocol.Buffer, enc *Encoding)
SerializeContent encodes the struct into its binary form without record ID.
type PlayerLeft ¶
type PlayerLeft struct { Local bool PlayerID uint8 Reason w3gs.LeaveReason Counter uint32 }
PlayerLeft record [0x17]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 dword | reason | 0x01 - connection closed by remote game | 0x0C - connection closed by local game | 0x0E - unknown (rare) (almost like 0x01) 1 byte | PlayerID 1 dword | result - see table below 1 dword | unknown (number of replays saved this warcraft session?)
func (*PlayerLeft) Deserialize ¶
func (rec *PlayerLeft) Deserialize(buf *protocol.Buffer, enc *Encoding) error
Deserialize decodes the binary data generated by Serialize.
type Record ¶
type Record interface { Serialize(buf *protocol.Buffer, enc *Encoding) error Deserialize(buf *protocol.Buffer, enc *Encoding) error }
Record interface.
func DeserializeRecord ¶
DeserializeRecord reads exactly one record from b and returns it in the proper (deserialized) record type.
type RecordDecoder ¶
type RecordDecoder struct { Encoding RecordFactory // contains filtered or unexported fields }
RecordDecoder keeps amortized allocs at 0 for repeated Record.Deserialize calls.
func NewRecordDecoder ¶
func NewRecordDecoder(e Encoding, f RecordFactory) *RecordDecoder
NewRecordDecoder initialization
func (*RecordDecoder) Deserialize ¶
func (dec *RecordDecoder) Deserialize(b []byte) (Record, int, error)
Deserialize reads exactly one record from b and returns it in the proper (deserialized) record type.
type RecordEncoder ¶
type RecordEncoder struct { Encoding // contains filtered or unexported fields }
RecordEncoder keeps amortized allocs at 0 for repeated Record.Serialize calls Byte slices are valid until the next Serialize() call
func NewRecordEncoder ¶
func NewRecordEncoder(e Encoding) *RecordEncoder
NewRecordEncoder initialization
type RecordFactory ¶
RecordFactory returns a struct of the appropiate type for a record ID
func NewFactoryCache ¶
func NewFactoryCache(factory RecordFactory) RecordFactory
NewFactoryCache initializes CacheFactory
type Replay ¶
type Replay struct { Header GameInfo SlotInfo PlayerInfo []*PlayerInfo PlayerExtra []*PlayerExtra Records []Record }
Replay information for Warcraft III recorded game
type SlotInfo ¶
SlotInfo record [0x19]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 word | number of data bytes following 1 byte | nr of SlotRecords following (== nr of slots on startscreen) n bytes | nr * SlotRecord 1 dword | RandomSeed 1 byte | SelectMode | 0x00 - team & race selectable (for standard custom games) | 0x01 - team not selectable | (map setting: fixed alliances in WorldEditor) | 0x03 - team & race not selectable | (map setting: fixed player properties in WorldEditor) | 0x04 - race fixed to random | (extended map options: random races selected) | 0xcc - Automated Match Making (ladder) 1 byte | StartSpotCount (nr. of start positions in map) For each slot: 1 byte | player id (0x00 for computer players) 1 byte | map download percent: 0x64 in custom, 0xff in ladder 1 byte | slotstatus: | 0x00 empty slot | 0x01 closed slot | 0x02 used slot 1 byte | computer player flag: | 0x00 for human player | 0x01 for computer player 1 byte | team number:0 - 11 | (team 12 == observer or referee) 1 byte | color (0-11): | value+1 matches player colors in world editor: | (red, blue, cyan, purple, yellow, orange, green, | pink, gray, light blue, dark green, brown) | color 12 == observer or referee 1 byte | player race flags (as selected on map screen): | 0x01=human | 0x02=orc | 0x04=nightelf | 0x08=undead | 0x20=random | 0x40=race selectable/fixed 1 byte | computer AI strength: (only present in v1.03 or higher) | 0x00 for easy | 0x01 for normal | 0x02 for insane | for non-AI players this seems to be always 0x01 1 byte | player handicap in percent (as displayed on startscreen) | valid values: 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x64 | (field only present in v1.07 or higher)
func (*SlotInfo) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type TimeSlot ¶
TimeSlot record [0x1E] / [0x1F]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 word | n = number of bytes that follow 1 word | time increment (milliseconds) | about 250 ms in battle.net | about 100 ms in LAN and single player n-2 byte | CommandData block(s) (not present if n=2)
func (*TimeSlot) Deserialize ¶
Deserialize decodes the binary data generated by Serialize.
type TimeSlotAck ¶
type TimeSlotAck struct {
Checksum []byte
}
TimeSlotAck record [0x22]
Format:
size/type | Description -----------+----------------------------------------------------------- 1 byte | number of bytes following (always 0x04 so far) 1 dword | checksum
func (*TimeSlotAck) Deserialize ¶
func (rec *TimeSlotAck) Deserialize(buf *protocol.Buffer, enc *Encoding) error
Deserialize decodes the binary data generated by Serialize.