-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathevedec.py
151 lines (131 loc) · 5.59 KB
/
evedec.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
'''
evedec.py
Reads and decrypts Eve Online python files and passes them to uncompyle2 to decompile.
-Doesn't manipulate Eve process. Can be run with or without Eve running.
-Searches for decryption key in the blue.dll file.
-Requires uncompyle2 for actual decompilation.
-Uses multiple processes to speed up decompilation.
Expects a evedec.ini file to specify Eve install location and output directory, e.g.:
[main]
eve_path = C:\Program Files (x86)\CCP\EVE\
store_path = ..\
'''
#function executed by each decompile process
def process_func(code_q, result_q, store_path, lock):
okay_files = failed_files = 0
try:
import sys, os, marshal, errno, Queue
import uncompyle2
while 1:
filename, marshalled_code = code_q.get(True, 5) #give up after 5 sec
if filename == None: #None is our end marker
break
try:
code = marshal.loads(marshalled_code[8:])
#prepend our store_path
filename = os.path.join(store_path, filename)
filename = os.path.abspath(filename)
try:
os.makedirs(os.path.dirname(filename))
except OSError as e:
#the dir may already exist, in which case ignore error
if e.errno != errno.EEXIST:
raise
try:
os.remove(filename+'_failed')
os.remove(filename+'c')
except OSError as e:
if e.errno != errno.ENOENT:
raise
with open(filename, 'w') as out_file:
uncompyle2.uncompyle('2.7', code, out_file)
except KeyboardInterrupt:
raise
except:
with lock:
print '### Can\'t decompile %s' % filename
sys.stdout.flush()
os.rename(filename, filename+'_failed')
with open(filename+'c', 'wb') as codefile:
codefile.write(marshalled_code)
failed_files += 1
else:
with lock:
print '+++ Okay decompiling %s' % filename
sys.stdout.flush()
okay_files += 1
except Queue.Empty: #timeout reached
pass
finally:
result_q.put((okay_files, failed_files))
#executed once by the starting process
if __name__ == '__main__':
#moved imports here so that the other processes don't import them unnecessarily
import sys
if sys.version[:3] != '2.7':
print >>sys.stderr, '!!! Wrong Python version : %s. Python 2.7 required.'
sys.exit(-1)
import os, cPickle, imp, zipfile, zlib, traceback
from Queue import Empty
from multiprocessing import Process, Queue, cpu_count, freeze_support, Lock
from datetime import datetime
from ConfigParser import ConfigParser
freeze_support()
startTime = datetime.now() #time this cpu hog
#Get path to Eve installation from evedec.ini file
config = ConfigParser()
config.read('evedec.ini')
eve_path = config.get('main', 'eve_path')
#use version info from eve's common.ini to create directory name
eveconfig = ConfigParser()
eveconfig.read(os.path.join(eve_path, 'start.ini'))
store_path = os.path.join(config.get('main', 'store_path'), \
'eve-%s.%s' % (eveconfig.get('main', 'version'), eveconfig.get('main', 'build')))
store_path = os.path.abspath(store_path)
#queue of marshalled code objects
code_queue = Queue()
#queue of process results
result_queue = Queue()
sys.stdout.flush()
try:
#create decompile processes
procs = []
print_lock = Lock()
for i in range(cpu_count()-1): #save one process for decompressing/decrypting
procs.append(Process(target=process_func,
args=(code_queue, result_queue, store_path, print_lock)));
#start procs now; they will block on empty queue
for p in procs:
p.start()
with zipfile.ZipFile(os.path.join(eve_path, 'code.ccp'), 'r') as zf:
for filename in zf.namelist():
if filename[-4:] == '.pyj':
code_queue.put( (filename[:-1], zlib.decompress(zf.read(filename))) )
elif filename[-4:] == '.pyc':
code_queue.put( (filename[:-1], zf.read(filename)) )
#this process is done except for waiting, so add one more decompile process
p = Process(target=process_func,
args=(code_queue, result_queue, store_path, print_lock))
p.start()
procs.append(p)
#add sentinel values to indicate end of queue
for p in procs:
code_queue.put( (None, None) )
#wait for decompile processes to finish
for p in procs:
p.join() #join() will block until p is finished
#pull results from the result queue
okay_files = failed_files = 0
try:
while 1: #will terminate when queue.get() generates Empty exception
(o, f) = result_queue.get(False)
okay_files += o
failed_files += f
except Empty:
pass
print '# decompiled %i files: %i okay, %i failed' % \
(okay_files + failed_files, okay_files, failed_files)
print '# elapsed time:', datetime.now() - startTime
except:
traceback.print_exc()
os._exit(0) #make Ctrl-C actually end process