• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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