Пример #1
0
        /// <summary>
        /// Compresses arm9/overlays with a variant of LZSS encoding specific to binary files. The files have an
        /// uncompressed header section followed by a compressed body, but they are actually decompressed backwards
        /// starting from the end of the file until reaching the header. This approach allows the file to be
        /// decompressed in-place once it is loaded into memory, eventually overwriting the entire compressed section
        /// with the decompressed file as it works backwards to the header.
        /// </summary>
        /// <param name="uncompressedData">The raw binary file data.</param>
        /// <param name="headerLength">The length of the uncompressed header (usually 0x4000).</param>
        /// <returns>The compressed binary file data.</returns>
        private static byte[] CompressBinary(byte[] uncompressedData, int headerLength)
        {
            // Make a copy of the data that needs to be compressed and reverse it so the standard LZ77 compression
            // algorithm can handle it
            byte[] inputData = new byte[uncompressedData.Length - headerLength];
            for (int destOffset = 0, sourceOffset = uncompressedData.Length - 1; sourceOffset >= headerLength; sourceOffset--)
            {
                inputData[destOffset++] = uncompressedData[sourceOffset];
            }

            // Compress the data now that it is in the correct order
            byte[] outputData = Compress(inputData, LzssBufferSize, OnzCompressionType, false, BinaryMinDistance);

            int outputDataLength     = headerLength + outputData.Length;
            int compressedDataLength = RoundUp(outputDataLength) + (2 * PointerLength);

            // Create a new array to hold the entire file
            byte[] compressedData = new byte[compressedDataLength];

            // Copy the uncompressed header directly from the original data
            Array.Copy(uncompressedData, 0, compressedData, 0, headerLength);

            // Append the compressed data, returning it to reverse order
            for (int destOffset = headerLength, sourceOffset = outputData.Length - 1; sourceOffset >= 0; sourceOffset--)
            {
                compressedData[destOffset++] = outputData[sourceOffset];
            }

            // Pad the compressed data to the next 4-byte boundary, so the length information in the footer will be
            // properly aligned
            for (int i = outputDataLength; i < RoundUp(outputDataLength); i++)
            {
                compressedData[i] = Padding;
            }

            // First comes the length of the compressed data (not including the uncompressed header) which takes up 3
            // bytes, with the 4th byte holding the length of the footer (including any padding)
            int footerLength            = compressedDataLength - outputDataLength;
            int compressedLengthPointer = (compressedDataLength - headerLength) | (footerLength << 24);

            StreamHelper.WriteBytes(compressedLengthPointer, compressedData, compressedDataLength - (2 * PointerLength));

            // Next comes the difference between the file sizes of the compressed and uncompressed binaries, which
            // tells the processor how far out to put the start of the decompressed data
            int decompressedLengthPointer = uncompressedData.Length - compressedDataLength;

            StreamHelper.WriteBytes(decompressedLengthPointer, compressedData, compressedDataLength - PointerLength);

            return(compressedData);
        }
Пример #2
0
        /// <summary>
        /// Compresses an arm9 executable with a variant of LZSS encoding specific to binary files.
        /// </summary>
        /// <param name="uncompressedData">The uncompressed arm9 file data.</param>
        /// <param name="headerLength">The length of the uncompressed header (usually 0x4000).</param>
        /// <param name="footer">The footer stripped from the original arm9 file during decompression.</param>
        /// <returns>The compressed arm9 file data.</returns>
        public static byte[] CompressArm9(byte[] uncompressedData, int headerLength, byte[] footer)
        {
            // Perform the standard compression routine
            byte[] compressedData = CompressBinary(uncompressedData, headerLength);

            // Arm9 executables have an additional footer pointing to the location in the decompressed section that
            // holds some important pointers, which needs to be appended to the compressed data
            byte[] arm9Data = new byte[compressedData.Length + footer.Length];
            Array.Copy(compressedData, 0, arm9Data, 0, compressedData.Length);
            Array.Copy(footer, 0, arm9Data, compressedData.Length, footer.Length);

            // Update the pointer to the end of the compressed data
            int footerPointerOffset = BitConverter.ToInt32(footer, PointerLength) + 0x14;

            StreamHelper.WriteBytes(compressedData.Length | 0x02000000, arm9Data, footerPointerOffset);

            return(arm9Data);
        }