コード例 #1
0
    void RemoveNotExsitBundles()
    {
        string[] files = Directory.GetFiles(ABPackHelper.BUILD_PATH, "*.ab", SearchOption.AllDirectories);
        ABPackHelper.ShowProgress("", 0);

        List <string> tempBundles = new List <string>();

        tempBundles.AddRange(abTypeMaps.Keys);

        for (int i = 0; i < files.Length; ++i)
        {
            var file = files[i].Replace("\\", "/");
            ABPackHelper.ShowProgress("Removing bundles...", (float)i / (float)files.Length);
            bool exist = false;
            for (int j = 0; j < tempBundles.Count; j++)
            {
                if (file.Contains(tempBundles[j]))
                {
                    exist = true;
                    break;
                }
            }

            if (!exist)
            {
                File.Delete(files[i]);
                File.Delete(files[i] + ".meta");
                File.Delete(files[i] + ".manifest");
                File.Delete(files[i] + ".manifest.meta");
            }
        }
        ABPackHelper.ShowProgress("", 1);
        AssetDatabase.Refresh();
    }
コード例 #2
0
    void BuildAssetBundle()
    {
        ABPackHelper.CopyToAssetBundle();
        RemoveNotExsitBundles();
        // 更新资源版本号
        string bundlePath = ABPackHelper.BUILD_PATH + LuaConst.osDir;

        gameVersion.VersionIncrease();
        //PlayerSettings.bundleVersion = gameVersion.ToString();
        File.WriteAllText(bundlePath + "/version.txt", gameVersion.ToString());
        ABPackHelper.SaveVersion(gameVersion.ToString());
        AssetDatabase.Refresh();
        BuildPipeline.BuildAssetBundles(bundlePath, ABPackHelper.buildOptions, ABPackHelper.GetBuildTarget());
        var manifestFile = ABPackHelper.BUILD_PATH + LuaConst.osDir + "/" + LuaConst.osDir;
        var saveABFile   = ABPackHelper.BUILD_PATH + LuaConst.osDir + "/" + LuaConst.osDir.ToLower();

        if (File.Exists(manifestFile))
        {
            var bytes = File.ReadAllBytes(manifestFile);
            File.Delete(manifestFile);
            File.WriteAllBytes(saveABFile + ".ab", bytes);
        }
        else
        {
            Debug.LogError("<<BuildAssetBundle>> Cant find root manifest. ps:" + manifestFile);
        }
        AssetDatabase.Refresh();
    }
