index.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. "use strict";
  2. window.packageRoot = "packages://encrypt-tool/";
  3. var Fs = require('fs');
  4. var Path = require('path');
  5. const jpgBufferHeader = {
  6. bufBegin: [0xff, 0xd8],
  7. bufEnd: [0xff, 0xd9],
  8. suffix: '.jpg'
  9. };
  10. const pngBufferHeader = {
  11. bufBegin: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
  12. bufEnd: [0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82],
  13. suffix: '.png'
  14. };
  15. //暂时没用,存着备用
  16. const imageBufferHeaders = [{
  17. bufBegin: [0x00, 0x00, 0x02, 0x00, 0x00],
  18. suffix: '.tga'
  19. },
  20. {
  21. bufBegin: [0x00, 0x00, 0x10, 0x00, 0x00],
  22. suffix: '.rle'
  23. },
  24. {
  25. bufBegin: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61],
  26. suffix: '.gif'
  27. },
  28. {
  29. bufBegin: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61],
  30. suffix: '.gif'
  31. },
  32. {
  33. bufBegin: [0x42, 0x4d],
  34. suffix: '.bmp'
  35. },
  36. {
  37. bufBegin: [0x0a],
  38. suffix: '.pcx'
  39. },
  40. {
  41. bufBegin: [0x49, 0x49],
  42. suffix: '.tif'
  43. },
  44. {
  45. bufBegin: [0x4d, 0x4d],
  46. suffix: '.tif'
  47. },
  48. {
  49. bufBegin: [0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x20, 0x20],
  50. suffix: '.ico'
  51. },
  52. {
  53. bufBegin: [0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x20, 0x20],
  54. suffix: '.cur'
  55. },
  56. {
  57. bufBegin: [0x46, 0x4f, 0x52, 0x4d],
  58. suffix: '.iff'
  59. },
  60. {
  61. bufBegin: [0x52, 0x49, 0x46, 0x46],
  62. suffix: '.ani'
  63. }]
  64. Editor.Panel.extend({
  65. style:`
  66. h2 {
  67. color: #f90;
  68. font-size: 15px;
  69. width: 200px;
  70. text-align: left
  71. }
  72. .middle {
  73. width: 200px;
  74. margin-left: 100px;
  75. align-items: center;
  76. }
  77. .inputstyle {
  78. width: 200px
  79. }
  80. .okstyle {
  81. margin-top: 50px;
  82. width: 100px;
  83. margin-left: 100px;
  84. }`,
  85. template:`
  86. <div class="middle layout vertical" >
  87. <h2>请输入签名:</h2>
  88. <ui-input placeholder="sign" class="inputstyle" v-value="signKey"></ui-input>
  89. </div>
  90. <div class="middle layout vertical">
  91. <h2>请输入密码:</h2>
  92. <ui-input placeholder="password" class="inputstyle" v-value="passWord"></ui-input>
  93. </div>
  94. <div class="middle layout vertical">
  95. <h2>请选择构项目建模板:</h2>
  96. <ui-select value="1" class="inputstyle" v-on:change="onBuildTypeChange" v-value="buildTypeSelect" id="buildType">
  97. <option value="0">link</option>
  98. <option value="1">default</option>
  99. </ui-select>
  100. </div>
  101. <ui-button class="okstyle" v-on:confirm="onDoEncrypt">加密</ui-button>
  102. <div style="width: 100%; height: 150px;margin: 0 0 0 0;">
  103. <h2 style="margin: 0 0 0 0">日志:</h2>
  104. <textarea class="flex-1" id="logTextArea" v-model="logView" style="width: 100%; height: 200px; background: #252525; resize: none; color: #fd942b; border-color: #fd942b;" disabled></textarea>
  105. </div>`,
  106. $:{
  107. logTextArea: "#logTextArea",
  108. },
  109. ready() {
  110. let logarea = this.$logTextArea;
  111. window.plugin = new window.Vue({
  112. el: this.shadowRoot,
  113. created: function() {
  114. this.initPluginCfg();
  115. },
  116. init: function() {
  117. },
  118. data: {
  119. logView: "",
  120. //默认的是这个
  121. signKey: "civiTool",
  122. passWord: "civiTool2020",
  123. engineVersion:"",
  124. rootPath: "",
  125. cfgPath: "",
  126. totalPng: [],
  127. hasEncrypt: 0,
  128. hasBuildProj: false,
  129. isNewVersion:false,
  130. buildTypeSelect:0,
  131. },
  132. computed: {},
  133. methods: {
  134. stringToHex(str) {
  135. if (str === "")    
  136. return "";  
  137. var hexCharCode = [];  
  138. for (var i = 0; i < str.length; i++) {
  139. hexCharCode.push((str.charCodeAt(i)));  
  140. }  
  141. return hexCharCode;
  142. },
  143. checkPngSig(fileBuffer) {
  144. let isEqual = false
  145. // 判断标识头前缀
  146. if (pngBufferHeader.bufBegin) {
  147. const buf = Buffer.from(pngBufferHeader.bufBegin);
  148. isEqual = buf.equals(fileBuffer.slice(0, pngBufferHeader.bufBegin.length));
  149. }
  150. // 判断标识头后缀
  151. if (isEqual && pngBufferHeader.bufEnd) {
  152. const buf = Buffer.from(pngBufferHeader.bufEnd);
  153. isEqual = buf.equals(fileBuffer.slice(-pngBufferHeader.bufEnd.length));
  154. }
  155. if (isEqual) {
  156. return pngBufferHeader.suffix;
  157. }
  158. return ''
  159. },
  160. walk(path) {
  161. var isPNG = function(pathStr) {
  162. let extname = Path.extname(pathStr);
  163. if (extname == ".png" || extname == ".PNG") {
  164. let buf = Fs.readFileSync(pathStr);
  165. let sig = this.checkPngSig(buf);
  166. if (sig == ".png") {
  167. return true;
  168. };
  169. } else {
  170. return false;
  171. }
  172. }.bind(this);
  173. //先取出文件夹
  174. var dirList = Fs.readdirSync(path);
  175. //遍历文件夹
  176. dirList.forEach(function(item) {
  177. //判断是不是文件夹
  178. if (Fs.statSync(path + '/' + item).isDirectory()) {
  179. if (this.isNewVersion) {
  180. if (item == "internal" ) {
  181. //默认的不管pass
  182. }else{
  183. this.walk(path + '/' + item);
  184. }
  185. }else{
  186. this.walk(path + '/' + item);
  187. }
  188. } else {
  189. //判断是不是PNG图片
  190. if (isPNG(path + '/' + item)) {
  191. //进行加密
  192. this.totalPng.push(path + '/' + item);
  193. } else {
  194. // pass
  195. }
  196. }
  197. }.bind(this));
  198. },
  199. onDoEncrypt() {
  200. if (this.hasBuildProj) {
  201. this.addLog("请不要重复点击");
  202. return;
  203. };
  204. Editor.log("encrypt tool init")
  205. this.logView = "";
  206. this.hasEncrypt = 0;
  207. this.totalPng = [];
  208. this.hasBuildProj = true;
  209. let target = this.getBuildTips();
  210. if (Fs.existsSync(this.rootPath + "/build/"+target)) {
  211. let imgh = this.rootPath + "/build/jsb-default/frameworks/cocos2d-x/cocos/platform/CCImage.h";
  212. let imgcpp = this.rootPath + "/build/jsb-default/frameworks/cocos2d-x/cocos/platform/CCImage.cpp";
  213. if (this.buildTypeSelect == 0) {
  214. let simulatorP = Editor.url('unpack://simulator', 'utf8');
  215. let Cpath = Path.resolve(simulatorP, '..');
  216. imgh = Cpath + "/cocos/platform/CCImage.h";
  217. imgcpp = Cpath + "/cocos/platform/CCImage.cpp";
  218. this.addLog("img.h:" + imgh);
  219. this.addLog("img.cpp:" + imgcpp);
  220. };
  221. let buf = Fs.readFileSync(imgh, "UTF-8");
  222. let tmp = buf.split("//civi encrypt,don't delete//");
  223. if (tmp.length < 2) {
  224. this.addLog("CCImage尚未添加代码,准备添加代码");
  225. let finalRes = this.addEncryptScriptHeader(buf);
  226. Fs.writeFileSync(imgh, finalRes);
  227. let cppbuf = Fs.readFileSync(imgcpp, "UTF-8");
  228. let finalSres = this.addEncryptScript(cppbuf);
  229. Fs.writeFileSync(imgcpp, finalSres);
  230. this.addLog("CCImage添加代码完成");
  231. } else {
  232. this.addLog("CCImage已经添加代码");
  233. this.addLog("更新签名及密码");
  234. let cppbuf = Fs.readFileSync(imgcpp, "UTF-8");
  235. let changeBuf = this.changeEncryptSignPass(cppbuf);
  236. Fs.writeFileSync(imgcpp, changeBuf);
  237. }
  238. } else {
  239. Editor.log("项目还未构建,请先构建"+target+"项目");
  240. this.addLog("项目还未构建,请先构建"+target+"项目");
  241. this.hasBuildProj = false
  242. return;
  243. }
  244. this.saveCfg();
  245. this.addLog("开始加密...请耐心等待");
  246. if (this.isNewVersion) {
  247. this.walk(this.rootPath + "/build/"+target+"/assets");
  248. }else{
  249. //this.walk(this.rootPath + "/build/"+target+"/res/raw-assets");
  250. this.walk(this.rootPath + "/library");
  251. }
  252. this.addLog("共检测到" + this.totalPng.length + "张需要加密的png");
  253. for (var i = 0; i < this.totalPng.length; i++) {
  254. let pathStr = this.totalPng[i];
  255. this.beginEncrypt(pathStr);
  256. };
  257. },
  258. beginEncrypt(pathStr) {
  259. let buf = Fs.readFileSync(pathStr);
  260. let newData = this.encryptPng(buf);
  261. Fs.writeFile(pathStr, newData,function(error) {
  262. this.addLog(pathStr + "加密完成");
  263. this.hasEncrypt = this.hasEncrypt + 1;
  264. if (this.hasEncrypt == this.totalPng.length) {
  265. this.addLog("全部加密完成");
  266. Editor.log("png资源加密完成");
  267. this.hasBuildProj = false;
  268. };
  269. }.bind(this));
  270. },
  271. onBuildTypeChange(e)
  272. {
  273. let t = e.target.value;
  274. this.checkBuildDir();
  275. },
  276. encryptPng(fileBuffer) {
  277. //预处理,先切头,再去尾
  278. let cutHeader = fileBuffer.slice(pngBufferHeader.bufBegin.length);
  279. let iendData = cutHeader.slice( - pngBufferHeader.bufEnd.length);
  280. let lastData;
  281. const buf = Buffer.from(pngBufferHeader.bufEnd);
  282. if (buf.equals(iendData)) {
  283. lastData = cutHeader.slice(0, -pngBufferHeader.bufEnd.length);
  284. } else {
  285. lastData = cutHeader;
  286. }
  287. let k = this.stringToHex(this.passWord);
  288. let klen = k.length;
  289. let kindex = 0;
  290. for (var i = 0; i < lastData.length; i++) {
  291. if (kindex >= klen) {
  292. kindex = 0;
  293. };
  294. lastData[i] = lastData[i] ^ k[kindex];
  295. kindex = kindex + 1;
  296. };
  297. let sign = new Buffer(this.signKey);
  298. let finalData = Buffer.concat([sign, lastData]);
  299. return finalData;
  300. },
  301. addLog(t) {
  302. let i = new Date;
  303. this.logView += "[" + i.toLocaleString() + "]: " + t + "\n",
  304. setTimeout(function() {
  305. logarea.scrollTop = logarea.scrollHeight;
  306. },10)
  307. },
  308. versionCompareHandle(versionA, versionB) {
  309. var vA = versionA.split('.');
  310. var vB = versionB.split('.');
  311. for (var i = 0; i < vA.length; ++i) {
  312. var a = parseInt(vA[i]);
  313. var b = parseInt(vB[i] || 0);
  314. if (a === b) {
  315. continue;
  316. }
  317. else {
  318. return a - b;
  319. }
  320. }
  321. if (vB.length > vA.length) {
  322. return -1;
  323. }
  324. else {
  325. return 0;
  326. }
  327. },
  328. initPluginCfg() {
  329. let e = Editor.libraryPath;
  330. this.rootPath = e.substring(0, e.length - 7);
  331. this.cfgPath = this.rootPath + "/packages/encrypt-tool/cfg.json";
  332. this.addLog("项目路径为" + e.substring(0, e.length - 7));
  333. this.readCfg();
  334. this.checkBuildDir();
  335. },
  336. checkBuildDir()
  337. {
  338. let target = this.getBuildTips()
  339. this.addLog("当前加密目标模板为"+target+",即"+target+"目录下的png将被加密");
  340. if (Fs.existsSync(this.rootPath + "/build/"+target)) {
  341. let projCfg = this.rootPath + "/build/"+target+"/.cocos-project.json";
  342. let buf = Fs.readFileSync(projCfg,"UTF-8");
  343. let cfg = JSON.parse(buf);
  344. this.engineVersion = cfg.engine_version;
  345. this.addLog("当前引擎版本为" + this.engineVersion);
  346. let ret = this.versionCompareHandle(this.engineVersion,"2.4.0");
  347. if (ret >= 0) {
  348. this.isNewVersion = true;
  349. };
  350. }else{
  351. Editor.log("项目还未构建,请先构建"+target+"项目");
  352. this.addLog("项目还未构建,请先构建"+target+"项目");
  353. }
  354. },
  355. saveCfg() {
  356. let newData = this.signKey + "civiEncrypt" + this.passWord + "civiEncrypt"+this.buildTypeSelect;
  357. Fs.writeFile(this.cfgPath, newData,function(error) {
  358. });
  359. },
  360. readCfg() {
  361. if (Fs.existsSync(this.cfgPath)) {
  362. let st = Fs.readFileSync(this.cfgPath, "UTF-8");
  363. let signData = st.split("civiEncrypt");
  364. if (signData.length > 1) {
  365. this.signKey = signData[0];
  366. this.passWord = signData[1];
  367. if (signData.length > 2) {
  368. this.buildTypeSelect = signData[2];
  369. };
  370. this.addLog("上次签名为" + this.signKey);
  371. this.addLog("上次密码为" + this.passWord);
  372. };
  373. } else {
  374. this.addLog("未检测到历史配置");
  375. }
  376. },
  377. getBuildTips()
  378. {
  379. if(this.buildTypeSelect== 1)
  380. {
  381. return "jsb-default";
  382. }
  383. return "jsb-link";
  384. },
  385. addEncryptScriptHeader(buf) {
  386. let splitStr = "PNG,";
  387. let addEnum = buf.split(splitStr);
  388. if (addEnum.length == 2) {
  389. buf = addEnum[0] + splitStr + "\n\
  390. //civi encrypt,don't delete//\n\
  391. ENCRYPTEDPNG, //加密后的Png图片\n\
  392. //civi encrypt,don't delete//" + addEnum[1];
  393. };
  394. let splitStr1 = "bool initWithImageFile(const std::string& path);";
  395. let tmp1 = buf.split(splitStr1);
  396. if (tmp1.length == 2) {
  397. buf = tmp1[0] + splitStr1 + "\n\
  398. //civi encrypt,don't delete//\n\
  399. void deEncryptPng(unsigned char **copyData, const char *key, ssize_t dataLen);\n\
  400. //civi encrypt,don't delete//" + tmp1[1];
  401. };
  402. let splitStr2 = "std::string _filePath;";
  403. let tmp2 = buf.split(splitStr2);
  404. if (tmp2.length == 2) {
  405. buf = tmp2[0] + splitStr2 + "\n\
  406. //civi encrypt signPass,don't delete//\n\
  407. std::string _signKey;\n\
  408. std::string _passWord;\n\
  409. //civi encrypt signPass,don't delete//" + tmp2[1];
  410. };
  411. let splitStr3 = "bool isEtc(const unsigned char * data, ssize_t dataLen);";
  412. let tmp3 = buf.split(splitStr3);
  413. if (tmp3.length == 2) {
  414. buf = tmp3[0] + splitStr3 + "\n\
  415. //civi encrypt,don't delete//\n\
  416. bool isEncryptedPng(const unsigned char * data,ssize_t dataLen);\n\
  417. //civi encrypt,don't delete//" + tmp3[1];
  418. };
  419. return buf;
  420. },
  421. addEncryptScript(buf) {
  422. let splitStr3 = ", _hasPremultipliedAlpha(false)";
  423. let addSignPass = buf.split(splitStr3);
  424. if (addSignPass.length == 2) {
  425. buf = addSignPass[0] + splitStr3 + "//civi encrypt signPass,don't delete//\n\
  426. ,_signKey(\"" + this.signKey + "\")\n\
  427. ,_passWord(\"" + this.passWord + "\")\n\
  428. //civi encrypt signPass,don't delete//" + addSignPass[1];
  429. };
  430. let splitStr4 = "NS_CC_END";
  431. let addEnum = buf.split(splitStr4);
  432. if (addEnum.length == 2) {
  433. buf = addEnum[0] + "//civi encrypt,don't delete//\n\
  434. void Image::deEncryptPng(unsigned char **copyData, const char *key, ssize_t dataLen)\
  435. {\n\
  436. static const unsigned char PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };\n\
  437. static const unsigned char PNG_IEND[] = { 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 };\n\
  438. unsigned char* data = *copyData;\n\
  439. memcpy(data, PNG_SIGNATURE, 8);\n\
  440. memcpy(data + (dataLen - 12), PNG_IEND, 12);\n\
  441. unsigned char* destart = data + 8;\n\
  442. unsigned char* de_end = data + dataLen - 13;\n\
  443. ssize_t keyLen = strlen(key);\n\
  444. ssize_t keyIndex = 0;\n\
  445. for(; destart <= de_end; destart++, keyIndex++)\
  446. {\n\
  447. if (keyIndex >= keyLen)\n\
  448. keyIndex = 0;\n\
  449. *destart ^= key[keyIndex];\n\
  450. }\n\
  451. }\n\
  452. bool Image::isEncryptedPng(const unsigned char *data, ssize_t dataLen)\n\
  453. {\n\
  454. if (dataLen > 8 && memcmp(\"wckj8888\", data, 8) == 0)\
  455. {\n\
  456. _signKey = \"wckj8888\";\n\
  457. }\
  458. if (dataLen <= _signKey.length() || memcmp(_signKey.c_str(), data, _signKey.length()) != 0)\
  459. {\n\
  460. return false;\n\
  461. }\n\
  462. return true;\n\
  463. }\n\
  464. //civi encrypt,don't delete//\n" + splitStr4;
  465. };
  466. let splitStr1 = "else if (isJpg(data, dataLen))";
  467. let tmp1 = buf.split(splitStr1);
  468. if (tmp1.length == 2) {
  469. buf = tmp1[0] + "//civi encrypt,don't delete//\n\
  470. else if (isEncryptedPng(data, dataLen))\
  471. {\n\
  472. return Format::ENCRYPTEDPNG;\n\
  473. }\n\
  474. //civi encrypt,don't delete//\n " + splitStr1 + tmp1[1];
  475. };
  476. let splitStr2 = "case Format::PNG:";
  477. let tmp2 = buf.split(splitStr2);
  478. if (tmp2.length == 2) {
  479. buf = tmp2[0] + "//civi encrypt,don't delete//\n\
  480. case Format::ENCRYPTEDPNG:\n\
  481. {\n\
  482. unsigned char* copyData = new unsigned char[unpackedLen + 20-_signKey.length()];\
  483. // 头部信息固定8 + 尾部信息固定12 - 插入密钥的长度\n\
  484. memcpy(copyData + 8, unpackedData + _signKey.length(), unpackedLen - _signKey.length());\n\
  485. deEncryptPng(&copyData, _passWord.c_str(), unpackedLen + 20-_signKey.length());\n\
  486. ret = initWithPngData(copyData, unpackedLen + 20-_signKey.length());\n\
  487. delete [] copyData;\n\
  488. }\n\
  489. break;\n\
  490. //civi encrypt,don't delete//\n " + splitStr2 + tmp2[1];
  491. };
  492. return buf;
  493. },
  494. changeEncryptSignPass(buf) {
  495. let splitStr2 = "//civi encrypt signPass,don't delete//";
  496. let tmp2 = buf.split(splitStr2);
  497. if (tmp2.length == 3) {
  498. buf = tmp2[0] + splitStr2 + "\n\
  499. ,_signKey(\"" + this.signKey + "\")\n\
  500. ,_passWord(\"" + this.passWord + "\")\n\
  501. //civi encrypt signPass,don't delete//" + tmp2[2];
  502. }
  503. return buf;
  504. },
  505. onBuildFinished(t) {
  506. }
  507. }
  508. })
  509. },
  510. messages: {
  511. "encrypt-tool:onBuildFinished":function(e, t) {
  512. }
  513. },
  514. });