1#!/usr/bin/python3 2# coding=utf-8 3# ============================================================================ 4# @brief Build Update Resource Pkg File 5 6# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 7# Licensed under the Apache License, Version 2.0 (the "License"); 8# you may not use this file except in compliance with the License. 9# You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, 15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16# See the License for the specific language governing permissions and 17# limitations under the License. 18# ============================================================================ 19 20""" 21用法: python pack.py [-i INDEX_FILE] [-d DATA_FILE] [res_dir] 22 res_dir 待打包目录路径, 仅删除时无需填写此参数 23 -i INDEX_FILE, --index-file INDEX_FILE 24 生成的索引文件名称 25 -d DATA_FILE, --data-file DATA_FILE 26 生成的数据文件名称, 仅删除时此参数无效 27""" 28 29from enum import IntEnum, auto 30import sys 31import os 32import struct 33import ctypes 34import shutil 35 36file_dir = os.path.dirname(os.path.realpath(__file__)) 37g_root = os.path.realpath(os.path.join(file_dir, "..", "..")) 38 39DEFAULT_INDEX_FILE = os.path.join(g_root, "output", "res", "res_index.bin") 40DEFAULT_DATA_FILE = os.path.join(g_root, "output", "res", "res_data.bin") 41 42class UpgResourceOperation(IntEnum): 43 ADD_FILE = 0 44 AMEND_FILE = auto() 45 DELETE_FILE = auto() 46 REMOVE_DIR = auto() 47 48class Structure(ctypes.LittleEndianStructure): 49 def encode(self): 50 return ctypes.string_at(ctypes.addressof(self), ctypes.sizeof(self)) 51 52 def decode(self, data): 53 ctypes.memmove(ctypes.addressof(self), data, ctypes.sizeof(self)) 54 return len(data) 55 56class UpgResourceNode(Structure): 57 _fields_ = [ 58 ("file_path", ctypes.c_char * 128), 59 ("offset", ctypes.c_uint32), 60 ("file_len", ctypes.c_uint32), 61 ("operation_type", ctypes.c_uint32), 62 ] 63 64class UpgResourceIndex(Structure): 65 _fields_ = [ 66 ("file_num", ctypes.c_uint32), 67 ("file_node", UpgResourceNode * 0), 68 ] 69 70def res_pack(res_dir, index_path=DEFAULT_INDEX_FILE, data_path=DEFAULT_DATA_FILE, deleted_files=[], removed_dirs=[]): 71 """ 72 打包指定资源目录,会先删除文件或目录,再修改/增加文件 73 :param res_dir: 待打包目录路径, 如果仅删除则填None 74 :param index_path: 生成的索引文件路径 75 :param data_path: 生成的数据文件路径, 仅删除时此参数无效 76 :param deleted_files: 待删除文件列表 77 :param removed_dirs: 待删除目录列表 78 :returns: 是否成功打包 79 """ 80 81 os.makedirs(os.path.dirname(os.path.realpath(index_path)), exist_ok=True) 82 83 with open(index_path, "wb") as index_file: 84 file_num = 0 85 index_file.write(b"\x00" * ctypes.sizeof(UpgResourceIndex)) 86 87 for name in deleted_files: 88 if not name.startswith("/"): 89 name = "/" + name 90 print(f"Delete file '{name}'") 91 92 node = UpgResourceNode() 93 node.file_path = name.encode() 94 node.offset = 0 95 node.file_len = 0 96 node.operation_type = UpgResourceOperation.DELETE_FILE 97 index_file.write(node.encode()) 98 99 file_num += 1 100 101 for name in removed_dirs: 102 if not name.startswith("/"): 103 name = "/" + name 104 print(f"Remove dir '{name}'") 105 106 node = UpgResourceNode() 107 node.file_path = name.encode() 108 node.offset = 0 109 node.file_len = 0 110 node.operation_type = UpgResourceOperation.REMOVE_DIR 111 index_file.write(node.encode()) 112 113 file_num += 1 114 115 if res_dir is not None: 116 pack_dir = os.path.realpath(res_dir) 117 if not os.path.isdir(pack_dir): 118 print("Error: invaild resource directory!") 119 return False 120 print(f"Packing resource directory: {pack_dir}") 121 122 data_file = None 123 for root, dirs, files in os.walk(pack_dir): 124 for name in files: 125 if data_file is None: 126 os.makedirs(os.path.dirname(os.path.realpath(data_path)), exist_ok=True) 127 data_file = open(data_path, "wb") 128 129 full_file_path = os.path.join(root, name) 130 file_path = full_file_path[len(pack_dir):].replace("\\", "/") 131 if len(file_path.encode()) > UpgResourceNode.file_path.size - 1: 132 print(f"The length of '{file_path}' is greater than {UpgResourceNode.file_path.size - 1}, skip!") 133 continue 134 print(f"Add '{full_file_path}' -> '{data_path}:{file_path}'") 135 136 node = UpgResourceNode() 137 node.file_path = file_path.encode() 138 node.offset = data_file.tell() 139 node.file_len = os.path.getsize(full_file_path) 140 node.operation_type = UpgResourceOperation.AMEND_FILE 141 index_file.write(node.encode()) 142 with open(full_file_path, "rb") as res_file: 143 shutil.copyfileobj(res_file, data_file) 144 145 file_num += 1 146 147 if data_file is not None: 148 data_file.close() 149 print(f"Added {file_num} files data into {data_path}") 150 151 index_file.seek(0) 152 index_file.write(struct.pack("<I", file_num)) 153 154 print(f"Finished, merge {file_num} nodes into {index_path}") 155 return True 156 157if __name__ == "__main__": 158 import argparse 159 parser = argparse.ArgumentParser() 160 parser.add_argument("res_dir", nargs="?", default=None, type=str, help="Resource directory path") 161 parser.add_argument("-i", "--index-file", default=DEFAULT_INDEX_FILE, type=str, help="Generated index file path") 162 parser.add_argument("-d", "--data-file", default=DEFAULT_DATA_FILE, type=str, help="Generated data file path") 163 args = parser.parse_args() 164 165 test_deleted_files = [ # 待删除文件 166 ] 167 test_removed_dirs = [ # 待删除目录 168 ] 169 res_pack(args.res_dir, args.index_file, args.data_file, test_deleted_files, test_removed_dirs) 170