fast-cocos 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@ function main() {
18
18
  return __awaiter(this, void 0, void 0, function* () {
19
19
  commander_1.program
20
20
  .name("xor")
21
- .description("XOR 图片文件加解密")
21
+ .description("XOR 图片文件加解密(输出与源文件同名,建议加密到单独目录)")
22
22
  .version(xor_1.XOR_VERSION)
23
23
  .argument("<mode>", "encrypt 或 decrypt", (v) => {
24
24
  if (v !== "encrypt" && v !== "decrypt") {
@@ -27,17 +27,16 @@ function main() {
27
27
  return v;
28
28
  })
29
29
  .argument("<input>", "输入文件或目录路径")
30
- .argument("[output]", "输出文件、目录路径(可选)")
30
+ .argument("[output]", "输出文件或目录(可选;目录时写入同名文件)")
31
31
  .option("-s, --sign <sign>", "签名", xor_1.DEFAULT_SIGN)
32
32
  .option("-k, --key <key>", "密钥", xor_1.DEFAULT_KEY)
33
- .option("-e, --encrypted-suffix <suffix>", "加密输出文件名后缀(不含扩展名时与原名组合,如 .enc)", xor_1.DEFAULT_ENCRYPTED_FILE_SUFFIX)
34
- .option("--ext <list>", "批量处理时的扩展名列表,逗号分隔(解密时默认会包含加密后缀对应扩展)")
33
+ .option("--ext <list>", "批量处理时的扩展名列表,逗号分隔")
35
34
  .addHelpText("after", `
36
35
  示例:
37
- fast-cocos xor encrypt sprite.png
38
- fast-cocos xor encrypt sprite.png -e .xor -s MyS1 -k MySecretKey123
36
+ fast-cocos xor encrypt a.png
37
+ fast-cocos xor encrypt a.png ./encrypted/
38
+ fast-cocos xor decrypt ./encrypted/a.png
39
39
  fast-cocos xor encrypt ./assets/ ./out/ --ext .png,.jpg
40
- fast-cocos xor decrypt ./out/sprite.enc -e .enc
41
40
  `)
42
41
  .action((mode, input, output) => {
43
42
  const o = commander_1.program.opts();
@@ -47,7 +46,6 @@ function main() {
47
46
  output,
48
47
  sign: o.sign,
49
48
  key: o.key,
50
- encryptedFileSuffix: o.encryptedSuffix,
51
49
  extensionsCsv: o.ext
52
50
  });
53
51
  });
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DEFAULT_ENCRYPTED_FILE_SUFFIX = exports.XOR_VERSION = exports.DEFAULT_IMAGE_EXTS = exports.DEFAULT_KEY = exports.DEFAULT_SIGN = void 0;
6
+ exports.XOR_VERSION = exports.DEFAULT_IMAGE_EXTS = exports.DEFAULT_KEY = exports.DEFAULT_SIGN = void 0;
7
7
  exports.xorTransform = xorTransform;
8
8
  exports.encryptFile = encryptFile;
9
9
  exports.decryptFile = decryptFile;
@@ -11,15 +11,14 @@ exports.batchProcess = batchProcess;
11
11
  exports.runXorCommand = runXorCommand;
12
12
  const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
+ const utils_1 = require("../../utils");
14
15
  /** 默认签名(4 字节) */
15
16
  exports.DEFAULT_SIGN = "CrP3";
16
17
  /** 默认密钥(16 字节) */
17
18
  exports.DEFAULT_KEY = "Xk9#mQ2$vL5@nR8w";
18
- /** 默认参与批量加密的图片扩展名 */
19
+ /** 默认参与批量处理的图片扩展名 */
19
20
  exports.DEFAULT_IMAGE_EXTS = [".png", ".jpg", ".jpeg", ".webp", ".bmp"];
20
21
  exports.XOR_VERSION = "1.0.0";
21
- /** 默认加密输出文件名后缀(如 sprite.png -> sprite.enc) */
22
- exports.DEFAULT_ENCRYPTED_FILE_SUFFIX = ".enc";
23
22
  /**
24
23
  * XOR 加密/解密(对称)
25
24
  */
@@ -33,24 +32,21 @@ function xorTransform(opts) {
33
32
  return result;
34
33
  }
35
34
  /**
36
- * 加密文件(签名头 + XOR
35
+ * 加密文件(签名头 + XOR),输出文件名与源文件 basename 相同(通常配合输出目录使用)
37
36
  */
38
37
  function encryptFile(opts) {
39
- var _a, _b, _c, _d;
38
+ var _a, _b, _c;
40
39
  const finalSign = (_a = opts.sign) !== null && _a !== void 0 ? _a : exports.DEFAULT_SIGN;
41
40
  const finalKey = (_b = opts.key) !== null && _b !== void 0 ? _b : exports.DEFAULT_KEY;
42
- const suffix = (_c = opts.encryptedFileSuffix) !== null && _c !== void 0 ? _c : exports.DEFAULT_ENCRYPTED_FILE_SUFFIX;
43
- let out = (_d = opts.outputPath) !== null && _d !== void 0 ? _d : null;
41
+ let out = (_c = opts.outputPath) !== null && _c !== void 0 ? _c : null;
44
42
  if (!out) {
45
- out = defaultEncryptedOutputPath(opts.inputPath, suffix);
43
+ out = opts.inputPath;
46
44
  }
47
45
  else if (out.endsWith("/") || out.endsWith(path_1.default.sep)) {
48
- const stem = path_1.default.basename(opts.inputPath, path_1.default.extname(opts.inputPath));
49
- out = path_1.default.join(out, stem + suffix);
46
+ out = path_1.default.join(out, path_1.default.basename(opts.inputPath));
50
47
  }
51
48
  else if (fs_1.default.existsSync(out) && fs_1.default.statSync(out).isDirectory()) {
52
- const stem = path_1.default.basename(opts.inputPath, path_1.default.extname(opts.inputPath));
53
- out = path_1.default.join(out, stem + suffix);
49
+ out = path_1.default.join(out, path_1.default.basename(opts.inputPath));
54
50
  }
55
51
  if (out === null) {
56
52
  throw new Error("输出路径解析失败");
@@ -61,30 +57,25 @@ function encryptFile(opts) {
61
57
  const signature = Buffer.from(finalSign, "utf-8");
62
58
  const output = Buffer.concat([signature, encrypted]);
63
59
  fs_1.default.writeFileSync(destPath, output);
64
- console.log(`加密: ${path_1.default.basename(opts.inputPath)} -> ${path_1.default.basename(destPath)} [sign:${finalSign}, key:${finalKey.substring(0, 4)}...]`);
60
+ console.info("\x1b[32m%s\x1b[0m", `加密: ${path_1.default.basename(opts.inputPath)} -> ${path_1.default.basename(destPath)} [sign:${finalSign}, key:${finalKey.substring(0, 4)}...]`);
65
61
  }
66
62
  /**
67
63
  * 解密文件(校验签名头 + XOR)
68
64
  * @returns 签名不匹配时为 false
69
65
  */
70
66
  function decryptFile(opts) {
71
- var _a, _b, _c, _d;
67
+ var _a, _b, _c;
72
68
  const finalSign = (_a = opts.sign) !== null && _a !== void 0 ? _a : exports.DEFAULT_SIGN;
73
69
  const finalKey = (_b = opts.key) !== null && _b !== void 0 ? _b : exports.DEFAULT_KEY;
74
- const suffix = (_c = opts.encryptedFileSuffix) !== null && _c !== void 0 ? _c : exports.DEFAULT_ENCRYPTED_FILE_SUFFIX;
75
- let out = (_d = opts.outputPath) !== null && _d !== void 0 ? _d : null;
70
+ let out = (_c = opts.outputPath) !== null && _c !== void 0 ? _c : null;
76
71
  if (!out) {
77
- out = defaultDecryptedOutputPath(opts.inputPath, suffix);
72
+ out = opts.inputPath;
78
73
  }
79
74
  else if (out.endsWith("/") || out.endsWith(path_1.default.sep)) {
80
- const base = path_1.default.basename(opts.inputPath);
81
- const stem = base.endsWith(suffix) ? base.slice(0, -suffix.length) : path_1.default.basename(opts.inputPath, path_1.default.extname(opts.inputPath));
82
- out = path_1.default.join(out, stem);
75
+ out = path_1.default.join(out, path_1.default.basename(opts.inputPath));
83
76
  }
84
77
  else if (fs_1.default.existsSync(out) && fs_1.default.statSync(out).isDirectory()) {
85
- const base = path_1.default.basename(opts.inputPath);
86
- const stem = base.endsWith(suffix) ? base.slice(0, -suffix.length) : path_1.default.basename(opts.inputPath, path_1.default.extname(opts.inputPath));
87
- out = path_1.default.join(out, stem);
78
+ out = path_1.default.join(out, path_1.default.basename(opts.inputPath));
88
79
  }
89
80
  if (out === null) {
90
81
  throw new Error("输出路径解析失败");
@@ -104,141 +95,95 @@ function decryptFile(opts) {
104
95
  return true;
105
96
  }
106
97
  /**
107
- * 递归批量处理目录
98
+ * 递归批量处理目录(输出与源文件同名;有 outputDir 时保持相对子目录结构)
108
99
  */
109
100
  function batchProcess(opts) {
110
- var _a, _b;
101
+ var _a;
111
102
  const { dirPath, mode, outputDir = null, sign = null, key = null } = opts;
112
- const suffix = (_a = opts.encryptedFileSuffix) !== null && _a !== void 0 ? _a : exports.DEFAULT_ENCRYPTED_FILE_SUFFIX;
113
- const extensions = (_b = opts.extensions) !== null && _b !== void 0 ? _b : defaultExtensionsForMode(mode, suffix);
114
- if (!fs_1.default.existsSync(dirPath)) {
115
- console.error(`目录不存在: ${dirPath}`);
103
+ const extensions = (_a = opts.extensions) !== null && _a !== void 0 ? _a : defaultExtensionsForMode(mode);
104
+ const rootDir = path_1.default.resolve(dirPath);
105
+ if (!fs_1.default.existsSync(rootDir)) {
106
+ console.error("\x1b[31m%s\x1b[0m", `目录不存在: ${rootDir}`);
116
107
  return;
117
108
  }
118
- if (outputDir && !fs_1.default.existsSync(outputDir)) {
119
- fs_1.default.mkdirSync(outputDir, { recursive: true });
109
+ const outRoot = outputDir ? path_1.default.resolve(outputDir) : null;
110
+ if (outRoot) {
111
+ utils_1.utils.mkdir(outRoot);
120
112
  }
121
- const files = fs_1.default.readdirSync(dirPath);
113
+ const allFiles = utils_1.utils.findFiles(rootDir, extensions);
122
114
  let processedCount = 0;
123
- for (const file of files) {
124
- const fullPath = path_1.default.join(dirPath, file);
125
- const stat = fs_1.default.statSync(fullPath);
126
- if (stat.isDirectory()) {
127
- const subOutputDir = outputDir ? path_1.default.join(outputDir, file) : null;
128
- batchProcess({ dirPath: fullPath, mode, outputDir: subOutputDir, extensions, sign, key, encryptedFileSuffix: suffix });
115
+ for (const fullPath of allFiles) {
116
+ const absIn = path_1.default.resolve(fullPath);
117
+ let outputFile;
118
+ if (outRoot) {
119
+ const rel = path_1.default.relative(rootDir, absIn);
120
+ outputFile = path_1.default.join(outRoot, rel);
121
+ utils_1.utils.mkdir(path_1.default.dirname(outputFile));
129
122
  }
130
123
  else {
131
- const ext = path_1.default.extname(file).toLowerCase();
132
- if (!extensions.includes(ext)) {
133
- continue;
134
- }
135
- let outputFile;
136
- if (mode === "encrypt") {
137
- const stem = path_1.default.basename(file, ext);
138
- outputFile = outputDir ? path_1.default.join(outputDir, stem + suffix) : path_1.default.join(path_1.default.dirname(fullPath), stem + suffix);
139
- encryptFile({ inputPath: fullPath, outputPath: outputFile, sign, key, encryptedFileSuffix: suffix });
140
- processedCount++;
141
- }
142
- else {
143
- const base = file;
144
- const stem = base.endsWith(suffix) ? base.slice(0, -suffix.length) : path_1.default.basename(file, ext);
145
- outputFile = outputDir ? path_1.default.join(outputDir, stem) : path_1.default.join(path_1.default.dirname(fullPath), stem);
146
- if (decryptFile({ inputPath: fullPath, outputPath: outputFile, sign, key, encryptedFileSuffix: suffix })) {
147
- processedCount++;
148
- }
149
- }
124
+ outputFile = absIn;
125
+ }
126
+ if (mode === "encrypt") {
127
+ encryptFile({ inputPath: absIn, outputPath: outputFile, sign, key });
128
+ processedCount++;
129
+ }
130
+ else if (decryptFile({ inputPath: absIn, outputPath: outputFile, sign, key })) {
131
+ processedCount++;
150
132
  }
151
133
  }
152
134
  if (processedCount > 0) {
153
- console.log(`\n${mode === "encrypt" ? "加密" : "解密"} 完成,共处理 ${processedCount} 个文件`);
135
+ console.info("\x1b[32m%s\x1b[0m", `\n${mode === "encrypt" ? "加密" : "解密"} 完成,共处理 ${processedCount} 个文件`);
154
136
  }
155
137
  }
156
138
  /**
157
139
  * 由 CLI 解析结果驱动:单文件或目录批量
158
140
  */
159
141
  function runXorCommand(opts) {
160
- var _a, _b, _c, _d;
142
+ var _a, _b, _c;
161
143
  const { mode, input } = opts;
162
- const suffix = (_a = opts.encryptedFileSuffix) !== null && _a !== void 0 ? _a : exports.DEFAULT_ENCRYPTED_FILE_SUFFIX;
163
- const sign = (_b = opts.sign) !== null && _b !== void 0 ? _b : exports.DEFAULT_SIGN;
164
- const key = (_c = opts.key) !== null && _c !== void 0 ? _c : exports.DEFAULT_KEY;
165
- const extensions = parseExtensionsCsv(opts.extensionsCsv, mode, suffix);
166
- console.log(`\n使用配置: 签名="${sign}", 密钥="${key.substring(0, 4)}***", 加密后缀="${suffix}"`);
144
+ const sign = (_a = opts.sign) !== null && _a !== void 0 ? _a : exports.DEFAULT_SIGN;
145
+ const key = (_b = opts.key) !== null && _b !== void 0 ? _b : exports.DEFAULT_KEY;
146
+ const extensions = parseExtensionsCsv(opts.extensionsCsv, mode);
147
+ console.log(`\n使用配置: 签名="${sign}", 密钥="${key.substring(0, 4)}***"`);
167
148
  const stats = fs_1.default.statSync(input);
168
149
  if (stats.isFile()) {
169
150
  let finalOutput = opts.output;
170
151
  if (!finalOutput) {
171
- if (mode === "encrypt") {
172
- finalOutput = defaultEncryptedOutputPath(input, suffix);
173
- }
174
- else {
175
- finalOutput = defaultDecryptedOutputPath(input, suffix);
176
- }
152
+ finalOutput = input;
177
153
  }
178
154
  else if (finalOutput.endsWith("/") || finalOutput.endsWith(path_1.default.sep)) {
179
- if (mode === "encrypt") {
180
- const stem = path_1.default.basename(input, path_1.default.extname(input));
181
- finalOutput = path_1.default.join(finalOutput, stem + suffix);
182
- }
183
- else {
184
- const base = path_1.default.basename(input);
185
- const stem = base.endsWith(suffix) ? base.slice(0, -suffix.length) : path_1.default.basename(input, path_1.default.extname(input));
186
- finalOutput = path_1.default.join(finalOutput, stem);
187
- }
155
+ finalOutput = path_1.default.join(finalOutput, path_1.default.basename(input));
188
156
  }
189
157
  else if (fs_1.default.existsSync(finalOutput) && fs_1.default.statSync(finalOutput).isDirectory()) {
190
- if (mode === "encrypt") {
191
- const stem = path_1.default.basename(input, path_1.default.extname(input));
192
- finalOutput = path_1.default.join(finalOutput, stem + suffix);
193
- }
194
- else {
195
- const base = path_1.default.basename(input);
196
- const stem = base.endsWith(suffix) ? base.slice(0, -suffix.length) : path_1.default.basename(input, path_1.default.extname(input));
197
- finalOutput = path_1.default.join(finalOutput, stem);
198
- }
158
+ finalOutput = path_1.default.join(finalOutput, path_1.default.basename(input));
199
159
  }
200
160
  if (mode === "encrypt") {
201
- encryptFile({ inputPath: input, outputPath: finalOutput, sign, key, encryptedFileSuffix: suffix });
161
+ encryptFile({ inputPath: input, outputPath: finalOutput, sign, key });
202
162
  }
203
163
  else {
204
- decryptFile({ inputPath: input, outputPath: finalOutput, sign, key, encryptedFileSuffix: suffix });
164
+ decryptFile({ inputPath: input, outputPath: finalOutput, sign, key });
205
165
  }
206
166
  }
207
167
  else if (stats.isDirectory()) {
208
168
  batchProcess({
209
169
  dirPath: input,
210
170
  mode,
211
- outputDir: (_d = opts.output) !== null && _d !== void 0 ? _d : null,
171
+ outputDir: (_c = opts.output) !== null && _c !== void 0 ? _c : null,
212
172
  extensions,
213
173
  sign,
214
- key,
215
- encryptedFileSuffix: suffix
174
+ key
216
175
  });
217
176
  }
218
177
  }
219
- function defaultEncryptedOutputPath(inputPath, suffix) {
220
- const dir = path_1.default.dirname(inputPath);
221
- const stem = path_1.default.basename(inputPath, path_1.default.extname(inputPath));
222
- return path_1.default.join(dir, stem + suffix);
223
- }
224
- function defaultDecryptedOutputPath(inputPath, suffix) {
225
- const dir = path_1.default.dirname(inputPath);
226
- const base = path_1.default.basename(inputPath);
227
- if (base.endsWith(suffix)) {
228
- return path_1.default.join(dir, base.slice(0, -suffix.length));
229
- }
230
- return inputPath;
231
- }
232
- function defaultExtensionsForMode(mode, encryptedSuffix) {
178
+ function defaultExtensionsForMode(mode) {
233
179
  if (mode === "decrypt") {
234
- const set = new Set([...exports.DEFAULT_IMAGE_EXTS, encryptedSuffix.toLowerCase()]);
235
- return [...set];
180
+ return [...exports.DEFAULT_IMAGE_EXTS];
236
181
  }
237
182
  return [...exports.DEFAULT_IMAGE_EXTS];
238
183
  }
239
- function parseExtensionsCsv(csv, mode, encryptedSuffix) {
184
+ function parseExtensionsCsv(csv, mode) {
240
185
  if (!csv || !csv.trim()) {
241
- return defaultExtensionsForMode(mode, encryptedSuffix);
186
+ return defaultExtensionsForMode(mode);
242
187
  }
243
188
  return csv
244
189
  .split(",")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-cocos",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "scripts": {
5
5
  "clean": "rm -rf dist",
6
6
  "start": "node ./dist/index.js",