Пример #1
0
    async def down_ex(self, session, source, region, target, extract):
        dl_target = os.path.join(self.dl_dir, region, target)
        check_target_path(dl_target)
        
        async with session.get(source) as resp:
            assert resp.status == 200
            if self.stdout_log:
                print(f'Download {dl_target} from {source}')
            
            try:
                with open(dl_target, 'wb') as f:
                    f.write(await resp.read())
            except:
                with open(os.path.join(dl_target, os.path.basename(dl_target)), 'wb') as f:
                    f.write(await resp.read())

            _, ext = os.path.splitext(dl_target)
            if len(ext) > 0:
                if self.stdout_log:
                    print('Skipped', dl_target)
                return None
            if extract is None:
                extract = os.path.dirname(target).replace('/', '_')
            ex_target = os.path.join(region, extract)
            am = AssetsManager(dl_target)
            texture_2d = {}
            for asset in am.assets.values():
                for obj in asset.objects.values():
                    result = unpack(obj, ex_target, self.ex_dir, self.ex_img_dir, stdout_log=self.stdout_log)
                    if result and result[1]:
                        texture_2d[result[0]] = result[1]
            if len(texture_2d) > 0:
                return texture_2d
Пример #2
0
def extract_assets(src):
	# load source
	am = AssetsManager(src)

	# iterate over assets
	for asset in am.assets.values():
		# assets without container / internal path will be ignored for now
		if not asset.container:
			continue

		# check which mode we will have to use
		num_cont = sum(1 for obj in asset.container.values() if obj.type in TYPES)
		num_objs = sum(1 for obj in asset.objects.values() if obj.type in TYPES)

		# check if container contains all important assets, if yes, just ignore the container
		if num_objs <= num_cont * 2:
			for asset_path, obj in asset.container.items():
				fp = os.path.join(DST, *asset_path.split('/')[IGNOR_DIR_COUNT:])
				export_obj(obj, fp)

		# otherwise use the container to generate a path for the normal objects
		else:
			extracted = []
			# find the most common path
			occurence_count = Counter(os.path.splitext(asset_path)[0] for asset_path in asset.container.keys())
			local_path = os.path.join(DST, *occurence_count.most_common(1)[0][0].split('/')[IGNOR_DIR_COUNT:])

			for obj in asset.objects.values():
				if obj.path_id not in extracted:
					extracted.extend(export_obj(obj, local_path, append_name=True))
Пример #3
0
def mp_download_extract(target, source_list, extract, region, dl_dir, overwrite, ex_dir, ex_img_dir, stdout_log):
    base_dl_target = os.path.join(dl_dir, region, target)
    check_target_path(base_dl_target)

    downloaded = []
    for idx, source in enumerate(source_list):
        if len(source_list) > 1:
            dl_target = base_dl_target + str(idx)
        else:
            dl_target = base_dl_target
        if overwrite or not os.path.exists(dl_target):
            if stdout_log:
                print(f'Download {dl_target} from {source}', flush=True)
            try:
                urllib.request.urlretrieve(source, dl_target)
            except Exception as e:
                print(str(e))
                continue
        downloaded.append(dl_target)
        print('.', end='', flush=True)

#     return target, extract, region, downloaded
# def mp_extract(target, extract, downloaded, region, ex_dir, ex_img_dir, stdout_log):

    if extract is None:
        extract = os.path.dirname(target).replace('/', '_')
    ex_target = os.path.join(region, extract)
    texture_2d = {}
    for dl_target in downloaded:
        am = AssetsManager(dl_target)
        for asset in am.assets.values():
            for obj in asset.objects.values():
                unpack(obj, ex_target, ex_dir, ex_img_dir, texture_2d, stdout_log=stdout_log)
        print('-', end='', flush=True)
    return texture_2d
Пример #4
0
def unpack_asset(file_path, destination_folder, root=None, source_folder=None):
    # load that file via AssetsManager
    am = AssetsManager(file_path)

    # iterate over all assets and named objects
    for asset in am.assets.values():
        for obj in asset.objects.values():
            # only process specific object types
            # print(obj.type, obj.container)
            obj_type_str = str(obj.type)
            if obj_type_str in unpack_dict:
                # parse the object data
                data = obj.read()

                # create destination path
                if root and source_folder:
                    intermediate = root.replace(source_folder, '')
                else:
                    intermediate = ''

                if obj_type_str == 'GameObject':
                    dest = os.path.join(destination_folder, intermediate)
                    unpack_dict[obj_type_str](data, dest)
                elif data.name:
                    dest = os.path.join(destination_folder, intermediate, data.name)
                    unpack_dict[obj_type_str](data, dest)
Пример #5
0
def load_mesh(filepath, require_name=None):
    am = AssetsManager(filepath)
    for obj in am.objects:
        if obj.type == "Mesh":
            objdata = obj.read()
            if require_name and require_name != objdata.name:
                continue
            data = objdata.export().splitlines()
            return data
Пример #6
0
 def extract_target(self, dl_target, ex_target, texture_2d):
     am = AssetsManager(dl_target)
     for asset in am.assets.values():
         for obj in asset.objects.values():
             unpack(obj,
                    ex_target,
                    self.ex_dir,
                    self.ex_img_dir,
                    texture_2d,
                    stdout_log=self.stdout_log)
Пример #7
0
    async def down_ex(self, session, source, region, target, extract):
        dl_target = os.path.join(self.dl_dir, region, target)
        check_target_path(dl_target)
        async with session.get(source) as resp:
            assert resp.status == 200
            if self.stdout_log:
                print(f'Download {dl_target}, {source}')
            with open(dl_target, 'wb') as f:
                f.write(await resp.read())

            ex_target = os.path.join(self.ex_dir, region, extract)
            am = AssetsManager(dl_target)
            for asset in am.assets.values():
                for obj in asset.objects.values():
                    unpack(obj, ex_target, self.stdout_log)
Пример #8
0
Файл: x.py Проект: izanavi/dldm
def _do(src):
    global g_containers
    am = AssetsManager(src)
    for asset in am.assets.values():
        for asset_path, obj in asset.container.items():
            contain(obj, asset_path)
            if not obj.path_id:
                af = obj.assets_file
                for o in af.objects.values():
                    #name = o.read().name
                    name = o.read().name.split('/')[-1]
                    if name:
                        path = asset_path + '/' + name
                    else:
                        path = asset_path + '/' + '_'
                    export_obj(o, path)
                continue
            export_obj(obj, asset_path)
Пример #9
0
Файл: x.py Проект: izanavi/dldm
def read_orig_file(src):
    global g_containers
    global queue, extracted
    am = AssetsManager(src)
    queue = {}
    extracted = {}

    for asset in am.assets.values():
        for o in asset.objects.values():
            dprint(o, o.read().name, o.path_id)
            queue[o.path_id] = o

    for asset in am.assets.values():
        for asset_path, obj in asset.container.items():
            if obj.path_id:
                export_obj(obj, asset_path, dup=True)
            else:
                print('obj no path_id')
                raise
    if ALLID:
        for _id, obj in queue.items():
            if obj and _id not in extracted:
                export_obj(obj, '__/' + str(_id), noext=True)
