• Hi Guest!

    We have posted a new VaM2 dev log on Patreon, starting a monthly cadence of written progress updates between Beta releases. Highlights include the new Gizmos System, Selection Carousel, and Modes System with Context-Specific Editing. Beta1.2 is 15 of 21 items complete.

    Read the full post on Patreon, or follow progress on the public Trello roadmap.
  Contains executable files or external scripts. Ensure you trust the creator and apply your own security measures.
Drop a .var, .vaj, or folder — every item gets your plugin auto-loaded in VaM.

Plugin Injector is a Windows desktop tool for VaM creators who package hair or clothing as .var files. It bakes a plugin preset into your package so that whenever a user loads your item, the plugin is already attached with the parameters you set. Nobody has to touch the Plugins tab.

What it does for every item it processes
  • Makes sure the MVRPluginManager component is listed in the item's .vaj, adding it if it is missing.
  • Adds or updates a plugin storable in the .vaj whose id is the item's own UID (read from the matching .vam) for clothing, or the hard-coded CustomScalp id for hair. The storable's plugins.plugin#0 field is set to your plugin path.
  • Saves the modified package as a new .var inside a PluginInjectorFixed/ subfolder. The original file is left alone.

Useful when your hair pack depends on a custom shader, when your clothing needs a specific physics or material plugin to look right, or any time you would rather have "load look, plugin runs" than tell users to install something separately.

02_主界面三张卡_WhatItDoes之后.png

The window has three action cards (Inject Plugin, Fix for Release, Fix Item Gender), a plugin selector row up top, a drop zone, and the VaM Root setting tucked under a collapsible section.

The tool injects plugins. It does not write them. You still need a .cs, a .cslist, or a .var that contains the plugin code.



Install
  1. Download the attached plugin-injector.zip and extract plugin-injector.exe from it.
  2. Put the exe anywhere. Single-file Tauri build, no installer, no extra DLLs.
  3. Double-click to run.

Windows SmartScreen will probably complain on first launch because the binary is not signed. Click "More info", then "Run anyway".



First-time setup: VaM Root

Click the VaM Root header to expand it, then drop your VaM folder (the one with VaM.exe and AddonPackages\ in it) onto the input. Typing or pasting the path works too.

03_VaMRoot展开_VaMRoot段落.png

What this is for: when you later run Fix for Release, the tool walks your disk looking for .clothingplugins preset files that match the plugins inside the package, and bundles the matches into the output. The lookup is rooted at this path. For each .vaj in the package at, say, Custom/Clothing/Female/lulu/myitem/, it checks <VaM Root>/Custom/Clothing/Female/lulu/myitem/ on disk.

If your .var already lives inside your VaM install (a .var under AddonPackages\), the tool auto-derives the root by walking up to find a Custom/ folder, so you can usually leave this empty. Set it explicitly when you process .var files from outside your VaM tree, for example a build or dist folder.

The Inject Plugin and Fix Item Gender flows do not use this at all. They work the same whether VaM Root is set or empty.

Settings persist across runs, including your saved plugin presets and which one is active.

Other header buttons
  • Dark / Light: theme toggle.
  • History: side panel with past inject and fix runs.
  • Pin: keep the window on top while you work in VaM.
  • 中文 / EN: UI language.

04_深色主题_Other header buttons之后.png



Main workflow: Inject Plugin

1. Set up your plugin

First launch ships with one preset already loaded: Stopper - ClothingPluginManager (path Stopper.ClothingPluginManager.latest:/Custom/Scripts/Stopper/ClothingPluginManager/ClothingPluginManager.cs). If that is the plugin you want to inject, you can skip this step entirely and jump to step 2.

Drop your plugin onto the top Drop to load plugin zone. It takes .cs, .cslist, or a .var that contains plugins. There is also a SRC field on the right if you would rather paste a path.

Save it as a named preset using the disk icon next to the input. Once saved, you can grab it later from the PRESETS dropdown.

05_预设已选_Step1段落.png

2. Drop a target onto Inject Plugin

Drop a target onto the Inject Plugin card. Three input types are accepted:
  • A packaged .var — the tool reads every .vaj inside.
  • A loose .vaj file — processed directly.
  • A folder — every .vaj found inside (recursively) is processed.
Items show up in a selectable list, grouped by category.

The toolbar gives you:
  • Select all / None to flip everything at once.
  • A Clothing / Hair filter to narrow by category.
  • Clicking a single row to toggle just that entry.

The action bar at the bottom shows Inject into N files.

06_文件列表待注入_Step2段落.png

3. Inject

Click Inject into N files. The file list is replaced by a result panel that reports what happened to each .vaj. These are the same messages that end up in the History panel.

07_注入结果PackageSaved_Step3段落.png

For each .vaj you will see one headline plus a couple of sub-notes:

Headline (top of the per-file card):
  • Injected successfully. — the .vaj was actually changed.
  • Already up to date. — nothing needed changing. This is what you see on a clean re-run with the same preset.