コード例 #3
0
    void CopyCompressedData2IOSStreaming()
    {
        string to = Application.streamingAssetsPath;

        if (Directory.Exists(to))
        {
            Directory.Delete(to, true);
        }
        Directory.CreateDirectory(to);
        string from = ABPackHelper.TEMP_ASSET_PATH + "IOSCompress";

        string[] files = Directory.GetFiles(from, "*", SearchOption.AllDirectories);
        for (int i = 0; i < files.Length; ++i)
        {
            var    dest = files[i].Replace("\\", "/").Replace(ABPackHelper.TEMP_ASSET_PATH.Replace("\\", "/") + "IOSCompress/", "");
            var    dirs = dest.Split('/');
            string path = to;
            ABPackHelper.ShowProgress("copy for ios..." + dest, (float)i / (float)files.Length);
            foreach (var dir in dirs)
            {
                if (dir.Contains('.'))
                {
                    continue;
                }
                path += "/" + dir;
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            File.Copy(files[i], to + "/" + dest, true);
        }
        ABPackHelper.ShowProgress("copy compressed data for ios...", 1);
        AssetDatabase.Refresh();
    }
コード例 #4
0
    void CopyAllBundles()
    {
        if (Directory.Exists(Application.streamingAssetsPath))
        {
            Directory.Delete(Application.streamingAssetsPath, true);
        }
        string targetPath = Application.streamingAssetsPath + "/" + LuaConst.osDir;

        Directory.CreateDirectory(targetPath);

        string        bundlePath     = ABPackHelper.BUILD_PATH + LuaConst.osDir;
        List <string> withExtensions = new List <string>()
        {
            ".ab", ".unity3d", ".txt", ".conf", ".pb"
        };

        string[] files = Directory.GetFiles(bundlePath, "*.*", SearchOption.AllDirectories)
                         .Where(s => withExtensions.Contains(Path.GetExtension(s).ToLower())).ToArray();
        ABPackHelper.ShowProgress("", 0);
        for (int i = 0; i < files.Length; ++i)
        {
            if (Path.GetFileName(files[i]) == "tempsizefile.txt" || Path.GetFileName(files[i]) == "luamd5.txt")
            {
                continue;
            }
            ABPackHelper.ShowProgress("Copying files...", (float)i / (float)files.Length);
            var tempStr = files[i].Replace(bundlePath, "").Replace("\\", "/").TrimStart('/');
            var dirs    = tempStr.Split('/');
            var tempDir = targetPath;
            for (int j = 0; j < dirs.Length - 1; ++j)
            {
                tempDir += "/" + dirs[j];
                if (!Directory.Exists(tempDir))
                {
                    Directory.CreateDirectory(tempDir);
                }
            }
            var  file     = ABPackHelper.GetRelativeAssetsPath(files[i]);
            bool needCopy = true;
            foreach (var key in abTypeMaps.Keys)
            {
                var value = abTypeMaps[key.ToLower()].Split('.');
                if (file.Contains(key) && Convert.ToInt32(value[0]) == 3)
                {
                    needCopy = false;
                    break;
                }
            }
            if (needCopy)
            {
                File.Copy(files[i], targetPath + "/" + tempStr);
            }
        }
        AssetDatabase.Refresh();
        ABPackHelper.ShowProgress("", 1);
        CompressWithZSTD(1024 * 1024 * 10);
    }
コード例 #5
0
 void BuildLuaAndCopyResources()
 {
     ABPackHelper.ShowProgress("", 1);
     AssetDatabase.Refresh();
     Packager.BuildAssetResource(ABPackHelper.GetBuildTarget());
     ABPackHelper.CopyAssets(ABPackHelper.BUILD_PATH + LuaConst.osDir);
     ABPackHelper.CopyToTempAssets();
     AssetDatabase.Refresh();
 }
コード例 #6
0
    public void SetPlayerSetting(string splash_image)
    {
        if (ABPackHelper.GetBuildTarget() == BuildTarget.Android || ABPackHelper.GetBuildTarget() == BuildTarget.iOS)
        {
            PlayerSettings.applicationIdentifier = "com." + company_name + "." + keystore_name.Replace('_', '.');
            PlayerSettings.companyName           = company_name;
            PlayerSettings.productName           = protuct_name;
            Texture2D splashImg = AssetDatabase.LoadAssetAtPath <Texture2D>("Assets/SplashImg/" + splash_image + ".jpg");
            PlayerSettings.virtualRealitySplashScreen = splashImg;
            var tex = AssetDatabase.LoadAssetAtPath <Texture2D>("Assets/AppIcon/" + app_icon + ".png");
            BuildTargetGroup targetGroup = BuildTargetGroup.Android;
            if (ABPackHelper.GetBuildTarget() == BuildTarget.iOS)
            {
                targetGroup = BuildTargetGroup.iOS;
            }

            PlayerSettings.SetIconsForTargetGroup(targetGroup, new Texture2D[1] {
                tex
            });
            if (ABPackHelper.GetBuildTarget() == BuildTarget.Android)
            {
                string keyStorePath = Application.dataPath.Replace("/Assets", "") + "/Public/KeyStore/password.txt";
                var    passwords    = File.ReadAllLines(keyStorePath);
                string password     = string.Empty;
                string aliasName    = string.Empty;
                foreach (var pass in passwords)
                {
                    var strs = pass.Split(':');
                    if (strs[0] == keystore_name)
                    {
                        password  = strs[1].TrimEnd('\n');
                        aliasName = strs[2].TrimEnd('\n');
                        break;
                    }
                }
                if (!string.IsNullOrEmpty(password))
                {
                    string storeName = Application.dataPath.Replace("/Assets", "") + "/Public/KeyStore/" + keystore_name + ".keystore";
                    PlayerSettings.Android.keystoreName = storeName.Replace("\\", "/");
                    PlayerSettings.Android.keystorePass = password;
                    PlayerSettings.Android.keyaliasName = aliasName;
                    PlayerSettings.Android.keyaliasPass = password;
                }
                else
                {
                    PlayerSettings.Android.keyaliasName = "Unsigned";
                }
            }
            AssetDatabase.Refresh();
        }
    }
コード例 #7
0
    private static void RenameLuaFiles()
    {
        string rootPath = IOS_RES_PATH + "/lua/";

        string[] files = Directory.GetFiles(rootPath, "*.unity3d", SearchOption.AllDirectories);
        ABPackHelper.ShowProgress("rename lua file...", 0);
        for (int i = 0; i < files.Length; ++i)
        {
            var file     = files[i];
            var fileName = Path.GetFileName(file);
            var bytes    = File.ReadAllBytes(file);
            File.Delete(file);
            var newName = RenameResFileWithRandomCode(fileName) + ".unity3d";
            File.WriteAllBytes(rootPath + newName, bytes);
            ABPackHelper.ShowProgress("rename lua file: " + fileName, (float)i / (float)files.Length);
        }
        ABPackHelper.ShowProgress("Finished...", 1);
        AssetDatabase.Refresh();
    }
コード例 #8
0
 public void CopySDK()
 {
     try
     {
         string fromPath = Application.dataPath.Replace("/Assets", "") + "/Public/" + sdk_path;
         string toPath   = Application.dataPath + "/Plugins/Android/";
         if (Directory.Exists(toPath))
         {
             Directory.Delete(toPath, true);
         }
         Directory.CreateDirectory(toPath);
         string[] files = Directory.GetFiles(fromPath, "*.*", SearchOption.AllDirectories);
         for (int i = 0; i < files.Length; ++i)
         {
             var    dest = files[i].Replace("\\", "/").Replace(fromPath + "/", "");
             var    dirs = dest.Split('/');
             string path = toPath;
             ABPackHelper.ShowProgress("copy sdk: " + dest, (float)i / (float)files.Length);
             foreach (var dir in dirs)
             {
                 if (dir.Contains("."))
                 {
                     continue;
                 }
                 path += "/" + dir;
                 if (!Directory.Exists(path))
                 {
                     Directory.CreateDirectory(path);
                 }
             }
             File.Copy(files[i], toPath + "/" + dest, true);
         }
         ABPackHelper.ShowProgress("finished...", 1);
         AssetDatabase.Refresh();
     }
     catch (Exception e)
     {
         Debug.LogException(e);
         EditorUtility.ClearProgressBar();
     }
 }
コード例 #9
0
    //递归所有C#代码
    static public void LoadDiectoryCS(DirectoryInfo dictoryInfo)
    {
        if (!dictoryInfo.Exists)
        {
            return;
        }
        FileInfo[] fileInfos = dictoryInfo.GetFiles("*.cs", SearchOption.AllDirectories);
        int        index     = 0;
        Regex      rx        = new Regex("[\u4e00-\u9fa5]+");

        foreach (FileInfo files in fileInfos)
        {
            ABPackHelper.ShowProgress("Check CHN in csripts", (float)index / (float)fileInfos.Length);
            string path = files.FullName.Replace("\\", "/");
            if (path.Contains("Editor/"))
            {
                continue;
            }
            string    assetPath = path.Substring(path.IndexOf("Assets/"));
            TextAsset textAsset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(TextAsset)) as TextAsset;
            string    text      = textAsset.text;
            //用正则表达式把代码里面两种字符串中间的字符串提取出来。
            Regex           reg = new Regex("\"[^\"]*\"");
            MatchCollection mc  = reg.Matches(text);
            foreach (Match m in mc)
            {
                if (rx.IsMatch(m.Value) && (!m.Value.StartsWith("//") && !m.Value.StartsWith("/*")))
                {
                    string format = m.Value.TrimStart('"').TrimEnd('"');
                    if (!Localization.Contains(format) && !string.IsNullOrEmpty(format))
                    {
                        Localization.Add(format);
                        staticWriteText += format + "\n";
                    }
                }
            }
            index++;
        }
        ABPackHelper.ShowProgress("Check CHN in csripts", 1);
    }
コード例 #10
0
    //递归所有UI Prefab
    static public void LoadDiectoryPrefab(DirectoryInfo dictoryInfo)
    {
        if (!dictoryInfo.Exists)
        {
            return;
        }
        FileInfo[] fileInfos = dictoryInfo.GetFiles("*.prefab", SearchOption.AllDirectories);
        int        index     = 0;

        foreach (FileInfo files in fileInfos)
        {
            ABPackHelper.ShowProgress("Check CHN in prefabs", (float)index / (float)fileInfos.Length);
            string     path      = files.FullName.Replace("\\", "/");
            string     assetPath = path.Substring(path.IndexOf("Assets/"));
            GameObject prefab    = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
            GameObject instance  = GameObject.Instantiate(prefab) as GameObject;
            SearchPrefabString(instance.transform);
            GameObject.DestroyImmediate(instance);
            index++;
        }
        ABPackHelper.ShowProgress("Check CHN in prefabs", 1);
    }
コード例 #11
0
    bool SetPlayerSavePath()
    {
        string ext          = "";
        string locationPath = "";
        string appName      = DateTime.Now.ToString("yyyyMMdd");

        switch (ABPackHelper.GetBuildTarget())
        {
        case BuildTarget.Android:
            ext          = "apk";
            locationPath = Path.Combine(Application.dataPath, "../android_apk/");
            break;

        case BuildTarget.StandaloneWindows:
        case BuildTarget.StandaloneWindows64:
            ext          = "exe";
            locationPath = Path.Combine(Application.dataPath, "../win_exe/");
            if (!Directory.Exists(locationPath))
            {
                Directory.CreateDirectory(locationPath);
            }
            break;

        case BuildTarget.iOS:
            locationPath = Application.dataPath.Replace("jyjh/Assets", "") + "xcode_proj";
            break;
        }
        if (!Directory.Exists(locationPath))
        {
            Directory.CreateDirectory(locationPath);
        }
        Apk_Save_Path = EditorUtility.SaveFilePanel(ABLanguage.PACK_SAVE_FOLDER, locationPath, appName, ext);
        if (string.IsNullOrEmpty(Apk_Save_Path))
        {
            return(false);
        }
        return(true);
    }
コード例 #12
0
    public static void CommitIOSCompressedData()
    {
        if (ABPackHelper.GetBuildTarget() != BuildTarget.iOS)
        {
            return;
        }

        try
        {
            string path = ABPackHelper.TEMP_ASSET_PATH + "/IOSCompress/";
            var    args = string.Format("/command:commit /path:{0} /logmsg:提交ios打包压缩资源", path);
            var    p    = Process.Start(TORTOISEPROC_NAME, args);
            p.WaitForExit();
        }
        catch (Exception e)
        {
            EditorUtility.DisplayDialog("错误", "资源上传svn错误: " + e.Message, "OK");
        }
        finally
        {
            AssetDatabase.Refresh();
        }
    }
コード例 #13
0
    public static void InsertRandomFile()
    {
        string[]  files = Directory.GetFiles(IOS_RES_PATH, "*.*", SearchOption.AllDirectories);
        Hashtable tbl   = new Hashtable();

        ABPackHelper.ShowProgress("insert random file...", 0);
        for (int i = 0; i < files.Length; ++i)
        {
            var path = Path.GetDirectoryName(files[i]);
            if (tbl.ContainsKey(path))
            {
                continue;
            }
            tbl.Add(path, "path");
            int randomNum = UnityEngine.Random.Range(30, 200);
            for (int j = 0; j < randomNum; ++j)
            {
                try
                {
                    string str       = GetRandomString(30);
                    var    save_path = Path.Combine(path, str);
                    if (File.Exists(save_path))
                    {
                        continue;
                    }
                    File.WriteAllBytes(save_path, Encoding.GetBytes(str));
                }
                catch
                {
                }
            }
            ABPackHelper.ShowProgress("insert random file...", (float)i / (float)files.Length);
        }
        ABPackHelper.ShowProgress("insert random file...", 1);
        AssetDatabase.Refresh();
    }
コード例 #14
0
    void BuildPlayer(bool packAllRes, bool forceUpdate)
    {
        var option = BuildOptions.None;

        if (debug)
        {
            option |= BuildOptions.AllowDebugging;
        }
        if (development)
        {
            option |= BuildOptions.Development;
        }
        if (autoConnectProfile)
        {
            option |= BuildOptions.ConnectWithProfiler;
        }
        var temps = Apk_Save_Path.Replace("\\", "/").Split('/');

        if ((ABPackHelper.GetBuildTarget() == BuildTarget.Android ||
             ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows64 ||
             ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows) &&
            sdkConfig != null)
        {
            string lastChannel = string.Empty;
            for (int i = 0; i < sdkConfig.items.Count; ++i)
            {
                StringBuilder final_path = new StringBuilder();
                for (int j = 0; j < temps.Length - 1; ++j)
                {
                    final_path.Append(temps[j] + "/");
                }
                var item = sdkConfig.items[i];
                if (item.need_subpack == 0 && !packAllRes)
                {
                    continue;
                }
                if (ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows64 || ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows)
                {
                    final_path.Append(DateTime.Now.ToString("yyyyMMdd") + "/");
                    if (!Directory.Exists(final_path.ToString()))
                    {
                        Directory.CreateDirectory(final_path.ToString());
                    }
                    final_path.Append(item.game_name + "_v");
                }
                else
                {
                    if (packAllRes)
                    {
                        if (item.development == 1)
                        {
                            option |= BuildOptions.Development;
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_allpack_dev_v");
                        }
                        else if (item.use_sdk == 1)
                        {
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_allpack_sdk_v");
                        }
                        else
                        {
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_allpack_test_v");
                        }
                    }
                    else
                    {
                        if (item.development == 1)
                        {
                            option |= BuildOptions.Development;
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_subpack_dev_v");
                        }
                        else if (item.use_sdk == 1)
                        {
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_subpack_sdk_v");
                        }
                        else
                        {
                            final_path.Append(item.game_name + DateTime.Now.ToString("yyyyMMdd") + "_subpack_test_v");
                        }
                    }
                }
                final_path.Append(gameVersion.ToString());
                if (ABPackHelper.GetBuildTarget() == BuildTarget.Android)
                {
                    final_path.Append(".apk");
                    if (File.Exists(final_path.ToString()))
                    {
                        File.Delete(final_path.ToString());
                    }
                    // 写入并保存sdk启用配置
                    item.CopyConfig();
                    item.CopySDK();
                    item.SetPlayerSetting(sdkConfig.splash_image);
                    item.SaveSDKConfig();
                    item.SplitAssets(sdkConfig.split_assets);
                    IncreaseLEBIAN_VERCODE(forceUpdate, item.update_along);
                    if (item.update_along == 0 && forceUpdate)
                    {
                        if (Directory.Exists(Application.streamingAssetsPath))
                        {
                            Directory.Delete(Application.streamingAssetsPath, true);
                        }
                    }
                }
                else if (ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows64 || ABPackHelper.GetBuildTarget() == BuildTarget.StandaloneWindows)
                {
                    final_path.Append(".exe");
                    if (Directory.Exists(final_path.ToString()))
                    {
                        Directory.Delete(final_path.ToString(), true);
                    }
                    item.CopyConfig();
                }
                AssetDatabase.Refresh();
                BuildPipeline.BuildPlayer(ABPackHelper.GetBuildScenes(), final_path.ToString(), ABPackHelper.GetBuildTarget(), option);
                AssetDatabase.Refresh();
                item.ClearSDK();

                SVNHelper.UpdateAll();
            }
        }
        else if (ABPackHelper.GetBuildTarget() == BuildTarget.iOS)
        {
            // 在上传目录新建一个ios_check.txt文件用于判断当前包是否出于提审状态
            string checkFile = ABPackHelper.ASSET_PATH + LuaConst.osDir + "/ios_check.txt";
            if (File.Exists(checkFile))
            {
                File.Delete(checkFile);
            }
            File.WriteAllText(checkFile, "1");

            XCConfigItem configItem = XCConfigItem.ParseXCConfig(XCodePostProcess.config_path);
            if (configItem != null)
            {
                PlayerSettings.applicationIdentifier = configItem.bundleIdentifier;
                PlayerSettings.productName           = configItem.product_name;
                configItem.CopyConfig();
            }
            IOSGenerateHelper.IOSConfusing();
            AssetDatabase.Refresh();
            BuildPipeline.BuildPlayer(ABPackHelper.GetBuildScenes(), Apk_Save_Path, ABPackHelper.GetBuildTarget(), option);
            AssetDatabase.Refresh();
        }

        Resources.UnloadUnusedAssets();
        GC.Collect();

        Debug.Log("<color=green>Build success!</color>");
    }
