示例#1
0
        /// <summary>
        /// Pack Archive PSB
        /// </summary>
        /// <param name="jsonPath">json path</param>
        /// <param name="key">crypt key</param>
        /// <param name="intersect">Only pack files which existed in info.psb.m</param>
        /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param>
        /// <param name="enableParallel">parallel process</param>
        /// <param name="keyLen">key length</param>
        public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true,
                                       int keyLen = 131)
        {
            if (!File.Exists(jsonPath))
            {
                return;
            }
            PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath);

            if (infoPsb.Type != PsbType.ArchiveInfo)
            {
                Console.WriteLine("Json is not an ArchiveInfo PSB.");
                return;
            }

            var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath);

            if (!resx.Context.ContainsKey(Context_ArchiveSource) ||
                resx.Context[Context_ArchiveSource] == null)
            {
                Console.WriteLine("ArchiveSource must be specified in resx.json Context.");
                return;
            }

            if (keyLen > 0)
            {
                resx.Context[Context_MdfKeyLength] = keyLen;
            }

            string infoKey = null;

            if (resx.Context[Context_MdfKey] is string mdfKey)
            {
                infoKey = mdfKey;
            }

            List <string> sourceDirs = null;

            if (resx.Context[Context_ArchiveSource] is string path)
            {
                sourceDirs = new List <string> {
                    path
                };
            }
            else if (resx.Context[Context_ArchiveSource] is IList paths)
            {
                sourceDirs = new List <string>(paths.Count);
                sourceDirs.AddRange(from object p in paths select p.ToString());
            }
            else
            {
                Console.WriteLine("ArchiveSource incorrect.");
                return;
            }

            var           baseDir = Path.GetDirectoryName(jsonPath);
            var           files   = new Dictionary <string, (string Path, ProcessMethod Method)>();
            var           suffix  = ArchiveInfoPsbGetSuffix(infoPsb);
            List <string> filter  = null;

            if (intersect)
            {
                filter = ArchiveInfoPsbCollectFiles(infoPsb, suffix);
            }

            void CollectFiles(string targetDir)
            {
                if (!Directory.Exists(targetDir))
                {
                    return;
                }

                foreach (var f in Directory.EnumerateFiles(targetDir))
                {
                    if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture))
                    {
                        continue;
                    }
                    else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture))
                    {
                        var name = Path.GetFileNameWithoutExtension(f);
                        if (preferPacked && files.ContainsKey(name) &&
                            files[name].Method != ProcessMethod.Compile)
                        {
                            //ignore
                        }
                        else
                        {
                            if (intersect && filter != null && !filter.Contains(name))
                            {
                                //ignore
                            }
                            else
                            {
                                files[name] = (f, ProcessMethod.Compile);
                            }
                        }
                    }
                    else
                    {
                        var name = Path.GetFileName(f);
                        if (!preferPacked && files.ContainsKey(name) &&
                            files[name].Method == ProcessMethod.Compile)
                        {
                            //ignore
                        }
                        else
                        {
                            if (intersect && filter != null && !filter.Contains(name))
                            {
                                //ignore
                            }
                            else
                            {
                                using var fs = File.OpenRead(f);
                                if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF")
                                {
                                    files[name] = (f, ProcessMethod.EncodeMdf);
                                }
                                else
                                {
                                    files[name] = (f, ProcessMethod.None);
                                }
                            }
                        }
                    }
                }
            }

            //Collect files
            foreach (var sourceDir in sourceDirs)
            {
                CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir));
            }

            var fileName    = Path.GetFileName(jsonPath);
            var packageName = Path.GetFileNameWithoutExtension(fileName);

            var coreName = PsbExtension.ArchiveInfoGetPackageName(packageName);

            fileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin";

            var fileInfoDic = new PsbDictionary(files.Count);
            var fmContext   = FreeMount.CreateContext(resx.Context);

            byte[] bodyBin = null;
            if (enableParallel)
            {
                var contents = new ConcurrentBag <(string Name, Stream Content)>();
                Parallel.ForEach(files, (kv) =>
                {
                    var fileNameWithoutSuffix = ArchiveInfoPsbGetFileName(kv.Key, suffix);
                    if (kv.Value.Method == ProcessMethod.None)
                    {
                        contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path)));
                        return;
                    }

                    var mdfContext = new Dictionary <string, object>(resx.Context);
                    var context    = FreeMount.CreateContext(mdfContext);
                    if (!string.IsNullOrEmpty(key))
                    {
                        mdfContext[Context_MdfKey] = key + fileNameWithoutSuffix + suffix;
                    }
                    else if (resx.Context[Context_MdfMtKey] is string mtKey)
                    {
                        mdfContext[Context_MdfKey] =
                            mtKey + fileNameWithoutSuffix + suffix;
                    }
                    else
                    {
                        mdfContext.Remove(Context_MdfKey);
                    }

                    mdfContext.Remove(Context_ArchiveSource);

                    if (kv.Value.Method == ProcessMethod.EncodeMdf)
                    {
                        contents.Add((fileNameWithoutSuffix, context.PackToShell(
                                          File.OpenRead(kv.Value.Path), "MDF")));
                    }
                    else
                    {
                        var content = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path);

                        var outputMdf = context.PackToShell(content.Psb.ToStream(), "MDF");
                        contents.Add((fileNameWithoutSuffix, outputMdf));
                    }
                });
