Class: Factorix::SerDes::Deserializer

Inherits:
Object
  • Object
show all
Defined in:
lib/factorix/ser_des/deserializer.rb

Overview

Deserialize data from binary format

This class provides methods to deserialize various data types from Factorio’s binary file format, following the specifications documented in the Factorio wiki.

Instance Method Summary collapse

Constructor Details

#initialize(stream, logger: nil) ⇒ Deserializer

Create a new Deserializer instance

Parameters:

  • stream (IO)

    An IO-like object that responds to #read

  • logger (Dry::Logger::Dispatcher) (defaults to: nil)

    optional logger

Raises:

  • (ArgumentError)

    If the stream doesn’t respond to #read



20
21
22
23
24
25
26
# File 'lib/factorix/ser_des/deserializer.rb', line 20

def initialize(stream, logger: nil)
  super(logger:)
  raise ArgumentError, "can't read from the given argument" unless stream.respond_to?(:read)

  @stream = stream
  logger.debug "Initializing Deserializer"
end

Instance Method Details

#eof?Boolean

Check if the stream is at EOF

Returns:

  • (Boolean)

    True if at end of file, false otherwise



195
# File 'lib/factorix/ser_des/deserializer.rb', line 195

def eof? = @stream.eof?

#read_boolBoolean

Read a boolean value

Returns:

  • (Boolean)

    Boolean value



90
# File 'lib/factorix/ser_des/deserializer.rb', line 90

def read_bool = read_u8 != 0

#read_bytes(length) ⇒ String

Read raw bytes from the stream

Parameters:

  • length (Integer)

    Number of bytes to read

Returns:

  • (String)

    Binary data read

Raises:



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/factorix/ser_des/deserializer.rb', line 34

def read_bytes(length)
  raise InvalidLengthError, "nil length" if length.nil?
  raise InvalidLengthError, "negative length #{length}" if length.negative?
  return +"" if length.zero?

  bytes = @stream.read(length)
  if bytes.nil? || bytes.size < length
    logger.debug("Unexpected EOF", requested_bytes: length)
    raise EOFError
  end

  bytes
end

#read_dictionaryHash Also known as: read_list

Read a dictionary

Returns:

  • (Hash)

    Dictionary of key-value pairs

See Also:



136
137
138
139
140
141
142
143
# File 'lib/factorix/ser_des/deserializer.rb', line 136

def read_dictionary
  length = read_u32
  logger.debug("Reading dictionary", length:)
  length.times.each_with_object({}) do |_i, dict|
    key = read_str_property
    dict[key] = read_property_tree
  end
end

#read_doubleFloat

Read a double-precision floating point number

Returns:

  • (Float)

    Double-precision floating point number

See Also:



110
# File 'lib/factorix/ser_des/deserializer.rb', line 110

def read_double = read_bytes(8).unpack1("d")

#read_game_versionGameVersion

Read a GameVersion object

Returns:



115
# File 'lib/factorix/ser_des/deserializer.rb', line 115

def read_game_version = GameVersion.from_numbers(read_u16, read_u16, read_u16, read_u16)

#read_longInteger

Read a signed long integer (8 bytes)

Returns:

  • (Integer)

    Signed long integer



125
# File 'lib/factorix/ser_des/deserializer.rb', line 125

def read_long = read_bytes(8).unpack1("q<")

#read_mod_versionMODVersion

Read a MODVersion object

Returns:



120
# File 'lib/factorix/ser_des/deserializer.rb', line 120

def read_mod_version = MODVersion.from_numbers(read_optim_u16, read_optim_u16, read_optim_u16)

#read_optim_u16Integer

Read a space-optimized 16-bit unsigned integer

Returns:

  • (Integer)

    16-bit unsigned integer

See Also:



67
68
69
70
# File 'lib/factorix/ser_des/deserializer.rb', line 67

def read_optim_u16
  byte = read_u8
  byte == 0xFF ? read_u16 : byte
end

#read_optim_u32Integer

Read a space-optimized 32-bit unsigned integer

Returns:

  • (Integer)

    32-bit unsigned integer

See Also:



76
77
78
79
# File 'lib/factorix/ser_des/deserializer.rb', line 76

def read_optim_u32
  byte = read_u8
  byte == 0xFF ? read_u32 : byte
end

#read_property_treeObject

Read a property tree

Returns:

  • (Object)

    Object read

Raises:



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/factorix/ser_des/deserializer.rb', line 155

def read_property_tree
  type = read_u8
  _any_type_flag = read_bool
  logger.debug("Reading property tree", type:)

  case type
  when 0
    # Handle type 0 - None (null value)
    #
    # @see https://wiki.factorio.com/Property_tree
    nil
  when 1
    read_bool
  when 2
    read_double
  when 3
    read_str_property
  when 4
    read_list
  when 5
    read_dictionary
  when 6
    # Handle type 6 - Signed integer
    #
    # @see https://wiki.factorio.com/Property_tree
    SignedInteger.new(read_long)
  when 7
    # Handle type 7 - Unsigned integer
    #
    # @see https://wiki.factorio.com/Property_tree
    UnsignedInteger.new(read_unsigned_long)
  else
    logger.debug("Unknown property type", type:)
    raise UnknownPropertyType, "Unknown property type: #{type}"
  end
end

#read_strString

Read a string

Returns:

  • (String)

    String read



95
96
97
98
# File 'lib/factorix/ser_des/deserializer.rb', line 95

def read_str
  length = read_optim_u32
  read_bytes(length).force_encoding(Encoding::UTF_8)
end

#read_str_propertyString

Read a string property

Returns:

  • (String)

    String property

See Also:



104
# File 'lib/factorix/ser_des/deserializer.rb', line 104

def read_str_property = read_bool ? "" : read_str

#read_u16Integer

Read an unsigned 16-bit integer

Returns:

  • (Integer)

    16-bit unsigned integer



56
# File 'lib/factorix/ser_des/deserializer.rb', line 56

def read_u16 = read_bytes(2).unpack1("v")

#read_u16_tuple(length) ⇒ Array<Integer>

Read a tuple of 16-bit unsigned integers

Parameters:

  • length (Integer)

    Number of integers to read

Returns:

  • (Array<Integer>)

    Array of 16-bit unsigned integers



85
# File 'lib/factorix/ser_des/deserializer.rb', line 85

def read_u16_tuple(length) = Array.new(length) { read_u16 }

#read_u32Integer

Read an unsigned 32-bit integer

Returns:

  • (Integer)

    32-bit unsigned integer



61
# File 'lib/factorix/ser_des/deserializer.rb', line 61

def read_u32 = read_bytes(4).unpack1("V")

#read_u8Integer

Read an unsigned 8-bit integer

Returns:

  • (Integer)

    8-bit unsigned integer



51
# File 'lib/factorix/ser_des/deserializer.rb', line 51

def read_u8 = read_bytes(1).unpack1("C")

#read_unsigned_longInteger

Read an unsigned long integer (8 bytes)

Returns:

  • (Integer)

    Unsigned long integer



130
# File 'lib/factorix/ser_des/deserializer.rb', line 130

def read_unsigned_long = read_bytes(8).unpack1("Q<")