Skip to content

Instantly share code, notes, and snippets.

@iGlitch
Created August 29, 2017 22:34
Show Gist options
  • Save iGlitch/cbb39217cbf7992fdf4a2ac9340592b4 to your computer and use it in GitHub Desktop.
Save iGlitch/cbb39217cbf7992fdf4a2ac9340592b4 to your computer and use it in GitHub Desktop.
Convert Switch .byaml to .xml
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