-
Notifications
You must be signed in to change notification settings - Fork 0
/
fs_helpers.py
196 lines (147 loc) · 5.25 KB
/
fs_helpers.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
import struct
from io import BytesIO
PADDING_BYTES = b"This is padding data to alignme"
class InvalidOffsetError(Exception):
pass
def data_len(data):
data_length = data.seek(0, 2)
return data_length
def make_copy_data(data):
copy_data = read_all_bytes(data)
return BytesIO(copy_data)
def read_all_bytes(data):
data.seek(0)
return data.read()
def read_bytes(data, offset, length):
data.seek(offset)
return data.read(length)
def write_bytes(data, offset, raw_bytes):
data.seek(offset)
data.write(raw_bytes)
def read_and_unpack_bytes(data, offset, length, format_string):
data.seek(offset)
requested_data = data.read(length)
unpacked_data = struct.unpack(format_string, requested_data)
return unpacked_data
def write_and_pack_bytes(data, offset, new_values, format_string):
packed_data = struct.pack(format_string, *new_values)
data.seek(offset)
data.write(packed_data)
def read_str(data, offset, length):
data_length = data.seek(0, 2)
if offset+length > data_length:
raise InvalidOffsetError("Offset 0x%X, length 0x%X is past the end of the data (length 0x%X)." % (offset, length, data_length))
data.seek(offset)
string = data.read(length).decode("shift_jis")
string = string.rstrip("\0") # Remove trailing null bytes
return string
def try_read_str(data, offset, length):
try:
return read_str(data, offset, length)
except UnicodeDecodeError:
return None
except InvalidOffsetError:
return None
def read_str_until_null_character(data, offset):
data_length = data.seek(0, 2)
if offset > data_length:
raise InvalidOffsetError("Offset 0x%X is past the end of the data (length 0x%X)." % (offset, data_length))
temp_offset = offset
str_length = 0
while temp_offset < data_length:
data.seek(temp_offset)
char = data.read(1)
if char == b"\0":
break
else:
str_length += 1
temp_offset += 1
data.seek(offset)
str = data.read(str_length).decode("shift_jis")
return str
def write_str(data, offset, new_string, max_length):
# Writes a fixed-length string.
# Although it is fixed-length, it still must have a null character terminating it, so the real max length is one less than the passed max_length argument.
str_len = len(new_string)
if str_len >= max_length:
raise Exception("String \"%s\" is too long (max length including null byte: 0x%X)" % (new_string, max_length))
padding_length = max_length - str_len
null_padding = b"\x00"*padding_length
new_value = new_string.encode("shift_jis") + null_padding
data.seek(offset)
data.write(new_value)
def write_magic_str(data, offset, new_string, max_length):
# Writes a fixed-length string that does not have to end with a null byte.
# This is for magic file format identifiers.
str_len = len(new_string)
if str_len > max_length:
raise Exception("String %s is too long (max length 0x%X)" % (new_string, max_length))
padding_length = max_length - str_len
null_padding = b"\x00"*padding_length
new_value = new_string.encode("shift_jis") + null_padding
data.seek(offset)
data.write(new_value)
def write_str_with_null_byte(data, offset, new_string):
# Writes a non-fixed length string.
str_len = len(new_string)
write_str(data, offset, new_string, str_len+1)
def read_u8(data, offset):
data.seek(offset)
return struct.unpack(">B", data.read(1))[0]
def read_u16(data, offset):
data.seek(offset)
return struct.unpack(">H", data.read(2))[0]
def read_u32(data, offset):
data.seek(offset)
return struct.unpack(">I", data.read(4))[0]
def read_float(data, offset):
data.seek(offset)
return struct.unpack(">f", data.read(4))[0]
def read_s8(data, offset):
data.seek(offset)
return struct.unpack(">b", data.read(1))[0]
def read_s16(data, offset):
data.seek(offset)
return struct.unpack(">h", data.read(2))[0]
def read_s32(data, offset):
data.seek(offset)
return struct.unpack(">i", data.read(4))[0]
def write_u8(data, offset, new_value):
new_value = struct.pack(">B", new_value)
data.seek(offset)
data.write(new_value)
def write_u16(data, offset, new_value):
new_value = struct.pack(">H", new_value)
data.seek(offset)
data.write(new_value)
def write_u32(data, offset, new_value):
new_value = struct.pack(">I", new_value)
data.seek(offset)
data.write(new_value)
def write_float(data, offset, new_value):
new_value = struct.pack(">f", new_value)
data.seek(offset)
data.write(new_value)
def write_s8(data, offset, new_value):
new_value = struct.pack(">b", new_value)
data.seek(offset)
data.write(new_value)
def write_s16(data, offset, new_value):
new_value = struct.pack(">h", new_value)
data.seek(offset)
data.write(new_value)
def write_s32(data, offset, new_value):
new_value = struct.pack(">i", new_value)
data.seek(offset)
data.write(new_value)
def align_data_to_nearest(data, size, padding_bytes=PADDING_BYTES):
current_end = data_len(data)
next_offset = current_end + (size - current_end % size) % size
padding_needed = next_offset - current_end
data.seek(current_end)
padding = padding_bytes*(padding_needed // len(padding_bytes))
padding += padding_bytes[:padding_needed % len(padding_bytes)]
data.write(padding)
def pad_offset_to_nearest(offset, size):
next_offset = offset + (size - offset % size) % size
return next_offset