diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index 0a9c63bd0..a677142fa 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -1,12 +1,12 @@ #! /usr/bin/env python -# untested on Linux - -# This script generates cross-references to show associated (handled) opcodes between the server and client. -# It will generate files for each client found and provide some basic information..such as opcode names and -# values, server handler and whether opcodes are translated on tx/rx. # -# It's currently limited to the 'Zone' server..but, can be expounded upon to include other servers, clients -# and other criteria and features. +# This script generates cross-references to show associated (handled) opcodes +# between the server and client. It will generate files for each client found +# and provide some basic information..such as opcode names and values, server +# handler and whether opcodes are translated on tx/rx. +# +# It's currently limited to the 'Zone' server..but, can be expounded upon to +# include other servers, clients and other criteria and features. import os @@ -15,14 +15,16 @@ import os from time import time, ctime -VERBOSE = True # pipes relativistic information to the console window -DEBUG = False # setting this to 'True' will pipe more information to the console window than you would ever want! +# pipes relativistic information to the console window +VERBOSE = True -repopath = os.getcwd() -repopath = repopath[:-14] # '\utils\scripts' - because I'm lazy and don't want to do it the right way... +# setting this to 'True' will pipe more information than you would ever want! +DEBUG = False -clients = [ '6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF' ] -servers = [ 'Login', 'World', 'Zone' ] +repopath = os.getcwd()[:-14] # '/utils/scripts' + +clients = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF'] +servers = ['Login', 'World', 'Zone', 'UCS'] clientops = {} serverops = {} @@ -34,439 +36,589 @@ handlers = {} outstreams = {} -def main() : +def main(): print('Loading source data...') - if not LoadServerOpcodes() : + if not loadserveropcodes(): print('Error! Could not load server opcodes') return - if not LoadClientOpcodes() : + if not loadclientopcodes(): print('Error! Could not load client opcodes') return - if not LoadClientTranslators() : + if not loadclienttranslators(): print('Error! Could not load client translators') return - if not LoadServerHandlers() : + if not loadserverhandlers(): print('Error! Could not load server handlers') return print('Creating output streams...') - if not CreateOutputDirectory() : - print('Output directory already exists or could not be created...') - #return # existing directory returns a failure..don't exit here..CreateOutputStreams() will catch makdir() problems + if not createoutputdirectory(): + print('Output directory could not be created...') + return - if not CreateOutputStreams() : + if not createoutputstreams(): print('Error! Could not open output files') return print('Parsing opcode data...') - if not ParseOpcodeData() : + if not parseopcodedata(): print('Error! Could not parse opcode data') return print('Destroying output streams...') - if not DestroyOutputStreams() : + if not destroyoutputstreams(): print('Error! Could not close output files') return -def LoadServerOpcodes() : +def loadserveropcodes(): # Server opcodes are auto-enumerated with a starting reference of '1' - try : - filename = '{0}{1}{2}'.format(repopath, '/common', '/emu_oplist.h') + try: + value = 0 + + filename = '{0}{1}{2}'.format( + repopath, + '/common', + '/emu_oplist.h') - if VERBOSE or DEBUG : print(filename) - - with open(filename, 'r') as datafile : - value = 0 - serverops['Null'] = value - - while True : - begin = 0 - end = 0 - + if VERBOSE or DEBUG: + print(filename) + + with open(filename, 'r') as datafile: + while True: dataline = datafile.readline() - if not dataline : break - if not dataline[:1] == 'N' : continue - - while not dataline[begin:(begin + 1)] == '(' : - if begin >= len(dataline) : break - else : begin = begin + 1 + if not dataline: + break - end = begin + 1 + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue + + value += 1 - while not dataline[(end - 1):end] == ')' : - if end >= len(dataline) : break - else : end = end + 1 + if dataline[:1] == 'N': + serverops[dataline[vbegin:vend]] = value - begin = begin + 1 # adjust out the '(' - end = end - 1 # adjust out the ')' - - if begin >= end or begin >= len(dataline) : continue - if end >= len(dataline) : continue - - value = value + 1 - - serverops[dataline[begin:end]] = value - - if DEBUG : print('({0}, {1}) = {2}'.format('Server', dataline[begin:end], serverops[dataline[begin:end]])) + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Server', + dataline[vbegin:vend], + serverops[dataline[vbegin:vend]])) datafile.close() + + filename = '{0}{1}{2}'.format( + repopath, + '/common', + '/mail_oplist.h') + + if VERBOSE or DEBUG: + print(filename) + + with open(filename, 'r') as datafile: + while True: + dataline = datafile.readline() - except : return False + if not dataline: + break + + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue + + value += 1 + + if dataline[:1] == 'N': + serverops[dataline[vbegin:vend]] = value + + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Server', + dataline[vbegin:vend], + serverops[dataline[vbegin:vend]])) + + datafile.close() + except: + return False return True -def LoadClientOpcodes() : +def loadclientopcodes(): badclients = [] - for client in clients : - try : - filename = '{0}{1}{2}{3}{4}'.format(repopath, '/utils/patches', '/patch_', client, '.conf') + for client in clients: + try: + filename = '{0}{1}{2}{3}{4}'.format( + repopath, + '/utils/patches', + '/patch_', + client, + '.conf') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : - while True : - kbegin = 0 - vbegin = 0 - kend = 0 - vend = 0 - + with open(filename, 'r') as datafile: + while True: dataline = datafile.readline() - if not dataline : break - if not dataline[0:3] == 'OP_' : continue + if not dataline: + break - kend = kbegin + 3 + kbegin = dataline.find('OP_') + kend = dataline.find('=', kbegin) - while not dataline[(kend - 1):kend] == '=' : - if kend >= len(dataline) : break - else : kend = kend + 1 - - kend = kend - 1 # adjust out the '=' - vbegin = kend + 1 + if not kbegin == 0: + continue + if kend == -1: + continue + + vbegin = dataline.find('0x', kend) vend = vbegin + 6 - if dataline[vbegin:vend] == '0x0000' : continue + if vbegin == -1: + continue + + value = int(dataline[(vbegin + 2):vend].lower(), 16) + + if value == 0: + continue + + clientops[client, dataline[kbegin:kend]] = '0x{0}'.format( + hex(value)[2:].zfill(4)) - clientops[client, dataline[kbegin:kend]] = dataline[vbegin:vend] - - if DEBUG : print('({0}, {1}) = {2}'.format(client, dataline[kbegin:kend], clientops[client, dataline[kbegin:kend]])) + if DEBUG: + print('({0}, {1}) = {2} (int: {3})'.format( + client, + dataline[kbegin:kend], + clientops[client, dataline[kbegin:kend]], + value)) datafile.close() + except: + badclients.append(client) - except : badclients.append(client) - - if len(badclients) > 0 : + if len(badclients) > 0: badentries = [] - for badclient in badclients : - if VERBOSE or DEBUG : print('Deleting client {0} from search criteria...'.format(badclient)) + for badclient in badclients: + if VERBOSE or DEBUG: + print('Deleting client {0} from search criteria...'.format( + badclient)) clients.remove(badclient) - if VERBOSE or DEBUG : print('Deleting any partial entries for client {0}...'.format(badclient)) + if VERBOSE or DEBUG: + print('Deleting any partial entries for client {0}...'.format( + badclient)) - for entry in clientops.keys() : - if entry[0] == client : + for entry in clientops.keys(): + if entry[0] == badclient: badentries.append(entry) - for badentry in badentries : + for badentry in badentries: del clientops[badentry] - if len(clients) == 0 : + if len(clients) == 0: return False return True -def LoadClientTranslators() : - for client in clients : - try : - if client == '6.2' : shortname = '{0}'.format('/Client62_ops.h') - else : shortname = '{0}{1}{2}'.format('/', client, '_ops.h') - - filename = '{0}{1}{2}'.format(repopath, '/common/patches', shortname) +def loadclienttranslators(): + for client in clients: + if client == '6.2': + shortname = '{0}'.format( + '/Client62_ops.h') + else: + shortname = '{0}{1}{2}'.format( + '/', + client, + '_ops.h') + + try: + filename = '{0}{1}{2}'.format( + repopath, + '/common/patches', + shortname) - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : + with open(filename, 'r') as datafile: while True: - begin = 0 - end = 0 - dataline = datafile.readline() - if not dataline : break - if not dataline[:1] == 'E' and not dataline[0:1] == 'D' : continue - - while not dataline[begin:(begin + 1)] == '(' : - if begin >= len(dataline) : break - else : begin = begin + 1 + if not dataline: + break - end = begin + 1 - - while not dataline[(end - 1):end] == ')' : - if end >= len(dataline) : break - else : end = end + 1 - - begin = begin + 1 # adjust out the '(' - end = end - 1 # adjust out the ')' - - if begin >= end or begin >= len(dataline) : continue - if end >= len(dataline) : continue + vbegin = dataline.find('OP_', 2) + vend = dataline.find(')', vbegin) + + if vbegin == -1: + continue + if vend == -1: + continue - if dataline[:1] == 'E' : - encodes.append((client, dataline[begin:end])) + if dataline[:1] == 'E': + encodes.append((client, dataline[vbegin:vend])) - if DEBUG : print('ENCODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], encodes.index((client, dataline[begin:end])))) - elif dataline[:1] == 'D' : - decodes.append((client, dataline[begin:end])) + if DEBUG: + print('ENCODE({0}, {1}) [floating index: {2}]'.format( + client, + dataline[vbegin:vend], + encodes.index((client, dataline[vbegin:vend])))) + elif dataline[:1] == 'D': + decodes.append((client, dataline[vbegin:vend])) - if DEBUG : print('DECODE({0}, {1}) [floating index: {2}]'.format(client, dataline[begin:end], decodes.index((client, dataline[begin:end])))) + if DEBUG: + print('DECODE({0}, {1}) [floating index: {2}]'.format( + client, + dataline[vbegin:vend], + decodes.index((client, dataline[vbegin:vend])))) datafile.close() + except: + # TODO: need to handle + return False - except : return False # TODO: need to handle - - if len(encodes) == 0 and len(decodes) == 0 : + # this will need to change if we ever have a client with 100% support + if len(encodes) == 0 and len(decodes) == 0: return False return True -def LoadServerHandlers() : +def loadserverhandlers(): # TODO: handle remarked out definitions; add other servers, if possible - try : - filename = '{0}{1}{2}'.format(repopath, '/zone', '/client_packet.cpp') + try: + filename = '{0}{1}{2}'.format( + repopath, + '/zone', + '/client_packet.cpp') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) - with open(filename, 'r') as datafile : + with open(filename, 'r') as datafile: dataline = datafile.readline() - while not dataline[:19] == 'void MapOpcodes() {' : + while not dataline[:19] == 'void MapOpcodes() {': dataline = datafile.readline() - if not dataline : break + if not dataline: + break - while True : - kbegin = 0 - vbegin = 0 - kend = 0 - vend = 0 - + while True: dataline = datafile.readline() - if not dataline : break - if dataline[0:1] == '}' : break + if not dataline: + break + if dataline[0:1] == '}': + break kbegin = dataline.find('OP_') + kend = dataline.find(']', kbegin) - if kbegin == -1 : continue + if kbegin == -1: + continue + if kend == -1: + continue - kend = kbegin + 1 - - while not dataline[kend:(kend + 1)] == ']' : - if kend >= len(dataline) : break - else : kend = kend + 1 + vbegin = dataline.find('Client::', kend) + vend = dataline.find(';', vbegin) - vbegin = dataline.find('&Client::') - - if vbegin == -1 : continue - - vbegin = vbegin + 1 - vend = vbegin + 9 - - while not dataline[vend:(vend + 1)] == ';' : - if vend >= len(dataline) : break - else : vend = vend + 1 + if vbegin == -1: + continue + if vend == -1: + continue handlers['Zone', dataline[kbegin:kend]] = dataline[vbegin:vend] - if DEBUG : print('({0}, {1}) = {2}'.format('Zone', dataline[kbegin:kend], handlers['Zone', dataline[kbegin:kend]])) + if DEBUG: + print('({0}, {1}) = {2}'.format( + 'Zone', + dataline[kbegin:kend], + handlers['Zone', dataline[kbegin:kend]])) datafile.close() - - except : return False # should probably do the same for this (badservers) as done for badclients above + except: + # should probably do the same for this (badservers) + # as done for badclients in the above function + return False return True -def CreateOutputDirectory() : - try : - outputpath = '{0}{1}'.format(repopath, '/utils/scripts/opcode_handlers_output') +def createoutputdirectory(): + outputpath = '' + + try: + outputpath = '{0}{1}'.format( + repopath, + '/utils/scripts/opcode_handlers_output') - if VERBOSE or DEBUG : print(outputpath) - - os.mkdir(outputpath) + if VERBOSE or DEBUG: + print(outputpath) + + if not os.path.exists(outputpath): + os.mkdir(outputpath) return True + except: + return False + + +def createoutputstreams(): + filename = '' - except : return False - - -def CreateOutputStreams() : - try : - for client in clients : - filename = '{0}{1}{2}{3}'.format(repopath, '/utils/scripts/opcode_handlers_output/', client, '_opcode_handlers.txt') + try: + for client in clients: + filename = '{0}{1}{2}{3}'.format( + repopath, + '/utils/scripts/opcode_handlers_output/', + client, + '_opcode_handlers.txt') - if VERBOSE or DEBUG : print(filename) + if VERBOSE or DEBUG: + print(filename) outstreams[client] = open(filename, 'w') - - outstreams[client].write('******************************************************\n') - outstreams[client].write('** Opcode-Handler analysis for \'{0}\' client\n'.format(client)) - outstreams[client].write('** script-generated file @ {0}\n'.format(ctime(time()))) - outstreams[client].write('**\n') - outstreams[client].write('** (only cross-linked (active) opcodes are listed)\n') - outstreams[client].write('******************************************************\n\n') - - except : - for client in clients : - if client in outstreams.keys() : + + message = \ + '------------------------------------------------------\n' \ + '|| Opcode-Handler analysis for \'{0}\' client\n' \ + '|| script-generated file @ {1}\n' \ + '||\n' \ + '|| (only cross-linked (active) opcodes are listed)\n' \ + '------------------------------------------------------\n' \ + '\n'.format( + client, + ctime(time())) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-2]) + except: + for client in clients: + if client in outstreams.keys(): outstreams[client].close() del outstreams[client] + + if DEBUG: + print('->CreatingClientStream(exception): {0}'.format( + client)) return False return True -def ParseOpcodeData() : +def parseopcodedata(): serveropnames = [] - for serveropkey in serverops.keys() : - if serveropkey == 'Null' : continue - - if DEBUG : print('->ServerOpKey: {0}'.format(serveropkey)) - + for serveropkey in serverops.keys(): serveropnames.append(serveropkey) - if len(serveropnames) == 0 : return False + if DEBUG: + print('->ServerOpKey: {0}'.format( + serveropkey)) + + if len(serveropnames) == 0: + return False - for server in servers : - if server == 'Login' or server == 'World' : continue # Login, World not implemented yet + for server in servers: + # Login, World, UCS not implemented yet + if not server == 'Zone': + if DEBUG: + print('->SkippingServerOpcodeParse: {0}'.format( + server)) + + continue handleropnames = [] - for handlerkey in handlers.keys() : - if handlerkey[0] == server : - if DEBUG : print('->HandlerKey: {0}'.format(handlerkey[1])) - + for handlerkey in handlers.keys(): + if handlerkey[0] == server: handleropnames.append(handlerkey[1]) + + if DEBUG: + print('->HandlerKey: {0}'.format( + handlerkey[1])) - if len(handleropnames) == 0 : return False - else : handleropnames.sort() # sort to process opcode names in ascending order + if len(handleropnames) == 0: + return False + else: + # sort to process opcode names in ascending order + handleropnames.sort() - for client in clients : + for client in clients: clientopnames = [] clientencodes = [] clientdecodes = [] - notranslation = 0 + totalopcodes = 0 + encodedecode = 0 encodeonly = 0 decodeonly = 0 - encodedecode = 0 - totalopcodes = 0 + notranslation = 0 - for clientopkey in clientops.keys() : - if clientopkey[0] == client : - if DEBUG : print('->ClientOpKey: {0}'.format(clientopkey[1])) - + for clientopkey in clientops.keys(): + if clientopkey[0] == client: clientopnames.append(clientopkey[1]) - - if len(clientopnames) == 0 : return False - - for encodeentry in encodes : - if encodeentry[0] == client : - if DEBUG : print('->EncodeEntry: {0}'.format(encodeentry[1])) + if DEBUG: + print('->ClientOpKey: {0}'.format( + clientopkey[1])) + + if len(clientopnames) == 0: + return False + + for encodeentry in encodes: + if encodeentry[0] == client: clientencodes.append(encodeentry[1]) - - if len(clientencodes) == 0 : return False - - for decodeentry in decodes : - if decodeentry[0] == client : - if DEBUG : print('->DecodeEntry: {0}'.format(decodeentry[1])) - clientdecodes.append(decodeentry[1]) + if DEBUG: + print('->EncodeEntry: {0}'.format( + encodeentry[1])) - if len(clientdecodes) == 0 : return False + if len(clientencodes) == 0: + return False + + for decodeentry in decodes: + if decodeentry[0] == client: + clientdecodes.append(decodeentry[1]) + + if DEBUG: + print('->DecodeEntry: {0}'.format( + decodeentry[1])) + + if len(clientdecodes) == 0: + return False - for handleropentry in handleropnames : - try : clientopindex = clientopnames.index(handleropentry) - except : clientopindex = -1 + for handleropentry in handleropnames: + try: + clientopindex = clientopnames.index(handleropentry) + except: + clientopindex = -1 - if clientopindex > -1 : + if clientopindex > -1: val0 = clientopnames[clientopindex] val1 = serverops[val0] val2 = clientops[(client, val0)] - - if DEBUG : print('->Opcode: {0} ({1}: {2} | {3}: {4})'.format(val0, server, val1, client, val2)) - - outstreams[client].write('Opcode: {0} | {1}: {2} | {3}: {4}\n'.format(val0, server, val1, client, val2)) - val3 = handlers[(server, val0)] - if DEBUG : print('->{0} Handler: {1}'.format(server, val3)) + try: + val4 = clientencodes.index(val0) > -1 + except: + val4 = False + try: + val5 = clientdecodes.index(val0) > -1 + except: + val5 = False - outstreams[client].write(' {0} Handler: {1}\n'.format(server, val3)) + message = \ + 'Opcode: {0} | {1}: {2} | {3}: {4}\n' \ + ' {5} Handler: \'{6}\'\n' \ + ' Encoded: {7} | Decoded: {8}\n\n'.format( + val0, + server, + val1, + client, + val2, + server, + val3, + val4, + val5) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-2]) + + totalopcodes += 1 + + if val4 and val5: + encodedecode += 1 + elif val4 and not val5: + encodeonly += 1 + elif not val4 and val5: + decodeonly += 1 + elif not val4 and not val5: + notranslation += 1 - try : val4 = clientencodes.index(val0) > -1 - except : val4 = False - try : val5 = clientdecodes.index(val0) > -1 - except : val5 = False + if DEBUG: + print('->EndOfOpcodeLoop: {0}'.format( + val0)) + + message = \ + 'Statistics -------------------------------------------\n' \ + '|| Server Opcode Type: {0}\n' \ + '|| Handled Opcodes: {1}\n' \ + '|| Bi-directional: {2}\n' \ + '|| Encodes Only: {3}\n' \ + '|| Decodes Only: {4}\n' \ + '|| No Translations: {5}\n' \ + '\n' \ + 'Notes ------------------------------------------------\n' \ + '|| Encodes are Server-to-Client and Decodes are' \ + ' Server-from-Client in context\n' \ + '|| \'Bi-directional\' indicates translations are performed on' \ + ' tx/rx packets\n' \ + '|| \'Encodes Only\' indicates translations only on tx packets' \ + ' - does not exclude direct packet rx\n' \ + '|| \'Decodes Only\' indicates translations only on rx packets' \ + ' - does not exclude direct packet tx\n' \ + '|| \'No Translations\' indicates no translations of tx/rx' \ + ' packets\n'.format( + server, + totalopcodes, + encodedecode, + encodeonly, + decodeonly, + notranslation) + + outstreams[client].write(message) + + if DEBUG: + print(message[:-1]) + print('->EndOfClientLoop: {0}'.format( + client)) - if DEBUG : print('Encoded: {0} | Decoded: {1}'.format(val4, val5)) - - outstreams[client].write(' Encoded: {0} | Decoded: {1}\n\n'.format(val4, val5)) - - totalopcodes = totalopcodes + 1 - - if val4 and val5 : encodedecode = encodedecode + 1 - elif val4 and not val5 : encodeonly = encodeonly + 1 - elif not val4 and val5 : decodeonly = decodeonly + 1 - elif not val4 and not val5 : notranslation = notranslation + 1 - - if DEBUG : print('->EndOfOpcodeLoop: {0}'.format(val0)) - - if DEBUG : print('->OpcodeCount: {0}'.format(totalopcodes)) - if DEBUG : print('->Translations: (Bi-directional: {0}, EncodeOnly: {1}, DecodeOnly: {2}, NoTranslation: {3})'.format(encodedecode, encodeonly, decodeonly, notranslation)) - - outstreams[client].write('Statistics *******************************************\n') - outstreams[client].write('** Handled Opcodes: {0}\n'.format(totalopcodes)) - outstreams[client].write('** Bi-directional Translations: {0}\n'.format(encodeonly)) - outstreams[client].write('** Encodes Only: {0}\n'.format(encodeonly)) - outstreams[client].write('** Decodes Only: {0}\n'.format(decodeonly)) - outstreams[client].write('** No Translations: {0}\n'.format(notranslation)) - outstreams[client].write('Notes ************************************************\n') - outstreams[client].write('** \'Bi-directional\' indicates translations are performed on tx/rx packets\n') - outstreams[client].write('** \'Encodes Only\' indicates translations only on tx packets - does not exclude direct packet rx\n') - outstreams[client].write('** \'Decodes Only\' indicates translations only on rx packets - does not exclude direct packet tx\n') - outstreams[client].write('** \'No Translations\' indicates no translations on tx/rx of packets\n') - - if DEBUG : print('->EndOfClientLoop: {0}'.format(client)) - - if DEBUG : print('->EndOfServerLoop: {0}'.format(server)) + if DEBUG: + print('->EndOfServerLoop: {0}'.format( + server)) return True -def DestroyOutputStreams() : - for client in clients : - if client in outstreams.keys() : +def destroyoutputstreams(): + for client in clients: + if client in outstreams.keys(): outstreams[client].close() del outstreams[client] + + if DEBUG: + print('->DestroyingClientStream: {0}'.format( + client)) return True