Archives and the VFS
dream_archive and vfstool index entries by the normalized key.
Library active
Byte-first normalized virtual resource paths: one spelling for every asset lookup in DreamWeave and OpenMW-style tools.
cargo add dream-pathA Morrowind plugin asks for Textures\Tx_Wood_01.DDS. The archive stores
textures\tx_wood_01.dds. A mod ships TEXTURES/Tx_Wood_01.dds as a loose file, and a script
asks for textures//tx_wood_01.dds. They are all the same resource, and every layer that looks
one up has to agree on that, or a texture goes missing in one tool and not in another.
dream-path is where that agreement lives. It turns any spelling of a virtual resource path into one normalized byte string. An archive reader, a VFS, a renderer and a script that all normalize through it cannot disagree about a key; five slightly different normalizers would be five slightly different bugs.
Archives, loose files, scripts and the renderer each receive paths spelled their own way. Normalizing them all the same way is what lets a lookup in one find what another stored.
Textures\Tx_Wood.DDS from a plugin, textures/tx_wood.dds from an archive, //TEXTURES/tx_wood.dds from a script.
Backslashes to slashes, ASCII to lowercase, repeated and leading separators gone. Every other byte kept.
textures/tx_wood.dds: the same bytes wherever the lookup happens, stored once as a NormalizedPath.
dream_archive and vfstool index entries by the normalized key.
require("@dream/path") gives scripts the same rules, byte for byte.
\ becomes /.A to Z become lowercase.NUL, .., a trailing /.That is all it does, on purpose. It does not decode legacy encodings, fold Unicode case, resolve
.., or interpret host paths, drive letters or URIs. Normalization shows the
literal consequences.
use dream_path::NormalizedPath;
let key = NormalizedPath::new(r"//Meshes\x\Ex_Door.NIF");
assert_eq!(key.as_bytes(), b"meshes/x/ex_door.nif");
assert_eq!(key.extension().unwrap(), "nif");local dreamPath = require("@dream/path")
assert(dreamPath.normalize([[Textures\Tx_Wood_01.DDS]]) == "textures/tx_wood_01.dds")dream_archive and
vfstool build on it. The crate has one dependency,
bstr.
Rust crate · 1.1.0
From your project's directory:
cargo add dream-path
Or by hand, in Cargo.toml:
[dependencies]
dream-path = "1.1.0"
Cargo reads a bare version as "this or any compatible newer release" and records the exact one in Cargo.lock.
[dependencies]
dream-path = { git = "https://github.com/DreamWeave-MP/dream_path" }
Cargo pins the commit it fetched in Cargo.lock; cargo update -p dream-path moves it forward.
Relicensed as MIT OR Apache-2.0 and built on l3i 1.1.0.
@dream/path module is unchanged.The same digest as the crates.io index's checksum, which Cargo checks on every download.
First stable release: the Luau API is an l3i extension providing @dream/path, normalization is faster, and the Rust and Luau APIs are under semver.
lua feature binds the crate as an l3i extension, lua::PathExtension, providing the module @dream/path. create_module, register_module, register_module_as and the standalone-lua feature are gone with mlua.lua feature needs l3i’s toolchain: clang, lld and cross-language thin LTO.mlua.luau-analysis feature (cargo test --features luau-analysis); cargo test --features lua does not build Luau’s analysis frontend.Compose dream_path::lua::PathExtension into the host’s RuntimePlan. A host that still wants the
dreamPath global adds RuntimePolicy::compat_global("@dream/path", "dreamPath"). The functions
and their results are unchanged.
The same digest as the crates.io index's checksum, which Cargo checks on every download.
The Lua API runs on Luau, with camelCase names.
mlua 0.12 and Luau. standalone-lua now selects Luau.fileName, isUtf8), and the module’s name is dreamPath.isNormalized in Lua.The same digest as the crates.io index's checksum, which Cargo checks on every download.