LPT26x-HSF-4MB-Hilink_14.2..../build/script/make_upg_res_pack.py
2025-05-13 22:00:58 +08:00

158 lines
6.0 KiB
Python
Executable File

#!/usr/bin/python3
# coding=utf-8
# ============================================================================
# @brief Build Update Resource Pkg File
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
# ============================================================================
"""
用法: python pack.py [-i INDEX_FILE] [-d DATA_FILE] [res_dir]
res_dir 待打包目录路径, 仅删除时无需填写此参数
-i INDEX_FILE, --index-file INDEX_FILE
生成的索引文件名称
-d DATA_FILE, --data-file DATA_FILE
生成的数据文件名称, 仅删除时此参数无效
"""
from enum import IntEnum, auto
import sys
import os
import struct
import ctypes
import shutil
file_dir = os.path.dirname(os.path.realpath(__file__))
g_root = os.path.realpath(os.path.join(file_dir, "..", ".."))
DEFAULT_INDEX_FILE = os.path.join(g_root, "output", "res", "res_index.bin")
DEFAULT_DATA_FILE = os.path.join(g_root, "output", "res", "res_data.bin")
class UpgResourceOperation(IntEnum):
ADD_FILE = 0
AMEND_FILE = auto()
DELETE_FILE = auto()
REMOVE_DIR = auto()
class Structure(ctypes.LittleEndianStructure):
def encode(self):
return ctypes.string_at(ctypes.addressof(self), ctypes.sizeof(self))
def decode(self, data):
ctypes.memmove(ctypes.addressof(self), data, ctypes.sizeof(self))
return len(data)
class UpgResourceNode(Structure):
_fields_ = [
("file_path", ctypes.c_char * 128),
("offset", ctypes.c_uint32),
("file_len", ctypes.c_uint32),
("operation_type", ctypes.c_uint32),
]
class UpgResourceIndex(Structure):
_fields_ = [
("file_num", ctypes.c_uint32),
("file_node", UpgResourceNode * 0),
]
def res_pack(res_dir, index_path=DEFAULT_INDEX_FILE, data_path=DEFAULT_DATA_FILE, deleted_files=[], removed_dirs=[]):
"""
打包指定资源目录,会先删除文件或目录,再修改/增加文件
:param res_dir: 待打包目录路径, 如果仅删除则填None
:param index_path: 生成的索引文件路径
:param data_path: 生成的数据文件路径, 仅删除时此参数无效
:param deleted_files: 待删除文件列表
:param removed_dirs: 待删除目录列表
:returns: 是否成功打包
"""
os.makedirs(os.path.dirname(os.path.realpath(index_path)), exist_ok=True)
with open(index_path, "wb") as index_file:
file_num = 0
index_file.write(b"\x00" * ctypes.sizeof(UpgResourceIndex))
for name in deleted_files:
if not name.startswith("/"):
name = "/" + name
print(f"Delete file '{name}'")
node = UpgResourceNode()
node.file_path = name.encode()
node.offset = 0
node.file_len = 0
node.operation_type = UpgResourceOperation.DELETE_FILE
index_file.write(node.encode())
file_num += 1
for name in removed_dirs:
if not name.startswith("/"):
name = "/" + name
print(f"Remove dir '{name}'")
node = UpgResourceNode()
node.file_path = name.encode()
node.offset = 0
node.file_len = 0
node.operation_type = UpgResourceOperation.REMOVE_DIR
index_file.write(node.encode())
file_num += 1
if res_dir is not None:
pack_dir = os.path.realpath(res_dir)
if not os.path.isdir(pack_dir):
print("Error: invaild resource directory!")
return False
print(f"Packing resource directory: {pack_dir}")
data_file = None
for root, dirs, files in os.walk(pack_dir):
for name in files:
if data_file is None:
os.makedirs(os.path.dirname(os.path.realpath(data_path)), exist_ok=True)
data_file = open(data_path, "wb")
full_file_path = os.path.join(root, name)
file_path = full_file_path[len(pack_dir):].replace("\\", "/")
if len(file_path.encode()) > UpgResourceNode.file_path.size - 1:
print(f"The length of '{file_path}' is greater than {UpgResourceNode.file_path.size - 1}, skip!")
continue
print(f"Add '{full_file_path}' -> '{data_path}:{file_path}'")
node = UpgResourceNode()
node.file_path = file_path.encode()
node.offset = data_file.tell()
node.file_len = os.path.getsize(full_file_path)
node.operation_type = UpgResourceOperation.AMEND_FILE
index_file.write(node.encode())
with open(full_file_path, "rb") as res_file:
shutil.copyfileobj(res_file, data_file)
file_num += 1
if data_file is not None:
data_file.close()
print(f"Added {file_num} files data into {data_path}")
index_file.seek(0)
index_file.write(struct.pack("<I", file_num))
print(f"Finished, merge {file_num} nodes into {index_path}")
return True
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("res_dir", nargs="?", default=None, type=str, help="Resource directory path")
parser.add_argument("-i", "--index-file", default=DEFAULT_INDEX_FILE, type=str, help="Generated index file path")
parser.add_argument("-d", "--data-file", default=DEFAULT_DATA_FILE, type=str, help="Generated data file path")
args = parser.parse_args()
test_deleted_files = [ # 待删除文件
]
test_removed_dirs = [ # 待删除目录
]
res_pack(args.res_dir, args.index_file, args.data_file, test_deleted_files, test_removed_dirs)