const Path=require("path"),Fs=require("fire-fs"),Encrypt=Editor.require("packages://encrypt-tool/panel/index.js"),CfgUtil=Editor.require("packages://hot-update-tools/core/CfgUtil.js"),Util=Editor.require("packages://hot-update-tools/core/Util.js"),OutPut=Editor.require("packages://hot-update-tools/core/OutPut.js"),GoogleAnalytics=Editor.require("packages://hot-update-tools/core/GoogleAnalytics.js"),Electron=require("electron"); const pngBufferHeader = { bufBegin: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], bufEnd: [0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82], suffix: '.png' }; Vue.component("manifest-gen",{ template:Fs.readFileSync(Editor.url("packages://hot-update-tools/panel/manifest-gen.html"),"utf-8"), mixins:[Editor.require("packages://hot-update-tools/panel/mixin.js")], data:()=>( { selectDestType:"", isUsedEncrypt:!1, version:"", serverRootDir:"", remoteServerVersion:"", hotAddressArray:[], resourceRootDir:"", genManifestDir:"", isShowUseAddrBtn:!1, isShowDelAddrBtn:!1, encryptSign: "", encryptPass: "", signKey: "civiTool", passWord: "civiTool2020", engineVersion:"", rootPath: "", cfgPath: "", totalPng: [], hasEncrypt: 0, hasBuildProj: !1, } ), computed:{ isValidResDir(){ return!(!this.resourceRootDir||!Fs.existsSync(this.resourceRootDir)) } }, created(){ this.$nextTick(()=>{ let e=CfgUtil.cfgData; e&&(this.selectDestType=e.selectDestType,this.isUsedEncrypt=e.isUsedEncrypt,this.version=e.version,this.serverRootDir=e.serverRootDir,this.resourceRootDir=e.resourceRootDir,this.hotAddressArray=e.hotAddressArray||[]),this.genManifestDir=OutPut.manifestDir,this._getRemoteServerVersion(),this._initResourceBuild() }) }, methods:{ _initResourceBuild(){ let e=Editor.Project.path,t=Path.join(e,"local/builder.json"); if(Fs.existsSync(t)){ let s=JSON.parse(Fs.readFileSync(t,"utf-8")),i=s.buildPath,r=Path.join(e,i),o=Path.join(r,`jsb-${s.template}`); if(!Fs.existsSync(o)){ let e=Path.join(r,s.platform);Fs.existsSync(e)&&(o=e) } this._checkResourceRootDir(o) }else this.log("发现没有构建项目, 使用前请先构建项目!") //初始化加密资源 this.initPluginCfg(); }, _isVersionPass(e,t){ if(void 0===e||null===e||void 0===t||null===t)return!1; let s=e.split("."),i=t.split("."),r=s.length>i.length?s.length:i.length; for(let e=0;eparseInt(r))return!0 } return!1 }, _updateShowUseAddrBtn(){ let e=this.$els.address.value; this.serverRootDir===e&&(this.isShowUseAddrBtn=!1) }, _updateShowEncryptInfo() { if(this.isUsedEncrypt==1) { this.encryptSign = "签名:"+this.signKey; this.encryptPass = "密码:"+this.passWord; } }, _addHotAddress(e){ let t=!0; for(let s=0;s{ if(4===t.readyState&&t.status>=200&&t.status<400){ let e=t.responseText,s=null; try{ s=JSON.parse(e) } catch(e){ return void this.log("获取远程版本号失败!") } this.remoteServerVersion=s.version }else t.status }),t.open("get",e,!0),t.setRequestHeader("If-Modified-Since","0"),t.send() }, onClickGenCfg(e){ GoogleAnalytics.eventCustom("GenManifest"),!this.version||this.version.length<=0?this.log("[生成] 版本号未填写"):!this.serverRootDir||this.serverRootDir.length<=0?this.log("[生成] 服务器地址未填写"):0!==this.resourceRootDir.length?this._checkResourceRootDir(this.resourceRootDir)&&(!this.genManifestDir||this.genManifestDir.length<=0?this.log("[生成] manifest文件生成地址未填写"):Fs.existsSync(this.genManifestDir)?(this._saveConfig(),this.onDoEncrypt(this.version,this.serverRootDir,this.resourceRootDir,this.genManifestDir)):this.log("[生成] manifest存储目录不存在: "+this.genManifestDir)):this.log("[生成] 请先指定 ") }, onClickOpenVersionDir(){this.openDir(OutPut.versionsDir+'/'+this.selectDestType)}, onOpenManifestDir(){this.openDir(this.genManifestDir)}, onOpenResourceDir(){this.openDir(this.resourceRootDir)}, onSelectResourceRootDir(){ let e=Editor.Dialog.openFile({title:"选择构建后的根目录",defaultPath:Editor.projectInfo.path,properties:["openDirectory"]}); if(-1!==e){ let t=e[0]; this._checkResourceRootDir(t)&&(this.resourceRootDir=t,this._saveConfig()) } }, onBtnClickDelSelectedHotAddress(){ let e=this.$els.address.value; if(this.hotAddressArray.length>0){ let t=!1; for(let s=0;s{}); } let d = h+'/UnZip/'; if(!Fs.existsSync(d)) { this.log("UnZip文件夹不存在,自动创建文件夹!!!"); Fs.mkdir(d,()=>{}); } this._saveConfig(); }, onChangeSelectHotAddress(e){ GoogleAnalytics.eventCustom("ChangeSelectHotAddress"),this.isShowUseAddrBtn=!0,this.isShowDelAddrBtn=!0,this._updateShowUseAddrBtn() }, userLocalIP(){ GoogleAnalytics.eventCustom("useLocalIP"); let e=Editor.require("packages://hot-update-tools/core/Util.js").getLocalIP(); e.length>0&&(this.serverRootDir="http://"+e,this.onInPutUrlOver(null)) }, onInPutUrlOver(e){ let t=this.serverRootDir; if("http://"===t||"https://"===t||"http"===t||"https"===t||"http:"===t||"https:"===t)return; let s=t.indexOf("http://"),i=t.indexOf("https://"); if(-1===s&&-1===i){ /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(([A-Za-z0-9-~]+)\.)+([A-Za-z0-9-~\/])+$/.test(t)||(this.log(t+" 不是以http://https://开头,或者不是网址, 已经自动修改"),this.serverRootDir="http://"+this.serverRootDir,this._getRemoteServerVersion()) }else this._getRemoteServerVersion(); this._addHotAddress(this.serverRootDir),this._updateShowUseAddrBtn(),this._saveConfig() }, _saveConfig(){ let e={selectDestType:this.selectDestType,isUsedEncrypt:this.isUsedEncrypt,version:this.version,serverRootDir:this.serverRootDir,resourceRootDir:this.resourceRootDir,genManifestDir:OutPut.manifestDir,localServerPath:this.localServerPath,hotAddressArray:this.hotAddressArray}; CfgUtil.saveConfig(e) }, onInputVersionOver(){ let e=CfgUtil.cfgData.genVersion,t=(CfgUtil.cfgData.buildTime,CfgUtil.cfgData.genTime,this.remoteServerVersion); null!==t&&void 0!==t&&(this._isVersionPass(this.version,t)?(this.log("上次构建时版本号: "+e),this._isVersionPass(this.version,e)?this.log("版本通过验证!"):this.log("[Warning] 要构建的版本低于上次构建版本: "+this.version+"<="+e)):this.log("[Warning] version 填写的版本低于远程版本")),this._saveConfig() }, onStopTouchEvent(e){e.preventDefault(),e.stopPropagation()}, onBtnClickHelpDoc(){ GoogleAnalytics.eventDoc(); Electron.shell.openExternal("https://tidys.github.io/plugin-docs-oneself/docs/hot-update-tools/") }, onBtnClickTellMe(){ GoogleAnalytics.eventQQ(); Electron.shell.openExternal("http://wpa.qq.com/msgrd?v=3&uin=774177933&site=qq&menu=yes") }, _genVersion(e,t,s,i){ this.log("[Build] 开始生成manifest配置文件...."); let r={version:e,packageUrl:t,remoteManifestUrl:"",remoteVersionUrl:"",assets:{},searchPaths:[]};"/"===t[t.length-1]?(r.remoteManifestUrl=t+"project.manifest",r.remoteVersionUrl=t+"version.manifest"):(r.remoteManifestUrl=t+"/project.manifest",r.remoteVersionUrl=t+"/version.manifest"); let o=i,n=s,l=(e,t)=>{ let s=Fs.statSync(e); if(!s.isDirectory())return; let i,r,o,a,h,d=Fs.readdirSync(e); for(let c=0;c{ try{ Fs.mkdirSync(e) }catch(e){ if("EEXIST"!==e.code) throw e } })(o),Fs.writeFileSync(a,JSON.stringify(r)),this.log("[Build] 生成 project.manifest成功"),delete r.assets,delete r.searchPaths,Fs.writeFileSync(h,JSON.stringify(r)),this.log("[Build] 生成 version.manifest成功"),this._packageVersion() }, _packageDir(e,t){ let s=Fs.readdirSync(e); for(let i=0;i{this.log("[Pack] 打包成功: "+d)}).on("error",e=>{this.log("[Pack] 打包失败:"+e.message)}) // Fs.existsSync(o)?Fs.existsSync(a)?(FsExtra.emptyDirSync(e),FsExtra.copySync(s,Path.join(e,"src")),FsExtra.copySync(l,Path.join(e,t)),FsExtra.copyFileSync(o,Path.join(e,"project.manifest")),FsExtra.copyFileSync(a,Path.join(e,"version.manifest")),this.log(`已经将热更包copy到: ${e}`),this._updateServerVersion()):this.log(a+"不存在, 请点击生成热更包"):this.log(o+"不存在, 请点击生成热更包") // this._copyFiles(f); }, _copyFiles(f) { let{manifestResDir:t}=Util,{resourceRootDir:r}=this,{manifestDir:i}=OutPut; let s=Path.join(r,"src"),l=Path.join(r,t); if(!Fs.existsSync(r))return void this.log("资源目录不存在: "+r+", 请先构建项目"); if(!Fs.existsSync(s))return void this.log(r+"不存在src目录, 无法拷贝文件"); if(!Fs.existsSync(l))return void this.log(r+"不存在res目录, 无法拷贝文件"); let o=Path.join(i,"project.manifest"),a=Path.join(i,"version.manifest"); if(Fs.existsSync(o)){ if(Fs.existsSync(a)){ Fs.emptyDirSync(f); Fs.copySync(s,Path.join(f,"src")) Fs.copySync(l,Path.join(f,t)) Fs.copyFileSync(o,Path.join(f,"project.manifest")) Fs.copyFileSync(a,Path.join(f,"version.manifest")) this.log(`已经将热更包copy到: ${f}`) // this._updateServerVersion() }else{ this.log(a+"不存在, 请点击生成热更包") } }else{ this.log(o+"不存在, 请点击生成热更包") } }, _checkResourceRootDir(e){ if(Fs.existsSync(e)){ let t=Path.join(e,"src"); if(!Fs.existsSync(t))return this.log(`没有发现 ${t}, 请先构建项目.`),!1; let s=["res","assets"]; for(let t=0;t{ this._genVersion(e,t,s,ii); }); } if(this.totalPng.length <= 0) { this.hasBuildProj = false; this._genVersion(e,t,s,ii); } }, changeEncryptSignPass(buf) { let splitStr2 = "//civi encrypt signPass,don't delete//"; let tmp2 = buf.split(splitStr2); if (tmp2.length == 3) { buf = tmp2[0] + splitStr2 + "\n\ ,_signKey(\"" + this.signKey + "\")\n\ ,_passWord(\"" + this.passWord + "\")\n\ //civi encrypt signPass,don't delete//" + tmp2[2]; } return buf; }, getBuildTips(){ // if(this.buildTypeSelect== 1) // { // return "jsb-default"; // } return "jsb-link"; }, versionCompareHandle(versionA, versionB) { var vA = versionA.split('.'); var vB = versionB.split('.'); for (var i = 0; i < vA.length; ++i) { var a = parseInt(vA[i]); var b = parseInt(vB[i] || 0); if (a === b) { continue; } else { return a - b; } } if (vB.length > vA.length) { return -1; } else { return 0; } }, initPluginCfg() { let e = Editor.libraryPath; this.rootPath = e.substring(0, e.length - 7); this.cfgPath = this.rootPath + "/packages/encrypt-tool/cfg.json"; this.log("项目路径为" + e.substring(0, e.length - 7)); this.readCfg(); this.checkBuildDir(); this._updateShowEncryptInfo(); }, checkBuildDir(){ let target = this.getBuildTips() this.log("当前加密目标模板为"+target+",即"+target+"目录下的png将被加密"); if (Fs.existsSync(this.rootPath + "/build/"+target)) { let projCfg = this.rootPath + "/build/"+target+"/.cocos-project.json"; let buf = Fs.readFileSync(projCfg,"UTF-8"); let cfg = JSON.parse(buf); this.engineVersion = cfg.engine_version; this.log("当前引擎版本为" + this.engineVersion); let ret = this.versionCompareHandle(this.engineVersion,"2.4.0"); if (ret >= 0) { this.isNewVersion = true; }; }else{ this.log("项目还未构建,请先构建"+target+"项目"); } }, saveCfg() { let newData = this.signKey + "civiEncrypt" + this.passWord + "civiEncrypt"+this.buildTypeSelect; Fs.writeFile(this.cfgPath, newData,function(error) { }); }, readCfg() { if (Fs.existsSync(this.cfgPath)) { let st = Fs.readFileSync(this.cfgPath, "UTF-8"); let signData = st.split("civiEncrypt"); if (signData.length > 1) { this.signKey = signData[0]; this.passWord = signData[1]; if (signData.length > 2) { this.buildTypeSelect = signData[2]; }; this.log("上次签名为" + this.signKey); this.log("上次密码为" + this.passWord); }; } else { this.log("未检测到历史配置"); } }, stringToHex(str) { if (str === "")     return "";   var hexCharCode = [];   for (var i = 0; i < str.length; i++) { hexCharCode.push((str.charCodeAt(i)));   }   return hexCharCode; }, checkPngSig(fileBuffer) { let isEqual = false // 判断标识头前缀 if (pngBufferHeader.bufBegin) { const buf = Buffer.from(pngBufferHeader.bufBegin); isEqual = buf.equals(fileBuffer.slice(0, pngBufferHeader.bufBegin.length)); } // 判断标识头后缀 if (isEqual && pngBufferHeader.bufEnd) { const buf = Buffer.from(pngBufferHeader.bufEnd); isEqual = buf.equals(fileBuffer.slice(-pngBufferHeader.bufEnd.length)); } if (isEqual) { return pngBufferHeader.suffix; } return '' }, walk(path) { var isPNG = function(pathStr) { let extname = Path.extname(pathStr); if (extname == ".png" || extname == ".PNG") { let buf = Fs.readFileSync(pathStr); let sig = this.checkPngSig(buf); if (sig == ".png") { return true; }; } else { return false; } }.bind(this); //先取出文件夹 var dirList = Fs.readdirSync(path); //遍历文件夹 dirList.forEach(function(item) { //判断是不是文件夹 if (Fs.statSync(path + '/' + item).isDirectory()) { if (this.isNewVersion) { if (item == "internal" ) { //默认的不管pass }else{ this.walk(path + '/' + item); } }else{ this.walk(path + '/' + item); } } else { //判断是不是PNG图片 if (isPNG(path + '/' + item)) { //进行加密 this.totalPng.push(path + '/' + item); } else { // pass } } }.bind(this)); }, beginEncrypt(pathStr, callback) { let buf = Fs.readFileSync(pathStr); let newData = this.encryptPng(buf); Fs.writeFile(pathStr, newData,function(error) { // this.log(pathStr + "加密完成"); this.hasEncrypt = this.hasEncrypt + 1; if (this.hasEncrypt == this.totalPng.length) { this.log("全部加密完成"); this.log("png资源加密完成"); this.hasBuildProj = false; if(callback) { callback(); } }; }.bind(this)); }, encryptPng(fileBuffer) { //预处理,先切头,再去尾 let cutHeader = fileBuffer.slice(pngBufferHeader.bufBegin.length); let iendData = cutHeader.slice( - pngBufferHeader.bufEnd.length); let lastData; const buf = Buffer.from(pngBufferHeader.bufEnd); if (buf.equals(iendData)) { lastData = cutHeader.slice(0, -pngBufferHeader.bufEnd.length); } else { lastData = cutHeader; } let k = this.stringToHex(this.passWord); let klen = k.length; let kindex = 0; for (var i = 0; i < lastData.length; i++) { if (kindex >= klen) { kindex = 0; }; lastData[i] = lastData[i] ^ k[kindex]; kindex = kindex + 1; }; let sign = new Buffer(this.signKey); let finalData = Buffer.concat([sign, lastData]); return finalData; }, } });