Ever wondered what’s the deal with *.xsiaddon files? What are they?
Well, they’re good ol’ XML with a bit of metadata and the files embedded in them are compressed with the zlib library and base64 encoded for safe plaintext storage.
How would we parse such a file? Check this out:
import os
import zlib
import base64
import xml.etree.ElementTree as ET
from pprint import pprint as pp
tmpDir = os.environ['TMP']
xsiaddonPath = r'C:\path\to\something.xsiaddon'
tree = ET.parse(xsiaddonPath) # Load file
# Get the root element ('xsi_file')
root = tree.getroot()
addonInfo = {
'version': root.find('AddonVersion').text,
'title': root.find('Title').text,
'author': root.find('AuthorName').text,
'email': root.find('AuthorEMail').text,
'url': root.find('URL').text,
'desc': root.find('Description').text,
'usage': root.find('Usage').text,
'installDirName': root.find('InstallDir').text,
'madeInVersion': root.attrib['xsi_version']
}
pp(addonInfo) # pretty print :)
# Now let's loop through all the files embedded in it:
addonFiles = root.findall('AddonItem')
for item in addonFiles:
fileName = item.attrib['filename']
relativeExtractionPath = item.attrib.setdefault('destination','\\')
fileCategory = item.attrib['type'] # SPDLs, Others, etc.
# XSIADDON ATTACHMENTS ARE BASE64-ENCODED STRINGS
# OF ZLIB-COMPRESSED DATA, but ONLY AFTER THE 12TH BYTE.
#
# In Halfdan's own words:
# "They're compressed using zlib but they don't have the
# gzip headers. The compression starts at the 12th byte.
# The first 12 bytes contain a textural representation
# of the size (+pad)."
#
decoded = base64.b64decode(item.text)
data = zlib.decompress( decoded[12:] )
fileInfo = {
'filename': fileName,
'filesize': len(data),
'extractTo': relativeExtractionPath,
'category': fileCategory,
}
pp(fileInfo)
# For demonstration sake, invent a temporary filepath:
filePath = os.path.normpath(tmpDir+'\\'+fileName)
# Write our file to disk:
f = open(filePath, 'wb')
f.write(data)
f.close()
print "Extracted %s to: %s" % (fileName, filePath)
print ( "Gee wiz, %s! That xsiaddon had %s file(s). --> %s"
% (os.environ['USERNAME'], len(addonFiles), xsiaddonPath) )
# Mission accomplished.
Easy as that! š
One reply on “How to read an XSIADDON file in Python”
Cool script! The only problem with this technique is that it loses the directory structure in the process.