This resource is a python 3 script that reads the VAR references in your preset files, compares them with your VARs, and creates a list with the differences.
Latest version features:
- Appearance presets
- Clothing presets
- Hair presets
- UI for selecting what presets to check
Why would I want to use this script?
- If you're one of those people (like me) that periodically deletes VARs to keep VaM light, and then later on realises a few presets are now broken and look hideous, then this script may be useful to you.
- If you're a nut that keeps only the latest version of VARs (also like me) and want to have all presets referencing that latest version for... hummm... well, no practical reason but it's super clean and neat that way, then this script may be useful to you too.
- Because you can and need no reason to do what you want
⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠
I know only very very very basic Python, so what you see is a compilation of code that I wrote or found online. It's not likely a optimal way to do it and for sure needs some more care, but it seems to work for the intended purpose. I'll expand it and hopefully make it neater in following versions.
If you're experienced in Python - sorry if the current code makes your eyes bleed a little - feel free to suggest improvements or even make a better solution than this script I'm presenting.
⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠
I know only very very very basic Python, so what you see is a compilation of code that I wrote or found online. It's not likely a optimal way to do it and for sure needs some more care, but it seems to work for the intended purpose. I'll expand it and hopefully make it neater in following versions.
If you're experienced in Python - sorry if the current code makes your eyes bleed a little - feel free to suggest improvements or even make a better solution than this script I'm presenting.
⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠ ⚠
The python script
- Copy the python code to a simple text file and name it whatever you want, just make sure the extension is
.py
; - Place it in your VaM root folder; or update the script paths accordingly
- Run the python script.
Script actions in a nutshell:
- Lists all the VARs you have in AddonPackages, keeping only the name with no extension
- Lists all the VAR references in your presets with the same name pattern as above - local files are ignored
- Compares the lists and creates a new list with the differences - VAR references in presets that are not found as VARs in AddonPackages
- Creates a txt file in the VaM root folder with the list of differences
Python:
#!/usr/bin/python3
import time
import json
import re
from pathlib import Path
# Preset VAR checker
# lists missing references between your presets and your VARs
appearancefolder = Path("Custom/Atom/Person/Appearance")
clothingfolder = Path("Custom/Atom/Person/Clothing")
hairfolder = Path("Custom/Atom/Person/Hair")
addonpackages = Path("AddonPackages")
varlist = []
varnamelistAppearances = []
varnamelistClothing = []
varnamelistHairs = []
# list for VAR files in AddonPackages
def varsAP():
print("Creating a list of your VAR files")
for var in addonpackages.rglob("*.var"):
varclean = var.name
varsplit = re.split(r".var",varclean).pop(0)
varlist.append(varsplit)
global allvars
allvars = sorted(list(set(varlist)), key=str.casefold)
print("VAR list created")
# List for appearance presets
def appearancescheck():
print("Creating a list of referenced VARs in appearance presets")
for files in appearancefolder.rglob("*.vap"):
vap = open(files, "r", encoding="utf8")
data = json.load(vap)
clothing = data["storables"][0]["clothing"]
hair = data["storables"][0]["hair"]
morph = data["storables"][0]["morphs"]
for item in clothing:
if ":/" not in item["id"]:
continue
else:
varnamesplit = re.split(r":",item["id"]).pop(0)
varnamelistAppearances.append(varnamesplit)
for item in hair:
if ":/" not in item["id"]:
continue
else:
varnamesplit = re.split(r":",item["id"]).pop(0)
varnamelistAppearances.append(varnamesplit)
for item in morph:
if ":/" not in item["uid"]:
continue
else:
varnamesplit = re.split(r":",item["uid"]).pop(0)
varnamelistAppearances.append(varnamesplit)
# remainder objects in appearance preset
objAppearance = data["storables"]
for item in objAppearance:
for value in item.values():
if ":/" in value:
varnamesplit = re.split(r":",value).pop(0)
varnamelistAppearances.append(varnamesplit)
else:
continue
varnameAppearances = sorted(list(set(varnamelistAppearances)), key=str.casefold)
print("VAR list from appearance presets created")
# Comparison with appearance presets
print("Comparing lists...")
setdiff = set(varnameAppearances).difference(allvars)
finalList = sorted(list(setdiff), key=str.casefold)
txtfile = Path(r"appearances_diff.txt")
with open (txtfile ,'w') as f:
for item in finalList:
f.write(f"{item}\n")
f.close()
print("Comparison with appearance presets completed and exported to appearances_diff.txt")
# List for clothing presets
def clothingcheck():
print("Creating a list of referenced VARs in clothing presets")
for files in clothingfolder.rglob("*.vap"):
vap = open(files, "r", encoding="utf8")
data = json.load(vap)
clothing = data["storables"][0]["clothing"]
for item in clothing:
if ":/" not in item["id"]:
continue
else:
varnamesplit = re.split(r":",item["id"]).pop(0)
varnamelistClothing.append(varnamesplit)
objClothing = data["storables"]
for item in objClothing:
for value in item.values():
if ":/" in value:
varnamesplit = re.split(r":",value).pop(0)
varnamelistClothing.append(varnamesplit)
else:
continue
varnameClothing = sorted(list(set(varnamelistClothing)), key=str.casefold)
print("VAR list from clothing presets created")
# Comparison with clothing presets
print("Comparing lists...")
setdiff = set(varnameClothing).difference(allvars)
finalList = sorted(list(setdiff), key=str.casefold)
txtfile = Path(r"clothing_diff.txt")
with open (txtfile ,'w') as f:
for item in finalList:
f.write(f"{item}\n")
f.close()
print("Comparison with clothing presets completed and exported to clothing_diff.txt")
# List for hair presets
print("Creating a list of referenced VARs in hair presets")
def hairscheck():
for files in hairfolder.rglob("*.vap"):
vap = open(files, "r", encoding="utf8")
data = json.load(vap)
hairs = data["storables"][0]["hair"]
for item in hairs:
if ":/" not in item["id"]:
continue
else:
varnamesplit = re.split(r":",item["id"]).pop(0)
varnamelistHairs.append(varnamesplit)
objHairs = data["storables"]
for item in objHairs:
for value in item.values():
if ":/" in value:
varnamesplit = re.split(r":",value).pop(0)
varnamelistHairs.append(varnamesplit)
else:
continue
varnameHairs = sorted(list(set(varnamelistHairs)), key=str.casefold)
print("VAR list from hair presets created")
# Comparison with hair presets
print("Comparing lists...")
setdiff = set(varnameHairs).difference(allvars)
finalList = sorted(list(setdiff), key=str.casefold)
txtfile = Path(r"hairs_diff.txt")
with open (txtfile ,'w') as f:
for item in finalList:
f.write(f"{item}\n")
f.close()
print("Comparison with hair presets completed and exported to hairs_diff.txt")
# Options screen
def options():
print("\n" + "--- This script must be run from your VaM root folder ---" + "\n")
print("\n" + "--- VAR preset checker ---" + "\n")
print(" Choose an option:" + "\n")
print(" (1) Appearance presets" + "\n")
print(" (2) Clothing presets" + "\n")
print(" (3) Hair presets" + "\n")
print(" (4) All of the above" + "\n" + "\n")
print(" (0) Exit the script" + "\n")
option = input("Type the option number and press 'enter': ")
if option == "1":
print("Chosen option: Appearance presets" + "\n")
time.sleep(1)
varsAP()
appearancescheck()
print("Option 1 completed")
input("Press enter to return to the options screen")
options()
elif option == "2":
print("Chosen option: Clothing presets" + "\n")
time.sleep(1)
varsAP()
clothingcheck()
print("Option 2 completed")
input("Press enter to return to the options screen")
options()
elif option == "3":
print("Chosen option: Hair presets" + "\n")
time.sleep(1)
varsAP()
hairscheck()
print("Option 3 completed")
input("Press enter to return to the options screen")
options()
elif option == "4":
print("Chosen option: All of the above" + "\n")
time.sleep(1)
varsAP()
appearancescheck()
clothingcheck()
hairscheck()
print("Option 4 completed")
input("Press enter to return to the options screen")
options()
elif option == "0":
print("Chosen option: Exit the script" + "\n")
print("Bye bye")
time.sleep(1)
raise SystemExit
else:
print("Unknown option. Write only the digit shown for the option and press 'enter'." + "\n")
time.sleep(2)
options()
# Call options screen
options()
Running the script in Windows
You can either run the script in a Windows command line or in a linux command line (WSL). You'll need python 3.7 or higher.
Windows command line
Install python from python.org or any other way you prefer. Using Command Prompt or Powershell go to your VaM folder and run the script (eg:
python .\preset_checker.py
).Linux command line in Windows
For those who prefer a linux command prompt you can use WSL (Windows Subsystem for Linux) and Windows Terminal. Install python according to the distro you installed (eg. for Ubuntu:
sudo apt install python
), then go to your VaM folder and run the script (eg: python ./preset_checker.py
).I have a xxxxx_diff.txt file, what now?
These are the unmatched references in your presets with your VAR files. You either don't have the VAR anymore or have it with a different version than the one referenced. The txt file will not say which preset has the reference shown; it could be a single reference in one preset, multiple references in one preset, or even multiple presets with the reference.
For this task you need to use a search tool to look inside the presets for the listed VAR name. I use dnGrep, free and full of options for all kinds of search parameters. Unneeded for this task but useful, add in the archive formats list in the preferences the extension var so that it can also look inside VARs. I also suggest using Notepad++ to open multiple preset files found with dnGrep, if you intend to change the referenced VAR version in the preset files.
As mentioned above, you may still have a VAR listed in txt file but with a different version, a quick windows search for the name will make it clear which is.
Preset with references for a VAR that doesn't exist
If the references are important, like texture files, then either get back the VAR or delete the preset. If not important, like some tiny morph adjustment, there will be an error when you load the preset unless you remove the reference.
Preset with references for a existing VAR with a different version
Most version differences in presets still maintain the same path and filename, and VaM will use the latest available if the specific version is not found. This will likely not give you any error unless the path/name changed between versions, and probably is not noticeable in a visible way.
If you want to update it to the latest version you have, search first for the VAR and what version you have, then replace the older version number to the new one.
Presets with latest in the VAR version
There may be a few cases where instead of a digit you have a latest in the version place. This will use the highest version of the VAR you have but the script doesn't have a ignore statement for this situation. It's likely a rare find as when saving a local preset in VaM it will give it a specific version, so I skipped a workaround that would add complexity for a rare situation.
If you use this script let me know if everything is working correcty or tips to improve the code.
See ZRXS's guide "How to package like a pro".