epk.py 7.68 KB
Newer Older
wanli's avatar
wanli committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#-*- coding: UTF-8 -*- 
#!/usr/bin/python

import os
import sys
import fs
import struct
import json
from collections import OrderedDict
import zlib
import pprint
import hashlib

from ctypes import *
import platform

wanli's avatar
wanli committed
17
lib_path = os.path.dirname(os.path.abspath(__file__))
wanli's avatar
wanli committed
18

wanli's avatar
wanli committed
19
if platform.system() == 'Windows':
wanli's avatar
wanli committed
20
    pDll = CDLL(os.sep.join([lib_path, "lib", "eheatshrink.dll"]))
wanli's avatar
wanli committed
21
elif platform.system() == 'Linux':
wanli's avatar
wanli committed
22
    pDll = CDLL(os.sep.join([lib_path, "lib", "libeheatshrink.so"]))
wanli's avatar
wanli committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

pDll.ecompress_size.restype = c_uint32
pDll.ecompress_size.argtypes = [c_void_p, c_uint32]

pDll.ecompress.restype = POINTER(c_uint8)
pDll.ecompress.argtypes = [c_void_p, c_uint32]

def heatshrink_compress(buf:bytes, level:int):
    count = len(buf)
    size = pDll.ecompress_size(buf, count)
    pDll.ecompress.restype = POINTER(c_uint8)
    pDll.ecompress.argtypes = [c_void_p, c_uint32]
    ret = pDll.ecompress(buf, count)
    arr = bytearray(size)
    i = 0
    while i < size:
        arr[i] = ret[i]
        i = i+1
    return arr

def str_to_hex(s):
    return ' '.join([hex(ord(c)).replace('0x', '') for c in s])

def hex_to_str(s):
    return ''.join([chr(i) for i in [int(b, 16) for b in s.split(' ')]])

def str_to_bin(s):
    return ' '.join([bin(ord(c)).replace('0b', '') for c in s])

def bin_to_str(s):
    return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])

wanli's avatar
wanli committed
55 56 57 58
def eprint(*args, **kwargs):
    # print(*args, **kwargs)
    pass

wanli's avatar
wanli committed
59 60 61 62 63 64 65 66 67

class EpkApp(object):

    def __init__(self, appName, appDir, algorithm='zlib',appVersion="1.0", output="epks"):
        super(EpkApp, self).__init__()
        
        self._appName = appName
        self._appDir = os.path.abspath(appDir)
        self.algorithm = algorithm
wanli's avatar
wanli committed
68 69 70 71
        eprint(sys.argv)
        eprint(appName)
        eprint(appDir)
        eprint(self._appDir)
wanli's avatar
wanli committed
72 73 74 75
        self._appVersion = appVersion
        self._appCRCCode = None
        self._files = []
        self._infoPath = os.sep.join([self._appDir, "%s.json" % self._appName])
wanli's avatar
wanli committed
76
        self._epksDir = output
wanli's avatar
wanli committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
        if not os.path.exists(self._epksDir):
            fs.open_fs(os.getcwd()).makedirs(output)
        self._epkName = os.sep.join([self._epksDir, "%s.epk" % self._appName])

    def compress(self):
        if self.algorithm == 'h':
            return heatshrink_compress
        return zlib.compress

    def epkInfo(self):
        epkInfo = OrderedDict({
            "appName": self._appName,
            "appVersion": self._appVersion,
            "files": self.fileinfos(self._appDir),
        })

        infocontent = json.dumps(epkInfo)
        with open(self._infoPath, "w", encoding='utf-8') as f:
            f.write(infocontent)

        return epkInfo
    
    def fileinfos(self, path):
        path = os.path.abspath(path)
        home_fs = fs.open_fs(path)
        files = []
        for jspath in home_fs.glob('*', namespaces=['details']):
            fpath = "C:/%s" % jspath.info.name
            fname = jspath.info.name
            fsize = jspath.info.size
wanli's avatar
wanli committed
107
            fbasename, fext = os.path.splitext(jspath.info.name)
wanli's avatar
wanli committed
108

wanliofficial's avatar
wanliofficial committed
109
            if fext in ["", ".exe", ".dll", ".nv", ".conf"]:
wanli's avatar
wanli committed
110 111 112 113 114 115 116 117 118 119 120
                continue

            finfo = {
                "path": fpath,
                "name": fname,
                "size": fsize,
                "basename": fbasename,
                "ext": fext
            }
            
            if self._infoPath == os.sep.join([path, fname]):
wanli's avatar
wanli committed
121
                eprint(finfo)
wanli's avatar
wanli committed
122 123 124
                files.insert(0, finfo)
            else:
                files.append(finfo)
wanliofficial's avatar
wanliofficial committed
125
            if fext == ".evue":
wanli's avatar
wanli committed
126 127 128
                self.fileMD5(finfo)

        return files
wanli's avatar
wanli committed
129 130 131 132 133 134 135

    def header(self, epk_start=0xAA, md5_offset=0, file_count=0):
        if self.algorithm == 'zlib':
            bytes_header = struct.pack("<BBLH", epk_start, 1 ,md5_offset, file_count)
        else:
            bytes_header = struct.pack("<BBLH", epk_start, 2 ,md5_offset, file_count)

wanli's avatar
wanli committed
136
        return bytes_header
wanli's avatar
wanli committed
137

