commit 443af6ce0a339700151a3830a142a940014b3701 Author: gered Date: Tue Feb 19 14:05:24 2013 -0500 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a11e4b --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# UnityPackage Extractor + +Extracts files/folders from a .unitypackage file, reading the 'pathname' +files it contains to build a "readable" file/folder structure which all +the files will be extracted and renamed to match. + +## Usage + + extractunitypackage.py input_file [output_path] + +**input_file** should be a .unitypackage file. The part of the filename +before the extension will be used as the name of the directory that the +packages contents will be extracted to. + +**output_path** is an optional path where the package's files will be +extracted to. If omitted, the current working directory is used. If +specified, the path should already exist. + +## Disclaimer + +This is a pretty bare-bones script which does a very, very minimal +amount of error checking. I take no responsibility if you lose files, +your computer blows up, etc, as a result of using this script. diff --git a/extractunitypackage.py b/extractunitypackage.py new file mode 100755 index 0000000..857b8ee --- /dev/null +++ b/extractunitypackage.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# +# UnityPackage Extractor +# +# Extracts files/folders from a .unitypackage file, reading the 'pathname' +# files it contains to rebuild a "readable" file/folder structure. +# +# Usage: extractunitypackage.py input_file [output_path] +# +# "input_file" should be a .unitypackage file. The part of the filename +# before the extension will be used as the name of the directory that the +# packages contents will be extracted to. +# +# "output_path" is an optional path where the package's files will be +# extracted to. If omitted, the current working directory is used. If +# specified, the path should already exist. + +import os +import shutil +import sys +import tarfile + +if len(sys.argv) < 2: + print 'No input file specified.' + sys.exit() + +name, extension = os.path.splitext(sys.argv[1]) + +outputDir = '' +if len(sys.argv) > 2: + outputDir = os.path.join(sys.argv[2], name) +else: + outputDir = './' + name +workingDir = './.working' + +# can't proceed if the output dir exists already +# but if the temp working dir exists, we clean it out before extracting +if os.path.exists(outputDir): + print 'Output dir "' + outputDir + '" exists. Aborting.' + sys.exit(); +if os.path.exists(workingDir): + shutil.rmtree(workingDir) + +# extract .unitypackage contents to a temporary space +tar = tarfile.open(sys.argv[1], 'r:gz') +tar.extractall(workingDir); +tar.close() + +# build association between the unitypackage's root directory names +# (which each have 1 asset in them) to the actual filename (stored in the 'pathname' file) +mapping = {} +for i in os.listdir(workingDir): + rootFile = os.path.join(workingDir, i) + asset = i + + if os.path.isdir(rootFile): + realPath = '' + + # we need to check if an 'asset' file exists (sometimes it won't be there + # such as when the 'pathname' file is just specifying a directory) + hasAsset = False + + for j in os.listdir(rootFile): + # grab the real path + if j == 'pathname': + lines = [line.strip() for line in open(os.path.join(rootFile, j))] + realPath = lines[0] # should always be on the first line + elif j == 'asset': + hasAsset = True + + # if an 'asset' file exists in this directory, then this directory + # contains a file that should be moved+renamed. otherwise we can + # ignore this directory altogether... + if hasAsset: + mapping[asset] = realPath + +# mapping from unitypackage internal filenames to real filenames is now built +# walk through them all and move the 'asset' files out and rename, building +# the directory structure listed in the real filenames we found as we go + +os.mkdir(outputDir) + +for asset in mapping: + path, filename = os.path.split(mapping[asset]) + + destDir = os.path.join(outputDir, path) + destFile = os.path.join(destDir, filename) + source = os.path.join(workingDir, asset, 'asset'); + + if not os.path.exists(destDir): + os.makedirs(destDir) + + shutil.move(source, destFile) + + print asset, '=>', mapping[asset] + +# done, cleanup any leftovers... +shutil.rmtree(workingDir)