示例#2
0
        /// <summary>
        /// Pack Archive PSB
        /// </summary>
        /// <param name="jsonPath">json path</param>
        /// <param name="key">crypt key</param>
        /// <param name="intersect">Only pack files which existed in info.psb.m</param>
        /// <param name="preferPacked">Prefer using PSB files rather than json files in source folder</param>
        /// <param name="enableParallel">parallel process</param>
        /// <param name="keyLen">key length</param>
        /// <param name="keepRaw">Do not try to compile json or pack MDF</param>
        public static void PackArchive(string jsonPath, string key, bool intersect, bool preferPacked, bool enableParallel = true,
                                       int keyLen = 131, bool keepRaw = false)
        {
            if (!File.Exists(jsonPath))
            {
                return;
            }
            PSB infoPsb = PsbCompiler.LoadPsbFromJsonFile(jsonPath);

            if (infoPsb.Type != PsbType.ArchiveInfo)
            {
                Console.WriteLine("Json is not an ArchiveInfo PSB.");
                return;
            }

            var resx = PsbResourceJson.LoadByPsbJsonPath(jsonPath);

            if (!resx.Context.ContainsKey(Context_ArchiveSource) ||
                resx.Context[Context_ArchiveSource] == null)
            {
                Console.WriteLine("ArchiveSource must be specified in resx.json Context.");
                return;
            }

            if (keyLen > 0)
            {
                resx.Context[Context_MdfKeyLength] = keyLen;
            }

            string infoKey = null;

            if (resx.Context[Context_MdfKey] is string mdfKey)
            {
                infoKey = mdfKey;
            }

            List <string> sourceDirs = null;

            if (resx.Context[Context_ArchiveSource] is string path)
            {
                sourceDirs = new List <string> {
                    path
                };
            }
            else if (resx.Context[Context_ArchiveSource] is IList paths)
            {
                sourceDirs = new List <string>(paths.Count);
                sourceDirs.AddRange(from object p in paths select p.ToString());
            }
            else
            {
                Console.WriteLine("ArchiveSource incorrect.");
                return;
            }

            var           baseDir = Path.GetDirectoryName(jsonPath);
            var           files   = new Dictionary <string, (string Path, ProcessMethod Method)>();
            var           suffix  = ArchiveInfoGetSuffix(infoPsb);
            List <string> filter  = null;

            if (intersect) //only collect files appeared in json
            {
                filter = ArchiveInfoCollectFiles(infoPsb, suffix).ToList();
            }

            void CollectFiles(string targetDir)
            {
                if (!Directory.Exists(targetDir))
                {
                    return;
                }

                foreach (var f in Directory.EnumerateFiles(targetDir))
                {
                    if (f.EndsWith(".resx.json", true, CultureInfo.InvariantCulture))
                    {
                        continue;
                    }
                    else if (f.EndsWith(".json", true, CultureInfo.InvariantCulture)) //json source, need compile
                    {
                        var name = Path.GetFileNameWithoutExtension(f);
                        if (preferPacked && files.ContainsKey(name) &&
                            files[name].Method != ProcessMethod.Compile) //it's always right no matter set or replace
                        {
                            //ignore
                        }
                        else
                        {
                            if (intersect && filter != null && !filter.Contains(name)) //this file is not appeared in json
                            {
                                //ignore
                            }
                            else
                            {
                                files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.Compile);
                            }
                        }
                    }
                    else
                    {
                        var name = Path.GetFileName(f);
                        if (!preferPacked && files.ContainsKey(name) &&
                            files[name].Method == ProcessMethod.Compile)
                        {
                            //ignore
                        }
                        else
                        {
                            if (intersect && filter != null && !filter.Contains(name))
                            {
                                //ignore
                            }
                            else
                            {
                                using var fs = File.OpenRead(f);
                                if (!MdfFile.IsSignatureMdf(fs) && name.DefaultShellType() == "MDF")
                                {
                                    files[name] = (f, keepRaw? ProcessMethod.None: ProcessMethod.EncodeMdf);
                                }
                                else
                                {
                                    files[name] = (f, ProcessMethod.None);
                                }
                            }
                        }
                    }
                }
            }

            //Collect files
            Console.WriteLine("Collecting files ...");
            foreach (var sourceDir in sourceDirs)
            {
                CollectFiles(Path.IsPathRooted(sourceDir) ? sourceDir : Path.Combine(baseDir, sourceDir));
            }

            Console.WriteLine($"Packing {files.Count} files ...");
            var bodyBinFileName = Path.GetFileName(jsonPath);
            var packageName     = Path.GetFileNameWithoutExtension(bodyBinFileName);

            var coreName = ArchiveInfoGetPackageName(packageName);

            bodyBinFileName = string.IsNullOrEmpty(coreName) ? packageName + "_body.bin" : coreName + "_body.bin";

            //using var mmFile =
            //    MemoryMappedFile.CreateFromFile(bodyBinFileName, FileMode.Create, coreName, );
            using var bodyFs = File.OpenWrite(bodyBinFileName);
            var fileInfoDic = new PsbDictionary(files.Count);
            var fmContext   = FreeMount.CreateContext(resx.Context);

            //byte[] bodyBin = null;
            if (enableParallel)
            {
                var contents = new ConcurrentBag <(string Name, Stream Content)>();
                Parallel.ForEach(files, (kv) =>
                {
                    var fileNameWithoutSuffix = ArchiveInfoGetFileNameRemoveSuffix(kv.Key, suffix);

                    if (kv.Value.Method == ProcessMethod.None)
                    {
                        contents.Add((fileNameWithoutSuffix, File.OpenRead(kv.Value.Path)));
                        return;
                    }

                    var mdfContext = new Dictionary <string, object>(resx.Context);
                    var context    = FreeMount.CreateContext(mdfContext);
                    if (!string.IsNullOrEmpty(key))
                    {
                        mdfContext[Context_MdfKey] = key + kv.Key;
                    }
                    else if (resx.Context[Context_MdfMtKey] is string mtKey)
                    {
                        mdfContext[Context_MdfKey] = mtKey + kv.Key;
                    }
                    else
                    {
                        mdfContext.Remove(Context_MdfKey);
                    }

                    mdfContext.Remove(Context_ArchiveSource);

                    if (kv.Value.Method == ProcessMethod.EncodeMdf)
                    {
                        using var mmFs = MemoryMappedFile.CreateFromFile(kv.Value.Path, FileMode.Open);

                        //using var fs = File.OpenRead(kv.Value.Path);
                        contents.Add((fileNameWithoutSuffix, context.PackToShell(mmFs.CreateViewStream(), "MDF"))); //disposed later
                    }
                    else
                    {
                        var content   = PsbCompiler.LoadPsbAndContextFromJsonFile(kv.Value.Path);
                        var stream    = content.Psb.ToStream();
                        var shellType = kv.Key.DefaultShellType(); //MARK: use shellType in filename, or use suffix in info?
                        if (!string.IsNullOrEmpty(shellType))
                        {
                            stream = context.PackToShell(stream, shellType); //disposed later
                        }
                        contents.Add((fileNameWithoutSuffix, stream));
                    }
                });