JD2022-TU1/main/tools/apps/Packager/AssetCooker/AssetCooker.cs

475 lines
No EOL
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using JD.Core.ProjectManaging;
using Ubisoft.Collections;
namespace Packager.AssetCooker
{
/// <summary>
/// This is the entry point for different possible tasks that can be done using AssetCooker
/// </summary>
public class AssetCooker : IDisposable
{
private readonly engineWrapper _engine;
private cookerWrapper _cooker;
private List<string> _outputList;
private readonly List<string> _inputFilesToProcess;
private readonly HashSet<string> _excludeFileList;
public Parameters.Platform Platform { get; set; }
public int RebaseEngineVersion { get; set; }
public int DataVersion { get; set; }
public string Inputs { get; set; }
public string MergeList { get; set; }
public bool ExitOnError { get; set; }
public string OutputFile { get; set; }
public string OutputDependenciesFolder { get; set; }
public string ExcludeFiles { get; set; }
public int ForceMipMap { get; set; }
public bool CookLogicToBinary { get; set; }
public string BundleOutput { get; set; }
public string CommonFat { get; set; }
public bool GetDependencies { get; set; }
public bool Cook { get; set; }
public bool OverrideScene { get; set; }
public string OriginalScene { get; set; }
public string ReplacementScene { get; set; }
public bool GenerateCookedList { get; set; }
public bool BuildAtl { get; set; }
public bool PackSgs { get; set; }
public string CacheKey { get; set; }
public bool Unique { get; set; }
public List<string> CookedFileList { get; set; }
public MessageLogger Logger { get; }
public AssetCooker()
{
Logger = new MessageLogger(nameof(AssetCooker));
_excludeFileList = new HashSet<string>();
_engine = new engineWrapper("");
InitializeCooker();
ForceMipMap = -1;
_outputList = new List<string>();
_inputFilesToProcess = new List<string>();
}
private void InitializeCooker()
{
_cooker = new cookerWrapper();
_cooker.init(DataPath.Value);
_cooker.enableCacheFiles();
Logger.AttachCookerWrapper(_cooker);
}
public void Run()
{
Initialize();
Execute();
}
public void Dispose()
{
Logger.DetachCookerWrapper(_cooker);
_cooker.destroy();
_engine.destroy();
}
private void Initialize()
{
ExtractMergeListContent();
ExtractInputFiles();
CreateOutputDirectory();
CreateExcludeFileLsit();
SetCookerParameters();
}
private void SetCookerParameters()
{
_cooker.setCacheKey(CacheKey);
_cooker.rebaseEngineVersion(RebaseEngineVersion);
}
private void CreateExcludeFileLsit()
{
if (ExcludeFiles == null)
return;
foreach (string file in ExcludeFiles.Split(','))
{
var fileContent = File.ReadAllLines(file.Trim());
_excludeFileList.AddRange(fileContent);
}
}
private void CreateOutputDirectory()
{
var directoryName = Path.GetDirectoryName(OutputFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
}
private void ExtractInputFiles()
{
if (Inputs == null)
return;
foreach (var input in Inputs.Split(','))
{
_inputFilesToProcess.Add(input.Trim());
}
}
private void ExtractMergeListContent()
{
if (MergeList != null)
{
_inputFilesToProcess.Clear();
_inputFilesToProcess.AddRange(File.ReadAllLines(MergeList).Distinct());
_inputFilesToProcess.Sort();
}
}
private void CookIt()
{
var cookingMaster = new Master(_cooker);
Logger.LogInfo($"{nameof(Master)} was created, its log file can be found at \"{cookingMaster.Logger.LogFilePath}\".");
cookingMaster.CookFiles(_outputList.ToArray(), Platform.ToString(), CookLogicToBinary, ForceMipMap, RebaseEngineVersion);
while (!cookingMaster.FinishedCookingFiles())
{
Thread.Sleep(100);
}
}
private void BundleIt()
{
var bundleName = GetBundleName();
_outputList = _outputList.Where(file => !file.EndsWith(".atl.ckd") && !file.EndsWith(".als") && !file.EndsWith(".sfi") && !file.EndsWith(".bik")).ToList();
_cooker.bundle(bundleName, Platform.ToString(), CookLogicToBinary, _outputList, DataVersion);
}
private void Execute()
{
CreateOutputFileList(_inputFilesToProcess);
if (GetDependencies)
{
ExtractDependencies();
}
if (Cook)
{
CookIt();
}
if (OverrideScene)
{
ReplaceOriginalScene();
}
if (BuildAtl)
{
BuildAtlasContainer();
}
if (PackSgs)
{
BuildSgsContainer();
}
if (_excludeFileList != null)
{
// exclude files from a list
var validOutputFiles = _outputList.Except(_excludeFileList);
_outputList = validOutputFiles.ToList();
}
if (GenerateCookedList)
{
GenerateCookedFileNames();
}
if (CommonFat != null)
{
_cooker.buildGlobalFat(CommonFat, Platform.ToString(), _outputList);
}
if (BundleOutput != null)
{
BundleIt();
}
if (Unique)
{
RemoveDuplicateFilesFromOutput();
}
if (OutputFile != null)
{
File.WriteAllLines(OutputFile, _outputList);
}
}
private string GetBundleName()
{
return BundleOutput.EndsWith(".ipk") ? BundleOutput.ToLower() : _cooker.getBundleName(BundleOutput.ToLower(), Platform.ToString());
}
private void RemoveDuplicateFilesFromOutput()
{
_outputList = _outputList.Distinct().ToList();
}
private void GenerateCookedFileNames()
{
_outputList = _outputList.Select(ToCookFileName).ToList();
CookedFileList = _outputList;
}
private string ToCookFileName(string originalName)
{
if (_cooker.isCookable(originalName))
{
var cookedname = "";
_cooker.getCookedNamePlatform(originalName, Platform.ToString(), ref cookedname);
return ChangeFileNameToCookedVersion(originalName, cookedname);
}
switch (originalName)
{
case "atlascontainer":
string uvAtlasContainer = null;
_cooker.getCookedNamePlatform("atlascontainer", Platform.ToString(), ref uvAtlasContainer);
return uvAtlasContainer;
case "sgscontainer":
string sgsContainer = null;
_cooker.getCookedNamePlatform("sgscontainer", Platform.ToString(), ref sgsContainer);
return sgsContainer;
}
return originalName;
}
private string ChangeFileNameToCookedVersion(string originalName, string cookedname)
{
// for png and jpg files,
// if the cooked file does not exist, consider that it was set as not to be cooked
var ext = Path.GetExtension(originalName);
if (ext == ".png" || ext == ".jpg")
{
var file = _cooker.getDataRoot() + cookedname;
file = file.Replace("/", @"\");
if (File.Exists(file))
{
return cookedname;
}
}
else
{
return cookedname;
}
return originalName;
}
private void BuildSgsContainer()
{
var sgsList = _outputList.Where(path => Path.GetExtension(path) == ".sgs").ToList();
string sgsContainer = null;
_cooker.getCookedNamePlatform("sgscontainer", Platform.ToString(), ref sgsContainer);
_cooker.buildSgsContainer(sgsContainer, Platform.ToString(), CookLogicToBinary, sgsList);
_outputList = _outputList.Except(sgsList).ToList();
_outputList.Add(sgsContainer);
}
private void BuildAtlasContainer()
{
// build atl container
var atlBuildList = new List<string>();
foreach (var file in _outputList)
{
var ext = Path.GetExtension(file);
if (!string.IsNullOrEmpty(ext))
ext = ext.Substring(1);
switch (ext)
{
case "tga":
case "png":
atlBuildList.Add(file);
break;
case "atl":
{
string tgaFile = Path.ChangeExtension(file, "tga");
string pngFile = Path.ChangeExtension(file, "png");
if (!_outputList.Contains(tgaFile) && !_outputList.Contains(pngFile))
{
atlBuildList.Add(file);
}
}
break;
}
}
string uvAtlasContainer = null;
_cooker.getCookedNamePlatform("atlascontainer", Platform.ToString(), ref uvAtlasContainer);
_cooker.buildAtlasContainer(uvAtlasContainer, Platform.ToString(), atlBuildList);
_outputList.Add(uvAtlasContainer);
}
private void ReplaceOriginalScene()
{
// derive scene config paths
var originalSgs = Path.ChangeExtension(OriginalScene, "sgs");
var replacementSgs = Path.ChangeExtension(ReplacementScene, "sgs");
// transform to cooked paths
string originalPath = null;
string replacementPath = null;
string originalSgsPath = null;
string replacementSgsPath = null;
_cooker.getCookedNamePlatform(OriginalScene, Platform.ToString(), ref originalPath);
_cooker.getCookedNamePlatform(ReplacementScene, Platform.ToString(), ref replacementPath);
_cooker.getCookedNamePlatform(originalSgs, Platform.ToString(), ref originalSgsPath);
_cooker.getCookedNamePlatform(replacementSgs, Platform.ToString(), ref replacementSgsPath);
// make paths absolute
originalPath = Path.Combine(_cooker.getDataRoot(), originalPath);
replacementPath = Path.Combine(_cooker.getDataRoot(), replacementPath);
originalSgsPath = Path.Combine(_cooker.getDataRoot(), originalSgsPath);
replacementSgsPath = Path.Combine(_cooker.getDataRoot(), replacementSgsPath);
if (string.Compare(originalPath, replacementPath, StringComparison.OrdinalIgnoreCase) != 0)
{
// override cooked files
File.Copy(replacementPath, originalPath, true);
File.Copy(replacementSgsPath, originalSgsPath, true);
// make sure the overridden files will get replaced by next cooking session
File.SetCreationTime(originalPath, new DateTime(1970, 1, 1));
File.SetLastAccessTime(originalPath, new DateTime(1970, 1, 1));
File.SetLastWriteTime(originalPath, new DateTime(1970, 1, 1));
File.SetCreationTime(originalSgsPath, new DateTime(1970, 1, 1));
File.SetLastAccessTime(originalSgsPath, new DateTime(1970, 1, 1));
File.SetLastWriteTime(originalSgsPath, new DateTime(1970, 1, 1));
}
}
[HandleProcessCorruptedStateExceptions]
private void ExtractDependencies()
{
// retrieve dependencies
var globalDependencies = new List<string>();
foreach (var file in _outputList)
{
var localDependencies = new List<string>();
try
{
GetFileDependencies(file, ref localDependencies);
}
catch (Exception exception)
{
Logger.LogException(exception);
}
globalDependencies.AddRange(localDependencies);
//Create an OutputFile for each InputFile
if (!string.IsNullOrEmpty(OutputDependenciesFolder))
{
var output = Path.Combine(OutputDependenciesFolder, Path.GetFileNameWithoutExtension(file) + ".txt");
try
{
File.WriteAllLines(output, localDependencies);
}
catch (Exception exception)
{
Logger.LogException(exception);
}
}
}
var uniqueDependencies = new HashSet<string>();
foreach (string file in globalDependencies)
uniqueDependencies.Add(file);
CreateOutputFileList(uniqueDependencies);
}
private void CreateOutputFileList(IEnumerable<string> fileList)
{
_outputList.Clear();
foreach (var filepath in fileList)
_outputList.Add(filepath.Trim());
}
private void GetFileDependencies(string rootFilename, ref List<string> files)
{
Stopwatch watch = Stopwatch.StartNew();
if (rootFilename == "common.alias")
{
_cooker.getGameDependencies(Platform.ToString(), files);
}
else
{
string depFile = rootFilename.Replace('\\', '/').ToLower();
files.Add(depFile);
_cooker.getDependencies(Platform.ToString(), depFile, files);
}
watch.Stop();
TimeSpan ts = watch.Elapsed;
Logger.LogInfo($"Time to get dependencies: {ts.TotalMilliseconds / 1000.0:f3} s ({files.Count} files)");
}
}
}