Sub-notes (under the headline):
  • Added MVRPluginManager component. — the item did not have it yet.
  • MVRPluginManager component already present. — the item already had it.
  • Added plugin storable. — no existing storable for this plugin, a fresh one was added.
  • Updated existing storable. — a storable was there but with different parameters; it got overwritten. This is a real change, not a no-op.
  • Storable ID & plugin path are correct. — existing storable matched exactly, left alone.
  • Fixed JSON formatting (trailing commas removed). — the original .vaj had non-strict JSON that got cleaned up while being rewritten.

And one final line per package: Package saved. (logged against Author.Package.Version.var)

4. Where the output goes

The new .var is written to a PluginInjectorFixed/ subfolder next to the original. So if you drop F:\my-stuff\lulu.fgqy.1.var, you get F:\my-stuff\PluginInjectorFixed\lulu.fgqy.1.var. Same filename, same Author.Package.Version. The original is not touched.

Drop the new file into VaM's AddonPackages\ to test, or share it directly.

5. History

The History panel keeps every inject and fix run as a session you can expand. Handy when you want to look back and check what you did to which package.

The trash icon at the top wipes everything. Expanding a session brings back the per-file detail.

08_历史面板_Step5段落收尾.png



The other two cards

Fix for Release

Drop a .var onto this card before you publish. It does two cleanup passes:
  • Namespaces every Custom/* path in .vaj and .vap files to Author.Package.latest:/Custom/*. Skip this and the package will quietly fall back to the user's local Custom/ folder, which breaks for anyone who does not happen to have the same files already installed. Note the .latest part: the rewrite always uses latest, never the exact version number from the filename. That way, when you ship version 2 of the package, existing references still resolve to whichever version the user has installed.
  • Bundles any .clothingplugins preset files it finds on disk that reference plugins already used in the package's .vaj files. The disk lookup is rooted at the VaM Root setting (see First-time setup). The summary at the top of the result panel always echoes which root was actually used, so you can verify it was the one you expected.

Output lands in the same PluginInjectorFixed/ subfolder.

Fix Item Gender

Drop a .var (or a folder) here to fix the itemType field inside .vam files so it matches the gender of the directory it lives in (Female or Male). The case that bites people: an item ended up in the wrong gender folder during export, and now VaM's category filter is hiding it.



Common errors


Common errors and fixes

MessageCauseFix
Invalid .var filename: expected Author.Package.VersionFile is not named Author.Package.Version.varRename to VaM's convention before processing
Cannot open .var: / Couldn't open .var file.File is locked, missing, or unreadableClose VaM (it might be holding the file), then check the path
Cannot read .var as zip: / Couldn't read .var as ZIP.Not a valid zip, or corruptRe-export the .var from VaM's Package Builder
No .vam file found in <path>Package contains no recognized hair or clothing .vamWrong package type. The tool only handles hair and clothing
Not a hair/clothing .vam — skipped.The selected item is not hair or clothingPick a different item
No "storables" array in .vaj.The .vaj is malformed (missing a required field)Re-save the item in VaM to regenerate a valid .vaj
Invalid .vap file. / Missing "storables" property.The preset file is broken, or was not saved from VaMRe-save the .vap from the item's Plugins Preset tab
Invalid UTF-8 in .vap / .vam / .clothingplugins.File contains non-UTF-8 bytes (rare, usually a bad text editor)Re-save from VaM, or convert to UTF-8 without BOM
Couldn't serialize .vam. / Couldn't write .vap / .vam file.Disk write failedCheck disk space and write permissions on the output directory
Couldn't create output directory. / Couldn't create output .var. / Failed to finalize output .var.Output cannot be createdCheck write permissions, or try a different output location
Failed to inject plugin. / Injection failed.Generic injection failureThe real reason is on the line above this one. Read that
Failed to process .vaj.One specific .vaj blew up mid-processOther items in the package still finish. Inspect that one .vaj
Skipped — id does not match any storable/plugin in .vaj files.The targeted id is not in any .vajCheck the preset was saved against the same item you are injecting into
Couldn't read .clothingplugins.An existing .clothingplugins inside the package is malformedOpen the package, delete the bad .clothingplugins, retry
Cannot determine config directoryThe app cannot find a writable settings folderRun as the same Windows user that owns VaM. Check %APPDATA% access





Notes
  • The original .var is never modified. Output always goes into a PluginInjectorFixed/ subfolder next to the source.
  • Running it twice on the same package with the same preset is a no-op. The headline becomes Already up to date. and nothing is written into the storable. Updated existing storable. means something actually changed (the storable was there but with different parameters and got overwritten), so use that as a signal that this re-run was not idempotent.
  • The plugin code still has to ship somewhere. The injector adds a reference, not the code itself. The .cs or .cslist should be inside the same .var (best), or in a dependency .var the package references.
  • The VaM Root setting only matters for namespacing. The inject flow works fine without it.



Credits

Built by lulu. Bug reports and feedback welcome on the hub thread.

Based on qdaro's Clothing Plugins Util, which the desktop scaffolding is forked from. Huge thanks to qdaro for the original work.
  Contains executable files or external scripts. Ensure you trust the creator and apply your own security measures.
Images and attachments
  • 01_顶部封面_紧贴Tagline之前.png
    01_顶部封面_紧贴Tagline之前.png
    7.5 MB · Views: 0
React to this content...

Share this resource

More resources from lulu_c137

Back
Top Bottom