diff --git a/JSONValidator/Sources/JSONValidator/JSONValidator.swift b/JSONValidator/Sources/JSONValidator/JSONValidator.swift index b3d14e8c..9941626b 100644 --- a/JSONValidator/Sources/JSONValidator/JSONValidator.swift +++ b/JSONValidator/Sources/JSONValidator/JSONValidator.swift @@ -65,6 +65,8 @@ public struct FileDefinition: Codable { public var url: String? /// The priority of this file. Higher priority files are installed later so they will overwrite lower priority files public var priority: Int + /// A path relative to the *top-level game directory* (should contain HigurashiEp##_data for a higurashi game's data folder) + public var relativeExtractionPath: String? } public struct FileOverrideDefinition: Codable { @@ -92,6 +94,8 @@ public struct FileOverrideDefinition: Codable { /// it's relative to the install directory /// - If null, no files are checked public var targetChecksums: [[String]]? + /// A path relative to the *top-level game directory* (should contain HigurashiEp##_data for a higurashi game's data folder) + public var relativeExtractionPath: String? } public enum OS: String, Codable, CaseIterable { @@ -131,7 +135,7 @@ public struct ModOptionFileDefinition: Codable { /// The url for the mod option public var url: String /// A path relative to the *top-level game directory* (should contain HigurashiEp##_data for a higurashi game's data folder) - public var relativeExtractionPath: String + public var relativeExtractionPath: String? /// The priority of this file (same system as above priorities) public var priority: Int } diff --git a/common.py b/common.py index a4bc1a90..845a2484 100644 --- a/common.py +++ b/common.py @@ -95,7 +95,7 @@ class Globals: GITHUB_MASTER_BASE_URL = "https://raw.githubusercontent.com/07th-mod/python-patcher/master/" # The installer info version this installer is compatibile with # Increment it when you make breaking changes to the json files - JSON_VERSION = 10 + JSON_VERSION = 11 # Define constants used throughout the script. Use function calls to enforce variables as const IS_WINDOWS = platform.system() == "Windows" @@ -878,7 +878,11 @@ def buildDownloadAndExtractionList(self): if not self.suppressDownloadStatus: commandLineParser.printSeventhModStatusUpdate(1, "Querying URLs to be Downloaded") for i, file in enumerate(self.modFileList): - self.addItemManually(file.url, self.defaultExtractionDir) + extractionDir = self.defaultExtractionDir + if file.relativeExtractionPath is not None: + extractionDir = os.path.join(self.defaultExtractionDir, file.relativeExtractionPath) + + self.addItemManually(file.url, extractionDir) self.downloadAndExtractionListsBuilt = True diff --git a/installConfiguration.py b/installConfiguration.py index 77f989ca..090d1bd7 100644 --- a/installConfiguration.py +++ b/installConfiguration.py @@ -108,7 +108,7 @@ def buildFileListSorted(self, datadir="", verbosePrinting=True): # for all other overrides, overwrite the value in the filesDict with a new ModFile currentModFile = filesDict[fileOverride.name] - filesDict[fileOverride.name] = ModFile(currentModFile.name, fileOverride.url, currentModFile.priority, id=fileOverride.id) + filesDict[fileOverride.name] = ModFile(currentModFile.name, fileOverride.url, currentModFile.priority, id=fileOverride.id, relativeExtractionPath=fileOverride.relativeExtractionPath) # Look for override-required files that weren't overridden for key, value in filesDict.items(): @@ -130,8 +130,8 @@ def buildFileListSorted(self, datadir="", verbosePrinting=True): class ModFile: modFileCounter = 0 - def __init__(self, name, url, priority, id=None): - # type: (str, Optional[str], int, str) -> None + def __init__(self, name, url, priority, id=None, relativeExtractionPath=None): + # type: (str, Optional[str], int, str, Optional[str]) -> None self.name = name self.url = url @@ -142,6 +142,9 @@ def __init__(self, name, url, priority, id=None): # Therefore, the 'later extracted' files are higher priority, that is archives with priority 3 will overwrite priority 0,1,2 archives self.priority = priority #consider renaming this "extractionOrder"? + # A path relative to the *top-level game directory* (should contain HigurashiEp##_data for a higurashi game's data folder) + self.relativeExtractionPath = relativeExtractionPath # type: str + # This variable is used to provide ordering which roughly matches the ordering in the JSON file # to ensure files are downloaded and extracted in a deterministic manner self.nativeOrder = ModFile.modFileCounter @@ -149,8 +152,8 @@ def __init__(self, name, url, priority, id=None): class ModFileOverride: - def __init__(self, name, id, os, steam, unity, url, targetChecksums): - # type: (str, str, List[str], Optional[bool], Optional[str], str, List[Tuple[str, str]]) -> None + def __init__(self, name, id, os, steam, unity, url, targetChecksums, relativeExtractionPath=None): + # type: (str, str, List[str], Optional[bool], Optional[str], str, List[Tuple[str, str]], Optional[str]) -> None self.name = name # type: str self.id = id """A unique identifier among all files and modfiles for this submod. Set manually as 'movie-unix' for example""" @@ -164,7 +167,9 @@ def __init__(self, name, id, os, steam, unity, url, targetChecksums): """This field can be None for no checksum checking. This field consists of a list of tuples. Each tuple is a pair of (PATH, CHECKSUM). If a file exists at PATH and matches CHECKSUM then this override will be accepted""" - + self.relativeExtractionPath = relativeExtractionPath # type: str + """A path relative to the *top-level game directory* + (should contain HigurashiEp##_data for a higurashi game's data folder)""" class ModOption: def __init__(self, name, description, group, type, isRadio, data, isGlobal=False, value=False): @@ -262,7 +267,7 @@ def __init__(self, mod, subMod): self.files = [] # type: List[ModFile] for subModFile in subMod['files']: - self.files.append(ModFile(name=subModFile['name'], url = subModFile.get('url'), priority=subModFile['priority'])) + self.files.append(ModFile(name=subModFile['name'], url = subModFile.get('url'), priority=subModFile['priority'], relativeExtractionPath=subModFile.get('relativeExtractionPath'))) self.fileOverrides = [] # type: List[ModFileOverride] for subModFileOverride in subMod['fileOverrides']: @@ -273,7 +278,8 @@ def __init__(self, mod, subMod): unity=subModFileOverride.get('unity'), url=subModFileOverride['url'], id=subModFileOverride['id'], - targetChecksums=subModFileOverride.get('targetChecksums') + targetChecksums=subModFileOverride.get('targetChecksums'), + relativeExtractionPath=subModFileOverride.get('relativeExtractionPath') )) # If no mod options are specified in the JSON, the 'self.modOptions' field defaults to the empty list ([])