Add metacrate-oxp extract/create/list CLI #111
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
Add a cross-platform
metacrate-oxpcommand-line tool with exactly three subcommands:This issue depends on #110. Implement the binary target in the same
metacrate-oxpCargo package as the OXP library; do not create another parser, archive abstraction, or CLI crate. All OXP decoding, validation, limits, and encoding must call the library from #110.Path behavior
extract /path/house.oxpcreates/path/house/.create /path/house/creates/path/house.oxp..oxpextension is removed.extractandcreatemust refuse to overwrite an existing target file or directory.std::path/std::fs; do not use platform-specific APIs.Extracted directory format
Every extracted folder contains:
Asset filenames use the canonical lowercase hyphenated UUID from the OXP asset-map key. The original asset name and description are metadata only and must never be used as a path.
manifest.jsonis pretty-printed UTF-8 JSON with a final newline and this versioned top-level shape:Requirements:
documentcontains every non-payload part ofOxpDocument: container/client metadata, feature version and asset mask, ordered linksets, all prim data, inventory, and all unknown compatible fields retained by #110.assetscontains one record for every entry in both rootassetandmesh_assetmaps.source_mapis exactly"asset"or"mesh_asset"; this is required to recreate the same root map.datavalue with itsfilereference. Retainname,description, declaredtype, and unknown asset-entry fields..,.., empty names, non-canonical UUID stems, and extensions inconsistent with the mapping below.manifest.json.assetsarray bysource_mapthen UUID and serialize maps deterministically where ordering is not semantically significant. Preserve linkset, prim-face, material, render-material, content, and all other semantic array order.source_fileis informational.createderives its output name from the folder, not from this field.Do not emit a second metadata file or a copy of the compressed OXP.
Asset extension mapping
Choose the extension from the OXP asset record's declared type string.
mesh_assetentries and declaredmeshassets always use.asset.meshor anymesh_assetentry.assettexture.j2csound.ogganimatn.animatnnotecard.txtlsltext,script.lsllslbyte.lslblandmark.landmarkcallcard.callingcardclothing.clothingbodypart.bodypartgesture.gesturetxtr_tga,img_tga.tgasnd_wav.wavjpeg.jpgmaterial.materialgltf.gltfglbin.glbsettings.settingssimstate.simstatelink.linklink_f.linkfoldercategory.categorywidget.widgetperson.personobject.objectinvalid,-1, reserved or unknown strings.binThe established object exporter normally skips embedded
objectandnoneinventory assets, stores meshes undermesh_asset, and most commonly embeds textures, sounds, landmarks, clothing/body parts, notecards, LSL source, animations, gestures, settings, and materials. The CLI must nevertheless extract every asset record actually present, including legacy, custom, reserved, and unknown types.Extensions describe the payload but never authorize transcoding.
extractwrites the exact OXPdatabytes;createreads them unchanged. In particular:.materialpayloads;.assetmesh files.If the same UUID appears in both asset maps, one UUID filename may be shared only when declared type, chosen extension, and payload bytes are identical. Otherwise extraction must fail instead of overwriting one payload.
extractextractmust:manifest.jsonafter all asset files succeed.Missing referenced assets are valid OXP diagnostics and do not create placeholder files. Only entries actually carrying embedded
dataappear inassets.createcreatemust:manifest.jsonin the supplied folder.source_map, declared type, expected filename, and safe relative basename.assets. Ignore unrelated files such as.DS_Store; never recursively scan.OxpDocument, restoring each payload to its originalassetormesh_assetrecord.<folder-name>.oxp.Creation must be semantically lossless, not compressed-byte-identical: decoding the original and recreated OXP must produce equal OXP documents, including unknown fields, and every embedded payload must be byte-identical.
JSON parse/type errors must report the JSON path or record/field name. Missing asset errors must include UUID and expected file path.
listlistdecodes the OXP through #110 but writes no files. It lists only embedded asset records, from bothassetandmesh_asset, sorted bysource_mapthen UUID.Write a stable tab-separated header and rows to stdout:
FILEis the filenameextractwould use.BYTESis the uncompressed payload length.listmust not inspect or convert payload contents.CLI behavior
metacrate-oxp --helpand each<command> --helpdocument usage and path derivation.Tests
Add deterministic offline integration tests covering:
.OXP..binfallback.extract->create-> decoded-document semantic equality, unknown LLSD field retention, exact payload equality, and preserved semantic array order.listordering, columns, byte counts, escaping, empty-document output, and absence of filesystem writes.Tests must use temporary directories and call the built binary as a subprocess. They must not access sibling repositories, viewer caches, network services, or platform-specific tools.
Documentation
Update
metacrate-oxpcrate and workspace documentation with:manifest.jsonschema;Validation gates
Run the focused CLI integration tests on Linux, Windows, and macOS.
Definition of done
A user can losslessly extract any supported OXP into a same-named folder with canonical UUID asset filenames and one complete
manifest.json, inspect its embedded assets withlist, and recreate a same-named OXP withcreate. The recreated document is semantically equal, asset payloads are byte-identical, no target is overwritten or left partial, and the same directory representation works on all supported platforms.