/// <summary> /// Parses a Sonic Heroes ONE file from a byte array and returns a ONE file structure back. /// </summary> /// <returns>The structure of a ONE Archive.</returns> // ReSharper disable once InconsistentNaming public static ONEArchive ParseONEFile(ref byte[] file) { ONEArchive oneArchive = new ONEArchive(); // Stores a pointer, increasing as we read the individual file structures. int pointer = 0; // Parse the header and individual sections. oneArchive.FileHeader = StructUtilities.ArrayToStructureUnsafe <ONEHeader>(ref file, pointer, ref pointer); oneArchive.FileNameSectionHeader = StructUtilities.ArrayToStructureUnsafe <ONEFileNameSectionHeader>(ref file, pointer, ref pointer); // Parse all of the filenames. int fileNameCount = oneArchive.FileNameSectionHeader.GetNameCount(); oneArchive.FileNames = new ONEFileName[fileNameCount]; for (int x = 0; x < fileNameCount; x++) { oneArchive.FileNames[x] = StructUtilities.ArrayToStructureUnsafe <ONEFileName>(ref file, pointer, ref pointer); } // Parse all of the files. oneArchive.Files = new List <ONEFile>(fileNameCount); int fileCount = oneArchive.FileNames.Count(x => x.ToString() != ""); // Some ONE files have been padded at the end of file, thus reading until the end of file may fail - only take as many files as we have filenames. while ((pointer < file.Length) && (oneArchive.Files.Count < fileCount)) { // Create ONE ArchiveFile ONEFile oneFile = new ONEFile(); // Parse the header. oneFile.ONEFileHeader = StructUtilities.ArrayToStructureUnsafe <ONEFileHeader>(ref file, pointer, ref pointer); // Now parse the data. oneFile.CompressedData = new byte[oneFile.ONEFileHeader.FileSize]; Array.Copy(file, pointer, oneFile.CompressedData, 0, oneFile.CompressedData.Length); pointer += oneFile.CompressedData.Length; oneArchive.Files.Add(oneFile); } return(oneArchive); }
/// <summary> /// Generates a Heroes ONE archive from the provided HeroesONE Archive structure. /// </summary> public List <byte> BuildHeroesONEArchive() { // Note: The file name section has 2 blank entries at file name index 0 and 1, we need to skip those. // Populate some data before we write it. ONEHeader fileHeader = new ONEHeader(); ONEFileNameSectionHeader fileNameSectionHeader = new ONEFileNameSectionHeader(); // Header fileHeader.RenderWareVersion = RwVersion; fileHeader.FileSize = 0; fileHeader.Null = 0; // Name Header fileNameSectionHeader.RenderWareVersion = RwVersion; fileNameSectionHeader.Unknown = 1; fileNameSectionHeader.FileNameSectionLength = ((Files.Count + 2) * ONEFileName.FileNameLength); // Populate ArchiveFile names List <ONEFileName> fileNames = new List <ONEFileName>(ONEArchive.MAX_FILE_COUNT); // Insert blank entries fileNames.Add(new ONEFileName("")); fileNames.Add(new ONEFileName("")); for (int x = 0; x < Files.Count; x++) { fileNames.Add(new ONEFileName(Files[x].Name)); } // Populate ArchiveFile Structure List <ONEFile> oneFiles = new List <ONEFile>(Files.Count); for (int x = 0; x < Files.Count; x++) { ONEFile oneFile = new ONEFile(); oneFile.CompressedData = Files[x].CompressedData; oneFile.ONEFileHeader.FileNameIndex = x + 2; // ArchiveFile names always start after two blank entries, for some reason... oneFile.ONEFileHeader.FileSize = oneFile.CompressedData.Length; oneFile.ONEFileHeader.RwVersion = Files[x].RwVersion; oneFiles.Add(oneFile); } // Calculate file size. fileHeader.FileSize += Marshal.SizeOf(fileNameSectionHeader); fileHeader.FileSize += (Marshal.SizeOf <ONEFileName>() * fileNames.Count); for (int x = 0; x < oneFiles.Count; x++) { fileHeader.FileSize += Marshal.SizeOf(oneFiles[x].ONEFileHeader); fileHeader.FileSize += oneFiles[x].CompressedData.Length; } // Generate ONE file. List <byte> file = new List <byte>(fileHeader.FileSize + Marshal.SizeOf <ONEFileHeader>()); file.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileHeader)); file.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileNameSectionHeader)); for (int x = 0; x < fileNames.Count; x++) { ONEFileName fileName = fileNames[x]; file.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileName)); } for (int x = 0; x < oneFiles.Count; x++) { ONEFileHeader localFileHeader = oneFiles[x].ONEFileHeader; file.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref localFileHeader)); file.AddRange(oneFiles[x].CompressedData); } return(file); }