/// <inheritdoc /> public VirtualFileSystemEntry Build(VirtualDirectory root, string hostOutputPath = null, string gameName = null, bool useCompression = false, bool useExtracted = false) { if (root == null) { throw new ArgumentNullException(nameof(root)); } Log.Builder.Info($"Building {Game} Mod"); if (hostOutputPath != null) { Log.Builder.Info($"Output directory: {hostOutputPath}"); } // Get game config var config = ConfigStore.Get(Game) as Persona34GameConfig ?? throw new InvalidOperationException("Game config is missing."); if (string.IsNullOrWhiteSpace(config.DvdRootOrIsoPath)) { throw new InvalidConfigException("Dvd root path/ISO path is not specified."); } // Get files Log.Builder.Trace($"DvdRootOrIsoPath = {config.DvdRootOrIsoPath}"); var dvdRootDirectory = Persona34Common.GetRootDirectory(config, out var isoFileSystem); // Find system config var systemConfigFile = dvdRootDirectory["SYSTEM.CNF"] as VirtualFile ?? throw new MissingFileException("SYSTEM.CNF is missing from the file source."); string executablePath; using (var systemConfigStream = systemConfigFile.Open()) { bool leaveOpen = isoFileSystem == null; executablePath = Ps2SystemConfig.GetExecutablePath(systemConfigStream, leaveOpen, true); } if (executablePath == null) { throw new MissingFileException("Executable file path is not specified in SYSTEM.CNF; Unable to locate executable file."); } Log.Builder.Info($"Executable path: {executablePath}"); var executableFile = ( VirtualFile )dvdRootDirectory[executablePath] ?? throw new MissingFileException("The executable file is missing from the dvd root file source."); // Some basic checks have been done, let's start generating the cvms var dvdRootDirectoryPath = hostOutputPath ?? Path.Combine(Path.GetTempPath(), "Persona34ModCompilerTemp_" + Path.GetRandomFileName()); Log.Builder.Trace($"Creating (temp?) output directory: {dvdRootDirectoryPath}"); Directory.CreateDirectory(dvdRootDirectoryPath); var bgmCvmFile = ( VirtualFile )dvdRootDirectory["BGM.CVM"]; var bgmCvmModified = false; var btlCvmFile = ( VirtualFile )dvdRootDirectory["BTL.CVM"]; var btlCvmModified = false; var dataCvmFile = ( VirtualFile )dvdRootDirectory["DATA.CVM"]; var dataCvmModified = false; var envCvmFile = ( VirtualFile )dvdRootDirectory["ENV.CVM"]; var envCvmModified = false; var newDvdRootDirectory = new VirtualDirectory(); // Process mod files Log.Builder.Info("Processing mod files"); foreach (var entry in root) { if (entry.EntryType == VirtualFileSystemEntryType.File) { Log.Builder.Info($"Adding file {entry.Name} to root directory"); entry.MoveTo(newDvdRootDirectory, true); continue; } var name = entry.Name.ToLowerInvariant(); var directory = ( VirtualDirectory )entry; switch (name) { case "bgm": UpdateAndRecompileCvm(ref bgmCvmFile, directory, Path.Combine(dvdRootDirectoryPath, "bgm.cvm"), newDvdRootDirectory); bgmCvmModified = true; break; case "btl": UpdateAndRecompileCvm(ref btlCvmFile, directory, Path.Combine(dvdRootDirectoryPath, "btl.cvm"), newDvdRootDirectory); btlCvmModified = true; break; case "data": UpdateAndRecompileCvm(ref dataCvmFile, directory, Path.Combine(dvdRootDirectoryPath, "data.cvm"), newDvdRootDirectory); dataCvmModified = true; break; case "env": { Log.Builder.Info("Replacing files in env.cvm"); if (envCvmFile == null) { throw new MissingFileException("Mod replaces files in env.cvm but env.cvm isn't present."); } UpdateAndRecompileCvm(ref envCvmFile, directory, Path.Combine(dvdRootDirectoryPath, "env.cvm"), newDvdRootDirectory); envCvmModified = true; } break; default: Log.Builder.Info($"Adding directory {entry.Name} to root directory"); entry.MoveTo(newDvdRootDirectory, true); break; } } // Patch executable var executableFilePath = executableFile.SaveToHost(dvdRootDirectoryPath); Log.Builder.Info($"Patching executable"); Log.Builder.Trace($"Executable file path: {executableFilePath}"); if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) { if (bgmCvmModified) { PatchExecutable(executableFilePath, bgmCvmFile.HostPath); } if (btlCvmModified) { PatchExecutable(executableFilePath, btlCvmFile.HostPath); } if (dataCvmModified) { PatchExecutable(executableFilePath, dataCvmFile.HostPath); } if (envCvmModified) { PatchExecutable(executableFilePath, envCvmFile.HostPath); } } else { if (bgmCvmModified) { PatchExecutableLinux(executableFilePath, bgmCvmFile.HostPath); } if (btlCvmModified) { PatchExecutableLinux(executableFilePath, btlCvmFile.HostPath); } if (dataCvmModified) { PatchExecutableLinux(executableFilePath, dataCvmFile.HostPath); } if (envCvmModified) { PatchExecutableLinux(executableFilePath, envCvmFile.HostPath); } } executableFile = VirtualFile.FromHostFile(executableFilePath); executableFile.MoveTo(newDvdRootDirectory, true); if (hostOutputPath != null) { Log.Builder.Info($"Copying files to output directory (might take a while): {hostOutputPath}"); newDvdRootDirectory.SaveToHost(hostOutputPath); } if (hostOutputPath != null && isoFileSystem != null) { isoFileSystem.Dispose(); } Log.Builder.Info("Done"); return(newDvdRootDirectory); }
/// <summary> /// Build a PS2 bootable iso from the files in the root directory. /// If output path is specified, it is expected to be a path to the output ISO. /// Otherwise, if present, the root's name will be used as the base name for the ISO output. /// Requires SYSTEM.CNF and an executable file to be present in the root's file structure. /// </summary> /// <param name="root"></param> /// <param name="hostOutputPath"></param> /// <returns>PS2 bootable ISO file.</returns> public VirtualFileSystemEntry Build(VirtualDirectory root, string hostOutputPath = null, string gameName = null, bool useCompression = false, bool useExtracted = false) { if (root == null) { throw new ArgumentNullException(nameof(root)); } // Hack: get rid of root's name and restore it later // This is so we can use FullName with the builder without having to strip the root's name var rootName = root.Name; root.Name = string.Empty; // Find system.cnf first, as we need it to get the executable file path var systemCnfFile = root["SYSTEM.CNF"] as VirtualFile ?? throw new MissingFileException("SYSTEM.CNF is missing."); var executablePath = Ps2SystemConfig.GetExecutablePath(systemCnfFile.Open(), false, true) ?? throw new MissingFileException( "Executable file path is not specified in SYSTEM.CNF; Unable to locate executable file."); var executableFile = root[executablePath] as VirtualFile ?? throw new MissingFileException($"Executable file {executablePath} is missing."); var isoBuilder = new CDBuilder { UseJoliet = false, UpdateIsolinuxBootTable = false, VolumeIdentifier = "AMICITIA" }; // system.cnf first isoBuilder.AddFile(systemCnfFile.Name, systemCnfFile); // executable second isoBuilder.AddFile(executablePath, executableFile); // And then the rest AddToIsoBuilderRecursively(isoBuilder, root, executablePath); // HACK: Restore root name root.Name = rootName; if (hostOutputPath != null) { isoBuilder.Build(hostOutputPath); return(VirtualFile.FromHostFile(hostOutputPath)); } else { string isoName; if (!string.IsNullOrWhiteSpace(root.Name)) { isoName = root.Name + ".iso"; } else { isoName = "PS2DVD.iso"; } var stream = isoBuilder.Build(); return(new VirtualFile(null, stream, isoName)); } }