summaryrefslogtreecommitdiff
path: root/ModSlots
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-07-29 20:05:57 -0400
committerAndrew Opalach <andrew@akon.city> 2025-07-29 20:05:57 -0400
commitf5aa1aa96db5178c788341c5e45bc7b5b477354b (patch)
treeef4af216a775d1553b5025a21c3ac80c8c7722b6 /ModSlots
parenta9d16fd96dd6e9db075d6a5458ab7f72ca733715 (diff)
downloadNewCamera-f5aa1aa96db5178c788341c5e45bc7b5b477354b.tar.gz
NewCamera-f5aa1aa96db5178c788341c5e45bc7b5b477354b.tar.bz2
NewCamera-f5aa1aa96db5178c788341c5e45bc7b5b477354b.zip
Fix fov, list all cameras, add ModSlots
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'ModSlots')
-rw-r--r--ModSlots/ArmorIDS.py355
-rwxr-xr-xModSlots/ModSlots.py261
-rw-r--r--ModSlots/cmd.txt1
-rw-r--r--ModSlots/requirements.txt2
4 files changed, 619 insertions, 0 deletions
diff --git a/ModSlots/ArmorIDS.py b/ModSlots/ArmorIDS.py
new file mode 100644
index 0000000..8526ab6
--- /dev/null
+++ b/ModSlots/ArmorIDS.py
@@ -0,0 +1,355 @@
+# https://github.com/Ezekial711/MonsterHunterWorldModding/wiki/Armor-IDs
+# Macro reformat, Remove None/Unavailable, sort u
+# Duplicates:
+# I guess this means the list has lots of errors, unless I'm missing something.
+# It would take a while to verify all this.
+'''
+ 'pl001_0000': 'Skull',
+ 'pl001_0000': 'Bushi "Homare"',
+ 'pl001_0000': 'Bushi "Sabi"',
+ 'pl001_0000': 'Unavailable',
+ 'pl002_0000': 'Hunter',
+ 'pl002_0010': 'Hunter',
+ 'pl003_0000': 'Bone',
+ 'pl004_0000': 'Alloy',
+ 'pl005_0000': 'Kestodon',
+ 'pl006_0000': 'Gajau',
+ 'pl009_0000': 'Kulu',
+ 'pl009_0001': 'Kulu+',
+ 'pl010_0000': 'Pukei',
+ 'pl010_0001': 'Pukei+',
+ 'pl010_0010': 'Pukei',
+ 'pl011_0000': 'Jyura',
+ 'pl011_0001': 'Jyura+',
+ 'pl013_0000': 'Kadachi',
+ 'pl013_0001': 'Kadachi+',
+ 'pl014_0000': 'Chain',
+ 'pl015_0000': 'Gastodon',
+ 'pl016_0000': 'Barnos',
+ 'pl019_0000': 'Butterfly',
+ 'pl020_0000': 'Anja',
+ 'pl020_0001': 'Anja+',
+ 'pl020_0010': 'Anja',
+ 'pl020_0011': 'Anja+',
+ 'pl022_0000': 'Girros',
+ 'pl022_0001': 'Girros+',
+ 'pl023_0000': 'Tzitzi',
+ 'pl023_0001': 'Tzitzi+',
+ 'pl024_0000': 'Lumu',
+ 'pl024_0001': 'Lumu+',
+ 'pl025_0000': 'High Metal',
+ 'pl028_0000': 'Baan',
+ 'pl028_0001': 'Baan+',
+ 'pl028_0010': 'Baan',
+ 'pl031_0000': 'Ingot',
+ 'pl037_0000': 'Unavailable',
+ 'pl038_0000': 'Zorah',
+ 'pl038_0010': 'Zorah',
+ 'pl042_0000': 'Bazel',
+ 'pl042_0010': 'Bazel',
+ 'pl043_0000': 'Damascus',
+ 'pl043_0010': 'Damascus',
+ 'pl044_0000': 'Dober',
+ 'pl045_0000': 'Xeno',
+ 'pl045_0010': 'Xeno',
+ 'pl045_0020': 'Xeno',
+ 'pl049_0000': 'Kulve Taroth',
+ 'pl049_0010': 'Kulve Taroth',
+ 'pl049_0020': 'Kulve',
+ 'pl054_0000': 'Skull',
+ 'pl057_0000': 'Butterfly',
+ 'pl058_0000': 'Shamos',
+ 'pl059_0000': 'Bushi "Homare"',
+ 'pl060_0000': 'Strategist',
+ 'pl061_0000': 'Dragonking',
+ 'pl072_0001': 'Azure Age',
+ 'pl091_0000': 'Artian',
+ 'pl093_0000': 'Black Belt',
+ 'pl095_0000': 'Unavailable',
+ 'pl095_0010': 'Unavailable',
+ 'pl098_0000': 'Duffel Penguin+',
+ 'pl100_0000': 'Sealed Dragon Cloth',
+ 'pl101_0000': 'Wyverian Ears',
+ 'pl110_0000': 'Beo+',
+ 'pl118_0000': 'Geralt',
+ 'pl121_0000': 'Guardian',
+ 'pl121_0000': 'Guardian+',
+ 'pl124_0000': 'Rose',
+ 'pl125_0000': 'Passion',
+ 'pl126_0000': 'Demonlord',
+ 'pl127_0000': 'Oolong',
+ 'pl128_0000': 'Astral',
+'''
+
+ArmorIDS = {
+ 'pl001_0000': 'Leather',
+ 'pl002_0000': 'Hunter\'s',
+ 'pl002_0010': 'Hunter\'s',
+ 'pl003_0000': 'Bone+',
+ 'pl004_0000': 'Alloy+',
+ 'pl005_0000': 'Kestodon+',
+ 'pl006_0000': 'Gajau+',
+ 'pl007_0000': 'Vespoid',
+ 'pl007_0001': 'Vespoid+',
+ 'pl008_0000': 'Jagras',
+ 'pl008_0001': 'Jagras+',
+ 'pl009_0000': 'Kulu-Ya-Ku',
+ 'pl009_0001': 'Kulu-Ya-Ku+',
+ 'pl010_0000': 'Pukei-Pukei',
+ 'pl010_0001': 'Pukei-Pukei+',
+ 'pl010_0010': 'Pukei-Pukei',
+ 'pl010_0101': 'Coral Pukei+',
+ 'pl011_0000': 'Jyuratodus',
+ 'pl011_0001': 'Jyuratodus+',
+ 'pl012_0000': 'Barroth',
+ 'pl012_0001': 'Barroth+',
+ 'pl013_0000': 'Tobi-Kadachi',
+ 'pl013_0001': 'Tobi-Kadachi+',
+ 'pl013_0101': 'Viper Kadachi+',
+ 'pl014_0000': 'Chainmail',
+ 'pl015_0000': 'Gastodon+',
+ 'pl016_0000': 'Barnos+',
+ 'pl017_0000': 'Hornetaur',
+ 'pl017_0001': 'Hornetaur+',
+ 'pl018_0000': 'Origin',
+ 'pl019_0000': 'King Beetle',
+ 'pl020_0000': 'Anjanath',
+ 'pl020_0001': 'Anjanath+',
+ 'pl020_0010': 'Anjanath',
+ 'pl020_0011': 'Anjanath+',
+ 'pl020_0101': 'Fulgur Anja+',
+ 'pl020_0111': 'Fulgur Anja+',
+ 'pl021_0000': 'Rathian',
+ 'pl021_0001': 'Rathian+',
+ 'pl021_0010': 'Rathian',
+ 'pl021_0011': 'Rathian+',
+ 'pl021_0100': 'Rath Heart',
+ 'pl021_0101': 'Rath Heart+',
+ 'pl021_0110': 'Rath Heart',
+ 'pl021_0111': 'Rath Heart+',
+ 'pl021_0200': 'Golden Lune+',
+ 'pl021_0210': 'Golden Lune+',
+ 'pl022_0000': 'Great Girros',
+ 'pl022_0001': 'Great Girros+',
+ 'pl023_0000': 'Tzitzi-Ya-Ku',
+ 'pl023_0001': 'Tzitzi-Ya-Ku+',
+ 'pl024_0000': 'Paolumu',
+ 'pl024_0001': 'Paolumu+',
+ 'pl024_0101': 'Lumu Phantasm+',
+ 'pl025_0000': 'High Metal+',
+ 'pl026_0000': 'Death Stench',
+ 'pl027_0000': 'Legiana',
+ 'pl027_0001': 'Legiana+',
+ 'pl027_0010': 'Legiana',
+ 'pl027_0011': 'Legiana+',
+ 'pl027_0500': 'Shrieking Legia+',
+ 'pl027_0510': 'Shrieking Legia+',
+ 'pl028_0000': 'Radobaan',
+ 'pl028_0001': 'Radobaan+',
+ 'pl028_0010': 'Radobaan',
+ 'pl029_0000': 'Odogaron',
+ 'pl029_0001': 'Odogaron+',
+ 'pl029_0010': 'Odogaron',
+ 'pl029_0011': 'Odogaron+',
+ 'pl029_0101': 'Death Garon+',
+ 'pl029_0111': 'Death Garon+',
+ 'pl030_0000': 'Nergigante',
+ 'pl030_0010': 'Nergigante',
+ 'pl030_0020': 'Nergigante',
+ 'pl030_0500': 'Ruiner Nergi+',
+ 'pl030_0510': 'Ruiner Nergi+',
+ 'pl031_0000': 'Ingot+',
+ 'pl032_0000': 'Vaal Hazak',
+ 'pl032_0010': 'Vaal Hazak',
+ 'pl032_0020': 'Vaal Hazak',
+ 'pl032_0500': 'Blackveil Hazak+',
+ 'pl032_0510': 'Blackveil Hazak+',
+ 'pl033_0000': 'Rathalos',
+ 'pl033_0001': 'Rathalos+',
+ 'pl033_0010': 'Rathalos',
+ 'pl033_0011': 'Rathalos+',
+ 'pl033_0100': 'Rath Soul',
+ 'pl033_0101': 'Rath Soul+',
+ 'pl033_0110': 'Rath Soul',
+ 'pl033_0111': 'Rath Soul+',
+ 'pl033_0200': 'Silver Sol+',
+ 'pl033_0210': 'Silver Sol+',
+ 'pl034_0000': 'Diablos',
+ 'pl034_0001': 'Diablos+',
+ 'pl034_0010': 'Diablos',
+ 'pl034_0011': 'Diablos+',
+ 'pl034_0100': 'Diablos Nero',
+ 'pl034_0101': 'Diablos Nero+',
+ 'pl034_0110': 'Diablos Nero',
+ 'pl034_0111': 'Diablos Nero+',
+ 'pl035_0000': 'Kirin',
+ 'pl035_0001': 'Kirin+',
+ 'pl035_0010': 'Kirin',
+ 'pl035_0011': 'Kirin+',
+ 'pl035_0020': 'Kirin',
+ 'pl036_0000': 'Brigade',
+ 'pl037_0000': 'Pulverizing Feather',
+ 'pl038_0000': 'Zorah+',
+ 'pl038_0010': 'Zorah+',
+ 'pl038_0020': 'Zorah',
+ 'pl039_0000': 'Dodogama',
+ 'pl039_0001': 'Dodogama+',
+ 'pl040_0000': 'Lavasioth',
+ 'pl040_0001': 'Lavasioth+',
+ 'pl040_0010': 'Lavasioth',
+ 'pl041_0000': 'Uragaan',
+ 'pl041_0001': 'Uragaan+',
+ 'pl041_0010': 'Uragaan',
+ 'pl042_0000': 'Bazelgeuse',
+ 'pl042_0010': 'Bazelgeuse',
+ 'pl042_0500': 'Seething Bazel+',
+ 'pl042_0510': 'Seething Bazel+',
+ 'pl043_0000': 'Damascus+',
+ 'pl043_0010': 'Damascus+',
+ 'pl044_0000': 'Dober+',
+ 'pl045_0000': 'Xeno\'jiiva',
+ 'pl045_0010': 'Xeno\'jiiva',
+ 'pl045_0020': 'Xeno\'jiiva',
+ 'pl046_0000': 'Teostra',
+ 'pl046_0001': 'Teostra+',
+ 'pl046_0010': 'Teostra',
+ 'pl046_0011': 'Teostra+',
+ 'pl046_0020': 'Teostra',
+ 'pl047_0000': 'Kushala',
+ 'pl047_0001': 'Kushala+',
+ 'pl047_0010': 'Kushala',
+ 'pl047_0011': 'Kushala+',
+ 'pl047_0020': 'Kushala',
+ 'pl048_0000': 'Guild Cross',
+ 'pl049_0000': 'Kulve',
+ 'pl049_0001': 'Kulve Taroth+',
+ 'pl049_0010': 'Kulve',
+ 'pl049_0011': 'Kulve Taroth+',
+ 'pl049_0020': 'Kulve Taroth',
+ 'pl050_0000': 'Deviljho',
+ 'pl050_0010': 'Deviljho',
+ 'pl050_0500': 'Savage Jho+',
+ 'pl050_0510': 'Savage Jho+',
+ 'pl051_0000': 'Lunastra',
+ 'pl051_0001': 'Lunastra+',
+ 'pl051_0010': 'Lunastra',
+ 'pl051_0011': 'Lunastra+',
+ 'pl051_0020': 'Lunastra',
+ 'pl052_0000': 'Alatreon+',
+ 'pl052_0010': 'Alatreon+',
+ 'pl053_0000': 'Commission',
+ 'pl054_0000': 'Skull Mask',
+ 'pl055_0000': 'Faux Felyne',
+ 'pl056_0000': 'Mosswine Mask',
+ 'pl057_0000': 'Queen Beetle',
+ 'pl057_0010': 'Butterfly',
+ 'pl058_0000': 'Shamos+',
+ 'pl059_0000': 'Crystal Earring',
+ 'pl060_0000': 'Strategist Spectacles',
+ 'pl061_0000': 'Dragonking Eyepatch',
+ 'pl062_0000': 'Shadow Shades',
+ 'pl063_0000': 'Sealed Eyepatch',
+ 'pl064_0000': 'Wiggler Head',
+ 'pl065_0000': 'Kulu-Ya-Ku Head',
+ 'pl066_0000': 'Bushi "Sabi"',
+ 'pl066_0010': 'Bushi "Homare"',
+ 'pl067_0000': 'Samurai',
+ 'pl068_0000': 'HARDUMMY',
+ 'pl068_0010': 'HARDUMMY',
+ 'pl068_0020': 'HARDUMMY',
+ 'pl069_0000': 'Ryu',
+ 'pl070_0000': 'Sakura',
+ 'pl071_0000': 'Dante',
+ 'pl072_0000': 'Azure Starlord',
+ 'pl072_0001': 'Azure Age+',
+ 'pl073_0000': 'Drachen',
+ 'pl074_0000': 'Blossom',
+ 'pl075_0000': 'Diver',
+ 'pl076_0000': 'Harvest',
+ 'pl077_0000': 'Orion',
+ 'pl078_0000': 'Gala Suit',
+ 'pl080_0000': 'Nargacuga+',
+ 'pl080_0010': 'Nargacuga+',
+ 'pl081_0000': 'Glavenus+',
+ 'pl081_0010': 'Glavenus+',
+ 'pl081_0100': 'Acidic Glavenus+',
+ 'pl081_0110': 'Acidic Glavenus+',
+ 'pl082_0000': 'Brachydios+',
+ 'pl082_0010': 'Brachydios+',
+ 'pl082_0500': 'Raging Brachy+',
+ 'pl082_0510': 'Raging Brachy+',
+ 'pl083_0000': 'Tigrex+',
+ 'pl083_0010': 'Tigrex+',
+ 'pl083_0100': 'Brute Tigrex+',
+ 'pl083_0110': 'Brute Tigrex+',
+ 'pl084_0000': 'Banbaro+',
+ 'pl084_0010': 'Banbaro+',
+ 'pl085_0000': 'Velkhana+',
+ 'pl085_0010': 'Velkhana+',
+ 'pl085_0020': 'Velkhana+',
+ 'pl086_0000': 'Namielle+',
+ 'pl086_0010': 'Namielle+',
+ 'pl086_0020': 'Namielle+',
+ 'pl087_0000': 'Safi\'jiiva+',
+ 'pl087_0010': 'Safi\'jiiva+',
+ 'pl088_0000': 'Shara Ishvalda+',
+ 'pl088_0010': 'Shara Ishvalda+',
+ 'pl089_0000': 'Barioth+',
+ 'pl089_0010': 'Barioth+',
+ 'pl089_0500': 'Frostfang Barioth+',
+ 'pl089_0510': 'Frostfang Barioth+',
+ 'pl091_0000': 'Artian+',
+ 'pl092_0000': 'Guildwork+',
+ 'pl093_0000': 'Black Belt+',
+ 'pl094_0000': 'Guild Palace+',
+ 'pl095_0000': 'Acrobat',
+ 'pl095_0010': 'Showman',
+ 'pl096_0000': 'Faux Kelbi',
+ 'pl097_0000': 'Faux Aptonoth',
+ 'pl098_0000': 'Duffel Penguin Mask+',
+ 'pl099_0000': 'Skull Scarf',
+ 'pl100_0000': 'Sealed Dragon Cloth+',
+ 'pl101_0000': 'Wyverian Ears+',
+ 'pl102_0000': 'Wulg+',
+ 'pl103_0000': 'Cortos+',
+ 'pl104_0000': 'Clockwork+',
+ 'pl105_0000': 'Direwolf+',
+ 'pl106_0000': 'Thermae',
+ 'pl107_0000': 'Zinogre+',
+ 'pl107_0010': 'Zinogre+',
+ 'pl107_0100': 'Stygian Zin+',
+ 'pl107_0110': 'Stygian Zin+',
+ 'pl108_0000': 'Yian Garuga+',
+ 'pl108_0010': 'Yian Garuga+',
+ 'pl109_0000': 'Rajang+',
+ 'pl109_0010': 'Rajang+',
+ 'pl109_0500': 'Furious Rajang+',
+ 'pl109_0510': 'Furious Rajang+',
+ 'pl110_0000': 'Beotodus+',
+ 'pl111_0000': 'Hare Band',
+ 'pl112_0000': 'Downy Crake',
+ 'pl113_0000': 'Pearlspring+',
+ 'pl113_0001': 'Goldspring Macaque',
+ 'pl114_0000': 'Kadachi Scarf',
+ 'pl115_0000': 'Buff Body+',
+ 'pl115_0001': 'Buff Body+',
+ 'pl116_0000': 'Beetle',
+ 'pl117_0000': 'Butterfly',
+ 'pl118_0000': 'Geralt of Rivia',
+ 'pl119_0000': 'Ciri',
+ 'pl120_0000': 'Bayek',
+ 'pl121_0000': 'Defender',
+ 'pl122_0000': 'Yukumo',
+ 'pl123_0000': 'Silver Knight',
+ 'pl124_0000': 'Rose+',
+ 'pl125_0000': 'Passion+',
+ 'pl126_0000': 'Demonlord+',
+ 'pl127_0000': 'Oolong+',
+ 'pl128_0000': 'Astral+',
+ 'pl129_0000': 'Fatalis+',
+ 'pl129_0010': 'Fatalis+',
+ 'pl130_0000': 'Leon+',
+ 'pl131_0000': 'Claire+',
+ 'pl500_0000': 'Innerwear',
+ 'pl501_0000': 'Innerwear'
+}
diff --git a/ModSlots/ModSlots.py b/ModSlots/ModSlots.py
new file mode 100755
index 0000000..164fb0e
--- /dev/null
+++ b/ModSlots/ModSlots.py
@@ -0,0 +1,261 @@
+#! /usr/bin/env python3
+
+import sys
+sys.dont_write_bytecode = True
+import os.path
+import hashlib
+from enum import Enum
+from zipfile import ZipFile
+#from unrardll import extract, names
+#from py7zr import SevenZipFile
+from ArmorIDS import ArmorIDS
+
+USAGE = f'Usage: {sys.argv[0]} <Slots.txt> <Path_to_Mods> <Path_to_Game>'
+
+if len(sys.argv) < 3:
+ print(USAGE)
+ sys.exit(1)
+
+Slots = sys.argv[1]
+Mods_Directory = sys.argv[2]
+Game_Directory = sys.argv[3]
+Extract_Directory = f'{Mods_Directory}/extract'
+Hash_Dir = f'{Game_Directory}/mod_slots_hash.txt'
+List_Dir = f'{Game_Directory}/mod_slots.txt'
+
+if not os.path.isfile(Slots):
+ print(f'{Slots} not a file.')
+ sys.exit(1)
+
+if not os.path.isdir(Mods_Directory):
+ print(f'Directory {Mods_Directory} doesn\'t exist.')
+ sys.exit(1)
+
+if not os.path.isdir(Extract_Directory):
+ os.mkdir(Extract_Directory)
+
+if not os.path.isdir(Game_Directory):
+ print(f'Directory {Game_Directory} doesn\'t exist.')
+ sys.exit(1)
+
+ArmorIDS_Inverted = { v: k for k, v in ArmorIDS.items() }
+
+class ArchiveType(Enum):
+ ZIP = 0
+ RAR = 1
+ SEVEN_ZIP = 2
+
+class Mod():
+ def __init__(self, path):
+ m = hashlib.sha256()
+ m.update(open(path, 'rb').read())
+ self.hash = m.hexdigest()
+ ext = path.split('.')[-1].lower()
+ if ext == 'zip':
+ self.type = ArchiveType.ZIP
+ self.obj = ZipFile(path, 'r')
+ self.files = []
+ for file in self.obj.infolist():
+ if file.filename[-1] == '/':
+ continue
+ self.files.append(file.filename)
+ elif ext == 'rar':
+ self.type = ArchiveType.RAR
+ pass
+ elif ext == '7z':
+ self.type = ArchiveType.SEVEN_ZIP
+ pass
+ self.toplevels = []
+ self.roots = []
+ self.ignored = []
+ self.overrides = {}
+ self.map = {}
+
+ def get_id_from_subpath(self, sub):
+ for armor_id in ArmorIDS.keys():
+ armor_id = armor_id[2:]
+ search = sub.find(armor_id)
+ if search >= 0:
+ return sub[search:search + 8]
+
+ def substitute_override_name(self, name):
+ if name[0] == '[':
+ name = name[1:-1]
+ if name in ArmorIDS_Inverted.keys():
+ name = ArmorIDS_Inverted[name][2:]
+ return name
+
+ def file_sort_key(self, x):
+ for i in range(0, len(self.roots)):
+ if x.startswith(self.roots[i]):
+ return i
+ return 0
+
+ def do_map(self, all_files):
+ for file in sorted(self.files, key=self.file_sort_key):
+ ignore_this = False
+ for ig in self.ignored:
+ if file.startswith(ig):
+ ignore_this = True
+ break
+ if ignore_this:
+ continue
+ path = file # Absolute path within archive.
+ for root in self.roots:
+ if file.startswith(root):
+ nativePC = file.find('nativePC')
+ if nativePC >= 0:
+ file = file[nativePC:]
+ break
+ if file in self.overrides.keys():
+ targets = self.overrides[file]
+ elif not (file.startswith('nativePC') or file in self.toplevels):
+ continue
+ else:
+ targets = [file]
+ for i in range(0, len(targets)):
+ target = targets[i]
+ # Substitute armor names with their IDs.
+ for key in self.overrides.keys():
+ if target.startswith(key):
+ name = self.substitute_override_name(self.overrides[key][0]) # Sub has to be first element.
+ target = target.replace(self.get_id_from_subpath(target), name)
+ if target in all_files:
+ print(f'DUPLICATE MAPPING: {target}.')
+ del targets[i]
+ continue
+ targets[i] = target
+ all_files.extend(targets)
+ print(path)
+ for target in targets:
+ print(' -> ' + target)
+ self.map[path] = targets
+
+ def add_to_hash(self, m):
+ output = f'{os.path.join(Extract_Directory, self.hash)}'
+ if not os.path.isdir(output):
+ os.mkdir(output)
+ if self.type == ArchiveType.ZIP:
+ self.obj.extractall(path=output)
+ for key in self.map:
+ m.update(open(os.path.join(output, key), 'rb').read())
+
+ def write(self, old_list):
+ output = f'{os.path.join(Extract_Directory, self.hash)}'
+ with open(List_Dir, 'a+') as l:
+ for key in self.map:
+ targets = self.map[key]
+ for target in targets:
+ 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.isfile(target):
+ os.remove(target)
+ os.symlink(f'{os.path.join(output, key)}', f'{target}')
+
+ def delete(self):
+ for key in self.map:
+ targets = self.map[key]
+ for target in targets:
+ target = os.path.join(Game_Directory, target)
+ if os.path.isfile(target):
+ os.remove(target)
+
+def is_armor_override(line):
+ sp = line.split('/')
+ if len(sp) >= 3 and sp[2] == 'slg':
+ return True
+ if sp[-1] in ['helm', 'body', 'arm', 'wst', 'leg']:
+ return True
+ return False
+
+Mods = []
+Current_Mod = None
+Current_Override = None
+with open(Slots, 'r') as f:
+ for line in f.read().splitlines():
+ if len(line) == 0 or line[0] == '#':
+ continue
+ if line[0] == ';':
+ if Current_Mod:
+ Mods.append(Current_Mod)
+ Current_Mod = None
+ path = os.path.join(Mods_Directory, line[1:])
+ if os.path.isfile(path):
+ Current_Mod = Mod(path)
+ else:
+ print(f'FILE NOT FOUND: {path}')
+ continue
+ cmd = ''
+ if line[0] == '*':
+ cmd = 'root'
+ elif line[0] == '<':
+ cmd = 'ignore'
+ elif line[0] == ':':
+ cmd = 'override'
+ elif line[0] == '>':
+ cmd = 'value'
+ if cmd:
+ line = line[1:]
+ if line.startswith('pl/'):
+ line = 'nativePC/' + line
+ if line.startswith('wp/'):
+ line = 'nativePC/' + line
+ if cmd == 'root' and Current_Mod:
+ Current_Mod.roots.append(line)
+ elif cmd == 'ignore' and Current_Mod:
+ Current_Mod.ignored.append(line)
+ elif cmd == 'override' and Current_Mod:
+ if not (is_armor_override(line) or line in Current_Mod.files):
+ print(f'FILE NOT FOUND: {line}')
+ Current_Mod = None
+ continue
+ Current_Override = line
+ Current_Mod.overrides[Current_Override] = []
+ elif cmd == 'value' and Current_Mod:
+ if Current_Override:
+ Current_Mod.overrides[Current_Override].append(line)
+ else:
+ Current_Mod.toplevels.append(line)
+ if Current_Mod:
+ Mods.append(Current_Mod)
+ Current_Mod = None
+
+Files = []
+M = hashlib.sha256()
+for mod in Mods:
+ mod.do_map(Files)
+ mod.add_to_hash(M)
+
+with open(List_Dir, 'a+') as l:
+ l.seek(0)
+ old_list = l.read().splitlines()
+ l.truncate(0)
+
+Mode = 'Write'
+if Mode == 'Write':
+ for mod in Mods:
+ mod.write(old_list)
+ 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_Dir, 'w+') as f:
+ f.write(M.hexdigest())
+if Mode == 'Verify':
+ if os.path.isfile(Hash_Dir):
+ with open(Hash_Dir, 'r') as f:
+ if f.read() == M.hexdigest():
+ print('Verification passed')
+ else:
+ print('Verification failed')
+elif Mode == 'Delete':
+ for mod in Mods:
+ mod.delete()
diff --git a/ModSlots/cmd.txt b/ModSlots/cmd.txt
new file mode 100644
index 0000000..e276cbd
--- /dev/null
+++ b/ModSlots/cmd.txt
@@ -0,0 +1 @@
+./ModSlots.py slots.txt /mnt/hdd/mods/mhw /mnt/ssd/SteamLibrary/steamapps/common/Monster\ Hunter\ World
diff --git a/ModSlots/requirements.txt b/ModSlots/requirements.txt
new file mode 100644
index 0000000..a93b1d1
--- /dev/null
+++ b/ModSlots/requirements.txt
@@ -0,0 +1,2 @@
+py7zr==1.0.0
+unrardll==0.1.7