Implement OXP export and import #110
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Objective
Implement complete, native Rust import and export of OXP (Onrez Package) object backups. An OXP file is a self-contained object-backup document containing linksets, prim properties, optional object inventory, and optional embedded assets.
The implementation must be usable without any external reference checkout, viewer process, .NET runtime, cache directory, or network service. This issue contains the format contract required for implementation.
Crate naming and workspace structure
Add two native MetaCrate crates:
metacrate-oxp: the format implementation, typed document model, validation, bounded decoder, and encoder.metacrate: the native umbrella crate, analogous to the existing compatibility umbrella, re-exportingmetacrate_oxpasoxp.The
libremetaverse-*prefix is reserved for compatibility-layer crates. New native MetaCrate functionality uses themetacrate-*prefix. Do not rename existing crates in this issue, and do not put the OXP implementation into the compatibility umbrella.Both crates must follow the workspace MSRV/lints and build on Linux, Windows, and macOS without platform-specific APIs.
Scope
metacrate-oxpmust:std::io::Read;std::io::Write;std::fs;OxpDocumentmodel for manifest metadata, linksets, prims, inventory records, and embedded assets;This crate owns the file format only. Rezzing objects, uploading assets, enforcing grid permissions, converting JPEG 2000, parsing mesh payloads, and presenting UI are outside this issue. Assets are returned as typed metadata plus opaque bytes; no automatic extraction to disk is required.
File framing
An OXP file has no outer magic number, filename table, or archive directory.
.oxpfile.This is zlib, not gzip and not raw DEFLATE. The established writer uses maximum zlib compression, but interoperability is semantic: valid encoders need not produce byte-identical compressed output.
The decoder must validate the zlib wrapper/checksum, enforce compressed and decompressed-size limits while streaming, parse exactly one root value, and reject truncated or trailing decompressed data.
Binary LLSD encoding
Every value begins with a one-byte ASCII tag. All counts and numeric payloads are big-endian/network order.
!0/1i+ 4-byte two's-complement i32r+ 8-byte IEEE-754 f64u+ 16 RFC 4122 bytes in network orders+ u32 byte length + bytesd+ 8-byte IEEE-754 seconds since Unix epochl+ u32 UTF-8 byte length + bytesb+ u32 byte length + bytes[+ u32 element count + encoded values +]{+ u32 pair count + pairs +}Each map key is encoded as
k+ u32 UTF-8 byte length + key bytes, immediately followed by its encoded value. Readers may accept the historical quoted notation-key form, but writers must emitkkeys. Counts describe contained elements/pairs and the closing delimiter is still mandatory.Reject negative/overflowing effective lengths, invalid UTF-8 in keys/strings/URIs, invalid UUID sizes, missing delimiters, count/delimiter disagreement, excessive nesting/node counts, and non-finite values where a typed OXP field requires a finite number.
Root document schema
The root is an LLSD map with these keys:
format_version2. Import historical supported versions up to 2; reject missing, non-positive, and greater values as unsupported.clientcreation_dateYYYY-MM-DD. This is a string, not LLSD Date.authorgridlinksetprimlinkset.assetinventorycontent.lo_feature_version1.lo_asset_mask0x01contents,0x02textures,0x04materials,0x08meshes.mesh_assetRoot map ordering is not significant. Writers should use stable ordering for reproducible uncompressed LLSD. Unknown root keys must be retained.
The asset mask is descriptive, not proof that every referenced asset exists: permissions, unavailable cache data, or download failures can produce partial packages.
Prim schema
Each
primkey is the original prim UUID in canonical text form. A prim map contains:Core placement and shape
parent: UUID for a child prim; omitted for linkset roots.attachment_point: i32 attachment-point identifier, roots only when applicable.position: three f64 values[x, y, z]. Child positions are root-relative; root positions are region/world placement metadata.scale: three f64 values.rotation: four f64 values[x, y, z, w].flags: LLSD binary containing exactly one big-endian u32.material: i32 legacy prim material code.clickaction: optional i32.volume: map with:path:curvei32;begin,end,scale_x,scale_y,shear_x,shear_y,twist,twist_begin,radius_offset,taper_x,taper_y,revolutions, andskewas f64.profile:curvei32;begin,end, andhollowas f64.A permissions-substituted default prim may contain only placement plus
flags,volume, andmaterial; do not require optional visual/metadata fields to exist.Faces and legacy materials
texture: array indexed by face. Each entry contains:imageidUUID;colorsfour-f64 RGBA array;scales,scalet,offsets,offsett,imagerot, and optionalglowas f64;bump,fullbright, andmedia_flagsas i32;media_datamap;gltf_overridestring containing a glTF material override JSON document;texgeni32:0default or1planar.materials: optional array of legacy per-face material maps. Enhanced exports pad this array with default/null material records so its index matches the face index. A material map uses:NormMap,SpecMap;NormOffsetX,NormOffsetY,NormRepeatX,NormRepeatY,NormRotation,SpecOffsetX,SpecOffsetY,SpecRepeatX,SpecRepeatY,SpecRotation; divide by10000.0for the represented value;SpecColorfour-value color array;SpecExp,EnvIntensity,DiffuseAlphaMode, andAlphaMaskCutoff.Preserve array order exactly. Do not compact padded
materials.Optional prim parameter blocks
sculpt:{ texture: UUID, type: i32 }for sculpted prims.mesh: enhanced mesh-placeholder reference with the same shape, normally type5;textureis the mesh UUID and the matching bytes are inmesh_asset.flexible:air_friction,gravity,tension,wind_sensitivityf64;simulate_lodi32;user_forcethree-f64 array.light:colorfour-f64 linear RGBA, plus f64radius,falloff, andcutoff.light_texture:textureUUID andparamsthree-f64 array.extended_mesh:flagsi32; bit0x01means animated mesh.render_material:{ entries: [{ te_idx: i32, id: UUID }, ...] }, mapping face indices 0..255 to PBR material asset UUIDs.reflection_probe: f64ambiance, f64clip_distance, i32flags; bits are0x01box volume,0x02dynamic content, and0x04mirror.particle: a map containing integer 0/1 flag keysLL_PART_INTERP_COLOR_MASK,LL_PART_INTERP_SCALE_MASK,LL_PART_BOUNCE_MASK,LL_PART_WIND_MASK,LL_PART_FOLLOW_SRC_MASK,LL_PART_FOLLOW_VELOCITY_MASK,LL_PART_TARGET_POS_MASK,LL_PART_TARGET_LINEAR_MASK,LL_PART_EMISSIVE_MASK,LL_PART_BEAM_MASK,LL_PART_SRC_PATTERN_DROP,LL_PART_SRC_PATTERN_EXPLODE,LL_PART_SRC_PATTERN_ANGLE,LL_PART_SRC_PATTERN_ANGLE_CONE, andLL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY; f64ParticleMaxAge,SourceMaxAge,SourceStartAge,InnerAngle,OuterAngle,BurstRate,BurstSpeedMin,BurstSpeedMax,BurstRadius; i32BurstPartCount; arraysStartColor/EndColor(four f64),StartScale/EndScale(two f64),AngularVelocity(three f64); and UUID stringsPartImageID,TargetId.Physics, identity, permissions, and contents
ExtraPhysics:PhysicsShapeTypei32 and f64Density,Friction,Restitution,GravityMultiplier.name,description,touch_name,sit_name: strings.creation_date: LLSD binary containing exactly one big-endian u64.creator_id,owner_id,group_id,last_owner_idUUIDs and corresponding*_nameLLSD maps. These may be absent after anonymization.base_mask,owner_mask,group_mask,everyone_mask,next_owner_mask: LLSD binary containing exactly one big-endian u32 each.sale_info:{ sale_type: string, sale_price: i32 }.content: ordered array of inventory-item UUIDs whose records are under rootinventory.Unknown prim keys and unknown keys inside recognized parameter maps must survive a round trip.
Embedded assets
Both
assetandmesh_assetmap canonical UUID strings to:Known
typevalues includetexture,material,mesh, and ordinary inventory asset types such as scripts, notecards, sounds, animations, gestures, clothing, and body parts. Preserve unknown type strings.Payload rules:
texture: original JPEG 2000/J2C bytes. System/default textures may be referenced but intentionally not embedded.material: a second, uncompressed binary-LLSD document indatawith root{ version: "1.1", type: "GLTF 2.0", data: <glTF JSON string> }. Readers should also tolerate material asset version1.0.mesh: raw simulator mesh/SLM asset bytes with the viewer-cache-only 12-byte preamble removed. OXP treats this payload as opaque and must not add/remove a preamble.Do not derive filenames from untrusted metadata or write assets during decode. UUID-keyed extraction can be a later, explicit consumer concern.
Inventory schema
The root
inventorymap is keyed by inventory item UUID. Each record contains:item_id,parent_id,asset_id: UUID;name,desc,type,inv_type: string;flags,created_at: i32;sale_info:sale_typestring andsale_pricei32;permissions: UUIDcreator_id,owner_id,last_owner_id,group_id; booleanis_owner_group; i32base_mask,owner_mask,group_mask,everyone_mask,next_owner_mask.Anonymized packages may zero identity UUIDs and set
created_atto zero. A blank notecard may have its null asset ID replaced by a generated asset UUID whose asset payload is an empty serialized notecard. Missing assets must be represented as missing, not fabricated.Import validation and compatibility
linksetmust resolve to exactly oneprim.parentreferences, when present, must resolve inside that linkset and must not create cycles.lo_feature_versionmeans standard OXP, but readers must still recognize enhanced fields by presence because early enhanced files were unlabelled.lo_feature_version >= 1, decode the known version-1 fields and preserve unknown future fields. Do not interpretlo_asset_maskas a completeness guarantee.format_version; do not silently guess another container format.Export behavior
format_version = 2.lo_*,mesh, ormesh_assetfields when no enhanced features are requested.lo_feature_version = 1, the accuratelo_asset_mask, face-aligned padded legacy materials,texgen, enhanced prim blocks, complete untruncated texture payloads, and mesh bytes undermesh_asset.Minimal public API
Keep the API small and conventional:
OxpDocumentplus typed child records;OxpLimitswith safe defaults for compressed bytes, decompressed bytes, nesting depth, total LLSD nodes, collection lengths, and total embedded asset bytes;OxpErrordistinguishing I/O, zlib, binary LLSD, unsupported version, limit exceeded, invalid UUID/type, and structural-reference failures;from_reader/from_bytesandto_writer/to_vec;Do not add async APIs, plugin traits, factories, viewer-specific callbacks, asset conversion services, or a second archive abstraction in this issue.
Tests
Add deterministic, offline tests that cover:
lo_asset_mask = 0x0f, mesh placeholder plus opaquemesh_asset, animated-mesh flag, face-aligned materials, texgen, light texture, PBR material plus its nested binary LLSD/glTF JSON, glTF override, and reflection probe flags.Check in small repository-owned fixtures or construct them directly from the byte grammar above. Tests and implementation must not call another program, access sibling repositories, use a viewer cache, or require a network connection.
Documentation
Document in
metacrate-oxpcrate docs:metacrate::oxpre-export.Update the workspace README crate table and naming explanation:
libremetaverse-*is compatibility-facing, while native functionality ismetacrate-*and is collected by themetacrateumbrella.Validation gates
The issue is complete only when all relevant workspace gates and these focused gates pass:
Also run the focused crate tests on Linux, Windows, and macOS. No platform may use a different on-disk representation.
Definition of done
A consumer can import standard and enhanced OXP bytes into a validated, typed native Rust document and export that document back to an interoperable
.oxpfile throughmetacrate::oxp. Linkset/face ordering, UUID references, unknown compatible fields, nested material data, and all embedded asset bytes survive round trips; malformed or oversized untrusted inputs fail with typed errors; and implementation/testing requires only this repository.