Advertisement
Zoinkity

Doom64 LZSS codec

Jan 18th, 2019
381
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.80 KB | None | 0 0
  1. def decode(data) -> bytes:
  2.     """Decompress bytes-like data, returning a bytes object."""
  3.     buf = iter(data)
  4.     out = bytearray()
  5.     cmd = 1
  6.     while True:
  7.         if cmd == 1:
  8.             cmd = next(buf) | 0x100
  9.         if cmd & 1:
  10.             p = next(buf) << 4
  11.             b = next(buf)
  12.             p |= (b >> 4)
  13.             b &= 0xF
  14.             if not b:
  15.                 break
  16.             p += 1
  17.             for i in range(b+1):
  18.                 out.append(out[-p])
  19.         else:
  20.             out.append(next(buf))
  21.         cmd >>= 1
  22.     return bytes(out)
  23.  
  24.  
  25. def length(data) -> (int, int):
  26.     """Returns a tuple (compressed size, decompressed size),
  27.     basically by running it through decompression."""
  28.     cmd = 1
  29.     out, pos = 0, 0
  30.     while True:
  31.         if cmd == 1:
  32.             cmd = data[pos] | 0x100
  33.             pos += 1
  34.         if cmd & 1:
  35.             b = data[pos+1]
  36.             pos += 2
  37.             b &= 0xF
  38.             if not b:
  39.                 break
  40.             out += b + 1
  41.         else:
  42.             out += 1
  43.             pos += 1
  44.         cmd >>= 1
  45.     return (pos, out)
  46.  
  47.  
  48. def _search(data, pos, sz, shoe=2, cap = 0x10, window=0x1000):
  49.     """Returns (position, length) for sz length substring at pos.
  50.     To recycle this code readily, shoe and cap set the min and max lengths,
  51.     and window adjusts the maximum distance.  More cost-effective done here."""
  52.     ml = min(cap, sz - pos)
  53.     if ml < shoe:
  54.         return 0, 0
  55.     mp = max(0, pos - window)
  56.     hitp, hitl = 0, shoe
  57.     if mp < pos:
  58.         hl = data[mp:pos+hitl].find(data[pos:pos+hitl])
  59.         while hl < (pos - mp):
  60.             while (hitl < ml) and (data[pos + hitl] == data[mp + hl + hitl]):
  61.                 hitl += 1
  62.             mp += hl
  63.             hitp = mp
  64.             if hitl == ml:
  65.                 return hitp, hitl
  66.             mp += 1
  67.             hitl += 1
  68.             if mp >= pos:
  69.                 break
  70.             hl = data[mp:pos+hitl].find(data[pos:pos+hitl])
  71.     # If length less than minimum length, return miss.
  72.     if hitl <= shoe:
  73.         hitl = 1
  74.     return hitp, hitl-1
  75.  
  76. def encode(data) -> bytes:
  77.     """Encodes data, returning a bytes object."""
  78.     sz = len(data)
  79.     out = bytearray(1)
  80.     c, cmds = 0, 0
  81.     pos, flag = 0, 1
  82.     while pos < sz:
  83.         hitp, hitl = _search(data, pos, sz)
  84.         if hitl < 2:
  85.             # Push a raw if copying isn't possible.
  86.             out.append(data[pos])
  87.             pos += 1
  88.         else:
  89.             # If the shoe fits, wear it...but check for a better fit ;*)
  90.             tstp, tstl = _search(data, pos+1, sz)
  91.             if (hitl + 1) < tstl:
  92.                 out.append(data[pos])
  93.                 pos += 1
  94.                 flag <<= 1
  95.                 if flag == 0x100:
  96.                     out[cmds] = c
  97.                     c, flag = 0, 1
  98.                     cmds = len(out)
  99.                     out.append(0)
  100.                 hitl = tstl
  101.                 hitp = tstp
  102.             c |= flag
  103.             e = pos - hitp - 1
  104.             e &= 0xFFF
  105.             pos += hitl
  106.             hitl -= 1
  107.             e <<= 4
  108.             e |= hitl & 0xF
  109.             out.append(e >> 8)
  110.             out.append(e & 0xFF)
  111.         # Advance the flag and refill if required.
  112.         flag <<= 1
  113.         if flag == 0x100:
  114.             out[cmds] = c
  115.             c, flag = 0, 1
  116.             cmds = len(out)
  117.             out.append(0)
  118.     # File ends with a "dead" copy command.
  119.     out.append(0)
  120.     out.append(0)
  121.     c |= flag
  122.     out[cmds] = c
  123.     return bytes(out)
  124.  
  125.  
  126. def main():
  127.     pass
  128.  
  129. if __name__ == '__main__':
  130.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement