Ruby Marshal serializer and deserializer for Godot written in GDScript.
The module implements a serializer and a deserializer for the Marshal format based on Ruby language.
It was created mainly as a bridge between Godot and the .rxdata files from RPG Maker.
Show/hide
| Data type | Supported | GDScript type/value |
|---|---|---|
NIL |
✅ | null |
TRUE, FALSE |
✅ | bool |
FIXNUM |
✅ | int |
EXTENDED |
❌ | |
UCLASS |
❌ | |
OBJECT |
✅ | Resource |
DATA |
✅ | Resource |
USERDEF |
✅ | Resource |
USRMARSHAL |
✅ | Resource |
FLOAT |
✅ | float |
BIGNUM |
✅ | int |
STRING |
✅ | String |
REGEXP |
❌ | |
ARRAY |
✅ | Array |
HASH |
✅ | Dictionary |
HASH_DEF |
✅ | Dictionary |
STRUCT |
✅ | Resource |
MODULE_OLD |
✅ | GDScript |
CLASS |
✅ | GDScript |
MODULE |
✅ | GDScript |
SYMBOL |
✅ | String |
SYMLINK |
✅ | String |
IVAR |
✅ | Resource |
LINK |
✅ | Variant |
If an error occurs a default behaviour will take place:
- if an object can not be serialized: the dump method will return [04, 08, 48] as PackedByteArray (same as serializing null);
- if an unknown data type is parsed: the parse method will return null or, if the error is unknown, it will cause a crash.
Integers in GDScript are represented as signed 64 bits, so make sure to not parse numbers beyond this cap or the number will be truncated.
Objects should have a constructor that can be called without any argument.
The following table contains the required methods for each data type:
| Data type | Load method | Argument type | Dump method | Return type | Informations |
|---|---|---|---|---|---|
STRUCT |
marshal_load | Dictionary[String, Variant] |
marshal_dump | Dictionary[String, Variant] |
The dictionary contains attributes name-value pairs. |
OBJECT |
marshal_load | Dictionary[String, Variant] |
marshal_dump | Dictionary[String, Variant] |
The dictionary contains attributes name-value pairs. |
USRMARSHAL |
marshal_load | Variant |
marshal_dump | Variant |
Data should be a compatible data type. |
USRDEF |
(static) marshal_unpack | PackedByteArray |
marshal_pack | PackedByteArray |
Data should follow the directives for Ruby pack. |
The serializer behaviour depends on the ObjectType flag.
The available ObjectType(s) are:
- Object
- UserObject
- Struct
Note that USRDEF doesn't require a specific ObjectType, instead it is implicitly flagged as it if the class implements marshal_unpack and marshal_pack.
Classes, modules and old modules are all parsed as GDScript classes since Godot doesn't have modules.
GDScript classes can be serialized as classes or modules, depending on the ClassType flag.
The available ClassType(s) are:
- Class
- Module
Ruby symbols are parsed as String(s) starting with ":" (double dot).
Vice versa String(s) starting with ":" are dumped as symbols.
var symbol: PackedByteArray = ... # :my_sym in Ruby
RubyMarshal.parse(symbol) # -> ":my_sym"
RubyMarshal.dump(":my_sym") # -> :my_symSince the Data type is deprecated in Ruby, the module can deserialize DATA type only for compatibility purpose but it can not serialize an object to a DATA type. Use RubyMarshal.ObjectType.Struct instead.
The Variant type in GDScript doesn't have a method to get its unique id (unlike Object with get_instance_id), so the ids are generated through hash.
This means that different Variant instances are treated as the same instance (thus have the same reference).
This affects the following types: float, String, Array and Dictionary.
For example:
var _hello: String = "hello"
var data: Array[PackedByteArray] = [
RubyMarshal.dump([_hello, _hello]), # reference, reference
RubyMarshal.dump(["hello", "hello"]), # object, object
RubyMarshal.dump([_hello, "hello"]), # reference, object
]
assert(data[0] == data[1] and data[1] == data[2]) # trueThis will cause the serializer to produce a different (but still compatible) byte string from Ruby.
The Ruby interpreter is required for the tests.
Run the scene res://tests.tscn (F5 by default). The output will be printed in the output panel.
godot --debug --headless tests/tests.tscnSome examples can be found inside the folder classes/.
RubyMarshal.register_class(MyClass, "ClassName", RubyMarshal.ClassType.Class, RubyMarshal.ObjectType.Object)var data: PackedByteArray = FileAccess.get_file_as_bytes("res://rxdata/Scripts.rxdata")
var scripts: Array = RubyMarshal.parse(data)var data: PackedByteArray = RubyMarshal.dump([MyClass.new(), "ciao", "mondo"])The module comes with a Zlib helper that can be used for inflate/deflate dynamic size data.
var data: PackedByteArray = Zlib.deflate("string to deflate")
assert("string to deflate" == Zlib.inflate(data))This module is not meant to be used as a substitute for any other data format in Godot.
Do NOT use this to manage and store important data.