Dernière activité 1 month ago

NX10/20 Parser of StepFever

Révision 65a2495318885cb45c2984bec826dfc75fbb22d3

nxdata.py Brut
1#!/usr/bin/env python
2from stepfever import *
3import time
4import struct
5
6SUPRESS_PRINT = False
7
8class NoteType:
9 Null = 0
10 Tap = 1
11 HoldHead = 2
12 HoldBody = 3
13 HoldTail = 4
14 RollHead = 5
15 Item = 6
16 Fake = 7
17 HoldHeadFake = 8
18 HoldBodyFake = 9
19 HoldTailFake = 10
20 RollHeadFake = 11
21 ItemFake = 12
22
23class NX10:
24
25 Null = 0x00
26 Fake = 0x73 # 0b00100011
27
28 Tap = 0xB3 # 0b01000011
29 HoldHead = 0xB4 # 0b01010111
30 HoldBody = 0xB6 # 0b01011011
31 HoldTail = 0xB7 # 0b01011111
32
33 HoldHeadFake = 0x74 # 0b00110111
34 HoldBodyFake = 0x76 # 0b00111011
35 HoldTailFake = 0x77 # 0b00111111
36 Item = 0xF1 # 0b01100001
37
38
39class NX20:
40
41 Null = 0x00
42
43 Effect = 0x41 # 0b01000001
44 DivBrain = 0x42 # 0b01000010
45 Fake = 0x23 # 0b00100011
46 Tap = 0x43 # 0b01000011
47 HoldHeadFake = 0x37 # 0b00110111
48 HoldHead = 0x57 # 0b01010111
49 HoldBodyFake = 0x3b # 0b00111011
50 HoldBody = 0x5B # 0b01011011
51 HoldTailFake = 0x3f # 0b00111111
52 HoldTail = 0x5F # 0b01011111
53 FakeItem = 0x21 # 0b00100001
54 Item = 0x61 # 0b01100001
55 Row = 0x80 # 0b10000000
56
57 MetaMissionLevel = 1000
58 MetaChartLevel = 1001
59
60 NoteSeedAction = 0
61 NoteSeedShield = 1
62 NoteSeedChange = 2
63 NoteSeedAcceleration = 3
64 NoteSeedFlash = 4
65 NoteSeedMineTap = 5
66 NoteSeedMineHold = 6
67 NoteSeedAttack = 7
68 NoteSeedDrain = 8
69 NoteSeedHeart = 9
70 NoteSeedSpeed2 = 10
71 NoteSeedRandom = 11
72 NoteSeedSpeed3 = 12
73 NoteSeedSpeed4 = 13
74 NoteSeedSpeed8 = 14
75 NoteSeedSpeed1 = 15
76 NoteSeedPotion = 16
77 NoteSeedRotate0 = 17
78 NoteSeedRotate90 = 18
79 NoteSeedRotate180 = 19
80 NoteSeedRotate270 = 20
81 NoteSeedSpeed_ = 21
82 NoteSeedBomb = 22
83 NoteSeedHyperPotion = 23
84
85NOTE_NX202SFC = {
86 NX20.Null : NoteType.Null,
87 NX20.Fake : NoteType.Fake,
88 NX20.Tap : NoteType.Tap,
89 NX20.HoldHead : NoteType.HoldHead,
90 NX20.HoldHeadFake : NoteType.HoldHeadFake,
91 NX20.HoldBody : NoteType.HoldBody,
92 NX20.HoldBodyFake : NoteType.HoldBodyFake,
93 NX20.HoldTail : NoteType.HoldTail,
94 NX20.FakeItem : NoteType.ItemFake,
95 NX20.Item : NoteType.Item
96 }
97
98NOTE_NX102SFC = {
99 NX10.Null : NoteType.Null,
100 NX10.Fake : NoteType.Fake,
101 NX10.Tap : NoteType.Tap,
102 NX10.HoldHead : NoteType.HoldHead,
103 NX10.HoldHeadFake : NoteType.HoldHeadFake,
104 NX10.HoldBody : NoteType.HoldBody,
105 NX10.HoldBodyFake : NoteType.HoldBodyFake,
106 NX10.HoldTail : NoteType.HoldTail,
107 NX10.Item : NoteType.Item
108 }
109
110MODE_NX202SFC = {
111 5 : ChartType.PUMP_SINGLE,
112 10 : ChartType.PUMP_DOUBLE
113 }
114
115def ParseStep(stepdata):
116 if not stepdata[0] in NOTE_NX202SFC or stepdata[0] == NX20.Effect:
117 return {"type":NoteType.Null,"seed": 0,"skin":0,"trigger":0}
118 else:
119 return {"type":NOTE_NX202SFC[stepdata[0]],"seed": stepdata[1],"skin":stepdata[2],"trigger":0}
120
121
122def ParseStepNX10(stepdata):
123 if not stepdata[0] in NOTE_NX102SFC:
124 return {"type":NoteType.Null,"seed": 0,"skin":0,"trigger":0}
125 else:
126 return {"type":NOTE_NX102SFC[stepdata[0]],"seed": stepdata[1],"skin":stepdata[2],"trigger":0}
127
128def GetChartData(file):
129 out = {"level":-1,"type":ChartType.PUMP_SINGLE,"color" : (255,255,255),"added":time.time(), "title":"Converted by Tool","author":"nx2sfm", "file" : file}
130 f = open(file,"rb")
131 head = f.read(20)
132 out["nxtype"] = head[:4]
133 if head[:4] == "NX20":
134 startcolumn = struct.unpack("I", head[4:8])[0]
135 numcolumns = struct.unpack("I", head[8:12])[0]
136 lightmap = struct.unpack("I", head[12:16])[0]
137 metablocks = struct.unpack("I", head[16:20])[0]
138 out["type"] = MODE_NX202SFC[numcolumns]
139 for i in range(metablocks):
140 data = f.read(8)
141 idx = struct.unpack("I", data[:4])[0]
142 value = struct.unpack("I", data[4:8])[0]
143 if idx == NX20.MetaChartLevel:
144 out["level"] = value
145 break
146 elif head[:4] == "NX10":
147 nxversion = struct.unpack("4s", head[0:4])[0]
148 startcolumn = struct.unpack("I", head[4:8])[0]
149 numcolumns = struct.unpack("I", head[8:12])[0]
150 splits = struct.unpack("I", head[12:16])[0]
151 lightmap = startcolumn > 9
152 out["type"] = MODE_NX202SFC[numcolumns]
153 else:
154 print "NXData::GetChartData() - Invalid file magic: %s" %head[:4]
155 f.close()
156 return out
157
158def ParseChartNX20(chart):
159 print "Parsing NX20 - %s" %chart["file"]
160 f = open(chart["file"], "rb")
161 mode = chart["type"]
162 startbpm = -1
163 bpmchanges = []
164 rushchanges = []
165 sfchanges = []
166 sschanges = []
167 effects = []
168 tcchanges = []
169 warps = []
170 bgchanges = []
171 rows = []
172 lastbpm = -1
173
174 beat = 0
175 time = 0
176 head = f.read(20)
177 nxversion = struct.unpack("4s", head[0:4])[0]
178 startcolumn = struct.unpack("I", head[4:8])[0]
179 numcolumns = struct.unpack("I", head[8:12])[0]
180 lightmap = struct.unpack("I", head[12:16])[0]
181 metablocks = struct.unpack("I", head[16:20])[0]
182
183 # Lets just skip the metadatablocks for now:
184 junk = f.read(8*metablocks)
185 numsplits = struct.unpack("I", f.read(4))[0]
186 lastsf = None
187 lastss = None
188
189 if not SUPRESS_PRINT:
190 print "Number of splits: %s" %numsplits
191 for s in range(numsplits):
192 systemselected = struct.unpack("I", f.read(4))[0]
193 metablocks = struct.unpack("I", f.read(4))[0]
194
195 # We will just skip the metadatablocks from split:
196 junk = f.read(8*metablocks)
197
198 numblocks = struct.unpack("I", f.read(4))[0]
199 if not SUPRESS_PRINT:
200 print " System Selected: %s" %systemselected
201 print " Number of blocks: %s" %numblocks
202 # Currently, I dont know how System Selected works, but when System Selected != 0, the machine seens to pick a random block.
203 # This converter is just for test, so we will pick the random block here on convert
204
205 SelectedBlock = 0 if systemselected == 0 else ((int)(random()*numblocks))
206 if not SUPRESS_PRINT:
207 print " Block Picked: %s" %SelectedBlock
208
209 for blk in range(0,numblocks):
210 steptime = struct.unpack("f", f.read(4))[0]
211 bpm = struct.unpack("f", f.read(4))[0]
212 mysteryblock = struct.unpack("f", f.read(4))[0]
213 delay = struct.unpack("f", f.read(4))[0]
214 speed = struct.unpack("f", f.read(4))[0]
215 beatsplit = struct.unpack("B", f.read(1))[0]
216 beatmeasure = struct.unpack("B", f.read(1))[0]
217 smoothspeed = struct.unpack("B", f.read(1))[0]
218 unknownflag = struct.unpack("B", f.read(1))[0]
219 divisionconds = struct.unpack("I", f.read(4))[0]
220 freeze = (speed<0);
221 bps = bpm / 60.0
222
223 startbpm = bpm if startbpm == -1 else startbpm
224
225 # We will skip division conditions too
226 junk = f.read(8*divisionconds)
227
228 numrows = struct.unpack("I", f.read(4))[0]
229 if SelectedBlock != blk:
230 if not SUPRESS_PRINT:
231 print " Skipping not selected block %s" %blk
232 for n in range(numrows):
233 rowt = struct.unpack("BBBB", f.read(4))[0]
234 if rowt[0] != NX20.Row:
235 junk = f.read(4*numcolumns)
236
237
238 else:
239 speed = abs(speed)
240 if not SUPRESS_PRINT:
241 print " CurrentTime: %s" %time
242 print " CurrentBeat: %s" %beat
243 print " Block(%s)" % blk
244 print " StepTime: %s" %steptime
245 print " BPM: %s" %bpm
246 print " Mystery Block: %s" %mysteryblock
247 print " Delay: %s" %delay
248 print " Freeze: %s"%freeze
249 print " Speed: %s"%speed
250 print " Beat Split: %s"%beatsplit
251 print " Beat Measure: %s"%beatmeasure
252 print " Smooth Speed: %s"%smoothspeed
253 print " Unknown Flag: %s"%unknownflag
254 print " Division Conditions: %s" %divisionconds
255 print " Number of Rows: %s" %numrows
256
257 # BPM Changes
258 if lastbpm != bpm:
259 bpmchanges.append({"time": steptime/1000, "bpm" : bpm})
260 lastbpm = bpm
261 delay = delay / 1000
262
263 # Delays
264 beat += delay * bps
265 time += delay
266
267 # ScrollFactor
268 sf = mysteryblock * beatsplit
269 lastsf = {"beat" : beat, "factor" : sf, "smooth" : smoothspeed>0, "smoothtime" : steptime}
270 if lastsf != None:
271 lastsf["smoothtime"] = (steptime - lastsf["smoothtime"]) / 1000
272 sfchanges.append(lastsf)
273 if not SUPRESS_PRINT:
274 print " Adding Scrollfactor Change - Factor: %s Smooth: %s Beat: %s SmoothTime: %s" %(lastsf["factor"],lastsf["smooth"],lastsf["beat"],lastsf["smoothtime"])
275
276 lastsf = {"beat" : beat, "factor" : sf, "smooth" : smoothspeed>0, "smoothtime" : steptime}
277
278 if lastss != None:
279 lastss["smoothtime"] = (steptime - lastss["smoothtime"]) / 1000
280 sschanges.append(lastss)
281 if not SUPRESS_PRINT:
282 print " Adding ScrollSpeed Change - Factor: %s Smooth: %s Beat: %s SmoothTime: %s" %(lastss["factor"],lastss["smooth"],lastss["beat"],lastss["smoothtime"])
283
284 lastss = {"beat" : beat, "factor" : speed, "smooth" : smoothspeed>0, "smoothtime" : steptime}
285
286 if s == numsplits-1:
287 if not SUPRESS_PRINT:
288 print " Adding last ss"
289 sschanges.append(lastss)
290
291 # Check for warps
292 if round(steptime*1000) != round(time*1000*1000):
293 # Lets do an warp!
294 WarpBeat = ((steptime/1000) - time ) * bps + beat;
295 if not SUPRESS_PRINT:
296 print " Time Warp from %s ms to %s ms from beat %s to beat %s" %(steptime,time*1000,WarpBeat,beat)
297 #warps.append({"beatFrom":beat,"beatTo":WarpBeat})
298 warps.append({"time":(steptime/1000),"beatTo":beat})
299
300 # Process Rows
301 for n in range(numrows):
302 notes = []
303 rowt = struct.unpack("BBBB",f.read(4))
304 if rowt[0] != NX20.Row: # If its not an empty row
305 notedata = ParseStep(rowt)
306 notedata["beat"] = beat
307 notes.append(notedata)
308 for k in range(numcolumns-1):
309 rowt = struct.unpack("BBBB",f.read(4))
310 notedata = ParseStep(rowt)
311 notedata["beat"] = beat
312 notes.append(notedata)
313 if len(notes) > 0:
314 rows.append({"beat":beat, "notes":notes})
315 beat += 1.0 / beatsplit
316 time += 1.0 / (beatsplit * bps)
317 f.close()
318 data = GenSFC(chart["id"], chart["level"], chart["type"], startbpm, chart["title"], chart["author"], bpmchanges, warps, rushchanges, sfchanges, sschanges, tcchanges, effects, bgchanges, rows)
319 return data
320
321def ParseChartNX10(chart):
322 print "Parsing NX10 %s" %chart["file"]
323 f = open(chart["file"], "rb")
324 mode = chart["type"]
325
326 startbpm = -1
327 bpmchanges = []
328 rushchanges = []
329 sfchanges = []
330 sschanges = []
331 effects = []
332 tcchanges = []
333 warps = []
334 bgchanges = []
335 rows = []
336 lastbpm = -1
337 beat = 0
338 time = 0
339
340
341 head = f.read(16)
342 nxversion = struct.unpack("4s", head[0:4])[0]
343 startcolumn = struct.unpack("I", head[4:8])[0]
344 numcolumns = struct.unpack("I", head[8:12])[0]
345 numsplits = struct.unpack("I", head[12:16])[0]
346 lightmap = startcolumn > 9
347
348 lastsf = None
349 lastss = None
350
351 if not SUPRESS_PRINT:
352 print "Format: %s - Start Column %d - Num Columns: %d - Splits: %d" %(nxversion,startcolumn,numcolumns,numsplits)
353 splitsd = f.read(4*numsplits)
354 splitsd = struct.unpack("%sI"%numsplits,splitsd)
355
356 for s in range(numsplits):
357 file_pos = splitsd[s]
358 f.seek(file_pos)
359 blocks = struct.unpack("I", f.read(4))[0]
360 if not SUPRESS_PRINT:
361 print "\tNumber of blocks %s" %blocks
362 blocksd = struct.unpack("%sI"%blocks, f.read(4*blocks))
363 for z in range(blocks):
364 f.seek(blocksd[z])
365 steptime = struct.unpack("f", f.read(4))[0]
366 bpm = struct.unpack("f", f.read(4))[0]
367 mysteryblock = struct.unpack("f", f.read(4))[0]
368 delay = struct.unpack("f", f.read(4))[0]
369 speed = struct.unpack("f", f.read(4))[0]
370 divisionconds = struct.unpack("I", f.read(4))[0]
371 beatsplit = struct.unpack("H", f.read(2))[0]
372 beatmeasure = struct.unpack("B", f.read(1))[0]
373 smoothspeed = struct.unpack("B", f.read(1))[0]
374 numrows = struct.unpack("I", f.read(4))[0]
375 freeze = (speed<0);
376 bps = bpm / 60.0
377 startbpm = bpm if startbpm == -1 else startbpm
378 if not SUPRESS_PRINT:
379 print "\t Block(%s)" % z
380 print "\t StepTime: %s" %steptime
381 print "\t BPM: %s" %bpm
382 print "\t Mystery Block: %s" %mysteryblock
383 print "\t Delay: %s" %delay
384 print "\t Freeze: %s" %freeze
385 print "\t Speed: %s" %speed
386 print "\t Beat Split: %s" %beatsplit
387 print "\t Beat Measure: %s" %beatmeasure
388 print "\t Smooth Speed: %s" %smoothspeed
389 print "\t Number of Rows: %s" %numrows
390
391 if divisionconds > 0:
392 # Skip that conditions
393 divd = f.read(4*10*2)
394
395 if lightmap:
396 if numrows > 0:
397 lmpd = f.read(4*numrows)
398 else:
399 speed = abs(speed)
400 # BPM Changes
401 if lastbpm != bpm:
402 bpmchanges.append({"time": steptime/1000, "bpm" : bpm})
403 lastbpm = bpm
404 # Delays
405 if time == 0:
406 time = steptime
407 beat = steptime * bps
408 else:
409 beat += delay * bps
410 time += delay
411
412 # ScrollFactor
413 sf = mysteryblock * beatsplit
414 lastsf = {"beat" : beat, "factor" : sf, "smooth" : smoothspeed>0, "smoothtime" : steptime}
415 if lastsf != None:
416 lastsf["smoothtime"] = (steptime - lastsf["smoothtime"]) / 1000
417 sfchanges.append(lastsf)
418 if not SUPRESS_PRINT:
419 print " Adding Scrollfactor Change - Factor: %s Smooth: %s Beat: %s SmoothTime: %s" %(lastsf["factor"],lastsf["smooth"],lastsf["beat"],lastsf["smoothtime"])
420
421 lastsf = {"beat" : beat, "factor" : sf, "smooth" : smoothspeed>0, "smoothtime" : steptime}
422
423 if lastss != None:
424 lastss["smoothtime"] = (steptime - lastss["smoothtime"]) / 1000
425 sschanges.append(lastss)
426 if not SUPRESS_PRINT:
427 print " Adding ScrollSpeed Change - Factor: %s Smooth: %s Beat: %s SmoothTime: %s" %(lastss["factor"],lastss["smooth"],lastss["beat"],lastss["smoothtime"])
428
429 lastss = {"beat" : beat, "factor" : speed, "smooth" : smoothspeed>0, "smoothtime" : steptime}
430
431 if s == numsplits-1:
432 if not SUPRESS_PRINT:
433 print " Adding last ss"
434 sschanges.append(lastss)
435
436 # Check for warps
437 if round(steptime/1000) != round(time):
438 # Lets do an warp!
439 WarpBeat = ((steptime/1000) - time ) * bps + beat;
440 if not SUPRESS_PRINT:
441 print " Time Warp from %s ms to %s ms from beat %s to beat %s" %(steptime/1000,time,WarpBeat,beat)
442 #warps.append({"beatFrom":beat,"beatTo":WarpBeat})
443 warps.append({"time":(steptime/1000),"beatTo":beat})
444
445 if numrows > 0:
446 rowd = struct.unpack("%sI"%numrows, f.read(4*numrows))
447 for r in range(numrows):
448 notes = []
449 if rowd[r] > 0:
450 f.seek(rowd[r])
451 for p in range(numcolumns):
452 data = struct.unpack("2B", f.read(2))
453 #{"type":NOTE_NX102SFC[stepdata[0]],"seed": stepdata[1],"skin":stepdata[2],"trigger":0}
454 notedata = ParseStepNX10([data[0], data[1] & 0xFC, data[1] & 0x03])
455 notedata["beat"] = beat
456 notes.append(notedata)
457 rows.append({"beat":beat, "notes":notes})
458 if bps != 0:
459 beat += 1.0 / beatsplit
460 time += 1.0 / (beatsplit * bps)
461 f.close()
462 data = GenSFC(chart["id"], chart["level"], chart["type"], startbpm, chart["title"], chart["author"], bpmchanges, warps, rushchanges, sfchanges, sschanges, tcchanges, effects, bgchanges, rows)
463 return data