Created
January 5, 2025 18:21
-
-
Save DaZombieKiller/808f03383659547e8d7f5e544e88f06a to your computer and use it in GitHub Desktop.
Duke Nukem 1+2 Remastered .DUKE archive extractor script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # Extracts files from Duke Nukem 1+2 Remastered .DUKE archives | |
| # | |
| import sys | |
| import zlib | |
| import struct | |
| from pathlib import Path | |
| from Crypto.Cipher import AES | |
| def unpack_archive(archive, output_dir): | |
| magic, version, entry_count, _ = struct.unpack("<4s3i", archive.read(0x10)) | |
| if magic != b'DUKE': | |
| print("invalid data archive") | |
| return | |
| names = {} | |
| entries = [] | |
| for _ in range(entry_count): | |
| entry = struct.unpack("<6i8s", archive.read(0x20)) | |
| entries.append(entry) | |
| for entry in entries: | |
| _, _, _, offset, length, _, _ = entry | |
| if length > 0x64: | |
| print("invalid file name length") | |
| return | |
| archive.seek(offset) | |
| names[entry] = archive.read(length).decode('utf8') | |
| for entry in entries: | |
| offset, length, uncompressed_length, _, _, _, nonce = entry | |
| output_path = Path(output_dir) / names[entry] | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| archive.seek(offset) | |
| key = bytes.fromhex("5755FB6CB9A571BC4F5562EE9299E808241594D1DFA675BEE9DBB51AC087ECCD") | |
| cipher = AES.new(key, AES.MODE_CTR, nonce=nonce) | |
| data = cipher.decrypt(archive.read(length)) | |
| if length != uncompressed_length: | |
| data = zlib.decompress(data, bufsize=uncompressed_length) | |
| with open(output_path, "wb") as output_file: | |
| output_file.write(data) | |
| print(names[entry]) | |
| def main(): | |
| if len(sys.argv) != 3: | |
| print(f"{sys.argv[0]} <archive path> <output dir>") | |
| return | |
| with open(sys.argv[1], "rb") as archive: | |
| unpack_archive(archive, sys.argv[2]) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment