Update, added support for reading weapon files:

Code:
import os
import shutil
import subprocess
import struct
import csv

def get_more_xmodels(filename):
    found = []
    if os.path.exists(filename):
        f = open(filename, "r")
        for line in f:
            if len(line) != 0:
                found.append(line.strip('\n').strip('\r'))
    return found

def get_more_materials(filename):
    found = []
    if os.path.exists(filename):
        f = open(filename, "r")
        for line in f:
            if len(line) != 0:
                found.append(line.strip('\n').strip('\r'))
    return found

def get_weapon_xmodels(filename):
    found = []
    models = []
    if os.path.exists(filename):
        f = open(filename, "r")
        for line in f:
            if len(line) != 0:
                found.append(line.strip('\n').strip('\r'))
    for weap in found:
        if os.path.exists(os.path.join(source_stock, "weapons", "mp", weap)):
            continue
        if os.path.exists(os.path.join(source, "weapons", "mp", weap)):
            with open(os.path.join(source, "weapons", "mp", weap)) as weapfile:
                reader = csv.reader(weapfile, delimiter = "\\")
                num = 0
                next_is_model = False
                for row in reader:
                    for elem in row:
                        num += 1
                        if next_is_model:
                            next_is_model = False
                            if "xmodel/" in elem:
                                models.append(elem)
                            else:
                                models.append("xmodel/" + elem)
                        elif "model" in elem.lower() and not num%2:
                            next_is_model = True
                        else:
                            next_is_model = False
        else:
            print("weapon not found:", weap, "for map:", filename)
    return models

def get_weapon_materials(filename):
    found = []
    materials = []
    if os.path.exists(filename):
        f = open(filename, "r")
        for line in f:
            if len(line) != 0:
                found.append(line.strip('\n').strip('\r'))
    for weap in found:
        if os.path.exists(os.path.join(source_stock, "weapons", "mp", weap)):
            continue
        if os.path.exists(os.path.join(source, "weapons", "mp", weap)):
            with open(os.path.join(source, "weapons", "mp", weap)) as weapfile:
                reader = csv.reader(weapfile, delimiter = "\\")
                num = 0
                next_is_material = False
                for row in reader:
                    for elem in row:
                        num += 1
                        if next_is_material:
                            next_is_material = False
                            materials.append(elem)
                        elif "icon" in elem and not num%2:
                            next_is_material = True
                        else:
                            next_is_material = False
        else:
            print("weapon not found:", weap, "for map:", filename)
    return materials


def get_xmodels(filename):
    models = []
    f = open(filename, "rb")
    for line in f:
        if line.startswith(bytes(r'"model"', "utf-8")):
            #print(line)
            strs = ""
            start = False
            for chars in line:
                if chr(chars) == " ":
                    start = True
                elif chr(chars) == "\n":
                    break
                elif start and chr(chars) == "*":
                    break;
                elif start and chr(chars) != "\"":
                    strs += chr(chars)
            if len(strs):
                if not strs in models:
                    models.append(strs)
    return models

def getxmodelparts(filename, mapname):
    parts = []
    f = open(filename, "rb")
    res = f.read(1)
    pos = 1
    startpos = -1
    count_255 = 0
    while len(res) == 1:
        if res[0] == 0:
            count_255 += 1
            if count_255 == 4:
                if startpos == -1:
                    startpos = pos
        else:
            count_255 = 0
        res = f.read(1)
        pos += 1
    f.close()
    f = open(filename, "rb")
    if startpos >= 1:
        f.read(startpos)
    else:
        f.close()
        print("bad stuff happened for part", filename, mapname)
        return parts
    char = f.read(1)
    pos = 1
    strs = ""
    while pos <= 32:
        if char == b"\x00":
            break;
        strs += char.decode("utf-8")
        char = f.read(1)
        pos += 1
    f.close()
    parts.append(strs)
    return parts

def getmaterialsxmodel(filename, mapname):
    mats = []
    f = open(filename, "rb")
    res = f.read(1)
    pos = 1
    startpos = -1
    count_255 = 0
    while len(res) == 1:
        if res[0] == 255:
            count_255 += 1
            if count_255 == 4:
                if startpos == -1:
                    startpos = pos
        else:
            count_255 = 0
        res = f.read(1)
        pos += 1
    f.close()
    f = open(filename, "rb")
    if startpos >= 0:
        f.read(startpos + 4)
    else:
        f.close()
        print("bad stuff happened for materialfind ", filename, mapname)
        return mats
    count = 0
    count += int(f.read(1)[0])
    count += int(f.read(1)[0]) * 256
    char = f.read(1)
    strs = ""
    while len(char) == 1 and len(mats) < count:
        if char == b"\x00":
            mats.append(strs)
            strs = ""
        else:
            strs += char.decode("utf-8")
        char = f.read(1)
    f.close()
    return mats
    

def get_loadscreen(filename):
    mats = []
    if not os.path.exists(filename):
        return mats
    with open(filename) as csvfile:
        reader = csv.reader(csvfile, delimiter = ",")
        for row in reader:
            if len(row) < 2:
                continue
            if len(row) == 2 and row[0].lower() == "levelbriefing":
                mats.append(row[1])
            else:
                print("bad stuff:", filename, row[0], row[1])
    return mats
                

def get_sounds(mapname):
    files = []
    if not os.path.exists(os.path.join(source, "soundaliases", mapname + ".csv")):
        return files
    with open(os.path.join(source, "soundaliases", mapname + ".csv")) as csvfile:
        file_index = -1
        loadspec_index = -1
        name_index = -1
        reader = csv.reader(csvfile, delimiter = ",")
        linenum = 0
        for row in reader:
            linenum += 1
            if len(row) < 3:
                continue
            if row[0].startswith(r",") or row[0].startswith(r"#") or row[0].startswith(r"\"") or len(row[0]) == 0:
                continue
            if file_index == -1 and "file" in row:
                file_index = row.index("file")
                if loadspec_index == -1 and "loadspec" in row:
                    loadspec_index = row.index("loadspec")
                if name_index == -1 and "name" in row:
                    name_index = row.index("name")
                continue
            if file_index == -1 or loadspec_index == -1:
                continue
            if name_index != -1 and len(row) > name_index and row[name_index] == "null":
                continue
            if len(row) <= loadspec_index or loadspec_index == -1:
                print(mapname, "has loadspec out-of-bounds for", row[0])
            elif row[loadspec_index] != mapname:
                print(mapname, "has invalid loadspec for", row[0])
            files.append(row[file_index])
    return files




def get_materials(filename):
    f = open(filename, "rb")
    data = f.read(8)
    l = f.read(4)
    total_l = 0
    multi = 1
    for e in l:
        total_l += int(e) * multi
        multi *= 256
    o = f.read(4)
    total_o = 0
    multi = 1
    for e in o:
        total_o += int(e) * multi
        multi *= 256
    f.close()
    curloc = total_o
    mats = []
    while (curloc < total_o + total_l):
        f = open(filename, "rb");
        f.read(curloc)
        mat = ""
        loc = curloc
        while loc < curloc + 64:
            r = f.read(1)
            loc += 1
            if r == b"\x00":
                break;
            mat += r.decode("utf-8")
        curloc += 72
        f.close()
        mats.append(mat)
    f.close()
    return mats

def get_images(filename):
    f = open(filename, "rb")
    f.read(4)
    o = f.read(4)
    total_o = 0
    multi = 1
    for e in o:
        total_o += int(e) * multi
        multi *= 256
    f.close()
    mats = []
    f = open(filename, "rb")
    f.read(total_o)
    r = f.read(1)
    mat = ""
    while(len(r) > 0):
        if(r == b"\x00"):
            if len(mat) != 0:
                mats.append(mat)
            mat = ""
        else:
            mat += r.decode("utf-8")
        r = f.read(1)
    f.close()
    return mats


startdir = "C:/JH2"
source = os.path.join(startdir, "merged")
source_stock = os.path.join(startdir, "stock")
dest_packs = os.path.join(startdir, "packs")
dest_soundalias = os.path.join(startdir, "soundaliases")
dest_empty = os.path.join(startdir, "empty_files")
dest_filelist = os.path.join(startdir, "filelist")
source_d3dbsp = os.path.join(source, "maps", "mp")
source_add_models = os.path.join(startdir, "add_models")
source_add_shaders = os.path.join(startdir, "add_shaders")
source_add_weapons = os.path.join(startdir, "add_weapons")

if os.path.exists(dest_packs):
    shutil.rmtree(dest_packs)
os.mkdir(dest_packs)


if os.path.exists(dest_empty):
    shutil.rmtree(dest_empty)
os.mkdir(dest_empty)

if os.path.exists(dest_filelist):
    shutil.rmtree(dest_filelist)
os.mkdir(dest_filelist)


if os.path.exists(dest_soundalias):
    shutil.rmtree(dest_soundalias)
os.mkdir(dest_soundalias)

for r,d,f in os.walk(source_d3dbsp):
    for files in f:
        if not files.endswith(".d3dbsp"):
            continue
        mapname = os.path.splitext(files)[0]
        #print(mapname)
        if not os.path.exists(os.path.join(dest_packs, mapname)):
            os.mkdir(os.path.join(dest_packs, mapname))
        if not os.path.exists(os.path.join(dest_packs, mapname, "maps")):
            os.mkdir(os.path.join(dest_packs, mapname, "maps"))
        if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp")):
            os.mkdir(os.path.join(dest_packs, mapname, "maps", "mp"))
        if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dbsp")):
            shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dbsp"), os.path.join(dest_packs, mapname, "maps", "mp"))
        if os.path.exists(os.path.join(source_d3dbsp, mapname + ".d3dprt")) and not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dprt")):
            shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dprt"), os.path.join(dest_packs, mapname, "maps", "mp"))
        mats = get_materials(source_d3dbsp + "/" + files)
        models = get_xmodels(source_d3dbsp + "/" + files)
        models2 = get_more_xmodels(os.path.join(source_add_models, mapname + ".txt"))
        models3 = get_weapon_xmodels(os.path.join(source_add_weapons, mapname + ".txt"))
        models = models + models2 + models3
        for m in models:
            if os.path.exists(os.path.join(source_stock, m)):
                continue
            if not os.path.exists(os.path.join(source, m)):
                print("Missing", m, "from map", mapname)
                continue
            if not os.path.exists(os.path.join(dest_packs, mapname, m)):
                if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, m))):
                    os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, m)))
                shutil.copy(os.path.join(source, m), os.path.dirname(os.path.join(dest_packs, mapname, m)))
            mat_model = getmaterialsxmodel(source + "/" + m, mapname)
            mats = mats + mat_model
            xparts = getxmodelparts(source + "/" + m, mapname)
            #if len(xparts) == 0:
            #    print("No parts in", m)
            for p in xparts:
                if os.path.exists(os.path.join(source_stock, "xmodelparts", p)):
                    #print("using stock for", p)
                    continue
                if not os.path.exists(os.path.join(source, "xmodelparts", p)):
                    print("missing part", p, "for map", mapname)
                    continue
                if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelparts", p)):
                    if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p))):
                        os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                    shutil.copy(os.path.join(source, "xmodelparts", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                    #print("copying xmodelpart", p)
                #else:
                #    print("too lazy to copy", p)
            for p in xparts:
                if os.path.exists(os.path.join(source_stock, "xmodelsurfs", p)):
                    continue
                if not os.path.exists(os.path.join(source, "xmodelsurfs", p)):
                    print("missing surf", p, "for map", mapname)
                    continue
                if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelsurfs", p)):
                    if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p))):
                        os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
                    shutil.copy(os.path.join(source, "xmodelsurfs", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
        if os.path.exists(source_d3dbsp + "/" + mapname + ".csv"):
            mats_loadscreen = get_loadscreen(source_d3dbsp + "/" + mapname + ".csv")
            mats = mats + mats_loadscreen
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".csv")):
                shutil.copy(os.path.join(source_d3dbsp + "/" + mapname + ".csv"), os.path.join(dest_packs, mapname, "maps", "mp"))
        mats2 = get_more_materials(os.path.join(source_add_shaders, mapname + ".txt"))
        mats3 = get_weapon_materials(os.path.join(source_add_weapons, mapname + ".txt"))
        mats = mats + mats2
        for mat in mats:
            if mat == "noshader":
                continue
            if os.path.exists(os.path.join(source_stock, "materials", mat)):
                continue
            if not os.path.exists(source + "/materials/" + mat):
                print("Could not find material:", mat, "from map:", mapname)
            elif not os.path.exists(os.path.join(dest_packs, mapname, "materials", mat)):
                if not os.path.exists(os.path.join(dest_packs, mapname, "materials")):
                    os.mkdir(os.path.join(dest_packs, mapname, "materials"))
                shutil.copy(os.path.join(source, "materials", mat), os.path.join(dest_packs, mapname, "materials"))
                imgs = get_images(os.path.join(dest_packs, mapname, "materials", mat))
                for img in imgs:
                    if img.lower() == "colormap" or img.lower() == "detailmap" or img.lower() == "normalmap" or img.lower() == "detailscale" or img.lower() == "specularmap":
                        continue
                    if os.path.exists(os.path.join(source_stock, "images", img + ".iwi")):
                        continue
                    if not os.path.exists(os.path.join(source, "images", img + ".iwi")):
                        print("Could not find image:", img + ".iwi", "from map:", mapname)
                    elif not os.path.exists(os.path.join(dest_packs, mapname, "images", img + ".iwi")):
                        if not os.path.exists(os.path.join(dest_packs, mapname, "images")):
                            os.mkdir(os.path.join(dest_packs, mapname, "images"))
                        shutil.copy(os.path.join(source, "images", img + ".iwi"), os.path.join(dest_packs, mapname, "images"))
        sounds = get_sounds(mapname)
        if len(sounds) > 0:
            if not os.path.exists(os.path.join(dest_soundalias, mapname + ".csv")):
                shutil.copy(os.path.join(source, "soundaliases", mapname + ".csv"), os.path.join(dest_soundalias, mapname + ".csv"))
            for sound in sounds:
                if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                    if os.path.exists(os.path.join(source_stock, "sound", sound)):
                        continue
                    if os.path.exists(os.path.join(source, "sound", sound)):
                        d = os.path.dirname(os.path.join(dest_packs, mapname, "sound", sound))
                        if not os.path.exists(d):
                            os.makedirs(d)
                        if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                            shutil.copy(os.path.join(source, "sound", sound), os.path.join(dest_packs, mapname, "sound", sound))
                    else:
                        print("Could not find sound:", sound, "from map", mapname)

