public VirtualFileSystemEntry Build(VirtualDirectory root, string hostOutputPath = null, string gameName = null, bool useCompression = false, bool useExtracted = false) { if (hostOutputPath == null) { // We need a path, so generate one. hostOutputPath = Path.Combine(Path.GetTempPath(), "Persona34IsoModBuilderTemp_" + Path.GetRandomFileName() + ".iso"); } else if (Directory.Exists(hostOutputPath)) { // Add file name if the path is a directory hostOutputPath = Path.Combine(hostOutputPath, "Amicitia.iso"); } // Build mod files var fileModBuilder = CreateFileModBuilder(); var tempDirectory = Path.Combine(Path.GetTempPath(), "Persona34IsoModBuilderTemp_" + Path.GetRandomFileName()); Directory.CreateDirectory(tempDirectory); var modFilesDirectory = (VirtualDirectory)fileModBuilder.Build(root, tempDirectory); var config = GetConfig(); if (!config.DvdRootOrIsoPath.EndsWith(".iso")) { throw new NotImplementedException("This can only be done with an ISO source right now!"); } // Modify & save new ISO Log.Builder.Info($"Modifying & saving ISO to {hostOutputPath} (this will take a while)"); if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) { Log.Builder.Info("Process will take longer as WINE must translate paths, please be patient."); } UltraISOUtility.ModifyIso(config.DvdRootOrIsoPath, hostOutputPath, modFilesDirectory.Select(x => x.HostPath)); // Delete temp directory Directory.Delete(tempDirectory, true); // We're done return(VirtualFile.FromHostFile(hostOutputPath)); }
/// <inheritdoc /> public VirtualFileSystemEntry Build(VirtualDirectory root, string hostOutputPath = null, string gameName = null, bool useCompression = false, bool useExtracted = false) { gameName = Game.ToString(); if (root == null) { throw new ArgumentNullException(nameof(root)); } //Get game config var config = ConfigStore.Get(Game) as ModCpkGameConfig ?? throw new InvalidOperationException("Game config is missing."); Log.Builder.Info($"Building {gameName} Mod"); Log.Builder.Info("Processing mod files"); var modFilesDirectory = new VirtualDirectory(null, "mod"); foreach (var entry in root) { if (entry.EntryType == VirtualFileSystemEntryType.Directory) { var directory = (VirtualDirectory)entry; var name = directory.Name.ToLowerInvariant(); switch (name) { case "mod": case "data": { // Move files in 'cpk' directory to 'mod' directory LogModFilesInDirectory(directory); foreach (var modFileEntry in directory) { modFileEntry.CopyTo(modFilesDirectory); } } break; default: // Move directory to 'mod' directory Log.Builder.Trace($"Adding directory {entry.FullName} to mod.cpk"); entry.CopyTo(modFilesDirectory); break; } } else { // Move file to 'mod' directory Log.Builder.Trace($"Adding file {entry.FullName} to mod.cpk"); entry.CopyTo(modFilesDirectory); } } bool.TryParse(config.Compression, out useCompression); // Build mod cpk Log.Builder.Info("Building mod.cpk"); var cpkModCompiler = new CpkModBuilder(); if (hostOutputPath != null) { Directory.CreateDirectory(hostOutputPath); } var cpkFilePath = hostOutputPath != null?Path.Combine(hostOutputPath, "mod.cpk") : null; var cpkNotWritable = File.Exists(cpkFilePath) && FileHelper.IsFileInUse(cpkFilePath); var cpkFileBuildPath = hostOutputPath != null?cpkNotWritable?Path.Combine(Path.GetTempPath(), "mod.cpk") : cpkFilePath : null; var cpkFile = cpkModCompiler.Build(modFilesDirectory, cpkFileBuildPath, gameName, useCompression); if (cpkFileBuildPath != cpkFilePath) { File.Copy(cpkFileBuildPath, cpkFilePath, true); File.Delete(cpkFileBuildPath); cpkFile = VirtualFile.FromHostFile(cpkFilePath); } Log.Builder.Info("Done!"); return(cpkFile); }
/// <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); }
/// <inheritdoc /> public VirtualFileSystemEntry Build(VirtualDirectory root, List <Mod> enabledMods, string hostOutputPath = null, string gameName = null, bool useCompression = false, bool useExtracted = false) { gameName = Game.ToString(); if (root == null) { throw new ArgumentNullException(nameof(root)); } //Get game config var config = ConfigStore.Get(Game) as ModCpkGameConfig ?? throw new InvalidOperationException("Game config is missing."); Log.Builder.Info($"Building {gameName} Mod"); Log.Builder.Info("Processing mod files"); bool pc = Convert.ToBoolean(config.PC); var modFilesDirectory = new VirtualDirectory(); if (!pc) { modFilesDirectory = new VirtualDirectory(null, "mod"); } else { modFilesDirectory = new VirtualDirectory(null, ""); } foreach (var entry in root) { if (entry.EntryType == VirtualFileSystemEntryType.Directory) { var directory = (VirtualDirectory)entry; var name = directory.Name.ToLowerInvariant(); switch (name) { case "mod": case "data": { // Move files in 'cpk' directory to 'mod' directory LogModFilesInDirectory(directory); foreach (var modFileEntry in directory) { modFileEntry.CopyTo(modFilesDirectory); } } break; default: // Move directory to 'mod' directory Log.Builder.Trace($"Adding directory {entry.FullName} to mod.cpk"); entry.CopyTo(modFilesDirectory); break; } } else { // Move file to 'mod' directory Log.Builder.Trace($"Adding file {entry.FullName} to mod.cpk"); entry.CopyTo(modFilesDirectory); } } bool.TryParse(config.Compression, out useCompression); // If PC Mode is enabled, clear and replace contents if (pc) { if (Directory.Exists(hostOutputPath)) { foreach (var directory in Directory.GetDirectories(hostOutputPath)) { Log.Builder.Info($"Replacing Output Path contents"); string[] stringArray = { "data00000", "data00001", "data00002", "data00003", "data00004", "data00005", "data00006", "movie00000", "movie00001", "movie00002", "snd", "data_e" }; if (stringArray.Any(Path.GetFileName(directory).ToLower().Equals)) { Directory.Delete(directory, true); } } } Directory.CreateDirectory(Path.GetFullPath(hostOutputPath)); modFilesDirectory.SaveToHost(hostOutputPath); Log.Builder.Info("Done!"); return(modFilesDirectory); } else { // Build mod cpk Log.Builder.Info($"Building mod.cpk"); var cpkModCompiler = new CpkModBuilder(); if (hostOutputPath != null) { Directory.CreateDirectory(hostOutputPath); } var cpkFilePath = hostOutputPath != null?Path.Combine(hostOutputPath, "mod.cpk") : null; var cpkNotWritable = File.Exists(cpkFilePath) && FileHelper.IsFileInUse(cpkFilePath); var cpkFileBuildPath = hostOutputPath != null?cpkNotWritable?Path.Combine(Path.GetTempPath(), "mod.cpk") : cpkFilePath : null; var cpkFile = cpkModCompiler.Build(modFilesDirectory, enabledMods, cpkFileBuildPath, gameName, useCompression); if (cpkFileBuildPath != cpkFilePath) { File.Copy(cpkFileBuildPath, cpkFilePath, true); File.Delete(cpkFileBuildPath); cpkFile = VirtualFile.FromHostFile(cpkFilePath); } Log.Builder.Info("Done!"); return(cpkFile); } }
/// <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)); } }