binary

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package binary provides low-level binary I/O utilities for reading Terraria file formats.

Overview

This package implements a binary reader with Terraria-specific data types including 7-bit encoded integers, .NET-style strings, and GUID handling. It is primarily used internally by the world and player packages.

Reader Usage

Create a reader from any io.Reader:

f, _ := os.Open("file.bin")
r := binary.NewReader(f)

version := r.ReadInt32()
name := r.ReadString()
flag := r.ReadBool()

if r.Err() != nil {
    log.Fatal(r.Err())
}

Seekable Reader

For files that require seeking (like world files), use NewSeekableReader:

f, _ := os.Open("world.wld")
r := binary.NewSeekableReader(f)

// Seek to a specific position
r.Seek(1000, io.SeekStart)

Supported Types

The reader supports these data types:

Error Handling

The reader uses sticky error handling - once an error occurs, all subsequent reads return zero values and the error persists. Check Reader.Err after a sequence of reads:

v1 := r.ReadInt32()
v2 := r.ReadInt32()
v3 := r.ReadString()

if err := r.Err(); err != nil {
    // Handle error from any of the reads
}

Position Tracking

Track the current read position with Reader.Offset:

before := r.Offset()
r.ReadBytes(100)
after := r.Offset()
fmt.Printf("Read %d bytes\n", after - before)

7-Bit Encoded Integers

Terraria (like .NET) uses 7-bit encoded integers for string lengths:

length := r.Read7BitEncodedInt() // Variable-length integer

This encoding uses the high bit as a continuation flag, allowing small numbers to use fewer bytes.

GUID/UUID Reading

.NET GUIDs have a specific byte order. Use Reader.ReadUUID:

uuid := r.ReadUUID() // Returns formatted UUID string

Package binary provides low-level binary I/O utilities for reading Terraria file formats. It wraps the standard library's encoding/binary package with Terraria-specific data types such as 7-bit encoded integers for string lengths, and provides convenient methods for reading common data structures.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader wraps an io.Reader and provides methods for reading Terraria binary data types. All multi-byte integers are read in little-endian format, which is the format used by Terraria's save files.

func NewReader

func NewReader(r io.Reader) *Reader

NewReader creates a new Reader that reads from r.

func (*Reader) Err

func (r *Reader) Err() error

Err returns any error that occurred during reading. Once an error occurs, all subsequent reads will return zero values.

func (*Reader) Offset

func (r *Reader) Offset() int64

Offset returns the current byte offset in the stream.

func (*Reader) Read7BitEncodedInt

func (r *Reader) Read7BitEncodedInt() int32

Read7BitEncodedInt reads a 7-bit encoded integer. This is the format used by .NET's BinaryReader.Read7BitEncodedInt(). Each byte contains 7 bits of the value, with the high bit indicating whether more bytes follow.

func (*Reader) ReadBits

func (r *Reader) ReadBits(n int) []bool

ReadBits reads n bits from the stream, reading whole bytes as needed. Returns a slice of booleans representing the bits.

func (*Reader) ReadBitsByte

func (r *Reader) ReadBitsByte() [8]bool

ReadBitsByte reads a single byte and returns it as an array of 8 booleans. Bit 0 (LSB) is at index 0, bit 7 (MSB) is at index 7.

func (*Reader) ReadBool

func (r *Reader) ReadBool() bool

ReadBool reads a single byte and returns true if non-zero.

func (*Reader) ReadByteValue added in v0.2.3

func (r *Reader) ReadByteValue() byte

ReadByteValue reads and returns a single byte.

This method intentionally does not implement io.ByteReader interface. The binary.Reader uses a sticky error pattern where errors are stored in r.err and checked via r.Err() after a sequence of reads. This pattern is consistent across all Reader methods (ReadInt32, ReadString, etc.).

Design Decision (Option 2: Rename): We renamed from ReadByte() to ReadByteValue() to avoid the go vet warning while maintaining API consistency. This preserves the sticky error pattern used throughout the package.

