ScrapHacks/scrapper.py
2017-10-08 03:05:53 +02:00

135 lines
4.9 KiB
Python

import argparse
from collections import OrderedDict
import glob
import os
import shutil
from construct import *
from tqdm import tqdm
setglobalstringencoding(None)
ScrapFile = Struct(
'path'/PascalString(Int32ul),
'size'/Int32ul,
'offset'/Int32ul,
'data'/OnDemandPointer(this.offset,Bytes(this.size)),
)
DummyFile = Struct(
'path'/PascalString(Int32ul),
'size'/Int32ul,
'offset'/Int32ul,
)
PackedHeader = Struct(
Const(b'BFPK'),
Const(b'\0\0\0\0'),
'files'/PrefixedArray(Int32ul,ScrapFile),
)
DummyHeader = Struct(
Const(b'BFPK'),
Const(b'\0\0\0\0'),
'files'/PrefixedArray(Int32ul,DummyFile),
)
parser = argparse.ArgumentParser(description='Unpack and Repack .packed files')
parser.add_argument('-u', '--unpack', action='store_true',
help='unpack file to \'extracted\' directory')
parser.add_argument('-r', '--repack', action='store_true',
help='repack file from \'extracted\' directory')
parser.add_argument(
'--reset',
action='store_true',
default=False,
help='restore backup')
parser.add_argument(
'scrap_dir',
metavar='Scrapland Directory',
type=str,
default=".",
help='Scrapland installation directory')
options = parser.parse_args()
scrap_dir = os.path.abspath(options.scrap_dir)
if options.reset:
print('Restoring Backups and removing extracted folder...')
for packed_file in glob.glob(os.path.join(scrap_dir, '*.packed.bak')):
outfile = os.path.basename(packed_file)
orig_filename = outfile[:-4]
if os.path.isfile(outfile):
print('deleting', orig_filename)
os.remove(orig_filename)
print('moving', outfile, '->', orig_filename)
shutil.move(outfile, orig_filename)
target_folder = os.path.join(
'extracted', os.path.basename(orig_filename))
print('deleting', target_folder)
shutil.rmtree(target_folder)
if os.path.isdir('extracted'):
input('Press enter to remove rest of extracted folder')
shutil.rmtree('extracted')
exit('Done!')
if not (options.unpack or options.repack):
parser.print_help()
exit()
pstatus = ''
if options.unpack:
if os.path.isdir('extracted'):
print("Removing extracted folder")
shutil.rmtree('extracted')
for packed_file in glob.glob(os.path.join(scrap_dir, '*.packed')):
os.chdir(scrap_dir)
BN=os.path.basename(packed_file)
target_folder = os.path.join(
'extracted', os.path.basename(packed_file))
os.makedirs(target_folder, exist_ok=True)
os.chdir(target_folder)
print('Unpacking {}'.format(os.path.basename(packed_file)))
with open(packed_file, 'rb') as pkfile:
data = PackedHeader.parse_stream(pkfile)
print("Offset:",hex(pkfile.tell()))
for file in tqdm(data.files,ascii=True):
folder, filename = os.path.split(file.path)
if folder:
os.makedirs(folder, exist_ok=True)
with open(file.path, 'wb') as outfile:
outfile.write(file.data())
print('\r' + ' ' * len(pstatus) + '\r', end='', flush=True)
os.chdir(scrap_dir)
if (options.unpack and options.repack):
input('Press enter to rebuild *.packed files from folders in \'extracted\' dir...') # noqa
pass
def file_gen(files,offset=0):
for real_path,size,path in files:
file=dict(
path=path,
offset=offset,
size=size)
yield file
offset+=file['size']
def make_header(files,offset=0):
files_list=list(file_gen(files,offset))
return DummyHeader.build(dict(files=files_list))
if options.repack:
for folder in glob.glob(os.path.join(scrap_dir, 'extracted', '*.packed')):
data=[]
filename=os.path.join(scrap_dir,os.path.basename(folder))
for root,folders,files in os.walk(folder):
for file in sorted(files):
file=os.path.join(root,file)
rel_path=bytes(file.replace(folder, '').replace('\\', '/').lstrip('/'), 'windows-1252')
size=os.stat(file).st_size
data.append((file,size,rel_path))
print("Found {} files for {}".format(len(data),filename))
offset=len(make_header(data))
print("Writing",filename)
header=make_header(data,offset)
with open(filename,"wb") as outfile:
outfile.write(header)
for file,size,rel_path in tqdm(data,ascii=True):
outfile.write(open(file,"rb").read())
print('Done!')