wanli's avatar
wanli committed
138 139 140 141 142 143 144 145
    
    def fileMD5(self, info):
        md5path = os.sep.join([self._appDir, "%s.md5" % info["basename"]])
        fpath = os.sep.join([self._appDir, info["name"]])
        with open(fpath, "rb") as f:
            filecontent = f.read()

        newmd5 = self.md5(filecontent)
146
        
wanli's avatar
wanli committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
        with open(md5path, "wb") as f:
            f.write(newmd5)
        return newmd5
    
    def sign(self, content):
        ret = b""
        for i in range(int(len(content) / 2 )):
            ret += struct.pack("<B", int("0x%s" % (content[i*2:i*2+2]), 16))
        ret = ret + b'EVM is NB ++!'
        return ret

    def md5(self, filecontent):
        newmd5 = ''
        content = filecontent
        for i in range(3):
            md5 = hashlib.md5()   #获取一个md5加密算法对象
            md5.update(content)  #指定需要加密的字符串
            newmd5 = md5.hexdigest()  #获取加密后的16进制字符串
wanli's avatar
wanli committed
165
            eprint("md5 == ",newmd5)
wanli's avatar
wanli committed
166 167 168 169 170 171 172 173 174 175 176 177 178
            content = self.sign(newmd5)

        ret = b""
        for i in range(int(len(newmd5) / 2 )):
            ret += struct.pack("<B", int("0x%s" % (newmd5[i*2:i*2+2]), 16))
            
        return ret

    def packFile(self, info, level=9):
        fname = info["name"]
        fpath = os.sep.join([self._appDir, fname])
        fext = info["ext"]

wanli's avatar
wanli committed
179

wanli's avatar
wanli committed
180 181 182 183 184 185 186 187 188 189 190 191
        fileBytes = b""
        if fext == "md5":
            fileBytes += struct.pack("<B", 1)
        else:
            fileBytes += struct.pack("<B", 2)

        _name = fname + "\0"

        fileBytes += struct.pack("<B", len(_name))
        fileBytes += struct.pack("<%ds" % len(_name), fname.encode("utf-8"))
        with open(fpath, "rb") as fc:
            fileContentBytes = fc.read()
wanli's avatar
wanli committed
192 193
            eprint(info["name"])
            eprint(len(fileContentBytes))
wanli's avatar
wanli committed
194 195 196 197 198 199 200
            fileBytes += struct.pack("<L", len(fileContentBytes))
        
        if fext == "md5":
            fileCompressBytes = fileContentBytes
        else:
            fileCompressBytes = self.compress()(fileContentBytes, level)

wanli's avatar
wanli committed
201 202 203
        eprint("===",fileCompressBytes[0])
        eprint(fileCompressBytes)

wanli's avatar
wanli committed
204
        fileBytes += struct.pack("<L", len(fileCompressBytes))
wanli's avatar
wanli committed
205 206
        eprint(fileBytes)

wanli's avatar
wanli committed
207
        fileBytes += fileCompressBytes
wanli's avatar
wanli committed
208

wanli's avatar
wanli committed
209 210 211 212 213 214 215 216 217
        return fileBytes

    def pack(self, level=9):
        for i in range(10):
            infos = self.epkInfo()
        # infos = self.epkInfo()
        # infos = self.epkInfo()
        epkFileBytes = b""

wanli's avatar
wanli committed
218
        ret = None
wanli's avatar
wanli committed
219 220 221 222 223
        epkFileContentBytes = b""
        file_count = len(infos["files"])
        with open(self._epkName, "wb") as f:
            for info in infos["files"]:
                epkFileContentBytes += self.packFile(info)
wanli's avatar
wanli committed
224
            
wanli's avatar
wanli committed
225 226 227 228

            epkFileContentLength = len(epkFileContentBytes)

            epkFileBytes += self.header(md5_offset= 8 + epkFileContentLength, file_count=file_count)
wanli's avatar
wanli committed
229

wanli's avatar
wanli committed
230
            epkFileBytes += epkFileContentBytes
wanli's avatar
wanli committed
231
            
wanli's avatar
wanli committed
232
            epkmd5Bytes = self.md5(epkFileBytes)
wanli's avatar
wanli committed
233

wanli's avatar
wanli committed
234
            epkFileBytes += struct.pack("<H", len(epkmd5Bytes))
wanli's avatar
wanli committed
235

wanli's avatar
wanli committed
236
            epkFileBytes += epkmd5Bytes
wanli's avatar
wanli committed
237

wanli's avatar
wanli committed
238
            crcBytes = zlib.crc32(epkFileBytes)
wanli's avatar
wanli committed
239

wanli's avatar
wanli committed
240
            epkFileBytes += struct.pack("<L", crcBytes)
wanli's avatar
wanli committed
241

wanli's avatar
wanli committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
            f.write(epkFileBytes)

            ret = {
                "epkfile": self._epkName,
                "epk_filecontent_size": epkFileContentLength,
                "md5_offset": 10 + epkFileContentLength,
                "file_count": file_count,
                "md5_length": len(epkmd5Bytes),
                "md5": epkmd5Bytes,
                "raw_crc": hex(crcBytes),
                "compress_level": level,
                "buff_length": len(epkFileBytes)
            }

            pprint.pprint(ret)

wanli's avatar
wanli committed
258 259
        return ret

wanli's avatar
wanli committed
260 261 262 263 264 265 266 267

def main(path, appName, algorithm):
    epk = EpkApp(appName, path, algorithm)
    epk.pack()


if __name__ == '__main__':
    main(sys.argv[1], sys.argv[2], sys.argv[3])