Created
August 29, 2017 22:34
-
-
Save iGlitch/cbb39217cbf7992fdf4a2ac9340592b4 to your computer and use it in GitHub Desktop.
Convert Switch .byaml to .xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys, os, io, struct, argparse | |
#import xml | |
def se(x): | |
y = x | |
l = len(x) | |
x = "" | |
c = 0 | |
while c < l: | |
x = x + y[l-c-1] | |
c = c + 1 | |
return x | |
def bhex(x): | |
if int(x) < 16: return hex(x).replace("0x", "0x0") | |
else: return hex(x) | |
def seh(x): | |
y = x | |
l = len(x) | |
x = "" | |
c = 0 | |
while c < l: | |
x = x + y[l-c-1] | |
c = c + 1 | |
y = x | |
x = "" | |
for char in y: | |
x = x + bhex(ord(char)) | |
x = int("0x" + x.replace("0x", ""), 16) | |
return x | |
def getntstr(addr): | |
retstring = "" | |
lastchar = "a" | |
while True: | |
newchar = data[addr] | |
if newchar != "\x00": | |
retstring = retstring + newchar | |
else: | |
break | |
lastchar = newchar | |
addr = addr + 1 | |
# if retstring == "": | |
# print "No string? @ " + hex(addr) | |
return retstring | |
class Path: | |
def __init__(self, addr): | |
self.addr = addr | |
self.px = struct.unpack("<f", data[addr:addr + 4])[0] | |
self.py = struct.unpack("<f", data[addr + 4:addr + 8])[0] | |
self.pz = struct.unpack("<f", data[addr + 8:addr + 0x0c])[0] | |
self.nx = struct.unpack("<f", data[addr + 0x0c:addr + 0x10])[0] | |
self.ny = struct.unpack("<f", data[addr + 0x10:addr + 0x14])[0] | |
self.nz = struct.unpack("<f", data[addr + 0x14:addr + 0x18])[0] | |
self.u = data[addr + 0x18:addr + 0x1c] | |
class PathTableNode: | |
def __init__(self, addr): | |
self.addr = addr | |
if ord(data[self.addr]) != 195: | |
print "PathTableNode initialized from wrong node type" | |
sys.exit(1) | |
self.numofentries = seh(data[self.addr+0x1:self.addr+0x4]) | |
self.offsets = [] | |
self.paths = [] | |
for entrynum in range(self.numofentries): | |
self.offsets.append(seh(data[self.addr + 0x4 + (4 * (entrynum)):self.addr + 0x8 + (4 * (entrynum))])) | |
self.paths.append(Path(self.addr + self.offsets[entrynum])) | |
class StringTableNode: | |
def __init__(self, addr): | |
self.addr = addr | |
if ord(data[self.addr]) != 194: | |
print "StringTableNode initialized from wrong node type @ " + str(self.addr) | |
sys.exit(1) | |
self.numofentries = seh(data[self.addr+0x1:self.addr+0x4]) | |
self.size = 0x4 + ((self.numofentries + 1) * 4) | |
self.end = self.addr + self.size | |
self.strings = [] | |
for entrynum in range(self.numofentries): | |
entryaddr = seh(data[self.addr+(0x4*(1+entrynum)):self.addr+(0x4*(1+entrynum))+0x4]) + self.addr | |
self.strings.append(getntstr(entryaddr)) | |
# print str(entrynum) + ": " + self.strings[entrynum] + " @ " + str(hex(entryaddr)) + " @ " + str(hex(self.addr+(0x4*(1+entrynum)))) | |
class DictionaryNode: | |
def __init__(self, addr): | |
self.addr = addr | |
if ord(data[self.addr]) != 193: | |
print "DictionaryNode initialized from wrong node type" | |
sys.exit(1) | |
self.numofentries = seh(data[self.addr+0x1:self.addr+0x4]) | |
self.nodes = [] | |
self.nodetypes = [] | |
self.names = [] | |
self.daddr = self.addr + 0x4 | |
global nodentbl | |
for entrynum in range(self.numofentries): | |
self.names.append(nodentbl.strings[seh(data[self.daddr + (entrynum * 8):self.daddr + (entrynum * 8) + 0x3])]) | |
self.nodetypes.append(data[self.daddr + (entrynum * 8) + 0x3]) | |
# print hex(ord(self.nodetypes[entrynum])) | |
if ord(self.nodetypes[entrynum]) == 160: | |
self.nodes.append(StringIndexNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 161: | |
self.nodes.append(PathIndexNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 192: | |
self.nodes.append(ArrayNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 193: | |
self.nodes.append(DictionaryNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 208: | |
self.nodes.append(BooleanNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 209: | |
self.nodes.append(IntegerNode(seh(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8]))) | |
elif ord(self.nodetypes[entrynum]) == 210: | |
self.nodes.append(FloatNode(data[self.daddr + (entrynum * 0x8) + 0x4:self.daddr + (entrynum * 0x8) + 0x8])) | |
else: | |
pass | |
# print "DictionaryNode attempted to initialize an invalid node of type " + hex(ord(self.nodetypes[entrynum])) + " at address " + str(hex(self.daddr + (entrynum * 0x8) + 0x4)) | |
class StringIndexNode: | |
def __init__(self, strtblindex): | |
self.strtblindex = strtblindex | |
global stringvtbl | |
self.string = stringvtbl.strings[int(self.strtblindex)] | |
# print self.string | |
class PathIndexNode: | |
def __init__(self, pathtblindex): | |
self.tblindex = pathtblindex | |
global pathvtbl | |
self.path = pathvtbl.paths[pathtblindex] | |
self.px = self.path.px | |
self.py = self.path.py | |
self.pz = self.path.pz | |
self.nx = self.path.nx | |
self.ny = self.path.ny | |
self.nz = self.path.nz | |
self.u = self.path.u | |
class BooleanNode: | |
def __init__(self, value): | |
# if ord(data[self.addr]) != 208: | |
# print "BooleanNode initialized from wrong node type" | |
# sys.exit(1) | |
self.value = value | |
if self.value == 1: | |
self.state = True | |
else: | |
self.state = False | |
class IntegerNode: | |
def __init__(self, value): | |
self.value = value | |
# if ord(data[self.addr]) != 209: | |
# print "IntegerNode initialized from wrong node type" | |
# sys.exit(1) | |
self.unsigned = self.value | |
if self.unsigned > 0x80000000: | |
self.int = (self.unsigned - 0x80000000) * (-1) | |
else: | |
self.int = self.unsigned | |
class FloatNode: | |
def __init__(self, value): | |
self.rawbin = value | |
self.float = struct.unpack("<f", self.rawbin) | |
class ArrayNode: | |
def __init__(self, addr): | |
self.addr = addr | |
if ord(data[self.addr]) != 192: | |
print "ArrayNode initialized from wrong node type" | |
sys.exit(1) | |
self.numofentries = seh(data[self.addr+0x1:self.addr+0x4]) | |
self.nodetypes = [] | |
self.nodes = [] | |
for entrynum in range(self.numofentries): | |
self.nodetypes.append(data[self.addr + 0x4 + entrynum]) | |
if (self.numofentries % 0x4) != 0: | |
self.headerend = self.addr + self.numofentries + (0x4 - (self.numofentries % 0x4)) + 0x4 | |
else: | |
self.headerend = self.addr + self.numofentries + 0x4 | |
for entrynum in range(self.numofentries): | |
if ord(self.nodetypes[entrynum]) == 160: | |
self.nodes.append(StringIndexNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 161: | |
self.nodes.append(PathIndexNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 192: | |
self.nodes.append(ArrayNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 193: | |
# print "initializing dict of num " + str(entrynum) + "... at address " + str(self.headerend + (entrynum * 0x4)) | |
self.nodes.append(DictionaryNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 208: | |
self.nodes.append(BooleanNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 209: | |
self.nodes.append(IntegerNode(seh(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4]))) | |
elif ord(self.nodetypes[entrynum]) == 210: | |
self.nodes.append(FloatNode(data[self.headerend + (entrynum * 0x4):self.headerend + (entrynum * 0x4) + 0x4])) | |
else: | |
print "ArrayNode attempted to initialize an invalid node of type " + hex(ord(self.nodetypes[entrynum])) | |
sys.exit(1) | |
def parse_byaml(data): | |
header = data[:20] | |
def invalid(e = False): | |
if e: print e | |
print "File is invalid." | |
sys.exit(1) | |
if se(header[:2]) == "YB": invalid("Big Endian is not supported") | |
elif se(header[:2]) != "BY": invalid() | |
fileversion = se(header[2:4]) | |
nodentbladdr = seh(header[4:8]) | |
if nodentbladdr != 0 and ord(data[nodentbladdr]) != 194: invalid("Node name table node is not a string node") | |
global pathvtble | |
if pathvtble: | |
global pathsused | |
pathvtbladdr = seh(header[0xc:0x10]) | |
if pathvtbladdr != 0: | |
if ord(data[pathvtbladdr]) != 195: invalid("Path value table node is not a path table node") | |
global pathvtbl | |
pathsused = True | |
pathvtbl = PathTableNode(pathvtbladdr) | |
else: | |
pathsused = False | |
rootnodeaddr = seh(header[0x10:0x14]) | |
else: | |
rootnodeaddr = seh(header[0xc:0x10]) | |
if rootnodeaddr != 0 and ord(data[rootnodeaddr]) != 192 and ord(data[rootnodeaddr]) != 193: invalid("Root node is not an array or dictionary node") | |
global nodentbl | |
global stringsused | |
stringvtbladdr = seh(header[8:0xc]) | |
if stringvtbladdr != 0: | |
if ord(data[stringvtbladdr]) != 194: invalid("String value table node is not a string node") | |
global stringvtbl | |
stringsused = True | |
stringvtbl = StringTableNode(stringvtbladdr) | |
else: | |
stringsused = False | |
nodentbl = StringTableNode(nodentbladdr) | |
global rootnode | |
if ord(data[rootnodeaddr]) == 192: rootnode = ArrayNode(rootnodeaddr) | |
if ord(data[rootnodeaddr]) == 193: rootnode = DictionaryNode(rootnodeaddr) | |
def parseArray(array, indention, fileobj, name=None): | |
if name: | |
fileobj.write(indention + "<array" + name + ">\n") | |
else: | |
fileobj.write(indention + "<array>\n") | |
for nodenum in range(len(array.nodes)): | |
node = array.nodes[nodenum] | |
if isinstance(node, ArrayNode): | |
parseArray(node, indention + " ", fileobj) | |
elif isinstance(node, DictionaryNode): | |
parseDict(node, indention + " ", fileobj) | |
elif isinstance(node, StringIndexNode): | |
fileobj.write(indention + " <string>" + node.string + "</string>\n") | |
elif isinstance(node, PathIndexNode): | |
fileobj.write(indention + " <path px=" + str(node.px) + " py=" + str(node.py) + " pz=" + str(node.pz) + " nx=" + str(node.nx) + " ny=" + str(node.ny) + " nz=" + str(node.nz) + "></path>\n") | |
elif isinstance(node, BooleanNode): | |
fileobj.write(indention + " <bool>" + str(node.state) + "</bool>\n") | |
elif isinstance(node, IntegerNode): | |
fileobj.write(indention + " <int>" + str(node.int) + "</int>\n") | |
elif isinstance(node, FloatNode): | |
fileobj.write(indention + " <float>" + str(node.float[0]) + "f</float>\n") | |
else: | |
"Attempted to parse invalid node: " + str(node) | |
sys.exit(1) | |
fileobj.write(indention + "</array>\n") | |
def parseDict(dictnode, indention, fileobj, name=None): | |
if name: | |
fileobj.write(indention + "<dict" + name + ">\n") | |
else: | |
fileobj.write(indention + "<dict>\n") | |
for nodenum in range(len(dictnode.nodes)): | |
node = dictnode.nodes[nodenum] | |
# global stringsused | |
# if stringsused: | |
nodename = dictnode.names[nodenum] | |
if nodename != "": | |
nodename = " " + nodename | |
else: | |
nodename = "NoName" | |
if isinstance(node, ArrayNode): | |
parseArray(node, indention + " ", fileobj, nodename) | |
elif isinstance(node, DictionaryNode): | |
parseDict(node, indention + " ", fileobj, nodename) | |
elif isinstance(node, StringIndexNode): | |
fileobj.write(indention + " <string" + nodename + ">" + node.string + "</string>\n") | |
elif isinstance(node, PathIndexNode): | |
fileobj.write(indention + " <path" + nodename + " px=" + str(node.px) + " py=" + str(node.py) + " pz=" + str(node.pz) + " nx=" + str(node.nx) + " ny=" + str(node.ny) + " nz=" + str(node.nz) + "></path>\n") | |
elif isinstance(node, BooleanNode): | |
fileobj.write(indention + " <bool" + nodename + ">" + str(node.state) + "</bool>\n") | |
elif isinstance(node, IntegerNode): | |
fileobj.write(indention + " <int" + nodename + ">" + str(node.int) + "</int>\n") | |
elif isinstance(node, FloatNode): | |
fileobj.write(indention + " <float" + nodename + ">" + str(node.float[0]) + "f</float>\n") | |
else: | |
print "Attempted to parse invalid node: " + str(node) + " named: " + nodename | |
sys.exit(1) | |
fileobj.write(indention + "</dict>\n") | |
# print len(result) | |
def xml_out(node): | |
xmlfile = open(args.dest, mode="wb+") | |
xmlfile.close() | |
xmlfile = open(args.dest, mode="ab+") | |
if isinstance(node, ArrayNode): | |
result = parseArray(node, "", xmlfile) | |
elif isinstance(node, DictionaryNode): | |
result = parseDict(node, "", xmlfile) | |
# print hex(node.nodes[max(range(len(node.nodes)))].addr) | |
xmlfile.close() | |
#def parse_xml(xmldata): | |
# | |
argparser = argparse.ArgumentParser(description = "Convert byaml files into .xml files.") | |
argparser.add_argument("source", metavar="sourcefile", type=str, help=".byaml file to convert") | |
argparser.add_argument("dest", metavar="destinationfile", type=str, help=".xml file to output to") | |
argparser.add_argument("--incpvtbl", dest="pathvtbleng", action="store_const", const=True, default=False, help="File includes a path value table. Try this if you are getting 'File is invalid.'") | |
#argparser.add_argument("--xml2byaml", dest="contype", action="store_const", const=parse_xml, default=parse_byaml, help="Convert xml to byaml, default is byaml to xml") | |
args = argparser.parse_args() | |
global pathvtble | |
pathvtble = args.pathvtbleng | |
byamlfile = io.open(args.source, mode = "rb", encoding = None) | |
filedata = byamlfile.read(os.fstat(byamlfile.fileno()).st_size) | |
data = filedata | |
byamlfile.close() | |
parse_byaml(filedata) | |
xml_out(rootnode) | |
#args.contype(filedata) | |
#if args.contype is parse_byaml: | |
# xml_out(rootnode) | |
#else: | |
# byaml_out(xfdata) | |
print "Done!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment