forked from mirror/Archipelago
* initial work on procedure patch
* more flexibility
load default procedure for version 5 patches
add args for procedure
add default extension for tokens and bsdiff
allow specifying additional required extensions for generation
* pushing current changes to go fix tloz bug
* move tokens into a separate inheritable class
* forgot the commit to remove token from ProcedurePatch
* further cleaning from bad commit
* start on docstrings
* further work on docstrings and typing
* improve docstrings
* fix incorrect docstring
* cleanup
* clean defaults and docstring
* define interface that has only the bare minimum required
for `Patch.create_rom_file`
* change to dictionary.get
* remove unnecessary if statement
* update to explicitly check for procedure, restore compatible version and manual override
* Update Files.py
* remove struct uses
* Update Rom.py
* convert KDL3 to APPP
* change class variables to instance variables
* Update worlds/Files.py
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
* Update worlds/Files.py
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
* move required_extensions to tuple
* fix missing tuple ellipsis
* fix classvar mixup
* rename tokens to _tokens. use hasattr
* type hint cleanup
* Update Files.py
* initial base for local items, need to finish
* coo not clean
* handle local items for real, appp cleanup
* actually make bosses send their locations
* fix cloudy park 4 rule, zero deathlink message
* remove redundant door_shuffle bool
when generic ER gets in, this whole function gets rewritten. So just clean it a little now.
* properly fix deathlink messages, fix fill error
* update docs
* add prefill items
* fix kine fill error
* Update Rom.py
* Update Files.py
* mypy and softlock fix
* Update Gifting.py
* mypy phase 1
* fix rare async client bug
* Update __init__.py
* typing cleanup
* fix stone softlock
because of the way Kine's Stone works, you can't clear the stone blocks before clearing the burning blocks, so we have to bring Burning from outside
* Update Rom.py
* Add option groups
* Rename to lowercase
* finish rename
* whoops broke the world
* fix animal duplication bug
* overhaul filler generation
* add Miku flavor
* Update gifting.py
* fix issues related to max_hs increase
* Update test_locations.py
* fix boss shuffle not working if level shuffle is disabled
* fix bleeding default levels
* Update options.py
* thought this would print seed
* yay bad merges
* forgot options too
* yeah lets just break generation while at it
* this is probably a problem
* cap required heart stars
* Revert "cap required heart stars"
This reverts commit 759efd3e2b.
* fix duplication removal placement, deprecated test option
* forgot that we need to account for what we place
* move location ids
* rewrite trap handling
* further stage renumber fixes
* forgot one more
* basic UT support
* fix local heart star checks
* fix pattern
---------
Co-authored-by: beauxq <beauxq@yahoo.com>
Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
def hal_decompress(comp: bytes) -> bytes:
|
|
"""
|
|
HAL decompression based on exhal by devinacker
|
|
https://github.com/devinacker/exhal
|
|
"""
|
|
inpos = 0
|
|
|
|
inval = 0
|
|
output = bytearray()
|
|
while inval != 0xFF:
|
|
remaining = 65536 - inpos
|
|
if remaining < 1:
|
|
return bytes()
|
|
inval = comp[inpos]
|
|
inpos += 1
|
|
if inval == 0xFF:
|
|
break
|
|
if (inval & 0xE0) == 0xE0:
|
|
command = (inval >> 2) & 0x07
|
|
length = (((inval & 0x03) << 8) | comp[inpos]) + 1
|
|
inpos += 1
|
|
else:
|
|
command = inval >> 5
|
|
length = (inval & 0x1F) + 1
|
|
if (command == 2 and ((len(output) + 2*length) > 65536)) or (len(output) + length) > 65536:
|
|
return bytes()
|
|
if command == 0:
|
|
output.extend(comp[inpos:inpos+length])
|
|
inpos += length
|
|
elif command == 1:
|
|
output.extend([comp[inpos] for _ in range(length)])
|
|
inpos += 1
|
|
elif command == 2:
|
|
output.extend([comp[x] for _ in range(length) for x in (inpos, inpos+1)])
|
|
inpos += 2
|
|
elif command == 3:
|
|
output.extend([comp[inpos] + i for i in range(length)])
|
|
inpos += 1
|
|
elif command == 4 or command == 7:
|
|
offset = (comp[inpos] << 8) | comp[inpos + 1]
|
|
if (offset + length) > 65536:
|
|
return bytes()
|
|
output.extend(output[offset:offset+length])
|
|
inpos += 2
|
|
elif command == 5:
|
|
offset = (comp[inpos] << 8) | comp[inpos + 1]
|
|
if (offset + length) > 65536:
|
|
return bytes()
|
|
output.extend([int('{:08b}'.format(x)[::-1], 2) for x in output[offset:offset+length]])
|
|
inpos += 2
|
|
elif command == 6:
|
|
offset = (comp[inpos] << 8) | comp[inpos + 1]
|
|
if offset < length - 1:
|
|
return bytes()
|
|
output.extend([output[offset - x] for x in range(length)])
|
|
inpos += 2
|
|
return bytes(output)
|