Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # Written with python 3.4.
- # TODO: format the various strings properly to account for used encodings and char sets.
- # Fair to note that isn't standardized now that the translation screwed it up...
- class course:
- def __init__(self, **kwargs):
- from collections import namedtuple
- self.Node = namedtuple("Node", ['xpos', 'ypos', 'zpos', 'width_l', 'width_r', 'track_attr', 'bank', 'pit', 'boost', 'dirt', 'ice', 'ramp', 'mine', 'gate', 'bg', 'sign'])
- nodes = kwargs.get('nodes', [])
- self.nodes = [self.Node(*i) for i in nodes]
- self.title = kwargs.get('title', '')
- self.scene = kwargs.get('scene', 0)
- self.sky = kwargs.get('sky', 0)
- self.music = kwargs.get('music', 0)
- # These are extensions found in fzep files.
- self.author = kwargs.get('author', '')
- self.flavor_e = kwargs.get('description_en', '')
- self.flavor_j = kwargs.get('description_jp', '')
- self.cartmusic = kwargs.get('cartmusic', 0)
- self.horizons = kwargs.get('horizons', [])
- def __bytes__(self):
- """Generates a CRSD from current data."""
- import struct
- pat = struct.Struct(">3f2hL")
- strips = ('both', 'left', 'right', 'center')
- others = ('center', 'left', 'right')
- out = bytearray()
- out.append(4)
- out.append(len(self.nodes))
- out.append(self.scene)
- out.append(self.sky)
- # Placeholder the checksum.
- out.extend(bytes(4))
- out.append(1)
- out.extend(struct.pack(">22s", self.title.encode()))
- out.append(self.music)
- bnks = list(bytes(64))
- pits = bytearray(64)
- bsts = bytearray(64)
- dirt = bytearray(64)
- slip = bytearray(64)
- jump = bytearray(64)
- mine = bytearray(64)
- gate = bytearray(64)
- objs = bytearray(64)
- sign = bytearray(64)
- for i,n in enumerate(self.nodes):
- out.extend(pat.pack(*n[0:6]))
- bnks[i] = n.bank
- pits[i] = 0xFF if n.pit is None else strips.index(n.pit)
- bsts[i] = 0xFF if n.boost is None else others.index(n.boost)
- dirt[i] = 0xFF if n.dirt is None else strips.index(n.dirt)
- slip[i] = 0xFF if n.ice is None else strips.index(n.ice)
- jump[i] = 0xFF if n.ramp is None else others.index(n.ramp)
- mine[i] = 0xFF if n.mine is None else others.index(n.mine)
- gate[i] = 0xFF if n.gate is None else n.gate
- objs[i] = 0xFF if n.bg is None else n.bg
- sign[i] = 0xFF if n.sign is None else n.sign
- # Pad for remaining unused nodes, then append the banks.
- out.extend(bytes(0x520 - len(out)))
- out.extend(struct.pack(">64h", *bnks))
- out.extend(pits)
- out.extend(bsts)
- out.extend(dirt)
- out.extend(slip)
- out.extend(jump)
- out.extend(mine)
- out.extend(gate)
- out.extend(objs)
- out.extend(sign)
- # Compute and set the checksum.
- out[4:8] = struct.pack(">L", checksum(out) & 0xFFFFFFFF)
- return bytes(out)
- @classmethod
- def from_crsd(cls, data, name=''):
- """Parses a CRSD file or original course data, returning a course instance.
- <data> should be either a bytes or bytearray object."""
- from struct import Struct
- node = Struct(">3f2hL")
- attr = Struct(">b63xb63xb63xb63xb63xb63xb63xb63xb")
- strips = ('both', 'left', 'right', 'center', None)
- others = ('center', 'left', 'right', None)
- n = []
- for i in range(data[1]):
- n.append(list(node.unpack_from(data, 32 + i * 20)))
- v = i << 1
- n[i].append(int.from_bytes(data[0x520 + v:0x522 + v], 'big', signed=True))
- v = attr.unpack_from(data, 0x5A0 + i)
- n[i].append(strips[v[0]])
- n[i].append(others[v[1]])
- n[i].append(strips[v[2]])
- n[i].append(strips[v[3]])
- n[i].append(others[v[4]])
- n[i].append(others[v[5]])
- n[i].append(None if v[6]<0 else v[6])
- n[i].append(None if v[7]<0 else v[7])
- n[i].append(None if v[8]<0 else v[8])
- if not name:
- name = data[9:data.find(b'\x00', 9, 0x1F)].decode()
- return cls(nodes=n, scene=data[2], sky=data[3], music=data[0x1F], title=name)
- @classmethod
- def from_fzep(cls, data):
- """Parses an fzep binary returning a course instance.
- <data> should be either a bytes or bytearray object."""
- import struct
- node = struct.Struct(">3f3h3B")
- hdr = struct.unpack_from(">6sH4x10B", data)
- others = ('center', 'left', 'right', None)
- tracktypes = (0x100001BF, 0x18000000, 0x08000040, 0x20000080,
- 0x000000C0, 0x00000100, 0x28000140, 0x080001C0)
- if hdr[0] != b"FZEPXT" or hdr[1] != 0x10A:
- raise TypeError("Not an FZEP v.1.10 file!")
- v = 22 + hdr[3]
- i = v + hdr[4]
- author = data[22:v].decode()
- # Not safe due to unused J font extension, but whatever.
- title = data[v:i].decode()
- v = i + hdr[5]
- dscr_e = data[i:v].decode()
- i = v + hdr[6]
- # Pretty sure you need to stick the leading half of each EUC JP wchar back on.
- dscr_j = data[v:i].decode()
- base = i + hdr[10] * 3
- h = []
- for j in range(i, base, 3):
- h.append([data[j], struct.unpack(">f", data[j+1:j+3]+b'\x00\x00')[0]])
- n = []
- for i in range(hdr[11]):
- v = node.unpack_from(data, base + i * 21)
- n.append([v[0], v[1], v[2], v[3], v[4], 0, v[5] & 0x1FF, None, None, None, None, None, None, None, None, None])
- j = (v[5] >> 9) & 7
- n[i][5] = tracktypes[j] | (v[6] >> 4)
- j = v[7] >> 2
- n[i][8] = others[v[7] & 3]
- n[i][11] = others[j & 3]
- j = v[7] >> 4
- n[i][12] = others[j & 3]
- j = v[7] >> 6
- n[i][13] = None if j==3 else j
- j = (v[5] >> 12) & 0xF
- n[i][14] = None if j==15 else j
- j = v[6] & 0xF
- n[i][15] = None if j==5 else j
- # This can probably be simplified.
- j = v[8] & 3
- if j == 1:
- n[i][7] = "right"
- elif j == 2:
- n[i][9] = "right"
- elif j == 3:
- n[i][10] = "right"
- j = (v[8] >> 2) & 3
- if j == 1:
- n[i][7] = "center"
- elif j == 2:
- n[i][9] = "center"
- elif j == 3:
- n[i][10] = "center"
- j = (v[8] >> 4) & 3
- if j == 1:
- n[i][7] = "left"
- elif j == 2:
- n[i][9] = "left"
- elif j == 3:
- n[i][10] = "left"
- j = v[8] & 0x33
- if j==0x11:
- n[i][7] = 'both'
- elif j==0x22:
- n[i][9] = 'both'
- elif j==0x33:
- n[i][10] = 'both'
- return cls(nodes=n, title=title, scene=hdr[9] & 0xF, sky=hdr[9]>>4, music=hdr[8],
- cartmusic=hdr[7], author=author, description_en=dscr_e,
- description_jp=dscr_j, horizons=h)
- def to_crsd(self):
- return bytes(self)
- def to_fzep(self):
- import struct
- tracks = (0x400006, 0x600000, 0x200001, 0x800002, 3, 4, 0xA00005, 0x200007)
- strips = ('both', 'left', 'right', 'center')
- others = ('center', 'left', 'right', None)
- pat = struct.Struct(">3f2h")
- out = bytearray(b'FZEPXT')
- out.append(1)
- out.append(10)
- out.extend(bytes(4))
- out.append(4)
- out.append(len(self.author))
- out.append(len(self.title))
- out.append(len(self.flavor_e))
- out.append(len(self.flavor_j))
- out.append(self.cartmusic)
- out.append(self.music)
- v = self.sky << 4
- v |= self.scene
- out.append(v)
- out.append(len(self.horizons))
- out.append(len(self.nodes))
- out.extend(self.author.encode())
- out.extend(self.title.encode())
- out.extend(self.flavor_e.encode())
- out.extend(self.flavor_j.encode())
- for n in self.horizons:
- out.append(n[0])
- out.extend(struct.pack(">f", n[1])[0:2])
- for n in self.nodes:
- out.extend(pat.pack(n.xpos, n.ypos, n.zpos, n.width_l, n.width_r))
- v = 0xF if n.bg is None else n.bg
- v <<= 3
- v |= tracks.index(n.track_attr >> 6)
- v <<= 1
- v |= n.bank >> 8
- out.append(v)
- out.append(n.bank & 0xFF)
- v = n.track_attr & 0xF
- v <<= 4
- v |= 5 if n.sign is None else n.sign
- out.append(v)
- v = 3 if n.gate is None else n.gate
- v <<= 2
- v |= others.index(n.mine)
- v <<= 2
- v |= others.index(n.ramp)
- v <<= 2
- v |= others.index(n.boost)
- out.append(v)
- v = 0
- if n.pit in ('left', 'both'):
- v |= 0x10
- elif n.dirt in ('left', 'both'):
- v |= 0x20
- elif n.ice in ('left', 'both'):
- v |= 0x30
- if n.pit == 'center':
- v |= 4
- elif n.dirt == 'center':
- v |= 8
- elif n.ice == 'center':
- v |= 0xC
- if n.pit in ('right', 'both'):
- v |= 1
- elif n.dirt in ('right', 'both'):
- v |= 2
- elif n.ice in ('right', 'both'):
- v |= 3
- out.append(v)
- return bytes(out)
- # Don't know for certain if these are static.
- # Values found before track info, at 800C3200.
- ekv = (0.699999988079071,
- 1.100000023841858,
- 5000.0,
- 15000.0,
- -15000.0,
- 2.200000047683716,
- 1.2000000476837158,
- 4.400000095367432,
- 0.8999999761581421,
- 0.800000011920929,
- 4.800000190734863)
- def checksum(crs, f=ekv):
- """Computes the checksum for CRSD <crs> using table of floats <f>."""
- from struct import Struct
- from array import array
- a = array("f", (0, 0, 0, 0))
- node = Struct(">3f2hL")
- attr = Struct(">b63xb63xb63xb63xb63xb63xb63xb63xb")
- crc = crs[1]
- y, u, l = int(f[2]) + 1, int(f[3]) + 1, int(f[4])
- if crs[0] != 4 or crs[31] > 14:
- return -1
- for i in range(crs[1]):
- xpos, ypos, zpos, width_l, width_r, track_attr = node.unpack_from(crs, 32 + 20*i)
- # Tests if all vertices in valid range and if not returns -1 as checksum.
- if xpos.__trunc__() not in range(l, u):
- return -1
- if ypos.__trunc__() not in range(-250, y):
- return -1
- if zpos.__trunc__() not in range(l, u):
- return -1
- a[0] = xpos
- a[1] = ypos
- a[2] = zpos
- track_attr &= ~0x4003E600
- track_attr *= 254 - i
- a[3] = i * f[6]
- a[3] += f[5]
- a[2] *= a[3]
- a[3] = i * f[8]
- a[3] += f[7]
- a[2] *= a[3]
- a[2] += width_l
- a[3] = i * f[0]
- a[3] += f[1]
- a[1] *= a[3]
- a[3] = i * f[9]
- a[3] += 5.5
- a[3] *= width_r
- a[3] *= f[10]
- a[0] += a[1]
- a[0] += a[2]
- a[0] += a[3]
- crc += a[0].__trunc__()
- v = i << 1
- crc += track_attr
- crc += int.from_bytes(crs[0x520 + v:0x522 + v], 'big', signed=True) * (0x93DE - v)
- v = attr.unpack_from(crs, 0x5A0 + i)
- crc += v[0] * i
- crc += v[1] * (i + 0x10)
- crc += v[2] * (i + 0x80)
- crc += v[3] * (i + 0x100)
- crc += v[4] * (i + 0x800)
- crc += v[5] * (i + 0x1000)
- crc += v[6] * (i + 0x8000)
- crc += v[7] * (i + 0x10000)
- crc += v[8] * (i + 0x80000)
- return int(crc) & 0xFFFFFFFF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement