Text reader files

Text reader files have the file extension .zrd, which could stand for Zipper Reader. Until 2022, I only knew of binary reader files. However, there exist text reader files, for example DefaultCtlConfig.zrd.

Investigation (MW3)

Although it was assumed the reader files were Lisp-like from the binary reader files, the text reader files confirm this:

(
  ⇥ KEYS (
  ⇥   ⇥ (CMD_ALPHASTRIKE  ⇥ keya(0x9c)  ⇥ joybtn(0x6))
  ⇥   ⇥ (CMD_AMS_TOGGLE  ⇥ keya(0x1e))
...
  ⇥ )
  ⇥ AXES (
  ⇥   ⇥ (Throttle  ⇥ joystick(Z)  ⇥ slope(-0.500000)  ⇥ intercept(0.500000)  ⇥ deadzone(0.050000))
  ⇥   ⇥ (Twist  ⇥ joystick(Rz)  ⇥ slope(-1.000000)  ⇥ intercept(0.000000)  ⇥ deadzone(0.000000))
  ⇥   ⇥ (Pitch  ⇥ joystick(Y)  ⇥ slope(1.000000)  ⇥ intercept(0.000000)  ⇥ deadzone(0.000000))
  ⇥   ⇥ (LR  ⇥ joystick(X)  ⇥ slope(-1.000000)  ⇥ intercept(0.000000)  ⇥ deadzone(0.000000))
  ⇥ )
)

Note that the whitespace delimiter used is a tab (indicated as ⇥ above).

There are a lot of interesting quirks with this lisp dialect. First, the whitespace delimiters are definitely tab, carriage return (CR), and line feed (LF), i.e. CR+LF don't seem to have a syntactic value. This is not unusual, but it isn't clear if a space is a valid delimiter. This also ties into the fact that strings don't seem to be quoted.

From the binary reader files, we know there are only four data types:

  • Integers (i32)
  • Floating-point numbers (f32, "floats")
  • Strings
  • Lists

Interestingly, the text reader files hint that at least mentally, there were more. For example, it seems like strings are always upper-case, and lower-case strings are symbols. This also leads to a concept of a "function" data type in the text reader, for example joybtn(0x6). In other Lisps, this would've been written as (joybtn 0x6). Also, maps/dictionaries are simply lists with implicit key-value pairs.

We don't know how the text reader files are precisely lexed. If I had to guess from binary reader files, the example above would be expressed in pseudo-JSON as follows:

[
  "KEYS",
  [
    ["CMD_ALPHASTRIKE", "keya", [0x9c], "joybtn", [0x6]],
    ["CMD_AMS_TOGGLE", "keya", [0x1e]],
...
  ],
  "AXES",
  [
    ["Throttle", "joystick", ["Z"], "slope", [-0.5], "intercept", [0.5], "deadzone", [0.05]],
    ["Twist", "joystick", ["Rz"], "slope", [-1.0], "intercept", [0.0], "deadzone", [0.0]],
    ["Pitch", "joystick", ["Y"], "slope", [1.0], "intercept", [0.0], "deadzone", [0.0]],
    ["LR", "joystick", ["X"], "slope", [-1.0], "intercept", [0.0], "deadzone", [0.0]]
  ]
]

I believe the engine has an implicit schema, in that it tries to find string values by index, and then any information/arguments it needs are retrieved from index + 1.

There are still questions. For example, what happens if we mess with the order of "AXES"? Presumably when parsing, it looks at list index 0 to figure out what to put where in already existing data structures in the engine.

Control configuration

The MechWarrior 3 engine uses DirectInput for controls. This also matches the key codes (keya) in the DefaultCtlConfig.zrd, they are DirectInput key codes. Below is a converter:





(try pressing some keys in the textbox)