mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-19 12:49:25 +08:00
295 lines
9.2 KiB
TypeScript
295 lines
9.2 KiB
TypeScript
import fs from 'fs-extra';
|
|
import { uniqueSymbol } from './utils.js';
|
|
import { resolve } from 'path';
|
|
import motaConfig from '../mota.config.js';
|
|
import compressing from 'compressing';
|
|
|
|
const SYMBOL = uniqueSymbol();
|
|
const MAX_SIZE = 100 * (1 << 20) - 20 * (1 << 10);
|
|
const sourceIndex: Record<string, string> = {};
|
|
const toMove: Stats[] = [];
|
|
const all = [
|
|
'bgms',
|
|
'sounds',
|
|
'autotiles',
|
|
'images',
|
|
'materials',
|
|
'tilesets',
|
|
'animates',
|
|
'fonts'
|
|
];
|
|
|
|
type Stats = fs.Stats & { name?: string };
|
|
|
|
export async function splitResorce(type: string) {
|
|
await fs.ensureDir('./dist-resource');
|
|
await fs.emptyDir('./dist-resource');
|
|
await readySplit();
|
|
|
|
await zipResource();
|
|
await split(type === 'dist' ? MAX_SIZE : void 0);
|
|
|
|
await endSplit(type);
|
|
}
|
|
|
|
async function readySplit() {
|
|
await fs.ensureDir('./_temp');
|
|
await fs.emptyDir('./_temp');
|
|
await fs.ensureDir('./_temp/origin');
|
|
await copyAll();
|
|
}
|
|
|
|
async function endSplit(type: string) {
|
|
await rewriteMain(type);
|
|
await fs.emptyDir('./_temp');
|
|
await fs.rmdir('./_temp');
|
|
}
|
|
|
|
async function zipResource() {
|
|
const zip = motaConfig.zip;
|
|
if (!zip) return;
|
|
for await (const [name, files] of Object.entries(zip)) {
|
|
const stream = new compressing.zip.Stream();
|
|
const dirs: string[] = [];
|
|
|
|
for await (const file of files) {
|
|
if (/^.+\/\*$/.test(file)) {
|
|
const dir = file.split('/')[0];
|
|
dirs.push(dir);
|
|
await fs.copy(`./_temp/origin/${dir}`, `./_temp/${dir}`);
|
|
} else {
|
|
const [dir, name] = file.split('/');
|
|
if (dirs.includes(dir)) dirs.push(dir);
|
|
await fs.ensureDir(`./_temp/${dir}`);
|
|
await fs.copyFile(
|
|
`./_temp/origin/${dir}/${name}`,
|
|
`./_temp/${dir}/${name}`
|
|
);
|
|
}
|
|
}
|
|
|
|
dirs.forEach(v => stream.addEntry(`./_temp/${v}`));
|
|
const dest = fs.createWriteStream(`./_temp/${name}`);
|
|
await new Promise<void>(res =>
|
|
stream.pipe(dest).on('finish', () => {
|
|
res();
|
|
})
|
|
);
|
|
const stat = await fs.stat(`./_temp/${name}`);
|
|
toMove.push({ ...stat, name: `./_temp/${name}` });
|
|
}
|
|
}
|
|
|
|
async function getRemainReource() {
|
|
const zip = motaConfig.zip;
|
|
if (!zip) return;
|
|
const values = Object.values(zip);
|
|
for await (const one of all) {
|
|
if (values.some(v => v.includes(`${one}/*`))) continue;
|
|
const list = await fs.readdir(`./_temp/origin/${one}`);
|
|
for await (const name of list) {
|
|
if (!values.some(vv => vv.includes(`${one}/${name}`))) {
|
|
const stat = await fs.stat(`./_temp/origin/${one}/${name}`);
|
|
toMove.push({
|
|
...stat,
|
|
name: `./_temp/origin/${one}/${name}`
|
|
});
|
|
}
|
|
}
|
|
}
|
|
toMove.sort((a, b) => {
|
|
if (a.name?.endsWith('.zip') && b.name?.endsWith('.zip')) {
|
|
return b.size - a.size;
|
|
}
|
|
if (a.name?.endsWith('.zip')) return -1;
|
|
if (b.name?.endsWith('.zip')) return 1;
|
|
return b.size - a.size;
|
|
});
|
|
}
|
|
|
|
async function split(max?: number) {
|
|
await getRemainReource();
|
|
|
|
const doSplit = async (index: string | number) => {
|
|
const base =
|
|
typeof index === 'string' ? index : `./dist-resource/${index}`;
|
|
|
|
await fs.ensureDir(base);
|
|
await generatePublishStructure(
|
|
base,
|
|
typeof index === 'string' ? 0 : index
|
|
);
|
|
|
|
let size = (await fs.stat(base)).size;
|
|
// 计算出要移动多少资源
|
|
const res = (() => {
|
|
if (!max) return toMove.splice(0, toMove.length);
|
|
let remain = max - size;
|
|
for (let i = 0; i < toMove.length; i++) {
|
|
const ele = toMove[i];
|
|
remain -= ele.size;
|
|
if (remain <= 0) {
|
|
return toMove.splice(0, i);
|
|
}
|
|
}
|
|
return toMove.splice(0, toMove.length);
|
|
})();
|
|
|
|
if (base.endsWith('dist')) {
|
|
await fs.ensureDir(resolve(base, 'resource'));
|
|
}
|
|
|
|
// 执行移动
|
|
await Promise.all(
|
|
res.map(async v => {
|
|
if (!v.name) return;
|
|
// 压缩包
|
|
if (v.name.endsWith('.zip')) {
|
|
const [, , name] = v.name.split('/');
|
|
const split = name.split('.');
|
|
const target = `${split
|
|
.slice(0, -1)
|
|
.join('.')}-${SYMBOL}.${split.at(-1)}`;
|
|
if (base.endsWith('dist')) {
|
|
await fs.ensureDir(resolve(base, 'resource/zip'));
|
|
return fs.copyFile(
|
|
v.name,
|
|
resolve(base, 'resource', 'zip', target)
|
|
);
|
|
} else {
|
|
await fs.ensureDir(resolve(base, 'zip'));
|
|
return fs.copyFile(
|
|
v.name,
|
|
resolve(base, 'zip', target)
|
|
);
|
|
}
|
|
}
|
|
|
|
// 非压缩包
|
|
if (!v.name.endsWith('.zip')) {
|
|
const [, , , type, name] = v.name.split('/');
|
|
const split = name.split('.');
|
|
const target = `${split
|
|
.slice(0, -1)
|
|
.join('.')}-${SYMBOL}.${split.at(-1)}`;
|
|
if (base.endsWith('dist')) {
|
|
await fs.ensureDir(resolve(base, 'resource', type));
|
|
} else {
|
|
await fs.ensureDir(resolve(base, type));
|
|
}
|
|
if (base.endsWith('dist')) {
|
|
return fs.copyFile(
|
|
v.name,
|
|
resolve(base, 'resource', type, target)
|
|
);
|
|
} else {
|
|
return fs.copyFile(v.name, resolve(base, type, target));
|
|
}
|
|
}
|
|
})
|
|
);
|
|
|
|
// 标记资源索引
|
|
res.forEach(v => {
|
|
if (!v.name) return;
|
|
// 压缩包
|
|
if (v.name.endsWith('.zip')) {
|
|
const [, , name] = v.name.split('/');
|
|
sourceIndex[`zip.${name}`] = index.toString();
|
|
}
|
|
// 非压缩包
|
|
if (!v.name.endsWith('.zip')) {
|
|
const [, , , type, name] = v.name.split('/');
|
|
sourceIndex[`${type}.${name}`] = index.toString();
|
|
}
|
|
});
|
|
|
|
if (toMove.length > 0) {
|
|
await doSplit(typeof index === 'string' ? 0 : index + 1);
|
|
}
|
|
};
|
|
await doSplit('dist');
|
|
}
|
|
|
|
async function copyAll() {
|
|
await Promise.all(
|
|
all.map(v => {
|
|
return fs.move(`./dist/project/${v}`, `./_temp/origin/${v}`);
|
|
})
|
|
);
|
|
}
|
|
|
|
async function rewriteMain(type: string) {
|
|
const main = await fs.readFile('./dist/main.js', 'utf-8');
|
|
const res = main
|
|
.replace(
|
|
/this\.RESOURCE_TYPE\s*\=\s*.*;/,
|
|
`this.RESOURCE_TYPE = '${type}';`
|
|
)
|
|
.replace(
|
|
/this\.RESOURCE_URL\s*\=\s*'.*'/,
|
|
`this.RESOURCE_URL = '/games/${motaConfig.resourceName}'`
|
|
)
|
|
.replace(
|
|
/this\.RESOURCE_SYMBOL\s*\=\s*'.*'/,
|
|
`this.RESOURCE_SYMBOL = '${SYMBOL}'`
|
|
)
|
|
.replace(
|
|
/this\.RESOURCE_INDEX\s*\=\s*\{.*\}/,
|
|
`this.RESOURCE_INDEX = ${JSON.stringify(sourceIndex, void 0, 8)}`
|
|
);
|
|
await fs.writeFile('./dist/main.js', res, 'utf-8');
|
|
}
|
|
|
|
/**
|
|
* 生成可发布目录
|
|
*/
|
|
async function generatePublishStructure(dir: string, index: number) {
|
|
await fs.ensureDir(resolve(dir, 'libs'));
|
|
await fs.ensureDir(resolve(dir, 'libs/thirdparty'));
|
|
await fs.ensureDir(resolve(dir, 'project'));
|
|
await Promise.all(
|
|
all.map(v => {
|
|
fs.ensureDir(resolve(dir, 'project', v));
|
|
fs.emptyDir(resolve(dir, 'project', v));
|
|
})
|
|
);
|
|
|
|
if (!dir.endsWith('dist')) {
|
|
await fs.writeFile(
|
|
resolve(dir, 'project/icons.js'),
|
|
`var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 =
|
|
{"autotile": {}}
|
|
`,
|
|
'utf-8'
|
|
);
|
|
await fs.writeFile(
|
|
resolve(dir, 'project/floors/none.js'),
|
|
'"none"',
|
|
'utf-8'
|
|
);
|
|
await fs.writeFile(resolve(dir, 'libs/none.js'), '"none"', 'utf-8');
|
|
|
|
await fs.copyFile('./script/template/main.js', resolve(dir, 'main.js'));
|
|
const data = await fs.readFile('./script/template/data.js', 'utf-8');
|
|
await fs.writeFile(
|
|
resolve(dir, 'project/data.js'),
|
|
data.replace('@name', `${motaConfig.resourceName}${index}`)
|
|
);
|
|
|
|
await fs.copyFile(
|
|
'./script/template/lz-string.min.js',
|
|
resolve(dir, 'libs/thirdparty/lz-string.min.js')
|
|
);
|
|
}
|
|
|
|
await Promise.all(
|
|
['animates', 'images', 'materials', 'sounds', 'tilesets'].map(v => {
|
|
fs.copyFile(
|
|
'./script/template/.h5data',
|
|
resolve(dir, `project/${v}/${v}.h5data`)
|
|
);
|
|
})
|
|
);
|
|
}
|