Class: Factorix::SerDes::Serializer

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

Overview

Serialize data to binary format

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

Instance Method Summary collapse

Constructor Details

#initialize(stream, logger: nil) ⇒ Serializer

Create a new Serializer instance

Parameters:

  • stream (IO)

    An IO-like object that responds to #write

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

    optional logger

Raises:

  • (ArgumentError)

    If the stream doesn’t respond to #write



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

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

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

Instance Method Details

#write_bool(bool) ⇒ void

This method returns an undefined value.

Write a boolean value

Parameters:

  • bool (Boolean)

    Boolean value



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

def write_bool(bool) = write_u8(bool ? 0x01 : 0x00)

#write_bytes(data) ⇒ void

This method returns an undefined value.

Write raw bytes to the stream

Parameters:

  • data (String)

    Binary data to write

Raises:

  • (ArgumentError)

    If data is nil



33
34
35
36
37
38
# File 'lib/factorix/ser_des/serializer.rb', line 33

def write_bytes(data)
  raise ArgumentError if data.nil?
  return if data.empty?

  @stream.write(data)
end

#write_dictionary(dict) ⇒ void

This method returns an undefined value.

Write a dictionary

Parameters:

  • dict (Hash)

    Dictionary of key-value pairs

See Also:



156
157
158
159
160
161
162
163
# File 'lib/factorix/ser_des/serializer.rb', line 156

def write_dictionary(dict)
  logger.debug("Writing dictionary", size: dict.size)
  write_u32(dict.size)
  dict.each do |(key, value)|
    write_str_property(key)
    write_property_tree(value)
  end
end

#write_double(dbl) ⇒ void

This method returns an undefined value.

Write a double-precision floating point number

Parameters:

  • dbl (Float)

    Double-precision floating point number

See Also:



126
# File 'lib/factorix/ser_des/serializer.rb', line 126

def write_double(dbl) = write_bytes([dbl].pack("d"))

#write_game_version(game_version) ⇒ void

This method returns an undefined value.

Write a GameVersion object

Parameters:



132
# File 'lib/factorix/ser_des/serializer.rb', line 132

def write_game_version(game_version) = game_version.to_a.each {|u16| write_u16(u16) }

#write_list(list) ⇒ void

This method returns an undefined value.

Write a list

Parameters:

  • list (Array)

    List of objects

See Also:



145
146
147
148
149
# File 'lib/factorix/ser_des/serializer.rb', line 145

def write_list(list)
  logger.debug("Writing list", size: list.size)
  write_optim_u32(list.size)
  list.each {|e| write_property_tree(e) }
end

#write_long(long) ⇒ void

This method returns an undefined value.

Write a signed long integer (8 bytes)

Parameters:

  • long (Integer)

    Signed long integer



169
# File 'lib/factorix/ser_des/serializer.rb', line 169

def write_long(long) = write_bytes([long].pack("q<"))

#write_mod_version(mod_version) ⇒ void

This method returns an undefined value.

Write a MODVersion object

Parameters:



138
# File 'lib/factorix/ser_des/serializer.rb', line 138

def write_mod_version(mod_version) = mod_version.to_a.each {|u16| write_optim_u16(u16) }

#write_optim_u16(uint16) ⇒ void

This method returns an undefined value.

Write a space-optimized 16-bit unsigned integer

Parameters:

  • uint16 (Integer)

    16-bit unsigned integer

See Also:



63
64
65
66
67
68
69
70
# File 'lib/factorix/ser_des/serializer.rb', line 63

def write_optim_u16(uint16)
  if uint16 < 0xFF
    write_u8(uint16 & 0xFF)
  else
    write_u8(0xFF)
    write_u16(uint16)
  end
end

#write_optim_u32(uint32) ⇒ void

This method returns an undefined value.

Write a space-optimized 32-bit unsigned integer

Parameters:

  • uint32 (Integer)

    32-bit unsigned integer

See Also:



77
78
79
80
81
82
83
84
# File 'lib/factorix/ser_des/serializer.rb', line 77

def write_optim_u32(uint32)
  if uint32 < 0xFF
    write_u8(uint32 & 0xFF)
  else
    write_u8(0xFF)
    write_u32(uint32)
  end
end

#write_property_tree(obj) ⇒ void

This method returns an undefined value.

Write a property tree

Parameters:

  • obj (Object)

    Object to write

Raises:



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/factorix/ser_des/serializer.rb', line 182

def write_property_tree(obj)
  logger.debug("Writing property tree", type: obj.class.name)
  case obj
  when nil
    # Type 0 - None (null value)
    write_u8(0)
    write_bool(false)
  when true, false
    # Type 1 - Boolean
    write_u8(1)
    write_bool(false)
    write_bool(obj)
  when Float
    # Type 2 - Number (double)
    write_u8(2)
    write_bool(false)
    write_double(obj)
  when String
    # Type 3 - String
    write_u8(3)
    write_bool(false)
    write_str_property(obj)
  when Array
    # Type 4 - List
    write_u8(4)
    write_bool(false)
    write_list(obj)
  when Hash
    # Type 5 - Dictionary
    write_u8(5)
    write_bool(false)
    write_dictionary(obj)
  when SignedInteger
    # Type 6 - Signed integer
    write_u8(6)
    write_bool(false)
    write_long(obj.__getobj__)
  when UnsignedInteger
    # Type 7 - Unsigned integer
    write_u8(7)
    write_bool(false)
    write_unsigned_long(obj.__getobj__)
  else
    logger.debug("Unknown property type", type: obj.class.name)
    raise UnknownPropertyType, "Unknown property type: #{obj.class}"
  end
end

#write_str(str) ⇒ void

This method returns an undefined value.

Write a string

Parameters:

  • str (String)

    String to write (must be UTF-8 encoded)

Raises:

  • (ArgumentError)

    If the string is not UTF-8 encoded



97
98
99
100
101
102
103
104
105
# File 'lib/factorix/ser_des/serializer.rb', line 97

def write_str(str)
  if str.encoding != Encoding::UTF_8 && !(str.encoding == Encoding::ASCII_8BIT && str.force_encoding(Encoding::UTF_8).valid_encoding?)
    logger.debug("String encoding error", expected: "UTF-8", got: str.encoding.name)
    raise ArgumentError, "String must be UTF-8 encoded, got #{str.encoding}"
  end

  write_optim_u32(str.bytesize)
  write_bytes(str.b)
end

#write_str_property(str) ⇒ void

This method returns an undefined value.

Write a string property

Parameters:

  • str (String)

    String to write

See Also:



112
113
114
115
116
117
118
119
# File 'lib/factorix/ser_des/serializer.rb', line 112

def write_str_property(str)
  if str.empty?
    write_bool(true)
  else
    write_bool(false)
    write_str(str)
  end
end

#write_u16(uint16) ⇒ void

This method returns an undefined value.

Write an unsigned 16-bit integer

Parameters:

  • uint16 (Integer)

    16-bit unsigned integer



50
# File 'lib/factorix/ser_des/serializer.rb', line 50

def write_u16(uint16) = write_bytes([uint16].pack("v"))

#write_u32(uint32) ⇒ void

This method returns an undefined value.

Write an unsigned 32-bit integer

Parameters:

  • uint32 (Integer)

    32-bit unsigned integer



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

def write_u32(uint32) = write_bytes([uint32].pack("V"))

#write_u8(uint8) ⇒ void

This method returns an undefined value.

Write an unsigned 8-bit integer

Parameters:

  • uint8 (Integer)

    8-bit unsigned integer



44
# File 'lib/factorix/ser_des/serializer.rb', line 44

def write_u8(uint8) = write_bytes([uint8].pack("C"))

#write_unsigned_long(ulong) ⇒ void

This method returns an undefined value.

Write an unsigned long integer (8 bytes)

Parameters:

  • ulong (Integer)

    Unsigned long integer



175
# File 'lib/factorix/ser_des/serializer.rb', line 175

def write_unsigned_long(ulong) = write_bytes([ulong].pack("Q<"))