/// <summary> /// Update the packedtime value of xanadu archive and xanadu file entry. /// </summary> /// <param name="file">Xanadu file entry</param> /// <param name="fileDateTime">Date of new compressed file</param> /// <returns>The xanadu file entry updated</returns> private static XanaduStructs.XanaduFileEntry UpdatePackedTime(XanaduStructs.XanaduFileEntry file, DateTime fileDateTime) { long elapsedTicks = fileDateTime.Ticks - originDate.Ticks; TimeSpan elapsedSpan = new TimeSpan(elapsedTicks); Utilities.Replace(xanaduArchive, (int)file.offset + diffLength, BitConverter.GetBytes((UInt32)elapsedSpan.TotalSeconds)); file.filePackedTime = (UInt32)elapsedSpan.TotalSeconds; return(file); }
/// <summary> /// Populates the details of each file entry within the archive. /// </summary> private static void PopulateFileEntries() { // Allocate Memory fileList = new List <XanaduStructs.XanaduFileEntry>((int)fileHeader.fileCount); // Convert xanaduArchive list to array byte[] xanaduArchiveArray = xanaduArchive.ToArray(); // Create file pointer & set to first entry. UInt32 filePointer = fileHeader.fileEntryOffset; // Read each file entry for (int x = 0; x < fileHeader.fileCount; x++) { // Generate file entry. XanaduStructs.XanaduFileEntry xanaduFileEntry = new XanaduStructs.XanaduFileEntry(); // Save start pointer of entry details xanaduFileEntry.offset = filePointer; // Read file entry details & increment pointer. xanaduFileEntry.filePackedTime = BitConverter.ToUInt32(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt32); xanaduFileEntry.unknown = BitConverter.ToUInt32(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt32); xanaduFileEntry.compressedSize = BitConverter.ToUInt32(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt32); xanaduFileEntry.uncompressedSize = BitConverter.ToUInt32(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt32); xanaduFileEntry.fileNameLength = BitConverter.ToUInt16(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt16); xanaduFileEntry.fileFlags = BitConverter.ToUInt16(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt16); xanaduFileEntry.fileOffset = BitConverter.ToUInt32(xanaduArchiveArray, (int)filePointer); filePointer += sizeof(UInt32); xanaduFileEntry.fileName = Encoding.ASCII.GetString(xanaduArchiveArray.SubArray((int)filePointer, (int)xanaduFileEntry.fileNameLength)); filePointer += (uint)xanaduFileEntry.fileName.Length; // Sanitize File name xanaduFileEntry.fileName = xanaduFileEntry.fileName.ForceValidFilePath(); // Trim file extension xanaduFileEntry.fileName = xanaduFileEntry.fileName.Substring(0, xanaduFileEntry.fileName.IndexOf(".") + 4); // Add onto list fileList.Add(xanaduFileEntry); } }
/// <summary> /// Compress files in .BRA Archive /// </summary> private static void CompressFiles() { Console.WriteLine("Work in progress..."); int countCompressedFiles = 0; int countIgnoredFiles = 0; for (int i = 0; i < fileList.Count; i++) { // Check if the file exists String fileNamePath = Path.Combine(folderPath, fileList[i].fileName); if (!File.Exists(fileNamePath)) { continue; } // Check if the file have been modified DateTime fileDateTime = File.GetLastWriteTime(fileNamePath); DateTime archiveTime = originDate.AddSeconds(fileList[i].filePackedTime); if (!compressAllFiles && (UInt32) new TimeSpan(fileDateTime.Ticks).TotalSeconds == (UInt32) new TimeSpan(archiveTime.Ticks).TotalSeconds) { countIgnoredFiles++; continue; } // Shows filename path Console.WriteLine(fileNamePath); // Compress a file using FileStream fileUncompressed = File.OpenRead(fileNamePath); MemoryStream compressStream = new MemoryStream(); using DeflateStream compressor = new DeflateStream(compressStream, CompressionMode.Compress, false); fileUncompressed.CopyTo(compressor); compressor.Close(); byte[] fileCompressed = compressStream.ToArray(); if (fileCompressed.Length > (fileList[i].compressedSize - 0x10)) { xanaduArchive.RemoveRange((int)fileList[i].fileOffset + 0x10, (int)fileList[i].compressedSize - 0x10); xanaduArchive.InsertRange((int)fileList[i].fileOffset + 0x10, fileCompressed); // update fileoffset of fileEntry for (int x = i + 1; x < fileList.Count; x++) { XanaduStructs.XanaduFileEntry fileEntry = fileList[x]; fileEntry.fileOffset += (UInt32)(fileCompressed.Length - fileList[i].compressedSize + 0x10); fileList[x] = fileEntry; } // update zsize and size diffLength += (int)(fileCompressed.Length - fileList[i].compressedSize + 0x10); fileList[i] = Update(fileUncompressed, fileCompressed, fileList[i]); } else if (fileCompressed.Length == (fileList[i].compressedSize - 0x10)) { // write file compressed in xanadu archive Utilities.Replace(xanaduArchive, (int)fileList[i].fileOffset + 0x10, fileCompressed); fileList[i] = Update(fileUncompressed, fileCompressed, fileList[i]); } else { // write file compressed in xanadu archive Utilities.Replace(xanaduArchive, (int)fileList[i].fileOffset + 0x10, fileCompressed); for (int x = 0; x < (fileList[i].compressedSize - fileCompressed.Length - 0x10); x++) { xanaduArchive[(int)fileList[i].fileOffset + 0x10 + fileCompressed.Length + x] = 0; } fileList[i] = Update(fileUncompressed, fileCompressed, fileList[i]); } // update packedTime fileList[i] = UpdatePackedTime(fileList[i], fileDateTime); countCompressedFiles++; } // update offset archive xanadu if (diffLength > 0) { for (int i = 0; i < fileList.Count; i++) { Utilities.Replace(xanaduArchive, (int)fileList[i].offset + diffLength + 0x14, BitConverter.GetBytes((UInt32)fileList[i].fileOffset)); } Utilities.Replace(xanaduArchive, 0x08, BitConverter.GetBytes((UInt32)(fileHeader.fileEntryOffset + diffLength))); } // Show number of files compressed added and ignored Console.Write("Files compressed added: " + countCompressedFiles + " | " + "Files ignored: " + countIgnoredFiles + " | "); // ignore write and backup if no file has been added if (countCompressedFiles == 0) { return; } // create backup to xanadu archive String fileBakPath = filePath + ".bak"; if (File.Exists(fileBakPath)) { File.Delete(fileBakPath); } File.Copy(filePath, fileBakPath); // write in xanadu archive File.Create(filePath).Write(xanaduArchive.ToArray()); }
/// <summary> /// Update the compressed and uncompressed values of xanadu archive and xanadu file entry. /// </summary> /// <param name="fileUncompressed">File no compressed</param> /// <param name="fileCompressed">File compressed</param> /// <param name="file">Xanadu file entry</param> /// <returns>The xanadu file entry updated</returns> private static XanaduStructs.XanaduFileEntry Update(FileStream fileUncompressed, byte[] fileCompressed, XanaduStructs.XanaduFileEntry file) { // update zsize and size of xanaduArchive (fileData) Utilities.Replace(xanaduArchive, (int)file.fileOffset, BitConverter.GetBytes((UInt32)fileUncompressed.Length)); // size Utilities.Replace(xanaduArchive, (int)file.fileOffset + sizeof(UInt32), BitConverter.GetBytes((UInt32)fileCompressed.Length)); // zsize // update zsize and size of xanaduArchive (fileEntry) Utilities.Replace(xanaduArchive, (int)file.offset + diffLength + sizeof(UInt32) * 2, BitConverter.GetBytes((UInt32)fileCompressed.Length + 0x10)); // zsize Utilities.Replace(xanaduArchive, (int)file.offset + diffLength + sizeof(UInt32) * 3, BitConverter.GetBytes((UInt32)fileUncompressed.Length)); // size // update zsize and size of fileEntry file.compressedSize = (UInt32)fileCompressed.Length + 0x10; file.uncompressedSize = (UInt32)fileUncompressed.Length; return(file); }