SwapBytes() public static method

public static SwapBytes ( uint value ) : uint
value uint
return uint
コード例 #1
0
        private static unsafe uint[] ReadBlockOffsets(MpqArchive archive, uint hash, long offset, int count)
        {
            int length       = count * sizeof(uint);
            var sharedBuffer = CommonMethods.GetSharedBuffer(length);

            if (archive.ReadArchiveData(sharedBuffer, 0, offset, length) != length)
            {
                throw new EndOfStreamException();
            }

            var offsets = new uint[count];

            Buffer.BlockCopy(sharedBuffer, 0, offsets, 0, length);

            if (!BitConverter.IsLittleEndian)
            {
                CommonMethods.SwapBytes(offsets);
            }

            // If hash is valid, decode the header
            if (hash != 0)
            {
                unchecked { CommonMethods.Decrypt(offsets, hash - 1); }
            }

            return(offsets);
        }
コード例 #2
0
        public static unsafe void DecryptWithEndianSwap(uint *data, uint hash, int length)
        {
            uint buffer, temp = 0xEEEEEEEE;

            for (int i = length; i-- != 0;)
            {
                unchecked
                {
                    temp  += encryptionTable[0x400 + (hash & 0xFF)];
                    buffer = CommonMethods.SwapBytes(*data) ^ (temp + hash);
                    temp  += buffer + (temp << 5) + 3;
                    *data++ = CommonMethods.SwapBytes(buffer);
                    hash = (hash >> 11) | (0x11111111 + ((hash ^ 0x7FF) << 21));
                }
            }
        }
