Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
K0lb3 committed Nov 8, 2021
0 parents commit c546465
Show file tree
Hide file tree
Showing 7 changed files with 475 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
103 changes: 103 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Byte-compiled / optimized / DLL files
__pycache__/
extracted/
raw/
*.apk
lib/
update_master.py

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
assets/asset_list.txt
163 changes: 163 additions & 0 deletions AssetBatchConverter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import os
import UnityPy
from collections import Counter
import json

TYPES = [
# Images
'Sprite',
'Texture2D',
# Text (filish)
'TextAsset',
'Shader',
'MonoBehaviour',
'Mesh'
# Font
'Font',
# Audio
'AudioClip',
]

ROOT = os.path.dirname(os.path.realpath(__file__))
DST = os.path.join(ROOT, "extracted")
IGNOR_DIR_COUNT=0

def extract_assets(src):
# load source
env = UnityPy.load(src)

# iterate over assets
for asset in env.assets:
# assets without container / internal path will be ignored for now
if not asset.container:
continue
# filter objects and put Texture2Ds at the end of the list
objs = sorted(
(
obj
for obj in asset.get_objects()
if obj.type.name in TYPES
),
key=lambda x: 1 if x.type == "Texture2D" else 0
)
cobjs = sorted(
(
(key, obj)
for key, obj in asset.container.items()
if obj.type.name in TYPES
),
key=lambda x: 1 if x[1].type == "Texture2D" else 0
)
# check which mode we will have to use
num_cont = len(cobjs)
num_objs = len(objs)

# check if container contains all important assets, if yes, just ignore the container
if num_objs <= num_cont * 2:
for asset_path, obj in cobjs:
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 objs:
if obj.path_id not in extracted:
try:
extracted.extend(export_obj(
obj, local_path, append_name=True))
except Exception as e:
print(e, obj.path_id)


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)

# streamlineable types
export = None
if obj.type == 'TextAsset':
if not extension:
extension = '.txt'
export = data.script

elif obj.type == "Font":
if data.m_FontData:
extension = ".ttf"
if data.m_FontData[0:4] == b"OTTO":
extension = ".otf"
export = data.m_FontData
else:
return [obj.path_id]

elif obj.type == "Mesh":
extension = ".obf"
export = data.export().encode("utf8")

elif obj.type == "Shader":
extension = ".txt"
export = data.export().encode("utf8")

elif obj.type == "MonoBehaviour":
# The data structure of MonoBehaviours is custom
# and is stored as nodes
# If this structure doesn't exist,
# it might still help to at least save the binary data,
# which can then be inspected in detail.
if obj.serialized_type.nodes:
extension = ".json"
export = json.dumps(
obj.read_typetree(),
indent=4,
ensure_ascii=False
).encode("utf8")
else:
extension = ".bin"
export = data.raw_data

if export:
with open(f"{fp}{extension}", "wb") as f:
f.write(export)

# non-streamlineable types
if obj.type == "Sprite":
data.image.save(f"{fp}.png")

return [obj.path_id, data.m_RD.texture.path_id, getattr(data.m_RD.alphaTexture, 'path_id', None)]

elif obj.type == "Texture2D":
if not os.path.exists(fp) and data.m_Width:
# textures can have size 0.....
data.image.save(f"{fp}.png")

elif obj.type == "AudioClip":
samples = data.samples
if len(samples) == 0:
pass
elif len(samples) == 1:
with open(f"{fp}.wav", "wb") as f:
f.write(list(data.samples.values())[0])
else:
os.makedirs(fp, exist_ok=True)
for name, clip_data in samples.items():
with open(os.path.join(fp, f"{name}.wav"), "wb") as f:
f.write(clip_data)
return [obj.path_id]


if __name__ == '__main__':
main()
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Blue Archive asset downloader

A small project that downloads all assets of the global version of Blue Archive and extracts them while it's at it.

The script updates the assets and even its own parameters on its own,
so all you have to do is execute the download_assets.py script after every update to get the latest files.

## Script Requirements

```
- Python 3.6+
- UnityPy 1.7.21
- requests
```cmd
pip install UnityPy==1.7.21
pip install requests
```


## TODO

- decryption of some files
-
Loading

0 comments on commit c546465

Please sign in to comment.