Esempio n. 1
0
class TTS_CLI:
    def __init__(self):
        self.preferences = Preferences()

        parser = argparse.ArgumentParser(
            description="Manipulate Tabletop Simulator files")
        parser.add_argument("-d",
                            "--directory",
                            help="Override TTS cache directory")
        parser.add_argument("-l",
                            "--loglevel",
                            help="Set logging level",
                            choices=['debug', 'info', 'warn', 'error'])
        subparsers = parser.add_subparsers(dest='parser',
                                           title='command',
                                           description='Valid commands.')
        subparsers.required = True

        # add list command
        parser_list = subparsers.add_parser('list',
                                            help="List installed mods.",
                                            description='''
    List installed mods.
    If no id is provided, then this will return a list of all installed modules.
    If an id is provided, then this will list the contents of that modules.
    ''')
        group_list = parser_list.add_mutually_exclusive_group()
        group_list.add_argument("-w",
                                "--workshop",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.workshop,
                                help="List workshop files (the default).")
        group_list.add_argument("-s",
                                "--save",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.save,
                                help="List saves.")
        group_list.add_argument("-c",
                                "--chest",
                                action="store_const",
                                metavar='save_type',
                                dest='save_type',
                                const=SaveType.chest,
                                help="List chest files.")

        parser_list.add_argument("id",
                                 nargs='?',
                                 help="ID of specific mod to list details of.")
        parser_list.set_defaults(func=self.do_list)

        # export command
        parser_export = subparsers.add_parser(
            'export',
            help="Export a mod.",
            description='Export a mod in a format suitible for later import.')
        group_export = parser_export.add_mutually_exclusive_group()
        group_export.add_argument("-w",
                                  "--workshop",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.workshop,
                                  help="ID is of workshop file (the default).")
        group_export.add_argument("-s",
                                  "--save",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.save,
                                  help="ID is of savegame file.")
        group_export.add_argument("-c",
                                  "--chest",
                                  action="store_const",
                                  dest='save_type',
                                  metavar='save_type',
                                  const=SaveType.chest,
                                  help="ID is of chest file.")
        parser_export.add_argument(
            "id", help="ID of mod/name of savegame to export.")
        parser_export.add_argument("-o",
                                   "--output",
                                   help="Location/file to export to.")
        parser_export.add_argument("-f",
                                   "--force",
                                   action="store_true",
                                   help="Force creation of export file.")
        parser_export.add_argument(
            "-d",
            "--download",
            action="store_true",
            help="Attempt to download missing cache files. (EXPERIMENTAL)")
        parser_export.set_defaults(func=self.do_export)

        # import command
        parser_import = subparsers.add_parser(
            'import',
            help="Import a mod.",
            description="Import an previously exported mod.")
        parser_import.add_argument("file", help="Mod pak file to import.")
        parser_import.set_defaults(func=self.do_import)

        # download command
        parser_download = subparsers.add_parser(
            'download',
            help='Download mod files.',
            description=
            'Attempt to download any missing files for an installed mod.')
        group_download = parser_download.add_mutually_exclusive_group()
        group_download.add_argument("-w",
                                    "--workshop",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.workshop,
                                    help="ID is of workshop file.")
        group_download.add_argument("-s",
                                    "--save",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.save,
                                    help="ID is of savegame file.")
        group_download.add_argument("-c",
                                    "--chest",
                                    action="store_const",
                                    dest='save_type',
                                    metavar='save_type',
                                    const=SaveType.chest,
                                    help="ID is of chest file.")
        group_download_target = parser_download.add_mutually_exclusive_group(
            required=True)
        group_download_target.add_argument("-a",
                                           "--all",
                                           action="store_true",
                                           help="Download all.")
        group_download_target.add_argument(
            "id", nargs='?', help="ID of mod/name of savegame to download.")
        parser_download.set_defaults(func=self.do_download)

        # cache command
        parser_cache = subparsers.add_parser('cache',
                                             help='Work with the cache.')
        subparsers_cache = parser_cache.add_subparsers(
            dest='parser_cache',
            title='cache_command',
            description='Valid sub-commands.')
        subparsers_cache.required = True
        parser_cache_create = subparsers_cache.add_parser(
            'create', help='(re)create cache directory')
        parser_cache_create.set_defaults(func=self.do_cache_create)

        # config command
        parser_config = subparsers.add_parser('config',
                                              help='Configure tts manager.')
        subparsers_config = parser_config.add_subparsers(
            dest='parser_config',
            title='config_command',
            description='Valid sub-commands.')
        subparsers_config.required = True
        parser_config_list = subparsers_config.add_parser(
            'list', help='List configuration.')
        parser_config_list.set_defaults(func=self.do_config_list)
        parser_config_validate = subparsers_config.add_parser(
            'validate', help='Validate configuration.')
        parser_config_validate.set_defaults(func=self.do_config_validate)
        parser_config_reset = subparsers_config.add_parser(
            'reset', help='Reset configuration.')
        parser_config_reset.set_defaults(func=self.do_config_reset)
        parser_config_set = subparsers_config.add_parser(
            'set', help='Set configuration parameters.')
        parser_config_set.set_defaults(func=self.do_config_set)
        parser_config_set.add_argument("-m",
                                       "--mod_location",
                                       choices=['documents', 'gamedata'],
                                       help="Where mods are stored.")
        parser_config_set.add_argument("-t",
                                       "--tts_location",
                                       help="TTS Install directory")

        args = parser.parse_args()

        # set logging
        if args.loglevel:
            logmap = {
                'debug': logging.DEBUG,
                'info': logging.INFO,
                'warn': logging.WARN,
                'error': logging.ERROR
            }
            logger().setLevel(logmap[args.loglevel])
        else:
            logger().setLevel(logging.WARN)

        # load filesystem values
        if args.directory:
            self.filesystem = FileSystem(os.path.abspath(args.directory))
        else:
            self.filesystem = self.preferences.get_filesystem()

        if (args.parser == 'list'
                or args.parser == 'export') and not args.save_type:
            # set default
            args.save_type = SaveType.workshop

        if (args.parser == 'config' and args.parser_config == 'set'
                and not args.mod_location and not args.tts_location):
            parser_config_set.error("At least one of -m or -t is required.")

        rc, message = args.func(args)
        if message:
            print(message)
        sys.exit(rc)

    def do_config_set(self, args):
        if args.mod_location:
            self.preferences.locationIsUser = args.mod_location == 'documents'
        if args.tts_location:
            self.preferences.TTSLocation = args.mod_location
        self.preferences.save()
        return 0, "Preferences set"

    def do_config_reset(self, args):
        self.preferences.reset()
        return 0, "Preferences Reset."

    def do_config_list(self, args):
        return 0, self.preferences

    def do_config_validate(self, args):
        if self.preferences.validate():
            return 0, "Configuration validated OK."
        else:
            return 1, "Configuration failed to validate."

    def do_cache_create(self, args):
        try:
            self.filesystem.create_dirs()
        except OSError as exception:
            return 1, "OS error: {0}".format(exception)
        return 0, "All directories created OK."

    def list_by_type(self, save_type):
        result = ""
        for (name, id) in describe_files_by_type(self.filesystem, save_type):
            result += "\n%s (%s)" % (name, id)
        return 0, result

    def list_item(self, data, filename, ident):
        if not data:
            # self.list_installed()
            return
        save = Save(savedata=data,
                    ident=ident,
                    filename=filename,
                    filesystem=self.filesystem)
        return 0, save

    def do_download(self, args):
        successful = True
        if not args.all:
            if not args.save_type:
                args.save_type = self.filesystem.get_json_filename_type(
                    args.id)
            if not args.save_type:
                return 1, "Unable to determine type of id %s" % args.id
            successful = download_file(self.filesystem, args.id,
                                       args.save_type)
        else:
            if args.save_type:
                for ident in self.filesystem.get_filenames_by_type(
                        args.save_type):
                    if not download_file(self.filesystem, ident,
                                         args.save_type):
                        successful = False
                        break
            else:
                for save_type in SaveType:
                    for ident in self.filesystem.get_filenames_by_type(
                            save_type):
                        if not download_file(self.filesystem, ident,
                                             save_type):
                            successful = False
                            break

        if successful:
            return 0, "All files downloaded."
        else:
            return 1, "Some files failed to download."

    def do_list(self, args):
        rc = 0
        result = None

        if not args.id:
            rc, result = self.list_by_type(args.save_type)
        else:
            if not args.save_type:
                args.save_type = self.filesystem.get_json_filename_type(
                    args.id)
            if not args.save_type:
                return 1, "Unable to determine type of id %s" % args.id
            filename = self.filesystem.get_json_filename_for_type(
                args.id, args.save_type)
            data = load_json_file(filename)
            rc, result = self.list_item(data, filename, args.id)
        return rc, result

    def do_export(self, args):
        filename = None
        if args.output:
            if os.path.isdir(args.output):
                filename = os.path.join(args.output, args.id + ".pak")
            else:
                filename = args.output
        else:
            filename = args.id + ".pak"

        data = None
        json_filename = None
        if not args.save_type:
            args.save_type = self.filesystem.get_json_filename_type(args.id)
        if not args.save_type:
            return 1, "Unable to determine type of id %s" % args.id

        json_filename = self.filesystem.get_json_filename_for_type(
            args.id, args.save_type)

        if not json_filename:
            return 1, "Unable to find filename for id %s (wrong -s/-w/-c specified?)" % args.id
        data = load_json_file(json_filename)
        if not data:
            return 1, "Unable to load data for file %s" % json_filename

        save = Save(savedata=data,
                    filename=json_filename,
                    ident=args.id,
                    save_type=args.save_type,
                    filesystem=self.filesystem)
        if not save.isInstalled:
            if not args.download:
                return 1, "Unable to find all urls required by %s. Rerun with -d to try and download them or open it within TTS.\n%s" % (
                    args.id, save)
            else:
                logger().info("Downloading missing files...")
                successful = save.download()
                if successful:
                    logger().info("Files downloaded successfully.")
                else:
                    return 1, "Some files failed to download"
        if os.path.isfile(filename) and not args.force:
            return 1, "%s already exists. Please specify another file or use '-f'" % filename
        logger().info("Exporting json file %s to %s" % (args.id, filename))
        save.export(filename)
        # TODO: exception handling
        return 0, "Exported %s to %s" % (args.id, filename)

    def do_import(self, args):
        return 0, save_helper.importPak(self.filesystem, args.file)