def extractAssets(sourcePath: str, targetPath: str):
    for root, directories, files in os.walk(sourcePath):
        for filename in files:

            filePath = os.path.join(root, filename)
            print("Reading " + filePath + "...")
            try:

                manager = AssetsManager(filePath)
                for asset in manager.assets.values():
                    for obj in asset.objects.values():

                        ##  Extract texture
                        if obj.type in ["Texture2D", "Sprite"]:

                            outputPath = root.replace(sourcePath, targetPath)
                            if not os.path.exists(outputPath):
                                os.makedirs(outputPath)

                            data = obj.read()
                            outputPath = os.path.join(outputPath, data.name)
                            outputPath, extension = os.path.splitext(
                                outputPath)
                            outputPath = outputPath + ".png"

                            if path.exists(outputPath):
                                continue

                            print("  Saving " + outputPath + "...")
                            image = data.image
                            image.save(outputPath)
                            continue

                        ##----------------------------------------------------------------------------------------
                        ##  Extract text
                        if obj.type in ["TextAsset"]:

                            outputPath = root.replace(sourcePath, targetPath)
                            if not os.path.exists(outputPath):
                                os.makedirs(outputPath)

                            data = obj.read()
                            outputPath = os.path.join(outputPath, data.name)
                            outputPath, extension = os.path.splitext(
                                outputPath)
                            outputPath = outputPath + ".txt"

                            if path.exists(outputPath):
                                continue

                            print("  Saving " + outputPath + "...")
                            with open(outputPath, "wb") as file:
                                file.write(data.script)

                            continue

                        ##----------------------------------------------------------------------------------------
                        if obj.type in ["AudioClip"]:

                            filePath = root.replace(sourcePath, targetPath)
                            if not os.path.exists(filePath):
                                os.makedirs(filePath)

                            data = obj.read()
                            for key in data.samples:

                                outputPath = os.path.join(filePath, key)
                                outputPath, extension = os.path.splitext(
                                    outputPath)
                                outputPath = outputPath + ".wav"

                                if path.exists(outputPath):
                                    continue

                                print("  Saving " + outputPath + "...")
                                sampleData = data.samples[key]
                                with open(outputPath, "wb") as file:
                                    file.write(sampleData)

                        ##----------------------------------------------------------------------------------------
                        ##  Any other format are not support yet
                        print("  ### Asset type '" + str(obj.type) +
                              "' not support yet...")

            except KeyboardInterrupt:
                sys.exit()

            except struct.error as err:
                print("### " + str(err))

            except:
                print("### Unknown error...")
