2012-07-12 18:07:58 -07:00
import os , os.path , sys
import urllib , zipfile
import shutil , glob , fnmatch
2012-08-02 00:51:51 -07:00
import subprocess , logging , re , shlex
2012-11-08 17:54:05 -08:00
import csv , ConfigParser
2012-08-01 03:25:45 -07:00
from hashlib import md5 # pylint: disable-msg=E0611
2012-11-08 17:54:05 -08:00
from pprint import pprint
from zipfile import ZipFile
2012-11-19 15:01:04 -08:00
from pprint import pprint
2013-03-09 12:39:36 -05:00
mcp_version = '7.42'
2012-07-12 18:07:58 -07:00
def download_deps ( mcp_path ):
ret = True
2013-04-23 07:40:49 -04:00
for lib in [ 'argo-3.2-src.jar' , 'guava-14.0-rc3.jar' , 'asm-4.1.tar.gz' , 'asm-debug-all-4.1.jar' , 'bcprov-debug-jdk15on-148.jar' , 'bcprov-jdk15on-148-src.zip' , 'guava-14.0-rc3-sources.jar' , 'scala-library.jar' ]:
2012-08-01 03:25:45 -07:00
libF = os . path . join ( mcp_path , 'lib' )
if not os . path . isdir ( libF ):
os . makedirs ( libF )
target = os . path . normpath ( os . path . join ( libF , lib ))
2012-07-12 18:07:58 -07:00
if not os . path . isfile ( target ):
try :
2012-08-31 11:04:34 -04:00
urllib . urlretrieve ( 'http://files.minecraftforge.net/fmllibs/' + lib , target )
2012-12-09 02:10:28 -08:00
print 'Downloaded %s ' % lib
2012-07-12 18:07:58 -07:00
except :
2013-02-26 00:16:57 -05:00
print 'Download %s failed, download manually from http://files.minecraftforge.net/fmllibs/ %s or http://files.minecraftforge.net/fmllibs/fml_libs_dev15.zip and place in mcp/lib' % ( lib , lib )
2012-07-12 18:07:58 -07:00
ret = False
return ret
2012-11-08 17:54:05 -08:00
def config_get_section ( config , section ):
dict = {}
options = config . options ( section )
for option in options :
try :
dict [ option ] = config . get ( section , option )
except :
dict [ option ] = None
return dict
def download_file ( url , target , md5 = None ):
name = os . path . basename ( target )
if os . path . isfile ( target ) and not md5 == None :
if not get_md5 ( target ) == md5 :
print 'Modified %s detected, removing' % name
os . remove ( target )
if not os . path . isfile ( target ):
try :
urllib . urlretrieve ( url , target )
if not md5 == None :
if not get_md5 ( target ) == md5 :
print 'Download of %s failed md5 check, deleting' % name
os . remove ( target )
return False
2012-12-09 02:10:28 -08:00
print 'Downloaded %s ' % name
2012-11-08 17:54:05 -08:00
except Exception as e :
print e
print 'Download of %s failed, download it manually from \' %s \' to \' %s \' ' % ( target , url , target )
return False
else :
print 'File Exists: %s ' % os . path . basename ( target )
return True
def download_native ( url , folder , name ):
if not os . path . exists ( folder ):
os . makedirs ( folder )
target = os . path . join ( folder , name )
if not download_file ( url + name , target ):
return False
2012-11-19 13:33:22 -08:00
zip = ZipFile ( target )
for name in zip . namelist ():
if not name . startswith ( 'META-INF' ) and not name . endswith ( '/' ):
out_file = os . path . join ( folder , name )
if not os . path . isfile ( out_file ):
print ' Extracting %s ' % name
out = open ( out_file , 'wb' )
out . write ( zip . read ( name ))
out . flush ()
out . close ()
zip . close ()
2012-11-08 17:54:05 -08:00
return True
def download_minecraft ( mcp_dir , fml_dir , version = None ):
versions_file = os . path . join ( fml_dir , 'mc_versions.cfg' )
if not os . path . isfile ( versions_file ):
print 'Could not find mc_versions.cfg in FML directory.'
sys . exit ( 1 )
2012-08-01 03:25:45 -07:00
2012-11-08 17:54:05 -08:00
config = ConfigParser . ConfigParser ()
config . read ( versions_file )
default = config_get_section ( config , 'default' )
if version is None :
2012-12-02 19:59:22 -08:00
version = default [ 'current_ver' ]
2012-08-13 21:02:55 -07:00
2012-11-08 17:54:05 -08:00
bin_folder = os . path . join ( mcp_dir , 'jars' , 'bin' )
if not os . path . exists ( bin_folder ):
os . makedirs ( bin_folder )
2012-08-13 21:02:55 -07:00
2012-11-08 17:54:05 -08:00
failed = False
for lib in default [ 'libraries' ] . split ( ' ' ):
failed = not download_file ( default [ 'base_url' ] + lib , os . path . join ( bin_folder , lib )) or failed
for native in default [ 'natives' ] . split ( ' ' ):
failed = not download_native ( default [ 'base_url' ], os . path . join ( bin_folder , 'natives' ), native ) or failed
if not config . has_section ( version ):
print 'Error: Invalid minecraft version, could not find \' %s \' in mc_versions.cfg' % version
sys . exit ( 1 )
mc_info = config_get_section ( config , version )
client_jar = os . path . join ( bin_folder , 'minecraft.jar' )
server_jar = os . path . join ( mcp_dir , 'jars' , 'minecraft_server.jar' )
# Remove any invalid files
file_backup ( os . path . join ( mcp_dir , 'jars' , 'bin' ), 'minecraft.jar' , mc_info [ 'client_md5' ])
file_backup ( os . path . join ( mcp_dir , 'jars' ), 'minecraft_server.jar' , mc_info [ 'server_md5' ])
2012-08-13 21:02:55 -07:00
2012-11-08 17:54:05 -08:00
failed = not download_file ( mc_info [ 'client_url' ], client_jar , mc_info [ 'client_md5' ]) or failed
failed = not download_file ( mc_info [ 'server_url' ], server_jar , mc_info [ 'server_md5' ]) or failed
# Backup clean jars, or delete if corrupted
file_backup ( os . path . join ( mcp_dir , 'jars' , 'bin' ), 'minecraft.jar' , mc_info [ 'client_md5' ])
file_backup ( os . path . join ( mcp_dir , 'jars' ), 'minecraft_server.jar' , mc_info [ 'server_md5' ])
if failed :
print 'Something failed verifying minecraft files, see log for details.'
sys . exit ( 1 )
def get_md5 ( file ):
if not os . path . isfile ( file ):
return ""
with open ( file , 'rb' ) as fh :
return md5 ( fh . read ()) . hexdigest ()
def pre_decompile ( mcp_dir , fml_dir ):
download_minecraft ( mcp_dir , fml_dir )
def file_backup ( base , file , md5 ):
bck_jar = os . path . join ( base , file + '.backup' )
2012-08-01 03:25:45 -07:00
src_jar = os . path . join ( base , file )
2012-11-08 17:54:05 -08:00
if not os . path . isfile ( src_jar ) and not os . path . isfile ( bck_jar ):
2012-08-01 03:25:45 -07:00
return
2012-07-12 18:07:58 -07:00
2012-11-08 17:54:05 -08:00
if os . path . isfile ( bck_jar ):
if get_md5 ( bck_jar ) == md5 :
if os . path . isfile ( src_jar ):
os . remove ( src_jar )
shutil . move ( bck_jar , src_jar )
else :
os . remove ( bck_jar )
if os . path . isfile ( src_jar ):
if not get_md5 ( src_jar ) == md5 :
print 'Modified %s detected, removing' % os . path . basename ( src_jar )
2012-07-12 18:07:58 -07:00
os . remove ( src_jar )
2012-11-08 17:54:05 -08:00
else :
shutil . copy ( src_jar , bck_jar )
2012-07-12 18:07:58 -07:00
def post_decompile ( mcp_dir , fml_dir ):
bin_dir = os . path . join ( mcp_dir , 'jars' , 'bin' )
back_jar = os . path . join ( bin_dir , 'minecraft.jar.backup' )
src_jar = os . path . join ( bin_dir , 'minecraft.jar' )
2012-08-01 03:25:45 -07:00
if not os . path . isfile ( src_jar ):
return
print 'Stripping META-INF from minecraft.jar'
2012-07-12 18:07:58 -07:00
zip_in = zipfile . ZipFile ( back_jar , mode = 'a' )
zip_out = zipfile . ZipFile ( src_jar , 'w' , zipfile . ZIP_DEFLATED )
for i in zip_in . filelist :
if not i . filename . startswith ( 'META-INF' ):
c = zip_in . read ( i . filename )
zip_out . writestr ( i . filename , c )
else :
print 'Skipping: %s ' % i . filename
zip_out . close ()
def reset_logger ():
log = logging . getLogger ()
while len ( log . handlers ) > 0 :
log . removeHandler ( log . handlers [ 0 ])
2012-11-19 15:01:04 -08:00
2012-07-12 18:07:58 -07:00
count = 0
def cleanup_source ( path ):
2013-01-11 13:16:23 -08:00
from rename_vars import rename_class
2012-07-12 18:07:58 -07:00
path = os . path . normpath ( path )
regex_cases_before = re . compile ( r '((case|default).+\r?\n)\r?\n' , re . MULTILINE ) #Fixes newline after case before case body
regex_cases_after = re . compile ( r '\r?\n(\r?\n[ \t]+(case|default))' , re . MULTILINE ) #Fixes newline after case body before new case
def updatefile ( src_file ):
global count
tmp_file = src_file + '.tmp'
count = 0
with open ( src_file , 'r' ) as fh :
buf = fh . read ()
def fix_cases ( match ):
global count
count += 1
return match . group ( 1 )
2013-01-11 13:16:23 -08:00
2012-07-12 18:07:58 -07:00
buf = regex_cases_before . sub ( fix_cases , buf )
buf = regex_cases_after . sub ( fix_cases , buf )
2013-01-11 13:16:23 -08:00
old = buf . replace ( ' \r ' , '' )
buf = rename_class ( old , MCP = True )
if count > 0 or buf != old :
2012-07-12 18:07:58 -07:00
with open ( tmp_file , 'w' ) as fh :
fh . write ( buf )
shutil . move ( tmp_file , src_file )
for path , _ , filelist in os . walk ( path , followlinks = True ):
sub_dir = os . path . relpath ( path , path )
for cur_file in fnmatch . filter ( filelist , '*.java' ):
src_file = os . path . normpath ( os . path . join ( path , cur_file ))
updatefile ( src_file )
2012-11-19 15:01:04 -08:00
2013-01-24 03:08:19 -08:00
compile_tools = True
def setup_fml ( fml_dir , mcp_dir , disable_at = False , disable_merge = False , enable_server = False , disable_client = False ):
global compile_tools
2012-07-12 18:07:58 -07:00
sys . path . append ( mcp_dir )
from runtime.decompile import decompile
from runtime.cleanup import cleanup
2012-08-01 03:25:45 -07:00
from runtime.commands import Commands , CLIENT , SERVER
2012-07-12 18:07:58 -07:00
src_dir = os . path . join ( mcp_dir , 'src' )
if os . path . isdir ( src_dir ):
os . chdir ( mcp_dir )
cleanup ( None , False )
reset_logger ()
os . chdir ( fml_dir )
if os . path . isdir ( src_dir ):
print 'Please make sure to backup your modified files, and say yes when it asks you to do cleanup.'
sys . exit ( 1 )
#download fernflower/argo/asm/guava
if not download_deps ( mcp_dir ):
sys . exit ( 1 )
2013-01-24 03:08:19 -08:00
compile_tools = True
2012-08-01 03:25:45 -07:00
def applyrg_shunt ( self , side , reobf = False , applyrg_real = Commands . applyrg ):
2013-01-24 03:08:19 -08:00
global compile_tools
2012-12-11 21:47:45 -08:00
if not self . has_wine and not self . has_astyle :
self . logger . error ( '!! Please install either wine or astyle for source cleanup !!' )
self . logger . error ( '!! This is REQUIRED by FML/Forge Cannot proceed !!' )
sys . exit ( 1 )
2012-08-01 03:25:45 -07:00
jars = { CLIENT : self . jarclient , SERVER : self . jarserver }
2012-11-19 13:33:22 -08:00
dir_bin = os . path . join ( fml_dir , 'bin' )
if not os . path . isdir ( dir_bin ):
os . makedirs ( dir_bin )
2012-08-01 03:25:45 -07:00
2012-11-19 13:33:22 -08:00
class_path = os . pathsep . join ([ '.' , os . path . join ( mcp_dir , 'lib' , '*' ), dir_bin ])
dir_common = os . path . join ( fml_dir , 'common' )
dir_trans = os . path . join ( dir_common , 'cpw' , 'mods' , 'fml' , 'common' , 'asm' , 'transformers' )
2013-04-05 10:26:33 -07:00
java = self . cmdjava . translate ( None , '"' )
javac = self . cmdjavac . translate ( None , '"' )
cmd_compile = '" %s " -Xlint:-options -deprecation -g -source 1.6 -target 1.6 -classpath " {classpath} " -sourcepath " {sourcepath} " -d " {outpath} " " {target} "' % javac
2012-11-19 13:33:22 -08:00
cmd_compile = cmd_compile . format ( classpath = class_path , sourcepath = dir_common , outpath = dir_bin , target = " {target} " )
2013-01-24 03:08:19 -08:00
if compile_tools :
2012-08-13 21:02:55 -07:00
self . logger . info ( '> Compiling AccessTransformer' )
2012-11-19 13:33:22 -08:00
if not runcmd ( self , cmd_compile . format ( target = os . path . join ( dir_trans , 'AccessTransformer.java' )), echo = False ):
2012-08-13 21:02:55 -07:00
sys . exit ( 1 )
self . logger . info ( '> Compiling MCPMerger' )
2012-11-19 13:33:22 -08:00
if not runcmd ( self , cmd_compile . format ( target = os . path . join ( dir_trans , 'MCPMerger.java' )), echo = False ):
2012-08-13 21:02:55 -07:00
sys . exit ( 1 )
2013-01-24 03:08:19 -08:00
compile_tools = False
2012-08-12 02:49:39 -07:00
2013-01-24 03:08:19 -08:00
if side == CLIENT :
if not disable_merge :
self . logger . info ( '> Running MCPMerger' )
2013-04-05 10:26:33 -07:00
forkcmd = ( '" %s " -classpath " {classpath} " cpw.mods.fml.common.asm.transformers.MCPMerger " {mergecfg} " " {client} " " {server} "' % java ) . format (
2013-01-24 03:08:19 -08:00
classpath = class_path , mergecfg = os . path . join ( fml_dir , 'mcp_merge.cfg' ), client = jars [ CLIENT ], server = jars [ SERVER ])
if not runcmd ( self , forkcmd ):
sys . exit ( 1 )
else :
self . logger . info ( '> MCPMerge disabled' )
if not disable_at :
self . logger . info ( '> Running AccessTransformer' )
2013-04-05 10:26:33 -07:00
forkcmd = ( '" %s " -classpath " {classpath} " cpw.mods.fml.common.asm.transformers.AccessTransformer " {jar} " " {fmlconfig} "' % java ) . format (
2013-01-24 03:08:19 -08:00
classpath = class_path , jar = jars [ side ], fmlconfig = os . path . join ( fml_dir , 'common' , 'fml_at.cfg' ))
2012-08-13 21:02:55 -07:00
2013-01-24 03:08:19 -08:00
forge_cfg = os . path . join ( fml_dir , '..' , 'common' , 'forge_at.cfg' )
if os . path . isfile ( forge_cfg ):
self . logger . info ( ' Forge config detected' )
forkcmd += ' " %s "' % forge_cfg
for dirname , dirnames , filenames in os . walk ( os . path . join ( fml_dir , '..' , 'accesstransformers' )):
for filename in filenames :
accesstransformer = os . path . join ( dirname , filename )
if os . path . isfile ( accesstransformer ):
self . logger . info ( ' Access Transformer " %s " detected' % filename )
forkcmd += ' " %s "' % accesstransformer
2012-08-13 21:02:55 -07:00
if not runcmd ( self , forkcmd ):
sys . exit ( 1 )
2013-01-24 03:08:19 -08:00
else :
self . logger . info ( '> Access Transformer disabled' )
2012-08-01 03:25:45 -07:00
2012-08-13 21:02:55 -07:00
self . logger . info ( '> Really Applying Retroguard' )
2012-08-01 03:25:45 -07:00
applyrg_real ( self , side , reobf )
2012-08-12 02:49:39 -07:00
#Check the original jars not the transformed jars
def checkjars_shunt ( self , side , checkjars_real = Commands . checkjars ):
self . jarclient = self . jarclient + '.backup'
self . jarserver = self . jarserver + '.backup'
2012-08-13 21:02:55 -07:00
#print 'Jar Check %s %s %s' % (side, self.jarclient, self.jarserver)
2012-08-12 02:49:39 -07:00
ret = checkjars_real ( self , side )
self . jarclient = self . jarclient [: - 7 ]
self . jarserver = self . jarserver [: - 7 ]
2012-08-12 04:27:36 -07:00
#print 'Jar Check out %s %s %s' % (side, self.jarclient, self.jarserver)
2012-08-12 02:49:39 -07:00
return ret
2012-07-12 18:07:58 -07:00
try :
pre_decompile ( mcp_dir , fml_dir )
os . chdir ( mcp_dir )
2012-08-01 03:25:45 -07:00
Commands . applyrg = applyrg_shunt
2012-08-12 02:49:39 -07:00
Commands . checkjars = checkjars_shunt
2012-07-12 18:07:58 -07:00
#decompile -d -n -r
2013-01-24 03:08:19 -08:00
# Conf JAD CSV -r -d -a -n -p -o -l -g -c -s
decompile ( None , False , False , True , True , False , True , False , False , False , False , not disable_client , enable_server )
2012-07-12 18:07:58 -07:00
reset_logger ()
os . chdir ( fml_dir )
post_decompile ( mcp_dir , fml_dir )
except SystemExit , e :
print 'Decompile Exception: %d ' % e . code
raise e
if not os . path . isdir ( src_dir ):
print 'Something went wrong, src folder not found at: %s ' % src_dir
sys . exit ( 1 )
#cleanup_source
cleanup_source ( src_dir )
2012-08-03 03:35:38 -07:00
os . chdir ( mcp_dir )
commands = Commands ( verify = True )
2013-01-24 03:08:19 -08:00
if not disable_client :
updatemd5_side ( mcp_dir , commands , CLIENT )
reset_logger ()
if enable_server :
updatemd5_side ( mcp_dir , commands , CLIENT )
reset_logger ()
2012-11-19 15:01:04 -08:00
2012-08-03 03:35:38 -07:00
os . chdir ( fml_dir )
2012-08-13 21:53:52 -07:00
def updatemd5_side ( mcp_dir , commands , side ):
sys . path . append ( mcp_dir )
from runtime.mcp import recompile_side , updatemd5_side
from runtime.commands import SIDE_NAME
recomp = recompile_side ( commands , side )
if recomp :
commands . logger . info ( '> Generating %s md5s' , SIDE_NAME [ side ])
commands . gathermd5s ( side , skip_fml = True )
2012-08-13 21:02:55 -07:00
def cmdsplit ( args ):
if os . sep == ' \\ ' :
args = args . replace ( ' \\ ' , ' \\\\ ' )
return shlex . split ( args )
def runcmd ( commands , command , echo = True ):
forklist = cmdsplit ( command )
process = subprocess . Popen ( forklist , stdout = subprocess . PIPE , stderr = subprocess . STDOUT , bufsize =- 1 )
output , _ = process . communicate ()
if echo :
for line in output . splitlines ():
2012-08-14 19:24:49 -07:00
commands . logger . info ( line )
2012-08-13 21:02:55 -07:00
if process . returncode :
if not echo :
for line in output . splitlines ():
2012-08-14 19:24:49 -07:00
commands . logger . info ( line )
2012-08-13 21:02:55 -07:00
return False
return True
2012-11-19 15:01:04 -08:00
def get_joined_srg ( mcp_dir ):
joined = os . path . join ( mcp_dir , 'conf' , 'joined.srg' )
values = { 'PK:' : {}, 'CL:' : {}, 'FD:' : {}, 'MD:' : {}}
if not os . path . isfile ( joined ):
print 'Could not read joined.srg, file not found'
sys . exit ( 1 )
else :
with open ( joined , 'r' ) as fh :
for line in fh :
pts = line . rstrip ( ' \r\n ' ) . split ( ' ' )
if pts [ 0 ] == 'MD:' :
values [ pts [ 0 ]][ pts [ 1 ] + ' ' + pts [ 2 ]] = pts [ 3 ] + ' ' + pts [ 4 ]
else :
values [ pts [ 0 ]][ pts [ 1 ]] = pts [ 2 ]
return values
2012-08-01 06:54:57 -07:00
def apply_fml_patches ( fml_dir , mcp_dir , src_dir , copy_files = True ):
2012-12-09 02:10:28 -08:00
#Delete /minecraft/cpw to get rid of the Side/SideOnly classes used in decompilation
2012-11-19 15:01:04 -08:00
cpw_mc_dir = os . path . join ( src_dir , 'minecraft' , 'cpw' )
2012-11-19 15:03:13 -08:00
if os . path . isdir ( cpw_mc_dir ):
shutil . rmtree ( cpw_mc_dir )
2012-08-10 18:25:11 -07:00
2012-07-12 18:07:58 -07:00
#patch files
print 'Applying Forge ModLoader patches'
2012-08-01 06:54:57 -07:00
sys . stdout . flush ()
2012-08-15 03:10:45 -07:00
if os . path . isdir ( os . path . join ( fml_dir , 'patches' , 'minecraft' )):
apply_patches ( mcp_dir , os . path . join ( fml_dir , 'patches' , 'minecraft' ), src_dir )
if copy_files and os . path . isdir ( os . path . join ( fml_dir , 'client' )):
copytree ( os . path . join ( fml_dir , 'client' ), os . path . join ( src_dir , 'minecraft' ))
2012-12-09 02:10:28 -08:00
if copy_files and os . path . isdir ( os . path . join ( fml_dir , 'common' )):
copytree ( os . path . join ( fml_dir , 'common' ), os . path . join ( src_dir , 'minecraft' ))
2012-08-15 03:10:45 -07:00
#delete argo
if os . path . isdir ( os . path . join ( src_dir , 'minecraft' , 'argo' )):
shutil . rmtree ( os . path . join ( src_dir , 'minecraft' , 'argo' ))
2012-10-20 00:06:55 -07:00
2013-02-08 14:50:17 -08:00
def finish_setup_fml ( fml_dir , mcp_dir , enable_server = False , disable_client = False , disable_rename = False ):
2012-07-12 18:07:58 -07:00
sys . path . append ( mcp_dir )
from runtime.updatenames import updatenames
from runtime.updatemd5 import updatemd5
from runtime.updatemcp import updatemcp
os . chdir ( mcp_dir )
2013-02-08 14:50:17 -08:00
if not disable_rename :
updatenames ( None , True , not disable_client , enable_server )
reset_logger ()
2013-01-24 03:08:19 -08:00
updatemd5 ( None , True , not disable_client , enable_server )
2012-07-12 18:07:58 -07:00
reset_logger ()
os . chdir ( fml_dir )
2012-08-01 06:54:57 -07:00
def apply_patches ( mcp_dir , patch_dir , target_dir , find = None , rep = None ):
2012-07-12 18:07:58 -07:00
sys . path . append ( mcp_dir )
from runtime.commands import cmdsplit
temp = os . path . abspath ( 'temp.patch' )
cmd = cmdsplit ( 'patch -p2 -i " %s " ' % temp )
if os . name == 'nt' :
applydiff = os . path . abspath ( os . path . join ( mcp_dir , 'runtime' , 'bin' , 'applydiff.exe' ))
cmd = cmdsplit ( '" %s " -uf -p2 -i " %s "' % ( applydiff , temp ))
for path , _ , filelist in os . walk ( patch_dir , followlinks = True ):
for cur_file in fnmatch . filter ( filelist , '*.patch' ):
patch_file = os . path . normpath ( os . path . join ( patch_dir , path [ len ( patch_dir ) + 1 :], cur_file ))
2012-08-01 06:54:57 -07:00
target_file = os . path . join ( target_dir , fix_patch ( patch_file , temp , find , rep ))
2012-08-12 02:49:39 -07:00
process = subprocess . Popen ( cmd , cwd = target_dir , bufsize =- 1 )
process . communicate ()
2012-07-12 18:07:58 -07:00
if os . path . isfile ( temp ):
os . remove ( temp )
2012-08-01 06:54:57 -07:00
def fix_patch ( in_file , out_file , find = None , rep = None ):
in_file = os . path . normpath ( in_file )
if out_file is None :
tmp_filename = in_file + '.tmp'
else :
out_file = os . path . normpath ( out_file )
tmp_file = out_file
dir_name = os . path . dirname ( out_file )
if dir_name :
if not os . path . exists ( dir_name ):
os . makedirs ( dir_name )
file = 'not found'
with open ( in_file , 'rb' ) as inpatch :
with open ( tmp_file , 'wb' ) as outpatch :
for line in inpatch :
line = line . rstrip ( ' \r\n ' )
if line [: 3 ] in [ '+++' , '---' , 'Onl' , 'dif' ]:
2012-08-02 02:08:30 -02:30
if not find == None and not rep == None :
2012-08-01 06:54:57 -07:00
line = line . replace ( ' \\ ' , '/' ) . replace ( find , rep ) . replace ( '/' , os . sep )
else :
line = line . replace ( ' \\ ' , '/' ) . replace ( '/' , os . sep )
outpatch . write ( line + os . linesep )
else :
outpatch . write ( line + os . linesep )
if line [: 3 ] == '---' :
file = line [ line . find ( os . sep , line . find ( os . sep ) + 1 ) + 1 :]
if out_file is None :
shutil . move ( tmp_file , in_file )
return file
2012-07-12 18:07:58 -07:00
#Taken from: http://stackoverflow.com/questions/7545299/distutil-shutil-copytree
def _mkdir ( newdir ):
"""works the way a good mkdir should :)
- already exists, silently complete
- regular file in the way, raise an exception
- parent directory(ies) does not exist, make them as well
"""
if os . path . isdir ( newdir ):
pass
elif os . path . isfile ( newdir ):
raise OSError ( "a file with the same name as the desired " \
"dir, ' %s ', already exists." % newdir )
else :
head , tail = os . path . split ( newdir )
if head and not os . path . isdir ( head ):
_mkdir ( head )
#print "_mkdir %s" % repr(newdir)
if tail :
os . mkdir ( newdir )
#Taken from: http://stackoverflow.com/questions/7545299/distutil-shutil-copytree
def copytree ( src , dst , verbose = 0 , symlinks = False ):
"""Recursively copy a directory tree using copy2().
The destination directory must not already exist.
If exception(s) occur, an Error is raised with a list of reasons.
If the optional symlinks flag is true, symbolic links in the
source tree result in symbolic links in the destination tree; if
it is false, the contents of the files pointed to by symbolic
links are copied.
XXX Consider this example code rather than the ultimate tool.
"""
if verbose == - 1 :
2012-08-04 20:36:27 -07:00
verbose = len ( os . path . abspath ( dst )) + 1
2012-07-12 18:07:58 -07:00
names = os . listdir ( src )
# os.makedirs(dst)
_mkdir ( dst ) # XXX
errors = []
for name in names :
srcname = os . path . join ( src , name )
dstname = os . path . join ( dst , name )
try :
if symlinks and os . path . islink ( srcname ):
linkto = os . readlink ( srcname )
os . symlink ( linkto , dstname )
elif os . path . isdir ( srcname ):
copytree ( srcname , dstname , verbose , symlinks )
else :
shutil . copy2 ( srcname , dstname )
if verbose > 0 :
2012-08-04 20:36:27 -07:00
print os . path . abspath ( dstname )[ verbose :]
2012-07-12 18:07:58 -07:00
# XXX What about devices, sockets etc.?
except ( IOError , os . error ), why :
errors . append (( srcname , dstname , str ( why )))
# catch the Error from the recursive copytree so that we can
# continue with other files
except Exception , err :
errors . extend ( err . args [ 0 ])
try :
shutil . copystat ( src , dst )
except WindowsError :
# can't copy file access times on Windows
pass
2012-08-08 05:48:06 -07:00
def cleanDirs ( path ):
if not os . path . isdir ( path ):
return
files = os . listdir ( path )
if len ( files ):
for f in files :
fullpath = os . path . join ( path , f )
if os . path . isdir ( fullpath ):
cleanDirs ( fullpath )
files = os . listdir ( path )
if len ( files ) == 0 :
os . rmdir ( path )
2012-07-12 18:07:58 -07:00
def merge_tree ( root_src_dir , root_dst_dir ):
for src_dir , dirs , files in os . walk ( root_src_dir ):
dst_dir = src_dir . replace ( root_src_dir , root_dst_dir )
if not os . path . exists ( dst_dir ):
os . mkdir ( dst_dir )
for file_ in files :
src_file = os . path . join ( src_dir , file_ )
dst_file = os . path . join ( dst_dir , file_ )
if os . path . exists ( dst_file ):
os . remove ( dst_file )
2012-08-01 18:59:45 -02:30
shutil . copy ( src_file , dst_dir )
2012-12-02 19:59:22 -08:00
def download_mcp ( mcp_dir , fml_dir , version = None ):
if os . path . isfile ( os . path . join ( mcp_dir , 'runtime' , 'commands.py' )):
print 'MCP Detected already, not downloading'
return True
if os . path . isdir ( mcp_dir ):
print 'Old MCP Directory exists, but MCP was not detected, please delete MCP directory at \' %s \' ' % mcp_dir
sys . exit ( 1 )
versions_file = os . path . join ( fml_dir , 'mc_versions.cfg' )
if not os . path . isfile ( versions_file ):
print 'Could not find mc_versions.cfg in FML directory.'
sys . exit ( 1 )
config = ConfigParser . ConfigParser ()
config . read ( versions_file )
default = config_get_section ( config , 'default' )
if version is None :
version = default [ 'current_ver' ]
if not config . has_section ( version ):
print 'Error: Invalid minecraft version, could not find \' %s \' in mc_versions.cfg' % version
sys . exit ( 1 )
mc_info = config_get_section ( config , version )
mcp_zip = os . path . join ( fml_dir , 'mcp %s .zip' % mc_info [ 'mcp_ver' ])
if not download_file ( mc_info [ 'mcp_url' ], mcp_zip , mc_info [ 'mcp_md5' ]):
sys . exit ( 1 )
if not os . path . isdir ( mcp_dir ):
_mkdir ( mcp_dir )
print 'Extracting MCP to \' %s \' ' % mcp_dir
zf = ZipFile ( mcp_zip )
2013-03-13 13:11:43 -07:00
#OS X's python 2.6.1 extractall has a bug in zipfile that makes it unzip directories as regular files.
for path in zf . namelist ():
if not path . endswith ( '/' ):
zf . extract ( path , mcp_dir )
2012-12-02 19:59:22 -08:00
zf . close ()
2012-12-09 02:10:28 -08:00
eclipse_dir = os . path . join ( mcp_dir , 'eclipse' )
if os . path . isdir ( eclipse_dir ):
shutil . rmtree ( eclipse_dir )
2012-12-11 21:47:45 -08:00
if os . name != 'nt' :
for path , _ , filelist in os . walk ( mcp_dir ):
for cur_file in fnmatch . filter ( filelist , '*.sh' ):
file_name = os . path . join ( path , cur_file )
process = subprocess . Popen ( cmdsplit ( 'chmod +x " %s "' % file_name ), stdout = subprocess . PIPE , stderr = subprocess . STDOUT , bufsize =- 1 )
output , _ = process . communicate ()
process = subprocess . Popen ( cmdsplit ( 'chmod +x " %s /runtime/bin/astyle-osx"' % mcp_dir ), stdout = subprocess . PIPE , stderr = subprocess . STDOUT , bufsize =- 1 )
output , _ = process . communicate ()
2012-12-02 19:59:22 -08:00
return True
2012-08-08 05:48:06 -07:00
def setup_mcp ( fml_dir , mcp_dir , dont_gen_conf = True ):
2012-11-19 15:01:04 -08:00
global mcp_version
2012-12-02 19:59:22 -08:00
if not download_mcp ( mcp_dir , fml_dir ):
sys . exit ( 1 )
2012-08-03 23:24:58 -07:00
backup = os . path . join ( mcp_dir , 'runtime' , 'commands.py.bck' )
2012-08-02 00:51:51 -07:00
runtime = os . path . join ( mcp_dir , 'runtime' , 'commands.py' )
patch = os . path . join ( fml_dir , 'commands.patch' )
print 'Setting up MCP'
if os . path . isfile ( backup ):
2012-12-09 02:10:28 -08:00
print 'Restoring commands.py backup'
2012-08-02 00:51:51 -07:00
if os . path . exists ( runtime ):
os . remove ( runtime )
shutil . copy ( backup , runtime )
else :
2012-12-09 02:10:28 -08:00
print 'Backing up commands.py'
2012-08-02 00:51:51 -07:00
shutil . copy ( runtime , backup )
if not os . path . isfile ( patch ):
2012-08-12 02:49:39 -07:00
raise Exception ( 'Commands.py patch not found %s ' % patch )
2012-08-02 00:51:51 -07:00
return
temp = os . path . abspath ( 'temp.patch' )
cmd = 'patch -i " %s " ' % temp
2012-12-09 02:10:28 -08:00
if os . name == 'nt' :
2012-08-02 00:51:51 -07:00
applydiff = os . path . abspath ( os . path . join ( mcp_dir , 'runtime' , 'bin' , 'applydiff.exe' ))
cmd = '" %s " -uf -i " %s "' % ( applydiff , temp )
if os . sep == ' \\ ' :
cmd = cmd . replace ( ' \\ ' , ' \\\\ ' )
cmd = shlex . split ( cmd )
fix_patch ( patch , temp )
process = subprocess . Popen ( cmd , cwd = os . path . join ( mcp_dir , 'runtime' ), bufsize =- 1 )
process . communicate ()
if os . path . isfile ( temp ):
2012-08-02 20:09:18 -02:30
os . remove ( temp )
2012-08-03 16:56:00 -07:00
2012-08-13 21:02:55 -07:00
try :
sys . path . append ( mcp_dir )
from runtime.commands import commands_sanity_check
commands_sanity_check ()
except ImportError as ex :
print 'Could not verify commands.py patch integrity, this typically means that you are not in a clean MCP environment.'
2012-11-19 15:01:04 -08:00
print 'Download a clean version of MCP %s and try again' % mcp_version
2012-08-13 21:02:55 -07:00
print ex
sys . exit ( 1 )
2012-08-12 02:49:39 -07:00
2012-08-08 05:48:06 -07:00
mcp_conf = os . path . join ( mcp_dir , 'conf' )
mcp_conf_bak = os . path . join ( mcp_dir , 'conf.bak' )
fml_conf = os . path . join ( fml_dir , 'conf' )
if not dont_gen_conf :
if os . path . isdir ( mcp_conf_bak ):
print 'Reverting old conf backup folder'
shutil . rmtree ( mcp_conf )
os . rename ( mcp_conf_bak , mcp_conf )
get_conf_copy ( mcp_dir , fml_dir )
print 'Backing up MCP Conf'
os . rename ( mcp_conf , mcp_conf_bak )
else :
shutil . rmtree ( mcp_conf )
print 'Copying FML conf'
shutil . copytree ( fml_conf , mcp_conf )
2012-11-20 01:55:41 -08:00
gen_renamed_conf ( mcp_dir , fml_dir )
2012-12-09 02:10:28 -08:00
#update workspace
2012-12-09 02:20:21 -08:00
if not os . path . isfile ( os . path . join ( fml_dir , 'fmlbuild.properties-sample' )):
2012-12-09 02:10:28 -08:00
mcp_eclipse = os . path . join ( mcp_dir , 'eclipse' )
2012-12-12 14:31:07 -08:00
2012-12-13 00:22:30 -08:00
if os . path . isdir ( os . path . join ( mcp_eclipse , 'Client' )) and os . path . isdir ( os . path . join ( mcp_eclipse , 'Server' )):
2012-12-12 14:31:07 -08:00
shutil . rmtree ( mcp_eclipse )
2012-12-09 02:20:21 -08:00
if not os . path . isdir ( mcp_eclipse ) and os . path . isdir ( os . path . join ( fml_dir , 'eclipse' )):
2012-12-09 02:10:28 -08:00
print 'Fixing MCP Workspace'
2012-12-09 02:20:21 -08:00
copytree ( os . path . join ( fml_dir , 'eclipse' ), mcp_eclipse )
2012-08-10 18:25:11 -07:00
def normaliselines ( in_filename ):
in_filename = os . path . normpath ( in_filename )
tmp_filename = in_filename + '.tmp'
with open ( in_filename , 'rb' ) as in_file :
with open ( tmp_filename , 'wb' ) as out_file :
out_file . write ( in_file . read () . replace ( ' \r\n ' , ' \n ' ))
shutil . move ( tmp_filename , in_filename )
2012-08-08 05:48:06 -07:00
def get_conf_copy ( mcp_dir , fml_dir ):
#Lets grab the files we dont work on
2012-10-20 00:06:55 -07:00
for file in [ 'astyle.cfg' , 'version.cfg' , 'newids.csv' ]:
2012-08-08 05:48:06 -07:00
dst_file = os . path . normpath ( os . path . join ( fml_dir , 'conf' , file ))
src_file = os . path . normpath ( os . path . join ( mcp_dir , 'conf' , file ))
if not os . path . isdir ( os . path . dirname ( dst_file )):
os . makedirs ( os . path . dirname ( dst_file ))
if os . path . exists ( dst_file ):
os . remove ( dst_file )
shutil . copy ( src_file , dst_file )
2012-08-10 18:25:11 -07:00
normaliselines ( dst_file )
2012-08-08 05:48:06 -07:00
print 'Grabbing: ' + src_file
common_srg = gen_merged_srg ( mcp_dir , fml_dir )
common_exc = gen_merged_exc ( mcp_dir , fml_dir )
common_map = gen_shared_searge_names ( common_srg , common_exc )
2012-11-19 17:45:53 -08:00
2012-08-08 05:48:06 -07:00
gen_merged_csv ( common_map , os . path . join ( mcp_dir , 'conf' , 'fields.csv' ), os . path . join ( fml_dir , 'conf' , 'fields.csv' ))
gen_merged_csv ( common_map , os . path . join ( mcp_dir , 'conf' , 'methods.csv' ), os . path . join ( fml_dir , 'conf' , 'methods.csv' ))
gen_merged_csv ( common_map , os . path . join ( mcp_dir , 'conf' , 'params.csv' ), os . path . join ( fml_dir , 'conf' , 'params.csv' ), main_key = 'param' )
2012-10-20 00:06:55 -07:00
2012-08-08 05:48:06 -07:00
def gen_merged_srg ( mcp_dir , fml_dir ):
print 'Generating merged Retroguard data'
srg_client = os . path . join ( mcp_dir , 'conf' , 'client.srg' )
srg_server = os . path . join ( mcp_dir , 'conf' , 'server.srg' )
if not os . path . isfile ( srg_client ) or not os . path . isfile ( srg_server ):
print 'Could not find client and server srg files in " %s "' % mcp_dir
return False
client = { 'PK:' : {}, 'CL:' : {}, 'FD:' : {}, 'MD:' : {}}
with open ( srg_client , 'r' ) as fh :
for line in fh :
pts = line . rstrip ( ' \r\n ' ) . split ( ' ' )
if pts [ 0 ] == 'MD:' :
client [ pts [ 0 ]][ pts [ 1 ] + ' ' + pts [ 2 ]] = pts [ 3 ] + ' ' + pts [ 4 ]
else :
client [ pts [ 0 ]][ pts [ 1 ]] = pts [ 2 ]
server = { 'PK:' : {}, 'CL:' : {}, 'FD:' : {}, 'MD:' : {}}
with open ( srg_server , 'r' ) as fh :
for line in fh :
pts = line . rstrip ( ' \r\n ' ) . split ( ' ' )
if pts [ 0 ] == 'MD:' :
server [ pts [ 0 ]][ pts [ 1 ] + ' ' + pts [ 2 ]] = pts [ 3 ] + ' ' + pts [ 4 ]
else :
server [ pts [ 0 ]][ pts [ 1 ]] = pts [ 2 ]
common = { 'PK:' : {}, 'CL:' : {}, 'FD:' : {}, 'MD:' : {}}
for type in common :
for key , value in client [ type ] . items ():
if key in server [ type ]:
if value == server [ type ][ key ]:
client [ type ] . pop ( key )
server [ type ] . pop ( key )
common [ type ][ key ] = value
for type in common :
for key , value in client [ type ] . items ():
common [ type ][ key ] = value #+ ' #C'
for type in common :
for key , value in server [ type ] . items ():
common [ type ][ key ] = value #+ ' #S'
#Print joined retroguard files
2012-11-01 23:53:13 -04:00
if fml_dir is None :
2012-11-17 19:08:47 -08:00
return common
2012-11-01 23:53:13 -04:00
2012-08-10 18:25:11 -07:00
with open ( os . path . join ( fml_dir , 'conf' , 'joined.srg' ), 'wb' ) as f :
2012-08-08 05:48:06 -07:00
for type in [ 'PK:' , 'CL:' , 'FD:' , 'MD:' ]:
for key in sorted ( common [ type ]):
f . write ( ' %s %s %s \n ' % ( type , key , common [ type ][ key ]))
return common
def gen_merged_exc ( mcp_dir , fml_dir ):
2012-10-20 12:27:30 -04:00
print 'Reading merged MCInjector config'
exc_joined = os . path . join ( mcp_dir , 'conf' , 'joined.exc' )
2012-08-08 05:48:06 -07:00
2012-10-20 12:27:30 -04:00
joined = {}
with open ( exc_joined , 'r' ) as fh :
2012-08-08 05:48:06 -07:00
for line in fh :
if not line . startswith ( '#' ):
pts = line . rstrip ( ' \r\n ' ) . split ( '=' )
2012-10-20 12:27:30 -04:00
joined [ pts [ 0 ]] = pts [ 1 ]
2012-08-08 05:48:06 -07:00
2012-10-20 12:27:30 -04:00
return joined
2012-08-08 05:48:06 -07:00
def gen_shared_searge_names ( common_srg , common_exc ):
field = re . compile ( r 'field_[0-9]+_[a-zA-Z_]+$' )
method = re . compile ( r 'func_[0-9]+_[a-zA-Z_]+' )
param = re . compile ( r 'p_[\w]+_\d+_' )
print 'Gathering list of common searge names'
searge = []
for key , value in common_srg [ 'FD:' ] . items ():
m = field . search ( value )
if not m is None :
if not m . group ( 0 ) in searge :
searge . append ( m . group ( 0 ))
for key , value in common_srg [ 'MD:' ] . items ():
m = method . search ( value )
if not m is None and not '#' in value :
if not m . group ( 0 ) in searge :
searge . append ( m . group ( 0 ))
for key , value in common_exc . items ():
m = param . findall ( value )
if not m is None :
for p in m :
if not p in searge :
searge . append ( p )
return searge
def gen_merged_csv ( common_map , in_file , out_file , main_key = 'searge' ):
reader = csv . DictReader ( open ( in_file , 'r' ))
print 'Generating merged csv for %s ' % os . path . basename ( in_file )
2012-08-10 18:25:11 -07:00
common = []
2012-08-08 05:48:06 -07:00
added = []
for row in reader :
2012-08-10 18:25:11 -07:00
if not row [ main_key ] in added :
row [ 'side' ] = '2'
common . append ( row )
added . append ( row [ main_key ])
writer = csv . DictWriter ( open ( out_file , 'wb' ), fieldnames = reader . fieldnames , lineterminator = ' \n ' )
2012-08-08 05:48:06 -07:00
writer . writeheader ()
2012-08-10 18:25:11 -07:00
for row in sorted ( common , key = lambda row : row [ main_key ]):
writer . writerow ( row )
2012-11-20 01:55:41 -08:00
def repackage_class ( pkgs , cls ):
if cls . startswith ( 'net/minecraft/src/' ):
tmp = cls [ 18 :]
if tmp in pkgs . keys ():
return ' %s / %s ' % ( pkgs [ tmp ], tmp )
return cls
typere = re . compile ( '([\[ZBCSIJFDV]|L([\w\/]+);)' )
def repackage_signature ( pkgs , sig ):
global typere
sig1 = sig
params = sig . rsplit ( ')' , 1 )[ 0 ][ 1 :]
ret = sig . rsplit ( ')' , 1 )[ 1 ]
sig = '('
for arg in typere . findall ( params ):
if len ( arg [ 1 ]) > 0 :
sig += 'L %s ;' % repackage_class ( pkgs , arg [ 1 ])
else :
sig += arg [ 0 ]
sig += ')'
for tmp in typere . findall ( ret ):
if len ( tmp [ 1 ]) > 0 :
sig += 'L %s ;' % repackage_class ( pkgs , tmp [ 1 ])
else :
sig += tmp [ 0 ]
return sig
def gen_renamed_conf ( mcp_dir , fml_dir ):
pkg_file = os . path . join ( fml_dir , 'conf' , 'packages.csv' )
srg_in = os . path . join ( mcp_dir , 'conf' , 'joined.srg' )
srg_out = os . path . join ( mcp_dir , 'conf' , 'packaged.srg' )
exc_in = os . path . join ( mcp_dir , 'conf' , 'joined.exc' )
exc_out = os . path . join ( mcp_dir , 'conf' , 'packaged.exc' )
pkgs = {}
if os . path . isfile ( pkg_file ):
with open ( pkg_file ) as fh :
reader = csv . DictReader ( fh )
for line in reader :
pkgs [ line [ 'class' ]] = line [ 'package' ]
print 'Creating re-packaged srg'
with open ( srg_in , 'r' ) as inf :
with open ( srg_out , 'wb' ) as outf :
for line in inf :
pts = line . rstrip ( ' \r\n ' ) . split ( ' ' )
if pts [ 0 ] == 'PK:' :
outf . write ( ' ' . join ( pts ) + ' \n ' )
elif pts [ 0 ] == 'CL:' :
pts [ 2 ] = repackage_class ( pkgs , pts [ 2 ])
outf . write ( 'CL: %s %s \n ' % ( pts [ 1 ], pts [ 2 ]))
elif pts [ 0 ] == 'FD:' :
tmp = pts [ 2 ] . rsplit ( '/' , 1 )
tmp [ 0 ] = repackage_class ( pkgs , tmp [ 0 ])
outf . write ( 'FD: %s %s / %s \n ' % ( pts [ 1 ], tmp [ 0 ], tmp [ 1 ]))
elif pts [ 0 ] == 'MD:' :
tmp = pts [ 3 ] . rsplit ( '/' , 1 )
pts [ 3 ] = ' %s / %s ' % ( repackage_class ( pkgs , tmp [ 0 ]), tmp [ 1 ])
pts [ 4 ] = repackage_signature ( pkgs , pts [ 4 ])
outf . write ( 'MD: %s %s %s %s \n ' % ( pts [ 1 ], pts [ 2 ], pts [ 3 ], pts [ 4 ]))
else :
print 'Line unknown in SRG: ' + line
outf . write ( line )
excre = re . compile ( '([\[ZBCSIJFDV]|L([\w\/]+);)' )
print 'Creating re-packaged exc'
with open ( exc_in , 'r' ) as inf :
with open ( exc_out , 'wb' ) as outf :
for line in inf :
line = line . rstrip ( ' \r\n ' )
cls = line . split ( '.' )[ 0 ]
named = line . rsplit ( '=' , 1 )[ 1 ]
line = line [ len ( cls ) + 1 : - 1 * len ( named ) - 1 ]
func = line . split ( '(' )[ 0 ]
tmp = named . split ( '|' , 1 )
if len ( tmp [ 0 ]) > 0 :
excs = tmp [ 0 ] . split ( ',' )
for x in range ( len ( excs )):
excs [ x ] = repackage_class ( pkgs , excs [ x ])
named = ' %s | %s ' % ( ',' . join ( excs ), tmp [ 1 ])
sig = repackage_signature ( pkgs , line [ len ( func ):])
cls = repackage_class ( pkgs , cls )
outf . write ( ' %s . %s%s = %s \n ' % ( cls , func , sig , named ))
2013-01-24 03:08:19 -08:00
print 'Creating re-packaged MCP patches'
2012-11-20 01:55:41 -08:00
2013-01-24 03:08:19 -08:00
def fix_patches ( patch_in , patch_tmp ):
regnms = re . compile ( r 'net \\ minecraft \\ src \\ (\w+)' )
with open ( patch_in , 'r' ) as fh :
buf = fh . read ()
def mapname ( match ):
return repackage_class ( pkgs , match . group ( 0 ) . replace ( ' \\ ' , '/' )) . replace ( '/' , ' \\ ' )
buf = regnms . sub ( mapname , buf )
with open ( patch_tmp , 'w' ) as fh :
fh . write ( buf )
shutil . move ( patch_tmp , patch_in )
patch_dir = os . path . join ( mcp_dir , 'conf' , 'patches' )
fix_patches ( os . path . join ( patch_dir , 'minecraft_ff.patch' ), os . path . join ( patch_dir , 'tmp.patch' ))
2013-02-25 21:18:08 -05:00
fix_patches ( os . path . join ( patch_dir , 'minecraft_server_ff.patch' ), os . path . join ( patch_dir , 'tmp.patch' ))