def _build_index_from_ab(ab, target_md5): # CAUTION: this function doesn't check if this ab has been indexed or not, so callers should check by themselves! # create containers # fetch first try: c = get_containers_from_ab(unitypack.load(open(target_md5, 'rb'))).keys() except Exception as e: logger.exception('get_containers_from_ab error') return # use set to sure each name add only once container_name_set = set(c) container_name_list = list(container_name_set) # print(len(container_name_set)) # SQL may be too long, so make smaller sets to execute for first in range(0, len(container_name_list), 500): # calc containers not in database part = container_name_list[first:first + 500] part_exclude = set(part) - {i['name'] for i in Container.objects.filter(name__in=part).values('name')} # print('exclude:',container_name_exclude_set) # then create those not in database Container.objects.bulk_create([Container(name=n) for n in part_exclude]) # query once to add the relations ab.container_set.add(*list(Container.objects.filter(name__in=part))) # filter here # create unity objects # fetch first try: objects_list = get_objects_from_ab(unitypack.load(open(target_md5, 'rb'))) except Exception as e: logger.exception('get_objects_from_ab error') return db_hash_set = {i[3] for i in objects_list} # create objects which don't exist db_hash_exclude = db_hash_set - {i['db_hash'] for i in UnityObject.objects.filter(db_hash__in=db_hash_set).values('db_hash')} unityobjects = [] db_hash_used_set = set() # even in a single file, there may be two objects have same db_hash! for name, path_id, data_hash, db_hash, asset_index, object_type in objects_list: if db_hash in db_hash_exclude and db_hash not in db_hash_used_set: unityobjects.append(UnityObject(name=name, data_hash=data_hash, db_hash=db_hash, type=object_type)) db_hash_used_set.add(db_hash) bulk_result = UnityObject.objects.bulk_create(unityobjects) # add relations relationships = [] db_hash_to_db_object = {uo.db_hash: uo for uo in UnityObject.objects.filter(db_hash__in=db_hash_set)} for name, path_id, data_hash, db_hash, asset_index, object_type in objects_list: relationships.append( UnityObjectRelationship(assetbundle=ab, unityobject=db_hash_to_db_object[db_hash], path_id=path_id, asset_index=asset_index)) UnityObjectRelationship.objects.bulk_create(relationships)
def main(): p = ArgumentParser() p.add_argument("files", nargs="+") p.add_argument("-s", "--strip", action="store_true", help="Strip extractable data") args = p.parse_args(sys.argv[1:]) for k, v in unitypack.engine.__dict__.items(): if isinstance(v, type) and issubclass(v, unitypack.engine.object.Object): yaml.add_representer(v, unityobj_representer) if args.strip: yaml.add_representer(unitypack.engine.mesh.Mesh, mesh_representer) yaml.add_representer(unitypack.engine.movie.MovieTexture, movietexture_representer) yaml.add_representer(unitypack.engine.text.Shader, shader_representer) yaml.add_representer(unitypack.engine.text.TextAsset, textasset_representer) yaml.add_representer(unitypack.engine.texture.Texture2D, texture2d_representer) for file in args.files: if file.endswith(".assets"): with open(file, "rb") as f: asset = unitypack.Asset.from_file(f) handle_asset(asset) continue with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: handle_asset(asset)
def parse_bundle(self, path): if os.path.basename(path) not in self.unity3d_filenames: # Only parse files that have a whitelisted name return with open(path, "rb") as f: bundle = unitypack.load(f) asset = bundle.assets[0] self.info("Processing %r" % (asset)) if os.path.basename(path) == "dbf.unity3d": self.parse_dbf_unity_asset(asset) return for obj in asset.objects.values(): if obj.type == "TextAsset": d = obj.read() if d.name in IGNORE_LOCALES: continue if d.script.startswith("<CardDefs>"): xml = ElementTree.fromstring(d.script) self.parse_full_carddefs(xml, d.name) elif d.script.startswith("<?xml "): xml = ElementTree.fromstring(d.script.encode("utf-8")) self.parse_single_entity_xml(xml, d.name, locale=None) else: self.error("Bad TextAsset: %r" % (d))
def parse_file(filename, item_lists): with open(filename, 'rb') as f: bundle = unitypack.load(f) for asset in bundle.assets: for _, object in asset.objects.items(): if object.type == 'TextAsset': parse_object(object, item_lists)
def parse_bundle(self, path): import unitypack with open(path, "rb") as f: bundle = unitypack.load(f) asset = bundle.assets[0] self.info("Processing %r" % (asset)) self.parse_dbf_asset(asset)
def main(): ugru = UnityGameResUnpack(sys.argv[1:]) for currentPath, dirs, files in os.walk(ugru.apkExtractedPath): for nowFile in files: try: nowFilePath = os.path.join(currentPath, nowFile) with open(nowFilePath, 'rb') as file: bundle = unitypack.load(file) except NotImplementedError as e: pass else: ugru.handleFile(nowFilePath, currentPath)
def main(): files = sys.argv[1:] for file in files: if file.endswith(".assets"): with open(file, "rb") as f: asset = unitypack.Asset.from_file(f) handle_asset(asset) continue with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: handle_asset(asset)
def extract_info(files, filter_ids): cards = {} textures = {} env = UnityEnvironment() for file in files: print("Reading %r" % (file)) f = open(file, "rb") bundle = unitypack.load(f, env) for asset in bundle.assets: print("Parsing %r" % (asset.name)) handle_asset(asset, textures, cards, filter_ids) return cards, textures
def extract_info(files): cards = {} textures = {} env = UnityEnvironment() for file in files: print("Reading %r" % (file)) with open(file, "rb") as f: bundle = unitypack.load(f, env) for asset in bundle.assets: print("Parsing %r" % (asset.name)) handle_asset(asset, textures, cards) return cards, textures
def main(): p = ArgumentParser() p.add_argument("files", nargs="+") p.add_argument("--output", "-o", required=True) p.add_argument("--all", action="store_true") p.add_argument("--images", action="store_true") p.add_argument("--models", action="store_true") p.add_argument("--text", action="store_true") p.add_argument("-q", action="store_true") p.add_argument("-qq", action="store_true") # flip images the "right" way up p.add_argument("--flip", action="store_true") # option for obj meshes (instead of js) p.add_argument("--obj", action="store_true") args = p.parse_args(sys.argv[1:]) utils.Echo.quiet = args.q utils.Echo.very_quiet = args.qq format_args = { "images": "Texture2D", "models": "Mesh", "text": "TextAsset", } handle_formats = [] for a, classname in format_args.items(): if args.all or getattr(args, a): handle_formats.append(classname) files = args.files if len(args.files) == 1: if os.path.isdir(args.files[0]): files = glob.glob(args.files[0] + "/*.unity3d") for file in files: bundle_name = utils.filename_no_ext(file) if bundle_name in EXCLUDES: info("Skipping %s..." % (bundle_name)) continue info("Extracting %s..." % (bundle_name)) save_path = os.path.join(args.output, bundle_name) with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: handle_asset(asset, handle_formats, save_path, args.flip, args.obj, args.quiet)
def unpack(infilepath, outpath): with open(infilepath, "rb") as f: bundle = unitypack.load(f) asset = bundle.assets[0] ab = asset.objects[1] for org_path, b in ab.read()["m_Container"]: obj = b["asset"].object if obj.type == "TextAsset": data = obj.read() if isinstance(data.script, str): outfilepath = os.path.join(outpath, os.path.normpath(org_path)) mkdir(os.path.dirname(outfilepath)) with open(outfilepath, "wb") as fo: fo.write(data.script.encode('utf-8'))
def main(): p = ArgumentParser() p.add_argument("--outdir", nargs="?", default="") p.add_argument("--skip-existing", action="store_true") p.add_argument("files", nargs="+") args = p.parse_args(sys.argv[1:]) textures = {} cards = {} env = unitypack.UnityEnvironment() for file in args.files: with open(file, "rb") as f: bundle = unitypack.load(f, env) for asset in bundle.assets: if asset.name.startswith("CAB-cards"): handle_cards_asset(asset, cards) else: handle_asset(asset, textures, cards) print("Found %i cards, %i textures including %i unique in use." % ( len(cards), len(textures), len(set(cards.values())) )) for id, path in cards.items(): if not path: print("%r does not have a texture" % (id)) continue if path not in textures: print("Path %r not found for %r" % (path, id)) continue pptr = textures[path] texture = pptr.resolve() png = os.path.join(args.outdir, "%s.png" % (id)) if args.skip_existing and os.path.exists(png): continue print("%r -> %r" % (path, png)) texture.image.save(png)
def main(): p = ArgumentParser() p.add_argument("input") p.add_argument("output") p.add_argument("--only") p.add_argument("--raw", action="store_true") p.add_argument("-qq", action="store_true") p.add_argument("-q", action="store_true") p.add_argument("--trace", action="store_true") args = p.parse_args(sys.argv[1:]) utils.Echo.quiet = args.q utils.Echo.very_quiet = args.qq files = [args.input] if os.path.isdir(args.input): files = glob.glob(args.input + "/*") redefine_shader() for file in files: info("Processing %s" % (file)) with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: for id, obj in asset.objects.items(): bundle_name = utils.filename_no_ext(file) try: if obj.type == "Shader": d = obj.read() if not args.only or (args.only and args.only in d.parsed_form.name): save_path = os.path.join( args.output, bundle_name, obj.type ) extract_shader(d, save_path, args.raw) except Exception as e: error("{0} ({1})".format(e, bundle_name)) if args.trace: raise
def extractimg(abpath, extractpath): for root, dirs, files in os.walk(abpath): for filename in files: if filename[:10] == 'character_': bundle = unitypack.load(open(abpath + "/" + filename, "rb")) for asset in bundle.assets: filenamearray = filename.split(".")[0].split("_") if filenamearray[-1] == 'jpvoice': continue charactername = "_".join(filenamearray[1:]) if not os.path.exists(extractpath + "/" + charactername): os.makedirs(extractpath + "/" + charactername) for id, object in asset.objects.items(): if object.type == "Texture2D": data = object.read() image = data.image.transpose(Image.FLIP_TOP_BOTTOM) image.save( extractpath + "/" + charactername + "/" + data.name + ".png", "PNG")
def main(): p = ArgumentParser() p.add_argument("--outdir", nargs="?", default="") p.add_argument("--skip-existing", action="store_true") p.add_argument("files", nargs="+") args = p.parse_args(sys.argv[1:]) textures = {} cards = {} env = unitypack.UnityEnvironment() for file in args.files: with open(file, "rb") as f: bundle = unitypack.load(f, env) for asset in bundle.assets: if asset.name.startswith("CAB-cards"): handle_cards_asset(asset, cards) else: handle_asset(asset, textures, cards) print("Found %i cards, %i textures including %i unique in use." % (len(cards), len(textures), len(set(cards.values())))) for id, path in cards.items(): if not path: print("%r does not have a texture" % (id)) continue if path not in textures: print("Path %r not found for %r" % (path, id)) continue pptr = textures[path] texture = pptr.resolve() png = os.path.join(args.outdir, "%s.png" % (id)) if args.skip_existing and os.path.exists(png): continue print("%r -> %r" % (path, png)) texture.image.save(png)
def unityfs(fs, outdir): dist = os.path.join(outdir, os.path.basename(fs)) with open(fs, 'rb') as fp: try: bundle = unitypack.load(fp) except Exception as e: print(e) return for asset in bundle.assets: try: assetobjs = asset.objects.items() except Exception as e: print(e) continue for pid, obj in assetobjs: try: extract_asset_item(pid, obj, dist) except Exception as e: print(e) continue
def main(): p = ArgumentParser() p.add_argument("files", nargs="+") p.add_argument("--all", action="store_true") p.add_argument("--audio", action="store_true") p.add_argument("--images", action="store_true") p.add_argument("--models", action="store_true") p.add_argument("--shaders", action="store_true") p.add_argument("--text", action="store_true") p.add_argument("--video", action="store_true") args = p.parse_args(sys.argv[1:]) format_args = { "audio": "AudioClip", "images": "Texture2D", "models": "Mesh", "shaders": "Shader", "text": "TextAsset", "video": "MovieTexture", } handle_formats = [] for a, classname in format_args.items(): if args.all or getattr(args, a): handle_formats.append(classname) for file in args.files: if file.endswith(".assets"): with open(file, "rb") as f: asset = unitypack.Asset.from_file(f) handle_asset(asset, handle_formats) continue with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: handle_asset(asset, handle_formats)
def main(): p = ArgumentParser() p.add_argument("bundles", nargs="+", type=FileType("rb")) p.add_argument("-o", "--outfile", nargs=1, type=FileType("wb")) p.add_argument("--dbf", nargs="?", type=FileType("r")) args = p.parse_args(sys.argv[1:]) build = detect_build(args.bundles[0].name) carddefs = {} entities = {} textures = {} whitelist = [ "cards.unity3d", "cards0.unity3d", "cards1.unity3d", "cards2.unity3d", "cardxml0.unity3d", ] for f in args.bundles: if os.path.basename(f.name) not in whitelist: f.close() continue bundle = unitypack.load(f) asset = bundle.assets[0] print("Processing %r" % (asset)) for obj in asset.objects.values(): if obj.type == "TextAsset": d = obj.read() if d.name in IGNORE_LOCALES: continue if d.script.startswith("<CardDefs>"): carddefs[d.name] = ElementTree.fromstring(d.script) elif d.script.startswith("<?xml "): entities[d.name] = ElementTree.fromstring(d.script) else: raise Exception("Bad TextAsset %r" % (d)) elif obj.type in ("CardDef", "MonoScript"): d = obj.read() if "m_GameObject" not in d: # We check for MonoScript because type checks through asset # references does not return the real class name yet. # This means we have to check for GameObject in the obj to # make sure it's actually a card. continue if d["m_GameObject"] is None: print("Missing m_GameObject for %r" % (obj)) continue cardid = d["m_GameObject"].resolve().name if "m_PortraitTexture" in d: ptr = d["m_PortraitTexture"] if not ptr: continue try: texture = ptr.resolve().name except NotImplementedError: texture = "" else: texture = reverse_texture_path(d.get("m_PortraitTexturePath", "")) if texture: textures[cardid] = texture if carddefs: xml = merge_locale_assets(carddefs) else: xml = merge_card_assets(entities, build) hero_powers = {} if args.dbf: print("Processing DBF %r" % (args.dbf.name)) dbfxml = ElementTree.parse(args.dbf) guids, hero_powers = load_dbf(dbfxml) clean_entourage_ids(xml, guids) if build < 6024: SHROUDED = "Can't be targeted by Spells or Hero Powers." else: SHROUDED = "Can't be targeted by spells or Hero Powers." SPARE_PART_RE = re.compile(r"PART_\d+") for entity in xml.findall("Entity"): id = entity.attrib["CardID"] description = entity.find("Tag[@enumID='184']/enUS") description = description.text if description is not None else "" # Clean up MasterPower whitespace power = entity.find("MasterPower") if power is not None: power.text = power.text.strip() if not power.text: entity.remove(power) overload = entity.find("Tag[@enumID='215']") if overload is not None: overload.attrib["value"] = str(guess_overload(description)) spellpower = entity.find("Tag[@enumID='192']") if spellpower is not None: spellpower.attrib["value"] = str(guess_spellpower(description)) spellpower.attrib["type"] = "Int" if SHROUDED in description: set_tag(entity, GameTag.CANT_BE_TARGETED_BY_ABILITIES, 1, type="Bool") set_tag(entity, GameTag.CANT_BE_TARGETED_BY_HERO_POWERS, 1, type="Bool") if "Can't attack." in description or "Can't Attack." in description: set_tag(entity, GameTag.CANT_ATTACK, 1, type="Bool") if SPARE_PART_RE.match(id): set_tag(entity, GameTag.SPARE_PART, 1, type="Bool") if id in textures: e = ElementTree.Element("Texture") e.text = textures[id] entity.append(e) if id in hero_powers: e = ElementTree.Element("HeroPower") e.attrib["cardID"] = hero_powers[id] entity.append(e) xml.attrib["build"] = str(build) print("Writing to %r" % (args.outfile[0].name)) args.outfile[0].write(pretty_xml(xml))
def unpackTexture(self, filePath: str): ret = [] filePathPath, filePathFile = os.path.split(filePath) _, ifpainting = os.path.split(filePathPath) # print(ifpainting,filePathFile[-3:]) if filePathFile[-3:] == "tex": needPintu = True else: needPintu = False savePath = os.path.join(self.outPath, filePath) self.makeDirs(savePath) with open(filePath, "rb") as f: # print(f.read(5)) try: bundle = unitypack.load(f) except NotImplementedError as e: # self.handdled += 1 # print(f"{self.handdled}/{self.fileCount} handdled") return ret # does # not start # with b'Unity' for asset in bundle.assets: needRemove = False # print("%s: %s:: %i objects" % (bundle, asset, len(asset.objects))) for id, object in asset.objects.items(): # print(object.type) # Let's say we only want TextAsset objects # print(object.type) if (object.type == "Texture2D"): d = object.read() # print(d.name) try: if d.format in ETC_SERIES: # self.waitHanddle.append(d) # continue needRemove = True TMP_PKM_FNAME = os.path.join( savePath, str(id) + TMP_PKM_FNAME_0) TMP_PPM_FNAME = os.path.join( savePath, str(id) + TMP_PPM_FNAME_0) bin_data = self.get_pkm_header( d.width, d.height, d.format) + d.image_data with open(TMP_PKM_FNAME, 'wb') as f: f.write(bin_data) cmd = ' '.join([ ETCPACK_CMD, TMP_PKM_FNAME, TMP_PPM_FNAME, ">/dev/null 2>&1" ]) if sys.platform == "win32": cmd = ' '.join([ ETCPACK_CMD, TMP_PKM_FNAME, TMP_PPM_FNAME ]) # print(cmd) ret0 = subprocess.check_output(cmd, shell=True) img = Image.open(TMP_PPM_FNAME) # img.show() else: try: img = d.image except Exception as e: print(filePath, str(e)) continue except: try: img = d.image except Exception as e: print(filePath, str(e)) continue if (img.mode not in ('RGBA', 'LA')): saveFileName = f"{d.name}.jpeg" savePathFile = os.path.join(savePath, saveFileName) img = ImageOps.flip(img) img.save(savePathFile, "JPEG", quality=self.QUALITY, optimize=True, progressive=True) if needPintu: pintuImgPath = savePathFile else: ret.append(savePathFile) print(" ->extract " + savePathFile) else: saveFileName = f"{d.name}.png" savePathFile = os.path.join(savePath, saveFileName) img = ImageOps.flip(img) img.save(savePathFile, "PNG") if needPintu: pintuImgPath = savePathFile else: ret.append(savePathFile) print(" ->extract " + savePathFile) elif object.type == "Mesh": d = object.read() try: mesh_data = OBJMesh(d).export() savePathFile = os.path.join(savePath, d.name) savePathFile = savePathFile + ".obj" self.write_to_file(savePathFile, mesh_data, mode="w") except NotImplementedError as e: print("WARNING: Could not extract %r (%s)" % (d, e)) mesh_data = pickle.dumps(d._obj) savePathFile = os.path.join(savePath, d.name) savePathFile = savePathFile + ".Mesh.pickle" self.write_to_file(savePathFile, mesh_data, mode="wb") if needPintu: pintuMeshPath = savePathFile if needRemove: try: # pass os.remove(TMP_PKM_FNAME) os.remove(TMP_PPM_FNAME) except: pass if needPintu: pinPic = az_paint_restore(pintuMeshPath, pintuImgPath) pintuImgSavePath = ".".join( pintuImgPath.split(".")[:-1]) + ".png" pinPic.save(pintuImgSavePath, "PNG") ret.append(pintuImgSavePath) print(" ->extract " + pintuImgSavePath) # self.handdled += 1 # print(f"{self.handdled}/{self.fileCount} handdled") return ret
async def get_sleeve_names() -> dict: global _sleeve_names if _sleeve_names is not None: return _sleeve_names sleeve_master = None sleevenametext = None res_ver = os.environ["RES_VER"] url = f"https://shadowverse.akamaized.net/dl/Manifest/{res_ver}/Eng/Windows/master_assetmanifest" async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() for line in text.splitlines(): fields = line.split(",") if len(fields) < 2: continue [name, hexcode, *_] = fields if name == "master_sleeve_master.unity3d": url = f"https://shadowverse.akamaized.net/dl/Resource/Eng/Windows/{hexcode}" async with aiohttp.ClientSession() as session: async with session.get(url) as response: data = await response.read() data = io.BytesIO(data) data.name = "" bundle = unitypack.load(data) for asset in bundle.assets: for _, obj in asset.objects.items(): if obj.type == "TextAsset": d = obj.read() if d.name == "sleeve_master": sleeve_master = d.script break if sleeve_master is not None: break elif name == "master_sleevenametext.unity3d": url = f"https://shadowverse.akamaized.net/dl/Resource/Eng/Windows/{hexcode}" async with aiohttp.ClientSession() as session: async with session.get(url) as response: data = await response.read() data = io.BytesIO(data) data.name = "" bundle = unitypack.load(data) for asset in bundle.assets: for _, obj in asset.objects.items(): if obj.type == "TextAsset": d = obj.read() if d.name == "sleevenametext": sleevenametext = d.script break if sleevenametext is not None: break sleevenametext = json.loads(sleevenametext) sleevenametext = sleevenametext["sleevenametext"]["Eng"] ret = {} lines = sleeve_master.splitlines() for line in lines[1:]: fields = line.split(",") if len(fields) < 2: continue [sleeve_id, sleeve_name, *_] = fields if not sleeve_id.isdigit(): continue sleeve_id = int(sleeve_id) ret[sleeve_id] = sleevenametext[sleeve_name] _sleeve_names = ret return ret
import unitypack with open('D:\\misc\\Hearthstone\\Data\Win\\cardxml0.unity3d') as f: bundle = unitypack.load(f) #for asset in bundle.assets: # print("%s: %s:: %i objects" % (bundle, asset, len(asset.objects)))
def main(): # setup the command arguments arg_parser = argparse.ArgumentParser() arg_parser.add_argument("input", help="the directory containing the unity3d files") arg_parser.add_argument("cache", help="the directory containing the cache files") arg_parser.add_argument("search", help="the search string, case insensitive") arg_parser.add_argument("--cache-only", action="store_true", help="only use cached files, do not try to build anything") arg_parser.add_argument("-q", action="store_true") arg_parser.add_argument("-qq", action="store_true") arg_parser.add_argument("--hide-errors", action="store_true", help="display any errors encountered reading an asset") args = arg_parser.parse_args(sys.argv[1:]) Echo.quiet = args.q Echo.very_quiet = args.qq Echo.hide_errors = args.hide_errors if os.path.isdir(args.input): files = glob.glob(args.input + "/*.unity3d") else: files = [args.input] results = [] search_term = args.search.lower() for file in files: file_name = filename_no_ext(file) # try and get the file from the cache cache = get_bundle_cache(args.cache, file_name) # if its not cached, created it or skip it if not cache: if args.cache_only: # if only interested in cached files, try the next file continue go_dict = {} with open(file, "rb") as f: bundle = unitypack.load(f) for asset in bundle.assets: asset_bundle_name = f"{file_name}/{asset.name}" # build the game object dict go_dict.update(build_dict(asset_bundle_name, asset)) # skip this file if dict is empty if len(go_dict) <= 0: continue # save dict as file cache cache = go_dict save_bundle_cache(args.cache, file_name, cache) # search this file for name in cache.keys(): if search_term in name: results.extend(cache[name]) if (len(results) > 0): for r in results: print(f"{r.id:22} {r.bundle:<16}{r.name}") else: print(f"No Results for '{search_term}'")
def main(): p = ArgumentParser() p.add_argument("bundles", nargs="+", type=FileType("rb")) p.add_argument("-o", "--outfile", nargs=1, type=FileType("wb")) p.add_argument("--dbf", nargs="?", type=FileType("r")) args = p.parse_args(sys.argv[1:]) build = detect_build(args.bundles[0].name) carddefs = {} entities = {} textures = {} whitelist = [ "cards.unity3d", "cards0.unity3d", "cards1.unity3d", "cards2.unity3d", "cardxml0.unity3d", ] for f in args.bundles: if os.path.basename(f.name) not in whitelist: f.close() continue bundle = unitypack.load(f) asset = bundle.assets[0] print("Processing %r" % (asset)) for obj in asset.objects.values(): if obj.type == "TextAsset": d = obj.read() if d.name in IGNORE_LOCALES: continue if d.script.startswith("<CardDefs>"): carddefs[d.name] = ElementTree.fromstring(d.script) elif d.script.startswith("<?xml "): entities[d.name] = ElementTree.fromstring(d.script) else: raise Exception("Bad TextAsset %r" % (d)) elif obj.type in ("CardDef", "MonoScript"): d = obj.read() if "m_GameObject" not in d: # We check for MonoScript because type checks through asset # references does not return the real class name yet. # This means we have to check for GameObject in the obj to # make sure it's actually a card. continue if d["m_GameObject"] is None: print("Missing m_GameObject for %r" % (obj)) continue cardid = d["m_GameObject"].resolve().name if "m_PortraitTexture" in d: ptr = d["m_PortraitTexture"] if not ptr: continue try: texture = ptr.resolve().name except NotImplementedError: texture = "" else: texture = reverse_texture_path( d.get("m_PortraitTexturePath", "")) if texture: textures[cardid] = texture if carddefs: xml = merge_locale_assets(carddefs) else: xml = merge_card_assets(entities, build) hero_powers = {} if args.dbf: print("Processing DBF %r" % (args.dbf.name)) dbfxml = ElementTree.parse(args.dbf) guids, hero_powers = load_dbf(dbfxml) clean_entourage_ids(xml, guids) if build < 6024: SHROUDED = "Can't be targeted by Spells or Hero Powers." else: SHROUDED = "Can't be targeted by spells or Hero Powers." SPARE_PART_RE = re.compile(r"PART_\d+") for entity in xml.findall("Entity"): id = entity.attrib["CardID"] description = entity.find("Tag[@enumID='184']/enUS") description = description.text if description is not None else "" # Clean up MasterPower whitespace power = entity.find("MasterPower") if power is not None: power.text = power.text.strip() if not power.text: entity.remove(power) overload = entity.find("Tag[@enumID='215']") if overload is not None: overload.attrib["value"] = str(guess_overload(description)) spellpower = entity.find("Tag[@enumID='192']") if spellpower is not None: spellpower.attrib["value"] = str(guess_spellpower(description)) spellpower.attrib["type"] = "Int" if SHROUDED in description: set_tag(entity, GameTag.CANT_BE_TARGETED_BY_ABILITIES, 1, type="Bool") set_tag(entity, GameTag.CANT_BE_TARGETED_BY_HERO_POWERS, 1, type="Bool") if "Can't attack." in description or "Can't Attack." in description: set_tag(entity, GameTag.CANT_ATTACK, 1, type="Bool") if SPARE_PART_RE.match(id): set_tag(entity, GameTag.SPARE_PART, 1, type="Bool") if id in textures: e = ElementTree.Element("Texture") e.text = textures[id] entity.append(e) if id in hero_powers: e = ElementTree.Element("HeroPower") e.attrib["cardID"] = hero_powers[id] entity.append(e) xml.attrib["build"] = str(build) print("Writing to %r" % (args.outfile[0].name)) args.outfile[0].write(pretty_xml(xml))