Skip to content

Commit 307855b

Browse files
First file upload.
1 parent 832f03e commit 307855b

File tree

6 files changed

+1812
-0
lines changed

6 files changed

+1812
-0
lines changed

ai6win_mes.py

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
import struct
2+
import json
3+
import os
4+
from library.silky_mes import SilkyMesScript, SilkyMesScriptError
5+
6+
7+
class AI6WINScript(SilkyMesScript):
8+
command_library = (
9+
(0x00, '', 'NULL'),
10+
(0x01, 'I', ''),
11+
(0x02, '', ''),
12+
(0x03, '', ''),
13+
(0x04, '', ''),
14+
(0x05, '', ''),
15+
(0x06, '', ''),
16+
17+
(0x0A, 'S', 'STR_CRYPT'),
18+
(0x0B, 'S', 'STR_UNCRYPT'),
19+
(0x0C, '', ''),
20+
(0x0D, '', ''),
21+
(0x0E, '', ''),
22+
(0x0F, '', ''),
23+
24+
(0x10, 'B', ''),
25+
(0x11, '', ''),
26+
(0x14, '>I', 'JUMP'),
27+
(0x15, '>I', 'MSG_OFSETTER'),
28+
(0x16, '>I', 'SPEC_OFSETTER'),
29+
(0x17, '', ''),
30+
(0x18, '', ''),
31+
(0x19, '>I', 'MESSAGE'),
32+
(0x1A, '>I', ''),
33+
(0x1B, '>I', ''),
34+
(0x1C, 'B', 'TO_NEW_STRING'),
35+
36+
(0x32, '>hh', ''),
37+
(0x33, 'S', 'STR_RAW'),
38+
(0x34, '', ''),
39+
(0x35, '', ''),
40+
(0x36, 'B', 'JUMP_2'),
41+
(0x37, '', ''),
42+
(0x38, '', ''), # AI6WIN only?
43+
(0x3A, '', ''),
44+
(0x3B, '', ''),
45+
(0x39, '', ''), # AI6WIN only?
46+
(0x3C, '', ''),
47+
(0x3D, '', ''),
48+
(0x3E, '', ''),
49+
(0x3F, '', ''), # AI6WIN only?
50+
51+
(0x40, '', ''), # AI6WIN only?
52+
(0x41, '', ''), # AI6WIN only?
53+
(0x42, '', ''),
54+
(0x43, '', ''),
55+
56+
(0xFA, '', ''),
57+
(0xFB, '', ''),
58+
(0xFC, '', ''),
59+
(0xFD, '', ''),
60+
(0xFE, '', ''),
61+
(0xFF, '', ''),
62+
)
63+
64+
# User methods.
65+
66+
def disassemble(self) -> None:
67+
"""Disassemble AI6WIN mes script."""
68+
self._offsets = []
69+
self._prm, self._first_offsets = self._diss_header()
70+
self._diss_other_offsets()
71+
if self._verbose:
72+
print("Parameters:", self._prm[0:1])
73+
print("First offsets:", len(self._first_offsets), self._first_offsets)
74+
print("True offsets:", len(self._offsets), self._offsets)
75+
self._disassemble_commands()
76+
77+
def assemble(self) -> None:
78+
"""Assemble Silky Engine mes script."""
79+
80+
self._prm, self._first_offsets, self._offsets = self._assemble_offsets_and_parameters()
81+
82+
if self._verbose:
83+
print("Parameters:", self._prm[0:1])
84+
print("First offsets:", len(self._first_offsets), self._first_offsets)
85+
print("True offsets:", len(self._offsets), self._offsets)
86+
self._assemble_script_file()
87+
88+
# Technical methods for assembling.
89+
90+
def _assemble_script_file(self) -> None:
91+
"""Assemble AI6WIN mes script."""
92+
in_file = open(self._txt_name, 'r', encoding=self.encoding)
93+
try:
94+
os.rename(self._mes_name, self._mes_name + '.bak')
95+
except OSError:
96+
pass
97+
out_file = open(self._mes_name, 'wb')
98+
99+
message_count = 0
100+
search_offset = [i[0] for i in self._offsets]
101+
102+
out_file.write(struct.pack('I', self._prm[0]))
103+
for first_offset in self._first_offsets:
104+
out_file.write(struct.pack('I', first_offset))
105+
106+
while True:
107+
line = in_file.readline()
108+
if line == '': # EOF.
109+
break
110+
if len(line) == 1: # To evade some nasty errors.
111+
continue
112+
if (line == '\n') or (line[0] == '$'):
113+
continue
114+
if line[1] == '0':
115+
out_file.write(bytes.fromhex(line[2:-1]))
116+
elif line[1] == '1':
117+
command_string = line[3:-1]
118+
command_index = -1
119+
for num, lib_entry in enumerate(self.command_library): # Check if it is written by name.
120+
if command_string == lib_entry[2]:
121+
command_index = num
122+
break
123+
if command_index == -1: # Check if it is written by hex.
124+
command_string = int(command_string, 16)
125+
for num, lib_entry in enumerate(self.command_library):
126+
if command_string == lib_entry[0]:
127+
command_index = num
128+
break
129+
if command_index == -1: # There is no such command (text). But this should be impossible!
130+
raise AI6WINScriptError("Error! There is no such command.\n{}".format(command_string))
131+
out_file.write(struct.pack('B', self.command_library[command_index][0]))
132+
133+
line = in_file.readline()
134+
135+
argument_list = json.loads(line)
136+
137+
this_command = self.command_library[command_index][0]
138+
offset_set = -1
139+
if this_command == 0x19:
140+
argument_list[0] = message_count
141+
message_count += 1
142+
else:
143+
for offset_entry in self.offsets_library:
144+
if this_command == offset_entry[0]:
145+
offset_set = offset_entry[1]
146+
break
147+
148+
if offset_set != -1:
149+
indexer = search_offset.index(argument_list[offset_set])
150+
argument_list[offset_set] = self._offsets[indexer][1]
151+
152+
argument_bytes = self.set_args(argument_list, self.command_library[command_index][1], self.encoding)
153+
out_file.write(argument_bytes)
154+
155+
in_file.close()
156+
out_file.close()
157+
158+
def _assemble_offsets_and_parameters(self) -> tuple:
159+
"""Assemble offsets and parameters of AI6WIN mes archive."""
160+
in_file = open(self._txt_name, 'r', encoding=self.encoding)
161+
162+
first_offsets = []
163+
offsets = []
164+
prm = [0, 0] # First shall be changed. Second is to work with functions inherited from silky_mes.
165+
166+
pointer = 0
167+
message_count = 0
168+
169+
while True:
170+
line = in_file.readline()
171+
if line == '': # EOF.
172+
break
173+
if len(line) == 1: # To evade some nasty errors.
174+
continue
175+
if (line == '\n') or (line[0] == '$'): # Line without text or comment should not be parsed as script.
176+
continue
177+
178+
# Actually code strings logic.
179+
180+
if line[1] == '0': # "Free bytes".
181+
pointer += len(line[2:-1].split(' '))
182+
elif line[1] == '1': # Command.
183+
command_string = line[3:-1]
184+
command_index = -1
185+
for num, lib_entry in enumerate(self.command_library): # Check if it is written by name.
186+
if command_string == lib_entry[2]:
187+
command_index = num
188+
break
189+
if command_index == -1: # Check if it is written by hex.
190+
command_string = int(command_string, 16)
191+
for num, lib_entry in enumerate(self.command_library):
192+
if command_string == lib_entry[0]:
193+
command_index = num
194+
break
195+
if command_index == -1: # There is no such command (text). But this should be impossible!
196+
raise AI6WINScriptError("Error! There is no such command.\n{}".format(command_string))
197+
198+
if self.command_library[command_index][0] == 0x19: # Since header save offsets to messages.
199+
message_count += 1
200+
first_offsets.append(pointer)
201+
202+
pointer += 1
203+
204+
# Okay, now is the time for getting arguments length!
205+
line = in_file.readline()
206+
argument_list = json.loads(line)
207+
if self.command_library[command_index][0] == 0x19: # For this to not cause any errors.
208+
argument_list[0] = 0
209+
argument_bytes = self.set_args(argument_list, self.command_library[command_index][1], self.encoding)
210+
pointer += len(argument_bytes)
211+
212+
elif line[1] == '2': # If label (of true offset).
213+
offset_array = []
214+
215+
offset_number = int(line[3:-1])
216+
offset_array.append(offset_number)
217+
offset_array.append(pointer)
218+
219+
offsets.append(offset_array)
220+
in_file.close()
221+
222+
prm[0] = message_count
223+
224+
return prm, first_offsets, offsets
225+
226+
# Technical methods for disassembling.
227+
228+
def _diss_other_offsets(self) -> None:
229+
"""Disassemble other offsets from the Silky Engine script."""
230+
pointer = self.get_true_offset(0)
231+
in_file = open(self._mes_name, 'rb')
232+
in_file.seek(pointer, 0)
233+
234+
if self._hackerman_mode:
235+
out_file = open("HACK.txt", 'w', encoding=self.encoding)
236+
237+
while True:
238+
pointer = in_file.tell()
239+
current_byte = in_file.read(1)
240+
if current_byte == b'':
241+
break
242+
current_byte = current_byte[0] # Get int from byte in the fastest way possible.
243+
lib_index = -1
244+
for i in range(len(self.command_library)):
245+
if (current_byte == self.command_library[i][0]):
246+
lib_index = i
247+
break
248+
if lib_index != -1:
249+
arguments_list = self.get_args(in_file, self.command_library[lib_index][1], current_byte,
250+
self.encoding)
251+
252+
if self._hackerman_mode:
253+
out_file.write("#1-{} {}\n".format(hex(current_byte), pointer))
254+
out_file.write(str(arguments_list))
255+
out_file.write("\n")
256+
257+
what_index = -1
258+
for entry_pos, offsets_entry in enumerate(self.offsets_library):
259+
if current_byte == offsets_entry[0]:
260+
what_index = entry_pos
261+
if what_index != -1:
262+
not_here = True
263+
good_offset = self.get_true_offset(arguments_list[self.offsets_library[what_index][1]])
264+
for i in range(len(self._offsets)):
265+
if good_offset == self._offsets[i]:
266+
not_here = False
267+
if not_here:
268+
self._offsets.append(good_offset)
269+
else:
270+
if self._hackerman_mode:
271+
out_file.write("#0-{} {}\n".format(hex(current_byte), pointer))
272+
273+
in_file.close()
274+
if self._hackerman_mode:
275+
out_file.close()
276+
277+
def _diss_header(self) -> tuple:
278+
"""Disassemble Silky Engine mes header."""
279+
first_offsets = []
280+
with open(self._mes_name, 'rb') as mes_file:
281+
prm = list(struct.unpack('I', mes_file.read(4)))
282+
for i in range(prm[0]):
283+
first_offsets.append(struct.unpack('I', mes_file.read(4))[0])
284+
285+
return prm, first_offsets
286+
287+
# Offsets methods.
288+
289+
def get_true_offset(self, raw_offset: int) -> int:
290+
"""Get true offset (as it is factically in the file)."""
291+
return raw_offset + self._prm[0] * 4 + 4
292+
293+
def set_true_offset(self, raw_offset):
294+
"""Set true offset (as it is factically in the arguments)."""
295+
return raw_offset - self._prm[0] * 4 - 4
296+
297+
298+
class AI6WINScriptError(SilkyMesScriptError):
299+
pass

0 commit comments

Comments
 (0)