コード例 #3
0
        /// <summary>Reads the encrypted <see cref="System.UInt32"/> table at the specified offset in the archive.</summary>
        /// <remarks>
        /// This method will place the bytes in their native order.
        /// Reading must be done by pinning the buffer and accessing it as an <see cref="System.UInt32"/> buffer.
        /// The only purpose of this method is to share code between the hash table and block table reading methods.
        /// Because of its specific behavior, it should not be used anywhere else…
        /// </remarks>
        /// <param name="buffer">The destination buffer.</param>
        /// <param name="tableLength">Length of the table in units of 16 bytes.</param>
        /// <param name="tableOffset">The offset in the archive.</param>
        /// <param name="dataLength">Length of the data to read.</param>
        /// <param name="hash">The hash to use for decrypting the data.</param>
        /// <param name="compressedReadBuffer">The compressed read buffer to use for holding temporary data.</param>
        private unsafe void ReadEncryptedUInt32Table(byte[] buffer, long tableLength, long tableOffset, long dataLength, uint hash, byte[] compressedReadBuffer)
        {
            int  uintCount      = checked ((int)(tableLength << 2));
            long realDataLength = checked (sizeof(uint) * uintCount);

            bool isCompressed = dataLength < realDataLength;
            // Stream.Read only takes an int length for now, and it is unlikely that the tables will ever exceed 2GB.
            // But anyway, if this ever happens in the future the overflow check should ensure us that the program will crash nicely.
            int dataLengthInt32 = checked ((int)dataLength);

            stream.Seek(archiveDataOffset + tableOffset, SeekOrigin.Begin);
            if (stream.Read(isCompressed ? compressedReadBuffer : buffer, 0, dataLengthInt32) != dataLengthInt32)
            {
                throw new EndOfStreamException();                 // Throw an exception if we are not able to read as many bytes as we were told we could read…

                fixed(byte *bufferPointer = buffer)
                {
                    uint *tablePointer = (uint *)bufferPointer;

                    // If the data is compressed, we will have to swap endianness three times on big endian platforms, but it can't be helped.
                    // (Endiannes swap is done by the Encryption.Decrypt method automatically when passing a byte buffer.)
                    // However, if we don't have to decompress, endiannes swap is only done once, before decrypting.

                    if (isCompressed)
                    {
                        if (hash != 0)
                        {
                            CommonMethods.Decrypt(compressedReadBuffer, hash, dataLengthInt32);                            // On big endian platforms : Read UInt32, Swap Bytes, Compute, Swap Bytes, Store UInt32
                        }
                        if (CommonMethods.DecompressBlock(compressedReadBuffer, dataLengthInt32, buffer, true) != realDataLength)
                        {
                            throw new InvalidDataException();                     // Only allow the exact amount of bytes as a result
                        }
                    }

                    if (!BitConverter.IsLittleEndian)
                    {
                        CommonMethods.SwapBytes(tablePointer, uintCount);
                    }

                    if (!isCompressed && hash != 0)
                    {
                        CommonMethods.Decrypt(tablePointer, hash, uintCount);
                    }
                }
        }
コード例 #4
0
        private unsafe byte[] ApplyBsd0Patch(ref PatchInfoHeader patchInfoHeader, ref PatchHeader patchHeader, uint patchLength, byte[] originalData)
        {
            byte[] patchData;

            if (patchLength < patchHeader.PatchLength)
            {
                patchData = UnpackRle();
            }
            else
            {
                patchData = new byte[patchLength];
                if (Read(patchData, 0, checked ((int)patchLength)) != patchLength)
                {
                    throw new EndOfStreamException();
                }
            }

            fixed(byte *patchDataPointer = patchData)
            {
                var bsdiffHeader = (PatchBsdiff40Header *)patchDataPointer;

                if (!BitConverter.IsLittleEndian)
                {
                    CommonMethods.SwapBytes((ulong *)patchDataPointer, sizeof(PatchBsdiff40Header) >> 3);
                }

                if (bsdiffHeader->Signature != 0x3034464649445342 /* 'BSDIFF40' */)
                {
                    throw new InvalidDataException(ErrorMessages.GetString("Bsd0PatchHeaderInvalidSignature"));
                }

                var controlBlock    = (uint *)(patchDataPointer + sizeof(PatchBsdiff40Header));
                var differenceBlock = (byte *)controlBlock + bsdiffHeader->ControlBlockLength;
                var extraBlock      = differenceBlock + bsdiffHeader->DifferenceBlockLength;

                if (!BitConverter.IsLittleEndian)
                {
                    CommonMethods.SwapBytes(controlBlock, bsdiffHeader->ControlBlockLength >> 2);
                }

                var patchBuffer = new byte[bsdiffHeader->PatchedFileSize];

                fixed(byte *originalDataPointer = originalData)
                fixed(byte *patchBufferPointer = patchBuffer)
                {
                    var sourcePointer      = originalDataPointer;
                    var destinationPointer = patchBufferPointer;
                    int sourceCount        = originalData.Length;
                    int destinationCount   = patchBuffer.Length;

                    while (destinationCount != 0)
                    {
                        uint differenceLength = *controlBlock++;
                        uint extraLength      = *controlBlock++;
                        uint sourceOffset     = *controlBlock++;

                        if (differenceLength > destinationCount)
                        {
                            throw new InvalidDataException(ErrorMessages.GetString("Bsd0PatchInvalidData"));
                        }
                        destinationCount = (int)(destinationCount - differenceLength);

                        // Apply the difference patch (Patched Data = Original data + Difference data)
                        for (; differenceLength-- != 0; destinationPointer++, sourcePointer++)
                        {
                            *destinationPointer = *differenceBlock++;
                            if (sourceCount > 0)
                            {
                                *destinationPointer += *sourcePointer;
                            }
                        }

                        if (extraLength > destinationCount)
                        {
                            throw new InvalidDataException(ErrorMessages.GetString("Bsd0PatchInvalidData"));
                        }
                        destinationCount = (int)(destinationCount - extraLength);

                        // Apply the extra data patch (New data)
                        for (; extraLength-- != 0;)
                        {
                            *destinationPointer++ = *extraBlock++;
                        }

                        sourcePointer += (sourceOffset & 0x80000000) != 0 ? unchecked ((int)(0x80000000 - sourceOffset)) : (int)sourceOffset;
                    }
                }

                return(patchBuffer);
            }
        }
コード例 #5
0
        private unsafe byte[] ApplyPatch(PatchInfoHeader patchInfoHeader, Stream baseStream)
        {
            PatchHeader patchHeader;

            Read((byte *)&patchHeader, sizeof(PatchHeader));
            if (!BitConverter.IsLittleEndian)
            {
                CommonMethods.SwapBytes((uint *)&patchHeader, sizeof(PatchHeader) >> 2);
            }

            if (patchHeader.Signature != 0x48435450 /* 'PTCH' */)
            {
                throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidSignature"));
            }
            if (patchHeader.PatchedFileSize != file.Size)
            {
                throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidFileSize"));
            }
            if (baseStream.Length != patchHeader.OriginalFileSize)
            {
                throw new InvalidDataException(ErrorMessages.GetString("PatchHeaderInvalidBaseFileSize"));
            }

            // Once the initial tests are passed, we can load the whole patch in memory.
            // This will take a big amount of memory, but will avoid having to unpack the file twice…

            var originalData = new byte[baseStream.Length];

            if (baseStream.Read(originalData, 0, originalData.Length) != originalData.Length)
            {
                throw new EndOfStreamException();
            }

            var md5 = CommonMethods.SharedMD5;

            var originalHash = md5.ComputeHash(originalData);

            PatchMD5ChunkData md5ChunkData;
            bool hasMD5 = false;

            while (true)
            {
                long chunkPosition = Position;
                var  chunkHeader   = stackalloc uint[2];

                if (Read((byte *)chunkHeader, 8) != 8)
                {
                    throw new EndOfStreamException();
                }
                if (!BitConverter.IsLittleEndian)
                {
                    CommonMethods.SwapBytes(chunkHeader, 2);
                }

                if (chunkHeader[0] == 0x5F35444D /* 'MD5_' */)
                {
                    if (Read((byte *)&md5ChunkData, sizeof(PatchMD5ChunkData)) != sizeof(PatchMD5ChunkData))
                    {
                        throw new EndOfStreamException();
                    }

                    if (!CommonMethods.CompareData(originalHash, md5ChunkData.OrginialFileMD5))
                    {
                        throw new InvalidDataException(ErrorMessages.GetString("PatchBaseFileMD5Failed"));
                    }

                    hasMD5 = true;
                }
                else if (chunkHeader[0] == 0x4D524658 /* 'XFRM' */)
                {
                    // This may not be a real problem, however, let's not handle this case for now… (May fail because of the stupid bogus patches…)
                    if (chunkPosition + chunkHeader[1] != Length)
                    {
                        throw new InvalidDataException(ErrorMessages.GetString("PatchXfrmChunkError"));
                    }

                    uint patchType;

                    if (Read((byte *)&patchType, 4) != 4)
                    {
                        throw new EndOfStreamException();
                    }
                    if (!BitConverter.IsLittleEndian)
                    {
                        patchType = CommonMethods.SwapBytes(patchType);
                    }

                    uint patchLength = chunkHeader[1] - 12;

                    byte[] patchedData;

                    if (patchType == 0x59504F43 /* 'COPY' */)
                    {
                        patchedData = ApplyCopyPatch(ref patchInfoHeader, ref patchHeader, patchLength, originalData);
                    }
                    if (patchType == 0x30445342 /* 'BSD0' */)
                    {
                        patchedData = ApplyBsd0Patch(ref patchInfoHeader, ref patchHeader, patchLength, originalData);
                    }
                    else
                    {
                        throw new NotSupportedException("Unsupported patch type: '" + CommonMethods.FourCCToString(chunkHeader[0]) + "'");
                    }

                    if (hasMD5)
                    {
                        var patchedHash = md5.ComputeHash(patchedData);

                        if (!CommonMethods.CompareData(patchedHash, md5ChunkData.PatchedFileMD5))
                        {
                            throw new InvalidDataException("PatchFinalFileMD5Failed");
                        }
                    }

                    return(patchedData);
                }
                else
                {
                    throw new InvalidDataException(string.Format(ErrorMessages.GetString("PatchUnknownChunk"), CommonMethods.FourCCToString(chunkHeader[0])));
                }

                Seek(chunkPosition + chunkHeader[1], SeekOrigin.Begin);
            }
        }
コード例 #6
0
        private unsafe byte[] ApplyBsd0Patch(ref PatchInfoHeader patchInfoHeader, ref PatchHeader patchHeader, uint patchLength, byte[] originalData)
        {
            byte[] patchData;

            if (patchLength < patchHeader.PatchLength)
            {
                patchData = UnpackRle(patchLength);
            }
            else
            {
                patchData = new byte[patchLength];
                if (Read(patchData, 0, checked ((int)patchLength)) != patchLength)
                {
                    throw new EndOfStreamException();
                }
            }

            fixed(byte *patchDataPointer = patchData)
            {
                var bsdiffHeader = (PatchBsdiff40Header *)patchDataPointer;

                if (!BitConverter.IsLittleEndian)
                {
                    CommonMethods.SwapBytes((ulong *)patchDataPointer, sizeof(PatchBsdiff40Header) / sizeof(ulong));
                }

                if (bsdiffHeader->Signature != 0x3034464649445342 /* 'BSDIFF40' */)
                {
                    throw new InvalidDataException(ErrorMessages.GetString("Bsd0PatchHeaderInvalidSignature"));
                }

                var controlBlock    = (uint *)(patchDataPointer + sizeof(PatchBsdiff40Header));
                var differenceBlock = (byte *)controlBlock + bsdiffHeader->ControlBlockLength;
                var extraBlock      = differenceBlock + bsdiffHeader->DifferenceBlockLength;

                if (!BitConverter.IsLittleEndian)
                {
                    CommonMethods.SwapBytes(controlBlock, bsdiffHeader->ControlBlockLength / sizeof(uint));
                }

                var patchedBuffer = new byte[bsdiffHeader->PatchedFileSize];

                uint o = 0;
                uint n = 0;

                try
                {
                    while (n < patchedBuffer.Length)
                    {
                        uint differenceLength = *controlBlock++;
                        uint extraLength      = *controlBlock++;
                        uint sourceOffset     = *controlBlock++;

                        // Apply the difference patch (Patched Data = Original data + Difference data)
                        for (uint i = 0; i < differenceLength; i++, n++, o++)
                        {
                            patchedBuffer[n] = differenceBlock[i];
                            if (o < originalData.Length)
                            {
                                patchedBuffer[n] += originalData[o];
                            }
                        }
                        differenceBlock += differenceLength;

                        // Apply the extra data patch (New data)
                        for (int e = 0; e < extraLength; e++)
                        {
                            patchedBuffer[n++] = extraBlock[e];
                        }
                        extraBlock += extraLength;

                        unchecked
                        {
                            o += (sourceOffset & 0x80000000) != 0 ? (0x80000000 - sourceOffset) : sourceOffset;
                        }
                    }
                }
                catch (IndexOutOfRangeException ex)
                {
                    throw new InvalidDataException(ErrorMessages.GetString("Bsd0PatchInvalidData"), ex);
                }

                return(patchedBuffer);
            }
        }