Пример #11
0
def load_images(filepath: str):
    am = AssetsManager(filepath)
    for obj in am.objects:
        if obj.type == "Texture2D":
            yield obj.read()
    def generateCards(self, imagePath, gwentPath):
        MAX_CARD_STRENGTH = 99
        MAX_CARD_PROVISIONS = 99
        MAX_CARD_ARMOR = 99

        # Save chosen paths to a file
        with open('savedPaths.txt', 'w') as pathFile:
            pathFile.write(gwentPath + '\n')
            pathFile.write(imagePath + '\n')

        # Select all or certain cards
        if self.allImages.get() and self.numberofFiles:
            cardsToGenerate = self.cardData
        else:
            cardsToGenerate = []
            for card in self.cardView.selection():
                cardsToGenerate.append(self.cardView.set(card, column='ID'))

        numberOfCards = len(cardsToGenerate)
        cardsCompleted = 0

        # Raise and error if gwent path, save path, or json file are not valid and if no cards were slected
        if not gwentPath or not (
                gwentPath.endswith("GWENT The Witcher Card Game")
                or gwentPath.endswith("Gwent")):
            messagebox.showerror(
                "No Gwent Path",
                "Path to Gwent installation was invalid or not specified.")
            return
        if not imagePath:
            messagebox.showerror("No Image Path",
                                 "No path to save images to was specified.")
            return
        if not self.numberofFiles:
            messagebox.showerror("No JSON Data",
                                 "No JSON card data file was selected.")
            return
        if not numberOfCards and not self.allImages.get():
            messagebox.showerror("No Card Selected", "No cards were selected.")
            return
        # Check to see if you have 4K graphics installed
        if (self.imageQuality.get() == "Uber (4K)") and not isfile("".join([
                gwentPath,
                "/Gwent_Data/StreamingAssets/bundledassets/cardassets/textures/standard/uber/10000000"
        ])):
            messagebox.showerror(
                "Uber Graphics Not Installed",
                "You do not have the 4K graphics for Gwent installed.")
            return

        # Launch progress bar window
        progressWindow = tk.Toplevel()
        progressWindow.title("Generating...")

        jsonFrame = ttk.Frame(progressWindow)

        # Add a label to show how many cards have been completed
        progressString = tk.StringVar()
        progressString.set(" ".join(
            [str(cardsCompleted), "of",
             str(numberOfCards), "completed"]))
        progressLabel = ttk.Label(jsonFrame, textvariable=progressString)
        progressLabel.pack(padx='10', pady='5', side='top')

        # Add a progress bar
        progressBar = ttk.Progressbar(jsonFrame)
        progressBar.config(orient='horizontal',
                           maximum=numberOfCards,
                           length=250,
                           mode='determinate')
        progressBar.pack(padx='10', pady='5', side='top')

        # Add a cancel button to quit early
        running = tk.BooleanVar()
        running.set(True)
        cancelButton = ttk.Button(jsonFrame,
                                  text='Cancel',
                                  command=lambda: running.set(False))
        cancelButton.pack(pady='5', side='top')

        jsonFrame.config(height='250', width='200')
        jsonFrame.pack(side='top')

        progressWindow.config(height='250', width='200')
        progressWindow.iconbitmap('assets/favicon.ico')
        progressWindow.resizable(False, False)
        progressWindow.update()

        # Set the dimensions of the final cards based on quality
        dimensions = (992, 1424)
        quality = "uber"
        if (self.imageQuality.get() == "High"):
            dimensions = (497, 713)
            quality = "high"
        elif (self.imageQuality.get() == "Medium"):
            dimensions = (249, 357)
            quality = "medium"
        elif (self.imageQuality.get() == "Low"):
            dimensions = (125, 179)
            quality = "low"

        for card in cardsToGenerate:
            # Exit if cancel was pressed
            if not running.get():
                break

            # if the card is valid and not a leader
            if self.cardData[card]['type'] != 'Leader' and self.cardData[card][
                    'type'] is not None and self.cardData[card]['name'][
                        'en-US'] is not None:
                # If the image is low quality, then export and crop it as a medium instead
                # because the low resolution card arts are stored differently in the game files the higher qualities
                if self.imageQuality.get() == "Low":
                    dimensions = (249, 357)
                    quality = "medium"

                # Export and crop the card art

                am = AssetsManager("".join([
                    gwentPath,
                    "/Gwent_Data/StreamingAssets/bundledassets/cardassets/textures/standard/",
                    quality, "/", self.cardData[card]['ingameArtId'], "0000"
                ]))
                for asset in am.assets.values():
                    for obj in asset.objects.values():
                        if obj.type == "Texture2D":
                            data = obj.read()
                            img = data.image

                newImg = img.crop((0, 0, dimensions[0], dimensions[1]))

                # Reset the quality specifics back to low if it is low and was exported as a medium
                if self.imageQuality.get() == "Low":
                    dimensions = (125, 179)
                    quality = "low"
                    newImg = newImg.resize(dimensions)

                # Add gold or bronze border based on type (border tier not card type)
                if self.addBorders.get():
                    asset = Image.open("".join(
                        ["assets/", self.cardData[card]['type'], ".png"]))
                    if (quality != "uber"):
                        asset = asset.resize(dimensions)
                    newImg = Image.alpha_composite(newImg, asset)

                # Retrieve card type
                cardType = self.cardData[card]['cardType']

                # If the optons said to add strength or type icons
                if self.addStrengthIcons.get():
                    # Get icon diamond based on faction
                    diamond = Image.open("".join([
                        "assets/", self.cardData[card]['faction'],
                        "_diamond.png"
                    ]))
                    # Get rarity icon based on rarity
                    rarity = Image.open("".join(
                        ["assets/", self.cardData[card]['rarity'], ".png"]))
                    # Combine the two together
                    strengthIcons = Image.alpha_composite(diamond, rarity)

                    # If the card is a unit add strength and potentially armor
                    if cardType == 'Unit':
                        # Check strength and add the corresponding number to the card
                        cardStrength = self.cardData[card]['strength']
                        if cardStrength > MAX_CARD_STRENGTH:
                            cardStrength = MAX_CARD_STRENGTH
                        strengthIcons = GwentCardExporter.placeNumber(
                            self, cardStrength, "strength", strengthIcons)

                        # Check if the card has armor and if so add the corresponding symbol and number
                        cardArmor = self.cardData[card]['armor']
                        if cardArmor > MAX_CARD_ARMOR:
                            cardArmor = MAX_CARD_ARMOR
                        if cardArmor > 0:
                            armor = Image.open("assets/Armor.png")
                            strengthIcons = Image.alpha_composite(
                                strengthIcons, armor)
                            strengthIcons = GwentCardExporter.placeNumber(
                                self, cardArmor, "armor", strengthIcons)

                    # If the card is a strategem add the banner icon
                    elif cardType == 'Strategem':
                        strategem = Image.open("assets/Strategem.png")
                        strengthIcons = Image.alpha_composite(
                            strengthIcons, strategem)

                    # If the card is a special add the flame icon
                    elif cardType == 'Spell':
                        special = Image.open("assets/Special.png")
                        strengthIcons = Image.alpha_composite(
                            strengthIcons, special)

                    # If the card is an artifact add the goblet icon
                    elif cardType == 'Artifact':
                        artifact = Image.open("assets/Artifact.png")
                        strengthIcons = Image.alpha_composite(
                            strengthIcons, artifact)

                    # Downsize if not generating 4K qualty
                    if (quality != "uber"):
                        strengthIcons = strengthIcons.resize(dimensions)

                    # Add the final composite to the image
                    newImg = Image.alpha_composite(newImg, strengthIcons)

                # If not a strategem and we are adding provisons
                if self.addProvisions.get() and not cardType == 'Strategem':
                    # Get provisons icon
                    provisionIcon = Image.open("assets/Provisions.png")
                    # Get provisions square background based on the faction
                    provisionSquare = Image.open("".join([
                        "assets/", self.cardData[card]['faction'], "_prov.png"
                    ]))
                    # Combine the two together
                    provisions = Image.alpha_composite(provisionIcon,
                                                       provisionSquare)

                    # Check the provisions number and add the corresponding number
                    cardProvisions = self.cardData[card]['provision']
                    if cardProvisions > MAX_CARD_PROVISIONS:
                        cardProvisions == MAX_CARD_ARMOR
                    provisions = GwentCardExporter.placeNumber(
                        self, cardProvisions, "provisions", provisions)

                    # Downsize if not generating in 4K quality
                    if (quality != "uber"):
                        provisions = provisions.resize(dimensions)

                    # Add the final composite to the image
                    newImg = Image.alpha_composite(newImg, provisions)

                # Save the card using the card's name and id number
                newImg.save("".join([
                    imagePath, "/",
                    self.cardData[card]['name']['en-US'].replace(':', ''), "_",
                    self.cardData[card]['ingameId'], "_", quality, ".png"
                ]))

            # Update progress label and progress bar
            cardsCompleted += 1
            progressString.set(" ".join(
                [str(cardsCompleted), "of",
                 str(numberOfCards), "completed"]))
            progressBar.step(1)
            progressWindow.update()

        progressWindow.destroy()