Benefits of Current Approach: - Consistent API: All methods follow the same error handling pattern - Simpler usage: Check errors once after multiple reads - Lower risk: No mixing of error handling patterns - Maintainable: Single, clear error handling strategy

Limitations of Current Approach: - Does not implement io.ByteReader interface - Cannot be used directly with standard library functions expecting io.ByteReader - Method name is less conventional (ReadByteValue vs ReadByte)

Future Consideration (Option 3: Implement io.ByteReader): If standard library compatibility becomes important, we could change the signature to ReadByte() (byte, error) to implement io.ByteReader. However, this would create API inconsistency:

Benefits of Option 3: - Implements standard io.ByteReader interface - Compatible with standard library functions - More idiomatic Go (errors returned directly)

Risks of Option 3:

  • API inconsistency: Only ReadByte would return errors directly
  • Breaks sticky error pattern for this one method
  • More complex: Mixing two error handling patterns
  • Higher risk: Easy to forget error checks, inconsistent with other methods
  • Internal methods (Read7BitEncodedInt, ReadBool, etc.) would need complex error handling to convert between patterns

Current Recommendation: Maintain Option 2 (rename) for consistency and simplicity. Consider Option 3 only if standard library io.ByteReader compatibility becomes a hard requirement.

func (*Reader) ReadBytes

func (r *Reader) ReadBytes(n int) []byte

ReadBytes reads and returns exactly n bytes.

func (*Reader) ReadFixedString

func (r *Reader) ReadFixedString(n int) string

ReadFixedString reads a fixed-length string of exactly n bytes.

func (*Reader) ReadFloat32

func (r *Reader) ReadFloat32() float32

ReadFloat32 reads a 32-bit floating point number in little-endian format.

func (*Reader) ReadFloat64

func (r *Reader) ReadFloat64() float64

ReadFloat64 reads a 64-bit floating point number in little-endian format.

func (*Reader) ReadInt16

func (r *Reader) ReadInt16() int16

ReadInt16 reads a signed 16-bit integer in little-endian format.

func (*Reader) ReadInt32

func (r *Reader) ReadInt32() int32

ReadInt32 reads a signed 32-bit integer in little-endian format.

func (*Reader) ReadInt64

func (r *Reader) ReadInt64() int64

ReadInt64 reads a signed 64-bit integer in little-endian format.

func (*Reader) ReadInt8

func (r *Reader) ReadInt8() int8

ReadInt8 reads a signed 8-bit integer.

func (*Reader) ReadString

func (r *Reader) ReadString() string

ReadString reads a length-prefixed string. The length is encoded as a 7-bit encoded integer, followed by the UTF-8 bytes.

func (*Reader) ReadUUID

func (r *Reader) ReadUUID() string

ReadUUID reads a 16-byte UUID and returns it as a formatted string.

func (*Reader) ReadUint16

func (r *Reader) ReadUint16() uint16

ReadUint16 reads an unsigned 16-bit integer in little-endian format.

func (*Reader) ReadUint32

func (r *Reader) ReadUint32() uint32

ReadUint32 reads an unsigned 32-bit integer in little-endian format.

func (*Reader) ReadUint64

func (r *Reader) ReadUint64() uint64

ReadUint64 reads an unsigned 64-bit integer in little-endian format.

func (*Reader) ReadUint8

func (r *Reader) ReadUint8() uint8

ReadUint8 reads an unsigned 8-bit integer.

func (*Reader) Skip

func (r *Reader) Skip(n int)

Skip advances the reader by n bytes without returning the data.

type SeekableReader

type SeekableReader struct {
	*Reader
	// contains filtered or unexported fields
}

SeekableReader is a Reader that can seek to specific positions.

func NewSeekableReader

func NewSeekableReader(rs io.ReadSeeker) *SeekableReader

NewSeekableReader creates a new SeekableReader from an io.ReadSeeker.

func (*SeekableReader) Seek

func (r *SeekableReader) Seek(offset int64, whence int) (int64, error)

Seek sets the offset for the next read, interpreted according to whence: io.SeekStart means relative to the start of the file, io.SeekCurrent means relative to the current offset, and io.SeekEnd means relative to the end.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL