Skip to content

Instantly share code, notes, and snippets.

@PandorasFox
Created April 30, 2023 18:18
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PandorasFox/0b771e0bc64dfeb2e212437ff1a26b93 to your computer and use it in GitHub Desktop.
Save PandorasFox/0b771e0bc64dfeb2e212437ff1a26b93 to your computer and use it in GitHub Desktop.
# godot compressed file unpacker/repacker
# i think this should Just Work(tm) for deflate and gzip assets
# sucks for anyone who needs to mess with fastLZ/zstd assets (hopefully not me later)
# works with like, python3.2 standard library (or thereabouts, i'm using 3.10)
import math
import os
import struct
import sys
import zlib
# gcpf container format:
# "GCPF" magic string
# u32 compression mode
# u32 block size
# u32 uncompressed size
# [compressed block sizes, (uncompressed_size // block_size)+1 times]
# [[compressed data]]
# "GCPF" magic string footer
class Block:
def __init__(self):
compressed_size = None
data = None
def unpack(input_fname: str, output_fname: str) -> None:
with open(input_fname, 'rb') as infile:
magic_string = infile.read(4)
if magic_string != b'GCPF':
print(f"magic string invalid. found {magic_string} instead; aborting.")
sys.exit(1)
compression_mode, blocksize, raw_size = struct.unpack("III", infile.read(12))
num_blocks = math.ceil(raw_size / blocksize)
blocks = []
for bnum in range(num_blocks):
block = Block()
block.compressed_size = struct.unpack("I", infile.read(4))[0]
blocks.append(block)
for block in blocks:
block.data = infile.read(block.compressed_size)
magic_string = infile.read(4)
if magic_string != b'GCPF':
print(f"magic string **footer** invalid. found {magic_string} instead; aborting.")
sys.exit(1)
with open(output_fname, 'wb') as outfile:
for block in blocks:
# TODO: switch on compression_mode here if needed
# wbits=40 should Just Work for gzip?
# wbits=-8 should make Deflate streams work too
outfile.write(zlib.decompress(block.data, wbits=40, bufsize=blocksize))
print(f"File unpacked to {output_fname}; enjoy :)")
def repack(input_fname: str, output_fname: str) -> None:
blocksize = 4096
compression_mode = 3
with open(input_fname, 'rb') as infile:
infile.seek(0, os.SEEK_END)
raw_size = infile.tell()
infile.seek(0)
num_blocks = math.ceil(raw_size / blocksize)
with open(output_fname, 'wb') as outfile:
outfile.write(b"GCPF")
outfile.write(struct.pack("III", compression_mode, blocksize, raw_size))
blocks = []
for bnum in range(num_blocks):
block = Block()
data = infile.read(blocksize)
gzip_machine = zlib.compressobj(wbits=31)
block.data = gzip_machine.compress(data)
block.data += gzip_machine.flush()
block.compressed_size = len(block.data)
outfile.write(struct.pack("I", block.compressed_size))
blocks.append(block)
for block in blocks:
outfile.write(block.data)
outfile.write(b"GCPF")
print(f"repacked {input_fname} into {output_fname}; enjoy! :)")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("usage: gcpfpack.py [unpack/repack] [input filename] [output filename]")
sys.exit(1)
args = sys.argv[1:]
mode = args[0]
input_fname = args[1]
output_fname = args[2]
if mode == "unpack":
unpack(input_fname, output_fname)
elif mode == "repack":
repack(input_fname, output_fname)
else:
print("Invalid mode provided (should be 'unpack' or 'repack')")
sys.exit(1)
@zen129318
Copy link

This save me so much time, in my attempt to save myself from the shittiness of xbox game pass. As once again it has decided to no longer recognizes that I have a subscription and should be able to launch the game.

So I bought it on steam. Thanks for saving my evening.

Zen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment