Maturity: Draft. Consolidated on 26 September 2026 from the installed 2.31 game (scripts, TweakDB, one vanilla item's full resource chain, the player body resources), the scripting RTTI dump, WolvenKit's save parsers, ArchiveXL 1.27.3 and EquipmentEx source, one decoded 2.31 save and the Modding Docs. Nothing on this page has runtime evidence yet. Since 26 September the Studio reads what a save's V wears and draws it over the body (body rendering): the clothing render plan's phases 1–4 (§6). Evidence grades follow the knowledge rules: source engine/framework/tool source or decompiled scripts, resource extracted game or mod resources, wiki Modding Docs text or image, runtime running game, hypothesis not yet established. The build plan is in the clothing render backlog.
This page answers five questions for XF Studio agents:
- How does a save record what V wears, and what does the Studio's save import read today?
- How does an item ID become the meshes that are drawn, per body gender, camera and body mod?
- How do garments layer over each other and hide the body under them?
- What materials do clothes use, and how much of that does the Studio already draw?
- What would rendering worn clothing in the Studio take? (Summary here; plan in the backlog page.)
Evidence snapshot
| Source | Version | What it established |
|---|---|---|
| Game scripts | 2.31 r6\ (SHA-256 2119046f…ee86), decompiled with redscript-cli 0.5.31 into a private scratch folder; cyberpunk/ | Which equipment fields persist, the wardrobe (transmog) and hide logic, the slot-to-tag table, underwear and censorship rules |
| Scripting RTTI dump | The dump psiberx exported, as published in red-dump-json at a8e52990 (May 2025, before 2.31) | Field lists of EquipmentSystemPlayerData, gameSLoadout, gameSSlotVisualInfo, gameClothingSet, gameItemID, the garment classes and appearanceAppearanceDefinition |
| WolvenKit | commit 11720772: WolvenKit.RED4/, WardrobeSystemClothingSetsParser.cs, ScriptableSystemsContainerParser.cs, Save/, Archive/ | Save node layouts for the wardrobe, item IDs and the script-system package |
| Reference save | 2.31, save version 269 | Node sizes and which classes its script-system package holds (§2.5) |
| Check save | 2.31, save version 269, 169 nodes (private) | The loadout the Studio's reader decodes end to end (§2.5) |
| WolvenKit hash lists | usedhashes.kark at the same commit | The depot paths of the cooked visual-tag preset (§4.1) |
| Installed TweakDB | 2.31 r6\, read with the Studio's tweakdb-flats.ts | Vanilla clothing record fields (§3.1) |
| Installed game resources | 2.31, read with the Studio's native archive reader (read-only, 26 September) | One item's whole chain: clothing.csv factory → player_ → t1_ / t1_ → part .ent → .mesh → multilayered materials; the resolver's cached player-body .ent and .mesh resources |
| ArchiveXL source | 1.27.3, MIT | Dynamic appearances, suffix and body-state attributes, visual tags and body-hiding rules, garment offsets |
| EquipmentEx source | commit 3208ff4c, MIT | Outfit persistence, outfit slots and their garment offsets, how it overrides the wardrobe |
| Codeware source | commit 613a1cb8, MIT | AttachmentSlotData (what an attachment slot shows), wardrobe helpers |
| Modding Docs clone | be2f44ee (canonical upstream/) | Item structure, suffixes and substitutions, dynamic variants, tags and their body diagrams, garment support, first-person fixes, body mods; pages and images cited inline |
Modding Docs links below are to that commit. Short forms: [items] ArchiveXL item structure explained (manavortex); [suffixes] ArchiveXL suffixes and substitutions (mana vortex); [tags] ArchiveXL tags (mana vortex, last updated by LadyLea); [garment] Garment support: how does it work? (crediting psiberx for the algorithm's explanation, redacted-c01 and Auska); [dynamic] ArchiveXL dynamic variants (mana vortex); [fpp] First-person perspective fixes (FronkenZeepa, updated by LadyLea); [bodies] ArchiveXL body mods and refits (mana vortex, updated by LadyLea).
1. The chain at a glance
Arrows
- Save: EquipmentSystemPlayerData arrow to gameItemID
- gameItemID arrow to TweakDB item record
- TweakDB item record arrow to Factory .csv
- Factory .csv arrow to Root .ent appearance
- Root .ent appearance arrow to .app definition
- .app definition arrow to Part .ent (mesh entity)
- Part .ent (mesh entity) arrow to .mesh appearance
- .app definition arrow to Visual tags + partsOverrides
- Visual tags + partsOverrides arrow to Hide other slots' items
- Part .ent (mesh entity) arrow to Garment assembler
Boxes
- Save: EquipmentSystemPlayerData
- equipped item per area; + wardrobe visual per area
- gameItemID
- (TweakDB record id)
- TweakDB item record
- entityName, appearanceName,; appearanceSuffixes, placementSlots,; garmentOffset
- Factory .csv
- entityName to root .ent
- Root .ent appearance
- chosen by name + suffixes; (or ArchiveXL dynamic name)
- .app definition
- partsValues, partsOverrides,; visualTags
- Part .ent (mesh entity)
- garment components with; layer prefixes t1_, l1_, s1_ ...
- .mesh appearance
- per-chunk materials; (mostly multilayered)
- Hide other slots' items
- and mask body chunks
- Garment assembler
- layer order and; GarmentSupport morphs
Mermaid source
flowchart TD
S["Save: EquipmentSystemPlayerData<br/>equipped item per area<br/>+ wardrobe visual per area"] --> I["gameItemID<br/>(TweakDB record id)"]
I --> R["TweakDB item record<br/>entityName, appearanceName,<br/>appearanceSuffixes, placementSlots,<br/>garmentOffset"]
R --> F["Factory .csv<br/>entityName to root .ent"]
F --> E["Root .ent appearance<br/>chosen by name + suffixes<br/>(or ArchiveXL dynamic name)"]
E --> A[".app definition<br/>partsValues, partsOverrides,<br/>visualTags"]
A --> P["Part .ent (mesh entity)<br/>garment components with<br/>layer prefixes t1_, l1_, s1_ ..."]
P --> M[".mesh appearance<br/>per-chunk materials<br/>(mostly multilayered)"]
A --> T["Visual tags + partsOverrides"]
T --> H["Hide other slots' items<br/>and mask body chunks"]
P --> G["Garment assembler<br/>layer order and<br/>GarmentSupport morphs"]
The item side mirrors the character-creator chain in the CC file chain from the .app onward: the same .app → part .ent → component → .mesh → material steps, with an item factory and TweakDB record in front instead of a creator option. The resolver's .app, partsOverrides, chunk-mask and dynamic-material rules therefore carry over [source: character-resolver.ts rules R6–R7].
2. What the save records
2.1 Equipment source
The player's equipment lives in the script system EquipmentSystem, whose persistent m_ holds one record per owner. EquipmentSystemPlayerData persists these fields (script names; the RTTI and the save drop the m_ prefix):
| Field | Type | Meaning |
|---|---|---|
m_ | SLoadout | equipAreas[], each {areaType: gamedataEquipmentArea, equipSlots[{itemID, slotID}], activeIndex}, plus equipmentSets |
m_ | [SSlotInfo] | The fixed slot table: area, attachment slot and the visual tag that hides it (§4.2) |
m_ | [SSlotVisualInfo] | One entry per clothing area: {areaType, isHidden, visualItem}, the wardrobe override and hide flag |
m_, m_ | Bool, gameWardrobeClothingSetIndex | Wardrobe state (Slot1…Slot7, INVALID) |
m_, m_, m_ | Weapons and hotkeys, not visual |
The clothing areas are Head, Face, OuterChest, InnerChest, Legs, Feet, Outfit, UnderwearTop and UnderwearBottom (InitializeClothingOverrideInfo; Outfit is excluded from the visual table by GetVisualSlotIndex). An item ID is {id: TweakDBID, rngSeed: u32, uniqueCounter: u16, flags: u8} [source: RTTI gameItemID]; in save item records WolvenKit reads it as a u64 TweakDBID, u32 seed, one structure byte, u16 counter and u8 flags [source: InventoryHelper.ReadItemInfo].
A TweakDBID is a CRC-32 of the record name plus the name's length, so it can't be turned back into a name, but it doesn't need to be: the Studio's TweakDB reader addresses a record's fields from its ID alone (childId(record, ".appearanceName")) [source: tweakdb-flats.ts].
2.2 Wardrobe (transmog) source
- What is drawn in an area is
GetVisualItemInSlot(area): the wardrobe'svisualItemwhen a wardrobe set is active and the area has one, otherwise the equipped item.IsSlotOverridenis true only while a set is active and the area'svisualItemis valid. - Sets are native
gameWardrobeSystemdata:ClothingSet {setID, clothingList: [SSlotVisualInfo], iconID}, at most seven.EquipWardrobeSetunequips anyOutfititem, marks the set active, then for each area equips its visual item or clears the area. - Save nodes.
WardrobeSystemlists the wardrobe's known appearances, each an appearance name and an item ID;WardrobeSystem_holds the sets, which WolvenKit decodes only as six fixed-size item entries per set with unknown fields [source: WolvenKit parsers]. The active set itself is native (ClothingSets gameWardrobeSystem.GetActiveClothingSetIndex):WardrobeSystem_starts, after the node ID, with a u8 set count and a u32 that is 8 in saves without sets, the value ofClothingSets gameWardrobeClothingSetIndex.INVALID(Slot1…Slot7= 0…6,COUNT7,INVALID8) [resource; RTTI dump] [hypothesis: that u32 is the active set's index; the Studio reads it so]. - Transmog blocking. Items tagged
TransmogBlockedkeep their own look (AddItemToSlot,ClearVisuals).
2.3 The "hide" flags source
There is no separate hide switch per item. An area is hidden in two ways, and both end in the same place:
- Hidden by the player in a wardrobe set. A set entry with no item clears that area:
ClearVisualssetsisHidden = true, and an item equipped there afterwards is attached with the appearance nameempty_(appearance_ default AddItemToSlot). - Hidden by another item's visual tag. When an item equips,
OnEquipProcessVisualTagshides every area whose tag the item carries (for example a coat taggedhide_hides the inner-chest item), andT1 ClearItemAppearanceswitches the hidden item toempty_. Unequipping restores it.appearance_ default
So "hidden" is recorded as isHidden in m_, and the drawn result of a hidden area is the empty appearance. Underwear is special: the bottom is hidden whenever the legs area shows an item or hide_ is active; the top is hidden only in censored builds (IsBuildCensored asks the character-customisation system whether nudity is allowed) when the inner chest shows an item or hide_ is active.
2.4 EquipmentEx outfits source
EquipmentEx is a script mod that replaces the wardrobe with named outfits over 30+ extra slots:
- Persistence.
EquipmentEx.OutfitSystem(aScriptableSystem) haspersistent m_, andstate: ref<OutfitState> OutfitStatepersistsm_,disabled m_,active m_(the outfit being worn:parts OutfitPart {m_),itemID, m_ slotID} m_(outfits OutfitSet {m_) andname, m_ parts, m_ timestamp} m_(user slot remaps). It is saved in themappings .datthrough the ordinary persistent-field mechanism, beside the vanilla equipment data (§2.5); there is no side file. - Slots.
OutfitSlots.*attachment-slot records (Head, Balaclava, Mask, Glasses, the torso layersTorsoUnder…TorsoAux, legs, feet, jewellery and body-suit slots), each with a default garment offset (for exampleHead310000,TorsoUnder120000,TorsoOuter210000), listed inscripts/and created by redscript at runtime (OutfitConfig.reds RegisterOutfitSlots.reds), so they are not in the compiled TweakDB. - Drawing. While an outfit is active, EquipmentEx clears every base area's visuals (
HideEquipment:ClearVisualson each area, then locks visual changes), attaches each part as a preview item in its outfit slot, and turns ArchiveXL's garment offsets on (§4.5). It makes the vanilla wardrobe calls no-ops and converts old wardrobe sets into outfits on first use.
What the game draws with EquipmentEx active therefore depends on EquipmentEx's script logic, not only on vanilla data in the save.
What a script reading of the wardrobe sees source 2.31
equipmentSystem.script:1537,wardrobeSystem.script:36; EquipmentEx 1.2.9 as installed; runtime session 6, 29 September 2026. The wardrobe system's active index isSlot1-Slot7orINVALID, andIsVisualSetActive()is that index being valid; EquipmentEx wraps it to OR in its own outfit, so with an EquipmentEx outfit on, a reading shows "active" with no wardrobe outfit (the XF bridge's 0.5.2wardrobe.statereportedset: 0withactive: trueand every area hidden). Under it, the vanilla wardrobe requests (EquipWardrobeSetRequest,UnequipWardrobeSetRequest,QuestHideSlotRequest) do nothing, andUnequipVisualsRequestclears an area's hide flag without the appearance reset EquipmentEx blocks, so an equipped helmet stays invisible (session 6: the bridge's show-equipped answered changed and the helmet didn't draw; its undo couldn't hide the area again, and the area was hidden again only later, after the kill switch and the helmet's removal, by a route not identified [hypothesis: EquipmentEx refreshing its outfit on an equipment change]).What works under both. The story's own pair,
QuestDisableWardrobeSetRequest(the quest system's "disable visual override",player.script:6312, and the undress scene,invisibleSceneStash.script:17) andQuestRestoreWardrobeSetRequest: the vanilla wardrobe remembers its outfit, takes it off and puts it back; EquipmentEx replaces both handlers with its ownDeactivate(parts kept) andEnable+Reactivate. And EquipmentEx's publicOutfitSystem(EquipItem(itemID),UnequipSlot,GetUsedSlots, with each part's item read throughTransactionSystem.GetItemInSloton its outfit slot) is what its own screen drives: the route by which session 6's helmet drew. The XF bridge 0.6 uses the pair forwardrobe.equip {suspend}/{resume}and the outfit system for{item}and exact undos (bridge README); both are [offline] until session 7.Driving it from another mod without compiling against it. EquipmentEx needs Codeware, so a mod that already requires both can reach
EquipmentEx.OutfitSystemby its class name in the scriptable systems container and call its public functions through Codeware's reflection (Reflection.GetClass(n"EquipmentEx.OutfitSystem").GetFunctions(), matched by short name and parameter type names, whereItemIDreflects asgameItemID; overloads such asEquipItem(TweakDBID|ItemID, opt TweakDBID)differ only by those), so a later EquipmentEx that changes a signature fails that one lookup instead of the whole script compile source Codewarev1.20.4scripts/,Reflection/ *.reds src/; EquipmentExApp/ Reflection/ ReflectionClass.hpp:43-51 3208ff4scripts/,OutfitSystem.reds:399-1012 Facade.reds:4(EquipmentEx.Version()). XF Runtime Bridge 0.6.1 does this [hypothesis until session 7's G6].Activate()does nothing while the outfit is blocked (IsBlocked: disabled by the story, or an outfit item taggedUnequipBlocked) and clears the parts before cloning the equipment;Reactivate()(what the story's restore request reaches) keeps them.
2.5 In the reference save resource
The reference 2.31 save has 330 nodes. The relevant ones: ScriptableSystemsContainer (89,926 bytes, one object package holding EquipmentSystemPlayerData with equipment, clothingSlotsInfo and clothingVisualsInfo, and also EquipmentEx.OutfitState with parts and outfits, since EquipmentEx is installed), inventory (13,073 bytes, 159 itemData children), WardrobeSystem (1,952 bytes, 326 known appearances) and WardrobeSystem_ (9 bytes: no vanilla sets). The package format is the one the native archive reader decodes for .ent/.app compiledData, except that the save variant stores a u32 CRUID count where a resource package has an i16 root index and a u16 count [source: WolvenKit RedPackageReader.File.cs]. Every object carries its own field table (names, stored types and offsets), so a class no RTTI dump knows (a script mod's) is still walkable, and a field of a type a reader doesn't decode is skipped by the next field's offset [resource: the Studio's reader, below].
A second, newer 2.31 save of the same V (the private check save: 169 nodes, ScriptableSystemsContainer 21,482 bytes: after the node ID a u32 size, then the package: version 4, six sections, 57 CRUIDs) holds two EquipmentSystemPlayerData objects: the player's (ownerID.hash = 1, with equipment, lastUsedStruct, clothingSlotsInfo, clothingVisualsInfo, hotkeys) and an owner with only ownerID (9000324), clothingSlotsInfo and clothingVisualsInfo. Fields at their default are not written (no activeIndex for slot 0, no isHidden when false, no uniqueCounter) resource. Its player wears a vest, a T-shirt, trousers, boots and the basic underwear bottom (hidden, under the trousers); a save taken while EquipmentEx dresses V hides every vanilla area (isHidden on each, HideEquipment), so the save alone shows no clothes resource.
2.6 What the Studio reads source
save-reader.ts decodes the creator appearance node (save import) and, in the same pass, the loadout (save-loadout.ts): the player's EquipmentSystemPlayerData from ScriptableSystemsContainer through a small self-describing package reader (save-package.ts: fixed-size values, TweakDBIDs, names and the enums it is told, arrays, handles as indexes, nested objects by their field tables; bounded by its chunk), and the active wardrobe set from WardrobeSystem_. It keeps, per clothing area, the active slot's item record ID and the saved visual item and hide flag. A save whose script data can't be read still loads its V, with no loadout. It never reads a script mod's data (EquipmentEx's outfit sits in the same package and is left alone). It reads no inventory, and nothing resolves TweakXL YAML yet (clothing render phase 5; the creator catalogue notes the same gap: cc-presentation.ts).
3. From item ID to drawn meshes
3.1 The TweakDB item record resource wiki
Vanilla clothing records in the 2.31 TweakDB (inheritance is already flattened into each record's flats):
| Record | entityName | appearanceName | appearanceSuffixes | placementSlots / area |
|---|---|---|---|---|
Items.TShirt_ | player_ | t1_ | Gender | AttachmentSlots.Chest / InnerChest |
Items.Shirt_ | player_ | t1_ | Gender, Camera, Partial | AttachmentSlots.Chest / InnerChest |
Items.Jacket_ | player_ | t2_ | Gender, Camera | AttachmentSlots.Torso / OuterChest |
Items.Pants_ | player_ | l1_ | Gender | AttachmentSlots.Legs |
Items.Boots_ | player_ | s1_ | Gender | AttachmentSlots.Feet |
Items.Helmet_ | player_ | h1_ | Gender, Camera | AttachmentSlots.Head |
Items.Glasses_ | player_ | f1_ | Gender, Camera | AttachmentSlots.Eyes |
Items.Underwear_ / _ | player_ / …_ | t1_ / l1_ | Gender | UnderwearTop / UnderwearBottom |
Records also carry garmentOffset (Int32), visualTags (a CName array, empty on the vanilla items read), isGarment, equipArea, itemType and useHeadgearGarmentAggregator [wiki: saltypigloaf's field dumps in the cyberware and iconic-weapon guides; resource: the 2.31 TweakDB]. The Studio's TweakDB reader decodes CName, String, TweakDBID and its arrays, CName arrays, Int32, Bool, Float, resource references and LocKeys (tweakdb-flats.ts). The suffix records are itemsFactoryAppearanceSuffix.Gender, .Camera, .Partial, .HairType (vanilla; their scriptedSystem is None, native) and ArchiveXL's .BodyType, .ArmsState, .FeetState, .LegsState [resource; source]. Mod items are usually added with TweakXL YAML ($base inheritance, $instances expansion), which is not in the compiled blob [wiki: [items], [dynamic]].
3.2 Factory, root entity and suffixes resource source wiki
entityNameis looked up in the item factory. Vanilla clothing usesbase\(andgameplay\ factories\ items\ clothing.csv ep1\with Phantom Liberty), a…\ clothing.csv C2dArraywhose rows map, for example,player_toinner_ torso_ item base\; WolvenKit serializes the rows asgameplay\ items\ equipment\ torso\ player_ inner_ torso_ item.ent compiledData: [[name, path, preload], …]resource. The engine's own factory list is native (ArchiveXL's FactoryIndex waits forvehicles.csv, the last of it, before adding the.xlfactories); the item factories it ships areitems\,clothing.csv items.csv,cyberware.csv,consumables.csvunderbase\, andgameplay\ factories\ ep1's clothing and cyberware [resource: present in the 2.31 archives]. ArchiveXL.xlfiles add factories (factories:) [source: FactoryIndex; wiki: [items]]; the reference profile's mods declare several hundred. The native reader cannot yet decode aC2dArray's rows (they sit in data after the properties) resource.- The root entity has one appearance per item look and suffix combination:
player_lists 754, for exampleinner_ torso_ item.ent t1_→tshirt_ 01_ basic_ 01_ &Female t1_/tshirt_ 01.app basic_, and01_ w t1_→shirt_ 01_ basic_ 01_ &Female&TPP&Part t1_/shirt_ 01.app basic_resource.01_ w_ partial - Suffixes are evaluated from
itemsFactoryAppearanceSuffixrecords:Gender(Female/Male),Camera(TPP/FPP),Partial(Full/Part, driven byhide_on the torso item) andT1part HairType(Short,Long,Dreads,Buzz,Bald) [wiki: [suffixes]]. ArchiveXL addsBodyType,ArmsState,FeetStateandLegsStatesuffix records and appends them to the default suffix order [source: ArchiveXLPuppetState/Extension.cpp OnTweakDBReady]. The most specific matching root appearance wins:&Female&TPPbeats&Female, which beats the bare name [wiki: [suffixes], "Suffix load order"]. - If a root entity has the visual tag
EmptyAppearance:FPP,:Maleor:Femaleand the selector carries that suffix, ArchiveXL returns the empty appearance [source:Garment/Extension.cpp OnResolveAppearance].
3.3 .app, part entity and mesh resource
t1_ definition basic_ has partsValues = base\ and one partsOverrides entry that sets component t1_'s meshAppearance to moro. That part entity holds one entGarmentSkinnedMeshComponent of mesh …\, with renderingPlaneAnimationParam = renderPlane and the visual tag Tight in its visualTagsSchema. The mesh has 32 appearances (moro, yellow, white_, …); each names the chunk materials, for example moro = ml_, decal, ml_. The male look (basic_) uses a separate _ part entity and mesh.
Camera and sleeve variants differ only in chunk masks: t1_'s basic_ clears chunk 1 of the shirt (mask …FFFD) and shows a cuff component that the third-person look hides (mask 0), and basic_ clears chunks 1 and 2 (…FFF9) resource. The resolver already applies partsOverrides and chunk masks this way for creator parts (rule R7).
3.4 ArchiveXL dynamic appearances source wiki
Newer mod items use one root appearance and substitution instead of a suffix matrix:
- The root
.entcarries the visual tagDynamicAppearanceand the record'sappearanceNamecontains!: the text before it is the base name, everything after is{variant},+splits it into{variant.1},{variant.2}…, andattr=valuepieces override attributes. Vanilla suffixes are blanked for such items [source:Garment/,Dynamic.cpp Extension.cppOnResolveSuffixes]. - Definition choice: every
.appdefinition whose base name matches is a candidate; names may carry!variantparts and&attr=valueconditions. A definition with variants scores 100 plus one per condition; an unconditional one is the fallback; the best matching one is cloned under the full name, and an empty definition results if candidates exist but none match. Components named with conditions are switched the same way inside each name group [source:Garment/States.cpp SelectDynamicAppearance,ToggleConditionalComponents]. - Substitution: mesh-component paths and
meshAppearancevalues starting with*get{attr}replaced from the name's parts first, then the entity state:gender(w/m),camera(tpp/fpp),body,arms,feet,sleeves,skin_,color hair_,type hair_,color eyes_,color nails_,color variant. A trailing?blanks a component whose value doesn't resolve. If a resolved mesh is missing, ArchiveXL retries withbodyand thenfeetset tobase_andbody flat[source:ProcessString,ResolvePath]. The wiki warns that a missing{variant}mesh crashes the game [wiki: [dynamic]]. - The Studio already expands
*…{attr}…material paths and ArchiveXL's creator appearance cloning (expandDynamicPath,dynamicAppearanceincharacter-resolver.ts); item definitions need the!/&selection and component-path substitution on top.
3.5 Body gender, body mods and body states source wiki
{gender}and theGendersuffix come from the player's body gender,{camera}from the vanilla camera suffix [source:CollectStateData,GetSuffixData].{body}: body mods declareplayer: bodyTypes: [Name]in.xland patch a component namedBody:Name(or tag the entity or a morph-target component) into the player entity; otherwise the value isbase_, which also covers vanilla-shaped replacers [source:body PuppetState/Extension.cpp GetBodyType; wiki: [bodies], with a table of known body tags].GetBodyTypechecks the live puppet's entity and visual tags first, then its components from the last, each against every declared body tag (Body:<name>, registered inConfigure) [source: ArchiveXL 1.27.3PuppetState/]. The wiki's body-mod guide patches aExtension.cpp Body:<name>entMeshComponentinto theplayer_/wa.ent player_scopes [wiki: [bodies]]. Refits then shipma.ent …_variants, and ArchiveXL resource links can point several body names at one mesh._ {body}.mesh {feet}(female V only in vanilla):Flat,Lifted,HighHeelsorFlatShoesfrom the feet item's tags (HighHeels,FlatShoes,force_);FlatFeet {arms}from the equipped arm cyberware;{sleeves}Partwhen the torso slot's item hashide_[source:T1part PuppetState/,Handler.cpp Attachment/].Extension.cpp - These are all derivable offline from the worn items, the body gender and the resolved player entity, so a resolver can compute them without the game.
4. Layering and body hiding
4.1 Where visual tags come from source wiki
An item's visual tags are the union of its root entity's visualTagsSchema and its resolved .app definition's visualTags. The game reads them through a cooked AppearanceNameVisualTagsPreset (entity path hash + appearance name → tags); ArchiveXL hooks GetVisualTags to add the tags from the resources, which is what makes .app tags work for mod items [source: ArchiveXL Garment/ OnGetVisualTags; RTTI gameAppearanceNameVisualTagsPreset_]. Hiding tags only take effect in the .app [wiki: [tags]].
The cooked preset is base\ (basegame_), and with Phantom Liberty ep1\ (ep1_), a superset (1,351 and 1,907 entities in 2.31, the player items' entries identical) [resource; paths from WolvenKit's hash list]. Each is a JsonResource whose root is gameAppearanceNameVisualTagsPreset {presets: [{entityPathHash, entityRigPathHash, appearancesToTags: [{appearanceName, visualTags}], commonVisualTags}]}, keyed by the root entity's path hash and the root appearance name (t2_ → Large; t1_ → Tight, Cotton); the vanilla hide tags live here (player_'s appearances use hide_, hide_, hide_, hide_, hide_, hide_, Coat, Collar) resource. The vanilla .app definitions read carry none. WolvenKit 9.0.1 doesn't serialize the resource; the Studio's native reader decodes it (8.3 MB and 13.0 MB of JSON) resource.
4.2 Vanilla slot hiding source
| Tag | Hides the item in | Attachment slot |
|---|---|---|
hide_ | OuterChest | AttachmentSlots.Torso |
hide_ | InnerChest | AttachmentSlots.Chest |
hide_ | Legs | AttachmentSlots.Legs |
hide_ | Feet | AttachmentSlots.Feet |
hide_ | Head | AttachmentSlots.Head |
hide_ | Face | AttachmentSlots.Eyes |
hide_ | UnderwearBottom | AttachmentSlots.UnderwearBottom |
Source: InitializeClothingSlotsInfo. IsVisualTagActive checks the active Outfit item and every visible clothing area; hide_ switches the inner torso to its partial (&Part) look while the outer chest is visible. Other base-game tags include hide_ [wiki: [tags]]. The 2.31 cooked preset (§4.1) also carries hide_, hide_, hide_ and, on the eight outfit_ quest looks only, hide_ resource. The engine handles these natively; how hide_ picks what to hide is not in any source we have. None of the preset's tags, and no TweakDB string, is ArchiveXL's hide_ [resource: the decoded preset; a byte search of tweakdb_], so no vanilla item hides V's head.
4.3 Masking the body resource source wiki
The female body t0_ is an entMorphTargetSkinnedMeshComponent whose entity carries the visual tag PlayerBodyPart; its mesh has eight chunks and one appearance per skin tone resource. ArchiveXL's bundled VisualTags.xl maps tags to components, prefixes and chunks source:
| Tag | Hides |
|---|---|
hide_ | h0_, he_, heb_, ht_, hx_, i1_, beards and four head morph components |
hide_ | a0_, left_, right_ |
hide_ | n0_, tx_, body chunks 0–3 (TPP body and the female FPP torso), seam fixes, nipples |
hide_ / hide_ / hide_ / hide_ | body chunk 0 / 1 / 2 / 3 |
hide_ | l0_, s0_, body chunks 4–7 |
hide_ / hide_ / hide_ / hide_ | body chunk 4 / 5 / 6 / 7 (the last three also mask the l0_ feet variants) |
HighHeels, FlatShoes | body chunks 5–7, and show chunks 0–2 of the matching l0_ feet mesh |
Any mod's .xl can add tags the same way (overrides: tags: <tag>: <component or prefix>: {hide|show: [chunks]}), and unlike partsOverrides a tag can also un-hide [source: Garment/; wiki: [tags], "Adding Custom tags", with body-mod examples]. ChunkMask turns a hide list into the mask of every other chunk (an empty list, or hide: 0, hides the whole component), a show list into the listed chunks' bits, and a bare number or list without an operation into a hiding mask; per component the showing masks are ORed and the hiding masks ANDed, then applied as (original | shows) & hides, by exact name and by prefix [source: ChunkMask.hpp, States.cpp ApplyChunkMaskOverride]. Only an .app definition's own visualTags register these rules (RegisterComponentOverrides(appearance)), never the preset's or the root entity's tags source, so vanilla items, whose definitions carry no tags, mask no body chunk this way: vanilla clothing relies on the garment assembler instead (§4.5). The prefix is the text up to the first _ when that _ is at index 2–5 [source: Garment/]. LadyLea's body diagrams on the tags page (female, male) colour the same eight body submeshes and list the tags beside them [wiki, image]; the female diagram also lists body submeshes for the base-game hide_ (4–6), hide_ (7) and hide_ (3), which neither the scripts nor ArchiveXL's rules show [hypothesis: they come from cooked data or the garment assembler].
4.4 partsOverrides onto the body source wiki
An item .app's partsOverrides entry with an empty partResource applies by component name to the whole player, so an item can hide body chunks directly; ArchiveXL records these per item on equip and applies them before the garment is computed (RegisterComponentOverrides, ApplyChunkMaskOverride: the original mask, then every show mask, then every hide mask, by exact name and by prefix) source. The wiki's example hides a submesh of t0_ this way [wiki: influencing other items].
4.5 Garment support: layer order and morphs
- Layer order comes from the component prefix:
s00,l010,a020,t030,h040,s150,l160,t170,i180,hh90,h1100,h2110,t2120, plus avisualTagsSchemamodifier (PlayerBodyPart−2000,Tight−1000,Normal0,Large+1000,XLarge+2000); components with the same prefix in one entity don't squish each other; with EquipmentEx the outfit slot's offset decides instead [wiki: [garment]]. The T-shirt above ist1_andTight; the body ist0_andPlayerBodyPartresource. - Garment offsets. The engine passes each item's
garmentOffsetinto the garment request; on entities ArchiveXL tracks it clears these offsets unlessArchiveXL.EnableGarmentOffsets()was called, which EquipmentEx does while an outfit is active [source: ArchiveXLApplyOffsetOverrides,src/; EquipmentExRed/ GarmentAssembler.hpp OutfitSystem.reds]. With ArchiveXL installed and no outfit active, the prefix and tag score alone decide. - Morphs. Garment and body meshes carry a
meshMeshParamGarmentSupport(chunkCapVertices,customMorph) and agarmentMeshParamGarmentwhose chunks store vertices, indices,morphOffsets,garmentFlagsand UVs; the female body mesh has both,customMorph = 1and empty caps [resource; field names from the RTTI]. The GarmentSupport shape morphs a lower layer inward where a higher layer covers it; its strength and a "cap" that stops a mesh crushing the layers below are painted as the_andGARMENTSUPPORTWEIGHT _vertex colours [wiki: [garment]; painting garment support by revenantFun]. The page's example image shows the same trousers tucked into two different boots [wiki, image].GARMENTSUPPORTCAP - The assembler is native. The per-vertex computation happens in the engine's garment assembler; ArchiveXL only swaps meshes and applies masks before it runs source. The RTTI names its inputs:
garmentGarmentLayerParams(bending offset, smoothing radius and strength, collar area, hidden-triangle removal with a ray length in cm) and a cooked per-.appentGarmentParameter(visible triangle indices and morph offset scales per chunk,hideComponent,removeHiddenTriangles) [source: RTTI]. How the offsets are chosen per vertex is not in any source we have [hypothesis: a short outward ray test per inner vertex against outer layers, givenrayLengthInCM]. Body mods without GarmentSupport data are why vanilla jackets clip on them [wiki: painting page].
4.6 First and third person source wiki
Each camera can use a different root appearance (&FPP/&TPP, &camera=), chunk masks, or separate _ meshes; the female player has separate first-person torso, neck and arm meshes (t0_ is in every ArchiveXL body rule) [source; wiki: [fpp], body cheat sheet]. Components with visibilityAnimationParam = invisible_ disappear in first person, and sleeve and glove components use renderingPlaneAnimationParam = renderPlane so they draw in front of the arms [wiki: [fpp]]. ArchiveXL adds custom Head and Eyes sub-slots to the TPP representation so they swap like vanilla headwear [source: Attachment/ OnAttachTPP]. The Studio only ever shows third person, so it resolves with Camera = TPP.
5. Clothing materials
- Colour variants are mesh appearances. A look picks a mesh appearance through
partsOverrides(or a*{variant}substitution), and each appearance maps chunks to material entries [resource: §3.3; wiki: [items]]. - Most garments are multilayered. In
t1_every look's main chunks use071_ pwa_ tank_ _ basic.mesh engine\with the shared maskmaterials\ multilayered.mt ml_and onet1_ 071.mlmask .mlsetupper colourway (ml_,t1_ 071_ basic.mlsetup …_,green_ moro.mlsetup …_), and a separateyellow.mlsetup decalchunk has its own material resource. The wiki names other multilayered variants and non-layered garments that use.mt/.mitextures [wiki: multilayered]. - Mod recolours use ArchiveXL dynamic materials (
@dynamicentries whose setup path contains{material}, several variables from ArchiveXL 1.27), resource patches that merge appearances into refits, and@contextvalues [wiki: ArchiveXL dynamic materials and resource patching pages]. The resolver already handles these for creator meshes (mod loading §5).
What the Studio already covers. The browser's layered adapter (layered-material.ts, layered-setup.ts) implements the game's multilayered.mt arithmetic (front-to-back coverage, levels, colour masks, microblends, the global normal), reads .mlsetup/.mltemplate through the resolver and bakes each material once into base colour, normal and roughness/metalness maps, then lights it as a standard surface (materials and shaders §4.6). It already draws creator piercings. For garments two limits matter: the bake is capped at 2,048 pixels, so a torso-sized garment gets roughly 2 pixels per millimetre against the 20 per millimetre the bake targets (fine at full-body framing, soft in close-ups), and garments also use templates the adapter doesn't know yet (decals on clothes, emissive, glass for visors, cloth and hair variants for hat hair) [source: BAKE_, BAKE_; hypothesis for the template list beyond the tank top].
6. How the Studio draws worn clothing
The clothing render plan's phases 1–4 are built (26 September 2026). What each step does, and what it doesn't yet:
- What V wears comes from the save (§2.6): per clothing area the item the game shows before other items' tags hide it (
GetVisualItemInSlot: a wardrobe set's visual item while one is active, never for underwear, else the equipped item) and the saved hide flag (wornAreas). - The Clothing setting (
clothing-dressing.ts, the character context'scharacter.setClothing, with its own Undo): As saved, Without headwear and face items (the default while the eye-makeup editor is open; the head and face areas), Underwear only (the save's underwear, each missing piece filled withItems.Underwear_/Basic_ 01_ Top _, the top for a female V only; the lowest state, provisional decision 3) and Choose areas (the save's items of the areas picked). It never edits the save or the recipe. The request carries the save's areas and the areas the setting shows (Bottom xfs/).character-request-5 - Items to garments (
clothing-resolver.ts, host ports inclothing-host.ts): TweakDB record → factory table (the game's item factories, then every.xlfactory) → root entity → most specific root appearance for the suffixes the item's own record lists (appearanceSuffixes), each evaluated for the V (female/male, third person,Partwhen the shown outer torso item carrieshide_, the creator's hair tag, the body type,T1part BaseArms;FeetStateandLegsStateare not evaluated yet) →.appdefinition → components, through the same definition resolver as a creator choice (resolveAppDefinition: R6–R10). The body type is ArchiveXL's (playerBodyType):base_unless a declared body type's tag is on the third-person player entity (body player_/wa_ tpp.ent player_) or an ArchiveXL patch of it; with a type declared but the entity unreadable, or two types present, it staysma_ tpp.ent base_with a gap. Tags are the record's, the cooked preset's, the root entity's and the definition's. The cooked preset is decoded in the native reader's worker (off the host's event loop, after an asynchronous check of the game's Oodle library; the same worker the resolver reads through, asked for this one resource's root class and a longer budget) and kept per archive identity; a failed read is logged and tried again next time. When the TweakDB can't be read, or the clothes can't be worked out at all, the clothing slot is unavailable with one plain line, never "none".body - Hiding: the vanilla slot table between shown areas; a saved hide flag stays only when no saved item's tag explains it (a wardrobe set's empty area), otherwise the tags decide again, so taking a coat off shows the shirt it hid; the underwear rules (§2.3, with the censored top rule, which follows "underwear on");
EmptyAppearance:<suffix>on the root. - Onto V (ArchiveXL
RegisterComponentOverrides): each drawn item's definitionvisualTagsthrough every.xltag rule, and its entity-widepartsOverrides(masks and mesh appearances), becomeComponentOverridesthe V's body and arms resolve with (chunk masks before materials, so a shown chunk gets its materials too). The head keeps its parts (it is the makeup's canvas); what an item would hide there is reported as the gapworn-item-hides-head. Footwear sets the feet group tolifted_(ArchiveXLfeet ResolveFeetState: any visible feet item;HighHeelsandFlatShoes/force_states come from the tags and theirFlatFeet .xlrules). - Record and drawing (
xfs/; the clothing slot came with v9): arender-detail-11 clothingslot of garment components, lower layer first, each with itsgarment(area, item record ID, layer score: prefix base plus its part entity's size tag, §4.5). Garments draw through the existing adapters (almost all chunks aremultilayered.mt, baked by the layered adapter;mesh_chunks as decals;decal metal_shadow meshes and other unsupported templates are left out and counted). A garment without shape keys of its own carries the body's applied shape (breast size) from the nearest body vertex (base.remt xfs_, through one search grid per load), and coincident layers are ordered by a polygon offset of their layer rank; both stand in for garment support (§4.5), which is not built. A garment is kept across loads while the body's shape is unchanged, so a clothing change that only re-masks the body loads and bakes only what changed. The Body toggle hides the clothes with the body, and off, the host prepares neither; the Character panel's Clothing section has the setting (only the states that change what V wears are offered), a switch per area the save dresses, and its own Undo and Redo.body_ shape
Not yet (plan phases 5–8): TweakXL items and ArchiveXL dynamic appearances (reported per item, "items a mod adds aren't read yet"), the runtime bridge's worn-item snapshot (the exact answer when a script mod such as EquipmentEx dresses V; a save taken then hides every area, and the Studio says so), garment support (layers clip at edges), and a wardrobe to pick items from the installed catalogue. Item labels are words of the appearance name, not the game's localised names. Provenance and captures: clothing render evidence.
In-game results (sessions 5 and 6)
From the XF bridge's wardrobe and inventory commands on 29 September 2026 (game 2.31, the test profile's save, feminine V; experiment 032):
- An active blank outfit hides everything equipped runtime. The bridge's wardrobe read answered an active outfit at set 0 with no stored sets, and every clothing area hidden with no outfit item, while the equipment still held items (earrings, shoes, and a vanilla helmet the bridge added). Nothing hidden that way draws: this is why an equipped helmet never showed on V in photo mode in sessions 4–6, Thread Locker aside.
- The helmet draws when equipped through the wardrobe screen runtime (the maintainer, after session 5). The bridge's own writes didn't manage it: equipping the item into the outfit was refused as if no outfit were active, and switching the Head area to show its equipped item was accepted, after which the read showed Head no longer hidden (
shows: "outfit"), yet the helmet still didn't draw; the undo didn't restore the area's hidden flag exactly. These are bridge bugs, open (experiment 032 §6). - Which system hides the areas is not settled hypothesis. EquipmentEx is enabled in the test profile, and while its outfit is active it clears every base area's visuals, locks visual changes and turns the vanilla wardrobe calls into no-ops (§2.4). That fits every observation above: all areas hidden with no vanilla visual item, no vanilla sets, and vanilla writes that don't take. The wardrobe screen that made the helmet draw would then be EquipmentEx's outfit screen, adding the helmet as an outfit part. A save taken in this state hides every vanilla area, as §2.5 found in the private check save. Test: read what the Head slot actually attaches (the item and its appearance name,
empty_when hidden) before and after equipping through that screen, and repeat once in a throwaway profile without EquipmentEx.appearance_ default
Design implication. A tool that asks "what does V show?" should read what each slot actually draws, not infer it from the vanilla wardrobe tables: that answer is the same whichever outfit system is installed, so it needs no outfit-mod adapter. The same holds for the Studio's future worn-item snapshot (ask 3).
Open questions
- The save package's objects are read from their own field tables (§2.6). Is the u32 after
WardrobeSystem_' set count the active set's index (8 =ClothingSets INVALIDin saves without sets; a save with an active set would confirm it)? - Answered (§4.1): the cooked preset is
base\(and theentities\ appearancename_ visualtags.json ep1superset); it holds the vanilla items' hide tags per root appearance. Still open: does the game read only theep1one when Phantom Liberty is installed (the Studio assumes so)? - Does the base game itself mask body submeshes for
hide_,L1 hide_andS1 hide_, as the tag diagram lists?Genitals - What exactly does the garment assembler compute per vertex, and can the cooked
entGarmentParameterbe reused offline for a fixed combination of layers? - Answered from source (28 September): the XF Eye Artistry component is now
hx_. ArchiveXL reads its prefix asxfs_ c<key>_ makeup hx_, which its bundledhide_hides with the rest of the head, exactly as it hides vanilla makeup (Head hx_) [source:000_ pwa_ _ basehead_ makeup_ eyes_ 01 Garment/,Prefix.cpp VisualTags.xl; resource]. Before, the name wasxfs_with the prefixc<key>_ makeup xfs_, which no rule hides.hide_works only from a mod item'sHead .appdefinition; vanilla helmets don't carry it (§4.2) and cover the makeup instead. Still open: the in-game check (ask 4), which sessions 5 and 6 couldn't run because an active outfit hid the helmet (above); pipeline guide. - How does the Studio learn EquipmentEx's runtime-created slot records (and their offsets) without a mod-specific adapter: a runtime TweakDB snapshot through the bridge, or only the bridge's worn-item snapshot?
In-game test asks
Batch these into one prepared session; the save reader and resolver they compare against are built (phases 1–4, §6; see the backlog):
- A reference outfit per layer (inner shirt, jacket, trousers tucked into boots, a hat) in third person and photo mode, captured from fixed camera presets, against the Studio's render of the same save.
- The same V with a vanilla wardrobe set active and one area set to empty, to check the hide path.
- With EquipmentEx: an active outfit, then the bridge's worn-item snapshot compared with what the save alone predicts.
- A mod item tagged
hide_(and a vanilla helmet) over XF Eye Artistry makeup built with theHead hx_component (question 5); next-sessions plan 2.6. Equip both through the wardrobe screen: with the test save's outfit active, an item equipped only in the inventory doesn't draw (above). - A refit on one body mod, to check
{body}resolution.
Related pages
CC file chain · Head CC rendering · Body rendering · Mod loading · Materials and shaders · Archive and resource formats · Piercings and jewellery · Tattoos · Runtime access · Save import · Clothing render backlog