Files
2025-08-04 10:46:00 +08:00

277 lines
11 KiB
TypeScript

import fs from 'fs';
import path from 'path';
type FileRoomData = { [filePath: string]: number[] };
type FileRoomJsonData = { [uuid: string]: string };
const ROOM_COUNT = 7;
const BASE64_KEYS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const HEXCHAR = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
const BASE64_VALUES = new Array(123);
for (let i = 0; i < 123; ++i) BASE64_VALUES[i] = 64;
for (let i = 0; i < 64; ++i) BASE64_VALUES[BASE64_KEYS.charCodeAt(i)] = i;
const _t = ['', '', '', ''];
const uuidTemplate = _t.concat(_t, '-', _t, '-', _t, '-', _t, '-', _t, _t, _t);
const indices = uuidTemplate.map(function (value: string, index: number) { return value === '-' ? NaN : index; }).filter(isFinite);
class SettingJSDecode {
private packedAssets: CCSettingsPackedAssets = null;
constructor(buildDir: string) {
const settingDir = path.join(buildDir, 'src');
const files = fs.readdirSync(settingDir);
for (const file of files) {
if (!file.startsWith('settings')) continue;
const settingsContent = fs.readFileSync(path.join(settingDir, file)).toString();
const ccSetting = this.evalSettings(settingsContent);
this.packedAssets = ccSetting?.packedAssets;
break;
}
}
private evalSettings(content: string): CCSettings {
content = content.replace('window.', 'settingsContainer.');
const settingsContainer: { _CCSettings?: CCSettings } = {};
eval(content);
return settingsContainer._CCSettings;
}
private toUUID(base64: string) {
let baselen = base64.length;
if (baselen < 22) {
return base64;
}
uuidTemplate[0] = base64[0];
uuidTemplate[1] = base64[1];
for (let i = 2, j = 2; i < 22; i += 2) {
let lhs = BASE64_VALUES[base64.charCodeAt(i)];
let rhs = BASE64_VALUES[base64.charCodeAt(i + 1)];
uuidTemplate[indices[j++]] = HEXCHAR[lhs >> 2];
uuidTemplate[indices[j++]] = HEXCHAR[((lhs & 3) << 2) | rhs >> 4];
uuidTemplate[indices[j++]] = HEXCHAR[rhs & 0xF];
}
return uuidTemplate.join('');
}
public pickPackageFiles(uuids: string[]): string[] {
if (!this.packedAssets) return uuids;
for (const fileName in this.packedAssets) {
const packageUUIDs = this.packedAssets[fileName];
let match = true;
const removedUUIDs: string[] = [];
for (const packageKey of packageUUIDs) {
const packageUUID = this.toUUID(packageKey);
const index = uuids.indexOf(packageUUID);
match = index >= 0;
if (match) {
removedUUIDs.push(...uuids.splice(index, 1));
} else {
uuids.push(...removedUUIDs);
match = false;
break;
}
}
match && uuids.push(fileName);
}
return uuids;
}
}
export class JsonPackage {
private readonly assetsDir: string;
private readonly buildDir: string;
private readonly resourcesDir: string;
private readonly outputDir: string;
private readonly uuidMap: { db: string, uuid: string, dest: string }[];
private readonly fileRoomData: FileRoomData;
private readonly settingJSDecode: SettingJSDecode;
constructor(projectDir: string, buildDir: string) {
this.assetsDir = path.join(projectDir, 'assets');
this.buildDir = buildDir;
this.resourcesDir = path.join(buildDir, 'res');
this.outputDir = path.join(buildDir, 'res_package');
this.settingJSDecode = new SettingJSDecode(buildDir);
const buildJson = fs.readFileSync(path.join(projectDir, 'build', 'build.json'));
const buildData = JSON.parse(buildJson.toString());
this.uuidMap = buildData.uuid;
this.fileRoomData = this.decodeRoomData();
}
//通过鱼属性表读取相关资源所属的房间
private decodeRoomData(): FileRoomData {
const filesRoomData: FileRoomData = {};
const gameConfigPath = path.join(this.assetsDir, 'resources', 'conf', 'allConf.json');
const gameConfigContent = fs.readFileSync(gameConfigPath);
const gameConfig = JSON.parse(gameConfigContent.toString());
const fishAttribute = this.dataArrayToObjectArray(gameConfig['FishAttribute']);
for (const fish of fishAttribute) {
//房间数据
const roomData: number[] = [];
const fishName: string = fish.name;
if (!fishName) continue;
for (let roomType = 1; roomType <= ROOM_COUNT; roomType++) {
roomData.push(fish[`room${roomType}`]);
}
//处理鱼资源、鱼技能资源
this.updateRoomData(filesRoomData, `resources/anim/clip/fish/${fishName}`, roomData);//鱼动画
this.updateRoomData(filesRoomData, `res/anim/texture/fish/${fishName}`, roomData);//鱼动画纹理
this.updateRoomData(filesRoomData, `resources/texture/hall/scenePage/${fishName}_rk`, roomData);//Loading图标
this.updateRoomData(filesRoomData, `resources/texture/hall/scenePage/${fishName}_rkdes`, roomData);//Loading文字
//处理背景资源
const backgroundString: string | null = fish.Background
if (backgroundString) {
const background = backgroundString.split('|');
switch (background[0]) {
case '1':
this.updateRoomData(filesRoomData, `resources/texture/mainScene/bg/${background[1]}.png`, roomData);//鱼背景静态图
break;
case '2':
}
}
//处理音效资源
const audioString: string | null = fish.bossBgm
}
return filesRoomData;
}
private getUUIDsInPath(filePath: string, uuids?: string[]): string[] {
uuids = uuids || [];
this.uuidMap.forEach(({ db, uuid }) => {
db = db.substring(12);//"db://assets/".length
if (!db.startsWith(filePath)) return;
if (uuids.indexOf(uuid) >= 0) return;
uuids.push(uuid);
});
return uuids;
}
private dataArrayToObjectArray(data: any[][]): any[] {
const title: string[] = data.shift()!;
const objArray: { [title: string]: string }[] = [];
for (const dataItem of data) {
const dataObj: { [title: string]: string } = {}
for (let i = 0, length = title.length; i < length; i++) {
const key: string = title[i];
if (!key) break;
dataObj[key] = dataItem[i];
}
objArray.push(dataObj);
}
return objArray;
}
private updateRoomData(container: FileRoomData, key: string, data: number[]): void {
const exitsData = container[key];
if (!exitsData) {
container[key] = data;
} else {
const result: number[] = [];
for (let i = 0, length = Math.max(exitsData.length, data.length); i < length; i++) {
result.push((exitsData[i] || data[i]) ? 1 : 0);
}
container[key] = result;
}
}
private mkdir(dir: string): void {
if (!fs.existsSync(dir)) {
let parentDir = path.dirname(dir);
this.mkdir(parentDir);
fs.mkdirSync(dir);
}
}
private pickPackageFile(dir: string, names: string[], cache?: FileRoomJsonData): FileRoomJsonData {
cache = cache || {};
const files = fs.readdirSync(dir);
for (let i = 0; i < files.length; i++) {
const subFileName = files[i];
const subFilePath = path.join(dir, subFileName)
const stat = fs.statSync(subFilePath);
if (stat.isDirectory()) {
this.pickPackageFile(subFilePath, names, cache);
} else {
if (path.extname(subFileName) !== '.json') continue;
const fileName = subFileName.substring(0, subFileName.indexOf('.'));
const nameIndex = names.indexOf(fileName);
if (nameIndex < 0) {
continue;
}
names.splice(nameIndex, 1);
const content = fs.readFileSync(subFilePath).toString();
cache[fileName] = content;
}
}
return cache;
}
public package(): void {
this.mkdir(this.outputDir);
const result: { uuid: string, name: string }[] = [];
for (let roomType = 1; roomType <= ROOM_COUNT; roomType++) {
const configName = `config_${roomType}.json`;
const uuids: string[] = [];
for (const filePath in this.fileRoomData) {
const fishRoomData = this.fileRoomData[filePath];
if (!fishRoomData[roomType - 1]) continue;
this.getUUIDsInPath(filePath, uuids);
}
this.getUUIDsInPath(`resources/spine/mainScene/nGun`, uuids);
this.getUUIDsInPath(`resources/spine/mainScene/yuwang`, uuids);
this.getUUIDsInPath(`resources/spine/mainScene/superWeapon`, uuids);
this.getUUIDsInPath(`resources/prefab/mainScene/preload`, uuids);
this.getUUIDsInPath(`resources/spine/mainScene/preload`, uuids);
this.getUUIDsInPath(`resources/prefab/mainScene/init`, uuids);
this.getUUIDsInPath(`resources/prefab/mainScene/skill`, uuids);
this.settingJSDecode.pickPackageFiles(uuids);
const contents: FileRoomJsonData = this.pickPackageFile(this.resourcesDir, uuids);
// console.log(`RoomUUIDs:\n${uuids.join(', ')}`);
// console.log(`RoomErrorUUIDs:\n${uuids.join(', ')}`);
for (const uuid in contents) result.push({ uuid, name: configName });
fs.writeFileSync(path.join(this.outputDir, configName), JSON.stringify(contents));
}
fs.writeFileSync(path.join(this.outputDir, 'result.json'), JSON.stringify(result));
fs.copyFileSync(path.join(__dirname, 'prj.js'), path.join(this.buildDir, 'src', 'prj.js'));
const indexPath = path.join(this.buildDir, 'index.html');
let htmlContent = fs.readFileSync(indexPath).toString();
const bodyEndIndex = htmlContent.indexOf('</body>');
htmlContent = `${htmlContent.substring(0, bodyEndIndex)}<script type="text/javascript">
(function () {
if(window._CCSettings.jsList){
window._CCSettings.jsList.push("prj.js");
}else{
window._CCSettings.jsList = ["prj.js"];
}
})();
</script>${htmlContent.substring(bodyEndIndex)}`
fs.writeFileSync(indexPath, htmlContent);
}
}
const args = process.argv;
const projectDir = args[2];
const buildDir = args[3]
new JsonPackage(projectDir, buildDir).package();