def __init__(self, fullInstallConfiguration, extractDirectlyToGameDirectory): # type: (installConfiguration.FullInstallConfiguration, bool) -> None """ Installer Init :param str directory: The directory of the game :param dict info: The info dictionary from server JSON file for the requested target """ self.info = fullInstallConfiguration self.directory = fullInstallConfiguration.installPath if common.Globals.IS_MAC: self.dataDirectory = path.join(self.directory, "Contents/Resources/Data") else: self.dataDirectory = path.join(self.directory, self.info.subModConfig.dataName) logger.getGlobalLogger().trySetSecondaryLoggingPath( os.path.join(self.dataDirectory, common.Globals.LOG_BASENAME) ) self.assetsDir = path.join(self.dataDirectory, "StreamingAssets") possibleSteamPaths = [ path.join(self.directory, "steam_api.dll"), path.join(self.directory, "Contents/Plugins/CSteamworks.bundle"), path.join(self.directory, "libsteam_api.so") ] self.isSteam = False for possibleSteamPath in possibleSteamPaths: if path.exists(possibleSteamPath): self.isSteam = True self.downloadDir = self.info.subModConfig.modName + " Downloads" self.extractDir = self.directory if extractDirectlyToGameDirectory else (self.info.subModConfig.modName + " Extraction") self.fileVersionManager = fileVersionManagement.VersionManager( subMod=self.info.subModConfig, modFileList=self.info.buildFileListSorted(datadir=self.dataDirectory), localVersionFolder=self.directory) modFileList = self.fileVersionManager.getFilesRequiringUpdate() self.downloaderAndExtractor = common.DownloaderAndExtractor(modFileList=modFileList, downloadTempDir=self.downloadDir, extractionDir=self.extractDir) self.downloaderAndExtractor.buildDownloadAndExtractionList() parser = installConfiguration.ModOptionParser(self.info) for opt in parser.downloadAndExtractOptionsByPriority: self.downloaderAndExtractor.addItemManually( url=opt.url, extractionDir=os.path.join(self.extractDir, opt.relativeExtractionPath), ) self.downloaderAndExtractor.printPreview()
def getInitStatus(requestData): initError = None if self.initErrorMessage is not None: initError = self.initErrorMessage self.initErrorMessage = None return { 'initCompleted': self.initCompleted, 'initErrorMessage': initError, 'consoleLines': logger.getGlobalLogger().threadSafeReadAll() }
def statusUpdate(requestData): return [ _loggerMessageToStatusDict(x) for x in logger.getGlobalLogger().threadSafeReadAll() ]
def mainUmineko(conf): # type: (installConfiguration.FullInstallConfiguration) -> None logger.getGlobalLogger().trySetSecondaryLoggingPath( os.path.join(conf.installPath, common.Globals.LOG_BASENAME)) isQuestionArcs = 'question' in conf.subModConfig.modName.lower() print("CONFIGURATION:") print("Install path", conf.installPath) print("Mod Option", conf.subModConfig.modName) print("Sub Option", conf.subModConfig.subModName) print("Is Question Arcs", isQuestionArcs) print("Is Windows", common.Globals.IS_WINDOWS) print("Is Linux", common.Globals.IS_LINUX) print("Is Mac", common.Globals.IS_MAC) ####################################### VALIDATE AND PREPARE FOLDERS ############################################### # do a quick verification that the directory is correct before starting installer if not os.path.isfile(os.path.join(conf.installPath, "arc.nsa")): raise Exception( "ERROR - wrong game path. Installation Stopped.\n" "There is no 'arc.nsa' in the game folder. Are you sure the correct game folder was selected?" ) for filename in os.listdir(conf.installPath): # Stop the user installing the mod on pirated versions of the game. # Use SHA256 hash of the lowercase filename to avoid listing the website names in our source code. if hashlib.sha256(filename.lower().encode('utf-8')).hexdigest() in [ '2c02ec6f6de9281a68975257a477e8f994affe4eeaaf18b0b56b4047885461e0', '4fae41c555fe50034065e59ce33a643c1d93ee846221ecc5756f00e039035076', ]: raise Exception( "\nInstall Failed - The {} mod is not compatible with the pirated version of the game\n" "(Detected file [{}]) Please install the latest Steam or Mangagamer release." .format(conf.subModConfig.modName, filename)) # Stop the user installing the mod on the old/original Japanese game. # This probably means the user placed a fake identifier (eg the game exe) in the old game's folder. if filename == 'snow.dll': raise Exception( "\nInstall Failed - The {} mod is not compatible with the old/original Japanese game.\n" "(Detected [{}]) Please install the latest Steam or Mangagamer release." .format(conf.subModConfig.modName, filename)) # Create aliases for the temp directories, and ensure they exist beforehand downloadTempDir = conf.subModConfig.modName + " Downloads" if os.path.isdir(downloadTempDir): print( "Information: Temp directories already exist - continued or overwritten install" ) common.makeDirsExistOK(downloadTempDir) ######################################## Query and Download Files ################################################## fileVersionManager = fileVersionManagement.VersionManager( subMod=conf.subModConfig, modFileList=conf.buildFileListSorted(), localVersionFolder=conf.installPath) filesRequiringUpdate = fileVersionManager.getFilesRequiringUpdate() print("Perform Full Install: {}".format( fileVersionManager.fullUpdateRequired())) downloaderAndExtractor = common.DownloaderAndExtractor( filesRequiringUpdate, downloadTempDir, conf.installPath, downloadProgressAmount=45, extractionProgressAmount=45) downloaderAndExtractor.buildDownloadAndExtractionList() parser = installConfiguration.ModOptionParser(conf) for opt in parser.downloadAndExtractOptionsByPriority: downloaderAndExtractor.addItemManually( url=opt.url, extractionDir=os.path.join(conf.installPath, opt.relativeExtractionPath), ) downloaderAndExtractor.printPreview() # Delete all non-checksummed files from the download folder, if they exist print("Removing non-checksummed downloads:") deleteExtractablesFromFolder( downloadTempDir, [x for x in downloaderAndExtractor.extractList if not x.fromMetaLink]) downloaderAndExtractor.download() # Treat the install as "started" once the "download" stage is complete fileVersionManager.saveVersionInstallStarted() ###################### Backup/clear the .exe and script files, and old graphics #################################### backupOrRemoveFiles(conf.installPath) if fileVersionManager.fullUpdateRequired(): # Remove old graphics from a previous installation, as they can conflict with the voice-only patch graphicsPathsToDelete = [ os.path.join(conf.installPath, x) for x in ['big', 'bmp', 'en'] ] for folderPath in graphicsPathsToDelete: if os.path.exists(folderPath): print("Deleting {}".format(folderPath)) try: shutil.rmtree(folderPath) except: print("WARNING: failed to remove folder {}".format( folderPath)) ######################################## Extract Archives ########################################################## downloaderAndExtractor.extract() ############################################# FIX .ARC FILE NAMING ################################################# # Steam release has arc files labeled arc.nsa, arc1.nsa, arc2.nsa, arc3.nsa. # Mangagamer release has only one arc file labeled arc.nsa # Generate dummy arc1-arc3 nsa files if they don't already exist, so the game can find the arc4.nsa that we provide for i in range(1, 4): nsaPath = os.path.join(conf.installPath, 'arc{}.nsa'.format(i)) if not os.path.exists(nsaPath): print( ".nsa archive check: Generating dummy [{}] as it does not already exist (Mangagamer)" .format(nsaPath)) with open(nsaPath, 'wb') as dummyNSAFile: dummyNSAFile.write(bytes([0, 0, 0, 0, 0, 6])) else: print(".nsa archive check: [{}] already exists (Steam)".format( nsaPath)) #################################### MAKE EXECUTABLE, WRITE HELPER SCRIPTS ######################################### gameBaseName = "Umineko5to8" if isQuestionArcs: gameBaseName = "Umineko1to4" if common.Globals.IS_MAC: print("Un-quarantining game executable") subprocess.call([ "xattr", "-d", "com.apple.quarantine", os.path.join(conf.installPath, gameBaseName + ".app") ]) print("Creating debug mode batch files") # write batch file to let users launch game in debug mode with open(os.path.join(conf.installPath, gameBaseName + "_DebugMode.bat"), 'w') as f: f.writelines([gameBaseName + ".exe --debug\n", "pause"]) #make the following files executable, if they exist makeExecutableList = [ os.path.join(conf.installPath, "Umineko1to4"), os.path.join(conf.installPath, "Umineko1to4.app/Contents/MacOS/umineko4"), os.path.join(conf.installPath, "Umineko5to8"), os.path.join(conf.installPath, "Umineko5to8.app/Contents/MacOS/umineko8") ] print("Making executables ... executable") for exePath in makeExecutableList: if os.path.exists(exePath): common.makeExecutable(exePath) # Patched game uses mysav folder, which Steam can't see so can't get incompatible saves by accident. # Add batch file which reverses this behaviour by making a linked folder from (saves->mysav) print("Creating EnableSteamSync.bat") with open(os.path.join(conf.installPath, "EnableSteamSync.bat"), 'w') as f: f.write(""" if exist saves ( ren saves backup-saves mklink /J saves mysav ) else ( mklink /J saves mysav ) pause """) # For now, don't copy save data steamGridExtractor.extractSteamGrid() fileVersionManager.saveVersionInstallFinished() if not parser.keepDownloads: print("Removing temporary downloads:") deleteExtractablesFromFolder(downloadTempDir, downloaderAndExtractor.extractList) commandLineParser.printSeventhModStatusUpdate( 100, "Umineko install script completed!")
def __init__(self, fullInstallConfiguration, extractDirectlyToGameDirectory, modOptionParser, forcedExtractDirectory=None): # type: (installConfiguration.FullInstallConfiguration, bool, installConfiguration.ModOptionParser, Optional[str]) -> None """ Installer Init :param str directory: The directory of the game :param dict info: The info dictionary from server JSON file for the requested target """ self.forcedExtractDirectory = forcedExtractDirectory self.info = fullInstallConfiguration self.directory = fullInstallConfiguration.installPath self.dataDirectory = self.getDataDirectory(self.directory) self.clearScripts = False # If true, will clear CompiledUpdateScripts before extraction stage self.languagePatchIsEnabled = False # True if at least one language patch will be installed logger.getGlobalLogger().trySetSecondaryLoggingPath( os.path.join(self.dataDirectory, common.Globals.LOG_BASENAME) ) self.assetsDir = path.join(self.dataDirectory, "StreamingAssets") possibleSteamPaths = [ path.join(self.directory, "steam_api.dll"), path.join(self.directory, "Contents/Plugins/CSteamworks.bundle"), path.join(self.directory, "libsteam_api.so") ] self.isSteam = False for possibleSteamPath in possibleSteamPaths: if path.exists(possibleSteamPath): self.isSteam = True self.downloadDir = self.info.subModConfig.modName + " Downloads" self.extractDir = self.directory if extractDirectlyToGameDirectory else (self.info.subModConfig.modName + " Extraction") if forcedExtractDirectory is not None: self.extractDir = forcedExtractDirectory self.fileVersionManager = fileVersionManagement.VersionManager( subMod=self.info.subModConfig, modFileList=self.info.buildFileListSorted(datadir=self.dataDirectory), localVersionFolder=self.directory) modFileList = self.fileVersionManager.getFilesRequiringUpdate() for modFile in modFileList: if modFile.name == 'script': self.clearScripts = True self.info.subModConfig.printEnabledOptions() self.downloaderAndExtractor = common.DownloaderAndExtractor(modFileList=modFileList, downloadTempDir=self.downloadDir, extractionDir=self.extractDir) self.downloaderAndExtractor.buildDownloadAndExtractionList() self.optionParser = modOptionParser for opt in self.optionParser.downloadAndExtractOptionsByPriority: self.downloaderAndExtractor.addItemManually( url=opt.url, extractionDir=os.path.join(self.extractDir, opt.relativeExtractionPath), ) if opt.group == 'Alternate Languages': self.clearScripts = True self.languagePatchIsEnabled = True self.downloaderAndExtractor.printPreview()
def main(conf): # type: (installConfiguration.FullInstallConfiguration) -> None logger.getGlobalLogger().trySetSecondaryLoggingPath( os.path.join(conf.installPath, common.Globals.LOG_BASENAME)) print("CONFIGURATION:") print("Install path", conf.installPath) print("Mod Option", conf.subModConfig.modName) print("Sub Option", conf.subModConfig.subModName) print("Is Windows", common.Globals.IS_WINDOWS) print("Is Linux", common.Globals.IS_LINUX) print("Is Mac", common.Globals.IS_MAC) if not common.Globals.IS_WINDOWS: raise Exception( "Error: Umineko Hane Mod (onscripter version) is not supported on Mac or Linux!" ) ####################################### VALIDATE AND PREPARE FOLDERS ############################################### # do a quick verification that the directory is correct before starting installer if not os.path.isfile(os.path.join(conf.installPath, "arc.nsa")): raise Exception( "ERROR - wrong game path. Installation Stopped.\n" "There is no 'arc.nsa' in the game folder. Are you sure the correct game folder was selected?" ) # Create aliases for the temp directories, and ensure they exist beforehand downloadTempDir = conf.subModConfig.modName + " Downloads" if os.path.isdir(downloadTempDir): print( "Information: Temp directories already exist - continued or overwritten install" ) common.makeDirsExistOK(downloadTempDir) ######################################## DOWNLOAD, BACKUP, THEN EXTRACT ############################################ fileVersionManager = fileVersionManagement.VersionManager( subMod=conf.subModConfig, modFileList=conf.buildFileListSorted(), localVersionFolder=conf.installPath) filesRequiringUpdate = fileVersionManager.getFilesRequiringUpdate() conf.subModConfig.printEnabledOptions() downloaderAndExtractor = common.DownloaderAndExtractor( filesRequiringUpdate, downloadTempDir, conf.installPath, downloadProgressAmount=45, extractionProgressAmount=45) downloaderAndExtractor.buildDownloadAndExtractionList() parser = installConfiguration.ModOptionParser(conf) for opt in parser.downloadAndExtractOptionsByPriority: downloaderAndExtractor.addItemManually( url=opt.url, extractionDir=os.path.join(conf.installPath, opt.relativeExtractionPath), ) downloaderAndExtractor.printPreview() # Download files # Delete all non-checksummed files from the download folder, if they exist for extractableItem in downloaderAndExtractor.extractList: extractableItemPath = os.path.join(downloadTempDir, extractableItem.filename) if not extractableItem.fromMetaLink and os.path.exists( extractableItemPath): print("Removing existing non-checksummed download: [{}]".format( extractableItemPath)) os.remove(extractableItemPath) downloaderAndExtractor.download() # Extract files fileVersionManager.saveVersionInstallStarted() downloaderAndExtractor.extract() fileVersionManager.saveVersionInstallFinished() commandLineParser.printSeventhModStatusUpdate( 100, "Umineko Hane install script completed!")