summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-04-02 21:58:50 -0400
committerAndrew Opalach <andrew@akon.city> 2026-04-02 21:58:50 -0400
commitda80badc0e0acaa11459aefeb486f7e58c473eb3 (patch)
tree519e9ae648675fad3a9dae05895ca4f8731037e4
parent9bebe7aa2ea80df948338e8b8cee256f7b6454d9 (diff)
downloadNewCamera-da80badc0e0acaa11459aefeb486f7e58c473eb3.tar.gz
NewCamera-da80badc0e0acaa11459aefeb486f7e58c473eb3.tar.bz2
NewCamera-da80badc0e0acaa11459aefeb486f7e58c473eb3.zip
ModSlots better extract logic, add shuffle
Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--ModSlots/ArmorIDs.py3
-rwxr-xr-xModSlots/ModSlots.py211
-rw-r--r--ModSlots/slots.example.txt177
3 files changed, 289 insertions, 102 deletions
diff --git a/ModSlots/ArmorIDs.py b/ModSlots/ArmorIDs.py
index 1863f36..d37af35 100644
--- a/ModSlots/ArmorIDs.py
+++ b/ModSlots/ArmorIDs.py
@@ -48,5 +48,6 @@ ArmorIDs = {
'Brigade': ['pl036_0000', 'slg001_0000'],
'Diver': ['pl075_0000', 'slg000_0000'],
'Azure Starlord': ['pl072_0000', 'slg072_0000'],
- 'Azure Age': ['pl072_0001', 'slg072_0001']
+ 'Azure Age': ['pl072_0001', 'slg072_0001'],
+ 'Vaal Hazak (β)': ['pl032_0010', 'slg032_0000']
}
diff --git a/ModSlots/ModSlots.py b/ModSlots/ModSlots.py
index 5a4f216..6460f4c 100755
--- a/ModSlots/ModSlots.py
+++ b/ModSlots/ModSlots.py
@@ -6,12 +6,15 @@ import os
import os.path
import shutil
import re
+import struct
+import time
+import random
from enum import Enum
from hashlib import sha256
from zipfile import ZipFile
try:
- from unrardll import extract, names
+ import unrardll
RAR_SUPPORT = True
except ImportError:
RAR_SUPPORT = False
@@ -24,6 +27,9 @@ except ImportError:
from ArmorIDs import ArmorIDs
+def flatten(xss):
+ return [x for xs in xss for x in xs]
+
NATIVEPC_SUBDIRS = ('ec', 'hm', 'npc', 'pg', 'posteffect', 'sound', 'ui', 'village', 'accessory', 'em', 'light', 'otomo', 'photo', 'quest', 'system', 'unit_resource', 'common', 'event', 'ngword_list', 'pc', 'pl', 'shortcut', 'vfx', 'wp', 'plugins')
USAGE = f'Usage: {sys.argv[0]} <Write/Verify/Delete> <Slots.txt> <Path_to_Mods> <Path_to_Game>'
@@ -37,7 +43,6 @@ Slots = sys.argv[2]
Mods_Directory = sys.argv[3]
Game_Directory = sys.argv[4]
Extract_Directory = f'{Mods_Directory}/extract'
-Hash_Path = f'{Game_Directory}/mod_slots_hash.txt'
List_Path = f'{Game_Directory}/mod_slots.txt'
if not os.path.isfile(Slots):
@@ -59,8 +64,8 @@ if not os.path.isdir(Game_Directory):
print(f'Directory {Game_Directory} doesn\'t exist.')
sys.exit(1)
-if Mode == 'verify' and not os.path.isfile(Hash_Path):
- print(f'Verification requested but previous hash not found at {Hash_Path}.')
+if Mode == 'verify' and not os.path.isfile(List_Path):
+ print(f'Verification requested but previous hash not found at {List_Path}.')
sys.exit(1)
Error_Messages = []
@@ -68,11 +73,20 @@ Error_Index = 1
def Error(msg):
global Error_Messages
global Error_Index
- msg = f'{Error_Index}: {msg}'
+ msg = f'E(#{Error_Index}): {msg}'
Error_Messages.append(msg)
Error_Index += 1
print(msg + '\n' + '^'*len(msg))
+Warning_Index = 1
+def Warn(msg):
+ global Error_Messages
+ global Warning_Index
+ msg = f'W(#{Warning_Index}): {msg}'
+ Error_Messages.append(msg)
+ Warning_Index += 1
+ print(msg + '\n' + '^'*len(msg))
+
class ArchiveType(Enum):
ZIP = 0
RAR = 1
@@ -80,6 +94,7 @@ class ArchiveType(Enum):
class Mod():
def __init__(self, path):
+ self.path = path
self.name = path.split('/')[-1]
m = sha256()
m.update(open(path, 'rb').read())
@@ -94,17 +109,32 @@ class Mod():
continue
self.files.append(file.filename)
elif ext == 'rar':
- if not SEVEN_ZIP_SUPPORT:
- raise Exception("Use of .rar archive without required package (unrardll).")
+ if not RAR_SUPPORT:
+ raise Exception('Use of .rar archive without required package (unrardll).')
self.type = ArchiveType.RAR
+ for header in list(unrardll.headers(path)):
+ if not header['is_dir']:
+ self.files.append(header['filename'])
elif ext == '7z':
if not SEVEN_ZIP_SUPPORT:
- raise Exception("Use of .7z archive without required package (py7zr).")
+ raise Exception('Use of .7z archive without required package (py7zr).')
self.type = ArchiveType.SEVEN_ZIP
self.obj = SevenZipFile(path, 'r')
for file in self.obj.list():
if not file.is_directory:
self.files.append(file.filename)
+ # Filter paths to fix automatic processing.
+ self.raw_files = self.files.copy()
+ for i, file in enumerate(self.files):
+ npc = file.lower().find('nativepc')
+ if npc >= 0:
+ cut = file[npc:npc + len('nativePC')]
+ if cut != 'nativePC':
+ print(f'Correcting nativePC capitalization in {file}.')
+ self.files[i] = file.replace(cut, 'nativePC')
+ self.raw_map = {}
+ for i in range(0, len(self.files)):
+ self.raw_map[self.files[i]] = self.raw_files[i]
self.roots = []
self.ignored = []
self.overrides = {}
@@ -136,9 +166,9 @@ class Mod():
def maybe_strip_root(self, file):
for root in self.roots:
if file.startswith(root):
- nativePC = file.find('nativePC')
- if nativePC >= 0:
- return file[nativePC:], root
+ npc = file.find('nativePC')
+ if npc >= 0:
+ return file[npc:], root
return file, None
def do_map(self, All_Files, Global_Overrides):
@@ -163,7 +193,8 @@ class Mod():
if len(target.split('/')[-1].split('.')) == 1:
print(f'Warning: Possible non-direct path as a direct override: {target}')
targets.append(target)
- elif not has_global_override: # Could switch to else.
+ # Add file as-is unless it could override an explicit target of a different file.
+ elif base not in flatten(self.overrides.values()) and not has_global_override:
targets.append(base)
# Substitute armor names, like '[Anja]', with their IDs.
for i, target in enumerate(targets):
@@ -217,43 +248,52 @@ class Mod():
output = f'{os.path.join(Extract_Directory, self.hash)}'
if not os.path.isdir(output):
os.mkdir(output)
+ do_extract = False
+ on_disk = [os.path.join(root, file) for root, dirs, files in os.walk(output) for file in files]
+ for key in self.map:
+ file = os.path.join(output, self.raw_map[key])
+ if file not in on_disk:
+ do_extract = True
+ break
+ if do_extract:
if self.type == ArchiveType.ZIP:
self.obj.extractall(path=output)
+ elif self.type == ArchiveType.RAR:
+ unrardll.extract(self.path, output)
elif self.type == ArchiveType.SEVEN_ZIP:
self.obj.extractall(path=output)
for key in self.map:
- m.update(open(os.path.join(output, key), 'rb').read())
+ m.update(open(os.path.join(output, self.raw_map[key]), 'rb').read())
- def write(self, old_list):
+ def write(self, old_list, list_file):
output = f'{os.path.join(Extract_Directory, self.hash)}'
- with open(List_Path, 'a+') as l:
- for key in self.map:
- targets = self.map[key]
- for target in targets:
- copy = target[0] == '|';
- if copy:
- target = target[1:]
- if target in old_list:
- old_list.remove(target)
- l.write(target + '\n')
- inc = Game_Directory
- for s in target.split('/')[:-1]:
- inc = os.path.join(inc, s)
- if not os.path.isdir(inc):
- os.mkdir(inc)
- target = os.path.join(Game_Directory, target)
- if os.path.islink(target):
- os.remove(target)
- elif os.path.isfile(target) or os.path.isdir(target):
- if not copy:
- Error(f'Not replacing/removing non-symlink file: {target}')
- continue
- file = os.path.join(output, key)
- if copy:
- print(f'{file}\n (detached) -> {target}')
- shutil.copyfile(file, target)
- else:
- os.symlink(file, target)
+ for key in self.map:
+ targets = self.map[key]
+ for target in targets:
+ copy = target[0] == '|';
+ if copy:
+ target = target[1:]
+ if target in old_list:
+ old_list.remove(target)
+ list_file.write(target + '\n')
+ inc = Game_Directory
+ for s in target.split('/')[:-1]:
+ inc = os.path.join(inc, s)
+ if not os.path.isdir(inc):
+ os.mkdir(inc)
+ target = os.path.join(Game_Directory, target)
+ if os.path.islink(target):
+ os.remove(target)
+ elif os.path.isfile(target) or os.path.isdir(target):
+ if not copy:
+ Warn(f'Not replacing/removing non-symlink file: {target}')
+ continue
+ file = os.path.join(output, self.raw_map[key])
+ if copy:
+ print(f'{file}\n (detached) -> {target}')
+ shutil.copyfile(file, target)
+ else:
+ os.symlink(file, target)
def delete(self):
for key in self.map:
@@ -263,7 +303,7 @@ class Mod():
if os.path.islink(target):
os.remove(target)
elif os.path.isfile(target) or os.path.isdir(target):
- Error(f'Not deleting non-symlink file: {target}')
+ Warn(f'Not deleting non-symlink file: {target}')
def is_armor_override(line):
sp = line.split('/')
@@ -275,20 +315,62 @@ def is_armor_override(line):
return True
return False
+Seed = time.time()
+if Mode == 'write':
+ random.seed(Seed)
+
+with open(List_Path, 'a+') as l:
+ l.seek(0)
+ old_list = l.read().splitlines()
+ if len(old_list) > 2:
+ old_hash = old_list[0]
+ try:
+ old_seed = struct.unpack('!d', bytes.fromhex(old_list[1]))[0]
+ except ValueError:
+ Error(f'Invalid seed \'{old_list[1]}\' in {List_Path}.')
+ else:
+ if Mode == 'verify':
+ random.seed(old_seed)
+ old_list = old_list[2:]
+ if Mode == 'write':
+ l.truncate(0)
+
Mods = []
+Shuffle = None
Current_Mod = None
Current_Override = None
Global_Overrides = {}
with open(Slots, 'r') as f:
- for line in f.read().splitlines():
+ for ln, line in enumerate(f.read().splitlines()):
if len(line) == 0 or line[0] == '#':
continue
if line == 'XXX':
break
- if line[0] == ';':
+ if line[0] in [';', 's']:
if Current_Mod:
Mods.append(Current_Mod)
Current_Mod = None
+ if line[0] == 's':
+ Shuffle = []
+ path = os.path.join(Mods_Directory, line[1:])
+ if os.path.isfile(path):
+ Shuffle.append(path)
+ else:
+ Error(f'File not found: {path}')
+ continue
+ elif line[0] == '+':
+ path = os.path.join(Mods_Directory, line[1:])
+ if os.path.isfile(path):
+ Shuffle.append(path)
+ else:
+ Error(f'File not found: {path}')
+ continue
+ elif line[0:4].upper() == 'ROLL':
+ Current_Mod = Mod(random.choice(Shuffle))
+ Current_Override = None
+ Shuffle = None
+ continue
+ if line[0] == ';':
path = os.path.join(Mods_Directory, line[1:])
if os.path.isfile(path):
Current_Mod = Mod(path)
@@ -296,7 +378,7 @@ with open(Slots, 'r') as f:
else:
Error(f'File not found: {path}')
continue
- cmd = ''
+ cmd = None
if line[0] == '&':
cmd = 'root'
elif line[0] == '<':
@@ -313,16 +395,21 @@ with open(Slots, 'r') as f:
line = line[1:]
if line.startswith(NATIVEPC_SUBDIRS):
line = 'nativePC/' + line
+ else:
+ continue
if cmd == 'root' and Current_Mod:
Current_Mod.roots.append(line)
elif cmd == 'ignore' and Current_Mod:
Current_Mod.ignored.append(line)
elif cmd == 'detach' and Current_Mod:
- Current_Mod.ignored.append(line)
- Current_Mod.map[line] = [f'|{line}']
+ if line not in Current_Mod.files:
+ Error(f'File not found: {Current_Mod.name}:{line}')
+ else:
+ Current_Mod.ignored.append(line)
+ Current_Mod.map[line] = [f'|{line}']
elif cmd == 'override' and Current_Mod:
if not (is_armor_override(line) or line in Current_Mod.files):
- Error(f'File not found: {line}')
+ Error(f'File not found: {Current_Mod.name}:{line}')
else:
Current_Override = line
Current_Mod.overrides[Current_Override] = []
@@ -353,29 +440,23 @@ for mod in Mods:
mod.do_map(All_Files, Global_Overrides)
mod.add_to_hash(M)
-with open(List_Path, 'a+') as l:
- l.seek(0)
- old_list = l.read().splitlines()
- if Mode == 'write':
- l.truncate(0)
-
if Mode == 'write':
- for mod in Mods:
- mod.write(old_list)
+ with open(List_Path, 'a+') as l:
+ l.write(M.hexdigest() + '\n')
+ l.write(hex(struct.unpack('!Q', struct.pack('!d', Seed))[0])[2:] + '\n')
+ for mod in Mods:
+ mod.write(old_list, l)
for old in old_list:
old_target = os.path.join(Game_Directory, old)
if os.path.islink(old_target):
print(f'Removing old link: {old_target}')
os.remove(old_target)
- with open(Hash_Path, 'w+') as f:
- f.write(M.hexdigest())
+
if Mode == 'verify':
- if os.path.isfile(Hash_Path):
- with open(Hash_Path, 'r') as f:
- if f.read() == M.hexdigest():
- print('Verification passed!')
- else:
- print('Verification failed.')
+ if old_hash == M.hexdigest():
+ print('Verification passed!')
+ else:
+ print('Verification failed.')
elif Mode == 'delete':
for mod in Mods:
mod.delete()
diff --git a/ModSlots/slots.example.txt b/ModSlots/slots.example.txt
index 76cb440..06de84d 100644
--- a/ModSlots/slots.example.txt
+++ b/ModSlots/slots.example.txt
@@ -12,18 +12,23 @@
<plugins/QuestLoader.dll
:dinput8.dll
:loader.dll
-:loader-config.json
+# Copy loader-config.json instead of linking it so any edits save to the game directory not the extracted mod folder.
+|loader-config.json
;SharpPluginLoader-0.0.7.2-linux.zip
:msvcrt.dll
+# DPSTickFix
+#;Fatalis Patch-3474-1-4-1601540952.zip
+# BetterInputs
+#;Fatalis Patch-4333-1-4-1601540927.zip
+
;2024 Patch 15.23 1.25-3473-1-25-1728572867.zip
;MHWI - Hide main menu ad banner-7826-1-0-1728933976.zip
-;MHWNewCamera_3.0.zip
-# Copy MHWNewCamera.json instead of symlink so our edits save to the game directory not the extracted mod folder.
-|plugins/CSharp/MHWNewCamera.json
+#;MHWNewCamera_3.0.zip
+#|plugins/CSharp/MHWNewCamera.json
;Fix_Tent_Animation_Face_Deform_v2.zip
@@ -42,35 +47,38 @@ XXX
:pl/f_equip/pl002_0000/wst/mod/Unshaved.mod3
>pl/f_equip/pl002_0000/wst/mod/f_wst002_0000.mod3
-;Version1.0 Normal Fishnet for Highpoly Nude MOD Version 4-5030-1-0-1610636569.zip
-<pl/f_equip/pl002_0000/body
-:pl/f_equip/pl002_0000/arm
->[Hornetaur]
-:pl/f_equip/pl002_0000/wst
->[Hornetaur]
-:pl/f_equip/pl002_0000/leg
->[Hornetaur]
+#;Version1.0 Normal Fishnet for Highpoly Nude MOD Version 4-5030-1-0-1610636569.zip
+#<pl/f_equip/pl002_0000/body
+#:pl/f_equip/pl002_0000/arm
+#>[Hornetaur]
+#:pl/f_equip/pl002_0000/wst
+#>[Hornetaur]
+#:pl/f_equip/pl002_0000/leg
+#>[Hornetaur]
-;Ver1.0 Pantie for Highpoly Nude MOD-3465-1-0-1589948707.zip
+#;Ver1.0 Pantie for Highpoly Nude MOD-3465-1-0-1589948707.zip
+;Ver2.0 HPNM V4 Pantie for Highpoly Nude MOD-3465-2-0-1605825156.rar
<pl/f_equip/HPNM_KirinPantie/CMM_Normal.tex
<pl/f_equip/HPNM_KirinPantie/CMM_Stripe.tex
<pl/f_equip/HPNM_KirinPantie/CMM_PolkaDot.tex
-:Pantie_ColorMaskMap/Translucent/CMM_TransVKS_Pantie_Palico.mrl3
+:Pantie_ColorMaskMap/CMM_Pantie_Palico.mrl3
>pl/f_equip/pl002_0000/wst/mod/f_wst002_0000.mrl3
:pl/f_equip/pl002_0000/wst
>[Kirin]
-;Ver3.1 SmallTitsVer_Highpoly Nude MOD with jiggle animation (Iceborne Compatible )-1965-3-1-1586846362.zip
-<pl/f_equip/HighPolyNudeModTextures/ColorUnchangeable_Nail_CMM.tex
-<pl/f_equip/HighPolyNudeModTextures/Nail_BML.tex
-<pl/f_equip/HighPolyNudeModTextures/Nail_CMM.tex
-<pl/f_equip/HighPolyNudeModTextures/Nail_Disable_snow_Col_CMM.tex
-<pl/f_equip/HighPolyNudeModTextures/Nail_NM.tex
-<pl/f_equip/HighPolyNudeModTextures/Nail_RMT.tex
-#:pl/f_equip/pl501_0000/body/mod/f_body501_0000.ctc
-#*pl/f_equip/pl[0-9]+_[0-9]+/body/mod/f_body[0-9]+_[0-9]+.ctc
-#:pl/f_equip/pl501_0000/leg/mod/f_leg501_0000.ctc
-#*pl/f_equip/pl[0-9]+_[0-9]+/leg/mod/f_leg[0-9]+_[0-9]+.ctc
+;Ver4.2 Small_Highpoly Nude MOD with jiggle animation-1965-4-2-1607065917.rar
+#;Ver3.1 SmallTitsVer_Highpoly Nude MOD with jiggle animation (Iceborne Compatible )-1965-3-1-1586846362.zip
+<pl/f_face/
+#<pl/f_equip/HighPolyNudeModTextures/ColorUnchangeable_Nail_CMM.tex
+#<pl/f_equip/HighPolyNudeModTextures/Nail_BML.tex
+#<pl/f_equip/HighPolyNudeModTextures/Nail_CMM.tex
+#<pl/f_equip/HighPolyNudeModTextures/Nail_Disable_snow_Col_CMM.tex
+#<pl/f_equip/HighPolyNudeModTextures/Nail_NM.tex
+#<pl/f_equip/HighPolyNudeModTextures/Nail_RMT.tex
+##:pl/f_equip/pl501_0000/body/mod/f_body501_0000.ctc
+##*pl/f_equip/pl[0-9]+_[0-9]+/body/mod/f_body[0-9]+_[0-9]+.ctc
+##:pl/f_equip/pl501_0000/leg/mod/f_leg501_0000.ctc
+##*pl/f_equip/pl[0-9]+_[0-9]+/leg/mod/f_leg[0-9]+_[0-9]+.ctc
#;Atrarch's Namielle For Dytsers Armor's-4490-4-0-1630065481.zip
#<pl/f_equip/pl
@@ -86,11 +94,11 @@ XXX
#:4k face textures/f_face_edit_NM.tex
#>pl/f_face/face000/mod/f_face_edit_NM.tex
-;Skin Glossiness Options.zip
-:skin_BM_dry.tex
->pl/f_equip/mangie_foam/skin_BM.tex
->pl/f_equip/mangie_fortune/skin_BM.tex
-*pl/f_equip/mangie/.*/skin.*_BM.tex
+#;Skin Glossiness Options.zip
+#:skin_BM_dry.tex
+#>pl/f_equip/mangie_foam/skin_BM.tex
+#>pl/f_equip/mangie_fortune/skin_BM.tex
+#*pl/f_equip/mangie/.*/skin.*_BM.tex
;Bikini Collections - Barefoot-2891-1-0-1584527076.zip
<pl/f_equip/pl
@@ -108,8 +116,8 @@ XXX
;Lewd Lingerie.zip
>[Jyura]
-;Secret Report.zip
->[Brigade]
+#;Secret Report.zip
+#>[Brigade]
;Eternal Summer.zip
# The uncensored files will be used with this order.
@@ -144,14 +152,111 @@ XXX
;Dark Widow.zip
>[Jyura+]
-;DOAXVV Angel_s Smile.zip
->[Diver]
-
;Lake Elven.zip
>[Azure Age]
;DOAXVV Foam Bikini.zip
>[Jagras]
-;Pistachio.zip
+;DOAXVV Cure Angel.zip
>[Jagras+]
+
+sBamboo Shoot.zip
++White Prince.zip
++Kaine.zip
++Nyo Latex.zip
++DOA6 Tamaki Bikini.zip
++Hebijo Shinobi Costume.zip
++Honoka Newcomer Costume.zip
++Leifang Yin Yang.zip
++Poll Winner - Black Rubber.zip
++Swimsuit.zip
++Sailor Outfit.zip
++Queen Marika.zip
++Bunny Girl Costume 3.zip
++Sailor Swimsuit.zip
++Desert Costume.zip
++2nd Design Contest Swimsuit.zip
++DOA6 Bunny Girl Costumes.zip
++DOA5LR Kasumi_s Battlesuit.zip
++Rabbit Joker.zip
++Princess Warrior.zip
++Pub Lass Bunny Girl Costume.zip
++Pistachio.zip
++DOA5LR Momiji Kimono.zip
++Stellar Scorpion.zip
++Destiny Child Mona Costume.zip
++Lingerie.zip
++Demonlord.zip
++Secret Report.zip
++DOAXVV Angel_s Smile.zip
++Tifa Mature Outfit.zip
++DOA5LR Rachel_s Halloween Costume.zip
++Bunny Girl Costume 1.zip
++2023 Halloween Costume.zip
++DOA6 Leather Jacket.zip
++DOAXVV Martini.zip
++Dark Widow.zip
++Fraise Noel.zip
++Nyo Pirate.zip
++Code Rouge.zip
++Venus Valkyrie.zip
++Venus Nine.zip
++Noble Tutu.zip
++Black Lily.zip
++DOA5LR Christie Halloween Costume.zip
++Endorphine Sky.zip
++DOAXVV Fortune.zip
++White Summer Bikini.zip
++Ice Dress.zip
++Lewd Sailor.zip
++Bunny Girl Costume 2.zip
++Peace.zip
++Rachel Crop Top.zip
++Marionette.zip
++Luminate Tube.zip
++Kunimitsu Outfit.zip
++Mikomai.zip
++Venus Cage.zip
++Lake Elven.zip
++Mood For Love.zip
++DOAXVV Foam Bikini.zip
++Aventure.zip
++Dragon Leotard.zip
++Christie_s Elegance Dress.zip
++DOAXVV Noctilca.zip
++Crimson Sakura.zip
++Nyotengu_s Halloween Costume.zip
++Momiji Costume.zip
++Fur Bikini.zip
++Claire Noir.zip
++DOAXVV Cure Angel.zip
++Auspicious Blossom.zip
++Momo Denim.zip
++Appetizer Shrimp.zip
++Ada Wong Dress.zip
++Christmas Gift.zip
++Jewel Topaz.zip
++DOA5LR Leifang_s Black Skirt.zip
++Hinode.zip
++Alluring Mandarin.zip
++Neoncrow.zip
++1st Swimsuit Contest.zip
++DOA6 Kunoichi Costumes (Ayane, Kasumi).zip
++Rachel Fiend Costume.zip
++Midnight Light.zip
++DOA6 Christie_s Costume.zip
++DOAXVV Velvet Time Pearl.zip
++Crystal.zip
++Kunoichi - Kasumi.zip
++Oppression.zip
++DOAXVV Asari.zip
++DOAXVV Narwhal.zip
++DOA6 Summer Breeze.zip
++Kasumi Award Outfit.zip
++Succubus Damaged.zip
++Lewd Lingerie.zip
++Kunoichi - Ayane.zip
++Nier Automata YoRHa 2B.zip
+ROLL
+>[Diver]