#!/usr/bin/python
#
# Converts a GameDB Excel XML spreadsheet to gamedb.json
# It takes its input from reading the xml file and outputs to stdout.
#
# Example usage:
#
# ./ConvertExcelToGameDB.py gamedb.xml > gamedb.json
# ./ConvertExcelToGameDB.py gamedb.xml --pp > gamedb.json - output the json in prettyprint mode
#
import xml.etree.ElementTree
import sys
import json
import GameDBUtils
xmlstr = open(sys.argv[1], 'rb').read()
root = xml.etree.ElementTree.fromstring(xmlstr)
prettyprint = False
if len(sys.argv)>=3:
param = str(sys.argv[2])
if param=="--pp":
prettyprint=True
#
# This helper function with create an empty list in the python object tree
# if a list with the given path doesn't exist in that object tree
# (This function assumes the object tree was already constructed with calls
# to UnflattenJson of the given paths, so it can assume various indexes are valid)
#
def AddEmptyArray( obj, path ):
pathList = path.split('.')
pathArray = pathList[0].split('[')
if len(pathArray) == 2:
# This is the case where it is an array, eg: sim.resources[1].cost
arrayName = pathArray[0]
arrayIndex = int(pathArray[1][:-1])
if not obj.has_key(arrayName): # Here we assume the list is inside a dict
obj[arrayName] = list()
# obj[arrayName].append("blah")
elif not len(pathList) == 1:
if len(obj[arrayName]) > arrayIndex:
AddEmptyArray( obj[arrayName][arrayIndex], path.split('.', 1)[1] )
else:
# Normal non-array item case
#print 'here: '
#print obj
if not len(pathList) == 1:
if pathList[0] in obj.keys():
AddEmptyArray( obj[pathList[0]], path.split('.', 1)[1] )
# Turn a string in to a typed python object
def MakeTyped(str, typ):
if typ == 'Number':
if '.' in str:
return float(str)
# allow the int/float conversions to except if input malformed
return int(str)
elif typ == 'Boolean':
if str == '1':
return True
elif str == '0':
return False
print 'Bad boolean value'
exit(0)
elif typ == 'String':
return str
print 'Unknown/unexpected type: ' + typ
exit(0)
# This is the python object that will be populated with the python representation of the GameDb
pyGameDb = {}
namespaces = { 'ss': 'urn:schemas-microsoft-com:office:spreadsheet' }
for sheet in root.iterfind('ss:Worksheet', namespaces):
tableName = sheet.attrib['{urn:schemas-microsoft-com:office:spreadsheet}Name']
# print 'Adding table: ' + tableName
newTable = []
pyGameDb[tableName] = newTable
for table in sheet.iterfind('ss:Table', namespaces):
rowIndex = 0
columnNames = []
for row in table.iterfind('ss:Row', namespaces):
if rowIndex == 0:
for cell in row.iterfind('ss:Cell', namespaces):
for data in cell.iterfind('ss:Data', namespaces):
columnNames.append(data.text)
else:
rowData = {}
columnIndex = 0
rowName = 'empty name'
ignoreList = []
for cell in row.iterfind('ss:Cell', namespaces):
data = cell.find('ss:Data', namespaces)
if columnIndex < len(columnNames):
col = columnNames[columnIndex]
if data == None:
if not "[" in col:
if GameDBUtils.UnflattenJson( rowData, col, "" ) == False:
print 'UnflattenJson error on col: ' + col + ' with row: ' + str(rowData)
else:
dataType = data.attrib['{urn:schemas-microsoft-com:office:spreadsheet}Type']
if data.text:
if 0 == columnIndex:
rowName = data.text
if GameDBUtils.UnflattenJson( rowData, col, MakeTyped(data.text, dataType) ) == False:
print 'UnflattenJson error on col: ' + col + ' and data: ' + data.text + ' with row: ' + str(rowData)
elif not "[" in col and dataType == 'String':
# Remove empty string for now because of CROM handling
ignoreList.append(col)
columnIndex += 1
if (columnIndex != len(columnNames)):
print 'Invalid row/cell ' + rowName + ' should have ' + str(len(columnNames)) + ' columns but has ' + str(columnIndex) + ' instead'
exit(1)
# Normalize the json so it still outputs an array object when it is empty
for col in columnNames:
if col not in ignoreList:
AddEmptyArray( rowData, col )
pyGameDb[tableName].append(rowData)
rowIndex += 1
# Output the python representation of the GameDb as json
if prettyprint==True:
print json.dumps(pyGameDb, indent=4, sort_keys=True)
else:
print json.dumps(pyGameDb, sort_keys=True)