LexManos

Python Reflection helper generator

Mar 23rd, 2012
245
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.43 KB | None | 0 0
  1. import re, os, shutil, sys, fnmatch, csv
  2. from time import gmtime, strftime
  3. from pprint import pprint
  4. from zipfile import ZipFile
  5. from classfile import ClassFile
  6.  
  7. base = 'C:\\Users\\Lex\\Dropbox\\Public\\MinecraftForge\\'
  8. fCsv = None
  9. mCsv = None
  10.  
  11. def main():
  12.     genMap('client.srg', 'minecraft.jar', '0')
  13.     genMap('server.srg', 'minecraft_server.jar', '1')
  14.    
  15. def genMap(srg, jar, side):
  16.     global fCsv, mCsv
  17.     print 'Generating reflection files for %s' % ('Client' if side == '0' else 'Server')
  18.     with open(srg) as f:
  19.         srg = f.readlines()
  20.     fCsv = readCSV('fields.csv', side)
  21.     mCsv = readCSV('methods.csv', side)
  22.     data = {}
  23.     classes = {}
  24.    
  25.     for line in srg:
  26.         pts = line.split()
  27.        
  28.         if pts[0] == 'CL:':
  29.             #print ('Class %s -> %s' % (pts[1], pts[2]))
  30.             data[pts[2] + '|' + pts[1]] = {}
  31.             classes[pts[1]] = pts[2]
  32.            
  33.         elif pts[0] == 'FD:':
  34.             ps1 = pts[1].rsplit('/', 1)
  35.             ps2 = pts[2].rsplit('/', 1)
  36.             cls = ps2[0] + '|' + ps1[0]
  37.             #if ps2[1] in fCsv:
  38.             #    ps2[1] = fCsv[ps2[1]]
  39.             #print 'Field: %s|%s -> %s -> %s' % (ps1[0], ps2[0], ps1[1], ps2[1])
  40.             if not 'Fields' in data[cls]:
  41.                 data[cls]['Fields'] = {}
  42.             data[cls]['Fields'][ps1[1]] = ps2[1]
  43.            
  44.         elif pts[0] == 'MD:':
  45.             ps1 = pts[1].rsplit('/', 1)
  46.             ps2 = pts[3].rsplit('/', 1)
  47.             cls = ps2[0] + '|' + ps1[0]
  48.             if '/' in ps1[0]:
  49.                 pts[2] = pts[2].replace(ps1[0].rsplit('/', 1)[0] + '/', '')
  50.             if '/' in ps2[0]:
  51.                 pts[4] = pts[4].replace(ps2[0].rsplit('/', 1)[0] + '/', '')
  52.             #if ps2[1] in mCsv:
  53.             #    ps2[1] = mCsv[ps2[1]]
  54.             #print 'Method: %s|%s -> %s%s -> %s%s' % (ps1[0], ps2[0], ps1[1], pts[2], ps2[1], pts[4])
  55.             if not 'Methods' in data[cls]:
  56.                 data[cls]['Methods'] = {}
  57.             data[cls]['Methods'][ps1[1] + pts[2]] = ps2[1] + pts[4]
  58.            
  59.     parseJar(jar, classes, data)
  60.    
  61.     with open('%s%s_Hierarchy.txt' % (base, ('Client' if side == '0' else 'Server')), 'w') as f:        
  62.         writeHierarchy(f, makeClassHierarchy(data))
  63.            
  64.     with open('%s%s.map' % (base, ('Client' if side == '0' else 'Server')), 'w') as f:
  65.         f.write('{\n');
  66.         writeData(f, data, '    ')        
  67.         f.write('}')
  68.        
  69.     with open('%s%s.java' % (base, ('Client' if side == '0' else 'Server')), 'w') as f:
  70.         f.write(
  71. """package net.minecraft.src.forge;
  72.  
  73. import java.util.Hashtable;
  74.  
  75. /*
  76. * This is auto-generated reflection obfusication maping helper class.
  77. * It is made to allow you easier access to the obfusicated names from both MCP and runtime.
  78. * This file was generated on %s
  79. */
  80.  
  81. public class ReflectionHelper%s
  82. {
  83. """ % (strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()), ('Client' if side == '0' else 'Server')))
  84.         writeJava(f, data, '    ')
  85.         f.write('}')        
  86.        
  87.        
  88. regs = {
  89.     'methods': re.compile(r'func_(?P<id>[0-9]+)_[a-zA-Z_]+'),
  90.     'fields' : re.compile(r'field_(?P<id>[0-9]+)_[a-zA-Z_]+')
  91. }
  92.  
  93.  
  94. def indentData(data, indent):
  95.     return data.replace("""
  96. """, """
  97. %s""" % indent);
  98.  
  99. def writeJava(file, data, indent=''):
  100.     keylist = data.keys()
  101.     keylist.sort()
  102.     table = {'obf' : {}, 'mcp' : {}}
  103.    
  104.     for key in keylist:
  105.         value = data[key]
  106.         ending = ''
  107.         if type(value) == dict:
  108.             if not shouldPrintJava(value):
  109.                 continue
  110.                
  111.             cls = key.split('|', 1)[0].rsplit('/', 1)[1]
  112.             file.write('%spublic static class r%s \n%s{\n' % (indent, cls, indent))
  113.            
  114.             if 'Fields' in value:
  115.                 writeJavaFields(file, value['Fields'], indent + '    ', table)
  116.             if 'Methods' in value:
  117.                 writeJavaMethods(file, value['Methods'], indent + '    ', table)
  118.                
  119.             file.write('%s}\n' % (indent))
  120.            
  121.     file.write(indentData("""
  122. private static void addObfNames()
  123. {\n""", indent))
  124.     keys = table['obf'].keys()
  125.     keys.sort()
  126.     for key in keys:
  127.         value = table['obf'][key]
  128.         value.sort()
  129.         file.write('%s    addMapping("%s"' % (indent, key))
  130.         for v in value: file.write(', %s' %v)
  131.         file.write(');\n')
  132.        
  133.        
  134.     file.write(indentData("""
  135. }
  136.  
  137. private static void addMCPNames()
  138. {""", indent))
  139.    
  140.     keys = table['mcp'].keys()
  141.     keys.sort()
  142.     for key in keys:
  143.         value = table['mcp'][key]
  144.         value.sort()
  145.         file.write('%s    addMapping("%s"' % (indent, key))
  146.         for v in value: file.write(', %s' %v)
  147.         file.write(');\n')
  148.        
  149.     file.write(indentData("""
  150. }
  151.  
  152. private static boolean hasInit = false;
  153. private static Hashtable<Integer, String> mapings = new Hashtable<Integer, String>();
  154. public static void init()
  155. {
  156.    if (hasInit) return;
  157.    if (net.minecraft.src.Entity.class.getName().equals("net.minecraft.src.Entity"))
  158.    {
  159.        addMCPNames();
  160.    }
  161.    else
  162.    {
  163.        addObfNames();
  164.    }
  165.    hasInit = true;
  166. }
  167.  
  168. private static void addMapping(String data, int... indexes)
  169. {
  170.    for(int x : indexes)
  171.    {
  172.        mapings.put(x, data);
  173.    }
  174. }
  175.  
  176. public static String getMapping(int index)
  177. {
  178.    init();
  179.    return (String)mapings.get(index);
  180. }""", indent))
  181.     file.write('\n')
  182.  
  183.    
  184.    
  185. def shouldPrintJava(data):
  186.     return 'Fields' in data or 'Methods' in data
  187.     if 'Fields' in data:
  188.         for key, value in data['Fields'].items():
  189.             if not regs['fields'].match(value) is None:
  190.                 return True
  191.                
  192.     if 'Methods' in data:
  193.         for key, value in data['Methods'].items():
  194.             if not regs['methods'].match(value.split('(', 1)[0]) is None:
  195.                 return True
  196.                
  197.     return False
  198.  
  199. def writeJavaFields(file, data, indent, table):
  200.     global fCsv
  201.     sort = {}
  202.     for key, value in data.items():
  203.         if value in fCsv:
  204.             sort[fCsv[value]] = key
  205.         else:
  206.             sort[value] = key
  207.    
  208.     keys = sort.keys()
  209.     keys.sort()    
  210.     comment = True
  211.    
  212.     for key in keys:
  213.         ret = regs['fields'].match(data[sort[key]])
  214.         if not ret is None:
  215.            
  216.             id = ret.group('id')
  217.             if comment:
  218.                 file.write('%spublic static class f //Fields\n%s{\n' % (indent, indent))
  219.                 file.write('%s    public static final int %s = %s;\n' % (indent, key, id))
  220.                 comment = False
  221.             else:
  222.                 file.write('%s    public static final int %s = %s;\n' % (indent, key, id))
  223.            
  224.             obf = sort[key]
  225.             t = table['obf']
  226.             if not obf in t: t[obf] = []
  227.             if not id in t[obf]: t[obf].append(id)
  228.            
  229.             t = table['mcp']
  230.             if not key in t: t[key] = []
  231.             if not id in t[key]: t[key].append(id)
  232.     if not comment: file.write('%s}\n' % (indent))
  233.            
  234. def writeJavaMethods(file, data, indent, table):
  235.     global mCsv
  236.     sort = {}
  237.     for key, value in data.items():
  238.         name = value.split('(', 1)[0]
  239.         if name in mCsv:
  240.             idx = mCsv[name]
  241.             if idx in sort:
  242.                 x = 1
  243.                 while '%s_%d' % (idx, x) in sort:
  244.                     x += 1
  245.                 sort['%s_%d' % (idx, x)] = key
  246.             else:
  247.                 sort[idx] = key
  248.         else:
  249.             sort[name] = key
  250.            
  251.     keys = sort.keys()
  252.     keys.sort()
  253.     comment = True
  254.    
  255.     for key in keys:
  256.         name = data[sort[key]].split('(', 1)[0]
  257.         ret = regs['methods'].match(name)
  258.         if not ret is None:
  259.             id = ret.group('id')
  260.             if comment:
  261.                 file.write('%spublic static class m //Methods\n%s{\n' % (indent, indent))
  262.                 file.write('%s    public static final int %s = %s; //%s\n' % (indent, key, id, '(' + data[sort[key]].split('(', 1)[1]))
  263.                 comment = False
  264.             else:
  265.                 file.write('%s    public static final int %s = %s; //%s\n' % (indent, key, id, '(' + data[sort[key]].split('(', 1)[1]))
  266.            
  267.             obf = sort[key].split('(', 1)[0]
  268.             t = table['obf']
  269.             if not obf in t: t[obf] = []
  270.             if not id in t[obf]: t[obf].append(id)
  271.     if not comment: file.write('%s}\n' % (indent))
  272.        
  273.        
  274. def writeData(file, data, indent='', hasMore=True):
  275.     keylist = data.keys()
  276.     keylist.sort()
  277.     for x in range(0, len(keylist)):
  278.         key = keylist[x]
  279.         value = data[key]
  280.         ending = (',' if x < len(keylist) - 1 else '')
  281.         if type(value) == dict:
  282.             if len(value) == 0:
  283.                 file.write('%s"%s": {}%s\n' % (indent, key, ending))
  284.             else:
  285.                 file.write('%s"%s": {\n' % (indent, key))
  286.                 writeData(file, value, indent + '    ', x < len(keylist))
  287.                 file.write('%s}%s\n' % (indent, ending))
  288.                
  289.         elif type(value) == str or type(value) == unicode:
  290.             file.write('%s"%s": "%s"%s\n' % (indent, key, value, ending))
  291.            
  292.         elif type(value) == list:
  293.             if len(value) == 0:
  294.                 file.write('%s"%s": []%s\n' % (indent, key, ending))
  295.             elif len(value) == 1:
  296.                 file.write('%s"%s": [ "%s" ]%s\n' % (indent, key, value[0], ending))
  297.             else:
  298.                 file.write('%s"%s" : [\n' % (indent, key))
  299.                 value.sort()
  300.                 for v in range(0, len(value)):
  301.                     file.write('%s    "%s"%s\n' % (indent, value[v], (',' if v < len(value) - 1 else '')))
  302.                 file.write('%s]%s\n' % (indent, ending))
  303.         else:
  304.             print type(value)
  305.            
  306. def readCSV(filename, side):
  307.     tCsv = csv.DictReader(open(filename, 'rb'))
  308.     data = {}
  309.     for row in tCsv:
  310.         if row['side'] == side:
  311.             data[row['searge']] = row['name']
  312.     return data
  313.  
  314. def parseJar(jar, classes, data):
  315.     zip = ZipFile(jar, 'r')
  316.     for className in zip.namelist():
  317.         if className.endswith('.class'):
  318.             className = className[0:-6]
  319.             if not className in classes:
  320.                 continue
  321.                
  322.             key = classes[className] + '|' + className
  323.            
  324.             classData = zip.read(className + '.class')
  325.            
  326.             c = ClassFile(classData)            
  327.             super = str(c.super_class.get_name())
  328.            
  329.             package1 = className.rsplit('/', 1)[0] + '/'
  330.             package2 = classes[className].rsplit('/', 1)[0] + '/'
  331.            
  332.             if super != 'java/lang/Object':
  333.                 if super in classes:
  334.                     data[key]['Super'] = classes[super].replace(package2, '') + '|' + super.replace(package1, '')
  335.                 else:
  336.                     data[key]['Super'] = super
  337.                
  338.             for interface in c.interfaces:
  339.                 if not 'Interfaces' in data[key]:
  340.                     data[key]['Interfaces'] = []
  341.                 intName = str(interface.get_name())
  342.                 if intName in classes:
  343.                     data[key]['Interfaces'].append(classes[intName].replace(package2, '') + '|' + intName.replace(package1, ''))
  344.                 else:
  345.                     data[key]['Interfaces'].append(intName)
  346.        
  347.     zip.close()
  348.  
  349. def makeClassHierarchy(data):
  350.     classes = {}
  351.     classes['java/lang/Enum'] = JavaNode('java/lang/Enum')
  352.     classes['java/lang/Thread'] = JavaNode('java/lang/Thread')
  353.    
  354.     for key, value in data.items():
  355.         classes[key.split('|')[0]] = JavaNode(key.split('|')[0])
  356.        
  357.     for key, value in data.items():
  358.         cls = key.split('|')[0]
  359.         package = cls.rsplit('/', 1)[0] + '/'
  360.        
  361.         super = 'java/lang/Object'
  362.         if 'Super' in value:
  363.             super = value['Super'].split('|')[0]
  364.             if not '/' in super:
  365.                 super = package + super
  366.            
  367.         if super in classes:
  368.             classes[super].children.append(classes[cls])
  369.             classes[cls].parent = classes[super]
  370.            
  371.     for key, value in classes.items():
  372.         if not value.parent == None:
  373.             del classes[key]
  374.     return classes
  375.    
  376. def writeHierarchy(file, classes, indent='', hasMore=True):
  377.     if type(classes) == dict:
  378.         keylist = classes.keys()
  379.         keylist.sort()
  380.        
  381.         file.write('{\r\n')
  382.         for x in range(0, len(keylist)):
  383.             writeHierarchy(file, classes[keylist[x]], '    ', x < len(keylist) - 1)
  384.         file.write('}')
  385.     else:
  386.         if len(classes.children) == 0:
  387.             file.write('%s"%s": {}%s\r\n' % (indent, classes.name.replace('net/minecraft/src/', ''), (',' if hasMore else '')))
  388.         else:
  389.             file.write('%s"%s": {\r\n' % (indent, classes.name.replace('net/minecraft/src/', '')))
  390.             classes.children.sort()
  391.             for x in range(0, len(classes.children)):
  392.                 writeHierarchy(file, classes.children[x], '    ' + indent, x < len(classes.children) - 1)
  393.             file.write('%s}%s\r\n' % (indent, ',' if hasMore else ''))
  394.  
  395. class JavaNode:
  396.     def __init__(self, name):
  397.         self.name = name
  398.         self.parent = None
  399.         self.children = []
  400.        
  401. if __name__ == '__main__':
  402.     main()
Advertisement
Add Comment
Please, Sign In to add comment