Skip to content
Weiyi Wang edited this page Feb 17, 2022 · 2 revisions

RSZ (RE Serialization?) is a common blob seen in various file formats (.user, .rcol, .scn, etc.). It is to store structured objects with any types (think JSON, but in binary and more cursed). The code parsing this blob is in https://github.com/wwylele/mhrice/tree/main/src/rsz . However, due to the nature of arbitrary data type, the existing code does not parse all possible types. You can extend the code to handle the type you are interested in.

How do I add code for parsing new types?

  1. get your TDB dump ready
  2. open the RSZ blob in hex editor.
  3. somewhere after the magic "RSZ", there should be a blob of data that looks like hash/gibberish, in groups of 8 bytes each. Record the first 4 bytes of each 8 bytes as little-endian (meaning you need to swap bytes) unsigned 32-bit integer in hex.
  4. Search the TDB dump for these integers. You should be able to find their corresponding types.
  5. make a new .rs file in the rsz folder. The file name should be related to the data type you are looking at. Register this file as a module by adding mod file_name; and pub use file_name::*; in rsz/mod.rs (replace file_name with the actual file name). In the new .rs file, add the following common header (you can later remove those you don't use)
    use super::*;
    use crate::rsz_bitflags;
    use crate::rsz_enum;
    use crate::rsz_struct;
    use bitflags::*;
    use serde::*;
    
  6. Translates types from TDB to rust code. For example:
    rsz_struct! { // wrap everything in this macro
        // define the type like a normal rust struct
        // but with an important attribute "rsz". The name in rsz(...)
        // must match exactly the full name you found in TDB
        #[rsz("snow.enemy.EnemyConditionDamageData.StockData")]
        #[derive(Debug, Serialize)] // optional, but you need these if you want to get json
        pub struct StockData { // struct name can be anything, but preferably match the name in TDB
            // now for every non-static field you see in TDB, add a field here.
            // also add a field for the base class if it has one.
            // ... but there maybe exceptions. This is where you need to use your reverse engineering brain
            // the field name can be anything, but preferably match the name in TDB
            // the type must be translated in the following rules
            //  - primitive types (integer, bool, float, String...) are translated to rust primitive types
            //  - list-like types (array, IList, ...) are translated to `Vec<_>`
            //  - enum types are translated to rust's fieldless enum. The enum type itself must be wrapped in rsz_enum! macro
            //  - for user-defined C# value types, either expand all children fields, or define the struct using
            //    rsz_struct! {#[rsz()] ...} (empty rsz() string).
            //  - for user-defined C# reference types, the corresponding struct type should be either already defined in other files,
            //    or you will define them in this file. Simply use that type.
            pub default_limit: f32,
            pub add_limit: f32,
            pub max_limit: f32,
            pub sub_value: f32,
            pub sub_interval: f32,
        }
    }
    
  7. near the bottom of rsz/mod.rs, register the types you just defined by r!(StockData, OtherKindsOfData,...);
  8. If everything goes fine, the Rsz parser should be able to handle the new type. If the parent file is a .user file, you can read the file using mhrice read-user -u TheFile.user. If the parent file is in other format, please refer to the corresponding file format wiki.
Clone this wiki locally