Пример #13
0
def extract_assets(src):
    global DST
    # load source
    am = AssetsManager(src)

    # iterate over assets
    for asset in am.assets.values():
        # assets without container / internal path will be ignored for now
        if not asset.container:
            continue
        else:
            contain(asset.container)

        # check which mode we will have to use
        if TYPE_FILTER:
            num_cont = sum( 1 for obj in asset.container.values() if obj.type in TYPE_FILTER)
            num_objs = sum( 1 for obj in asset.objects.values() if obj.type in TYPE_FILTER)
        else:
            num_cont = sum( 1 for obj in asset.container.values() )
            num_objs = sum( 1 for obj in asset.objects.values() )

        # check if container contains all important assets, if yes, just ignore the container
        if num_objs <= num_cont * 2:
            for asset_path, obj in asset.container.items():
                fp = os.path.join(DST, *asset_path.split('/')[IGNOR_DIR_COUNT:])
                export_obj(obj, fp)
        # otherwise use the container to generate a path for the normal objects
        else:
            extracted = []
            # find the most common path
            occurence_count = Counter(os.path.splitext(asset_path)[0] for asset_path in asset.container.keys())
            for i in asset.container.keys():
#                if 'master' in i :
#                    print(occurence_count)
#                    exit()
            local_path = os.path.join(DST, *occurence_count.most_common(1)[0][0].split('/')[IGNOR_DIR_COUNT:])

            for obj in asset.objects.values():
                if obj.path_id not in extracted:
                    extracted.extend(export_obj(obj, local_path, append_name=True))

