-
Notifications
You must be signed in to change notification settings - Fork 1
/
LinFile.py
234 lines (215 loc) · 7.46 KB
/
LinFile.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#coding=utf8
import rpErrorHandler
from Tkinter import *
from OpCodes import *
import os
import struct
class LinFile():
def __init__(self,
fn = '',
baseoffset = -1,
num_strings = -1,
string_list = [],
action_list = [],
pars_list = [],
opcode_list = [],
type = -1,
second_int = -1):
self.fn = fn
self.baseoffset = baseoffset
self.num_strings = num_strings
self.string_list = string_list
self.action_list = action_list
self.pars_list = pars_list
self.opcode_list = opcode_list
self.type = type
self.second_int = second_int
# Can't forget it ;)
self.strange_byte = -1
pass
def clear(self):
self.baseoffset = -1
self.num_strings = -1
self.type = -1
self.string_list = []
self.action_list = []
self.pars_list = []
self.opcode_list = []
self.second_int = -1
pass
##########################
## Decoder
##########################
def decodeLinFile(self, fn):
# Clear everything
self.clear()
# Open file
self.fn = fn
fp = open(fn, 'rb')
# Get type
fp.seek(0)
self.type = struct.unpack('I',fp.read(4))[0]
# Get second int
self.second_int = struct.unpack('I',fp.read(4))[0]
# Get offset
self.baseoffset = struct.unpack('I',fp.read(4))[0]
# Read action list
self.decodeLinBase(fp)
# Read strings
if self.baseoffset < os.path.getsize(fn):
self.decodeLinStrings(fp)
fp.close()
pass
def decodeLinBase(self, fp):
# We should skip 3 first integers, because those're file type
# some integer (???) and base offset
fp.seek(12)
while fp.tell() < self.baseoffset:
curByte = fp.read(1)
# If that's an operation
if curByte == b'\x70':
opCode = struct.unpack('B', fp.read(1))[0]
action, pars = self.ResolveOpCode(opCode,fp)
self.opcode_list.append(opCode)
self.action_list.append(action)
self.pars_list.append(pars)
# Just some bytecode
else:
byte = struct.unpack('B', curByte)[0]
self.opcode_list.append(0xFF)
self.action_list.append('Bytecode')
self.pars_list.append([('code',byte)])
pass
def decodeLinStrings(self, fp):
fp.seek(self.baseoffset)
self.num_strings = struct.unpack('I',fp.read(4))[0]
self.string_list = [None] * self.num_strings
for i in xrange(self.num_strings):
# Jump to the next text offset value
fp.seek(self.baseoffset + 4 + i * 4)
# Read it
fileoffset = struct.unpack('I',fp.read(4))[0]
offset = self.baseoffset + fileoffset
# Jump to this offset
fp.seek(offset)
# Read everything until 00 00
string = ''
while 1:
word = fp.read(2)
if word == '\x00\x00':
self.string_list[i] = string
break
elif word == '\x0a\x00':
string += '\x0d\x00\x0a\x00'
else:
string += word
# Get this strange closing byte
fp.seek(self.baseoffset + 4 + self.num_strings * 4)
self.strange_byte = fp.read(4)
pass
##########################
## Encoder
##########################
def encodeLinFile(self, fn):
fp = open(fn, 'wb')
# Put file type
fp.write(struct.pack('I',self.type))
# Get second int
fp.write(struct.pack('I',self.second_int))
# Check string base offset for 0x04 alignment
add_zeros = 0x4 - (self.baseoffset % 0x4)
if add_zeros == 4:
add_zeros = 0
self.baseoffset += add_zeros
fp.write(struct.pack('I',self.baseoffset))
# Encode base
for i in xrange(len(self.action_list)):
action = self.action_list[i]
code = self.opcode_list[i]
pars = self.pars_list[i]
## If not a bytecode
if code != 0xFF:
fp.write(b'\x70')
fp.write(struct.pack('B', code))
self.encodeOp(code, pars, fp)
else:
fp.write(struct.pack('B', pars[0][1]))
# Add alignment zeros
for i in xrange(add_zeros):
fp.write(struct.pack('B', 0))
# Encode strings header
for i in xrange(len(self.string_list)):
# Some line modifications
try:
self.string_list[i] = self.string_list[i].encode('utf16')
except:
print "Can't encode string to utf16"
self.string_list[i] = self.string_list[i].replace('\x0d\x00\x0a\x00', '\x0a\x00') + '\x00\x00'
# Total number of strings
fp.write(struct.pack('I', len(self.string_list)))
# Calculating offset for each string
# Each offset is encoded as 4-byte integer plus 2 integers
# for header (total) and last int (dunno what it is)
offset = (len(self.string_list) + 2)*4
str_len = [offset]
for s in self.string_list[:-1]:
str_len.append(len(s) + str_len[-1])
# Write offsets to file
for t_len in str_len:
fp.write(struct.pack('I', t_len))
# Append the last int
try:
fp.write(self.strange_byte)
except:
True
# Write all texts
for s in self.string_list:
fp.write(s)
fp.close()
pass
##########################
## Decode OpCodes
##########################
def UnknownOp(fp, code):
global baseoffset
string = []
a = struct.unpack('B', fp.read(1))[0]
while a != 0x70 and fp.tell() < baseoffset:
string.append(a)
a = struct.unpack('B', fp.read(1))[0]
# Return 1 byte back
fp.seek(-1, 1)
RetStr = "UnknownOp(%s)" % (string)
return (RetStr, code, string)
def ResolveOpCode(self, code, fp):
pars_out = []
# Get parameters for the given OpCode
pars = OP_PARAMS.get(code, None)
# If OpCode is unknown
if pars == None:
action = 'UnknownOp_%d' % code
pars_out.append((None, None))
else:
# First get name of the function
action = OP_FUNCTIONS.get(code, None)
# If the name is empty - just use names like op_66 or op_12, so they can be split by _
if not action:
action = 'op_%d' % code
for par in pars:
name = par[0]
value = struct.unpack('%s' % par[1], fp.read(int('%d' % par[2])) )[0]
pars_out.append((name, value))
# Return name and pars list
return action, pars_out
##########################
## Encode OpCodes
##########################
def encodeOp(self, code, pars, fp):
i = 0
for parType in OP_PARAMS[code]:
fp.write(struct.pack('%s' % parType[1], pars[i][1]))
i += 1
pass
# from LinFile import *
# Lin = LinFile()
# fp = open('e00_001_180.lin', 'rb')