加入3.5 修复水印
This commit is contained in:
1 parent
ef01cdd6ee
commit
e82a62a9ad
19 files changed
+5894
-38
No files matched your search
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
// Version-pinned, complete official export sets for manual Eagle testing.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { execFile } = require('child_process');
|
||||
const root = path.resolve(process.argv[2] || path.join(__dirname, '.fixtures'));
|
||||
const revisions = {
|
||||
'3.4': 'ef5013143178918ddfc7cabfbddeae7aec7cf84e',
|
||||
'3.5': '2cd9467d2736b7f3a0b81382f117c73823bfb44b',
|
||||
'3.6': '654c20e5b0e523040b6366bbd1042510d2645134',
|
||||
'3.7': '9639bcc81722d7178fd9d1cdc1a3d55a4c91f989',
|
||||
'3.8': '8b4844bd4b193ba9e54487ed397a777993cbad56',
|
||||
'4.0': '425ce416bb218b28caeec47b317aa57cd7140375',
|
||||
'4.1': '77a5db0ec6d16331f5efbaa7662bba9355bd3424',
|
||||
'4.2': 'e7dc1435fa4a0083ab431f1b28e083c14a1f5c68',
|
||||
'4.3': 'ba17cf88b198c6531c6c51d8d4e1e4b3c3ab47dd'
|
||||
};
|
||||
const names = ['spineboy', 'goblins', 'vine', 'stretchyman', 'tank', 'coin', 'raptor'];
|
||||
const curl = (url, file) => new Promise((resolve, reject) => execFile('curl', ['--http1.1', '-LfsS', '--retry', '2', '--max-time', '60', url, '-o', file], error => error ? reject(error) : resolve()));
|
||||
function blobHash(file) {
|
||||
const bytes = fs.readFileSync(file);
|
||||
return crypto.createHash('sha1').update('blob ' + bytes.length + '\0').update(bytes).digest('hex');
|
||||
}
|
||||
async function pool(jobs) {
|
||||
let cursor = 0;
|
||||
await Promise.all(Array.from({ length: 6 }, async () => { while (cursor < jobs.length) await jobs[cursor++](); }));
|
||||
}
|
||||
(async () => {
|
||||
fs.mkdirSync(root, { recursive: true });
|
||||
const inventory = [];
|
||||
for (const [version, revision] of Object.entries(revisions)) {
|
||||
const folder = path.join(root, version); fs.mkdirSync(folder, { recursive: true });
|
||||
let previous = [];
|
||||
try { previous = JSON.parse(fs.readFileSync(path.join(folder, '.sources.json'), 'utf8')).files; } catch (_) {}
|
||||
const treeFile = path.join(folder, '.tree.json');
|
||||
await curl(`https://api.github.com/repos/EsotericSoftware/spine-runtimes/git/trees/${revision}?recursive=1`, treeFile);
|
||||
const tree = JSON.parse(fs.readFileSync(treeFile, 'utf8'));
|
||||
if (tree.truncated || !tree.tree || tree.sha !== revision) throw new Error('Incomplete source tree: ' + version);
|
||||
const license = tree.tree.find(f => f.path === 'LICENSE');
|
||||
if (license) {
|
||||
const dest = path.join(folder, 'LICENSE.txt');
|
||||
if (!fs.existsSync(dest) || blobHash(dest) !== license.sha) await curl(`https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/${revision}/LICENSE`, dest);
|
||||
if (blobHash(dest) !== license.sha) throw new Error('License checksum mismatch: ' + version);
|
||||
}
|
||||
const files = tree.tree.filter(f => f.type === 'blob' && names.some(n => f.path.startsWith('examples/' + n + '/export/')) && /\.(json|skel|atlas|png)$/.test(f.path) && !/-pma/.test(f.path));
|
||||
for (const old of previous.filter(old => !files.some(f => f.path === old.path))) {
|
||||
const parts = old.path.split('/'), dest = path.join(folder, parts[1], parts.slice(3).join('/'));
|
||||
if (fs.existsSync(dest) && blobHash(dest) === old.sha) {
|
||||
fs.unlinkSync(dest);
|
||||
if (!fs.readdirSync(path.dirname(dest)).length) fs.rmdirSync(path.dirname(dest));
|
||||
}
|
||||
}
|
||||
await pool(files.map(f => async () => {
|
||||
const parts = f.path.split('/'), dest = path.join(folder, parts[1], parts.slice(3).join('/'));
|
||||
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
||||
if (fs.existsSync(dest) && blobHash(dest) === f.sha) return;
|
||||
const temp = dest + '.download';
|
||||
await curl(`https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/${revision}/${f.path}`, temp);
|
||||
if (blobHash(temp) !== f.sha) throw new Error('Checksum mismatch: ' + f.path);
|
||||
fs.renameSync(temp, dest);
|
||||
}));
|
||||
for (const f of files.filter(f => f.path.endsWith('.json'))) {
|
||||
const parts = f.path.split('/');
|
||||
const data = JSON.parse(fs.readFileSync(path.join(folder, parts[1], parts.slice(3).join('/')), 'utf8'));
|
||||
if (!data.skeleton || !data.skeleton.spine.startsWith(version + '.')) throw new Error('Wrong export version: ' + f.path);
|
||||
}
|
||||
const entry = { version, revision, files: files.map(f => ({ path: f.path, sha: f.sha, size: f.size })) };
|
||||
fs.writeFileSync(path.join(folder, '.sources.json'), JSON.stringify(entry, null, 2));
|
||||
inventory.push(entry);
|
||||
console.log(version + ': ' + files.length + ' official files verified');
|
||||
}
|
||||
const rows = inventory.map(e => '| ' + e.version + ' | ' + new Set(e.files.map(f => f.path.split('/')[1])).size + ' | ' + e.files.length + ' | `' + e.revision + '` |').join('\n');
|
||||
fs.writeFileSync(path.join(root, 'README.md'), '# Spine 官方测试资源\n\n按版本和示例分目录,骨骼、Atlas 和贴图均来自官方仓库,所有文件已校验 Git blob SHA-1。这里下载的是普通 Alpha 导出,未混入同名 PMA 图集。\n\n| 版本系列 | 示例组 | 文件数 | 固定提交 |\n|---|---:|---:|---|\n' + rows + '\n\n将单个示例文件夹导入 Eagle。JSON 与 SKEL 可分别测试;测试复合后缀时请复制文件后将 `.atlas` 改为 `.atlas.txt`、`.skel` 改为 `.skel.bytes`。保留原始版本作为对照。每个版本的 `.sources.json` 记录来源文件及校验值。\n\n官方仓库:https://github.com/EsotericSoftware/spine-runtimes\n');
|
||||
console.log('Saved to ' + root);
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
||||
Reference in new issue
Block a user