turbine-lib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- turbine_lib/__init__.py +0 -0
- turbine_lib/binarydata.py +133 -0
- turbine_lib/database.py +148 -0
- turbine_lib/datexportapi.py +307 -0
- turbine_lib/datfile.py +338 -0
- turbine_lib/ddssubfile.py +305 -0
- turbine_lib/font_convert.py +353 -0
- turbine_lib/fontsubfile.py +34 -0
- turbine_lib/libs/datexport.dll +0 -0
- turbine_lib/libs/msvcp71.dll +0 -0
- turbine_lib/libs/msvcp90.dll +0 -0
- turbine_lib/libs/msvcr71.dll +0 -0
- turbine_lib/libs/zlib1T.dll +0 -0
- turbine_lib/subfile.py +88 -0
- turbine_lib/subfiledata.py +30 -0
- turbine_lib/textsubfile.py +382 -0
- turbine_lib/textutils.py +29 -0
- turbine_lib/utils.py +117 -0
- turbine_lib-0.1.0.dist-info/METADATA +10 -0
- turbine_lib-0.1.0.dist-info/RECORD +22 -0
- turbine_lib-0.1.0.dist-info/WHEEL +5 -0
- turbine_lib-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import os
|
|
3
|
+
import struct
|
|
4
|
+
from utils import compare_files_byte_by_byte, compare_files_detailed, compare_files
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# bmfont
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SourceCharInfo:
|
|
11
|
+
def __init__(self, id, x, y, width, height, xoffset, yoffset, xadvance, page, channel):
|
|
12
|
+
self.Id = id
|
|
13
|
+
self.X = x
|
|
14
|
+
self.Y = y
|
|
15
|
+
self.Width = width
|
|
16
|
+
self.Height = height
|
|
17
|
+
self.XOffset = xoffset
|
|
18
|
+
self.YOffset = yoffset
|
|
19
|
+
self.XAdvance = xadvance
|
|
20
|
+
self.Page = page
|
|
21
|
+
self.Channel = channel
|
|
22
|
+
# 计算rightBearingX = XAdvance - XOffset - Width
|
|
23
|
+
self.RightBearingX = xadvance - xoffset - width
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Padding:
|
|
27
|
+
def __init__(self, up, right, down, left):
|
|
28
|
+
self.Left = left
|
|
29
|
+
self.Right = right
|
|
30
|
+
self.Up = up
|
|
31
|
+
self.Down = down
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Spacing:
|
|
35
|
+
def __init__(self, horizontal, vertical):
|
|
36
|
+
self.Horizontal = horizontal
|
|
37
|
+
self.Vertical = vertical
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SourceFontInfo:
|
|
41
|
+
def __init__(self, filename):
|
|
42
|
+
self.Filename = filename
|
|
43
|
+
self.IsValid = False
|
|
44
|
+
|
|
45
|
+
# 初始化所有属性
|
|
46
|
+
self.FontName = ""
|
|
47
|
+
self.FontSize = 0
|
|
48
|
+
self.Bold = False
|
|
49
|
+
self.Italic = False
|
|
50
|
+
self.FixedHeight = False
|
|
51
|
+
self.Charset = 0
|
|
52
|
+
self.Unicode = False
|
|
53
|
+
self.StretchH = 0
|
|
54
|
+
self.Smooth = False
|
|
55
|
+
self.Antialiasing = 0
|
|
56
|
+
self.Padding = None
|
|
57
|
+
self.Spacing = None
|
|
58
|
+
self.Outline = 0
|
|
59
|
+
self.LineHeight = 0
|
|
60
|
+
self.Base = 0
|
|
61
|
+
self.ScaleW = 0
|
|
62
|
+
self.ScaleH = 0
|
|
63
|
+
self.Pages = 0
|
|
64
|
+
self.AlphaChannel = 0
|
|
65
|
+
self.RedChannel = 0
|
|
66
|
+
self.GreenChannel = 0
|
|
67
|
+
self.BlueChannel = 0
|
|
68
|
+
self.DdsFilename = ""
|
|
69
|
+
self.CharsCount = 0
|
|
70
|
+
self.Chars = []
|
|
71
|
+
self.DdsWidth = 0
|
|
72
|
+
self.DdsHeight = 0
|
|
73
|
+
|
|
74
|
+
if filename:
|
|
75
|
+
self.IsValid = self._read(filename)
|
|
76
|
+
# pass
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def Filename(self):
|
|
80
|
+
return self._filename
|
|
81
|
+
|
|
82
|
+
@Filename.setter
|
|
83
|
+
def Filename(self, value):
|
|
84
|
+
self._filename = value
|
|
85
|
+
# if value:
|
|
86
|
+
# self.IsValid = self._read(value)
|
|
87
|
+
|
|
88
|
+
def _read(self, filename):
|
|
89
|
+
try:
|
|
90
|
+
with open(filename, 'rb') as f:
|
|
91
|
+
data = f.read()
|
|
92
|
+
|
|
93
|
+
# file identifier: BMF3
|
|
94
|
+
if data[0] != 66 or data[1] != 77 or data[2] != 70 or data[3] != 3:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# 使用内存视图来模拟BinaryReader
|
|
98
|
+
offset = 0
|
|
99
|
+
|
|
100
|
+
# file identifier
|
|
101
|
+
offset += 4
|
|
102
|
+
|
|
103
|
+
# block type 1: info
|
|
104
|
+
# read type identifier and size
|
|
105
|
+
offset += 1 # 跳过类型标识符
|
|
106
|
+
block1_size = struct.unpack_from('<I', data, offset)[0]
|
|
107
|
+
offset += 4
|
|
108
|
+
|
|
109
|
+
# read content
|
|
110
|
+
self.FontSize = struct.unpack_from('<h', data, offset)[0]
|
|
111
|
+
offset += 2
|
|
112
|
+
|
|
113
|
+
bit_field_byte = struct.unpack_from('B', data, offset)[0]
|
|
114
|
+
offset += 1
|
|
115
|
+
|
|
116
|
+
# 解析bit field
|
|
117
|
+
self.Smooth = bool(bit_field_byte & 1)
|
|
118
|
+
self.Unicode = bool(bit_field_byte & 2)
|
|
119
|
+
self.Italic = bool(bit_field_byte & 4)
|
|
120
|
+
self.Bold = bool(bit_field_byte & 8)
|
|
121
|
+
self.FixedHeight = bool(bit_field_byte & 16)
|
|
122
|
+
|
|
123
|
+
self.Charset = struct.unpack_from('B', data, offset)[0]
|
|
124
|
+
offset += 1
|
|
125
|
+
self.StretchH = struct.unpack_from('<H', data, offset)[0]
|
|
126
|
+
offset += 2
|
|
127
|
+
self.Antialiasing = struct.unpack_from('B', data, offset)[0]
|
|
128
|
+
offset += 1
|
|
129
|
+
|
|
130
|
+
# Padding
|
|
131
|
+
padding_up = struct.unpack_from('B', data, offset)[0]
|
|
132
|
+
offset += 1
|
|
133
|
+
padding_right = struct.unpack_from('B', data, offset)[0]
|
|
134
|
+
offset += 1
|
|
135
|
+
padding_down = struct.unpack_from('B', data, offset)[0]
|
|
136
|
+
offset += 1
|
|
137
|
+
padding_left = struct.unpack_from('B', data, offset)[0]
|
|
138
|
+
offset += 1
|
|
139
|
+
self.Padding = Padding(padding_up, padding_right, padding_down, padding_left)
|
|
140
|
+
|
|
141
|
+
# Spacing
|
|
142
|
+
spacing_horizontal = struct.unpack_from('B', data, offset)[0]
|
|
143
|
+
offset += 1
|
|
144
|
+
spacing_vertical = struct.unpack_from('B', data, offset)[0]
|
|
145
|
+
offset += 1
|
|
146
|
+
self.Spacing = Spacing(spacing_horizontal, spacing_vertical)
|
|
147
|
+
|
|
148
|
+
self.Outline = struct.unpack_from('B', data, offset)[0]
|
|
149
|
+
offset += 1
|
|
150
|
+
|
|
151
|
+
# FontName
|
|
152
|
+
font_name_bytes = data[offset:offset + (block1_size - 14)]
|
|
153
|
+
# self.FontName = font_name_bytes.decode('utf-8').rstrip('\x00') # 移除可能的空字符
|
|
154
|
+
offset += (block1_size - 14)
|
|
155
|
+
|
|
156
|
+
# block type 2: common
|
|
157
|
+
# read type identifier and size
|
|
158
|
+
offset += 1 # 跳过类型标识符
|
|
159
|
+
block2_size = struct.unpack_from('<I', data, offset)[0]
|
|
160
|
+
offset += 4
|
|
161
|
+
|
|
162
|
+
# read content
|
|
163
|
+
self.LineHeight = struct.unpack_from('<H', data, offset)[0]
|
|
164
|
+
offset += 2
|
|
165
|
+
self.Base = struct.unpack_from('<H', data, offset)[0]
|
|
166
|
+
offset += 2
|
|
167
|
+
self.ScaleW = struct.unpack_from('<H', data, offset)[0]
|
|
168
|
+
offset += 2
|
|
169
|
+
self.ScaleH = struct.unpack_from('<H', data, offset)[0]
|
|
170
|
+
offset += 2
|
|
171
|
+
self.Pages = struct.unpack_from('<H', data, offset)[0]
|
|
172
|
+
offset += 2
|
|
173
|
+
|
|
174
|
+
# skip bit field
|
|
175
|
+
offset += 1
|
|
176
|
+
|
|
177
|
+
self.AlphaChannel = struct.unpack_from('B', data, offset)[0]
|
|
178
|
+
offset += 1
|
|
179
|
+
self.RedChannel = struct.unpack_from('B', data, offset)[0]
|
|
180
|
+
offset += 1
|
|
181
|
+
self.GreenChannel = struct.unpack_from('B', data, offset)[0]
|
|
182
|
+
offset += 1
|
|
183
|
+
self.BlueChannel = struct.unpack_from('B', data, offset)[0]
|
|
184
|
+
offset += 1
|
|
185
|
+
|
|
186
|
+
# block type 3: pages
|
|
187
|
+
# read type identifier and size
|
|
188
|
+
offset += 1 # 跳过类型标识符
|
|
189
|
+
block3_size = struct.unpack_from('<I', data, offset)[0]
|
|
190
|
+
offset += 4
|
|
191
|
+
|
|
192
|
+
# read content
|
|
193
|
+
# assume we have no more than 1 page here
|
|
194
|
+
dds_filename_bytes = data[offset:offset + block3_size - 1] # 去掉结尾的null字符
|
|
195
|
+
self.DdsFilename = dds_filename_bytes.decode('utf-8')
|
|
196
|
+
offset += block3_size
|
|
197
|
+
|
|
198
|
+
# block type 4: chars
|
|
199
|
+
# read type identifier and size
|
|
200
|
+
offset += 1 # 跳过类型标识符
|
|
201
|
+
block4_size = struct.unpack_from('<I', data, offset)[0]
|
|
202
|
+
offset += 4
|
|
203
|
+
|
|
204
|
+
# read content
|
|
205
|
+
self.CharsCount = block4_size // 20
|
|
206
|
+
self.Chars = []
|
|
207
|
+
for i in range(self.CharsCount):
|
|
208
|
+
id = struct.unpack_from('<I', data, offset)[0]
|
|
209
|
+
offset += 4
|
|
210
|
+
x = struct.unpack_from('<H', data, offset)[0]
|
|
211
|
+
offset += 2
|
|
212
|
+
y = struct.unpack_from('<H', data, offset)[0]
|
|
213
|
+
offset += 2
|
|
214
|
+
width = struct.unpack_from('<H', data, offset)[0]
|
|
215
|
+
offset += 2
|
|
216
|
+
height = struct.unpack_from('<H', data, offset)[0]
|
|
217
|
+
offset += 2
|
|
218
|
+
xoffset = struct.unpack_from('<h', data, offset)[0]
|
|
219
|
+
offset += 2
|
|
220
|
+
yoffset = struct.unpack_from('<h', data, offset)[0]
|
|
221
|
+
offset += 2
|
|
222
|
+
xadvance = struct.unpack_from('<h', data, offset)[0]
|
|
223
|
+
offset += 2
|
|
224
|
+
page = struct.unpack_from('B', data, offset)[0]
|
|
225
|
+
offset += 1
|
|
226
|
+
channel = struct.unpack_from('B', data, offset)[0]
|
|
227
|
+
offset += 1
|
|
228
|
+
|
|
229
|
+
char_info = SourceCharInfo(id, x, y, width, height, xoffset, yoffset, xadvance, page, channel)
|
|
230
|
+
self.Chars.append(char_info)
|
|
231
|
+
|
|
232
|
+
# skip block 5 (如果存在)
|
|
233
|
+
|
|
234
|
+
return True
|
|
235
|
+
except Exception as e:
|
|
236
|
+
raise Exception(f"Error reading font file: {e}")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# 目标字体
|
|
240
|
+
class DestCharInfo:
|
|
241
|
+
def __init__(self, info):
|
|
242
|
+
self.Id = int(info.Id) & 0xFFFF # unsigned word
|
|
243
|
+
self.X = int(info.X) & 0xFFFF # unsigned word
|
|
244
|
+
self.Y = int(info.Y) & 0xFFFF # unsigned word
|
|
245
|
+
self.Width = int(info.Width) & 0xFF # unsigned byte
|
|
246
|
+
self.Height = int(info.Height) & 0xFF # unsigned byte
|
|
247
|
+
self.XOffset = int(info.XOffset) # signed byte
|
|
248
|
+
# 确保XOffset在有符号字节范围内 [-128, 127]
|
|
249
|
+
if self.XOffset > 127:
|
|
250
|
+
self.XOffset -= 256
|
|
251
|
+
elif self.XOffset < -128:
|
|
252
|
+
self.XOffset += 256
|
|
253
|
+
|
|
254
|
+
self.YOffset = int(info.YOffset) # signed byte处理,但存储为unsigned byte
|
|
255
|
+
# 确保YOffset在有符号字节范围内 [-128, 127],但转换为unsigned byte [0, 255]
|
|
256
|
+
if self.YOffset > 127:
|
|
257
|
+
self.YOffset -= 256
|
|
258
|
+
elif self.YOffset < -128:
|
|
259
|
+
self.YOffset += 256
|
|
260
|
+
# 转换为unsigned byte范围
|
|
261
|
+
self.YOffset = self.YOffset & 0xFF
|
|
262
|
+
|
|
263
|
+
# 计算rightBearingX
|
|
264
|
+
self.RightBearingX = int(info.XAdvance - info.XOffset - info.Width) # signed byte
|
|
265
|
+
if self.RightBearingX > 127:
|
|
266
|
+
self.RightBearingX -= 256
|
|
267
|
+
elif self.RightBearingX < -128:
|
|
268
|
+
self.RightBearingX += 256
|
|
269
|
+
|
|
270
|
+
self.XAdvance = int(info.XAdvance) # signed byte
|
|
271
|
+
if self.XAdvance > 127:
|
|
272
|
+
self.XAdvance -= 256
|
|
273
|
+
elif self.XAdvance < -128:
|
|
274
|
+
self.XAdvance += 256
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class DestFontInfo:
|
|
278
|
+
def __init__(self, info):
|
|
279
|
+
self.FontSize = abs(info.FontSize)
|
|
280
|
+
self.Base = info.Base
|
|
281
|
+
self.LineHeight = int(info.LineHeight) & 0xFFFF # short
|
|
282
|
+
# if self.LineHeight > 32767:
|
|
283
|
+
# self.LineHeight -= 65536
|
|
284
|
+
self.Charset = int(info.Charset) & 0xFFFF # short
|
|
285
|
+
# if self.Charset > 32767:
|
|
286
|
+
# self.Charset -= 65536
|
|
287
|
+
self.CharsCount = info.CharsCount
|
|
288
|
+
self.Padding = int(info.Padding.Left) & 0xFF # byte
|
|
289
|
+
self.ScaleW = info.FontSize # 新增:用于作为width
|
|
290
|
+
self.ScaleH = info.FontSize # 新增:用于作为height
|
|
291
|
+
|
|
292
|
+
self.Chars = []
|
|
293
|
+
for i in range(self.CharsCount):
|
|
294
|
+
self.Chars.append(DestCharInfo(info.Chars[i]))
|
|
295
|
+
|
|
296
|
+
def import_font(self, fontbin_filename):
|
|
297
|
+
# 创建输出目录
|
|
298
|
+
now = datetime.datetime.now()
|
|
299
|
+
dir_name = f"output_{now.strftime('%Y-%m-%d')}"
|
|
300
|
+
dir_path = os.path.join(os.getcwd(), dir_name)
|
|
301
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
302
|
+
|
|
303
|
+
# 构建输出文件路径
|
|
304
|
+
|
|
305
|
+
path = os.path.join(dir_path, fontbin_filename)
|
|
306
|
+
# 写入处理后的数据
|
|
307
|
+
with open(path, 'wb') as fs:
|
|
308
|
+
# 写入头部数据 - 根据新格式调整
|
|
309
|
+
fs.write(struct.pack('<I', 0)) # masterFileID
|
|
310
|
+
fs.write(struct.pack('<I', self.ScaleW)) # width
|
|
311
|
+
fs.write(struct.pack('<I', self.ScaleH)) # height
|
|
312
|
+
fs.write(struct.pack('<I', self.CharsCount)) # CharsCount
|
|
313
|
+
|
|
314
|
+
# 写入字符信息 - 根据新格式调整
|
|
315
|
+
for i in range(self.CharsCount):
|
|
316
|
+
char = self.Chars[i]
|
|
317
|
+
fs.write(struct.pack('<B', char.Width)) # Chars[i].Width
|
|
318
|
+
fs.write(struct.pack('<B', char.Height)) # Chars[i].Height
|
|
319
|
+
fs.write(struct.pack('<b', char.XOffset)) # Chars[i].bearingX
|
|
320
|
+
fs.write(struct.pack('<b', char.RightBearingX)) # Chars[i].rightBearingX
|
|
321
|
+
fs.write(struct.pack('<B', char.YOffset)) # Chars[i].bearingY
|
|
322
|
+
fs.write(struct.pack('<H', char.Id)) # Chars[i].id
|
|
323
|
+
fs.write(struct.pack('<H', char.X)) # Chars[i].X
|
|
324
|
+
fs.write(struct.pack('<H', char.Y)) # Chars[i].Y
|
|
325
|
+
|
|
326
|
+
# 写入结尾数据
|
|
327
|
+
fs.write(struct.pack('<I', 0)) # p
|
|
328
|
+
fs.write(struct.pack('<I', 0)) # q
|
|
329
|
+
fs.write(struct.pack('<I', 0)) # r
|
|
330
|
+
fs.write(struct.pack('<I', 0)) # ddsId1
|
|
331
|
+
fs.write(struct.pack('<I', 0)) # ddsId2
|
|
332
|
+
return path
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def fnt_to_fontbin(bmfont_file):
|
|
336
|
+
"""
|
|
337
|
+
bmfont转换为fontbin
|
|
338
|
+
"""
|
|
339
|
+
source_font = SourceFontInfo(bmfont_file)
|
|
340
|
+
if not source_font.IsValid:
|
|
341
|
+
print("Invalid source_font file!")
|
|
342
|
+
raise ValueError("Invalid font file!")
|
|
343
|
+
destFontInfo = DestFontInfo(source_font)
|
|
344
|
+
output_fontbin = destFontInfo.import_font("template.fontbin")
|
|
345
|
+
print(f'fontbin生成目录:{output_fontbin}')
|
|
346
|
+
return output_fontbin
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if __name__ == '__main__':
|
|
350
|
+
font = SourceFontInfo(r'bmfont1.14a\output.fnt')
|
|
351
|
+
destFontInfo = DestFontInfo(font)
|
|
352
|
+
output_fontbin = destFontInfo.import_font('test.fontbin')
|
|
353
|
+
print(f"生成字体成功:{output_fontbin}")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from subfile import Subfile
|
|
2
|
+
from binarydata import BinaryData
|
|
3
|
+
from subfiledata import SubfileData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FONT:
|
|
7
|
+
"""Marker class for FONT file type"""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SubfileFONT(Subfile):
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def build_for_export(file_data):
|
|
15
|
+
"""
|
|
16
|
+
Build data for export from FONT file
|
|
17
|
+
:param file_data: BinaryData with file data
|
|
18
|
+
:return: SubfileData with processed data
|
|
19
|
+
"""
|
|
20
|
+
result = SubfileData()
|
|
21
|
+
result.binary_data = file_data
|
|
22
|
+
result.options["ext"] = ".fontbin"
|
|
23
|
+
return result
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def build_for_import(old_data, data):
|
|
27
|
+
"""
|
|
28
|
+
Build data for import to FONT file
|
|
29
|
+
:param old_data: BinaryData with old data
|
|
30
|
+
:param data: SubfileData with new data
|
|
31
|
+
:return: BinaryData with processed data
|
|
32
|
+
"""
|
|
33
|
+
# return old_data.cut_data(0, 4) + data.binary_data.cut_data(4)
|
|
34
|
+
return data.binary_data
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
turbine_lib/subfile.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
class FILE_TYPE(Enum):
|
|
4
|
+
TEXT = '.text'
|
|
5
|
+
JPG = '.jpg'
|
|
6
|
+
DDS='.dds'
|
|
7
|
+
WAV = '.wav'
|
|
8
|
+
OGG = '.ogg'
|
|
9
|
+
FONT = '.fontbin'
|
|
10
|
+
UNKNOWN = '.unknown'
|
|
11
|
+
|
|
12
|
+
def file_type_from_string(ext):
|
|
13
|
+
"""Convert string extension to FILE_TYPE enum"""
|
|
14
|
+
if ext == ".txt":
|
|
15
|
+
return FILE_TYPE.TEXT
|
|
16
|
+
elif ext == ".jpg":
|
|
17
|
+
return FILE_TYPE.JPG
|
|
18
|
+
elif ext == ".dds":
|
|
19
|
+
return FILE_TYPE.DDS
|
|
20
|
+
elif ext == ".wav":
|
|
21
|
+
return FILE_TYPE.WAV
|
|
22
|
+
elif ext == ".ogg":
|
|
23
|
+
return FILE_TYPE.OGG
|
|
24
|
+
elif ext == ".fontbin":
|
|
25
|
+
return FILE_TYPE.FONT
|
|
26
|
+
else:
|
|
27
|
+
return FILE_TYPE.UNKNOWN
|
|
28
|
+
|
|
29
|
+
def file_type_from_file_contents(file_id, file_data):
|
|
30
|
+
"""Determine file type from file contents"""
|
|
31
|
+
# Text check based on file_id
|
|
32
|
+
if (file_id >> 24) == 0x25:
|
|
33
|
+
return FILE_TYPE.TEXT
|
|
34
|
+
|
|
35
|
+
# Font check based on file_id
|
|
36
|
+
if (file_id >> 24) == 0x42:
|
|
37
|
+
return FILE_TYPE.FONT
|
|
38
|
+
|
|
39
|
+
# jpeg / dds check
|
|
40
|
+
if (file_id >> 24) == 0x41:
|
|
41
|
+
soi = file_data.to_number(2, 24)
|
|
42
|
+
# long long marker = file_data.ToNumber<2>(26)
|
|
43
|
+
|
|
44
|
+
# auto markerSize = header.ToNumber<short>(28)
|
|
45
|
+
# auto four = header.ToNumber<int>(30)
|
|
46
|
+
|
|
47
|
+
if soi == 0xD8FF:
|
|
48
|
+
return FILE_TYPE.JPG
|
|
49
|
+
return FILE_TYPE.DDS
|
|
50
|
+
|
|
51
|
+
# Ogg and Wav check
|
|
52
|
+
if len(file_data) > 11:
|
|
53
|
+
if file_data[8] == 0x4F and file_data[9] == 0x67 and file_data[10] == 0x67 and file_data[11] == 0x53:
|
|
54
|
+
return FILE_TYPE.OGG
|
|
55
|
+
|
|
56
|
+
if file_data[8] == 0x52 and file_data[9] == 0x49 and file_data[10] == 0x46 and file_data[11] == 0x46:
|
|
57
|
+
return FILE_TYPE.WAV
|
|
58
|
+
|
|
59
|
+
return FILE_TYPE.UNKNOWN
|
|
60
|
+
|
|
61
|
+
def string_from_file_type(file_type):
|
|
62
|
+
"""Convert FILE_TYPE enum to string extension"""
|
|
63
|
+
if file_type == FILE_TYPE.TEXT.value or file_type == FILE_TYPE.TEXT:
|
|
64
|
+
return ".txt"
|
|
65
|
+
elif file_type == FILE_TYPE.JPG.value or file_type == FILE_TYPE.JPG:
|
|
66
|
+
return ".jpg"
|
|
67
|
+
elif file_type == FILE_TYPE.DDS.value or file_type == FILE_TYPE.DDS:
|
|
68
|
+
return ".dds"
|
|
69
|
+
elif file_type == FILE_TYPE.WAV.value or file_type == FILE_TYPE.WAV:
|
|
70
|
+
return ".wav"
|
|
71
|
+
elif file_type == FILE_TYPE.OGG.value or file_type == FILE_TYPE.OGG:
|
|
72
|
+
return ".ogg"
|
|
73
|
+
elif file_type == FILE_TYPE.FONT.value or file_type == FILE_TYPE.FONT:
|
|
74
|
+
return ".fontbin"
|
|
75
|
+
else:
|
|
76
|
+
return ".subfile"
|
|
77
|
+
|
|
78
|
+
# Template class for Subfile - will be implemented in specific subfile type files
|
|
79
|
+
class Subfile:
|
|
80
|
+
"""Base class for subfile processing"""
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def build_for_import(old_data, outer_data):
|
|
84
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def build_for_export(inner_data):
|
|
88
|
+
raise NotImplementedError("Subclasses must implement this method")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
|
|
3
|
+
class SubfileData:
|
|
4
|
+
|
|
5
|
+
def __init__(self, binary_data=None, text_data="", options=None):
|
|
6
|
+
if binary_data is None:
|
|
7
|
+
# Default constructor
|
|
8
|
+
from binarydata import BinaryData
|
|
9
|
+
self.binary_data = BinaryData()
|
|
10
|
+
self.text_data = ""
|
|
11
|
+
self.options = yaml.safe_load("{}") if options is None else options
|
|
12
|
+
else:
|
|
13
|
+
# Constructor with parameters
|
|
14
|
+
self.binary_data = binary_data
|
|
15
|
+
self.text_data = text_data
|
|
16
|
+
self.options = options if options is not None else yaml.safe_load("{}")
|
|
17
|
+
|
|
18
|
+
def empty(self):
|
|
19
|
+
return (len(self.binary_data) == 0 and
|
|
20
|
+
len(self.text_data) == 0 and
|
|
21
|
+
(self.options is None or self.options == yaml.safe_load("{}")))
|
|
22
|
+
|
|
23
|
+
def __eq__(self, other):
|
|
24
|
+
if not isinstance(other, SubfileData):
|
|
25
|
+
return False
|
|
26
|
+
return (self.binary_data == other.binary_data and
|
|
27
|
+
self.text_data == other.text_data)
|
|
28
|
+
|
|
29
|
+
def __ne__(self, other):
|
|
30
|
+
return not self.__eq__(other)
|