Exemplo n.º 1
0
 def __init__(self, games, exoDosDir, gamesDosDir, outputDir,
              metadataHandler, logger):
     self.games = games
     self.exoDosDir = exoDosDir
     self.logger = logger
     self.gamesDosDir = gamesDosDir
     self.outputDir = outputDir
     self.metadataHandler = metadataHandler
     self.confConverter = ConfConverter(self.games, self.exoDosDir,
                                        self.outputDir, self.logger)
Exemplo n.º 2
0
 def __init__(self, game, genre, outputDir, collectionVersion,
              useGenreSubFolders, metadata, conversionType, conversionConf,
              exoCollectionDir, fullnameToGameDir, scriptDir, logger):
     self.game = game
     self.collectionVersion = collectionVersion
     self.genre = genre
     self.outputDir = outputDir
     self.useGenreSubFolders = useGenreSubFolders
     self.metadata = metadata
     self.conversionType = conversionType
     self.exoCollectionDir = exoCollectionDir
     self.logger = logger
     self.fullnameToGameDir = fullnameToGameDir
     self.conversionConf = conversionConf
     self.scriptDir = scriptDir
     self.confConverter = ConfConverter(self)
Exemplo n.º 3
0
class GameGenerator:
    def __init__(self, game, genre, outputDir, collectionVersion,
                 useGenreSubFolders, metadata, conversionType, conversionConf,
                 exoCollectionDir, fullnameToGameDir, scriptDir, logger):
        self.game = game
        self.collectionVersion = collectionVersion
        self.genre = genre
        self.outputDir = outputDir
        self.useGenreSubFolders = useGenreSubFolders
        self.metadata = metadata
        self.conversionType = conversionType
        self.exoCollectionDir = exoCollectionDir
        self.logger = logger
        self.fullnameToGameDir = fullnameToGameDir
        self.conversionConf = conversionConf
        self.scriptDir = scriptDir
        self.confConverter = ConfConverter(self)

    ##### Utils functions ####

    # Checks if collection is win3x or dos
    def isWin3x(self):
        return util.isWin3x(self.collectionVersion)

    # Returns local parent output dir of the generated game
    def getLocalParentOutputDir(self):
        return os.path.join(
            self.outputDir,
            self.genre) if self.useGenreSubFolders else self.outputDir

    # returns local game ouput dir of the generated game
    def getLocalGameOutputDir(self):
        return os.path.join(self.getLocalParentOutputDir(), self.game + ".pc")

    # returns local game data output dir of the generated game
    def getLocalGameDataOutputDir(self):
        return self.getLocalGameOutputDir() if self.isWin3x(
        ) else os.path.join(self.getLocalGameOutputDir(), self.game)

    ################################

    ##### Generation functions #####

    # Converts game
    def convertGame(self):
        self.copyGameFiles()
        self.confConverter.process(self)
        self.postConversion()

    # Copy game files and game dosbox.conf to output dir
    def copyGameFiles(self):
        self.logger.log("  copy dosbox conf")
        # Copy dosbox.conf in game.pc
        shutil.copy2(
            os.path.join(
                util.getCollectionGamesConfDir(self.exoCollectionDir,
                                               self.collectionVersion),
                self.game, "dosbox.conf"),
            os.path.join(self.getLocalGameDataOutputDir(), "dosbox.conf"))
        # Create blank file with full game name
        f = open(os.path.join(self.getLocalGameOutputDir(),
                              util.getCleanGameID(self.metadata, '.txt')),
                 'w',
                 encoding='utf8')
        f.write(self.metadata.desc)
        f.close()
        # Handle first-game-of-a-serie dependencies
        needsFirstGame = {
            'roadware':
            ['Roadwar 2000 (1987).zip'],  # @mount a .\Games\roadwar -t floppy
            'eob2': ['Eye of the Beholder (1991).zip'
                     ],  # mount a .\Games\eob1\ -t floppy
            'bardtal2':
            ["Bard's Tale 1, The - Tales Of The Unknown (1987).zip"],
            # mount a .\Games\bardtal1 -t floppy
            'bardtal3': [
                "Bard's Tale 1, The - Tales Of The Unknown (1987).zip",
                # mount a .\Games\bardtal1 -t floppy
                "Bard's Tale 2, The - The Destiny Knight (1988).zip"
            ],
            # @mount b .\Games\bardtal2 -t floppy
            'MM2': ['Might and Magic - Book 1 (1986).zip'
                    ],  # mount a .\Games\MM1\ -t floppy
            'vengexca': ['Spirit of Excalibur (1990).zip'
                         ],  # @mount a .\Games\spirexc -t floppy
            'WC2DLX':
            ['Wing Commander (1990).zip'],  # mount a .\Games\WC\WING\GAMEDAT\
            'darkdes2': ['Dark Designs I - Grelminars Staff (1990).zip'
                         ],  # mount a .\Games\darkdes1 -t floppy
            'whalvoy2':
            ["Whale's Voyage (1993).zip"]  # @mount e .\Games\whalvoy1\WVCD
        }
        if self.game in needsFirstGame:
            for previousGameZip in needsFirstGame[self.game]:
                # unzip game dependency
                with ZipFile(
                        os.path.join(
                            util.getCollectionGamesDir(self.exoCollectionDir,
                                                       self.collectionVersion),
                            previousGameZip), 'r') as zipFile:
                    # Extract all the contents of zip file in current directory
                    self.logger.log("  unzipping previous game" +
                                    previousGameZip)
                    zipFile.extractall(path=util.getCollectionGamesDir(
                        self.exoCollectionDir, self.collectionVersion))
                # copy its directory or directory part to the inside of the second game dir
                shutil.move(
                    os.path.join(
                        util.getCollectionGamesDir(self.exoCollectionDir,
                                                   self.collectionVersion),
                        self.fullnameToGameDir.get(
                            os.path.splitext(previousGameZip)[0])),
                    os.path.join(self.getLocalGameOutputDir()))

    ######################################

    ###### Post-conversion functions #####

    # Post-conversion operations for a given game for various conversion types
    def postConversion(self):
        if self.conversionType == util.retropie:
            self.postConversionForRetropie()
        elif self.conversionType in [util.esoteric, util.simplemenu]:
            self.postConversionForOpenDingux()
        elif self.conversionType == util.mister:
            self.postConversionForMister()
        elif self.conversionType == util.recalbox:
            self.postConversionForRecalbox()
        elif self.conversionType == util.batocera:
            self.postConversionForBatocera()
        elif self.conversionType == util.emuelec:
            self.postConversionForEmuelec()

    # Post-conversion for Emuelec for a given game
    def postConversionForEmuelec(self):
        self.logger.log("  Emuelec post-conversion")
        # create pcdata and pc subfolders in outputdir
        if not os.path.exists(os.path.join(self.outputDir, 'pcdata')):
            os.mkdir(os.path.join(self.outputDir, 'pcdata'))
        if not os.path.exists(os.path.join(self.outputDir, 'pc')):
            os.mkdir(os.path.join(self.outputDir, 'pc'))
        # move *.pc folder to pcdata folder
        shutil.move(os.path.join(self.getLocalGameOutputDir()),
                    os.path.join(self.outputDir, 'pcdata'))
        os.rename(os.path.join(self.outputDir, 'pcdata', self.game + '.pc'),
                  os.path.join(self.outputDir, 'pcdata', self.game))
        # move *.bat *.map and *.cfg to pc/*.pc folder and rename *.cfg to dosbox-SDL2.conf
        emuelecConfOutputDir = os.path.join(
            self.outputDir, 'pc', self.genre, self.game +
            ".pc") if self.useGenreSubFolders else os.path.join(
                self.outputDir, 'pc', self.game + ".pc")
        if not os.path.exists(emuelecConfOutputDir):
            os.makedirs(emuelecConfOutputDir)
        shutil.move(
            os.path.join(self.outputDir, 'pcdata', self.game, 'dosbox.bat'),
            emuelecConfOutputDir)
        shutil.move(
            os.path.join(self.outputDir, 'pcdata', self.game, 'dosbox.cfg'),
            os.path.join(emuelecConfOutputDir, 'dosbox-SDL2.conf'))
        shutil.copy2(
            os.path.join(self.outputDir, 'pcdata', self.game,
                         util.getCleanGameID(self.metadata, '.txt')),
            emuelecConfOutputDir)
        if os.path.exists(
                os.path.join(self.outputDir, 'pcdata', self.game,
                             'mapper.map')):
            shutil.move(
                os.path.join(self.outputDir, 'pcdata', self.game,
                             'mapper.map'), emuelecConfOutputDir)
        # modify dosbox-SDL2.conf to add mount c /storage/roms/pcdata/game at the beginning of autoexec.bat
        dosboxCfg = open(
            os.path.join(emuelecConfOutputDir, 'dosbox-SDL2.conf'), 'a')
        # add mount c at end of dosbox.cfg
        romsFolder = util.getRomsFolderPrefix(self.conversionType,
                                              self.conversionConf)
        emuelecGameDir = romsFolder + "/" + self.genre + "/" + self.game if self.useGenreSubFolders else romsFolder + "/" + self.game
        dosboxCfg.write("mount c " + emuelecGameDir + "\n")
        dosboxCfg.write("c:\n")
        # copy all instructions from dosbox.bat to end of dosbox.cfg
        dosboxBat = open(os.path.join(emuelecConfOutputDir, "dosbox.bat"),
                         'r')  # retroarch dosbox.bat
        for cmdLine in dosboxBat.readlines():
            dosboxCfg.write(cmdLine)
        # delete dosbox.bat
        dosboxCfg.close()
        dosboxBat.close()
        # delete dosbox.bat
        os.remove(os.path.join(emuelecConfOutputDir, "dosbox.bat"))

    # Post-conversion for Recalbox for a given game
    def postConversionForRecalbox(self):
        self.logger.log("  Recalbox post-conversion")
        p2kTemplate = open(
            os.path.join(self.scriptDir, 'data', 'P2K.template.txt'), 'r')
        p2kFile = open(os.path.join(self.getLocalParentOutputDir(),
                                    self.game + '.pc.p2k.cfg'),
                       'w',
                       encoding='utf-8')
        for line in p2kTemplate.readlines():
            p2kFile.write(line.replace('{GameID}', self.metadata.name))
        p2kFile.close()
        p2kTemplate.close()

    def postConversionForBatocera(self):
        self.logger.log("  Batocera post-conversion")
        pad2key = os.path.join(self.scriptDir, 'data', 'padto.keys')
        shutil.copy2(pad2key, self.getLocalGameOutputDir())
        # TODO handle mapper type joy / nojoy
        # TODO load keyb2joypad file
        # TODO convert pad2key chosen file from json to python object
        # TODO replace modified controls in pad2key file
        # TODO needs to add mapping for ctrl + F4 in pad2key

    # Post-conversion for MiSTeR for a given game
    def postConversionForMister(self):
        self.logger.log("  MiSTer post-conversion")
        # Remove any C: from dosbox.bat, rename to launch.bat, remove dosbox.cfg
        os.remove(os.path.join(self.getLocalGameOutputDir(), 'dosbox.cfg'))
        # Move CDs to cdgames/gamefolder and rename commands
        mister.batsAndMounts(self)
        shutil.move(
            os.path.join(self.getLocalGameOutputDir(),
                         util.getCleanGameID(self.metadata, '.txt')),
            os.path.join(self.getLocalGameOutputDir(), '2_About.txt'))
        # Remove unused CDs
        mister.removeUnusedCds(self.game, self.getLocalGameDataOutputDir(),
                               self.logger)
        # Remove any COMMAND.COM and CHOICE.EXE files, as they are not compatible with MiSTeR
        if self.isWin3x:
            tobeRemoved = [
                file for file in os.listdir(self.getLocalGameOutputDir())
                if file.lower() in ['command.com', 'choice.exe']
            ]
            for fileToRemove in tobeRemoved:
                self.logger.log("    remove non-compatible file %s" %
                                fileToRemove)
                os.remove(
                    os.path.join(self.getLocalGameOutputDir(), fileToRemove))
        else:
            tobeRemoved = [
                file for file in os.listdir(
                    os.path.join(self.getLocalGameDataOutputDir()))
                if file.lower() in ['command.com', 'choice.exe']
            ]
            for fileToRemove in tobeRemoved:
                self.logger.log("    remove non-compatible file %s" %
                                fileToRemove)
                os.remove(
                    os.path.join(self.getLocalGameDataOutputDir(),
                                 fileToRemove))
        # Create about.jpg combining About.txt and pic of the game
        if self.metadata.frontPic is not None:
            cover = os.path.join(
                self.getLocalGameOutputDir(),
                '5_About' + os.path.splitext(self.metadata.frontPic)[-1])
            shutil.move(
                os.path.join(self.outputDir, 'downloaded_images',
                             ntpath.basename(self.metadata.frontPic)), cover)
            aboutTxt = open(os.path.join(self.getLocalGameOutputDir(),
                                         '2_About.txt'),
                            'r',
                            encoding='utf-8')
            mister.text2png(
                self.scriptDir, aboutTxt.read(), cover,
                os.path.join(self.getLocalGameOutputDir(), '2_About.jpg'))
            aboutTxt.close()
            os.remove(os.path.join(self.getLocalGameOutputDir(),
                                   '2_About.txt'))
            os.remove(
                os.path.join(
                    self.getLocalGameOutputDir(),
                    '5_About' + os.path.splitext(self.metadata.frontPic)[-1]))

        misterCleanName = util.getCleanGameID(self.metadata, '').replace('+', '').replace("'", '').replace('µ',
                                                                                                           'mu') \
            .replace('¿', '').replace('é', 'e').replace('á', '').replace('ō', 'o').replace('#', '').replace('½', '') \
            .replace('$', '').replace('à', 'a').replace('&', 'and').replace(',', '')

        util.misterCleanNameToGameDir[misterCleanName] = self.game

        if not os.path.exists(os.path.join(self.outputDir, 'games')):
            os.mkdir(os.path.join(self.outputDir, 'games'))

        if self.conversionConf['preExtractGames']:
            # Create zero sized zip as the game will be pre-extracted
            open(
                os.path.join(self.outputDir, 'games',
                             misterCleanName + '.zip'), 'w').close()
            # Move game.pc folder to games-data
            if not os.path.exists(os.path.join(self.outputDir, 'games-data')):
                os.mkdir(os.path.join(self.outputDir, 'games-data'))
            shutil.move(
                os.path.join(self.getLocalGameOutputDir()),
                os.path.join(self.outputDir, 'games-data', misterCleanName))
        else:
            # Zip internal game dir to longgamename.zip
            self.logger.log('    Rezipping game to %s.zip' % misterCleanName)
            shutil.make_archive(
                os.path.join(self.getLocalParentOutputDir(), misterCleanName),
                'zip', self.getLocalGameOutputDir())
            # Delete everything unrelated
            shutil.rmtree(os.path.join(self.getLocalGameOutputDir()))
            # Move archive to games folder
            shutil.move(
                os.path.join(self.getLocalParentOutputDir(),
                             misterCleanName + '.zip'),
                os.path.join(self.outputDir, 'games'))

    # Post-conversion for openDingux for a given game
    def postConversionForOpenDingux(self):
        self.logger.log("  opendingux post-conversion")
        openDinguxPicDir = '.previews' if self.conversionType == util.esoteric else '.media'
        # Copy image to opendingux img folder for game.pc
        distPicPath = os.path.join(self.getLocalParentOutputDir(),
                                   openDinguxPicDir)
        if not os.path.exists(distPicPath):
            os.mkdir(distPicPath)
        shutil.copy2(self.metadata.frontPic,
                     os.path.join(distPicPath, self.game + '.pc.png'))
        # Resize image
        util.resize(os.path.join(distPicPath, self.game + '.pc.png'))
        # Copy image to opendingux img folder for game.pc/dosbox.bat
        dosboxBatPicPath = os.path.join(self.getLocalGameOutputDir(),
                                        openDinguxPicDir)
        if not os.path.exists(dosboxBatPicPath):
            os.mkdir(dosboxBatPicPath)
        shutil.copy2(os.path.join(distPicPath, self.game + '.pc.png'),
                     os.path.join(dosboxBatPicPath, 'dosbox.png'))
        # Generate RG350 mapper
        mapper = open(os.path.join(self.getLocalGameOutputDir(), "mapper.map"),
                      'w')
        mapper.write('key_space "key 308"\n')
        mapper.write('key_lshift "key 32"\n')
        mapper.write('key_lctrl "key 304"\n')
        mapper.write('key_lalt "key 306"\n')
        mapper.write('key_esc "key 27"\n')
        mapper.write('key_enter "key 13"\n')
        mapper.write('key_up "key 273"\n')
        mapper.write('key_down "key 274"\n')
        mapper.write('key_right "key 275"\n')
        mapper.write('key_left "key 276"\n')
        mapper.write('key_n "key 9"\n')
        mapper.write('key_y "key 8"\n')
        mapper.close()

    # POst-conversion for Retropie for a given game
    def postConversionForRetropie(self):
        self.logger.log("  retropie post-conversion")
        dosboxCfg = open(
            os.path.join(self.getLocalGameOutputDir(), "dosbox.cfg"), 'a')
        # add mount c at end of dosbox.cfg
        romsFolder = util.getRomsFolderPrefix(self.conversionType,
                                              self.conversionConf)
        retropieGameDir = romsFolder + "/" + self.genre + "/" + self.game + ".pc" if self.useGenreSubFolders else romsFolder + "/" + self.game + ".pc"
        dosboxCfg.write("mount c " + retropieGameDir + "\n")
        dosboxCfg.write("c:\n")
        # copy all instructions from dosbox.bat to end of dosbox.cfg
        dosboxBat = open(
            os.path.join(self.getLocalGameOutputDir(), "dosbox.bat"),
            'r')  # retroarch dosbox.bat
        for cmdLine in dosboxBat.readlines():
            dosboxCfg.write(cmdLine)
        # delete dosbox.bat
        dosboxCfg.close()
        dosboxBat.close()
        os.remove(os.path.join(self.getLocalGameOutputDir(), "dosbox.bat"))
        # move dosbox.cfg to {game}.conf at top level
        shutil.move(
            os.path.join(self.getLocalGameOutputDir(), "dosbox.cfg"),
            os.path.join(self.getLocalParentOutputDir(),
                         util.getCleanGameID(self.metadata, '.conf')))
Exemplo n.º 4
0
class ExoDOSConverter():
    def __init__(self, games, exoDosDir, gamesDosDir, outputDir,
                 metadataHandler, logger):
        self.games = games
        self.exoDosDir = exoDosDir
        self.logger = logger
        self.gamesDosDir = gamesDosDir
        self.outputDir = outputDir
        self.metadataHandler = metadataHandler
        self.confConverter = ConfConverter(self.games, self.exoDosDir,
                                           self.outputDir, self.logger)

    def convertGames(self):
        self.logger.log("Loading metadatas...")
        self.metadataHandler.parseXmlMetadata()
        self.logger.log("")
        if not os.path.exists(os.path.join(self.outputDir,
                                           'downloaded_images')):
            os.mkdir(os.path.join(self.outputDir, 'downloaded_images'))
        if not os.path.exists(os.path.join(self.outputDir, 'manuals')):
            os.mkdir(os.path.join(self.outputDir, 'manuals'))

        gamelist = self.metadataHandler.initXml(self.outputDir)

        count = 1
        total = len(self.games)
        errors = dict()

        for game in self.games:
            try:
                self.convertGame(game, gamelist, total, count)
            except:
                self.logger.log('Error %s while converting game %s' %
                                (sys.exc_info()[0], game))
                excInfo = traceback.format_exc()
                errors[game] = excInfo

            count = count + 1

        self.metadataHandler.writeXml(self.outputDir, gamelist)
        self.logger.log('\n<--------- Finished Process --------->')

        if len(errors.keys()) > 0:
            self.logger.log('\n<--------- Errors rundown --------->')
            self.logger.log('%i errors were found during process' %
                            len(errors.keys()))
            self.logger.log('See error log in your outputDir')
            logFile = open(os.path.join(self.outputDir, 'error_log.txt'), 'w')
            for key in list(errors.keys()):
                logFile.write("Found error when processing %s" % key + " :\n")
                logFile.write(errors.get(key))
                logFile.write("\n")
            logFile.close()
        elif os.path.exists(os.path.join(self.outputDir, 'error_log.txt')):
            # Delete log from previous runs
            os.remove(os.path.join(self.outputDir, 'error_log.txt'))

    def convertGame(self, game, gamelist, total, count):
        genre = self.metadataHandler.buildGenre(
            self.metadataHandler.metadatas.get(game))

        if not os.path.exists(os.path.join(self.outputDir, genre,
                                           game + ".pc")):
            self.logger.log(">>> %i/%i >>> %s: starting conversion" %
                            (count, total, game))

            self.metadataHandler.processGame(game, gamelist, genre,
                                             self.outputDir)

            if not os.path.exists(os.path.join(self.exoDosDir, "Games", game)):
                self.logger.log("  needs installation...")
                #automatic F and N
                subprocess.call("cmd /C (echo Y&echo F&echo N) | Install.bat",
                                cwd=os.path.join(self.gamesDosDir, game),
                                shell=False)
                self.logger.log("  installed")
            else:
                self.logger.log("  already installed")

            self.copyGameFiles(game, genre)
            self.confConverter.process(game, genre)
        else:
            self.logger.log(
                ">>> %i/%i >>> %s: already converted in output folder" %
                (count, total, game))
            self.metadataHandler.processGame(game, gamelist, genre,
                                             self.outputDir)

        self.logger.log("")

    def copyGameFiles(self, game, genre):
        dest = os.path.join(self.outputDir, genre, game + ".pc", game)
        self.logger.log("  copy game data")
        # Copy game files in game.pc/game
        shutil.copytree(os.path.join(self.exoDosDir, "Games", game), dest)
        self.logger.log("  copy dosbox conf")
        # Copy dosbox.conf in game.pc
        shutil.copy2(
            os.path.join(self.exoDosDir, "Games", "!dos", game, "dosbox.conf"),
            os.path.join(dest, "dosbox.conf"))