コード例 #15
0
    //提取lua上的中文
    static public void LoadDiectoryLua(DirectoryInfo dictoryInfo)
    {
        if (!dictoryInfo.Exists)
        {
            return;
        }
        FileInfo[]    fileInfos    = dictoryInfo.GetFiles("*.lua", SearchOption.AllDirectories);
        int           index        = 0;
        Regex         rx           = new Regex("[\u4e00-\u9fa5]+");
        List <string> chnLuaAssets = new List <string>();

        foreach (FileInfo files in fileInfos)
        {
            ABPackHelper.ShowProgress("Check CHN in lua", (float)index / (float)fileInfos.Length);
            string   path      = files.FullName.Replace("\\", "/");
            string   assetPath = path.Substring(path.IndexOf("Assets/"));
            string[] lines     = File.ReadAllLines(assetPath);
            if (assetPath.Contains("language/") || assetPath.Contains("xlsdata/") ||
                assetPath.Contains("Setting/"))
            {
                continue;
            }
            //用正则表达式把代码里面两种字符串中间的字符串提取出来。
            Regex reg = new Regex("\"[^\"]*\"");
            foreach (string line in lines)
            {
                MatchCollection mc = reg.Matches(line);
                foreach (Match m in mc)
                {
                    if (rx.IsMatch(m.Value))
                    {
                        string prefix = line.Substring(0, line.Length - m.Value.Length - 1);
                        if (prefix.EndsWith("print(") || prefix.EndsWith("log(") || prefix.EndsWith("logWarning(") ||
                            prefix.EndsWith("logWarn(") || prefix.EndsWith("logError(") || prefix.EndsWith("error("))
                        {
                            continue;
                        }
                        string format = m.Value.TrimStart('"').TrimEnd('"');
                        if (!Localization.Contains(format) && !string.IsNullOrEmpty(format))
                        {
                            Localization.Add(format);
                            staticWriteText += format + "\n";
                            if (!chnLuaAssets.Contains(assetPath))
                            {
                                chnLuaAssets.Add(assetPath);
                            }
                        }
                    }
                }
            }
            index++;
        }
        ABPackHelper.ShowProgress("Check CHN in lua", 1);
        string luaCHNoutPath = Application.dataPath + "/Res/I18N/lua_out.txt";

        if (File.Exists(luaCHNoutPath))
        {
            File.Delete(luaCHNoutPath);
        }
        File.WriteAllLines(luaCHNoutPath, chnLuaAssets.ToArray());
    }
コード例 #16
0
 void ClearABName()
 {
     ABPackHelper.ClearAllAbName();
 }