for subdir in os.listdir(dest_packs):
    mapdir = os.path.join(dest_packs, subdir)
    for src_dir, dirs, files in os.walk(mapdir):
        dst_dir_empty = src_dir.replace(mapdir, dest_empty)
        if not os.path.exists(dst_dir_empty):
            os.mkdir(dst_dir_empty)
        for file_ in files:
            src_file = os.path.join(src_dir, file_)
            dst_empty_file = os.path.join(dst_dir_empty, file_)
            if not os.path.exists(dst_empty_file):
                open(dst_empty_file, 'a').close()

for subdir in os.listdir(dest_packs):
    for r,d,f in os.walk(os.path.join(dest_packs, subdir)):
        for files in f:
            if files.endswith(".d3dbsp"):
                mapname = os.path.splitext(files)[0]
                iwd = subdir
                mapname = mapname.lower()
                f = open(dest_filelist + "/" + mapname + ".txt", "a")
                f.write(iwd + ",\n")
                f.close()
input("Press enter to start the packing of the maps")
p = subprocess.Popen(os.path.join(startdir, "zip_packs.bat"), cwd=dest_packs)
stdout, stderr = p.communicate()
It works the same as the add_models/add_shaders, only now create your mapname.txt containing line-ending separated weaponnames in add_weapons folder