Skip to content

Commit

Permalink
Add requiring a target checksum to accept a ModFileOverride
Browse files Browse the repository at this point in the history
  • Loading branch information
drojf committed Nov 20, 2020
1 parent 20cc36a commit 8c4e6d4
Showing 1 changed file with 50 additions and 5 deletions.
55 changes: 50 additions & 5 deletions installConfiguration.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
from __future__ import unicode_literals

import os

import hashlib
import common

try:
from typing import List, Optional, Dict, Set
from typing import List, Optional, Dict, Set, Tuple
except:
pass # Just needed for pycharm comments


def getSHA256(path):
"""Gets the SHA256 Hex digest of a file at path as a string,
e.g. '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'"""
with open(path, "rb") as file:
m = hashlib.sha256()
m.update(file.read())
return m.hexdigest()

def getUnityVersion(datadir, verbosePrinting=True):
# type: (str, bool) -> str
"""
Expand All @@ -31,6 +39,26 @@ def getUnityVersion(datadir, verbosePrinting=True):
raise OldUnityException(unityVersion)
return unityVersion

def checkChecksumListMatches(installPath, checksumList):
#type: (str, List[(str, str)]) -> bool
"""Returns true if any checksum in the checksum list matches"""
for relativePath, checksum in checksumList:
path = os.path.join(installPath, relativePath)
if not os.path.exists(path):
print("checkChecksumListMatches(): File at {} does not exist, skipping this file".format(path))
continue

actualChecksum = getSHA256(path)

if actualChecksum.lower() == checksum.lower():
print("checkChecksumListMatches(): File at {} has matching checksum {}".format(path, actualChecksum))
return True
else:
print("checkChecksumListMatches(): File at {} has wrong checksum {} (expected {})".format(path, actualChecksum, checksum))
continue

return False

class FullInstallConfiguration:
# contains all the install information required to install the game to a given path

Expand Down Expand Up @@ -68,6 +96,12 @@ def buildFileListSorted(self, datadir="", verbosePrinting=True):
if fileOverride.unity is not None and fileOverride.unity != unityVersion:
continue

# If the file has some checksum requirements, check whether the files exist/match the required checksums
# If datadir is defined, use the datadir (Higurashi), otherwise use the install path (other games)
if fileOverride.targetChecksums is not None:
if not checkChecksumListMatches(datadir if datadir else self.installPath, fileOverride.targetChecksums):
continue

# 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)
Expand Down Expand Up @@ -111,8 +145,8 @@ def __init__(self, name, url, priority, id=None):


class ModFileOverride:
def __init__(self, name, id, os, steam, unity, url):
# type: (str, str, List[str], Optional[bool], Optional[str], str) -> None
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
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"""
Expand All @@ -122,6 +156,10 @@ def __init__(self, name, id, os, steam, unity, url):
"""This can be 'None' if the override applies to both mac and steam"""
self.unity = unity #type: Optional[str]
self.url = url # type: str
self.targetChecksums = targetChecksums # type: List[Tuple[str, str]]
"""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"""


class ModOption:
Expand Down Expand Up @@ -230,7 +268,8 @@ def __init__(self, mod, subMod):
steam=subModFileOverride.get('steam'),
unity=subModFileOverride.get('unity'),
url=subModFileOverride['url'],
id=subModFileOverride['id']
id=subModFileOverride['id'],
targetChecksums=subModFileOverride.get('targetChecksums')
))

# If no mod options are specified in the JSON, the 'self.modOptions' field defaults to the empty list ([])
Expand Down Expand Up @@ -351,6 +390,11 @@ def describe(self, candidate):
if len(out) > 1:
out += ", "
out += "unity: " + candidate.unity
if candidate.targetChecksums is not None:
if len(out) > 1:
out += ", "
out += "checksums: " + str(candidate.targetChecksums)

return out + ")"

def __str__(self):
Expand All @@ -361,6 +405,7 @@ def __str__(self):
if hasUnity:
out += ", unity: {}".format(self.unity)
out += ") but the available versions had the requirements " + ", ".join(self.describe(candidate) for candidate in self.candidates)
out += "\nPlease check your game is up-to-date. If it is fully up-to-date, please send the game log to the developers on our discord server."
return out


Expand Down

0 comments on commit 8c4e6d4

Please sign in to comment.