@@ -54,6 +54,313 @@ cdef struct pixel:
54
54
uint8_t damage_modifier_2 # modifier for damage (part 2)
55
55
56
56
57
+ cdef fused ProcessDrawingCmdsVariant:
58
+ SMXLayerVariant
59
+ SMXMainLayer8to5Variant
60
+ SMXMainLayer4plus1Variant
61
+ SMXOutlineLayerVariant
62
+ SMXShadowLayerVariant
63
+
64
+
65
+ cdef process_drawing_cmds(ProcessDrawingCmdsVariant variant,
66
+ const uint8_t[:] & data_raw,
67
+ vector[pixel] & row_data,
68
+ Py_ssize_t rowid,
69
+ Py_ssize_t first_cmd_offset,
70
+ Py_ssize_t first_color_offset,
71
+ int chunk_pos,
72
+ size_t expected_size):
73
+ """
74
+ TODO: docstring
75
+ """
76
+
77
+ if ProcessDrawingCmdsVariant is SMXLayerVariant:
78
+ pass
79
+
80
+ # position in the command array, we start at the first command of this row
81
+ cdef Py_ssize_t dpos_cmd = first_cmd_offset
82
+
83
+ # is the end of the current row reached?
84
+ cdef bool eor = False
85
+
86
+ cdef uint8_t cmd = 0
87
+ cdef uint8_t lower_crumb = 0
88
+ cdef int pixel_count = 0
89
+
90
+ if ProcessDrawingCmdsVariant is SMXMainLayer8to5Variant
91
+ # Position in the pixel data array
92
+ cdef Py_ssize_t dpos_color = first_color_offset
93
+
94
+ # Position in the compression chunk.
95
+ cdef bool odd = chunk_pos
96
+ cdef int px_dpos = 0 # For loop iterator
97
+
98
+ cdef vector[uint8_t] pixel_data
99
+ pixel_data.reserve(4 )
100
+
101
+ # Pixel data temporary values that need further decompression
102
+ cdef uint8_t pixel_data_odd_0 = 0
103
+ cdef uint8_t pixel_data_odd_1 = 0
104
+ cdef uint8_t pixel_data_odd_2 = 0
105
+ cdef uint8_t pixel_data_odd_3 = 0
106
+
107
+ # Mask for even indices
108
+ # cdef uint8_t pixel_mask_even_0 = 0xFF
109
+ cdef uint8_t pixel_mask_even_1 = 0 b00000011
110
+ cdef uint8_t pixel_mask_even_2 = 0 b11110000
111
+ cdef uint8_t pixel_mask_even_3 = 0 b00111111
112
+
113
+ if ProcessDrawingCmdsVariant is SMXMainLayer4plus1Variant:
114
+ # Position in the pixel data array
115
+ cdef Py_ssize_t dpos_color = first_color_offset
116
+
117
+ # Position in the compression chunk
118
+ cdef uint8_t dpos_chunk = chunk_pos
119
+
120
+ cdef uint8_t palette_section_block = 0
121
+ cdef uint8_t palette_section = 0
122
+
123
+ if ProcessDrawingCmdsVariant is SMXShadowLayerVariant || ProcessDrawingCmdsVariant is SMXOutlineLayerVariant:
124
+ cdef uint8_t nextbyte = 0
125
+
126
+
127
+ # work through commands till end of row.
128
+ while not eor:
129
+ if row_data.size() > expected_size:
130
+ raise Exception (
131
+ f" Only {expected_size:d} pixels should be drawn in row {rowid:d} "
132
+ f" with layer type {self.info.layer_type:#x}, but we have {row_data.size():d} "
133
+ f" already!"
134
+ )
135
+
136
+ # fetch drawing instruction
137
+ cmd = data_raw[dpos_cmd]
138
+
139
+ # Last 2 bits store command type
140
+ lower_crumb = 0 b00000011 & cmd
141
+
142
+ if lower_crumb == 0 b00000011:
143
+ # eor (end of row) command, this row is finished now.
144
+ eor = True
145
+ dpos_cmd += 1
146
+
147
+ if ProcessDrawingCmdsVariant is SMXShadowLayerVariant:
148
+ # shadows sometimes need an extra pixel at
149
+ # the end
150
+ if row_data.size() < expected_size:
151
+ # copy the last drawn pixel
152
+ # (still stored in nextbyte)
153
+ #
154
+ # TODO: confirm that this is the
155
+ # right way to do it
156
+ row_data.push_back(pixel(color_shadow,
157
+ nextbyte, 0 , 0 , 0 ))
158
+ continue
159
+
160
+ elif lower_crumb == 0 b00000000:
161
+ # skip command
162
+ # draw 'count' transparent pixels
163
+ # count = (cmd >> 2) + 1
164
+
165
+ pixel_count = (cmd >> 2 ) + 1
166
+
167
+ for _ in range (pixel_count):
168
+ row_data.push_back(pixel(color_transparent, 0 , 0 , 0 , 0 ))
169
+
170
+ elif lower_crumb == 0 b00000001:
171
+ # color_list command
172
+ # draw the following 'count' pixels
173
+ # pixels are stored in 5 byte chunks
174
+ # even pixel indices have their info stored
175
+ # in byte[0] - byte[3]. odd pixel indices have
176
+ # their info stored in byte[1] - byte[4].
177
+ # count = (cmd >> 2) + 1
178
+
179
+ pixel_count = (cmd >> 2 ) + 1
180
+
181
+ if ProcessDrawingCmdsVariant is SMXMainLayer8to5Variant:
182
+ for _ in range (pixel_count):
183
+ # Start fetching pixel data
184
+ if odd:
185
+ # Odd indices require manual extraction of each of the 4 values
186
+
187
+ # Palette index. Essentially a rotation of (byte[1]byte[2])
188
+ # by 6 to the left, then masking with 0x00FF.
189
+ pixel_data_odd_0 = data_raw[dpos_color + 1 ]
190
+ pixel_data_odd_1 = data_raw[dpos_color + 2 ]
191
+ pixel_data.push_back((pixel_data_odd_0 >> 2 ) | (pixel_data_odd_1 << 6 ))
192
+
193
+ # Palette section. Described in byte[2] in bits 4-5.
194
+ pixel_data.push_back((pixel_data_odd_1 >> 2 ) & 0x03 )
195
+
196
+ # Damage mask 1. Essentially a rotation of (byte[3]byte[4])
197
+ # by 6 to the left, then masking with 0x00F0.
198
+ pixel_data_odd_2 = data_raw[dpos_color + 3 ]
199
+ pixel_data_odd_3 = data_raw[dpos_color + 4 ]
200
+ pixel_data.push_back(((pixel_data_odd_2 >> 2 ) | (pixel_data_odd_3 << 6 )) & 0xF0 )
201
+
202
+ # Damage mask 2. Described in byte[4] in bits 0-5.
203
+ pixel_data.push_back((pixel_data_odd_3 >> 2 ) & 0x3F )
204
+
205
+ row_data.push_back(pixel(color_standard,
206
+ pixel_data[0 ],
207
+ pixel_data[1 ],
208
+ pixel_data[2 ],
209
+ pixel_data[3 ]))
210
+
211
+ # Go to next pixel
212
+ dpos_color += 5
213
+
214
+ else :
215
+ # Even indices can be read "as is". They just have to be masked.
216
+ for px_dpos in range (4 ):
217
+ pixel_data.push_back(data_raw[dpos_color + px_dpos])
218
+
219
+ row_data.push_back(pixel(color_standard,
220
+ pixel_data[0 ],
221
+ pixel_data[1 ] & pixel_mask_even_1,
222
+ pixel_data[2 ] & pixel_mask_even_2,
223
+ pixel_data[3 ] & pixel_mask_even_3))
224
+
225
+ odd = not odd
226
+ pixel_data.clear()
227
+
228
+ if ProcessDrawingCmdsVariant is SMXMainLayer4plus1Variant:
229
+ palette_section_block = data_raw[dpos_color + (4 - dpos_chunk)]
230
+
231
+ for _ in range (pixel_count):
232
+ # Start fetching pixel data
233
+ palette_section = (palette_section_block >> (2 * dpos_chunk)) & 0x03
234
+ row_data.push_back(pixel(color_standard,
235
+ data_raw[dpos_color],
236
+ palette_section,
237
+ 0 ,
238
+ 0 ))
239
+
240
+ dpos_color += 1
241
+ dpos_chunk += 1
242
+
243
+ # Skip to next chunk
244
+ if dpos_chunk > 3 :
245
+ dpos_chunk = 0
246
+ dpos_color += 1 # Skip palette section block
247
+ palette_section_block = data_raw[dpos_color + 4 ]
248
+
249
+ if ProcessDrawingCmdsVariant is SMXShadowLayerVariant:
250
+ for _ in range (pixel_count):
251
+ dpos_color += 1
252
+ nextbyte = data_raw[dpos_color]
253
+
254
+ row_data.push_back(pixel(color_shadow,
255
+ nextbyte, 0 , 0 , 0 ))
256
+
257
+ if ProcessDrawingCmdsVariant is SMXOutlineLayerVariant:
258
+ # we don't know the color the game wants
259
+ # so we just draw index 0
260
+ row_data.push_back(pixel(color_outline,
261
+ 0 , 0 , 0 , 0 ))
262
+
263
+ if ProcessDrawingCmdsVariant is SMXMainLayer8to5Variant:
264
+ elif lower_crumb == 0 b00000010:
265
+ # player_color command
266
+ # draw the following 'count' pixels
267
+ # pixels are stored in 5 byte chunks
268
+ # even pixel indices have their info stored
269
+ # in byte[0] - byte[3]. odd pixel indices have
270
+ # their info stored in byte[1] - byte[4].
271
+ # count = (cmd >> 2) + 1
272
+
273
+ pixel_count = (cmd >> 2 ) + 1
274
+
275
+ for _ in range (pixel_count):
276
+ # Start fetching pixel data
277
+ if odd:
278
+ # Odd indices require manual extraction of each of the 4 values
279
+
280
+ # Palette index. Essentially a rotation of (byte[1]byte[2])
281
+ # by 6 to the left, then masking with 0x00FF.
282
+ pixel_data_odd_0 = data_raw[dpos_color + 1 ]
283
+ pixel_data_odd_1 = data_raw[dpos_color + 2 ]
284
+ pixel_data.push_back((pixel_data_odd_0 >> 2 ) | (pixel_data_odd_1 << 6 ))
285
+
286
+ # Palette section. Described in byte[2] in bits 4-5.
287
+ pixel_data.push_back((pixel_data_odd_1 >> 2 ) & 0x03 )
288
+
289
+ # Damage modifier 1. Essentially a rotation of (byte[3]byte[4])
290
+ # by 6 to the left, then masking with 0x00F0.
291
+ pixel_data_odd_2 = data_raw[dpos_color + 3 ]
292
+ pixel_data_odd_3 = data_raw[dpos_color + 4 ]
293
+ pixel_data.push_back(((pixel_data_odd_2 >> 2 ) | (pixel_data_odd_3 << 6 )) & 0xF0 )
294
+
295
+ # Damage modifier 2. Described in byte[4] in bits 0-5.
296
+ pixel_data.push_back((pixel_data_odd_3 >> 2 ) & 0x3F )
297
+
298
+ row_data.push_back(pixel(color_player,
299
+ pixel_data[0 ],
300
+ pixel_data[1 ],
301
+ pixel_data[2 ],
302
+ pixel_data[3 ]))
303
+
304
+ # Go to next pixel
305
+ dpos_color += 5
306
+
307
+ else :
308
+ # Even indices can be read "as is". They just have to be masked.
309
+ for px_dpos in range (4 ):
310
+ pixel_data.push_back(data_raw[dpos_color + px_dpos])
311
+
312
+ row_data.push_back(pixel(color_player,
313
+ pixel_data[0 ],
314
+ pixel_data[1 ] & pixel_mask_even_1,
315
+ pixel_data[2 ] & pixel_mask_even_2,
316
+ pixel_data[3 ] & pixel_mask_even_3))
317
+
318
+ odd = not odd
319
+ pixel_data.clear()
320
+
321
+ if ProcessDrawingCmdsVariant is SMXMainLayer4plus1Variant:
322
+ elif lower_crumb == 0 b00000010:
323
+ # player_color command
324
+ # draw the following 'count' pixels
325
+ # 4 pixels are stored in every 5 byte chunk.
326
+ # palette indices are contained in byte[0] - byte[3]
327
+ # palette sections are stored in byte[4]
328
+ # count = (cmd >> 2) + 1
329
+
330
+ pixel_count = (cmd >> 2 ) + 1
331
+
332
+ for _ in range (pixel_count):
333
+ # Start fetching pixel data
334
+ palette_section = (palette_section_block >> (2 * dpos_chunk)) & 0x03
335
+ row_data.push_back(pixel(color_player,
336
+ data_raw[dpos_color],
337
+ palette_section,
338
+ 0 ,
339
+ 0 ))
340
+
341
+ dpos_color += 1
342
+ dpos_chunk += 1
343
+
344
+ # Skip to next chunk
345
+ if dpos_chunk > 3 :
346
+ dpos_chunk = 0
347
+ dpos_color += 1 # Skip palette section block
348
+ palette_section_block = data_raw[dpos_color + 4 ]
349
+
350
+ else :
351
+ raise Exception (
352
+ f" unknown smx main graphics layer drawing command: " +
353
+ f" {cmd:#x} in row {rowid:d}"
354
+ )
355
+
356
+ # Process next command
357
+ dpos_cmd += 1
358
+ if ProcessDrawingCmdsVariant is SMXMainLayer8to5Variant || ProcessDrawingCmdsVariant is SMXMainLayer4plus1Variant:
359
+ return dpos_cmd, dpos_color, odd, row_data
360
+ if ProcessDrawingCmdsVariant is SMXOutlineLayerVariant || ProcessDrawingCmdsVariant is SMXShadowLayerVariant:
361
+ return dpos_cmd, dpos_cmd, chunk_pos, row_data
362
+
363
+
57
364
class SMX :
58
365
"""
59
366
Class for reading/converting compressed SMP files (delivered
0 commit comments