mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-22 21:42:54 +00:00
195 lines
3.7 KiB
Python
195 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import struct
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
# default endian
|
|
ENDIAN = ">"
|
|
|
|
# defaults
|
|
DEFAULT_PCK_TYPE = 3
|
|
DEFAULT_XML_VERSION = 4
|
|
DEFAULT_ASSET_TYPE = 0
|
|
|
|
|
|
def write_u32(f, value):
|
|
f.write(struct.pack(f"{ENDIAN}I", value))
|
|
|
|
|
|
def write_utf16_string(f, text):
|
|
"""
|
|
PCK strings:
|
|
uint32 length
|
|
UTF-16 bytes
|
|
uint32 padding
|
|
"""
|
|
|
|
encoding = "utf-16-be" if ENDIAN == ">" else "utf-16-le"
|
|
|
|
encoded = text.encode(encoding)
|
|
|
|
write_u32(f, len(text))
|
|
f.write(encoded)
|
|
|
|
# padding
|
|
write_u32(f, 0)
|
|
|
|
|
|
def collect_folder_assets(folder_path):
|
|
|
|
assets = []
|
|
|
|
for root, _, files in os.walk(folder_path):
|
|
|
|
for file in files:
|
|
|
|
full_path = os.path.join(root, file)
|
|
|
|
rel_path = os.path.relpath(full_path, folder_path)
|
|
|
|
# PCKs usually use backslashes
|
|
rel_path = rel_path.replace("/", "\\")
|
|
rel_path = rel_path.replace("\\\\", "\\")
|
|
|
|
with open(full_path, "rb") as f:
|
|
data = f.read()
|
|
|
|
assets.append({
|
|
"name": rel_path,
|
|
"size": len(data),
|
|
"type": DEFAULT_ASSET_TYPE,
|
|
"params": {},
|
|
"data": data,
|
|
})
|
|
|
|
return assets
|
|
|
|
|
|
def pack_folder_to_pck(input_folder, output_pck):
|
|
|
|
assets = collect_folder_assets(input_folder)
|
|
|
|
if not assets:
|
|
raise ValueError("No files found in folder")
|
|
|
|
# parameter lookup table
|
|
lookup = [
|
|
"PATH",
|
|
"TYPE",
|
|
"XMLVERSION",
|
|
]
|
|
|
|
print(f"Assets: {len(assets)}")
|
|
|
|
with open(output_pck, "wb") as f:
|
|
|
|
# ----- HEADER -----
|
|
|
|
write_u32(f, DEFAULT_PCK_TYPE)
|
|
|
|
write_u32(f, len(lookup))
|
|
|
|
# ----- PARAMETER LOOKUP TABLE -----
|
|
|
|
for idx, key in enumerate(lookup):
|
|
|
|
write_u32(f, idx)
|
|
|
|
write_utf16_string(f, key)
|
|
|
|
# optional XMLVERSION
|
|
if "XMLVERSION" in lookup:
|
|
write_u32(f, DEFAULT_XML_VERSION)
|
|
|
|
# ----- ASSET TABLE -----
|
|
|
|
write_u32(f, len(assets))
|
|
|
|
for asset in assets:
|
|
|
|
write_u32(f, asset["size"])
|
|
write_u32(f, asset["type"])
|
|
|
|
write_utf16_string(f, asset["name"])
|
|
|
|
print(f"Indexing: {asset['name']} ({asset['size']} bytes)")
|
|
|
|
# ----- ASSET DATA -----
|
|
|
|
for asset in assets:
|
|
|
|
params = asset["params"]
|
|
|
|
write_u32(f, len(params))
|
|
|
|
for key, value in params.items():
|
|
|
|
key_index = lookup.index(key)
|
|
|
|
write_u32(f, key_index)
|
|
|
|
write_utf16_string(f, value)
|
|
|
|
f.write(asset["data"])
|
|
|
|
print(f"Writing: {asset['name']}")
|
|
|
|
print(f"\nDone! Wrote: {output_pck}")
|
|
|
|
|
|
def main():
|
|
|
|
global ENDIAN
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Pack a folder into a Minecraft Legacy Console .pck"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"input",
|
|
help="Input folder"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"-o",
|
|
"--output",
|
|
help="Output .pck filename"
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--little",
|
|
action="store_true",
|
|
help="Write little-endian PCK"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
input_path = args.input
|
|
|
|
if not os.path.isdir(input_path):
|
|
print(f"Input folder not found: {input_path}")
|
|
sys.exit(1)
|
|
|
|
if args.little:
|
|
ENDIAN = "<"
|
|
|
|
output_path = args.output
|
|
|
|
if not output_path:
|
|
output_path = os.path.basename(
|
|
os.path.normpath(input_path)
|
|
) + ".pck"
|
|
|
|
try:
|
|
pack_folder_to_pck(input_path, output_path)
|
|
|
|
except Exception as e:
|
|
print(f"\nERROR: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|