def filter(path, otype):
    global TYPE_FILTER
    global PATH_FILTER
    pick = 0
    if PATH_FILTER:
        for i in PATH_FILTER:
            if i in path:
                pick = 1
    else:
        pick = 1
    if TYPE_FILTER and otype not in TYPE_FILTER:
        pick *= 0
    return pick


def export_obj(obj, fp: str, append_name: bool = False) -> list:
    if not filter(fp, obj.type):
        return []
    #if obj.type in ['Material', 'Shader']: # bugged
    #if obj.type in ['MeshFilter', 'Mesh', 'Material']: # bugged
        #return []
    data = obj.read()
    if data.name :
        slash = data.name.rfind('/')
        if slash != -1:
            aname = data.name[slash+1:]
        else:
            aname = data.name
        if aname == '':
            aname = '_'
    else :
        aname = '_'

    if append_name:
        bname = aname.encode('utf8')
        if len(bname) > 200:
            aname = aname[:50]
        fp = os.path.join(fp, aname)
    #dprint('aname', aname)
    #dprint('fp', fp)

    #dprint(str(obj.type)+':'+fp)
    fp, extension = os.path.splitext(fp)
    os.makedirs(os.path.dirname(fp), exist_ok=True)

    if 0 :
        pass
    elif obj.type == 'MonoScript':
        return monoscript(data, obj, fp, extension)
    elif obj.type == 'MonoBehaviour':
        return monobehaviour(data, obj, fp, extension)
    elif obj.type == 'TextAsset':
        return textasset(data, obj, fp, extension)
    elif obj.type == "Sprite":
        return sprite(data, obj, fp, extension)
    elif obj.type == "Texture2D":
        return texture2d(data, obj, fp, extension)
    elif obj.type == "GameObject":
        return gameobject(data, obj, fp, extension)
    else:
        return common(data, obj, fp, extension)
    return [obj.path_id]


def common(data, obj, fp, extension):
    return []
    f = open(f"{fp}.txt", 'w')
    #fb = open(f"{fp}.bin", 'wb')
    f.write(data.dump())
    #fb.write(data.get_raw_data())
    return [obj.path_id]

def gameobject(data, obj, fp, extension):
    fp += '.go.txt'
    with open(f"{fp}", 'a') as f:
        f.write('%s\n++++++++++++++++++++\n'%obj.path_id)
        go = obj.read()
        cs = go.components
        for i in cs:
            data = i.read()
            f.write('%s\n--------------------\n'%data.path_id)
            f.write(data.dump().replace('\r',''))
            f.write('\n')
        f.close()
    return [obj.path_id]

def monobehaviour(data, obj, fp, extension):
    if not extension:
        extension = '.txt'
    if data.name == '':
        return []
    with open(f"{fp}{extension}", 'a') as f:
        f.write(str(obj.path_id))
        f.write('\n+++++++++++++++\n')
        f.write(data.dump().replace('\r',''))
    return [obj.path_id]

