1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| # Sdorica_ABC.py import os from UnityPy import AssetsManager from collections import Counter import zipfile import logging
logging.basicConfig(level=logging.ERROR,format='%(message)s')
TYPES = ['TextAsset', 'Sprite', 'Texture2D']
ROOT = os.path.dirname(os.path.realpath(__file__))
# source folder ASSETS = os.path.join(ROOT, 'AssetBundles') # destination folder DST = os.path.join(ROOT, 'textasset') # number of dirs to ignore # e.g. IGNOR_DIR_COUNT = 2 will reduce # 'assets/assetbundles/images/story_picture/small/15.png' # to # 'images/story_picture/small/15.png'
IGNOR_DIR_COUNT = 0
os.makedirs(DST, exist_ok=True)
def main(): for root, dirs, files in os.walk(ASSETS): del dirs for f in files: #print(f) src = os.path.realpath(os.path.join(root, f)) try: extract_assets(src) except: pass
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))
def export_obj(obj, fp: str, append_name: bool = False) -> list: if obj.type not in TYPES: return [] data = obj.read() if append_name: fp = os.path.join(fp, data.name)
fp, extension = os.path.splitext(fp) os.makedirs(os.path.dirname(fp), exist_ok=True)
if obj.type == 'TextAsset': if not extension: extension = '.txt' with open(f"{fp}{extension}", 'wb') as f: f.write(data.script)
elif obj.type == 'MonoBehaviour': extension = '.txt' metadata =data.type_tree['_serializedStateValues'][0] with open(f"{fp}{extension}", 'w') as f: f.write(metadata)
elif obj.type == "Sprite": 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)]
elif obj.type == "Texture2D": extension = ".png" fp = f"{fp}{extension}" if not os.path.exists(fp): try: data.image.save(fp) except EOFError: pass
elif obj.type == 'AudioClip': if not extension: extension = '.wav' try: samples = data.samples for item in samples: audio = samples.get(item).obj with open(f"{fp}{extension}", 'wb') as f: f.write(audio) except Exception as error: logging.error(error) pass
return [obj.path_id]
if __name__ == '__main__':
main()
|