This document will serve as a technical reference for the very old MMORPG Rose Online. The goal is that this information is never lost and may help decades later.
Table of Contents
Table of Contents
- Introduction
- Core Systems
- File Format Specifications
- Client-Side Rendering Pipeline
- Blender Plugin Implementation Guide
- Code Examples and Reference Implementations
- Quick Reference
- Appendices
1. Introduction
1.1 Rose Online Asset System Overview
Rose Online uses a sophisticated asset management system organized around a Virtual Filesystem (VFS) architecture. Assets are stored in proprietary binary formats optimized for real-time rendering in the game engine.
Key Characteristics:
- Coordinate System: Y-up, centimeter-based (cm)
- File Format Versions: Multiple format versions with backward compatibility
- Asset Organization: Hierarchical structure with clear separation of concerns
- Rendering Pipeline: GPU-accelerated with skinned mesh support
Asset File Extensions:
| Extension | Description | Primary Use |
|---|---|---|
.zms | Mesh geometry | Character models, objects, terrain tiles |
.zmd | Skeleton definition | Bone hierarchies for skinned meshes |
.zmo | Motion/animation | Character animations, object animations |
.zon | Zone definition | Terrain composition, grid system |
.zsc | Object composition | Multi-part object assembly |
.ifo | Zone object placement | NPC spawns, objects, events |
.him | Heightmap | Terrain elevation data |
.til | Tile index | Terrain tile mapping |
.chr | Character definition | NPC and character asset references |
1.2 File Format Relationships
graph TD
ZON[ZON Zone File] -->|References| TIL[TIL Tile Index]
ZON -->|Uses| HIM[HIM Heightmap]
ZON -->|Contains| Tiles[Tiles Block]
IFO[IFO Zone Object File] -->|Instantiates| ZSC[ZSC Object Files]
IFO -->|References| CHR[CHR Character File]
IFO -->|Places| Objects[Zone Objects]
CHR -->|References| ZMD[ZMD Skeleton]
CHR -->|References| ZSC
CHR -->|References| ZMO[ZMO Animation]
CHR -->|Maps| Motions[Motion Actions]
ZSC -->|Contains| ZMS[ZMS Mesh]
ZSC -->|Contains| Materials[ZSC Materials]
ZSC -->|Defines| Parts[Object Parts]
ZSC -->|Contains| Effects[Effect References]
ZMS -->|Requires| ZMD
ZMS -->|Contains| Vertices[Vertex Data]
ZMS -->|Contains| Indices[Index Data]
ZMS -->|Contains| BoneWeights[Bone Weights]
ZMO -->|Targets| ZMD bones
ZMO -->|Contains| Channels[Animation Channels]
ZMO -->|Contains| FrameEvents[Frame Events]
ZMO -->|Extended| EZMO[EZMO/3ZMO]
ZMD -->|Contains| Bones[Regular Bones]
ZMD -->|Contains| DummyBones[Dummy Bones]
HIM -->|Provides| Heights[Terrain Heights]
TIL -->|Provides| Indices[Tile Indices]
Complete Asset Loading Workflows
Zone Loading and Composition Workflow:
1. Load ZON File ├─ Parse ZoneInfo block (grid configuration) ├─ Parse EventPositions block (spawn points) ├─ Parse Textures block (texture paths) └─ Parse Tiles block (tile composition) 2. Load Terrain Data ├─ Load HIM file (heightmap) ├─ Load TIL file (tile indices) └─ Generate terrain mesh from ZON tiles + HIM heights 3. Load IFO File ├─ Parse object blocks (NPCs, monsters, decorations) ├─ Parse spawn points ├─ Parse warp positions └─ Parse water planes 4. Load Zone Objects ├─ For each IFO object: │ ├─ Load ZSC file (object composition) │ ├─ For each ZSC part: │ │ ├─ Load ZMS mesh file │ │ ├─ Load ZSC material │ │ ├─ Apply material to mesh │ │ └─ Parent to object entity │ └─ For each ZSC effect: │ └─ Load and spawn effect └─ Place object at IFO position with transform
Character/NPC Assembly Pipeline:
1. Load CHR File
├─ Parse skeleton file list
├─ Parse motion file list
├─ Parse effect file list
└─ Parse character definitions
2. For Character/NPC:
├─ Load ZMD skeleton file
│ ├─ Parse bone hierarchy
│ ├─ Create bone entities
│ ├─ Compute bind pose matrices
│ ├─ Compute inverse bind matrices
│ └─ Create SkinnedMesh component
│
├─ Load ZSC object files (one per body part)
│ ├─ For each ZSC object:
│ │ ├─ Parse material definitions
│ │ ├─ Parse object parts
│ │ ├─ Parse effect definitions
│ │ └─ Load ZMS mesh files
│
├─ Load ZMS mesh files
│ ├─ Parse vertex data (positions, normals, UVs)
│ ├─ Parse bone weights and indices
│ ├─ Parse index data
│ └─ Create mesh entities
│
├─ Bind meshes to skeleton
│ ├─ Attach to bone entities (or dummy bones)
│ ├─ Apply SkinnedMesh component
│ └─ Set material properties
│
├─ Load ZMO animation files
│ ├─ Parse frame data
│ ├─ Parse channel data (position, rotation, scale)
│ ├─ Parse frame events
│ └─ Create animation handles
│
└─ Attach effects to dummy bones
├─ Load effect files from CHR
├─ Spawn effect entities
└─ Parent to dummy bone entities
Animation Application Pipeline:
1. Load ZMO Animation File ├─ Parse FPS and frame count ├─ Parse channel definitions ├─ Parse frame data per channel └─ Parse frame events 2. Create ZmoAsset ├─ Organize channels by bone ID ├─ Store translation vectors per frame ├─ Store rotation quaternions per frame ├─ Store scale values per frame └─ Store frame events 3. Animation Playback ├─ Calculate current frame from time ├─ Get frame fraction for interpolation ├─ Sample bone transforms: │ ├─ Position: Linear interpolation │ ├─ Rotation: Quaternion slerp │ └─ Scale: Linear interpolation ├─ Apply transforms to bone entities └─ Trigger frame events 4. Skinned Mesh Update ├─ GPU computes vertex positions ├─ Using bone weights and current pose ├─ Apply skinning matrices └─ Render final mesh
Detailed File Type Interactions:
ZON ↔ HIM/TIL Interaction:
Purpose: Generate terrain geometry from zone definition
Data Flow:
ZON File:
├─ Grid per patch (float)
├─ Grid size (float)
├─ Tile textures (array of paths)
└─ Tiles (array of ZonTile)
├─ Layer1 texture index
├─ Layer2 texture index
├─ Offset1, Offset2 (UV offsets)
├─ Blend flag
└─ Rotation enum
HIM File:
├─ Width, Height (grid dimensions)
└─ Heights array (width × height floats)
TIL File:
├─ Width, Height (grid dimensions)
└─ Tiles array (width × height indices)
Terrain Generation:
For each grid cell (x, y):
├─ Get tile index from TIL[x][y]
├─ Get tile definition from ZON.tiles[tile_index]
├─ Get texture path from ZON.textures[tile.layer1]
├─ Get height from HIM[x][y]
├─ Apply tile rotation and offset
├─ Generate vertices with height
└─ Apply texture coordinates
Client Implementation (observed in rendering code):
- Terrain is rendered as a grid of tiles
- Each tile is a quad with 4 vertices
- Vertices are positioned using HIM heights
- UVs are computed from TIL tile indices and ZON tile data
- Layer blending is applied if tile.blend is true
IFO ↔ ZSC/ZMS/ZMD Interaction:
Purpose: Place objects in zone with correct models and transforms
Data Flow:
IFO File:
├─ Object type (Deco, NPC, MonsterSpawn, etc.)
├─ Object ID (references CHR or ZSC)
├─ Position (X, Y, Z)
├─ Rotation (quaternion XYZW)
├─ Scale (X, Y, Z)
└─ Additional type-specific data
For NPC Objects:
├─ Load CHR file by NPC ID
│ ├─ Get skeleton file path
│ ├─ Get model IDs
│ ├─ Get motion IDs
│ └─ Get effect IDs
│
├─ Load ZMD skeleton
├─ Load ZSC model files (from CHR.model_ids)
│ └─ For each ZSC object:
│ ├─ Load ZMS meshes
│ ├─ Load materials
│ └─ Create mesh entities
│
└─ Load ZMO animations (from CHR.motion_ids)
└─ Create animation handles
For Decoration Objects:
├─ Load ZSC file directly (from IFO.object_id)
└─ Same process as above
CHR ↔ ZMD/ZSC/ZMO Interaction:
Purpose: Define character/NPC asset composition and animations
Data Flow:
CHR File: ├─ Skeleton files (array of paths) ├─ Motion files (array of paths) ├─ Effect files (array of paths) └─ Character definitions (map: ID → NpcModelData) NpcModelData: ├─ Skeleton index (into skeleton_files) ├─ Name ├─ Model IDs (array: into ZSC objects) ├─ Motion IDs (array of tuples: (motion_id, file_index)) └─ Effect IDs (array of tuples: (motion_id, file_index))
ZSC ↔ ZMS/Material Interaction:
Purpose: Define multi-part objects with materials and transforms
Data Flow:
ZSC File: ├─ Meshes (array of paths to ZMS files) ├─ Materials (array of ZscMaterial) ├─ Effects (array of paths to effect files) └─ Objects (array of ZscObject) ZscObject: └─ Parts (array of ZscObjectPart) ZscObjectPart: ├─ Mesh ID (into meshes array) ├─ Material ID (into materials array) ├─ Transform (position, rotation, scale) ├─ Bone index (optional: bind to skeleton bone) ├─ Dummy index (optional: bind to dummy bone) ├─ Parent index (optional: parent part) └─ Animation path (optional: animation override) ZscMaterial: ├─ Path (texture file) ├─ Alpha enabled, two-sided ├─ Alpha test (threshold) ├─ Z write/test enabled ├─ Blend mode ├─ Specular enabled ├─ Alpha value └─ Glow type and color
ZMO ↔ ZMD Interaction:
Purpose: Drive skeleton bones with animation data
Data Flow:
ZMO File: ├─ FPS (frames per second) ├─ Frame count ├─ Channels (array of (bone_id, channel_type)) └─ Frame events (array of frame numbers) ZmoChannel Types: ├─ Position: Vec3 per frame ├─ Rotation: Quat4 per frame ├─ Scale: f32 per frame ├─ Normal: Vec3 per frame (morph targets) ├─ UV1-UV4: Vec2 per frame (texture animation) ├─ Alpha: f32 per frame └─ Texture: f32 per frame
ZMS ↔ ZMD Interaction:
Purpose: Bind mesh vertices to skeleton bones for skinning
Data Flow:
ZMS File: ├─ Format flags (vertex attributes) ├─ Bone count ├─ Bone mapping (version 5/6 only) ├─ Vertex count ├─ Vertex data (based on flags): │ ├─ Positions (Vec3) │ ├─ Normals (Vec3) │ ├─ Colors (Vec4) │ ├─ Bone weights (Vec4: w1, w2, w3, w4) │ ├─ Bone indices (Vec4: i1, i2, i3, i4) │ ├─ Tangents (Vec3) │ └─ UV1-UV4 (Vec2) ├─ Triangle indices (u16) └─ Material face counts (u16)
Effect System Integration:
Purpose: Attach particle and mesh effects to objects
Data Flow:
CHR File: └─ Effect IDs (array of tuples: (motion_id, file_index)) ZSC File: └─ Effects (array of paths to effect files) ##### Vehicle Assembly Pipeline: **Purpose**: Assemble vehicle models with skeleton and parts **Data Flow**:
Vehicle Item Database:
└─ Vehicle item data
├─ Vehicle type (Cart/CastleGear)
├─ Base motion index
├─ Base avatar motion index
└─ Dummy effect file IDs
Client-Side Rendering Integration Summary:
The Rose Offline client integrates all file types through a hierarchical asset loading system:
- VFS Layer: Unified file access across packed archives and host filesystem
- Asset Server: Asynchronous loading and caching of assets
- ECS Components:
SkinnedMesh: Bone binding data (inverse bind matrices, joint entities)CharacterModel: Character parts and animation handlesNpcModel: NPC-specific dataVehicleModel: Vehicle-specific dataObjectMaterial: Material properties and textures
- Systems: Update bone poses, apply animations, render skinned meshes
Coordinate Transformations Applied in Client:
- All positions:
(x, y, z) → (x, z, -y) / 100.0 - All rotations:
(w, x, y, z) → (x, z, -y, w) - Scale: 100.0 (cm to m conversion)
Blender Import Implications:
- Apply inverse coordinate transformations for Blender’s coordinate system
- Preserve bone hierarchy and parent-child relationships
- Convert bone weights to vertex groups
- Map frame-based animations to Blender actions
- Convert materials to Principled BSDF nodes
- Handle dummy bones as separate bone entities
- Preserve animation events for Blender NLA tracks
1.3 Blender Integration Strategy
Conversion Goals:
- Coordinate System Conversion: Rose Online (cm, Y-up) → Blender (m, Y-up)
- Data Model Mapping: Binary structures → Blender Python API objects
- Hierarchy Preservation: Bone and object relationships maintained
- Material Translation: Rose Online materials → Blender Principled BSDF
- Animation Support: Frame-based animation → Blender NLA tracks
Data Model Mapping:
| Rose Online Concept | Blender Equivalent |
|---|---|
| ZMS Mesh | bpy.types.Mesh |
| ZMD Skeleton | bpy.types.Armature |
| ZMD Bone | bpy.types.Bone |
| ZMO Animation | bpy.types.Action |
| ZSC Object | Collection of mesh objects |
| Material | bpy.types.Material |
| Bone Weights | Vertex Groups |
2. Core Systems
2.1 Virtual Filesystem (VFS) Abstraction Layer
Architecture Overview:
The VFS provides a unified interface for accessing game assets across multiple storage devices (packed archives, host filesystem).
Key Components:
// VFS Device Trait
trait VirtualFilesystemDevice {
fn open_file(&self, path: &VfsPath) -> Result<VfsFile>;
fn exists(&self, path: &VfsPath) -> bool;
}
// VFS Implementation
struct VirtualFilesystem {
devices: Vec<Box<dyn VirtualFilesystemDevice + Send + Sync>>
}
Path Normalization:
All paths are normalized to:
- Forward slashes (
/) - Uppercase
- No leading/trailing whitespace
- No double slashes
Example: 3DDATA\AVATAR\MALE.ZMD → 3DDATA/AVATAR/MALE.ZMD
File Loading Mechanism:
// Generic file loading
vfs.read_file::<ZmsFile, _>("3DDATA/MODELS/CHARACTER.ZMS")?;
// With options
vfs.read_file_with::<ZonFile, _, _>("DATA/ZONES/01.ZON", &options)?;
Caching Strategy:
- Assets are loaded on-demand
- VFS devices are queried in order
- First successful read wins
- No built-in caching (handled by application layer)
2.2 Coordinate System Conversions
Rose Online Coordinate System:
- Units: Centimeters (cm)
- Up Axis: Y-up
- Handedness: Right-handed
- Forward: -Z direction
- Right: +X direction
Blender Coordinate System:
- Units: Meters (m)
- Up Axis: Y-up
- Handedness: Right-handed
- Forward: -Y direction (in view space)
- Right: +X direction
Conversion Formulas:
Position Conversion:
# Rose Online (cm) to Blender (m)
def convert_position(x, y, z):
return (x / 100.0, y / 100.0, z / 100.0)
# For client rendering (observed in model_loader.rs):
# Rose: (x, y, z) → Blender: (x, z, -y) / 100.0
def convert_position_for_client(x, y, z):
return (x / 100.0, z / 100.0, -y / 100.0)
Rotation Conversion:
# Rose Online quaternion (W, X, Y, Z) to Blender (W, X, Y, Z)
# Note: Some files store as WXYZ, others as XYZW
def convert_quaternion(w, x, y, z):
# Blender uses (w, x, y, z)
return (w, x, y, z)
# For client rendering (observed):
# Rose: (x, y, z, w) → Blender: (x, z, -y, w)
def convert_quaternion_for_client(x, y, z, w):
return (x, z, -y, w)
Scale Conversion:
# ZMS mesh data is scaled by 100.0
# ZMD bone positions are in cm
def convert_scale(value):
return value / 100.0
Coordinate System Mappings:
| Rose Online Axis | Blender Axis | Notes |
|---|---|---|
| +X | +X | Same direction |
| +Y | +Z | Client rotates Y to Z |
| +Z | -Y | Client inverts Z to -Y |
2.3 Bone Binding Matrices and Skinning
Bone Hierarchy Structure:
Bones form a tree structure where each bone has a parent index (0 = root).
pub struct ZmdBone {
pub parent: u16, // Parent bone index (0 = root)
pub position: Vec3<f32>, // Position in parent space (cm)
pub rotation: Quat4<f32>, // Rotation in parent space (WXYZ)
}
Bone-to-Vertex Binding System:
Each vertex can be bound to up to 4 bones with weights.
// Vertex bone binding data pub bone_weights: Vec<[f32; 4]>, // Weight for each of 4 bones pub bone_indices: Vec<[u16; 4]>, // Bone indices (mapped to skeleton)
Weight Normalization: The 4 weights should sum to 1.0 for proper skinning.
Skinning Transformation Pipeline:
- Bind Pose: Bone positions at rest (from ZMD)
- Inverse Bind Matrix: Pre-computed for each bone
- Current Pose: Bone transformation from animation (ZMO)
- Skinning Matrix:
CurrentPose × InverseBindMatrix - Vertex Transformation:
Σ (Weight_i × SkinningMatrix_i × VertexPosition)
Bone Pose Matrices and Bone Spaces:
Bone Spaces:
- Local Space: Position/rotation relative to parent
- Object Space: Position/rotation relative to mesh origin
- World Space: Position/rotation in game world
2.4 Animation Track Interpolation
Frame-Based Animation System:
Animations are stored as discrete frames with fixed FPS.
pub struct ZmoFile {
pub fps: usize, // Frames per second (typically 30)
pub num_frames: usize, // Total frame count
pub channels: Vec<(u32, ZmoChannel)>, // (bone_index, channel_data)
pub frame_events: Vec<u16>, // Frame event triggers
pub interpolation_interval_ms: Option<u32>, // For 3ZMO format
}
Channel Types and Data Structures:
| Channel Type | Value | Data Type | Purpose |
|---|---|---|---|
| Empty | 1 | – | No animation data |
| Position | 2 | Vec3 | Bone position |
| Rotation | 4 | Quat4 | Bone rotation (WXYZ) |
| Normal | 8 | Vec3 | Vertex normal |
| Alpha | 16 | f32 | Transparency |
| UV1 | 32 | Vec2 | First UV set |
| UV2 | 64 | Vec2 | Second UV set |
| UV3 | 128 | Vec2 | Third UV set |
| UV4 | 256 | Vec2 | Fourth UV set |
| Texture | 512 | f32 | Texture animation |
| Scale | 1024 | f32 | Scale factor |
Frame Events and Interpolation Intervals:
Frame Events:
- Triggered at specific frames
- Used for sound effects, particle spawns, etc.
- Event IDs: 10, 20-28, 56-57, 66-67 (attack events)
3ZMO Extended Format:
- Includes
interpolation_interval_msfield - Specifies custom interpolation timing
- Allows for variable frame rates
2.5 Material System
Material Properties and Flags:
pub struct ZscMaterial {
pub path: VfsPathBuf, // Material file path
pub is_skin: bool, // Skin/shader type
pub alpha_enabled: bool, // Transparency enabled
pub two_sided: bool, // Double-sided rendering
pub alpha_test: Option<f32>, // Alpha test threshold
pub z_write_enabled: bool, // Depth write
pub z_test_enabled: bool, // Depth test
pub blend_mode: ZscMaterialBlend, // Blending mode
pub specular_enabled: bool, // Specular highlights
pub alpha: f32, // Global alpha
pub glow: Option<ZscMaterialGlow>, // Glow effect
}
Blend Modes and Transparency:
| Blend Mode | Value | Description | Blender Equivalent |
|---|---|---|---|
| Normal | 0 | Standard alpha blending | Alpha Blend |
| Lighten | 1 | Additive blending | Add Mix |
Alpha Test:
- Threshold value (0-1) for pixel discard
- Typical value:
alpha_ref / 256.0 - Pixels below threshold are discarded
Glow Effects and Shader Integration:
pub enum ZscMaterialGlow {
Simple(Vec3<f32>), // Simple glow color
Light(Vec3<f32>), // Light-based glow
Texture(Vec3<f32>), // Texture-based glow
TextureLight(Vec3<f32>), // Texture + light glow
Alpha(Vec3<f32>), // Alpha-based glow
}
Texture Referencing and UV Mapping:
UV Channels:
- ZMS supports up to 4 UV sets (UV1-UV4)
- UV1 is primary texture coordinates
- UV2-UV4 used for multi-texturing or effects
Texture Paths:
- Stored in ZSC material definitions
- Resolved through VFS
- File extensions:
.dds,.tga,.jpg
2.6 Vertex Format Flags and Data Layout
ZmsFormatFlags Bitfield Structure:
bitflags! {
pub struct ZmsFormatFlags: u32 {
const POSITION = (1 << 1); // 0x0002
const NORMAL = (1 << 2); // 0x0004
const COLOR = (1 << 3); // 0x0008
const BONE_WEIGHT = (1 << 4); // 0x0010
const BONE_INDEX = (1 << 5); // 0x0020
const TANGENT = (1 << 6); // 0x0040
const UV1 = (1 << 7); // 0x0080
const UV2 = (1 << 8); // 0x0100
const UV3 = (1 << 9); // 0x0200
const UV4 = (1 << 10); // 0x0400
}
}
Vertex Attribute Variations:
Version 5/6 (Legacy):
- Each vertex preceded by 32-bit vertex ID
- Bone indices stored as 32-bit, mapped to 16-bit
- Position scaled by 100.0 (cm to m conversion)
Version 7/8 (Modern):
- No vertex IDs
- Bone indices stored as 16-bit directly
- No scaling (already in correct units)
Position, Normal, Color, Bone Weights:
pub position: Vec<[f32; 3]>, // X, Y, Z position pub normal: Vec<[f32; 3]>, // X, Y, Z normal vector pub color: Vec<[f32; 4]>, // R, G, B, A vertex color pub bone_weights: Vec<[f32; 4]>, // Weight for 4 bones pub bone_indices: Vec<[u16; 4]>, // Bone index mapping
UV Coordinate Sets (UV1-4):
pub uv1: Vec<[f32; 2]>, // Primary UV coordinates pub uv2: Vec<[f32; 2]>, // Secondary UV coordinates pub uv3: Vec<[f32; 2]>, // Tertiary UV coordinates pub uv4: Vec<[f32; 2]>, // Quaternary UV coordinates
Tangent and Binormal Data:
pub tangent: Vec<[f32; 3]>, // Tangent vector for normal mapping
Note: Binormals can be computed from tangent and normal:
binormal = cross(normal, tangent)
Material Face Mapping:
pub material_num_faces: Vec<u16>, // Face count per material
Faces are grouped by material index for efficient rendering.
3. File Format Specifications
3.1 ZMS Mesh Files (.zms)
File Header and Magic Number:
Offset Type Description
0x00 char[7] Magic string ("ZMS0005", "ZMS0006", "ZMS0007", "ZMS0008")
Version Detection:
| Magic String | Version | Notes |
|---|---|---|
| ZMS0005 | 5 | Legacy format |
| ZMS0006 | 6 | Legacy format with material mapping |
| ZMS0007 | 7 | Modern format |
| ZMS0008 | 8 | Modern format with strip indices |
Binary Layout and Data Structures:
Version 5/6 Layout:
Offset Type Description 0x00 char[7] Magic string 0x07 u32 Format flags (ZmsFormatFlags) 0x0B Vec3 Bounding box minimum 0x17 Vec3 Bounding box maximum 0x23 u32 Bone count 0x27 u32[] Bone mapping (bone_count × 4 bytes) 0x?? u32 Vertex count 0x?? Vertex[] Vertex data (based on format flags) 0x?? u32 Triangle count 0x?? u16[] Index data (triangle_count × 3) 0x?? u32 Material ID count (version 6+) 0x?? u16[] Material face counts
Version 7/8 Layout:
Offset Type Description 0x00 char[7] Magic string 0x07 u32 Format flags (ZmsFormatFlags) 0x0B Vec3 Bounding box minimum 0x17 Vec3 Bounding box maximum 0x23 u16 Bone count 0x25 u16[] Bone indices (bone_count × 2 bytes) 0x?? u16 Vertex count 0x?? Vertex[] Vertex data (based on format flags) 0x?? u16 Triangle count 0x?? u16[] Index data (triangle_count × 3) 0x?? u16 Material ID count 0x?? u16[] Material face counts 0x?? u16 Strip index count (version 8) 0x?? u16[] Strip indices 0x?? u16 Pool type (version 8)
Vertex Data Arrays:
Vertex Data Structure (per vertex):
struct Vertex {
// Present if POSITION flag set
float position[3]; // X, Y, Z
// Present if NORMAL flag set
float normal[3]; // X, Y, Z
// Present if COLOR flag set
float color[4]; // R, G, B, A
// Present if BONE_WEIGHT and BONE_INDEX flags set
float bone_weight[4]; // W1, W2, W3, W4
uint32 bone_index[4]; // I1, I2, I3, I4 (v5/6)
uint16 bone_index[4]; // I1, I2, I3, I4 (v7/8)
// Present if TANGENT flag set
float tangent[3]; // X, Y, Z
// Present if UV1 flag set
float uv1[2]; // U, V
// Present if UV2 flag set
float uv2[2]; // U, V
// Present if UV3 flag set
float uv3[2]; // U, V
// Present if UV4 flag set
float uv4[2]; // U, V
};
Version 5/6: Each vertex preceded by 32-bit vertex ID.
Material Face Mapping:
pub material_num_faces: Vec<u16>
- Each entry specifies number of triangles for a material
- Faces are grouped contiguously by material
- Used for efficient multi-material rendering
Bone Index Mapping:
Version 5/6:
let bone_count = reader.read_u32()?;
let mut bones = Vec::new();
for _ in 0..bone_count {
let _ = reader.read_u32()?; // Unused
bones.push(reader.read_u32()? as u16);
}
Bone indices in vertices are 32-bit, mapped through this table to 16-bit.
Version 7/8:
let bone_count = reader.read_u16()?;
let mut bones = Vec::new();
for _ in 0..bone_count {
bones.push(reader.read_u16()?);
}
Bone indices in vertices are already 16-bit, no mapping needed.
Vertex Attribute Flags:
See Section 2.6 for flag definitions.
Scaling Factors:
Version 5/6: Position data is scaled by 100.0 (cm to m conversion).
for [x, y, z] in position.iter_mut() {
*x /= 100.0;
*y /= 100.0;
*z /= 100.0;
}
Version 7/8: No scaling applied.
Parsing Algorithms and Edge Cases:
- Invalid Bone Index: Throw error if bone index >= bone_count
- Missing Attributes: Only read attributes present in format flags
- Empty Mesh: Handle vertex_count = 0 gracefully
- Material Count: May be 0 (single material mesh)
3.2 ZMD Skeleton Files (.zmd)
File Header and Magic Number:
Offset Type Description
0x00 char[7] Magic string ("ZMD0002" or "ZMD0003")
Version Detection:
| Magic String | Version | Notes |
|---|---|---|
| ZMD0002 | 2 | Legacy format |
| ZMD0003 | 3 | Modern format with dummy bone rotation |
Binary Layout and Data Structures:
Offset Type Description 0x00 char[7] Magic string 0x07 u32 Bone count 0x0B Bone[] Bone data 0x?? u32 Dummy bone count 0x?? DummyBone[] Dummy bone data
Bone Count and Hierarchy:
let bone_count = reader.read_u32()? as usize; let mut bones = Vec::with_capacity(bone_count);
Bone Data Structure:
struct ZmdBone {
uint32 parent; // Parent bone index (0 = root)
char name[]; // Null-terminated bone name
float position[3]; // X, Y, Z (cm)
float rotation[4]; // W, X, Y, Z (quaternion)
};
Bone Name and ID Mapping:
Bone names are stored but not typically used in rendering. Bone ID is the array index.
Dummy Bone Support:
Dummy bones are attachment points for weapons, effects, etc.
struct DummyBone {
char name[]; // Null-terminated name
uint32 parent; // Parent bone index
float position[3]; // X, Y, Z (cm)
float rotation[4]; // W, X, Y, Z (version 3 only)
};
Version 2: Dummy bones have identity rotation (0, 0, 0, 1).
Pose Transformation Matrices:
Not stored in ZMD file. Computed at runtime from bone hierarchy.
Bone-to-Object Binding:
Bones are in local space (relative to parent). Object space matrices computed by traversing hierarchy.
Scaling from cm to m:
Bone positions are in centimeters. Scale by 0.01 for meters.
3.3 ZMO Animation Files (.zmo)
File Header and Magic Number:
Offset Type Description
0x00 char[7] Magic string ("ZMO0002")
Channel Definitions and Types:
struct ChannelDef {
uint32 channel_type; // 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024
uint32 bone_index; // Target bone index
};
Frame Data Structures:
Frame data is stored per channel, not per frame.
Channel 0: Frame 0: value Frame 1: value ... Frame N: value Channel 1: Frame 0: value ...
Extended Format Support (EZMO/3ZMO):
Extended data at end of file:
End-4: char[4] Magic ("EZMO" or "3ZMO")
End-8: u32 Extended data offset
Offset: u16 Frame event count
Offset+2: u16[] Frame events (event_count)
...
3ZMO Only:
- After frame events:
u32 interpolation_interval_ms
Frame Events and Interpolation Intervals:
Frame Events:
pub frame_events: Vec<u16>
Triggered at specific frames. Attack events: 10, 20-28, 56-57, 66-67.
Interpolation Interval:
pub interpolation_interval_ms: Option<u32>
Custom interpolation timing for 3ZMO format.
Channel Interpolation Methods:
- Position: Linear interpolation
- Rotation: Quaternion Slerp
- UV/Scale/Alpha: Linear interpolation
3.4 ZON Zone Files (.zon)
Block-Based File Structure:
Offset Type Description 0x00 u32 Block count 0x04 BlockHeader[] Block headers (count × 8 bytes) ... Block Data...
Block Header:
struct BlockHeader {
uint32 block_type; // Block type enum
uint32 block_offset; // Offset to block data
};
Block Type Enumeration:
enum BlockType {
ZoneInfo = 0,
EventPositions = 1,
Textures = 2,
Tiles = 3,
Economy = 4,
};
ZoneInfo Block – Grid Configuration:
Offset Type Description 0x00 u32[3] Unknown (12 bytes) 0x0C u32 Grid per patch 0x10 f32 Grid size 0x14 u32[2] Unknown (8 bytes)
EventPositions Block – Spawn Points:
struct EventPosition {
float position[3]; // X, Y, Z
uint8 name_len; // Name length
char name[name_len]; // Event name
};
Textures Block – Texture References:
struct TextureRef {
uint8 path_len; // Path length
char path[path_len]; // Texture path
};
Tiles Block – Tile Composition:
struct ZonTile {
uint32 layer1; // Primary layer texture index
uint32 layer2; // Secondary layer texture index
uint32 offset1; // Primary layer offset
uint32 offset2; // Secondary layer offset
uint32 blend; // Blend flag (0/1)
uint32 rotation; // Rotation enum
uint32 padding; // 4 bytes padding
};
ZonTile Structure and Layer Composition:
Rotation Enum:
pub enum ZonTileRotation {
Unknown = 0,
None = 1,
FlipHorizontal = 2,
FlipVertical = 3,
Flip = 4,
Clockwise90 = 5,
CounterClockwise90 = 6,
};
Tile Offset and Blend Modes:
- Offset: UV offset for texture sampling
- Blend: Enable/disable layer blending
- Rotation: Texture rotation transformation
Spatial Partitioning and Grid System:
Zone is divided into a grid of tiles. Each tile references textures from the Textures block.
3.5 ZSC Object Composition Files (.zsc)
Material List Structure:
struct MaterialList {
uint16 material_count;
MaterialEntry materials[material_count];
};
ZscMaterial Properties:
struct ZscMaterial {
char path[]; // Null-terminated path
uint16 is_skin; // 0/1
uint16 alpha_enabled; // 0/1
uint16 two_sided; // 0/1
uint16 alpha_test_enabled; // 0/1
uint16 alpha_ref; // Alpha reference (0-255)
uint16 z_test_enabled; // 0/1
uint16 z_write_enabled; // 0/1
uint16 blend_mode; // 0=Normal, 1=Lighten
uint16 specular_enabled; // 0/1
float alpha; // Global alpha
uint16 glow_type; // 0=None, 2=Simple, 3=Light, 4=TextureLight, 5=Alpha
float glow_color[3]; // R, G, B
};
Object Part Definitions:
struct ObjectPart {
uint16 mesh_id; // Mesh index
uint16 material_id; // Material index
// Properties follow (variable length)
};
Mesh and Material References:
- Mesh List: Array of mesh file paths
- Material List: Array of material definitions
- Effect List: Array of effect file paths
Part Transforms (Position, Rotation, Scale):
struct Transform {
float position[3]; // X, Y, Z
float rotation[4]; // W, X, Y, Z (quaternion)
float scale[3]; // X, Y, Z
};
Bone Index and Dummy Bone Binding:
struct BoneBinding {
uint16 bone_index; // Regular bone index
uint16 dummy_index; // Dummy bone index
};
Parent-Child Relationships:
struct ParentRef {
uint16 parent_id; // Parent part index (0 = none)
};
Parent IDs are 1-indexed. Subtract 1 to get array index.
Collision Shape and Flags:
struct CollisionData {
uint16 flags; // Collision flags
// Bits 0-2: Shape (0=None, 1=Sphere, 2=AABB, 3=OBB, 4=Polygon)
// Bit 3: NOT_MOVEABLE
// Bit 4: NOT_PICKABLE
// Bit 5: HEIGHT_ONLY
// Bit 6: NOT_CAMERA_COLLISION
// Bit 7: PASSTHROUGH
};
Animation Path References:
struct AnimationRef {
uint8 property_id; // Must be 30
uint8 size; // Path length
char path[size]; // Animation path
};
Object Effects and Properties:
struct ObjectEffect {
uint16 effect_id; // Effect index
uint16 effect_type; // 0=Normal, 1=DayNight, 2=LightContainer
Transform transform; // Position, rotation, scale
uint16 parent; // Parent part index
};
3.6 IFO Zone Object Files (.ifo)
Object Type Enumeration:
enum BlockType {
DeprecatedMapInfo = 0,
DecoObject = 1,
Npc = 2,
CnstObject = 3,
SoundObject = 4,
EffectObject = 5,
AnimatedObject = 6,
DeprecatedWater = 7,
MonsterSpawn = 8,
WaterPlanes = 9,
Warp = 10,
CollisionObject = 11,
EventObject = 12,
};
Object Placement Data:
struct IfoObject {
uint8 name_len; // Name length
char name[name_len]; // Object name
uint16 warp_id; // Warp ID
uint16 event_id; // Event ID
uint32 object_type; // Object type enum
uint32 object_id; // Object ID
uint32 minimap_x; // Minimap X position
uint32 minimap_y; // Minimap Y position
float rotation[4]; // X, Y, Z, W (quaternion)
float position[3]; // X, Y, Z
float scale[3]; // X, Y, Z
};
Minimap Position and ID:
Used for minimap display and object identification.
Warp and Event References:
- Warp ID: Link to warp destination
- Event ID: Link to event script
Spawn Point Definitions:
struct MonsterSpawnPoint {
IfoObject object; // Spawn position
char spawn_name[]; // Spawn name
uint32 basic_count; // Basic spawn count
BasicSpawn basic_spawns[basic_count];
uint32 tactic_count; // Tactic spawn count
TacticSpawn tactic_spawns[tactic_count];
uint32 interval; // Spawn interval (seconds)
uint32 limit_count; // Max monsters
uint32 range; // Spawn range
uint32 tactic_points; // Tactic points
};
struct BasicSpawn {
char monster_name[];
uint32 monster_id;
uint32 count;
};
Object-Specific Properties:
NPC:
struct IfoNpc {
IfoObject object;
uint32 ai_id; // AI behavior ID
char quest_file_name[]; // Quest file
};
Effect Object:
struct IfoEffect {
IfoObject object;
char effect_path[]; // Effect file path
};
Sound Object:
struct IfoSound {
IfoObject object;
char sound_path[]; // Sound file path
uint32 range; // Sound range
uint32 interval; // Play interval (seconds)
};
Monster Spawn Configuration:
See MonsterSpawnPoint above.
NPC and Character Placement:
See IfoNpc structure above.
Animated Object Definitions:
Objects with animation references. Uses standard IfoObject structure.
Collision Object Properties:
Objects that participate in physics collision.
Deco and CNST Object Types:
- Deco: Decorative objects (no collision)
- Cnst: Construction objects (with collision)
Effect and Sound Object Types:
See IfoEffect and IfoSound structures above.
3.7 HIM Heightmap Files (.him)
File Header and Magic Number:
No magic string. Direct data layout.
Offset Type Description 0x00 u32 Width 0x04 u32 Height 0x08 u32[2] Unknown (8 bytes) 0x10 f32[] Height data (width × height)
Heightmap Grid Structure:
2D grid of floating-point height values.
Height Data Encoding:
32-bit float per grid cell.
Grid Size and Resolution:
Width and height in grid cells.
Coordinate Mapping:
Grid coordinates map directly to world coordinates (with scaling).
Heightmap Scaling Factors:
Height values are in centimeters. Scale by 0.01 for meters.
3.8 TIL Tile Index Files (.til)
Tile Index Structure:
Offset Type Description 0x00 u32 Width 0x04 u32 Height 0x08 Tile[] Tile data (width × height)
Tile Entry:
Offset Type Description 0x00 u8[3] Unknown (3 bytes) 0x03 u32 Tile index
Texture Mapping:
Tile indices reference textures from ZON file’s Textures block.
Tile Coordinate System:
2D grid matching HIM heightmap resolution.
Tile Size and Resolution:
Each tile covers a fixed area of terrain.
Texture Atlas Organization:
Textures are individual files, not a single atlas.
Tile-to-Texture Mapping Algorithms:
def get_tile_texture_index(til_file, zon_file, x, y):
tile_index = til_file.get_clamped(x, y)
texture_path = zon_file.tile_textures[tile_index]
return texture_path
3.9 CHR Character Files (.chr)
Character Definition Structure:
struct ChrFile {
uint16 skeleton_count;
char skeleton_files[skeleton_count][];
uint16 motion_count;
char motion_files[motion_count][];
uint16 effect_count;
char effect_files[effect_count][];
uint16 character_count;
CharacterData characters[character_count];
};
NPC Data Organization:
Characters are indexed by NPC ID.
Skeleton, Motion, and Effect References:
- Skeleton: ZMD file index
- Motion: ZMO file index (per action)
- Effect: Effect file index (per action)
Character Type Classification:
NPCs, monsters, and other entities.
Asset Path Resolution:
Paths are resolved through VFS.
Character-Specific Properties:
struct NpcModelData {
uint16 skeleton_index; // Skeleton file index
char name[]; // NPC name
uint16 mesh_count; // Number of mesh parts
uint16 model_ids[mesh_count]; // ZSC model IDs
uint16 motion_count; // Number of motions
MotionRef motions[motion_count]; // (motion_id, file_index)
uint16 effect_count; // Number of effects
EffectRef effects[effect_count]; // (motion_id, file_index)
};
struct MotionRef {
uint16 motion_id; // Action ID
uint16 file_index; // Motion file index
};
struct EffectRef {
uint16 motion_id; // Trigger motion ID
uint16 file_index; // Effect file index
};
6.3 Edge Cases and Format Variations
Version Differences and Compatibility:
- ZMS v5/6: Vertex IDs, bone index mapping, scaling
- ZMS v7/8: No vertex IDs, direct bone indices, no scaling
- ZMD v2: Dummy bones have identity rotation
- ZMD v3: Dummy bones have explicit rotation
Missing or Optional Data Handling:
- Use default values for missing attributes
- Skip empty arrays gracefully
- Validate data bounds before access
Invalid or Corrupted File Detection:
- Check magic strings
- Validate version numbers
- Verify data bounds
- Check for read errors
Format Extensions and Customizations:
- EZMO/3ZMO extended animation format
- Custom collision shapes in ZSC
- Additional property IDs in ZSC
7. Quick Reference
7.1 File Format Summary Table
| Format | Magic | Versions | Key Features | Complexity |
|---|---|---|---|---|
| ZMS | ZMS000X | 5-8 | Mesh, skinning, materials | High |
| ZMD | ZMD000X | 2-3 | Skeleton, dummy bones | Medium |
| ZMO | ZMO0002 | 2 | Animation, frame events | Medium |
| ZON | None | – | Zone, tiles, events | High |
| ZSC | None | – | Objects, materials, effects | High |
| IFO | None | – | Object placement, spawns | High |
| HIM | None | – | Heightmap | Low |
| TIL | None | – | Tile indices | Low |
| CHR | None | – | Character definitions | Medium |
7.2 Key Constants and Conversions
Scaling Factors:
MESH_SCALE = 100.0 # ZMS v5/6 position scaling CM_TO_M = 0.01 # Centimeters to meters
Binary Data Type Sizes:
| Type | Size |
|---|---|
| u8 | 1 byte |
| u16 | 2 bytes |
| u32 | 4 bytes |
| f32 | 4 bytes |
| Vec3 | 12 bytes |
| Quat4 | 16 bytes |
7.4 Troubleshooting Guide
Common Import Errors:
| Error | Cause | Solution |
|---|---|---|
| Invalid magic | Wrong file type | Check file extension |
| Version mismatch | Unsupported version | Update parser |
| Bone index out of range | Corrupted data | Validate indices |
| Missing vertex data | Format flags wrong | Check flags |
Asset Loading Workflow:
participant App
participant VFS
participant ZON
participant ZMS
participant ZMD
participant ZMO
App->>VFS: Load Zone
VFS->>ZON: Read ZON file
ZON-->>VFS: Zone data
App->>VFS: Load Mesh
VFS->>ZMS: Read ZMS file
ZMS-->>VFS: Mesh data
App->>VFS: Load Skeleton
VFS->>ZMD: Read ZMD file
ZMD-->>VFS: Skeleton data
App->>VFS: Load Animation
VFS->>ZMO: Read ZMO file
ZMO-->>VFS: Animation data
Bone Hierarchy Structure:
Root[Bone 0: Root] --> Bone1[Bone 1]
Root --> Bone2[Bone 2]
Bone1 --> Bone3[Bone 3]
Bone1 --> Bone4[Bone 4]
Bone2 --> Bone5[Bone 5]
Animation Interpolation Pipeline:
Frame1[Frame 1] -->|t=0.0| Interp[Interpolation] Frame2[Frame 2] -->|t=1.0| Interp Interp -->|Linear/Slerp| Result[Animated Pose]
#### Zone Composition System:
ZON[ZON File] --> Tiles[Tiles Block] ZON --> Textures[Textures Block] ZON --> Events[EventPositions Block] Tiles --> HIM[HIM Heightmap] Tiles --> TIL[TIL Tile Index] Textures --> TextureFiles[.DDS/.TGA Files] Events --> Spawns[Spawn Points]
“`
8.3 Glossary
Technical Terminology:
- VFS: Virtual Filesystem – Abstracted file access layer
- ECS: Entity Component System – Architecture pattern
- WGSL: WebGPU Shading Language – Shader language
- Skinned Mesh: Mesh with bone-based vertex deformation
- Inverse Bind Matrix: Matrix transforming from bind pose to bone space
Rose Online-Specific Terms:
- ZMS: Z-Model-Structure – Mesh file format
- ZMD: Z-Model-Definition – Skeleton file format
- ZMO: Z-Motion-Object – Animation file format
- ZON: Z-One – Zone file format
- ZSC: Z-Structure-Composition – Object composition format
- IFO: Information File Object – Zone object placement format
- HIM: Height Map – Terrain elevation format
- TIL: Tile – Terrain tile index format
- CHR: Character – Character definition format
Blender-Specific Terms:
- Armature: Skeleton/bone hierarchy
- Action: Animation data
- Vertex Group: Bone weight assignment
- Material: Surface properties
- Mesh: Geometry data
- NLA: Non-Linear Animation – Animation layering system