def monoscript(data, obj, fp, extension):
    if not extension:
        extension = '.txt'
    with open(f"{fp}{extension}", 'w') as f:
        f.write(str(obj.path_id))
        f.write('\n+++++++++++++++\n')
        f.write(data.dump().replace('\r',''))
    return [obj.path_id]

def textasset(data, obj, fp, extension):
    if not extension:
        extension = '.txt'
    with open(f"{fp}{extension}", 'wb') as f:
        f.write(data.script)
    return [obj.path_id]

def sprite(data, obj, fp, extension):
    extension = ".png"
    data.image.save(f"{fp}{extension}")
    return [obj.path_id, data.m_RD.texture.path_id, getattr(data.m_RD.alphaTexture, 'path_id', None)]

def texture2d(data, obj, fp, extension):
    extension = ".png"
    fp = f"{fp}{extension}"
    if not os.path.exists(fp):
        try:
            data.image.save(fp)
        except EOFError:
            pass
    return [obj.path_id]


if __name__ == '__main__':
    main()
    def generateCards(self, imagePath, gwentPath):
        # Maximum values so a correct file is always chosen
        MAX_CARD_STRENGTH = 13
        MAX_CARD_ARMOR = 10
        MAX_CARD_PROVISIONS = 14

        # Save chosen paths to a file
        with open('savedPaths.txt', 'w') as pathFile:
            pathFile.write(gwentPath + '\n')
            pathFile.write(imagePath + '\n')

        # Select all or certain cards
        if self.allImages.get() and self.numberofFiles:
            cardsToGenerate = self.cardData
        else:
            cardsToGenerate = []
            for card in self.cardView.selection():
                cardsToGenerate.append(self.cardView.set(card, column='ID'))

        numberOfCards = len(cardsToGenerate)
        cardsCompleted = 0

        # Raise and error if gwent path, save path, or json file are not valid and if no cards were slected
        if not gwentPath or not (gwentPath.endswith("GWENT The Witcher Card Game") or gwentPath.endswith("Gwent")):
            messagebox.showerror("No Gwent Path", "Path to Gwent installation was invalid or not specified.")
            return
        if not imagePath:
            messagebox.showerror("No Image Path", "No path to save images to was specified.")
            return
        if not self.numberofFiles:
            messagebox.showerror("No JSON Data", "No JSON card data file was selected.")
            return
        if not numberOfCards and not self.allImages.get():
            messagebox.showerror("No Card Selected", "No cards were selected.")
            return

        # Launch progress bar window
        progressWindow = tk.Toplevel()
        progressWindow.title("Generating...")

        jsonFrame = ttk.Frame(progressWindow)

        # Add a label to show how many cards have been completed
        progressString = tk.StringVar()
        progressString.set(" ".join([str(cardsCompleted), "of", str(numberOfCards), "completed"]))
        progressLabel = ttk.Label(jsonFrame, textvariable = progressString)
        progressLabel.pack(padx='10', pady='5', side='top')

        # Add a progress bar
        progressBar = ttk.Progressbar(jsonFrame)
        progressBar.config(orient='horizontal', maximum=numberOfCards, length=250, mode='determinate')
        progressBar.pack(padx='10', pady='5', side='top')
        
        # Add a cancel button to quit early
        running = tk.BooleanVar()
        running.set(True)
        cancelButton = ttk.Button(jsonFrame, text='Cancel', command = lambda: running.set(False))
        cancelButton.pack(pady='5', side='top')

        jsonFrame.config(height='250', width='200')
        jsonFrame.pack(side='top')

        progressWindow.config(height='250', width='200')
        progressWindow.iconbitmap('assets/favicon.ico')
        progressWindow.resizable(False, False)
        progressWindow.update()

        for card in cardsToGenerate:

            # Exit if cancel was pressed
            if not running.get():
                break

            # if the card is valid and not a leader
            if self.cardData[card]['type'] != 'Leader' and self.cardData[card]['type'] is not None and self.cardData[card]['name']['en-US'] is not None:

                # Export and crop the card art
                am = AssetsManager("".join([gwentPath, "/Gwent_Data/StreamingAssets/bundledassets/cardassets/textures/standard/high/", self.cardData[card]['ingameArtId'], "0000"]))
                for asset in am.assets.values():
                    for obj in asset.objects.values():
                        if obj.type == "Texture2D":
                            data = obj.read()
                            img = data.image
                newImg = img.crop((0,0,496,712))

                # Add gold or bronze border based on type (border tier not card type)
                if self.addBorders.get():
                    asset = Image.open("".join(["assets/", self.cardData[card]['type'], ".png"]))
                    newImg = Image.alpha_composite(newImg, asset)

                # Retrieve card type
                cardType = self.cardData[card]['cardType']

                # If the optons said to add strength or type icons
                if self.addStrengthIcons.get():
                    # Add icon diamond based on faction
                    asset = Image.open("".join(["assets/", self.cardData[card]['faction'], "_diamond.png"]))
                    newImg = Image.alpha_composite(newImg, asset)

                    # Add rarity icon based on rarity
                    asset = Image.open("".join(["assets/", self.cardData[card]['rarity'], ".png"]))
                    newImg = Image.alpha_composite(newImg, asset)

                    # If the card is a strategem add the banner icon (no provisions needed)
                    if cardType == 'Strategem':
                        asset = Image.open("assets/Strategem.png")
                        newImg = Image.alpha_composite(newImg, asset)

                    # If the card is a unit add strength and potentially armor
                    elif cardType == 'Unit':
                        # Check strength and add the corresponding number to the card
                        cardStrength = self.cardData[card]['strength']
                        if cardStrength > MAX_CARD_STRENGTH:
                            cardStrength = 0
                        asset = Image.open("".join(["assets/s_", str(cardStrength), ".png"]))
                        newImg = Image.alpha_composite(newImg, asset)

                        # Check if the card has armor and if so add the corresponding symbol and number
                        cardArmor = self.cardData[card]['armor']
                        if cardArmor <= MAX_CARD_ARMOR and cardArmor > 0:
                            asset = Image.open("assets/armor.png")
                            newImg = Image.alpha_composite(newImg, asset)
                            asset = Image.open("".join(["assets/a_", str(cardArmor), ".png"]))
                            newImg = Image.alpha_composite(newImg, asset)

                    # If the card is a special add the flame icon
                    elif cardType == 'Spell':
                        # place flame icon
                        asset = Image.open("assets/Special.png")
                        newImg = Image.alpha_composite(newImg, asset)

                    # If the card is an artifact add the goblet icon
                    elif cardType == 'Artifact':
                        # place goblet icon
                        asset = Image.open("assets/Artifact.png")
                        newImg = Image.alpha_composite(newImg, asset)

                # If not a strategem and we are adding provisons
                if self.addProvisions.get() and not cardType == 'Strategem':
                    # Add provisons icon
                    asset = Image.open("assets/provisions.png")
                    newImg = Image.alpha_composite(newImg, asset)

                    # Add provisions square background based on the faction
                    asset = Image.open("".join(["assets/", self.cardData[card]['faction'], "_prov.png"]))
                    newImg = Image.alpha_composite(newImg, asset)

                    # Check the provisions number and add the corresponding number
                    cardProvisions = self.cardData[card]['provision']
                    if cardProvisions > MAX_CARD_PROVISIONS:
                        cardProvisions == 0
                    asset = Image.open("".join(["assets/p_", str(cardProvisions), ".png"]))
                    newImg = Image.alpha_composite(newImg, asset)
                
                # Save the card using the card's name and id number
                newImg.save("".join([imagePath, "/", self.cardData[card]['name']['en-US'].replace(':', ''), "_", self.cardData[card]['ingameId'], ".png"]))
            
            # Update progress label and progress bar
            cardsCompleted += 1
            progressString.set(" ".join([str(cardsCompleted), "of", str(numberOfCards), "completed"]))
            progressBar.step(1)
            progressWindow.update()
        
        progressWindow.destroy()