// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.Linq; using System.Text; using System.IO; using AutomationTool; using UnrealBuildTool; using System.Diagnostics; public class HTMLPakAutomation { ProjectParams Params; DeploymentContext SC; string PakOrderFileLocation; Dictionary DependencyJson; public static bool CanCreateMapPaks(ProjectParams Param) { bool UseAsyncLevelLoading = false; var ConfigCache = new UnrealBuildTool.ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", Path.GetDirectoryName(Param.RawProjectPath), CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine")); ConfigCache.GetBool("/Script/HTML5PlatformEditor.HTML5TargetSettings", "UseAsyncLevelLoading", out UseAsyncLevelLoading); if (Param.Run) return false; return UseAsyncLevelLoading; } public HTMLPakAutomation(ProjectParams InParam, DeploymentContext InSC) { Params = InParam; SC = InSC; var PakOrderFileLocationBase = CommandUtils.CombinePaths(SC.ProjectRoot, "Build", SC.FinalCookPlatform, "FileOpenOrder"); PakOrderFileLocation = CommandUtils.CombinePaths(PakOrderFileLocationBase, "GameOpenOrder.log"); if (!CommandUtils.FileExists_NoExceptions(PakOrderFileLocation)) { // Use a fall back, it doesn't matter if this file exists or not. GameOpenOrder.log is preferred however PakOrderFileLocation = CommandUtils.CombinePaths(PakOrderFileLocationBase, "EditorOpenOrder.log"); } string PakPath = Path.Combine(new string[] { Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5", SC.ShortProjectName}); if (Directory.Exists(PakPath)) { Directory.Delete(PakPath,true); } // read in the json file. string JsonFile = CommandUtils.CombinePaths(new string[] { SC.ProjectRoot, "Saved", "Cooked", "HTML5", Params.ShortProjectName, "MapDependencyGraph.json" }); string text = File.ReadAllText(JsonFile); DependencyJson = fastJSON.JSON.Instance.ToObject>(text); } /// /// Create Emscripten Data package with Necessary Packages. /// /// public void CreateEmscriptenDataPackage(string PackagePath, string FinalDataLocation) { // we need to operate in the root using (new PushedDirectory(Path.Combine(PackagePath))) { string PythonPath = HTML5SDKInfo.PythonPath(); string PackagerPath = HTML5SDKInfo.EmscriptenPackager(); string CmdLine = string.Format("\"{0}\" \"{1}\" --preload {2}\\ --js-output=\"{1}.js\"", PackagerPath, FinalDataLocation, SC.ShortProjectName); CommandUtils.RunAndLog(CommandUtils.CmdEnv, PythonPath, CmdLine); } } /// /// Create a Pak from Engine Contents. /// public void CreateEnginePak() { string StagedDir = Path.Combine(Params.BaseStageDirectory, "HTML5");// CommandUtils.CombinePaths(SC.ProjectRoot, "Saved", "Cooked", "HTML5"); string PakPath = Path.Combine( new string [] { Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5", SC.ShortProjectName, "Content","Paks"}); if (!Directory.Exists(PakPath)) { Directory.CreateDirectory(PakPath); } // find list of files in the Engine Dir. string[] files = Directory.GetFiles( StagedDir + "\\Engine\\", "*", SearchOption.AllDirectories); Dictionary UnrealPakResponseFile = new Dictionary(); // create response file structure. foreach( string file in files ) { string MountPoint = file.Remove(0, StagedDir.Length + 1); UnrealPakResponseFile[file] = "../../../" + MountPoint; } RunUnrealPak(UnrealPakResponseFile, PakPath + "\\EnginePak.pak", null, "", true); } /// /// Create a pak from a minimum set of game files /// public void CreateGamePak() { // create a pak of game config and first level files. string StagedDir = Path.Combine(Params.BaseStageDirectory, "HTML5");// CommandUtils.CombinePaths(SC.ProjectRoot, "Saved", "Cooked", "HTML5"); string PakPath = Path.Combine(new string[] { Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5", Params.ShortProjectName, "Content", "Paks" }); if (!Directory.Exists(PakPath)) { Directory.CreateDirectory(PakPath); } List AllFiles = new List(); try { string[] SlateFiles = Directory.GetFiles(StagedDir + "\\" + Params.ShortProjectName + "\\Content\\Slate", "*", SearchOption.AllDirectories); AllFiles.AddRange(SlateFiles); } catch ( Exception /*Ex*/) { // not found. } try { string[] Resources = Directory.GetFiles(StagedDir + "\\" + Params.ShortProjectName + "\\Content\\Resources", "*", SearchOption.AllDirectories); AllFiles.AddRange(Resources); } catch (Exception /*Ex*/) { // not found. } string[] ConfigFiles = Directory.GetFiles(StagedDir + "\\" + Params.ShortProjectName + "\\Config", "*", SearchOption.AllDirectories); AllFiles.AddRange(ConfigFiles); string[] TopLevelFiles = Directory.GetFiles(StagedDir + "\\" + Params.ShortProjectName + "\\", "*", SearchOption.TopDirectoryOnly); AllFiles.AddRange(TopLevelFiles); Dictionary UnrealPakResponseFile = new Dictionary(); // create response file structure. foreach (string file in AllFiles) { string MountPoint = file.Remove(0, StagedDir.Length + 1); UnrealPakResponseFile[file] = "../../../" + MountPoint; } RunUnrealPak(UnrealPakResponseFile, PakPath + "\\" + Params.ShortProjectName + ".pak", null, "", true); } /// /// Create ContentDirectory Pak. /// public void CreateContentDirectoryPak() { object[] Value = (object[])DependencyJson["ContentDirectoryAssets"]; List ContentDirectory = Array.ConvertAll(Value, Convert.ToString).ToList(); CreatePak("ContentDirectoryAssets", ContentDirectory); } /// /// Create Maps Paks based on JSON file created during Cooking. (-GenerateDependenciesForMaps) /// public void CreateMapPak() { // read in the json file. object[] Value = (object[])DependencyJson["ContentDirectoryAssets"]; List ContentDirectory = Array.ConvertAll(Value, Convert.ToString).ToList(); foreach (string Pak in DependencyJson.Keys) { object[] ObjectList = (object[])DependencyJson[Pak]; List Assets = Array.ConvertAll(ObjectList, Convert.ToString).ToList(); // add itself. Assets.Add(Pak); if (Pak != "ContentDirectoryAssets") { // remove content directory assets. Assets = Assets.Except(ContentDirectory).ToList(); string[] Tokens = Pak.Split('/'); CreatePak(Tokens[Tokens.Length - 1], Assets); } } } /// /// Create Delta Paks based on Level Transitions. /// /// /// public void CreateDeltaMapPaks(string MapFrom, string MapTo) { foreach (string NameKey in DependencyJson.Keys) { if (NameKey.Contains(MapFrom)) { MapFrom = NameKey; } if (NameKey.Contains(MapTo)) { MapTo = NameKey; } } List MapFromAssets = Array.ConvertAll(DependencyJson[MapFrom] as object[], Convert.ToString).ToList(); MapFromAssets.Add(MapFrom); List MapToAssets = Array.ConvertAll(DependencyJson[MapTo] as object[], Convert.ToString).ToList(); MapToAssets.Add(MapTo); object[] Value = (object[])DependencyJson["ContentDirectoryAssets"]; List ContentDirectory = Array.ConvertAll(Value, Convert.ToString).ToList(); // Delta. List DeltaAssets = MapToAssets.Except(MapFromAssets).Except(ContentDirectory).ToList(); // create a pak file for this delta. string[] Tokens_MapFrom = MapFrom.Split('/'); string[] Tokens_MapTo = MapTo.Split('/'); string DeltaPakName = Tokens_MapFrom[Tokens_MapFrom.Length - 1] + "_" + Tokens_MapTo[Tokens_MapTo.Length - 1]; CreatePak(DeltaPakName, DeltaAssets); } /// /// Writes a pak response file to disk /// /// /// private static void WritePakResponseFile(string Filename, Dictionary ResponseFile, bool Compressed) { using (var Writer = new StreamWriter(Filename, false, new System.Text.UTF8Encoding(true))) { foreach (var Entry in ResponseFile) { string Line = String.Format("\"{0}\" \"{1}\"", Entry.Key, Entry.Value); if (Compressed) { Line += " -compress"; } Writer.WriteLine(Line); } } } /// /// Create a Pak file /// private void RunUnrealPak(Dictionary UnrealPakResponseFile, string OutputLocation, string EncryptionKeys, string PlatformOptions, bool Compressed) { if (UnrealPakResponseFile.Count < 1) { return; } string PakName = Path.GetFileNameWithoutExtension(OutputLocation); string UnrealPakResponseFileName = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LogFolder, "PakList_" + PakName + ".txt"); WritePakResponseFile(UnrealPakResponseFileName, UnrealPakResponseFile, Compressed); var UnrealPakExe = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine/Binaries/Win64/UnrealPak.exe"); string CmdLine = CommandUtils.MakePathSafeToUseWithCommandLine(OutputLocation) + " -create=" + CommandUtils.MakePathSafeToUseWithCommandLine(UnrealPakResponseFileName); if (!String.IsNullOrEmpty(EncryptionKeys)) { CmdLine += " -sign=" + CommandUtils.MakePathSafeToUseWithCommandLine(EncryptionKeys); } if (GlobalCommandLine.Installed) { CmdLine += " -installed"; } CmdLine += " -order=" + CommandUtils.MakePathSafeToUseWithCommandLine(PakOrderFileLocation); if (GlobalCommandLine.UTF8Output) { CmdLine += " -UTF8Output"; } CmdLine += PlatformOptions; CommandUtils.RunAndLog(CommandUtils.CmdEnv, UnrealPakExe, CmdLine, Options: CommandUtils.ERunOptions.Default | CommandUtils.ERunOptions.UTF8Output); } /// /// Create a Pak. /// /// /// private void CreatePak(string Name, List Assets) { string CookedDir = Path.Combine(Params.BaseStageDirectory, "HTML5"); string PakPath = Path.Combine(new string[] { Path.GetDirectoryName(Params.RawProjectPath), "Binaries", "HTML5", Params.ShortProjectName, "Content", "Paks" }); if (!Directory.Exists(PakPath)) { Directory.CreateDirectory(PakPath); } Assets.Add(Name); List Files = new List(); foreach( string Asset in Assets) { string GameDir = "/Game/"; if (Asset.StartsWith(GameDir)) { string PathOnDiskasset = CommandUtils.CombinePaths(CookedDir, Params.ShortProjectName, "Content", Asset.Remove(0, GameDir.Length) + ".uasset"); string PathOnDiskmap = CommandUtils.CombinePaths(CookedDir, Params.ShortProjectName, "Content", Asset.Remove(0, GameDir.Length)+ ".umap"); if (File.Exists(PathOnDiskmap)) { Files.Add(PathOnDiskmap); } else if (File.Exists(PathOnDiskasset)) { Files.Add(PathOnDiskasset); } else { System.Console.WriteLine(Asset + " not found, skipping !!"); } } } // we have all the files. Dictionary UnrealPakResponseFile = new Dictionary(); // create response file structure. foreach (string file in Files) { string MountPoint = file.Remove(0, CookedDir.Length + 1); UnrealPakResponseFile[file] ="../../../" + MountPoint; } RunUnrealPak(UnrealPakResponseFile, PakPath + "\\"+ Name + ".pak", null, "", true); } }