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.
- package/dist/xor/src/index.js +6 -8
- package/dist/xor/src/xor.js +57 -112
- package/package.json +1 -1
package/dist/xor/src/index.js
CHANGED
|
@@ -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("
|
|
34
|
-
.option("--ext <list>", "批量处理时的扩展名列表,逗号分隔(解密时默认会包含加密后缀对应扩展)")
|
|
33
|
+
.option("--ext <list>", "批量处理时的扩展名列表,逗号分隔")
|
|
35
34
|
.addHelpText("after", `
|
|
36
35
|
示例:
|
|
37
|
-
fast-cocos xor encrypt
|
|
38
|
-
fast-cocos xor encrypt
|
|
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
|
});
|
package/dist/xor/src/xor.js
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
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 =
|
|
43
|
+
out = opts.inputPath;
|
|
46
44
|
}
|
|
47
45
|
else if (out.endsWith("/") || out.endsWith(path_1.default.sep)) {
|
|
48
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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 =
|
|
72
|
+
out = opts.inputPath;
|
|
78
73
|
}
|
|
79
74
|
else if (out.endsWith("/") || out.endsWith(path_1.default.sep)) {
|
|
80
|
-
|
|
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
|
-
|
|
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
|
|
101
|
+
var _a;
|
|
111
102
|
const { dirPath, mode, outputDir = null, sign = null, key = null } = opts;
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
if (!fs_1.default.existsSync(
|
|
115
|
-
console.error(`目录不存在: ${
|
|
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
|
-
|
|
119
|
-
|
|
109
|
+
const outRoot = outputDir ? path_1.default.resolve(outputDir) : null;
|
|
110
|
+
if (outRoot) {
|
|
111
|
+
utils_1.utils.mkdir(outRoot);
|
|
120
112
|
}
|
|
121
|
-
const
|
|
113
|
+
const allFiles = utils_1.utils.findFiles(rootDir, extensions);
|
|
122
114
|
let processedCount = 0;
|
|
123
|
-
for (const
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
const
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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.
|
|
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
|
|
142
|
+
var _a, _b, _c;
|
|
161
143
|
const { mode, input } = opts;
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
161
|
+
encryptFile({ inputPath: input, outputPath: finalOutput, sign, key });
|
|
202
162
|
}
|
|
203
163
|
else {
|
|
204
|
-
decryptFile({ inputPath: input, outputPath: finalOutput, sign, key
|
|
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: (
|
|
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
|
|
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
|
-
|
|
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
|
|
184
|
+
function parseExtensionsCsv(csv, mode) {
|
|
240
185
|
if (!csv || !csv.trim()) {
|
|
241
|
-
return defaultExtensionsForMode(mode
|
|
186
|
+
return defaultExtensionsForMode(mode);
|
|
242
187
|
}
|
|
243
188
|
return csv
|
|
244
189
|
.split(",")
|