コード例 #17
0
    void CompressWithZSTD(long maxFileSize)
    {
        string outPutPath = Application.streamingAssetsPath + "/" + LuaConst.osDir;

        ABPackHelper.ShowProgress("Hold on...", 0);
        var dirInfo = new DirectoryInfo(outPutPath);
        var dirs    = dirInfo.GetDirectories();
        Dictionary <int, List <string> > allFiles = new Dictionary <int, List <string> >();
        // data原始包控制在10M左右
        long curSize  = 0;
        int  tmpIndex = 0;

        for (int i = 0; i < dirs.Length; ++i)
        {
            if (dirs[i].Name == "lua")
            {
                continue;
            }
            var abFileInfos = dirs[i].GetFiles("*.*", SearchOption.AllDirectories);
            for (int j = 0; j < abFileInfos.Length; ++j)
            {
                if (abFileInfos[j].FullName.EndsWith(".meta"))
                {
                    continue;
                }
                if (curSize >= maxFileSize)
                {
                    curSize = 0;
                    tmpIndex++;
                }
                if (curSize == 0)
                {
                    allFiles.Add(tmpIndex, new List <string>());
                }
                var fileName = ABPackHelper.GetRelativeAssetsPath(abFileInfos[j].FullName);
                allFiles[tmpIndex].Add(fileName);
                curSize += File.ReadAllBytes(fileName).Length;
            }
        }
        int index = 0;

        // 合并生成的bundle文件,合成10M左右的小包(二进制)
        foreach (var key in allFiles.Keys)
        {
            var tmpName = "data" + key;
#if UNITY_IOS
            tmpName = IOSGenerateHelper.RenameResFileWithRandomCode(tmpName);
#endif
            var savePath = string.Format("{0}/{1}.tmp", outPutPath, tmpName);
            ABPackHelper.ShowProgress("Streaming data...", (float)index++ / (float)allFiles.Count);
            using (var fs = new FileStream(savePath, FileMode.CreateNew))
            {
                using (var writer = new BinaryWriter(fs))
                {
                    for (int i = 0; i < allFiles[key].Count; ++i)
                    {
                        var bytes  = File.ReadAllBytes(allFiles[key][i]);
                        var abName = allFiles[key][i].Replace("Assets/StreamingAssets/" + LuaConst.osDir + "/", "");
                        writer.Write(abName);
                        writer.Write(bytes.Length);
                        writer.Write(bytes);
                    }
                }
            }
        }
        ABPackHelper.ShowProgress("Finished...", 1);
        for (int i = 0; i < dirs.Length; ++i)
        {
            if (dirs[i].Name == "lua")
            {
                continue;
            }
            Directory.Delete(dirs[i].FullName, true);
        }
        AssetDatabase.Refresh();

        // 对合并后的文件进行压缩
        ABPackHelper.ShowProgress("Hold on...", 0);
        var pakFiles = Directory.GetFiles(outPutPath, "*.tmp", SearchOption.AllDirectories);
        for (int i = 0; i < pakFiles.Length; ++i)
        {
            var savePath = string.Format("{0}/{1}.bin", outPutPath, Path.GetFileNameWithoutExtension(pakFiles[i]));
            ABPackHelper.ShowProgress("compress with zstd...", (float)i / (float)pakFiles.Length);
            var fileName = ABPackHelper.GetRelativeAssetsPath(pakFiles[i]);
            using (var compressFs = new FileStream(savePath, FileMode.CreateNew))
            {
                using (var compressor = new Compressor(new CompressionOptions(CompressionOptions.MaxCompressionLevel)))
                {
                    var bytes = compressor.Wrap(File.ReadAllBytes(fileName));
#if UNITY_IOS
                    bytes = Crypto.Encode(bytes);
#endif
                    compressFs.Write(bytes, 0, bytes.Length);
                }
            }
            File.Delete(fileName);
        }
        ABPackHelper.ShowProgress("Finished...", 1);

        // 生成包体第一次进入游戏解压缩配置文件
        StringBuilder builder  = new StringBuilder();
        string[]      allfiles = Directory.GetFiles(outPutPath, "*.*", SearchOption.AllDirectories);
        for (int i = 0; i < allfiles.Length; ++i)
        {
            if (allfiles[i].EndsWith(".meta"))
            {
                continue;
            }
            if (allfiles[i].EndsWith("datamap.ab"))
            {
                continue;
            }

            var fileName = allfiles[i].Replace(outPutPath, "").Replace("\\", "/").TrimStart('/');
            builder.Append(fileName);
            builder.Append('|');
            builder.Append(MD5.ComputeHashString(allfiles[i]));
            builder.Append("\n");
        }
        var packFlistPath = outPutPath + "/packlist.txt";
        if (File.Exists(packFlistPath))
        {
            File.Delete(packFlistPath);
        }
        File.WriteAllText(packFlistPath, builder.ToString());
        AssetDatabase.Refresh();
    }