-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpxattr.py
More file actions
305 lines (259 loc) · 12.6 KB
/
pxattr.py
File metadata and controls
305 lines (259 loc) · 12.6 KB
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
import os
os.environ["PYSDL2_DLL_PATH"] = "./"
from re import M
from constants import *
# from constants import *
class pxattrProcess:
def __init__(self):
self.path = ''
self.savePath = 'NULL.pxattr'
self.fileName = '.pxattr'
self.header = b''
self.version = 255 #0 = Kero Blaster; 1 = Pitest; 2 = Pre 0.116 Starfrog/Rockfish 2011
self.width = 16
self.height = 16
self.compression = 0
self.attributes = []
self.attributeArray = [[]]
self.attributeArrayEdit = [[]] #pxattr from memory
@staticmethod
def UInt(x, file): #int from read file
return int.from_bytes(file.read(x), 'little')
def pxOpen(self, path): #opens - reads file - converts to list.
with open(path, 'rb') as file:
self.fileName = os.path.basename(path)
const.editWindowName('pxAttribute: {path}'.format(path=self.fileName))
self.savePath = path
self.header = file.read(8) #TODO: include starfrog 0.103 and 0.104 support. (Also Rockfish support I think.)
if self.header == b'kmMAP02\x00':
self.version = 1
const.editPxRes(1)
const.placeOdd = False#kerom
self.width = self.UInt(2, file)
self.height = self.UInt(2, file)
self.compression = self.UInt(1, file)
elif self.header == b'pxMAP01\x00':
self.version = 0
const.editPxRes(2)
const.placeOdd = True
self.width = self.UInt(2, file)
self.height = self.UInt(2, file)
self.compression = self.UInt(1, file)
#kero blaster
else:
self.width = int.from_bytes(self.header[0:2], 'little')
self.height = int.from_bytes(self.header[2:4], 'little')
#get file size.
file.seek(0, os.SEEK_END)
remainingFileSize = file.tell() - 4
if self.width*self.height == remainingFileSize:
self.version = 2
self.compression = 0
const.editPxRes(2)
const.placeOdd = True
file.seek(4)
else:
raise ValueError("bad header")
self.size = self.width*self.height
if self.size < 1: raise ValueError('ERROR: pxattr size is invalid.')
attributes = self.pxRead(self.size, self.compression, self.version, file)
file.close()
# const.editing = True
if self.compression == 2: #Vertical
self.attributeArray = self.pxArray(attributes, False)
# self.attributes = [attribute for sublist in self.attributeArray for attribute in sublist] #flattens array.
else:
self.attributeArray = self.pxArray(attributes, False) #convert lists to arrays
def pxCreate(self, fileName, version, width, height): #assigns class attributes - creates file.
self.path = fileName
self.savePath = fileName
self.version = version
if version == 0:
self.header = b'pxMAP01\x00'
elif version == 1:
self.header = b'kmMAP02\x00'
elif version == 2:
self.header = b''
self.width = width
self.height = height
self.attributeArray = [[0 for x in range(self.width)] for y in range(self.height)]
self.attributeArrayEdit = self.attributeArray
#create file:
# self.pxWrite(self.version, self.compression)
def pxRead(self, size, compression, version, file): #Read pxattr attribute data.
if compression != 0: file.read(4)
attributes = []
if version == 0 or version == 2: #Kero Blaster
if compression == 0:
attributes = list(file.read())
else: #Kero Blaster Compression
while len(attributes) < size:
attributes.extend([self.UInt(1, file)]*self.UInt(1, file))
elif version == 1: #KeroM
attribute = file.read(2)
if compression == 0:
repeat = 0
while len(attributes) < size:
nextAttribute = file.read(2)
repeat += 1
if nextAttribute != attribute: #in kerom attributes have xy ids.
attributes.extend([attribute[0]+(attribute[1]*16)]*repeat) #(attribute[0] is x and [1] is y)
attribute = nextAttribute; repeat = 0 #move to next attribute, reset repeat.
else: #KeroM Compression
while len(attributes) < self.size:
repeat = self.UInt(2, file)
attributes.extend([attribute[0]+(attribute[1]*16)]*repeat)
attribute = file.read(2)
return attributes
def pxEdit(self, attribute):
#[0] tile, [1] inner tuple with x and y info
#subslice into array with tuple in [y][x] format.
self.attributeArrayEdit[mouse.tilePos[1]][mouse.tilePos[0]] = attribute
def pxResize(self, sizeX, sizeY):
if sizeX == self.width and sizeY == self.height: #size is same
return
#width
if self.width > sizeX: #sizeX less than width
for row in self.attributeArrayEdit:
del row[sizeX:]
elif self.width < sizeX: #sizeX more than width
for row in self.attributeArrayEdit:
row.extend([0] * (sizeX - self.width))
# Resize height
if self.height > sizeY: #sizeY less than height
del self.attributeArrayEdit[sizeY:]
elif self.height < sizeY: #sizeY more than height
for _ in range(sizeY - self.height):
self.attributeArrayEdit.append([0] * sizeX)
self.width = sizeX
self.height = sizeY
#TEMP:
def pxSave(self):
if self.savePath[-7:] != '.pxattr':
self.savePath += '.pxattr'
with open(self.savePath, 'wb') as file:
if self.version == 1:
file.write(b'kmMAP02\x00')
elif self.version == 0:
file.write(b'pxMAP01\x00')
#doesn't write anything for header if version 2
file.write(self.width.to_bytes(2, 'little'))
file.write(self.height.to_bytes(2, 'little'))
if self.version in range(0, 2): #rockfish doesnt have tile compression
file.write(self.compression.to_bytes(1, 'little'))
if self.version == 1 and self.compression == 0: #keroM
for attributes in self.attributeArrayEdit:
for attribute in attributes:
file.write((attribute%16).to_bytes(1)+(attribute//16).to_bytes(1))
elif self.compression != 0: #if compression exists
compressedAttributes = self.pxCompress(self.version, self.compression)
file.write(len(compressedAttributes).to_bytes(4, 'little')+bytes(compressedAttributes))#writing byte size and bytes
else: #kero blaster no compression
for attributes in self.attributeArrayEdit:
file.write(bytes(attributes))
file.close()
def pxSaveAs(self):
savePath = filedialpy.saveFile(initial_dir="./",
initial_file=self.fileName,
title="Save pxAttr file.",
filter=["*.pxattr"]
)
if savePath != '':
self.savePath = savePath
self.pxSave()
else:
return
def pxWrite(self, game, compression):
with open(self.savePath, 'wb') as file:
if game == 0:
file.write(b'pxMAP01\x00')
elif game == 1:
file.write(b'kmMAP02\x00')
file.write(self.width.to_bytes(2, 'little'))
file.write(self.height.to_bytes(2, 'little'))
if game in range(0, 2):
file.write(compression.to_bytes(1, 'little'))
if game == 1 and compression == 0: #keroM
for attributes in self.attributeArrayEdit:
for attribute in attributes:
file.write((attribute%16).to_bytes(1)+(attribute//16).to_bytes(1))
elif compression != 0: #if compression exists
compressedAttributes = self.pxCompress(game, compression)
file.write(len(compressedAttributes).to_bytes(4, 'little')+bytes(compressedAttributes))#writing byte size and bytes
else: #kero blaster no compression/rockfish
for attributes in self.attributeArrayEdit:
file.write(bytes(attributes))
file.close()
def pxArray(self, attributes, split): #converts list to array or array to list
if split:
pass
else:
if self.compression == 2: #compression 2 = vertical. Shuffles around entries so it's layed out correctly.
return [attributes[i::self.height] for i in range(self.height)]
else: #horizontal or no compression:
return [attributes[i:i+self.width] for i in range(0, self.size, self.width)]
def pxCompress(self, game, compression):
def processAttr(RLE, attributes, intAttribute, repeat, b):
if intAttribute != attributes or (repeat == 256 and game == 0 or repeat == 65536):
if game == 1:
RLE += (attributes % 16).to_bytes(1)+(attributes// 16).to_bytes(1) #if keroM add xy id
else:
RLE += attributes.to_bytes(1) #add indexed id
if repeat > 65535 and game == 1: #uInt16
return RLE+b'\xff\xff', intAttribute, 0
elif repeat > 255 and game == 0: #uInt8
return RLE+b'\xff', intAttribute, 0
return RLE+repeat.to_bytes(b, 'little'), intAttribute, 0 #edited RLE, move attribute to next tile, repeat = 0.
return RLE, attributes, repeat #return unedited
bValue = 2 if game == 1 else 1 #read 2 bytes if kerom
attribute = self.attributeArray[0][0] #start value
RLE = b''
repeat = 0
if compression == 1: #horizontal
for row in self.attributeArray:
for intAttribute in row:
RLE, attribute, repeat = processAttr(RLE, attribute, intAttribute, repeat, bValue)
repeat += 1
else: #vertical
for x in range(self.width):
for y in range(self.height):
RLE, attribute, repeat = processAttr(RLE, attribute, self.attributeArray[y][x], repeat, bValue)
repeat += 1
#last tile:
if game == 1:
RLE += (attribute%16).to_bytes(1)+(attribute//16).to_bytes(1)+repeat.to_bytes(2, 'little')
else:
RLE += attribute.to_bytes(1)+repeat.to_bytes(1)
return RLE
def findAttributeText(attribute):
os.chdir(os.path.dirname(os.path.abspath(__file__)))
if pxattr.version == 0:
path = paths.attributeInfoKB
elif pxattr.version == 1:
path = paths.attributeInfoPT
else:
path = paths.attributeInfo2012
with open(path, 'r+') as file:
if attribute >= 100:
iAmount = 3
elif attribute >= 10:
iAmount = 2
else:
iAmount = 1
while True:
line = file.readline()
if line[0] == '$':
def fetchNumber(line, index):
strg = ''
for char in line[index:]:
if char == '-' or char == ' ':
break
strg += char
return strg
lowRange = fetchNumber(line, 1)
highRange = fetchNumber(line, len(lowRange)+2)
if attribute in range(int(lowRange), int(highRange)+1):
return line[len(lowRange) + len(highRange) + 3:]
elif line[0:iAmount] == str(attribute):
return line[iAmount+1:-1]
pxattr = pxattrProcess()