public static unsafe string SFileGetFileHash(MpqFileSafeHandle hFile)
        {
            if (hFile.IsInvalid || hFile.IsClosed)
            {
                throw new InvalidOperationException();
            }

            var handle = hFile.DangerousGetHandle();
            var header = (_TMPQFileHeader *)handle.ToPointer();

            var md5   = header->pFileEntry->md5;
            var chars = new char[32];

            fixed(char *c = chars)
            {
                int b;

                for (var i = 0; i < 16; i++)
                {
                    b            = md5[i] >> 4;
                    c[i * 2]     = (char)(55 + b + (((b - 10) >> 31) & -7));
                    b            = md5[i] & 0xF;
                    c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7));
                }

                return(new string(c).Substring(0, 32));
            }
        }
        public static unsafe uint SFileGetFilePointer(MpqFileSafeHandle hFile)
        {
            if (hFile.IsInvalid || hFile.IsClosed)
            {
                throw new InvalidOperationException();
            }

            var handle = hFile.DangerousGetHandle();
            var header = (_TMPQFileHeader *)handle.ToPointer();

            return(header->dwFilePos);
        }
        public static unsafe DateTime?SFileGetFileTime(MpqFileSafeHandle hFile)
        {
            if (hFile.IsInvalid || hFile.IsClosed)
            {
                throw new InvalidOperationException();
            }

            var handle = hFile.DangerousGetHandle();
            var header = (_TMPQFileHeader *)handle.ToPointer();
            var time   = header->pFileEntry->FileTime;

            if (time == 0)
            {
                return(null);
            }
            else
            {
                return(DateTime.FromFileTimeUtc((long)time));
            }
        }