UnityPack icon indicating copy to clipboard operation
UnityPack copied to clipboard

Support for UnityWebData1.0 files

Open andyearnshaw opened this issue 6 years ago • 3 comments

Example UnityWebData1.0 File

The signature on these files is UnityWebData1.0.

These files just appear to contain one or more .unity3d files, so it would be great to cut out the middle-man of extracting them and then extracting the .unity3d files.

andyearnshaw avatar May 01 '18 14:05 andyearnshaw

QuickBMS script for these files:

idstring "UnityWebData1.0\0"
get HEADLEN long
do
	get OFF long
	get SIZE long
	get NAMEZ long
	getdstring NAME NAMEZ
	log NAME OFF SIZE
	savepos i
while i < HEADLEN

einstein95 avatar Aug 29 '19 09:08 einstein95

python code

from unitypack.utils import BinaryReader

SIGNATURE = 'UnityWebData1.0'

class DataFile:
    def load(self, file):
        buf = BinaryReader(file, endian="<")
        self.path = file.name

        self.signature = buf.read_string()
        header_length = buf.read_int()
        if self.signature != SIGNATURE:
            raise NotImplementedError('Invalid signature {}'.format(repr(self.signature)))

        self.blobs = []
        while buf.tell() < header_length:
            offset = buf.read_int()
            size = buf.read_int()
            namez = buf.read_int()
            name = buf.read_string(namez)
            self.blobs.append({ 'name': name, 'offset': offset, 'size': size })
        if buf.tell() > header_length:
            raise NotImplementedError('Read past header length, invalid header')

        for blob in self.blobs:
            buf.seek(blob['offset'])
            blob['data'] = buf.read(blob['size'])
            if len(blob['data']) < blob['size']:
                raise NotImplementedError('Invalid size or offset, reading past file')

usage:

import os
f = open('data', 'rb')
df = DataFile()
df.load(f)
EXTRACTION_DIR = 'extracted'
for blob in df.blobs:
    print('extracting @ {}:\t{} ({})'.format(blob['offset'], blob['name'], blob['size']))
    dest = os.path.join(EXTRACTION_DIR, blob['name'])
    os.makedirs(os.path.dirname(dest), exist_ok=True)
    with open(dest, 'wb') as f:
        f.write(blob['data'])

mildsunrise avatar Dec 01 '19 20:12 mildsunrise

Maybe this is obvious, but once it's extracted, remember to set base_path to the extracted dir:

file = open(os.path.join(EXTRACTION_DIR, 'data.unity3d'), 'rb')
env = unitypack.environment.UnityEnvironment(base_path=EXTRACTION_DIR)
bundle = unitypack.load(file, env)

Or start python from the extracted directory, because base_path defaults to the current directory. This should be documented, IMO.

Otherwise it won't find the assets referenced from the bundle, i.e.:

KeyError: "No such asset: 'sharedassets0.resource'"

mildsunrise avatar Dec 02 '19 17:12 mildsunrise