WDATA File

Rusty Hearts .wdata File Format

.wdata files are a binary container used by Rusty Hearts that bundle all of a map’s runtime data into a single package file that tell the engine what files to use (models, navmeshes, scripts), interactive volumes called EventBoxes (npc/enemies spawn points, triggers, portals, camera zones, etc.), animated background objects (AniBG), item containers (ItemBoxes), in-world gimmicks (traps, platforms), scripting triggers (events/conditions/actions), and cutscene data (Scenes and SceneResources). Together, these sections package all of a map’s resources into a single, versioned binary format that the engine can load and execute at runtime.

File Header and Versioning

Signature: Each .wdata file begins with a UTF-16 little-endian string signature. The signature is `"stairwaygames."

Note: Each string in the .wdata file is stored with a 2-byte length prefix. For example, "stairwaygames." would be stored as 0x0F 0x00 (15 in little-endian) followed by the 15 UTF-16 characters.

private string ReadWString(BinaryReader br)
    {
        ushort len = br.ReadUInt16();
        return len == 0 ? string.Empty
                        : Encoding.Unicode.GetString(br.ReadBytes(len * 2));
    }

Version Fields: Immediately after the signature, a Main Version number is stored. This main version dictates the presence of additional sub-version fields and format variations:

  • Main Version (int32): Primary version of the .wdata format. This field is always present and must be read first.

  • EventBox Version (int32): Present if Main Version >= 7. Version number for the EventBox data structures (see EventBoxes section).

  • AniBG Version (int32): Present if Main Version >= 7. Version for Animated Background (AniBG) structures.

  • ItemBox Version (int32): Present if Main Version >= 7. Version for ItemBox structures.

  • Gimmick Version (int32): Present if Main Version >= 8. Version for Gimmick structures.

  • Reserved Field (v9) (int32): Present if Main Version >= 9. This 32-bit value is read but not used by the parser (likely reserved for trigger/event counts related to the scripting system).

  • Reserved Field (v16) (int32): Present if Main Version >= 16. Another reserved 32-bit value (unused by current readers; possibly introduced for future extensions).

  • Reserved Fields (v18) (int32 × 2): Present if Main Version >= 18. Two reserved 32-bit integers (unused by current readers; possibly introduced for future extensions).

Paths Section

Following the header, the .wdata file contains a series of file path strings. A placeholder string ".\" (dot + backslash) is used to indicate an “empty” path;. All paths in this section use this format, and any ".\" value should be interpreted as “no path” or not used.

The paths appear in the following order:

  • ModelPath (WString): Path to the main map model (.mmp).

  • NavMeshPath (WString): Path to the navigation mesh file for the map (walkable space navigation).

  • NavHeightPath (WString, if Main Version ≥ 2): Path to the navigation height-map file. This is present only in version 2 and above.

  • EventBoxPath (WString): Path to an external event box definition file. In practice, event boxes are embedded in the .wdata (for v7+), so this path is often empty (".\").

EventBoxes Section

The EventBoxes section contains definitions of various interactive or trigger volumes in the map (spawn points, triggers, portals, etc.). This section is present only if Main Version >= 7, since EventBoxes were introduced in version 7 of the format. If the main version is below 7, no EventBox data will be present (the file proceeds directly to the next section).

The EventBoxes section begins with an index table that outlines the types of event boxes included and where their data is located. The structure is as follows:

uint32 typeCount;
struct EventBoxIndexEntry {
    uint32 offset;  // file offset where data for this type begins
    uint32 count;   // number of EventBox records of this type
} entries[typeCount];

Here, typeCount is the number of distinct event box type slots included. Each EventBoxIndexEntry corresponds to an EventBox type ID from 0 up to typeCount-1 in sequential order. The index of each entry implicitly represents the EventBox type (e.g. the first entry is type 0, second is type 1, etc.). The offset is an absolute file position (from beginning of the file) where the data records for that type are stored, and count is how many such records exist. The entries may include types with zero count – if count is 0, it means no objects of that type are present (the offset in that case can be ignored). The entries are not guaranteed to be sorted by offset in the file, so a reader should use the provided offsets to seek to each type’s block. (In practice, type entries often appear in ascending order of type ID, but the actual data blocks might not be in type ID order, hence the need for offsets and the sorting performed by the reader.)

After the index table, the EventBox records for each type follow, typically grouped by type. To read them, one would iterate through the index entries (often in order of increasing offset) and for each entry, seek to the given offset and read count records of that type.

EventBox Types Overview

Each EventBox record begins with a common Oriented Bounding Box (OBB) header, which defines the volume in 3D space. This OBB header consists of:

  • NameWString: The name of the event box.

  • Position – 3×float: The 3D position (X, Y, Z) of the box’s center.

  • Scale – 3×float: Scale factors applied to the box (typically 1,1,1 for standard size).

  • Rotation – 4×float: Rotation quaternion (X, Y, Z, W) defining the box’s orientation in the map.

  • Extents – 3×float: The half-dimensions of the box (extent in X, Y, Z from the center). This defines the size of the box along each local axis.

All EventBox types include these fields at the start. In the structures below, we denote this common header as OBB base. After the OBB, each EventBox type may have additional fields specific to its function. The availability of some fields depends on the EventBox Version (the sub-version read from the header).

The EventBox types and their IDs are listed below:

enum EventBoxType
{
    CameraBox = 0,
    RespawnBox = 1,
    StartPointBox = 2,
    TriggerBox = 3,
    SkidBox = 4,
    EventHitBox = 5,
    NpcBox = 6,
    PortalBox = 7,
    SelectMapPortalBox = 8,
    InAreaBox = 9,
    EtcBox = 10,
    CameraBlockBox = 11,
    CutoffBox = 12,
    CameraTargetBox = 13,
    UnusedBox = 14,
    MiniMapIconBox = 15,
    EnvironmentReverbBox = 16,
    WaypointBox = 17,
    ObstacleBox = 18
}

Type ID
EventBox Type
Description (brief)

0

CameraBox

Camera trigger volume with predefined camera views and settings.

1

RespawnBox

Enemy spawn point volume (with spawn counts and respawn info).

2

StartPointBox

Map start positions marker.

3

TriggerBox

Generic trigger volume (can activate events, with state/motions).

4

SkidBox

Slide/knockback volume (imposes velocity and timing on entry).

5

EventHitBox

Trigger volume for damage events (inflicts damage or effects).

6

NpcBox

NPC spawn point (defines an NPC by name and IDs).

7

PortalBox

Map transition portal (warps to another map/portal ID).

8

SelectMapPortalBox

Portal that opens a dungeon map selection UI.

9

InAreaBox

Area trigger for map events (e.g. enters area to trigger event).

10

EtcBox

Miscellaneous trigger/marker (generic ID, no additional data).

11

CameraBlockBox

Region that blocks camera movement or line-of-sight.

12

CutoffBox

Navigation cutoff volume (e.g. for AI navmesh or event boundaries).

13

CameraTargetBox

Camera focus target volume (e.g. for boss or point of interest).

14

(Unused/Unknown)

No known usage; may be unused/reserved.

15

MiniMapIconBox

Position of a minimap icon (with icon type).

16

EnvironmentReverbBox

Region that changes audio environment reverb settings.

17

WaypointBox

Pathfinding waypoint node (with links to other waypoints).

18

ObstacleBox

Obstacle volume (associated with obstacle path, no extra data).

Notes: Type 14 does not appear in any known .wdata files or the decompiled reader logic, indicating type 14 is likely unused or reserved. Each type’s specific data structure is detailed below. In the structures, EventBoxVersion is abbreviated as evVer, referring to the EventBox sub-version that was read from the header.

CameraBox (Type 0)

A CameraBox defines a volume that, when the player or camera enters it, triggers specific camera viewpoints or cinematic camera behavior. It can contain up to 4 predefined camera configurations.

struct CameraBox {
    OBB base;               // Common OBB fields (Name, Position, Scale, Rotation, Extents)
    uint32 Category;        // Camera box category (only present if evVer >= 7)
    CameraInfo CameraInfos[4];
};

struct CameraInfo {
    // Camera target name (the object or bone the camera should track)
    WString CameraTarget;   // Present for each entry if evVer >= 3; if evVer < 3, only the first entry has a CameraTarget (others are omitted)
    WString CameraName;     // Name/identifier for this camera shot
    // Camera transform and lens settings:
    float CameraPosX, CameraPosY, CameraPosZ;   // Camera position coordinates
    float CameraRotX, CameraRotY, CameraRotZ;   // Camera rotation (Euler angles or orientation vector)
    float FOV;                                  // Field of view (in degrees)
    float Frustum[14];                          // Additional frustum parameters (14 floats, usually related to view frustum extents/clipping planes)
    bool  BuildPVS;         // If evVer >= 5, a boolean flag (stored as uint32) indicating to build PVS (Potentially Visible Set) for this camera
    
    if (evVer >= 4) {  
        // Various object lists that control rendering and visibility when this camera is active:
        uint32 PVS_BG_Count;
        uint32 PVS_EventBox_Count;
        uint32 RenderBg_Count;
        uint32 RenderAni_Count;
        uint32 RenderItem_Count;
        uint32 RenderGimmick_Count;
        uint32 NoRenderBg_Count;
        uint32 NoRenderAni_Count;
        uint32 NoRenderItem_Count;
        uint32 NoRenderGimmick_Count;
        uint32 BuildPos_Count;  // Number of "build position" points
        
        // PVS (Potentially Visible Set) lists:
        uint32 PVS_BG_IDs[PVS_BG_Count];  // IDs of background objects always visible
        if (evVer < 6) {
            uint32 PVS_EventBox_IDs[PVS_EventBox_Count];  // IDs of event boxes visible (pre-v6)
        } else {
            struct { uint32 Category; WString Name; } PVS_EventEntries[PVS_EventBox_Count];  
            // Each entry has a category ID and name for event boxes visible (v6+ splits EventBox references into category+name)
        }
        // Render/NoRender lists – each entry is an object reference that should be forced rendered or hidden for this camera.
        // The format of each entry depends on the Main Version (not the evVer):
        // If Main Version >= 22, each entry has a uint32 Category ID followed by a WString Key (name).
        // If Main Version < 22, each entry is just a WString (no category; implicitly category is 0 or not used).
        struct Entry { uint32 Category; WString Key; };  // (Definition for clarity)
        Entry RenderBgList[RenderBg_Count];         // Background objects to force render
        Entry RenderAniList[RenderAni_Count];       // Animated BG objects to force render
        Entry RenderItemList[RenderItem_Count];     // ItemBox objects to force render
        Entry RenderGimmickList[RenderGimmick_Count]; // Gimmick objects to force render
        Entry NoRenderBgList[NoRenderBg_Count];     // Background objects to hide
        Entry NoRenderAniList[NoRenderAni_Count];   // AniBG objects to hide
        Entry NoRenderItemList[NoRenderItem_Count]; // ItemBox objects to hide
        Entry NoRenderGimmickList[NoRenderGimmick_Count]; // Gimmick objects to hide
        
        float3 BuildPosList[BuildPos_Count];  // List of positions (each 3 floats) used for building PVS volumes or other camera-related reference points.
    }
};

Explanation: The CameraBox includes an optional Category field (a 32-bit value) introduced in EventBoxVersion 7. The core of a CameraBox is the array of 4 CameraInfo entries. Each CameraInfo provides a camera’s target (only the first had a target in older versions) and a name, followed by 21 floats of camera parameters (position, rotation, FOV, and 14 frustum parameters). If the EventBox version is 5 or above, BuildPVS is a flag indicating that entering this camera box should trigger PVS computation (the code reads a uint and treats nonzero as true).

For EventBoxVersion 4 and above, a series of counts is present, followed by multiple lists that control which objects are rendered or excluded when this camera view is active. The PVS_BG list (IDs of background models) and PVS_EventBox list define which static world and event objects are considered visible in this camera’s context. Notably, the format of the PVS EventBox list changed in version 6: earlier versions store only a list of IDs (integers), whereas v6+ stores a list of structures with a category and name (likely to allow more flexible identification). The Render/NoRender lists specify additional objects to force-show or force-hide for this camera. In main versions 22 and above, each entry in those lists carries an extra category identifier before the object’s name/key, whereas in earlier versions only the name is stored. Finally, BuildPos points (if any) are positions in space related to the camera setup (possibly used by the engine to build volumes for PVS or camera transitions).

A CameraBox record in the file will contain all the above fields in sequence. Not all camera info entries may be meaningfully used by the game (often only the first one might be used for simple camera triggers), but the file allocates space for 4 entries regardless. If fewer than 4 are configured, later entries may have default or empty values (e.g., empty strings, zeroes).

RespawnBox (Type 1)

A RespawnBox defines an enemy spawn volume, used in dungeon maps to spawn waves of enemies.

struct RespawnBox {
    OBB base;            // OBB header
    int32 TotalEnemyNum; // Total number of enemies that can spawn from this point
    int32 EnemyNum;      // Number of enemies to spawn at one time (spawn wave size)
    WString EnemyName;   // Enemy type name to spawn
    float RespawnTime;   // Respawn delay in seconds (time before respawn)
    WString RespawnMotion; // Animation/motion to play on spawn
    bool InCheck;        // If true, possibly indicates "spawn when in area"
    bool RandomDirection; // If true, spawned enemies face a random direction
    bool Difficulty[5];  // Array of 5 booleans (each stored as int32) corresponding to difficulty flags (one for each difficulty level, e.g.,Normal, Hard, etc.)
};

StartPointBox (Type 2)

A StartPointBox marks a start location of the player in a map.

struct StartPointBox {
    OBB base;
    int32 ID;   // Identifier for the start point
};

The ID is used to differentiate multiple start points or to reference a start point in scripts.

TriggerBox (Type 3)

A generic TriggerBox is a volume that triggers scripted events or actions. It often has an associated state and can play an animation or motion when activated.

struct TriggerBox {
    OBB base;
    int32 State;          // Initial state or type of trigger
    WString ActionMotion; // Name of a motion or action to perform when triggered
    WString Motion;       // Possibly another motion/animation name (or secondary action)
    if (evVer >= 9) {
        int32 SignpostTextID;   // ID of a signpost text
        float SignpostPosX, SignpostPosY, SignpostPosZ; // Coordinates for repositioning a signpost display (a 3D vector)
    }
};

For EventBoxVersion 9 and above, TriggerBox gained fields for a “signpost” – likely an on-screen prompt or indicator. The file stores an integer SignpostTextID (which could reference a string table for the prompt text) and a 3D reposition vector for the signpost (perhaps to offset the prompt’s location). These fields are absent in older versions (evVer < 9).

SkidBox (Type 4)

A SkidBox (sometimes called SlideBox) applies a force or velocity to entities entering it, possibly causing the player or object to slide.

struct SkidBox {
    OBB base;
    int32 State;        // State or type 
    float3 Velocity;    // A 3D velocity vector applied to the entity
    float EndTime;      // Duration (seconds) after entering before effect ends
    float Duration;     // Possibly total duration of the sliding effect (or time the velocity is applied)
};

The Velocity is read as three floats (X, Y, Z) and likely represents an impulse direction and magnitude. EndTime and Duration are two float values, one might be the time at which the skid ends and the other the time over which it occurs (they might be used by the engine to control the easing of the effect).

EventHitBox (Type 5)

An EventHitBox triggers a scripted event when a character or object is hit or enters the volume, often dealing damage or causing a specific effect (like a trap).

struct EventHitBox {
    OBB base;
    uint32 State;         // State identifier
    WString AniBGName;    // Name of an associated AniBG (animated object) or effect to trigger
    float Damage;         // Damage value to apply (if this box causes damage)
    float3 Direction;     // Direction vector of the force/knockback applied
    WString DamageMotion; // Motion to play on the entity when hit (e.g., stagger animation)
    uint32 HitCount;      // Number of hit timing entries that follow
    uint32 TempHitCount;  // Number of "temporary" hit timing entries (for secondary hits)
    float HitTimes[HitCount];         // Array of hit timing values (in seconds)
    float TempHitTimes[TempHitCount]; // Array of secondary hit timing values
};

The EventHitBox is one of the more complex event boxes. After the initial fields, two counts are read (HitCount and TempHitCount), followed by that many float values for each list. These likely define at what times the event deals damage or triggers effects after activation (e.g., a trap might hit multiple times). The TempHitTimes could be used for a secondary effect or a temporary hit registration separate from the main hits. In summary, this structure allows an EventHitBox to inflict damage (Damage) in a certain direction (Direction) and possibly multiple times according to the timing arrays.

NpcBox (Type 6)

An NpcBox designates an NPC spawn in the map.

struct NpcBox {
    OBB base;
    WString NpcName;   // Name of the NPC (usually corresponds to an NPC template or type
    int32 ID;          // NPC ID
    int32 InstanceID;  // Instance ID (unique identifier if multiple of same NPC)
};

These IDs allow the game or scripts to reference the NPC. For example, ID tie to a specific NPC type, and InstanceID could differentiate multiple instances of the same NPC type.

PortalBox (Type 7)

A PortalBox is a volume that teleports the player to a different map or location (a portal between maps).

struct PortalBox {
    OBB base;
    WString WarpMapName; // Name of the target map to warp to
    int32 ID;            // Portal ID in the current map
    int32 MsgType;       // Message type or portal type (controls how the portal behaves/UI)
    int32 WarpMapID;     // ID of the target map
    int32 WarpPortalID;  // ID of the target portal within the target map (to arrive at)
    bool  Active;        // Whether the portal is active/enabled
};

The PortalBox contains both name and ID references for the destination. WarpMapName is the internam name of the map, while WarpMapID is a corresponding map ID. WarpPortalID is the ID of a specific entry point in the target map (so you can have multiple portals within a map). MsgType indicates the type of portal (for instance, whether it requires a confirmation). The Active flag can disable a portal until some condition is met.

SelectMapPortalBox (Type 8)

A SelectMapPortalBox is a portal that presents a selection (e.g., the UI for choosing a dungeon) rather than immediately warping to a fixed map.

struct SelectMapPortalBox {
    OBB base;
    int32 ID;       // Identifier for this portal (current map context)
    int32 MsgType;  // Type of portal/message (likely defines the selection UI or behavior)
    bool  Active;   // Whether this portal is active
};

InAreaBox (Type 9)

An InAreaBox triggers an event when an entity (usually the player) is inside a certain area. Often used for events that should happen when you enter or stand in a region.

struct InAreaBox {
    OBB base;
    WString WarpMapName; // Name of a map or area (often used to designate a location or trigger target)
    int32 ID;            // An identifier for this area trigger
    bool  Active;        // Whether the trigger is enabled
};

EtcBox (Type 10)

EtcBox stands for "Etcetera" box – a catch-all volume for miscellaneous triggers or markers. It has no special fields beyond the OBB:

struct EtcBox {
    OBB base;
    int32 ID;   // Generic identifier (meaning defined by scenario/scripts)
};

This can be used by scripts for various purposes (e.g., to mark locations for script events) when no dedicated type exists. The ID allows distinguishing multiple EtcBoxes.

CameraBlockBox (Type 11)

A CameraBlockBox defines a region that blocks the camera or the camera’s line-of-sight (LOS), often to prevent the camera from clipping through walls or seeing out of bounds. It has no extra fields:

struct CameraBlockBox {
    OBB base;
    // No additional data; the presence of this box itself signals a camera blockage volume
};

The engine uses the OBB of this box to constrain or collide the camera against it.

CutoffBox (Type 12)

A CutoffBox marks an area for navigation or AI cutoff. It likely tells the game AI or navigation mesh to treat an area as off-limits or triggers a special behavior when an entity crosses it.

struct CutoffBox {
    OBB base;
    int32 CutoffType; // Type or category of cutoff (the game may define meanings for certain values)
};

CutoffTypecould correspond to predefined behaviors (e.g., 0 = no cut, 1 = block navmesh, 2 = trigger event cutscene, etc.)

CameraTargetBox (Type 13)

A CameraTargetBox is used to signal a point or area that the camera should focus on (for example, when a boss appears, the camera might target this box).

struct CameraTargetBox {
    OBB base;
    // (No fixed fields directly after base in older versions)
    if (evVer < 8) {
        WString NameDeprecated; // In older versions, a name string was stored (now deprecated)
    } else {
        int32 NameTextID;       // In v8+, an integer ID is used instead of a name string
    }
    bool TargetLocalPC;         // If true, target the local player character (present if evVer >= 2)
};

Version differences: Originally (EventBoxVersion < 8), the file stored a WString for the target’s name (possibly the name of the entity to focus on). In EventBoxVersion 8 and above, this was replaced by a numeric NameTextID (likely referencing a string or an object ID).

The TargetLocalPC flag appears if EventBoxVersion >= 2. If true, it might indicate the camera target should dynamically follow the local player character (PC), rather than a fixed point. Essentially, this box can serve as a focus target for the camera system, with options to use a preset name/ID or just always focus the player.

MiniMapIconBox (Type 15)

A MiniMapIconBox determine the position of a icon on the mini-map.

struct MiniMapIconBox {
    OBB base;
    int32 IconType; // Identifier for which icon type to show on the minimap
};

The IconType corresponds to a specific icon graphic or indicator defined by the game (for example, a treasure chest icon, a warning sign, etc.).

EnvironmentReverbBox (Type 16)

An EnvironmentReverbBox defines an area with special audio reverb properties.

struct EnvironmentReverbBox {
    OBB base;
    int32 ReverbType; // Identifier for the reverb/echo setting to apply in this area
};

The ReverbType would map to audio environment presets (e.g., 0 = default, 1 = cave reverb, 2 = sewer echo, etc.). When the listener (player) is inside this volume, the audio engine changes the ambient reverb to the specified type.

WaypointBox (Type 17)

A WaypointBox represents a node in a network of waypoints, often used for patrol paths or navigation of moving entities (or possibly triggers). It can link to other waypoints.

struct WaypointBox {
    OBB base;
    int32 ID;      // Waypoint ID number
    float Range;   // Radius or range of this waypoint’s effect (how close something must be to count as reaching it)
    int32 LinkCount; // Number of linked waypoints
    int32 Links[LinkCount];       // Array of IDs of other WaypointBoxes that are linked to this one
    float LinkDistances[LinkCount]; // Array of distance values for each link (float distance to the corresponding linked waypoint)
};

The WaypointBox essentially contains an ID and a connectivity list. Each link has an associated distance (which could be the actual distance or a weight for that path). These could be used for AI navigation or telling a moving platform how to travel between waypoints. The Range might specify how close an entity must get to this waypoint to consider it “reached” or could define an area of influence.

ObstacleBox (Type 18)

An ObstacleBox defines the position of obstacles that the player can collide.

struct ObstacleBox {
    OBB base;
    // No additional fields
};

ObstacleBoxes likely work in conjunction with the Obstacle Path (discussed in the next section) to define regions that obstacles occupy.

Summary of EventBoxes: The EventBox section provides a flexible way to embed a variety of gameplay triggers and volume-based effects in the map data. Many fields are conditionally present depending on the EventBoxVersion (which tracks format changes over game updates).

AniBG Section (Animated Backgrounds)

After EventBoxes, the .wdata contains the AniBG section, which lists Animated Background objects in the map. These are typically looping animations (e.g. fire).

This section begins with a 32-bit AniBG count, followed by that many AniBG entries. Each entry has the following structure:

struct AniBG {
    OBB base;           // Name (identifier of the object) and transform of the animated object
    WString Model;      // Path to the model file for this animated object
    WString Motion;     // Name of the the animation (motion) to play on the model
    bool   Loop;        // Whether the animation loops
    int32  LightIndex;  // Light index to use
    int32  CoverIndex;  // Cover index (possibly for layer ordering or occlusion grouping)
    bool   Shadow;      // Cast shadow flag (present if AniBGVersion >= 3)
    bool   MoveWeight;  // "Move weight" flag (present if AniBGVersion >= 4) – exact usage unclear, perhaps for physics or weighted movement
    float  PVSRad;      // PVS radius (present if AniBGVersion >= 5) – radius used for visibility culling optimization
};

Field meanings: Model is a path to a .mdatafile for the object. Motion is the name of the animation that the model will play. Loop indicates if the motion should loop continuously. LightIndex and CoverIndex are likely indices into lighting and cover (occlusion mask) tables used by the engine (they might determine which dynamic lights affect the object, and how it is occluded by world geometry). Shadow indicates if the object should cast a shadow. MoveWeight might indicate if the object’s movement influences characters (or has physics weight) – in some contexts, this is used to signal moving platforms or weighted movement. PVSRad (Potentially Visible Set Radius) is used for performance: it might define a radius for when the object should load or be considered in view (similar to LOD distance or for clustering PVS computations).

ItemBox Section

ItemBox lists interactive item containers (like chests or crates that drop items).

struct ItemBox {
    OBB base;
    WString Model;      // Path to the item box model file
    WString Motion;     // Path to an animation file (e.g., open animation)
    WString TablePath;  // Path to the item drop table
    bool   Loop;        // Whether the motion loops
    bool   OpenEnable;  // "Open enabled" flag (present if ItemBoxVersion >= 3)
};

Notes: The TablePath usually points to a table defining what items the box contains (loot table). The OpenEnable flag (introduced in ItemBoxVersion 3) is likely used by the game to mark whether the box can be opened/interacted with. In older versions, this field is absent, so item boxes might always be interactable or require different logic. The model and motion are similar to AniBG, except these typically represent a breakable or openable object. The OBB defines the physical area of the box (for collision and trigger).

Gimmick Section

The Gimmick section describes miscellaneous interactive objects or mechanisms (traps, levers, etc.) referred to in Rusty Hearts as “gimmicks.”

struct Gimmick {
    OBB base;
    WString Model;     // Model path for the gimmick object
    WString Motion;    // Motion/animation name for the gimmick
    int32 LoopFlag;    // Loop flag or mode (0/1 or a mode value)
    int32 LightIndex;  // Light index (lighting group)
    int32 CoverIndex;  // Cover index (occlusion group)
    int32 Shadow;      // Cast shadow flag (0 or 1)
    int32 MoveWeight;  // Move weight flag (0 or 1)
    int32 TemplateID;  // Template/Type ID of the gimmick
};

Each Gimmick defines an object in the world with a model and (optionally) an animation. The TemplateID refers to a predefined gimmick type in the gimmicktemplatetable.rh table. The various flags and indices (LoopFlag, Shadow, MoveWeight) have similar meanings as in AniBG/ItemBox: whether the motion loops, whether it casts a shadow, and whether its movement has physical weight or influence.

Obstacle Path (v2+)

If the Main Version >= 2, the .wdata includes an Obstacle Path string after the Gimmick section. This is a wide string (WString) read with the same method as other paths, and it represents the path to an obstacle data file.

if (MainVersion >= 2) {
    WString ObstaclePath;
}
  • ObstaclePath: Path to the obstacle definition file (if any). If this string is ".\" or empty, it implies no external obstacle path is used. If provided, it might point to a file containing waypoints or spline paths for obstacles (e.g., moving platforms or hazards) in the map.

In the game’s loading logic, if ObstaclePath is present and not empty, it would load that file to get additional obstacle movement information. The ObstacleBox event boxes (type 18) likely relate to this path – for example, marking where obstacles start or collide.

Additional Paths (Trailing Paths)

After the obstacle path (or directly after Gimmicks, if version < 2), there are two trailing path fields:

WString MocPath;
WString AniBGPath;

These are read unconditionally (for all versions) at the end of the sections:

  • MocPath: Usually a path to a MOC file. In Rusty Hearts, .moc files likely refer to motion cutscene or camera motion files. This could specify a scripted camera or event file associated with the map (possibly the intro or outro cutscene of the level). Often this field may be empty (".\"), meaning no associated motion/cutscene file.

  • AniBGPath: A path to an external Animated BG definition file. This might not be heavily used in practice, since AniBGs are listed in the file itself. It could be for specifying an alternate source of AniBG data or for editor organizational purposes. Usually found empty as well.

Triggers Section

The Triggers section contains data for scripted triggers and events tied to the level. This part of the format underwent changes in later versions of Rusty Hearts. We will describe the older format first (for versions prior to 9) and then the newer format (for version ≥ 9), as they differ significantly.

Older Trigger Format (Main Version < 9)

In the original format, the .wdata directly includes a list of triggers and their associated event/condition/action names.

int32 reserved0;           // always present (usually 0)
WString scriptDir;         // a directory or path for script files (often ".\" indicating none)
int32 reserved1;           // another reserved field (usually 0)
WString mainScript;        // main script file name for the level (could be an entry script)
int32 triggerCount;        // number of trigger entries in this file

// List of triggers:
for (int i = 0; i < triggerCount; ++i) {
    WString Name;          // name of the trigger (identifier used in scripts)
    WString Comment;       // a comment or description for the trigger (for editor use)
    int32 eventCount;      // number of "Event" script calls for this trigger
    int32 conditionCount;  // number of "Condition" script calls for this trigger
    int32 actionCount;     // number of "Action" script calls for this trigger
    WString Events[eventCount];       // list of event function names (as strings)
    WString Conditions[conditionCount]; // list of condition function names (strings)
    WString Actions[actionCount];       // list of action function names (strings)
}

In this format, each trigger has a name and an comment, and then lists of script function names that define what events it listens to, what conditions must be met, and what actions to execute when triggered. These names correspond to Lua script functions. The mainScript at the beginning is the filename of a main script for the level’s triggers . The scriptDir might indicate the root directory where trigger scripts are located.

Example: In a level, you might have triggerCount = 2. The first trigger could be named "TriggerDoor" with a comment "Opens the door when all enemies defeated", eventCount = 1 (Event "OnEnemiesDead"), conditionCount = 0, actionCount = 1 (Action "OpenDoor"). The second trigger might be "TriggerChest", etc. These strings tie into the game’s scripting system; when the conditions are met, it calls the specified actions.

New Trigger Format (Main Version ≥ 9)

Starting with Main Version 9 (and refined through versions 16 and 18), the trigger data shifted to an externalized script system. Instead of listing each trigger’s events and actions in the .wdata, the file now provides global script references.

int32 reserved0;
WString scriptDir;
int32 reserved1;
WString mainScript;
int32 eventScriptCount;
int32 conditionScriptCount;
int32 actionScriptCount;
WString EventScriptPaths[eventScriptCount];
WString ConditionScriptPaths[conditionScriptCount];
WString ActionScriptPaths[actionScriptCount];

After the mainScript string, instead of a triggerCount, the file directly lists how many distinct event, condition, and action script files are used in this level, followed by the paths (names) of those script files. Each of those script files contains the actual logic for triggers. The mainScript coordinate them (like an entry point).

In the game’s loading code, we can see that for version≥9, it reads nEventNum, nConditionNum, nActionNum (corresponding to the three counts) and then reads each path and loads the script (using something like M3dSWorldManager::FindLua to load the Lua script file). It then registers each event, condition, and action with a trigger manager (M3dSTrigger::AddEvent, etc.) as it reads them. This implies that the actual triggers (linking events to conditions and actions) are defined in those scripts rather than explicitly in the data file.

As a result, when Main Version >= 9, the triggerCount and per-trigger lists are omitted. The file no longer contains individual trigger entries at all – those are handled by the scripts referenced. The reserved fields remain for compatibility, but triggerCount is not present; instead we have the three script count integers.

Summary (Trigger handling):

  • For version < 9: Use the older format – read triggerCount and then read each trigger’s event/condition/action lists.

  • For version ≥ 9: Skip reading a trigger count (it’s not there). Instead, read the three script counts and then that many script path strings

Scenes Section

The Scenes section defines cutscene cameras and effects used in the map (commonly for cutscenes or special camera transitions). It is divided into two parts: first, a list of scene camera definitions, and second, for each scene, additional data about what to show/hide during that scene.

The section begins with a scene count, followed by that many scene definitions:

int32 sceneCount;
Scene scenes[sceneCount];

struct Scene {
    WString File;             // Path to the camera file `.mec`
    float FadeInPreview;      // Fade-in time for preview (camera fade in at scene start)
    float FadeHoldPreview;    // Time to hold after fade-in (preview)
    float FadeOutPreview;     // Fade-out time for preview (end of scene)
    uint32 Category;          // Scene category or type flag
    // The next 6 floats are stored in an array and then assigned:
    float SceneFadeIn;        // Fade-in time for the scene itself (camera effect)
    float SceneFadeHold;      // Hold time during scene (camera effect)
    float SceneFadeOut;       // Fade-out time for the scene (camera effect)
    float BlendTime;          // Blend time between this scene camera and the next (camera transition blend)
    float FogNear;            // Fog near distance during scene
    float FogFar;             // Fog far distance during scene
    float3 Position;          // Camera position for the scene
    float3 Rotation;          // Camera rotation 
    float FOV;                // Camera field of view for the scene
    float AspectRatio;        // Camera aspect ratio for the scene
    // (After reading, this Scene is stored and later augmented with more info below)
};

Essentially, each scene has a camera setup (position, rotation, FOV, aspect) and a series of timing parameters for fades and blends. The FadeInPreview/Hold/Out are often used for a preview window in the editor (or possibly the initial fade when the scene is triggered in-game). The SceneFadeIn/Hold/Out are presumably the actual in-game camera fades when the scene plays. FogNear and FogFar define the fog distances to use during the scene (overriding the level’s normal fog), and BlendTime is the cross-fade duration used if transitioning between scenes or back to gameplay. The Category could be used to classify scenes (for example, 0 for normal, 1 for boss intro, etc., or maybe it’s used as an ID to trigger the scene).

After reading the scene list, this next data includes names, indices, and lists of objects to manipulate during the scene:

for (int i = 0; i < sceneCount; ++i) {
    WString Name;             // Name of the scene 
    int32 eventEntryCount;    // Number of event entries (scene events)
    int32 sceneIdCount;       // Number of scene indices in the index list
    int32 renderBgCount_User;    // Count of user-specified render BG entries
    int32 renderAniCount_User;   // Count of user-specified render AniBG entries
    int32 renderItemCount_User;  // Count of user-specified render ItemBox entries
    int32 renderGimmickCount_User; // Count of user-specified render Gimmick entries
    int32 noRenderBgCount_User;   // Count of user-specified *no-render* BG entries
    int32 noRenderAniCount_User;  // Count of no-render AniBG entries
    int32 noRenderItemCount_User; // Count of no-render ItemBox entries
    int32 noRenderGimmickCount_User; // Count of no-render Gimmick entries

    uint32 SceneIndices[sceneIdCount];  // List of indices (IDs) related to this scene
    SceneElement EventEntries[eventEntryCount];  // Event scene entries triggered in this scene
    // Each SceneElement is a pair: { uint32 Category; WString Key }.
    // It could represent something like an event trigger or an object to manipulate.
    // EventEntries are read from the file as Category + Key pairs

    // Render/NoRender lists (user-specified):
    SceneElement RenderBgUser[renderBgCount_User];
    SceneElement RenderAniBgUser[renderAniCount_User];
    SceneElement RenderItemBoxUser[renderItemCount_User];
    SceneElement RenderGimmickUser[renderGimmickCount_User];
    SceneElement NoRenderBgUser[noRenderBgCount_User];
    SceneElement NoRenderAniBgUser[noRenderAniCount_User];
    SceneElement NoRenderItemBoxUser[noRenderItemCount_User];
    SceneElement NoRenderGimmickUser[noRenderGimmickCount_User];
};

Interpretation:

  • Name: A name for the scene

  • SceneIndices: An array of indices (IDs). These might refer to specific cameras or scene resources (for instance, linking to entries in the SceneResource section by ID). The game might use these IDs to quickly enable/disable objects relevant to the scene.

  • EventEntries (EventScenes in code): A list of scene events that should occur. Each has a Category and a Key. Category likely identifies the type of event (e.g., 0 = play animation, 1 = trigger effect, etc.) and Key is a string key or identifier (possibly the name of an animation, an event marker, or an entity name). These could instruct the engine to do certain things when the scene starts (e.g., play a certain cutscene or trigger an NPC action).

  • RenderXUser / NoRenderXUser: These eight lists allow overriding object visibility during the scene. For example, RenderBgUser contains background objects that should be forced visible, NoRenderBgUser those that should be hidden, and so forth for AniBGs, ItemBoxes, and Gimmicks. Each entry has a Category and Key: likely the Category here corresponds to an object type or group, and the Key is the object’s name or identifier. Essentially, these lists fine-tune the environment for the scene (show or hide certain things so the scene looks correct).

It’s worth noting that the SceneIndices and EventEntries might correlate with the SceneResources section (discussed next). For example, an EventEntry might trigger a specific scene resource by key.

Scene Resources Section

The Scene Resources section defines reusable scene elements such as model animations, camera motions, and sound or effect cues that the scenes (above) can reference. This is essentially a database of cutscene content for the level, which scenes can pull from by key.

The section starts with a 32-bit entry count, followed by that many SceneResource entries. Each SceneResource has the following structure:

int32 sceneResourceCount;
SceneResource sceneResources[sceneResourceCount];

struct SceneResource {
    WString Key;          // Key name of this scene resource (identifier used by scenes)
    int32 aliasCount;
    WString Aliases[aliasCount];  // Alternate string keys that map to this resource (aliases)
    int32 pathCount;
    float Delay;          // Delay before starting (only present if pathCount > 0, otherwise 0)
    SceneData Paths[pathCount];
    int32 cueCount;
    Cue Cues[cueCount];
    int32 soundCount;
    SoundRecord Sounds[soundCount];
    uint32 Unk1;
    uint32 Unk2;
    float  Unk3;
    uint32 Unk4;
    int32 ambientCount;
    AmbientRecord Ambients[ambientCount];
};

struct SceneData {
    WString Model;      // Model to use in this scene path element
    WString Motion;     // Motion/animation for the model
    WString Name;       // Name for this scene element (could be an object or actor name)
    WString EventName;  // Event name or marker for this element
    uint32 Time;        // Start time of this element in the scene (milliseconds or frames)
    uint32 Hold;        // Duration to hold this element (or duration of the motion)
    float  BlendTime;   // Blend time into the next element (only present for all but the last element)
};

struct Cue {
    float  Delay;   // Time delay before the cue (seconds)
    WString Name;   // Name of the cue (could be an event or effect identifier)
    uint32 ID;      // ID of the cue (likely ties to a specific effect or action)
    float  Start;   // Start time of the cue (seconds, or an absolute timeline position)
};

struct SoundRecord {
    float Start;    // Start time of the sound (seconds)
    float FadeIn;   // Fade-in duration for the sound (seconds)
    float FadeOut;  // Fade-out duration (seconds)
    float VolMax;   // Maximum volume level
    float VolMin;   // Minimum volume level
    WString Path;   // Path to the sound file (sound effect or music)
};

struct AmbientRecord {
    float Start;       // Start time for the ambient sound (seconds)
    WString Path;      // Path to ambient sound
    bool PlayOnStart;  // If true, play immediately on scene start
    bool Loop;         // If true, loop the ambient sound
};

Now, let’s break down how this is stored and used, with references to how it’s read:

  • Key: A unique string key for the SceneResource. Scenes refer to resources by this key (for instance, a scene’s EventEntry might use a key to activate a particular SceneResource).

  • Aliases: A list of alternative names (strings) that also refer to this SceneResource. This allows multiple identifiers to map to the same resource (perhaps for convenience or versioning).

  • Paths: This is the core of the SceneResource – it defines an animated sequence or track. pathCount indicates how many segments or keyframes the sequence has. If pathCount is greater than 0, the next value in the file is a float Delay. This Delay is a delay (in seconds) before starting this resource’s sequence (a way to offset the start of this resource relative to scene start). After the delay, it reads pathCount entries of SceneData:

    Each SceneData defines a model and an animation (Motion) to play, possibly representing a character or object performing an action in the scene. It also has a Name and EventName which could tag this animation segment (for example, Name might be the character’s name, EventName could be a specific animation event label). Time and Hold are unsigned ints, likely representing the start time and hold (duration) for this segment in the scene timeline (these could be in milliseconds given they are uint32).

    Importantly, BlendTime: for each SceneData except the last, a float is stored representing the blend duration into the next segment. This allows smooth interpolation between animations. The last segment doesn’t have a following blend (so no extra float after it). The reading code reflects this: it reads BlendTime only if the current index p is less than pathCount - 1.

    Thus, in the binary layout, each SceneData entry consists of 4 WStrings and 2 uint32s, and if it’s not the last entry, it is followed by a float (BlendTime). The next entry’s data comes after that float, or if it was the last entry, the sequence moves on.

  • Cues: After the Paths, an int32 cueCount is read, followed by that many Cue entries. A Cue might represent a timed trigger for a special effect or camera cut within the scene (e.g., an explosion or a camera shake). Each Cue has a delay (when to trigger), a name (identifier of what to trigger), an ID (possibly linking to a specific effect or function), and a start time (which might duplicate the concept of delay or be used differently – could be the absolute time or a parameter). The distinction between Delay and Start in the cue is subtle; possibly Delay is an offset from scene start, and Start could be duration or something needed for the cue's effect.

  • Sounds: Next, an int32 soundCount and that many SoundRecord entries are read. These define sounds to play during the scene, such as voice lines or sound effects. Each SoundRecord includes when to start the sound, how to fade it in/out, volume range, etc., and the path to the sound file. The volume max/min might be used for random variance or distance effects. Typically, these would be one-shot sounds or background music for the scene.

  • Unk1, Unk2, Unk3, Unk4: After the sounds, four additional fields are read: two uint32s, one float, and another uint32. These are currently unknown or reserved. We denote them as Unk1, Unk2, Unk3, Unk4.

  • Ambients: Finally, an int32 ambientCount is read, followed by that many AmbientRecord entries. AmbientRecords likely define ambient sounds or environmental loops. Each has a start time, a path (to an ambient loop or sound environment preset), and two booleans: PlayOnStart and Loop. PlayOnStart indicates the ambient sound should begin playing as soon as the scene starts (the Start time might then act as a volume fade or something), and Loop indicates whether the ambient sound should loop continuously once started. These could be used for background ambiance during the scene (like wind, crowd noise, etc.).

Collectively, a SceneResource encapsulates all the data needed for a particular sequence or element of a cutscene (an animated character, a moving prop, a sound sequence, etc.). The Key is how the Scene knows which resource to invoke. For example, if a Scene has an EventEntry with Category X and Key "DragonBossIntro", that might correspond to a SceneResource with Key "DragonBossIntro" which contains the model, animation, sounds, etc., for the boss intro cutscene. The Scenes section’s indices and event entries likely orchestrate which SceneResources play when the scene is triggered.

Summary: The Scenes and SceneResources sections work together to define in-game cinematics or camera events. Scenes provide the camera and timing context, plus references (EventEntries/IndexList) that pick which resources (from SceneResources) to activate. SceneResources provide the actual content (models, animations, sounds) that will play out. This separation allows reusing the same resource in multiple scenes or triggers.

Conclusion

This covers the .wdata file structure for Rusty Hearts, reflecting the analyzed decompiled C++ server reader logic. Each section of the file is delineated clearly in the binary format, and conditional fields are governed by version numbers stored in the header.

When implementing a parser:

  • Always read the header first and use the version fields to decide which parts of the format to expect (especially for EventBoxes and Triggers).

  • Use the provided counts and offsets to navigate sections like EventBoxes and Scenes.

  • Pay attention to conditional blocks (e.g., if a version is lower, do not attempt to read fields that were added in higher versions).

  • Strings are all wide (UTF-16) and often use ".\" to denote empty path values.

  • The file is mostly sequential; however, the EventBox data is out-of-line and must be sought via offsets.

By following the structures outlined above, one can reliably parse .wdata files, extract map references, triggers, and cutscene information, and use them for analysis or modding of Rusty Hearts map data files.

Last updated