Example #1
0
        /// <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);
        }
Example #2
0
        /// <summary>
        /// Generates a Shadow ONE Archive from the current Archive instance.
        /// </summary>
        /// <param name="isShadow60Archive">Set to false to save in Shadow ONE Ver 0.50 archive format; true for ONE Ver 0.60 archive format.</param>
        /// <returns></returns>
        public unsafe List <byte> BuildShadowONEArchive(bool isShadow60Archive)
        {
            // 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();
            ONEFileVersion    fileVersion;
            int               fileCount;
            ONEPadding        padding;
            List <IFileEntry> localFiles = new List <IFileEntry>();
            List <byte>       fileData   = new List <byte>(5000 * 1000); // 5MB default buffer size.

            // Header
            fileHeader.RenderWareVersion = RwVersion;
            fileHeader.FileSize          = 0;

            // Set One Version String
            fileVersion = isShadow60Archive ? new ONEFileVersion("One Ver 0.60") : new ONEFileVersion("One Ver 0.50");

            // The amount of files present within the archive.
            fileCount = this.Files.Count;

            // Generate the generic padding used.
            for (int x = 1; x < ONEPadding.PADDING_LENGTH; x++)
            {
                padding.Padding[x] = 0xCD;
            }

            // Insert file definitions.
            // Dummies first!
            ONE50FileEntry dummmyEntry = new ONE50FileEntry();

            // Actual files.
            for (int x = 0; x < fileCount; x++)
            {
                // Set file entry;
                IFileEntry fileEntry;
                if (isShadow60Archive)
                {
                    fileEntry = new ONE60FileEntry(Files[x].Name);
                }
                else
                {
                    fileEntry = new ONE50FileEntry(Files[x].Name);
                }

                // Set file size.
                fileEntry.FileSize = Prs.Decompress(ref Files[x].CompressedData).Length;
                localFiles.Add(fileEntry);
            }

            // Calculate offsets and total file size.
            int filePointer = 0;

            // 0xC header ignored purposefully, it is only metadata after actual file is created.
            filePointer += Unsafe.SizeOf <ONEFileVersion>();
            filePointer += Unsafe.SizeOf <int>();
            filePointer += Unsafe.SizeOf <ONEPadding>();

            // Offset for dummy files
            filePointer += Unsafe.SizeOf <ONE50FileEntry>() * (2);
            filePointer += Unsafe.SizeOf <ONE50FileEntry>() * (fileCount);

            // Note: Both file entry types have equal length.
            for (int x = 0; x < fileCount; x++)
            {
                localFiles[x].FileOffset = filePointer; // Offset for dummy files.
                filePointer += Files[x].CompressedData.Length;
            }

            fileHeader.FileSize = filePointer;

            // Start writing through file data.
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileHeader));
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileVersion));
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref fileCount));
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref padding));

            // Add dummies
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref dummmyEntry));
            fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe(ref dummmyEntry));

            // File entries
            for (int x = 0; x < localFiles.Count; x++)
            {
                if (isShadow60Archive)
                {
                    fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe((ONE60FileEntry)localFiles[x]));
                }
                else
                {
                    fileData.AddRange(StructUtilities.ConvertStructureToByteArrayUnsafe((ONE50FileEntry)localFiles[x]));
                }
            }

            // File data
            for (int x = 0; x < Files.Count; x++)
            {
                fileData.AddRange(Files[x].CompressedData);
            }

            return(fileData);
        }