diff --git a/main.js b/main.js index 93c98e58697c4d1b991bc09a82769e9e4f2a093b..5b7436679845c382bf5bb10fcf4d588a450483cb 100644 --- a/main.js +++ b/main.js @@ -4,6 +4,7 @@ const ChildProcess = require('child_process'); const Os = require('os'); const FileUtil = require('./utils/file-util'); const ConfigManager = require('./config-manager'); +const AtlasInfoHelper = require('./utils/atlas-info-helper'); /** 压缩引擎绝对路径 */ let pngquantPath = null; @@ -15,6 +16,8 @@ let logger = null; let excludeFolders = null; /** 需要排除的文件 */ let excludeFiles = null; +/** 图集id对应的原图路径信息 */ +let atlasInfos = null; module.exports = { @@ -132,6 +135,12 @@ module.exports = { excludeFolders = config.excludeFolders ? config.excludeFolders.map(value => Path.normalize(value)) : []; // 需要排除的文件 excludeFiles = config.excludeFiles ? config.excludeFiles.map(value => Path.normalize(value)) : []; + // 搜集图集信息 + try { + atlasInfos = AtlasInfoHelper.collectAtlasInfos(options.dest); + } catch (e) { + Editor.error(e); + } // 开始压缩 Editor.log('[PAC]', '开始压缩 PNG 资源,请勿进行其他操作!'); @@ -220,11 +229,17 @@ function testFilePath(path) { function getAssetPath(filePath) { const basename = Path.basename(filePath); const uuid = basename.slice(0, basename.indexOf('.')); - const abPath = Editor.assetdb.uuidToFspath(uuid); + let abPath = Editor.assetdb.uuidToFspath(uuid); if (!abPath) { - // 图集资源 - // 暂时还没有找到办法处理 - return null; + // 图集资源,返回图集中第一张图的路径做初略判断 + // 考虑到多层结构的图集是无法做子目录单独排除,目前的处理是等效的 + let spriteFramePaths = atlasInfos[uuid]; + if (!spriteFramePaths || spriteFramePaths.length === 0) { + Editor.log(`[PAC] 无法获取图集信息 ${uuid},默认压缩处理`); + return null; + } + + abPath = spriteFramePaths[0]; } // 资源根目录 const assetsPath = Path.join((Editor.Project.path || Editor.projectPath), 'assets/'); diff --git a/utils/atlas-info-helper.js b/utils/atlas-info-helper.js new file mode 100644 index 0000000000000000000000000000000000000000..cd1e916aa675b5a7bd84479599746115f6910a1b --- /dev/null +++ b/utils/atlas-info-helper.js @@ -0,0 +1,259 @@ +/* + * @Author: GT + * @Date: 2021-01-02 15:28:34 + * @LastEditors: GT + * @LastEditTime: 2021-01-04 18:50:04 + */ +const Fs = require('fs'); +const Path = require('path'); +const FileUtil = require('./file-util'); + +const AtlasInfoHelper = { + getShortUuid(atlasPath) { + const basename = Path.basename(atlasPath); + const uuid = basename.slice(0, basename.indexOf('.')); + return uuid; + }, + + hasAssetPath(filePath) { + const basename = Path.basename(filePath); + const uuid = basename.slice(0, basename.indexOf('.')); + const assetPath = Editor.assetdb.uuidToFspath(uuid); + return assetPath && assetPath.length > 0; + }, + + collectAtlasPath(dir) { + let result = []; + FileUtil.map(dir, (filePath, stats) => { + if (Path.extname(filePath) !== '.png') + return; + + // if (filePath.includes(internalPath)) + // return; + + if (!this.hasAssetPath(filePath)) { + result.push(filePath); + } + }); + + return result; + }, + + // 获取dir目录下的第一层子目录列表 + getSubDirs(dir) { + let result = []; + if (Fs.existsSync(dir)) { + const stats = Fs.statSync(dir); + if (stats.isDirectory()) { + const names = Fs.readdirSync(dir); + for (const name of names) { + let subDir = Path.join(dir, name); + let subDirStats = Fs.statSync(subDir); + if (subDirStats.isDirectory()) + result.push(subDir); + } + } + } + + return result; + }, + + getSubPackageConfigPath(dir) { + if (!Fs.existsSync(dir)) { + return null; + } + + const names = Fs.readdirSync(dir); + for (const name of names) { + if (name.startsWith("config.") && name.endsWith(".json")) { + return Path.join(dir, name); + } + } + + return null; + }, + + dumpAtlasSpritInfos(filePath, atlasNames) { + let result = []; + let obj = JSON.parse(Fs.readFileSync(filePath, 'utf8')); + + // assume assets is always array + if (Array.isArray(obj)) { + // merged meta + let assets = obj; + for (let i = 0, n = assets.length; i < n; ++i) { + let info = assets[i]; + if (info["__type__"] !== "cc.SpriteFrame") + continue; + + let content = info["content"]; + if (!content || !content["texture"]) + continue; + + let texture = content["texture"]; + if (atlasNames.indexOf(texture) == -1) { + // not the atlas we are looking for + continue; + } + + let tokens = Path.basename(filePath).split("."); + result.push({ + name: content["name"], + texture: texture, + index: i, + pack_uuid: tokens[0], + pack_version: tokens[1], + asset_uuid22: "", // fill later + origin_path: "", // fill later + }); + } + } else { + // standalone meta + do { + let info = obj; + if (info["__type__"] !== "cc.SpriteFrame") + break; + + let content = info["content"]; + if (!content || !content["texture"]) + break; + + let texture = content["texture"]; + if (atlasNames.indexOf(texture) == -1) { + // not the atlas we are looking for + continue; + } + + let tokens = Path.basename(filePath).split("."); + result.push({ + name: content["name"], + texture: texture, + index: -1, + pack_uuid: "", + pack_version: tokens[1], + asset_uuid22: "", + origin_path: Editor.assetdb.uuidToFspath(tokens[0]) + }); + } while (false); + } + + return result; + }, + + fillSpriteUuids(spriteInfos, subPackageConfigPath) { + // Editor.log("subPackageConfigPath"); + // Editor.log(subPackageConfigPath); + if (!subPackageConfigPath) + return; + + let subPackageInfo = JSON.parse(Fs.readFileSync(subPackageConfigPath, 'utf8')); + let packs = subPackageInfo["packs"]; + let uuids = subPackageInfo["uuids"]; + for (let packUuid of Object.keys(packs)) { + let pack = packs[packUuid]; + for (let spriteInfo of spriteInfos) { + if (spriteInfo["origin_path"]) + // already filled + continue; + + if (spriteInfo["pack_uuid"] === packUuid) { + let uuid22Guess = pack[spriteInfo["index"]]; + if (typeof uuid22Guess === 'number') { + // redirect to uuids if itss a number + uuid22Guess = uuids[uuid22Guess]; + } + + if (!uuid22Guess) { + Editor.error(`error in pack ${packUuid}`); + } + + spriteInfo["asset_uuid22"] = uuid22Guess; + let uuid36 = cc.assetManager.utils.decodeUuid(spriteInfo["asset_uuid22"]); + spriteInfo["origin_path"] = Editor.assetdb.uuidToFspath(uuid36); + } + } + } + }, + + /** + * 搜集dir资源目录下参与图集的碎图,碎图结构如下 + * { + * name: string, // 碎图原文件名 + * texture: string, // 碎图所在的atlas名字 + * index: string, // 碎图在json package里的下标 + * pack_uuid: string, // json package的uuid,出现在文件名前段 + * pack_version: string,// 9位短id,出现在文件名后段(根据json里的key推断叫做version) + * asset_uuid22: string,// 22位uuid,出现在json文件中 + * origin_path: string, // 资源打包前路径 + * } + */ + collectSpritsInResDir(resDir, atlasNames) { + let result = []; + let subDirs = this.getSubDirs(resDir); + for (let subDir of subDirs) { + // Editor.log(`subDir = ${subDir}`); + let importDir = Path.join(subDir, "import"); + let subResult = []; + FileUtil.map(importDir, (filePath, stats) => { + if (Path.extname(filePath) !== '.json') + return; + + let spriteInfos = this.dumpAtlasSpritInfos(filePath, atlasNames); + subResult = subResult.concat(spriteInfos); + }); + + this.fillSpriteUuids(subResult, this.getSubPackageConfigPath(subDir)); + result = result.concat(subResult); + } + + // Editor.log("sprites info") + // Editor.log(result); + + return result; + }, + + collectAtlasInfos(destDir) { + let atlasInfos = {}; + + // collect 9 digits uuid of all atlas files + let folders = ['res', 'assets', 'subpackages', 'remote']; + for (let i = 0, n = folders.length; i < n; ++i) { + let resDir = Path.join(destDir, folders[i]); + if (!Fs.existsSync(resDir)) + continue; + + // Editor.log(`[PAC] 搜集目录${resDir}`); + let atlasPaths = this.collectAtlasPath(resDir); + if (atlasPaths.length == 0) { + // no atlas found + continue; + } + + // Editor.log(`[PAC] 图集:`); + // Editor.log(atlasPaths); + + let atlasNames = atlasPaths.map(x => this.getShortUuid(x)); + let spriteInfos = this.collectSpritsInResDir(resDir, atlasNames); + + // Editor.log(`[PAC] 碎图信息:`); + // Editor.log(spriteInfos); + + // make atlas->spriteFrame relationship + for (let spriteInfo of spriteInfos) { + let texture = spriteInfo["texture"]; + let origin_path = spriteInfo["origin_path"]; + let sfs = atlasInfos[texture]; + if (!sfs) + sfs = atlasInfos[texture] = []; + + sfs.push(origin_path); + } + + // Editor.log(atlasPaths); + } + + return atlasInfos; + } +} + +module.exports = AtlasInfoHelper;