diff options
Diffstat (limited to 'src')
274 files changed, 65046 insertions, 0 deletions
diff --git a/src/FileStream.cpp b/src/FileStream.cpp new file mode 100644 index 0000000..1a21170 --- /dev/null +++ b/src/FileStream.cpp @@ -0,0 +1,2294 @@ +/*****************************************************************************/ +/* FileStream.cpp                         Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* File stream support for StormLib                                          */ +/*                                                                           */ +/* Windows support: Written by Ladislav Zezula                               */ +/* Mac support:     Written by Sam Wilkins                                   */ +/* Linux support:   Written by Sam Wilkins and Ivan Komissarov               */ +/* Big-endian:      Written & debugged by Sam Wilkins                        */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 11.06.10  1.00  Lad  Derived from StormPortMac.cpp and StormPortLinux.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" +#include "FileStream.h" + +#ifdef _MSC_VER +#pragma comment(lib, "wininet.lib") +#endif + +//----------------------------------------------------------------------------- +// Local defines + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE ((HANDLE)-1) +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4800)                  // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) +#endif + +//----------------------------------------------------------------------------- +// Local functions - platform-specific functions + +#ifndef PLATFORM_WINDOWS +static int nLastError = ERROR_SUCCESS; + +int GetLastError() +{ +    return nLastError; +} + +void SetLastError(int nError) +{ +    nLastError = nError; +} +#endif + +#ifndef PLATFORM_LITTLE_ENDIAN +void ConvertPartHeader(void * partHeader) +{ +    PPART_FILE_HEADER theHeader = (PPART_FILE_HEADER)partHeader; + +    theHeader->PartialVersion = SwapUInt32(theHeader->PartialVersion); +    theHeader->Flags          = SwapUInt32(theHeader->Flags); +    theHeader->FileSizeLo     = SwapUInt32(theHeader->FileSizeLo); +    theHeader->FileSizeHi     = SwapUInt32(theHeader->FileSizeHi); +    theHeader->BlockSize      = SwapUInt32(theHeader->BlockSize); +} +#endif + +//----------------------------------------------------------------------------- +// Preparing file bitmap for a complete file of a given size + +#define DEFAULT_BLOCK_SIZE 0x4000 + +static bool Dummy_GetBitmap( +    TFileStream * pStream, +    TFileBitmap * pBitmap, +    DWORD Length, +    LPDWORD LengthNeeded) +{ +    ULONGLONG FileSize = 0; +    DWORD TotalLength; +    DWORD BlockCount; +    DWORD BitmapSize; +    DWORD LastByte; +    bool bResult = false; + +    // Get file size and calculate bitmap length +    FileStream_GetSize(pStream, &FileSize); +    BlockCount = (DWORD)(((FileSize - 1) / DEFAULT_BLOCK_SIZE) + 1); +    BitmapSize = (DWORD)(((BlockCount - 1) / 8) + 1); + +    // Calculate and give the total length +    TotalLength = sizeof(TFileBitmap) + BitmapSize; +    if(LengthNeeded != NULL) +        *LengthNeeded = TotalLength; + +    // Has the caller given enough space for storing the structure? +    if(Length >= sizeof(TFileBitmap)) +    { +        memset(pBitmap, 0, sizeof(TFileBitmap)); +        pBitmap->EndOffset  = FileSize; +        pBitmap->IsComplete = 1; +        pBitmap->BitmapSize = BitmapSize; +        pBitmap->BlockSize  = DEFAULT_BLOCK_SIZE; +        bResult = true; +    } + +    // Do we have enough space to fill the bitmap as well? +    if(Length >= TotalLength) +    { +        LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); + +        // Fill the full blocks +        memset(pbBitmap, 0xFF, (BlockCount / 8)); +        pbBitmap += (BlockCount / 8); +        bResult = true; + +        // Supply the last block +        if(BlockCount & 7) +        { +            LastByte = (1 << (BlockCount & 7)) - 1; +            pbBitmap[0] = (BYTE)LastByte; +        } +    } + +    return bResult; +} + +//----------------------------------------------------------------------------- +// Local functions - base file support + +static bool BaseFile_Read( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                        // Pointer to data to be read +    DWORD dwBytesToRead)                    // Number of bytes to read from the file +{ +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; +    DWORD dwBytesRead = 0;                  // Must be set by platform-specific code + +#ifdef PLATFORM_WINDOWS +    { +        // Note: StormLib no longer supports Windows 9x. +        // Thus, we can use the OVERLAPPED structure to specify +        // file offset to read from file. This allows us to skip +        // one system call to SetFilePointer + +        // Update the byte offset +        pStream->Base.File.FilePos = ByteOffset; + +        // Read the data +        if(dwBytesToRead != 0) +        { +            OVERLAPPED Overlapped; + +            Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); +            Overlapped.Offset = (DWORD)ByteOffset; +            Overlapped.hEvent = NULL; +            if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped)) +                return false; +        } +/* +        // If the byte offset is different from the current file position, +        // we have to update the file position +        if(ByteOffset != pStream->Base.File.FilePos) +        { +            LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); + +            SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); +            pStream->Base.File.FilePos = ByteOffset; +        } + +        // Read the data +        if(dwBytesToRead != 0) +        { +            if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL)) +                return false; +        } +*/ +    } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        ssize_t bytes_read; + +        // If the byte offset is different from the current file position, +        // we have to update the file position +        if(ByteOffset != pStream->Base.File.FilePos) +        { +            lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); +            pStream->Base.File.FilePos = ByteOffset; +        } + +        // Perform the read operation +        if(dwBytesToRead != 0) +        { +            bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead); +            if(bytes_read == -1) +            { +                nLastError = errno; +                return false; +            } +             +            dwBytesRead = (DWORD)(size_t)bytes_read; +        } +    } +#endif + +    // Increment the current file position by number of bytes read +    // If the number of bytes read doesn't match to required amount, return false +    pStream->Base.File.FilePos = ByteOffset + dwBytesRead; +    if(dwBytesRead != dwBytesToRead) +        SetLastError(ERROR_HANDLE_EOF); +    return (dwBytesRead == dwBytesToRead); +} + +/** + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ + +static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) +{ +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; +    DWORD dwBytesWritten = 0;               // Must be set by platform-specific code + +#ifdef PLATFORM_WINDOWS +    { +        // Note: StormLib no longer supports Windows 9x. +        // Thus, we can use the OVERLAPPED structure to specify +        // file offset to read from file. This allows us to skip +        // one system call to SetFilePointer + +        // Update the byte offset +        pStream->Base.File.FilePos = ByteOffset; + +        // Read the data +        if(dwBytesToWrite != 0) +        { +            OVERLAPPED Overlapped; + +            Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); +            Overlapped.Offset = (DWORD)ByteOffset; +            Overlapped.hEvent = NULL; +            if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped)) +                return false; +        } +/* +        // If the byte offset is different from the current file position, +        // we have to update the file position +        if(ByteOffset != pStream->Base.File.FilePos) +        { +            LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); + +            SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); +            pStream->Base.File.FilePos = ByteOffset; +        } + +        // Read the data +        if(dwBytesToWrite != 0) +        { +        if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) +            return false; +    } +*/ +    } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        ssize_t bytes_written; + +        // If the byte offset is different from the current file position, +        // we have to update the file position +        if(ByteOffset != pStream->Base.File.FilePos) +        { +            lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); +            pStream->Base.File.FilePos = ByteOffset; +        } + +        // Perform the read operation +        bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite); +        if(bytes_written == -1) +        { +            nLastError = errno; +            return false; +        } +         +        dwBytesWritten = (DWORD)(size_t)bytes_written; +    } +#endif + +    // Increment the current file position by number of bytes read +    pStream->Base.File.FilePos = ByteOffset + dwBytesWritten; + +    // Also modify the file size, if needed +    if(pStream->Base.File.FilePos > pStream->Base.File.FileSize) +        pStream->Base.File.FileSize = pStream->Base.File.FilePos; + +    if(dwBytesWritten != dwBytesToWrite) +        SetLastError(ERROR_DISK_FULL); +    return (dwBytesWritten == dwBytesToWrite); +} + +static bool BaseFile_GetPos( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset)                 // Pointer to file byte offset +{ +    *pByteOffset = pStream->Base.File.FilePos; +    return true; +} + +static bool BaseFile_GetSize( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pFileSize)                   // Pointer where to store file size +{ +    *pFileSize = pStream->Base.File.FileSize; +    return true; +} + +/** + * \a pStream Pointer to an open stream + * \a NewFileSize New size of the file + */ +static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +{ +#ifdef PLATFORM_WINDOWS +    { +        LONG FileSizeHi = (LONG)(NewFileSize >> 32); +        LONG FileSizeLo; +        DWORD dwNewPos; +        bool bResult; + +        // Set the position at the new file size +        dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN); +        if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS) +            return false; + +        // Set the current file pointer as the end of the file +        bResult = (bool)SetEndOfFile(pStream->Base.File.hFile); + +        // Restore the file position +        FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); +        FileSizeLo = (LONG)(pStream->Base.File.FilePos); +        SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); +        return bResult; +    } +#endif +     +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        if(ftruncate((intptr_t)pStream->Base.File.hFile, (off_t)NewFileSize) == -1) +        { +            nLastError = errno; +            return false; +        } +         +        return true; +    } +#endif +} + +static bool BaseFile_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ +    *pFileTime = pStream->Base.File.FileTime; +    return true; +} + +// Renames the file pointed by pStream so that it contains data from pNewStream +static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream) +{ +#ifdef PLATFORM_WINDOWS +    // Delete the original stream file. Don't check the result value, +    // because if the file doesn't exist, it would fail +    DeleteFile(pStream->szFileName); + +    // Rename the new file to the old stream's file +    return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    // "rename" on Linux also works if the target file exists +    if(rename(pNewStream->szFileName, pStream->szFileName) == -1) +    { +        nLastError = errno; +        return false; +    } +     +    return true; +#endif +} + +static void BaseFile_Close(TFileStream * pStream) +{ +    if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) +    { +#ifdef PLATFORM_WINDOWS +        CloseHandle(pStream->Base.File.hFile); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +        close((intptr_t)pStream->Base.File.hFile); +#endif +    } + +    // Also invalidate the handle +    pStream->Base.File.hFile = INVALID_HANDLE_VALUE; +} + +static bool BaseFile_Create( +    TFileStream * pStream, +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS +    { +        DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + +        pStream->Base.File.hFile = CreateFile(szFileName, +                                              GENERIC_READ | GENERIC_WRITE, +                                              dwWriteShare | FILE_SHARE_READ, +                                              NULL, +                                              CREATE_ALWAYS, +                                              0, +                                              NULL); +        if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) +            return false; +    } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        intptr_t handle; +         +        handle = open(szFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +        if(handle == -1) +        { +            nLastError = errno; +            return false; +        } +         +        pStream->Base.File.hFile = (HANDLE)handle; +    } +#endif + +    // Fill-in the entry points +    pStream->BaseRead    = BaseFile_Read; +    pStream->BaseWrite   = BaseFile_Write; +    pStream->BaseGetPos  = BaseFile_GetPos; +    pStream->BaseGetSize = BaseFile_GetSize; +    pStream->BaseSetSize = BaseFile_SetSize; +    pStream->BaseSetSize = BaseFile_SetSize; +    pStream->BaseGetTime = BaseFile_GetTime; +    pStream->BaseClose   = BaseFile_Close; + +    // Reset the file position +    pStream->Base.File.FileSize = 0; +    pStream->Base.File.FilePos = 0; +    pStream->dwFlags = dwStreamFlags; +    return true; +} + +static bool BaseFile_Open( +    TFileStream * pStream, +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS +    { +        ULARGE_INTEGER FileSize; +        DWORD dwDesiredAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? GENERIC_READ : GENERIC_ALL; +        DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + +        // Open the file +        pStream->Base.File.hFile = CreateFile(szFileName, +                                              dwDesiredAccess, +                                              dwWriteShare | FILE_SHARE_READ, +                                              NULL, +                                              OPEN_EXISTING, +                                              0, +                                              NULL); +        if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) +            return false; + +        // Query the file size +        FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); +        pStream->Base.File.FileSize = FileSize.QuadPart; + +        // Query last write time +        GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime); +    } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    { +        struct stat fileinfo; +        int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; +        intptr_t handle; + +        // Open the file +        handle = open(szFileName, oflag); +        if(handle == -1) +        { +            nLastError = errno; +            return false; +        } + +        // Get the file size +        if(fstat(handle, &fileinfo) == -1) +        { +            nLastError = errno; +            return false; +        } + +        // time_t is number of seconds since 1.1.1970, UTC. +        // 1 second = 10000000 (decimal) in FILETIME +        // Set the start to 1.1.1970 00:00:00 +        pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); +        pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size; +        pStream->Base.File.hFile = (HANDLE)handle; +    } +#endif + +    // Fill-in the entry points +    pStream->BaseRead    = BaseFile_Read; +    pStream->BaseWrite   = BaseFile_Write; +    pStream->BaseGetPos  = BaseFile_GetPos; +    pStream->BaseGetSize = BaseFile_GetSize; +    pStream->BaseSetSize = BaseFile_SetSize; +    pStream->BaseGetTime = BaseFile_GetTime; +    pStream->BaseClose   = BaseFile_Close; + +    // Reset the file position +    pStream->Base.File.FilePos = 0; +    pStream->dwFlags = dwStreamFlags; +    return true; +} + +//----------------------------------------------------------------------------- +// Local functions - base memory-mapped file support + +static bool BaseMap_Read( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                        // Pointer to data to be read +    DWORD dwBytesToRead)                    // Number of bytes to read from the file +{ +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos; + +    // Do we have to read anything at all? +    if(dwBytesToRead != 0) +    { +        // Don't allow reading past file size +        if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize) +            return false; + +        // Copy the required data +        memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead); +    } + +    // Move the current file position +    pStream->Base.Map.FilePos += dwBytesToRead; +    return true; +} + +static bool BaseMap_GetPos( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset)                // Pointer to file byte offset +{ +    *pByteOffset = pStream->Base.Map.FilePos; +    return true; +} + +static bool BaseMap_GetSize( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pFileSize)                  // Pointer where to store file size +{ +    *pFileSize = pStream->Base.Map.FileSize; +    return true; +} + +static bool BaseMap_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ +    *pFileTime = pStream->Base.Map.FileTime; +    return true; +} + +static void BaseMap_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS +    if(pStream->Base.Map.pbFile != NULL) +        UnmapViewOfFile(pStream->Base.Map.pbFile); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    if(pStream->Base.Map.pbFile != NULL) +        munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize); +#endif + +    pStream->Base.Map.pbFile = NULL; +} + +static bool BaseMap_Open( +    TFileStream * pStream, +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + +    ULARGE_INTEGER FileSize; +    HANDLE hFile; +    HANDLE hMap; +    bool bResult = false; + +    // Open the file for read access +    hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); +    if(hFile != NULL) +    { +        // Retrieve file size. Don't allow mapping file of a zero size. +        FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); +        if(FileSize.QuadPart != 0) +        { +            // Retrieve file time +            GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + +            // Now create mapping object +            hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); +            if(hMap != NULL) +            { +                // Map the entire view into memory +                // Note that this operation will fail if the file can't fit +                // into usermode address space +                pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); +                if(pStream->Base.Map.pbFile != NULL) +                { +                    pStream->Base.Map.FileSize = FileSize.QuadPart; +                    pStream->Base.Map.FilePos = 0; +                    bResult = true; +                } + +                // Close the map handle +                CloseHandle(hMap); +            } +        } + +        // Close the file handle +        CloseHandle(hFile); +    } + +    // If the file is not there and is not available for random access, +    // report error +    if(bResult == false) +        return false; +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +    struct stat fileinfo; +    intptr_t handle; +    bool bResult = false; + +    // Open the file +    handle = open(szFileName, O_RDONLY); +    if(handle != -1) +    { +        // Get the file size +        if(fstat(handle, &fileinfo) != -1) +        { +            pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0); +            if(pStream->Base.Map.pbFile != NULL) +            { +                // time_t is number of seconds since 1.1.1970, UTC. +                // 1 second = 10000000 (decimal) in FILETIME +                // Set the start to 1.1.1970 00:00:00 +                pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); +                pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size; +                pStream->Base.Map.FilePos = 0; +                bResult = true; +            } +        } +        close(handle); +    } + +    // Did the mapping fail? +    if(bResult == false) +    { +        nLastError = errno; +        return false; +    } +#endif + +    // Fill-in entry points +    pStream->BaseRead    = BaseMap_Read; +    pStream->BaseGetPos  = BaseMap_GetPos; +    pStream->BaseGetSize = BaseMap_GetSize; +    pStream->BaseGetTime = BaseMap_GetTime; +    pStream->BaseClose   = BaseMap_Close; +    pStream->dwFlags = dwStreamFlags; +    return true; +} + +//----------------------------------------------------------------------------- +// Local functions - base HTTP file support + +static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR * szServerName) +{ +    // Check for HTTP +    if(!_tcsnicmp(szFileName, _T("http://"), 7)) +        szFileName += 7; + +    // Cut off the server name +    if(szServerName != NULL) +    { +        while(szFileName[0] != 0 && szFileName[0] != _T('/')) +            *szServerName++ = *szFileName++; +        *szServerName = 0; +    } +    else +    { +        while(szFileName[0] != 0 && szFileName[0] != _T('/')) +            *szFileName++; +    } + +    // Return the remainder +    return szFileName; +} + +static bool BaseHttp_Read( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                        // Pointer to data to be read +    DWORD dwBytesToRead)                    // Number of bytes to read from the file +{ +#ifdef PLATFORM_WINDOWS +    ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos; +    DWORD dwTotalBytesRead = 0; + +    // Do we have to read anything at all? +    if(dwBytesToRead != 0) +    { +        HINTERNET hRequest; +        LPCTSTR szFileName; +        LPBYTE pbBuffer = (LPBYTE)pvBuffer; +        TCHAR szRangeRequest[0x80]; +        DWORD dwStartOffset = (DWORD)ByteOffset; +        DWORD dwEndOffset = dwStartOffset + dwBytesToRead; +        BYTE Buffer[0x200]; + +        // Open HTTP request to the file +        szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL); +        hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); +        if(hRequest != NULL) +        { +            // Add range request to the HTTP headers +            // http://www.clevercomponents.com/articles/article015/resuming.asp +            _stprintf(szRangeRequest, _T("Range: bytes=%d-%d"), dwStartOffset, dwEndOffset); +            HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);  + +            // Send the request to the server +            if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) +            { +                while(dwTotalBytesRead < dwBytesToRead) +                { +                    DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead; +                    DWORD dwBlockBytesRead = 0; + +                    // Read the block from the file +                    if(dwBlockBytesToRead > sizeof(Buffer)) +                        dwBlockBytesToRead = sizeof(Buffer); +                    InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead); + +                    // Check for end +                    if(dwBlockBytesRead == 0) +                        break; + +                    // Move buffers +                    dwTotalBytesRead += dwBlockBytesRead; +                    pbBuffer += dwBlockBytesRead; +                } +            } +            InternetCloseHandle(hRequest); +        } +    } + +    // Increment the current file position by number of bytes read +    pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead; + +    // If the number of bytes read doesn't match the required amount, return false +    if(dwTotalBytesRead != dwBytesToRead) +        SetLastError(ERROR_HANDLE_EOF); +    return (dwTotalBytesRead == dwBytesToRead); + +#else + +    // Not supported +    pStream = pStream; +    pByteOffset = pByteOffset; +    pvBuffer = pvBuffer; +    dwBytesToRead = dwBytesToRead; +    SetLastError(ERROR_NOT_SUPPORTED); +    return false; + +#endif +} + +static bool BaseHttp_GetPos( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pByteOffset)                // Pointer to file byte offset +{ +    *pByteOffset = pStream->Base.Http.FilePos; +    return true; +} + +static bool BaseHttp_GetSize( +    TFileStream * pStream,                  // Pointer to an open stream +    ULONGLONG * pFileSize)                  // Pointer where to store file size +{ +    *pFileSize = pStream->Base.Http.FileSize; +    return true; +} + +static bool BaseHttp_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ +    *pFileTime = pStream->Base.Http.FileTime; +    return true; +} + +static void BaseHttp_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS +    if(pStream->Base.Http.hConnect != NULL) +        InternetCloseHandle(pStream->Base.Http.hConnect); +    pStream->Base.Http.hConnect = NULL; + +    if(pStream->Base.Http.hInternet != NULL) +        InternetCloseHandle(pStream->Base.Http.hInternet); +    pStream->Base.Http.hInternet = NULL; +#else +    pStream = pStream; +#endif +} + +static bool BaseHttp_Open( +    TFileStream * pStream, +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + +    HINTERNET hRequest; +    DWORD dwTemp = 0; +    bool bFileAvailable = false; +    int nError = ERROR_SUCCESS; + +    // Don't connect to the internet +    if(!InternetGetConnectedState(&dwTemp, 0)) +        nError = GetLastError(); + +    // Initiate the connection to the internet +    if(nError == ERROR_SUCCESS) +    { +        pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"), +                                                    INTERNET_OPEN_TYPE_PRECONFIG, +                                                    NULL, +                                                    NULL, +                                                    0); +        if(pStream->Base.Http.hInternet == NULL) +            nError = GetLastError(); +    } + +    // Connect to the server +    if(nError == ERROR_SUCCESS) +    { +        TCHAR szServerName[MAX_PATH]; +        DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE; + +        // Initiate connection with the server +        szFileName = BaseHttp_ExtractServerName(szFileName, szServerName); +        pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet, +                                                      szServerName, +                                                      INTERNET_DEFAULT_HTTP_PORT, +                                                      NULL, +                                                      NULL, +                                                      INTERNET_SERVICE_HTTP, +                                                      dwFlags, +                                                      0); +        if(pStream->Base.Http.hConnect == NULL) +            nError = GetLastError(); +    } + +    // Now try to query the file size +    if(nError == ERROR_SUCCESS) +    { +        // Open HTTP request to the file +        hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); +        if(hRequest != NULL) +        { +            if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) +            { +                ULONGLONG FileTime = 0; +                DWORD dwFileSize = 0; +                DWORD dwDataSize; +                DWORD dwIndex = 0; + +                // Check if the MPQ has Last Modified field +                dwDataSize = sizeof(ULONGLONG); +                if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex)) +                    pStream->Base.Http.FileTime = FileTime; + +                // Verify if the server supports random access +                dwDataSize = sizeof(DWORD); +                if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex)) +                { +                    if(dwFileSize != 0) +                    { +                        pStream->Base.Http.FileSize = dwFileSize; +                        pStream->Base.Http.FilePos = 0; +                        bFileAvailable = true; +                    } +                } +            } +            InternetCloseHandle(hRequest); +        } +    } + +    // If the file is not there and is not available for random access, +    // report error +    if(bFileAvailable == false) +    { +        BaseHttp_Close(pStream); +        return false; +    } + +    // Fill-in entry points +    pStream->BaseRead    = BaseHttp_Read; +    pStream->BaseGetPos  = BaseHttp_GetPos; +    pStream->BaseGetSize = BaseHttp_GetSize; +    pStream->BaseGetTime = BaseHttp_GetTime; +    pStream->BaseClose   = BaseHttp_Close; +    pStream->dwFlags = dwStreamFlags; +    return true; + +#else + +    // Not supported +    pStream = pStream; +    szFileName = szFileName; +    SetLastError(ERROR_NOT_SUPPORTED); +    return false; + +#endif +} + +//----------------------------------------------------------------------------- +// Local functions - linear stream support + +static bool LinearStream_Read( +    TLinearStream * pStream,                // Pointer to an open stream +    ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                        // Pointer to data to be read +    DWORD dwBytesToRead)                    // Number of bytes to read from the file +{ +    ULONGLONG ByteOffset; +    ULONGLONG EndOffset; +    LPBYTE pbBitmap; +    DWORD BlockIndex; +    DWORD ByteIndex; +    DWORD BitMask; + +    // At this point, we must have a bitmap set +    assert(pStream->pBitmap != NULL); + +    // If we have data map, we must check if the data block is present in the MPQ +    if(dwBytesToRead != 0) +    { +        DWORD BlockSize = pStream->pBitmap->BlockSize; + +        // Get the offset where we read it from +        if(pByteOffset == NULL) +            pStream->BaseGetPos(pStream, &ByteOffset); +        else +            ByteOffset = *pByteOffset; +        EndOffset = ByteOffset + dwBytesToRead; + +        // If the start of the area is within the region +        // protected by data map, check each block +        if(ByteOffset < pStream->pBitmap->EndOffset) +        { +            // Cut the end of the stream protected by the data map +            EndOffset = STORMLIB_MIN(EndOffset, pStream->pBitmap->EndOffset); + +            // Calculate the initial block index +            BlockIndex = (DWORD)(ByteOffset / BlockSize); +            pbBitmap = (LPBYTE)(pStream->pBitmap + 1); + +            // Parse each block +            while(ByteOffset < EndOffset) +            { +                // Prepare byte index and bit mask +                ByteIndex = BlockIndex / 8; +                BitMask = 1 << (BlockIndex & 0x07); + +                // If that bit is not set, it means that the block is not present +                if((pbBitmap[ByteIndex] & BitMask) == 0) +                { +                    SetLastError(ERROR_FILE_CORRUPT); +                    return false; +                } + +                // Move to tne next block +                ByteOffset += BlockSize; +                BlockIndex++; +            } +        } +    } + +    // Now if all tests passed, we can call the base read function +    return pStream->BaseRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); +} + +static bool LinearStream_Switch(TLinearStream * pStream, TLinearStream * pNewStream) +{ +    // Sanity checks +    assert((pNewStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR); +    assert((pNewStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE); +    assert((pStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR); +    assert((pStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE); + +    // Close the new stream +    pNewStream->BaseClose(pNewStream); + +    // Close the source stream +    pStream->BaseClose(pStream); + +    // Rename the new data source file to the existing file +    if(!BaseFile_Switch(pStream, pNewStream)) +        return false; + +    // Now we have to open the "pStream" again +    if(!BaseFile_Open(pStream, pStream->szFileName, pNewStream->dwFlags)) +        return false; + +    // We need to cleanup the new data stream +    FileStream_Close(pNewStream); +    return true; +} + +static bool LinearStream_GetBitmap( +    TLinearStream * pStream, +    TFileBitmap * pBitmap, +    DWORD Length, +    LPDWORD LengthNeeded) +{ +    DWORD TotalLength; +    bool bResult = false; + +    // Assumed that we have bitmap now +    assert(pStream->pBitmap != NULL); + +    // Give the bitmap length +    TotalLength = sizeof(TFileBitmap) + pStream->pBitmap->BitmapSize; +    if(LengthNeeded != NULL) +        *LengthNeeded = TotalLength; + +    // Do we have enough space to fill at least the bitmap structure? +    if(Length >= sizeof(TFileBitmap)) +    { +        // Enough space for complete bitmap? +        if(Length >= TotalLength) +        { +            memcpy(pBitmap, pStream->pBitmap, TotalLength); +            bResult = true; +        } +        else +        { +            memcpy(pBitmap, pStream->pBitmap, sizeof(TFileBitmap)); +            bResult = true; +        } +    } + +    return bResult; +} + +static void LinearStream_Close(TLinearStream * pStream) +{ +    // Free the data map, if any +    if(pStream->pBitmap != NULL) +        STORM_FREE(pStream->pBitmap); +    pStream->pBitmap = NULL; +     +    // Call the base class for closing the stream +    return pStream->BaseClose(pStream); +} + +static bool LinearStream_Open(TLinearStream * pStream) +{ +    // No extra work here really; just set entry points +    pStream->StreamRead    = pStream->BaseRead; +    pStream->StreamWrite   = pStream->BaseWrite; +    pStream->StreamGetPos  = pStream->BaseGetPos; +    pStream->StreamGetSize = pStream->BaseGetSize; +    pStream->StreamSetSize = pStream->BaseSetSize; +    pStream->StreamGetTime = pStream->BaseGetTime; +    pStream->StreamGetBmp  = (STREAM_GETBMP)Dummy_GetBitmap; +    pStream->StreamSwitch  = (STREAM_SWITCH)LinearStream_Switch; +    pStream->StreamClose   = (STREAM_CLOSE)LinearStream_Close; +    return true; +} + +//----------------------------------------------------------------------------- +// Local functions - partial stream support + +static bool IsPartHeader(PPART_FILE_HEADER pPartHdr) +{ +    // Version number must be 2 +    if(pPartHdr->PartialVersion == 2) +    { +        // GameBuildNumber must be an ASCII number +        if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2])) +        { +            // Block size must be power of 2 +            if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0) +                return true; +        } +    } + +    return false; +} + +static bool PartialStream_Read( +    TPartialStream * pStream, +    ULONGLONG * pByteOffset, +    void * pvBuffer, +    DWORD dwBytesToRead) +{ +    ULONGLONG RawByteOffset; +    LPBYTE pbBuffer = (LPBYTE)pvBuffer; +    DWORD dwBytesRemaining = dwBytesToRead; +    DWORD dwPartOffset; +    DWORD dwPartIndex; +    DWORD dwBytesRead = 0; +    DWORD dwBlockSize = pStream->BlockSize; +    bool bResult = false; +    int nFailReason = ERROR_HANDLE_EOF;             // Why it failed if not enough bytes was read + +    // If the byte offset is not entered, use the current position +    if(pByteOffset == NULL) +        pByteOffset = &pStream->VirtualPos; + +    // Check if the file position is not at or beyond end of the file +    if(*pByteOffset >= pStream->VirtualSize) +    { +        SetLastError(ERROR_HANDLE_EOF); +        return false; +    } + +    // Get the part index where the read offset is +    // Note that the part index should now be within the range, +    // as read requests beyond-EOF are handled by the previous test +    dwPartIndex = (DWORD)(*pByteOffset / pStream->BlockSize); +    assert(dwPartIndex < pStream->BlockCount); + +    // If the number of bytes remaining goes past +    // the end of the file, cut them +    if((*pByteOffset + dwBytesRemaining) > pStream->VirtualSize) +        dwBytesRemaining = (DWORD)(pStream->VirtualSize - *pByteOffset); + +    // Calculate the offset in the current part +    dwPartOffset = (DWORD)(*pByteOffset) & (pStream->BlockSize - 1); + +    // Read all data, one part at a time +    while(dwBytesRemaining != 0) +    { +        PPART_FILE_MAP_ENTRY PartMap = pStream->PartMap + dwPartIndex; +        DWORD dwBytesInPart; + +        // If the part is not present in the file, we fail the read +        if((PartMap->Flags & 3) == 0) +        { +            nFailReason = ERROR_FILE_CORRUPT; +            bResult = false; +            break; +        } + +        // If we are in the last part, we have to cut the number of bytes in the last part +        if(dwPartIndex == pStream->BlockCount - 1) +            dwBlockSize = (DWORD)pStream->VirtualSize & (pStream->BlockSize - 1); + +        // Get the number of bytes reamining in the current part +        dwBytesInPart = dwBlockSize - dwPartOffset; + +        // Compute the raw file offset of the file part +        RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo); +        if(RawByteOffset == 0) +        { +            nFailReason = ERROR_FILE_CORRUPT; +            bResult = false; +            break; +        } + +        // If the number of bytes in part is too big, cut it +        if(dwBytesInPart > dwBytesRemaining) +            dwBytesInPart = dwBytesRemaining; + +        // Append the offset within the part +        RawByteOffset += dwPartOffset; +        if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) +        { +            nFailReason = ERROR_FILE_CORRUPT; +            bResult = false; +            break; +        } + +        // Increment the file position +        dwBytesRemaining -= dwBytesInPart; +        dwBytesRead += dwBytesInPart; +        pbBuffer += dwBytesInPart; + +        // Move to the next file part +        dwPartOffset = 0; +        dwPartIndex++; +    } + +    // Move the file position by the number of bytes read +    pStream->VirtualPos = *pByteOffset + dwBytesRead; +    if(dwBytesRead != dwBytesToRead) +        SetLastError(nFailReason); +    return (dwBytesRead == dwBytesToRead); +} + +static bool PartialStream_GetPos( +    TPartialStream * pStream, +    ULONGLONG & ByteOffset) +{ +    ByteOffset = pStream->VirtualPos; +    return true; +} + +static bool PartialStream_GetSize( +    TPartialStream * pStream,               // Pointer to an open stream +    ULONGLONG & FileSize)                   // Pointer where to store file size +{ +    FileSize = pStream->VirtualSize; +    return true; +} + +static bool PartialStream_GetBitmap( +    TPartialStream * pStream, +    TFileBitmap * pBitmap, +    DWORD Length, +    LPDWORD LengthNeeded) +{ +    LPBYTE pbBitmap; +    DWORD TotalLength; +    DWORD BitmapSize = 0; +    DWORD ByteOffset; +    DWORD BitMask; +    bool bResult = false; + +    // Do we have stream bitmap? +    BitmapSize = ((pStream->BlockCount - 1) / 8) + 1; + +    // Give the bitmap length +    TotalLength = sizeof(TFileBitmap) + BitmapSize; +    if(LengthNeeded != NULL) +        *LengthNeeded = TotalLength; + +    // Do we have enough to fill at least the header? +    if(Length >= sizeof(TFileBitmap)) +    { +        // Fill the bitmap header +        pBitmap->StartOffset = 0; +        pBitmap->EndOffset  = pStream->VirtualSize; +        pBitmap->IsComplete = 1; +        pBitmap->BitmapSize = BitmapSize; +        pBitmap->BlockSize = pStream->BlockSize; +        pBitmap->Reserved = 0; +         +        // Is there at least one incomplete block? +        for(DWORD i = 0; i < pStream->BlockCount; i++) +        { +            if(pStream->PartMap[i].Flags != 3) +            { +                pBitmap->IsComplete = 0; +                break; +            } +        } + +        bResult = true; +    } +     +    // Do we have enough space for supplying the bitmap? +    if(Length >= TotalLength) +    { +        // Fill the file bitmap +        pbBitmap = (LPBYTE)(pBitmap + 1); +        for(DWORD i = 0; i < pStream->BlockCount; i++) +        { +            // Is the block there? +            if(pStream->PartMap[i].Flags == 3) +            { +                ByteOffset = i / 8; +                BitMask = 1 << (i & 7); +                pbBitmap[ByteOffset] |= BitMask; +            } +        } +        bResult = true; +    } + +    return bResult; +} + +static void PartialStream_Close(TPartialStream * pStream) +{ +    // Free the part map +    if(pStream->PartMap != NULL) +        STORM_FREE(pStream->PartMap); +    pStream->PartMap = NULL; + +    // Clear variables +    pStream->VirtualSize = 0; +    pStream->VirtualPos = 0; + +    // Close the base stream +    assert(pStream->BaseClose != NULL); +    pStream->BaseClose(pStream); +} + +static bool PartialStream_Open(TPartialStream * pStream) +{ +    PART_FILE_HEADER PartHdr; +    ULONGLONG VirtualSize;                  // Size of the file stored in part file +    ULONGLONG ByteOffset = {0}; +    DWORD BlockCount; + +    // Sanity check +    assert(pStream->BaseRead != NULL); + +    // Attempt to read PART file header +    if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) +    { +        // We need to swap PART file header on big-endian platforms +        BSWAP_PART_HEADER(&PartHdr); + +        // Verify the PART file header +        if(IsPartHeader(&PartHdr)) +        { +            // Calculate the number of parts in the file +            VirtualSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); +            assert(VirtualSize != 0); +            BlockCount = (DWORD)((VirtualSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); + +            // Allocate the map entry array +            pStream->PartMap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); +            if(pStream->PartMap != NULL) +            { +                // Load the block map +                if(pStream->BaseRead(pStream, NULL, pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY))) +                { +                    // Swap the array of file map entries +                    BSWAP_ARRAY32_UNSIGNED(pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY)); + +                    // Fill the members of PART file stream +                    pStream->VirtualSize   = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo; +                    pStream->VirtualPos    = 0; +                    pStream->BlockCount    = BlockCount; +                    pStream->BlockSize     = PartHdr.BlockSize; + +                    // Set new function pointers +                    pStream->StreamRead    = (STREAM_READ)PartialStream_Read; +                    pStream->StreamGetPos  = (STREAM_GETPOS)PartialStream_GetPos; +                    pStream->StreamGetSize = (STREAM_GETSIZE)PartialStream_GetSize; +                    pStream->StreamGetTime = pStream->BaseGetTime; +                    pStream->StreamGetTime = pStream->BaseGetTime; +                    pStream->StreamGetBmp  = (STREAM_GETBMP)PartialStream_GetBitmap; +                    pStream->StreamClose   = (STREAM_CLOSE)PartialStream_Close; +                    return true; +                } + +                // Free the part map +                STORM_FREE(pStream->PartMap); +                pStream->PartMap = NULL; +            } +        } +    } + +    SetLastError(ERROR_BAD_FORMAT); +    return false; +} + +//----------------------------------------------------------------------------- +// Local functions - encrypted stream support + +static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; + +static const char * AuthCodeArray[] = +{ +    // Diablo III: Agent.exe (1.0.0.954) +    // Address of decryption routine: 00502b00                              +    // Pointer to decryptor object: ECX +    // Pointer to key: ECX+0x5C +    // Authentication code URL: http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt +    //                                                                                           -0C-    -1C--08-    -18--04-    -14--00-    -10- +    "UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK",         // Diablo III Installer (deDE): "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX" +    "MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP",         // Diablo III Installer (enGB): "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ" +    "8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP",         // Diablo III Installer (enSG): "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS" +    "EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ",         // Diablo III Installer (enUS): "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG" +    "PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT",         // Diablo III Installer (esES): "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3" +    "X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2",         // Diablo III Installer (esMX): "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS" +    "5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2",         // Diablo III Installer (frFR): "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5" +    "478JD2K56EVNVVY4XX8TDWYT5B8KB254",         // Diablo III Installer (itIT): "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T" +    "8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A",         // Diablo III Installer (koKR): "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH" +    "LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB",         // Diablo III Installer (plPL): "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK" +    "K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG",         // Diablo III Installer (ptBR): "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB" +    "NDVW8GWLAYCRPGRNY8RT7ZZUQU63VLPR",         // Diablo III Installer (ruRU): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +    "6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H",         // Diablo III Installer (zhTW): "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8" +//  "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",         // Diablo III Installer (zhCN): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + +    // Note: Starcraft II (Wings of Liberty): Installer.exe (4.1.1.4219) +    // Address of decryption routine: 0053A3D0 +    // Pointer to decryptor object: ECX +    // Pointer to key: ECX+0x5C +    // Authentication code URL: http://dist.blizzard.com/mediakey/sc2-authenticationcode-enUS.txt +    //                                                                                          -0C-    -1C--08-    -18--04-    -14--00-    -10- +    "Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX",         // SC2 Wings of Liberty (deDE): "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V" +    "G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH",         // SC2 Wings of Liberty (enGB): "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D" +    "W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW",         // SC2 Wings of Liberty (enSG): "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE" +    "3DH5RE5NVM5GTFD85LXGWT6FK859ETR5",         // SC2 Wings of Liberty (enUS): "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG" +    "8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ",         // SC2 Wings of Liberty (esES): "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P" +    "A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54",         // SC2 Wings of Liberty (esMX): "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U" +    "ZG7J9K938HJEFWPQUA768MA2PFER6EAJ",         // SC2 Wings of Liberty (frFR): "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76" +    "NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2",         // SC2 Wings of Liberty (itIT): "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B" +    "3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F",         // SC2 Wings of Liberty (koKR): "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA" +    "2NSFB8MELULJ83U6YHA3UP6K4MQD48L6",         // SC2 Wings of Liberty (plPL): "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3" +    "QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E",         // SC2 Wings of Liberty (ptBR): "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX" +    "VHB378W64BAT9SH7D68VV9NLQDK9YEGT",         // SC2 Wings of Liberty (ruRU): "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V" +    "U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE",         // SC2 Wings of Liberty (zhTW): "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ" + +    NULL +}; + +static DWORD Rol32(DWORD dwValue, DWORD dwRolCount) +{ +    DWORD dwShiftRight = 32 - dwRolCount; + +    return (dwValue << dwRolCount) | (dwValue >> dwShiftRight); +} + +static void CreateKeyFromAuthCode( +    LPBYTE pbKeyBuffer, +    const char * szAuthCode) +{ +    LPDWORD KeyPosition = (LPDWORD)(pbKeyBuffer + 0x10); +    LPDWORD AuthCode32 = (LPDWORD)szAuthCode; + +    memcpy(pbKeyBuffer, szKeyTemplate, MPQE_CHUNK_SIZE); +    KeyPosition[0x00] = AuthCode32[0x03]; +    KeyPosition[0x02] = AuthCode32[0x07]; +    KeyPosition[0x03] = AuthCode32[0x02]; +    KeyPosition[0x05] = AuthCode32[0x06]; +    KeyPosition[0x06] = AuthCode32[0x01]; +    KeyPosition[0x08] = AuthCode32[0x05]; +    KeyPosition[0x09] = AuthCode32[0x00]; +    KeyPosition[0x0B] = AuthCode32[0x04]; +    BSWAP_ARRAY32_UNSIGNED(pbKeyBuffer, MPQE_CHUNK_SIZE); +} + +static void DecryptFileChunk( +    DWORD * MpqData, +    LPBYTE pbKey, +    ULONGLONG ByteOffset, +    DWORD dwLength) +{ +    ULONGLONG ChunkOffset; +    DWORD KeyShuffled[0x10]; +    DWORD KeyMirror[0x10]; +    DWORD RoundCount = 0x14; + +    // Prepare the key +    ChunkOffset = ByteOffset / MPQE_CHUNK_SIZE; +    memcpy(KeyMirror, pbKey, MPQE_CHUNK_SIZE); +    BSWAP_ARRAY32_UNSIGNED(KeyMirror, MPQE_CHUNK_SIZE); +    KeyMirror[0x05] = (DWORD)(ChunkOffset >> 32); +    KeyMirror[0x08] = (DWORD)(ChunkOffset); + +    while(dwLength >= MPQE_CHUNK_SIZE) +    { +        // Shuffle the key - part 1 +        KeyShuffled[0x0E] = KeyMirror[0x00]; +        KeyShuffled[0x0C] = KeyMirror[0x01]; +        KeyShuffled[0x05] = KeyMirror[0x02]; +        KeyShuffled[0x0F] = KeyMirror[0x03]; +        KeyShuffled[0x0A] = KeyMirror[0x04]; +        KeyShuffled[0x07] = KeyMirror[0x05]; +        KeyShuffled[0x0B] = KeyMirror[0x06]; +        KeyShuffled[0x09] = KeyMirror[0x07]; +        KeyShuffled[0x03] = KeyMirror[0x08]; +        KeyShuffled[0x06] = KeyMirror[0x09]; +        KeyShuffled[0x08] = KeyMirror[0x0A]; +        KeyShuffled[0x0D] = KeyMirror[0x0B]; +        KeyShuffled[0x02] = KeyMirror[0x0C]; +        KeyShuffled[0x04] = KeyMirror[0x0D]; +        KeyShuffled[0x01] = KeyMirror[0x0E]; +        KeyShuffled[0x00] = KeyMirror[0x0F]; +         +        // Shuffle the key - part 2 +        for(DWORD i = 0; i < RoundCount; i += 2) +        { +            KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x02]), 0x07); +            KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0E]), 0x09); +            KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x0A]), 0x0D); +            KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x03]), 0x12); + +            KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x04]), 0x07); +            KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x0C]), 0x09); +            KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x07]), 0x0D); +            KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x06]), 0x12); + +            KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x01]), 0x07); +            KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x05]), 0x09); +            KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x0B]), 0x0D); +            KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x08]), 0x12); + +            KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x00]), 0x07); +            KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x0F]), 0x09); +            KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x09]), 0x0D); +            KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x0D]), 0x12); + +            KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x09]), 0x07); +            KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x0E]), 0x09); +            KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x04]), 0x0D); +            KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x08]), 0x12); + +            KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x0A]), 0x07); +            KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x0C]), 0x09); +            KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x01]), 0x0D); +            KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0D]), 0x12); + +            KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x07]), 0x07); +            KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x05]), 0x09); +            KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x00]), 0x0D); +            KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x03]), 0x12); + +            KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x0B]), 0x07); +            KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x0F]), 0x09); +            KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x02]), 0x0D); +            KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x06]), 0x12); +        } + +        // Decrypt one data chunk +        BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE); +        MpqData[0x00] = MpqData[0x00] ^ (KeyShuffled[0x0E] + KeyMirror[0x00]); +        MpqData[0x01] = MpqData[0x01] ^ (KeyShuffled[0x04] + KeyMirror[0x0D]); +        MpqData[0x02] = MpqData[0x02] ^ (KeyShuffled[0x08] + KeyMirror[0x0A]); +        MpqData[0x03] = MpqData[0x03] ^ (KeyShuffled[0x09] + KeyMirror[0x07]); +        MpqData[0x04] = MpqData[0x04] ^ (KeyShuffled[0x0A] + KeyMirror[0x04]); +        MpqData[0x05] = MpqData[0x05] ^ (KeyShuffled[0x0C] + KeyMirror[0x01]); +        MpqData[0x06] = MpqData[0x06] ^ (KeyShuffled[0x01] + KeyMirror[0x0E]); +        MpqData[0x07] = MpqData[0x07] ^ (KeyShuffled[0x0D] + KeyMirror[0x0B]); +        MpqData[0x08] = MpqData[0x08] ^ (KeyShuffled[0x03] + KeyMirror[0x08]); +        MpqData[0x09] = MpqData[0x09] ^ (KeyShuffled[0x07] + KeyMirror[0x05]); +        MpqData[0x0A] = MpqData[0x0A] ^ (KeyShuffled[0x05] + KeyMirror[0x02]); +        MpqData[0x0B] = MpqData[0x0B] ^ (KeyShuffled[0x00] + KeyMirror[0x0F]); +        MpqData[0x0C] = MpqData[0x0C] ^ (KeyShuffled[0x02] + KeyMirror[0x0C]); +        MpqData[0x0D] = MpqData[0x0D] ^ (KeyShuffled[0x06] + KeyMirror[0x09]); +        MpqData[0x0E] = MpqData[0x0E] ^ (KeyShuffled[0x0B] + KeyMirror[0x06]); +        MpqData[0x0F] = MpqData[0x0F] ^ (KeyShuffled[0x0F] + KeyMirror[0x03]); +        BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE); + +        // Update byte offset in the key +        KeyMirror[0x08]++; +        if(KeyMirror[0x08] == 0) +            KeyMirror[0x05]++; + +        // Move pointers and decrease number of bytes to decrypt +        MpqData  += (MPQE_CHUNK_SIZE / sizeof(DWORD)); +        dwLength -= MPQE_CHUNK_SIZE; +    } +} + +static bool DetectFileKey(LPBYTE pbKeyBuffer, LPBYTE pbEncryptedHeader) +{ +    ULONGLONG ByteOffset = 0; +    BYTE FileHeader[MPQE_CHUNK_SIZE]; + +    // We just try all known keys one by one +    for(int i = 0; AuthCodeArray[i] != NULL; i++) +    { +        // Prepare they decryption key from game serial number +        CreateKeyFromAuthCode(pbKeyBuffer, AuthCodeArray[i]); + +        // Try to decrypt with the given key  +        memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE); +        DecryptFileChunk((LPDWORD)FileHeader, pbKeyBuffer, ByteOffset, MPQE_CHUNK_SIZE); + +        // We check the decrypted data +        // All known encrypted MPQs have header at the begin of the file, +        // so we check for MPQ signature there. +        if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q') +            return true; +    } + +    // Key not found, sorry +    return false; +} + +static bool EncryptedStream_Read( +    TEncryptedStream * pStream,             // Pointer to an open stream +    ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                        // Pointer to data to be read +    DWORD dwBytesToRead)                    // Number of bytes to read from the file +{ +    ULONGLONG StartOffset;                  // Offset of the first byte to be read from the file +    ULONGLONG ByteOffset;                   // Offset that the caller wants +    ULONGLONG EndOffset;                    // End offset that is to be read from the file +    DWORD dwBytesToAllocate; +    DWORD dwBytesToDecrypt; +    DWORD dwOffsetInCache; +    LPBYTE pbMpqData = NULL; +    bool bResult = false; + +    // Get the byte offset +    if(pByteOffset == NULL) +        pStream->BaseGetPos(pStream, &ByteOffset); +    else +        ByteOffset = *pByteOffset; + +    // Cut it down to MPQE chunk size +    StartOffset = ByteOffset; +    StartOffset = StartOffset & ~(MPQE_CHUNK_SIZE - 1); +    EndOffset = ByteOffset + dwBytesToRead; + +    // Calculate number of bytes to decrypt +    dwBytesToDecrypt = (DWORD)(EndOffset - StartOffset); +    dwBytesToAllocate = (dwBytesToDecrypt + (MPQE_CHUNK_SIZE - 1)) & ~(MPQE_CHUNK_SIZE - 1); + +    // Allocate buffers for encrypted and decrypted data +    pbMpqData = STORM_ALLOC(BYTE, dwBytesToAllocate); +    if(pbMpqData) +    { +        // Get the offset of the desired data in the cache +        dwOffsetInCache = (DWORD)(ByteOffset - StartOffset); + +        // Read the file from the stream as-is +        if(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) +        { +            // Decrypt the data +            DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate); + +            // Copy the decrypted data +            memcpy(pvBuffer, pbMpqData + dwOffsetInCache, dwBytesToRead); +            bResult = true; +        } +        else +        { +            assert(false); +        } + +        // Free decryption buffer         +        STORM_FREE(pbMpqData); +    } + +    // Free buffers and exit +    return bResult; +} + +static bool EncryptedStream_Open(TEncryptedStream * pStream) +{ +    ULONGLONG ByteOffset = 0; +    BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; + +    // Sanity check +    assert(pStream->BaseRead != NULL); + +    // Load one MPQE chunk and try to detect the file key +    if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) +    { +        // Attempt to decrypt the MPQ header with all known keys +        if(DetectFileKey(pStream->Key, EncryptedHeader)) +        { +            // Assign functions +            pStream->StreamRead    = (STREAM_READ)EncryptedStream_Read; +            pStream->StreamGetPos  = pStream->BaseGetPos; +            pStream->StreamGetSize = pStream->BaseGetSize; +            pStream->StreamGetTime = pStream->BaseGetTime; +            pStream->StreamGetBmp  = (STREAM_GETBMP)Dummy_GetBitmap; +            pStream->StreamClose   = pStream->BaseClose; + +            // We need to reset the position back to the begin of the file +            pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, 0); +            return true; +        } + +        // An unknown key +        SetLastError(ERROR_UNKNOWN_FILE_KEY); +    } +    return false; +} + +//----------------------------------------------------------------------------- +// Public functions + +/** + * This function creates a new file for read-write access + * + * - If the current platform supports file sharing, + *   the file must be created for read sharing (i.e. another application + *   can open the file for read, but not for write) + * - If the file does not exist, the function must create new one + * - If the file exists, the function must rewrite it and set to zero size + * - The parameters of the function must be validate by the caller + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + *   and free all memory that has been allocated in the process of stream creation, + *   including the TFileStream structure itself + * + * \a szFileName Name of the file to create + */ + +TFileStream * FileStream_CreateFile( +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +    TFileStream * pStream; + +    // We only support creation of linear, local file +    if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) +    { +        SetLastError(ERROR_NOT_SUPPORTED); +        return NULL; +    } + +    // Allocate file stream structure for linear stream +    pStream = STORM_ALLOC(TFileStream, 1); +    if(pStream != NULL) +    { +        // Reset entire structure to zero +        memset(pStream, 0, sizeof(TFileStream)); +        _tcscpy(pStream->szFileName, szFileName); + +        // Attempt to create the disk file +        if(BaseFile_Create(pStream, szFileName, dwStreamFlags)) +        { +            // Fill the stream provider functions +            pStream->StreamRead    = pStream->BaseRead; +            pStream->StreamWrite   = pStream->BaseWrite; +            pStream->StreamGetPos  = pStream->BaseGetPos; +            pStream->StreamGetSize = pStream->BaseGetSize; +            pStream->StreamSetSize = pStream->BaseSetSize; +            pStream->StreamGetTime = pStream->BaseGetTime; +            pStream->StreamGetBmp  = (STREAM_GETBMP)Dummy_GetBitmap;; +            pStream->StreamSwitch  = (STREAM_SWITCH)LinearStream_Switch; +            pStream->StreamClose   = pStream->BaseClose; +            return pStream; +        } + +        // File create failed, delete the stream +        STORM_FREE(pStream); +        pStream = NULL; +    } + +    // Return the stream +    return pStream; +} + +/** + * This function opens an existing file for read or read-write access + * - If the current platform supports file sharing, + *   the file must be open for read sharing (i.e. another application + *   can open the file for read, but not for write) + * - If the file does not exist, the function must return NULL + * - If the file exists but cannot be open, then function must return NULL + * - The parameters of the function must be validate by the caller + * - The function must check if the file is a PART file, + *   and create TPartialStream object if so. + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + *   and free all memory that has been allocated in the process of stream creation, + *   including the TFileStream structure itself + * + * \a szFileName Name of the file to open + * \a dwStreamFlags specifies the provider and base storage type + */ + +TFileStream * FileStream_OpenFile( +    const TCHAR * szFileName, +    DWORD dwStreamFlags) +{ +    TFileStream * pStream = NULL; +    size_t StreamSize = 0; +    bool bStreamResult = false; +    bool bBaseResult = false; + +    // Allocate file stream for each stream provider +    switch(dwStreamFlags & STREAM_PROVIDER_MASK) +    { +        case STREAM_PROVIDER_LINEAR:    // Allocate structure for linear stream  +            StreamSize = sizeof(TLinearStream); +            break; + +        case STREAM_PROVIDER_PARTIAL: +            dwStreamFlags |= STREAM_FLAG_READ_ONLY; +            StreamSize = sizeof(TPartialStream); +            break; + +        case STREAM_PROVIDER_ENCRYPTED: +            dwStreamFlags |= STREAM_FLAG_READ_ONLY; +            StreamSize = sizeof(TEncryptedStream); +            break; + +        default: +            return NULL; +    } + +    // Allocate the stream for each type +    pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize); +    if(pStream == NULL) +        return NULL; + +    // Fill the stream structure with zeros +    memset(pStream, 0, StreamSize); +    _tcscpy(pStream->szFileName, szFileName); + +    // Now initialize the respective base provider +    switch(dwStreamFlags & BASE_PROVIDER_MASK) +    { +        case BASE_PROVIDER_FILE: +            bBaseResult = BaseFile_Open(pStream, szFileName, dwStreamFlags); +            break; + +        case BASE_PROVIDER_MAP: +            dwStreamFlags |= STREAM_FLAG_READ_ONLY; +            bBaseResult = BaseMap_Open(pStream, szFileName, dwStreamFlags); +            break; + +        case BASE_PROVIDER_HTTP: +            dwStreamFlags |= STREAM_FLAG_READ_ONLY; +            bBaseResult = BaseHttp_Open(pStream, szFileName, dwStreamFlags); +            break; +    } + +    // If we failed to open the base storage, fail the operation +    if(bBaseResult == false) +    { +        STORM_FREE(pStream); +        return NULL; +    } + +    // Now initialize the stream provider +    switch(dwStreamFlags & STREAM_PROVIDER_MASK) +    { +        case STREAM_PROVIDER_LINEAR: +            bStreamResult = LinearStream_Open((TLinearStream *)pStream); +            break; + +        case STREAM_PROVIDER_PARTIAL: +            bStreamResult = PartialStream_Open((TPartialStream *)pStream); +            break; + +        case STREAM_PROVIDER_ENCRYPTED: +            bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream); +            break; +    } + +    // If the operation failed, free the stream and set it to NULL +    if(bStreamResult == false) +    { +        // Only close the base stream +        pStream->BaseClose(pStream); +        STORM_FREE(pStream); +        pStream = NULL; +    } + +    return pStream; +} + +/** + * Reads data from the stream + * + * - Returns true if the read operation succeeded and all bytes have been read + * - Returns false if either read failed or not all bytes have been read + * - If the pByteOffset is NULL, the function must read the data from the current file position + * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored + *   and the function just adjusts file pointer. + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be read + * \a dwBytesToRead Number of bytes to read from the file + * + * \returns + * - If the function reads the required amount of bytes, it returns true. + * - If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF + * - If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF + */ +bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) +{ +    assert(pStream->StreamRead != NULL); +    return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); +} + +/** + * This function writes data to the stream + * + * - Returns true if the write operation succeeded and all bytes have been written + * - Returns false if either write failed or not all bytes have been written + * - If the pByteOffset is NULL, the function must write the data to the current file position + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ +bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) +{ +    if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +        return false; + +    assert(pStream->StreamWrite != NULL); +    return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); +} + +/** + * This function returns the current file position + * \a pStream + * \a ByteOffset + */ +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ +    assert(pStream->StreamGetPos != NULL); +    return pStream->StreamGetPos(pStream, pByteOffset); +} + +/** + * Returns the size of a file + * + * \a pStream Pointer to an open stream + * \a FileSize Pointer where to store the file size + */ +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ +    assert(pStream->StreamGetSize != NULL); +    return pStream->StreamGetSize(pStream, pFileSize); +} + +/** + * Sets the size of a file + * + * \a pStream Pointer to an open stream + * \a NewFileSize File size to set + */ +bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +{                                  +    if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +        return false; + +    assert(pStream->StreamSetSize != NULL); +    return pStream->StreamSetSize(pStream, NewFileSize); +} + +/** + * Returns the last write time of a file + * + * \a pStream Pointer to an open stream + * \a pFileType Pointer where to store the file last write time + */ +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ +    assert(pStream->StreamGetTime != NULL); +    return pStream->StreamGetTime(pStream, pFileTime); +} + +/** + * Returns the stream flags + * + * \a pStream Pointer to an open stream + * \a pdwStreamFlags Pointer where to store the stream flags + */ +bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags) +{ +    *pdwStreamFlags = pStream->dwFlags; +    return true; +} + +/** + * Switches a stream with another. Used for final phase of archive compacting. + * Performs these steps: + * + * 1) Closes the handle to the existing MPQ + * 2) Renames the temporary MPQ to the original MPQ, overwrites existing one + * 3) Opens the MPQ stores the handle and stream position to the new stream structure + * + * \a pStream Pointer to an open stream + * \a pTempStream Temporary ("working") stream (created during archive compacting) + */ +bool FileStream_Switch(TFileStream * pStream, TFileStream * pNewStream) +{ +    if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) +        return false; + +    assert(pStream->StreamSwitch != NULL); +    return pStream->StreamSwitch(pStream, pNewStream); +} + +/** + * Returns the file name of the stream + * + * \a pStream Pointer to an open stream + */ +TCHAR * FileStream_GetFileName(TFileStream * pStream) +{ +    assert(pStream != NULL); +    return pStream->szFileName; +} + +/** + * Returns true if the stream is read-only + * + * \a pStream Pointer to an open stream + */ +bool FileStream_IsReadOnly(TFileStream * pStream) +{ +    return (pStream->dwFlags & STREAM_FLAG_READ_ONLY) ? true : false; +} + +/** + * This function enabled a linear stream to include data bitmap. + * Used by MPQs v 4.0 from WoW. Each file block is represented by + * a bit in the bitmap. 1 means the block is present, 0 means it's not. + * + * \a pStream Pointer to an open stream + * \a pBitmap Pointer to file bitmap + */ + +bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap) +{ +    TLinearStream * pLinearStream; + +    // It must be a linear stream. +    if((pStream->dwFlags & STREAM_PROVIDER_MASK) != STREAM_PROVIDER_LINEAR) +        return false; +    pLinearStream = (TLinearStream *)pStream; + +    // Two bitmaps are not allowed +    if(pLinearStream->pBitmap != NULL) +        return false; + +    // We need to change some entry points +    pLinearStream->StreamRead   = (STREAM_READ)LinearStream_Read; +    pLinearStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBitmap; + +    // Using data bitmap renders the stream to be read only. +    pLinearStream->dwFlags |= STREAM_FLAG_READ_ONLY; +    pLinearStream->pBitmap = pBitmap; +    return true; +} + +/** + * This function retrieves the file bitmap. A file bitmap is an array + * of bits, each bit representing one file block. A value of 1 means + * that the block is present in the file, a value of 0 means that the + * block is not present. + * + * \a pStream Pointer to an open stream + * \a pBitmap Pointer to buffer where to store the file bitmap + * \a Length  Size of buffer pointed by pBitmap, in bytes + * \a LengthNeeded If non-NULL, the function supplies the necessary byte size of the buffer + */ +bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded) +{ +    assert(pStream->StreamGetBmp != NULL); +    return pStream->StreamGetBmp(pStream, pBitmap, Length, LengthNeeded); +} + +/** + * This function closes an archive file and frees any data buffers + * that have been allocated for stream management. The function must also + * support partially allocated structure, i.e. one or more buffers + * can be NULL, if there was an allocation failure during the process + * + * \a pStream Pointer to an open stream + */ +void FileStream_Close(TFileStream * pStream) +{ +    // Check if the stream structure is allocated at all +    if(pStream != NULL) +    { +        // Close the stream provider. +        // This will also close the base stream +        assert(pStream->StreamClose != NULL); +        pStream->StreamClose(pStream); + +        // Free the stream itself +        STORM_FREE(pStream); +    } +} + +//----------------------------------------------------------------------------- +// main - for testing purposes + +#ifdef __STORMLIB_TEST__ +int FileStream_Test(const TCHAR * szFileName, DWORD dwStreamFlags) +{ +    TFileStream * pStream; +    TMPQHeader MpqHeader; +    ULONGLONG FilePos; +    TMPQBlock * pBlock; +    TMPQHash * pHash; + +    InitializeMpqCryptography(); + +    pStream = FileStream_OpenFile(szFileName, dwStreamFlags); +    if(pStream == NULL) +        return GetLastError(); + +    // Read the MPQ header +    FileStream_Read(pStream, NULL, &MpqHeader, MPQ_HEADER_SIZE_V2); +    if(MpqHeader.dwID != ID_MPQ) +        return ERROR_FILE_CORRUPT; + +    // Read the hash table +    pHash = STORM_ALLOC(TMPQHash, MpqHeader.dwHashTableSize); +    if(pHash != NULL) +    { +        FilePos = MpqHeader.dwHashTablePos; +        FileStream_Read(pStream, &FilePos, pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash)); +        DecryptMpqBlock(pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash), MPQ_KEY_HASH_TABLE); +        STORM_FREE(pHash); +    } + +    // Read the block table +    pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize); +    if(pBlock != NULL) +    { +        FilePos = MpqHeader.dwBlockTablePos; +        FileStream_Read(pStream, &FilePos, pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock)); +        DecryptMpqBlock(pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock), MPQ_KEY_BLOCK_TABLE); +        STORM_FREE(pBlock); +    } + +    FileStream_Close(pStream); +    return ERROR_SUCCESS; +} +#endif + +/* +int FileStream_Test() +{ +    TFileStream * pStream; + +    InitializeMpqCryptography(); + +    // +    // Test 1: Write to a stream +    // + +    pStream = FileStream_CreateFile("E:\\Stream.bin", 0); +    if(pStream != NULL) +    { +        char szString1[100] = "This is a single line\n\r"; +        DWORD dwLength = strlen(szString1); + +        for(int i = 0; i < 10; i++) +        { +            if(!FileStream_Write(pStream, NULL, szString1, dwLength)) +            { +                printf("Failed to write to the stream\n"); +                return ERROR_CAN_NOT_COMPLETE; +            } +        } +        FileStream_Close(pStream); +    } + +    // +    // Test2: Read from the stream +    // + +    pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +    if(pStream != NULL) +    { +        char szString1[100] = "This is a single line\n\r"; +        char szString2[100]; +        DWORD dwLength = strlen(szString1); + +        // This call must end with an error +        if(FileStream_Write(pStream, NULL, "aaa", 3)) +        { +            printf("Write succeeded while it should fail\n"); +            return -1; +        } + +        for(int i = 0; i < 10; i++) +        { +            if(!FileStream_Read(pStream, NULL, szString2, dwLength)) +            { +                printf("Failed to read from the stream\n"); +                return -1; +            } + +            szString2[dwLength] = 0; +            if(strcmp(szString1, szString2)) +            { +                printf("Data read from file are different from data written\n"); +                return -1; +            } +        } +        FileStream_Close(pStream); +    } + +    // +    // Test3: Open the temp stream, write some data and switch it to the original stream +    // + +    pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +    if(pStream != NULL) +    { +        TFileStream * pTempStream; +        ULONGLONG FileSize; + +        pTempStream = FileStream_CreateFile("E:\\TempStream.bin", 0); +        if(pTempStream == NULL) +        { +            printf("Failed to create temp stream\n"); +            return -1; +        } + +        // Copy the original stream to the temp +        if(!FileStream_GetSize(pStream, FileSize)) +        { +            printf("Failed to get the file size\n"); +            return -1; +        } + +        while(FileSize != 0) +        { +            DWORD dwBytesToRead = (DWORD)FileSize; +            char Buffer[0x80]; + +            if(dwBytesToRead > sizeof(Buffer)) +                dwBytesToRead = sizeof(Buffer); + +            if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead)) +            { +                printf("CopyStream: Read source file failed\n"); +                return -1; +            } + +            if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead)) +            { +                printf("CopyStream: Write target file failed\n"); +                return -1; +            } + +            FileSize -= dwBytesToRead; +        } + +        // Switch the streams +        // Note that the pTempStream is closed by the operation +        FileStream_Switch(pStream, pTempStream); +        FileStream_Close(pStream); +    } + +    // +    // Test4: Read from the stream again +    // + +    pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +    if(pStream != NULL) +    { +        char szString1[100] = "This is a single line\n\r"; +        char szString2[100]; +        DWORD dwLength = strlen(szString1); + +        for(int i = 0; i < 10; i++) +        { +            if(!FileStream_Read(pStream, NULL, szString2, dwLength)) +            { +                printf("Failed to read from the stream\n"); +                return -1; +            } + +            szString2[dwLength] = 0; +            if(strcmp(szString1, szString2)) +            { +                printf("Data read from file are different from data written\n"); +                return -1; +            } +        } +        FileStream_Close(pStream); +    } + +    return 0; +} +*/ + diff --git a/src/FileStream.h b/src/FileStream.h new file mode 100644 index 0000000..d00c82e --- /dev/null +++ b/src/FileStream.h @@ -0,0 +1,189 @@ +/*****************************************************************************/ +/* FileStream.h                           Copyright (c) Ladislav Zezula 2012 */ +/*---------------------------------------------------------------------------*/ +/* Description: Definitions for FileStream object                            */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 14.04.12  1.00  Lad  The first version of FileStream.h                    */ +/*****************************************************************************/ + +#ifndef __FILESTREAM_H__ +#define __FILESTREAM_H__ + +//----------------------------------------------------------------------------- +// Function prototypes + +typedef bool (*STREAM_READ)( +    struct TFileStream * pStream,       // Pointer to an open stream +    ULONGLONG * pByteOffset,            // Pointer to file byte offset. If NULL, it reads from the current position +    void * pvBuffer,                    // Pointer to data to be read +    DWORD dwBytesToRead                 // Number of bytes to read from the file +    ); + +typedef bool (*STREAM_WRITE)( +    struct TFileStream * pStream,       // Pointer to an open stream +    ULONGLONG * pByteOffset,            // Pointer to file byte offset. If NULL, it writes to the current position +    const void * pvBuffer,              // Pointer to data to be written +    DWORD dwBytesToWrite                // Number of bytes to read from the file +    ); + +typedef bool (*STREAM_GETPOS)( +    struct TFileStream * pStream,       // Pointer to an open stream +    ULONGLONG * pByteOffset             // Pointer to store current file position +    ); + +typedef bool (*STREAM_GETSIZE)( +    struct TFileStream * pStream,       // Pointer to an open stream +    ULONGLONG * pFileSize               // Receives the file size, in bytes +    ); + +typedef bool (*STREAM_SETSIZE)( +    struct TFileStream * pStream,       // Pointer to an open stream +    ULONGLONG FileSize                  // New size for the file, in bytes +    ); + +typedef bool (*STREAM_GETTIME)( +    struct TFileStream * pStream, +    ULONGLONG * pFT +    ); + +typedef bool (*STREAM_SWITCH)( +    struct TFileStream * pStream, +    struct TFileStream * pNewStream +    ); + +typedef bool (*STREAM_GETBMP)( +    TFileStream * pStream, +    TFileBitmap * pBitmap, +    DWORD Length, +    LPDWORD LengthNeeded +    ); + +typedef void (*STREAM_CLOSE)( +    struct TFileStream * pStream +    ); + +//----------------------------------------------------------------------------- +// Local structures - part file structure + +typedef struct _PART_FILE_HEADER +{ +    DWORD PartialVersion;                   // Always set to 2 +    char  GameBuildNumber[0x20];            // Minimum build number of the game that can use this MPQ +    DWORD Flags;                            // Flags (details unknown) +    DWORD FileSizeLo;                       // Low 32 bits of the contained file size +    DWORD FileSizeHi;                       // High 32 bits of the contained file size +    DWORD BlockSize;                        // Size of one file block, in bytes + +} PART_FILE_HEADER, *PPART_FILE_HEADER; + +// Structure describing the block-to-file map entry +typedef struct _PART_FILE_MAP_ENTRY +{ +    DWORD Flags;                            // 3 = the block is present in the file +    DWORD BlockOffsLo;                      // Low 32 bits of the block position in the file +    DWORD BlockOffsHi;                      // High 32 bits of the block position in the file +    DWORD LargeValueLo;                     // 64-bit value, meaning is unknown +    DWORD LargeValueHi; + +} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; + +//----------------------------------------------------------------------------- +// Local structures + +union TBaseData +{ +    struct +    { +        ULONGLONG FileSize;                 // Size of the file +        ULONGLONG FilePos;                  // Current file position +        ULONGLONG FileTime;                 // Date/time of last modification of the file +        HANDLE hFile;                       // File handle +    } File; + +    struct +    { +        ULONGLONG FileSize;                 // Mapped file size +        ULONGLONG FilePos;                  // Current stream position +        ULONGLONG FileTime;                 // Date/time of last modification of the file +        LPBYTE pbFile;                      // Pointer to mapped view +    } Map; + +    struct +    { +        ULONGLONG FileSize;                 // Size of the internet file +        ULONGLONG FilePos;                  // Current position in the file +        ULONGLONG FileTime;                 // Date/time of last modification of the file +        HANDLE hInternet;                   // Internet handle +        HANDLE hConnect;                    // Connection to the internet server +    } Http; +}; + +//----------------------------------------------------------------------------- +// Structure for linear stream + +struct TFileStream +{ +    // Stream provider functions +    STREAM_READ    StreamRead;              // Pointer to stream read function for this archive. Do not use directly. +    STREAM_WRITE   StreamWrite;             // Pointer to stream write function for this archive. Do not use directly. +    STREAM_GETPOS  StreamGetPos;            // Pointer to function that returns current file position +    STREAM_GETSIZE StreamGetSize;           // Pointer to function returning file size +    STREAM_SETSIZE StreamSetSize;           // Pointer to function changing file size +    STREAM_GETTIME StreamGetTime;           // Pointer to function retrieving the file time +    STREAM_GETBMP  StreamGetBmp;            // Pointer to function that retrieves the file bitmap +    STREAM_SWITCH  StreamSwitch;            // Pointer to function changing the stream to another file +    STREAM_CLOSE   StreamClose;             // Pointer to function closing the stream + +    // Stream provider data members +    TCHAR szFileName[MAX_PATH];             // File name +    DWORD dwFlags;                          // Stream flags + +    // Base provider functions +    STREAM_READ    BaseRead; +    STREAM_WRITE   BaseWrite; +    STREAM_GETPOS  BaseGetPos;              // Pointer to function that returns current file position +    STREAM_GETSIZE BaseGetSize;             // Pointer to function returning file size +    STREAM_SETSIZE BaseSetSize;             // Pointer to function changing file size +    STREAM_GETTIME BaseGetTime;             // Pointer to function retrieving the file time +    STREAM_CLOSE   BaseClose;               // Pointer to function closing the stream + +    // Base provider data members +    TBaseData Base;                         // Base provider data + +    // Followed by stream provider data, with variable length +}; + +//----------------------------------------------------------------------------- +// Structure for linear stream + +struct TLinearStream : public TFileStream +{ +    TFileBitmap * pBitmap;                  // Pointer to the stream bitmap +}; + +//----------------------------------------------------------------------------- +// Structure for partial stream + +struct TPartialStream : public TFileStream +{ +    ULONGLONG VirtualSize;                  // Virtual size of the file +    ULONGLONG VirtualPos;                   // Virtual position in the file +    DWORD     BlockCount;                   // Number of file blocks. Used by partial file stream +    DWORD     BlockSize;                    // Size of one block. Used by partial file stream + +    PPART_FILE_MAP_ENTRY PartMap;           // File map, variable length +}; + +//----------------------------------------------------------------------------- +// Structure for encrypted stream + +#define MPQE_CHUNK_SIZE 0x40                // Size of one chunk to be decrypted + +struct TEncryptedStream : public TFileStream +{ +    BYTE Key[MPQE_CHUNK_SIZE];              // File key +}; + +#endif // __FILESTREAM_H__ diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp new file mode 100644 index 0000000..b73a213 --- /dev/null +++ b/src/SBaseCommon.cpp @@ -0,0 +1,1736 @@ +/*****************************************************************************/ +/* SBaseCommon.cpp                        Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for StormLib, used by all SFile*** modules               */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 24.03.03  1.00  Lad  The first version of SFileCommon.cpp                 */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/* 12.06.04  1.01  Lad  Renamed to SCommon.cpp                               */ +/* 06.09.10  1.01  Lad  Renamed to SBaseCommon.cpp                           */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2012"; + +//----------------------------------------------------------------------------- +// Local variables + +LCID    lcFileLocale = LANG_NEUTRAL;            // File locale +USHORT  wPlatform = 0;                          // File platform + +//----------------------------------------------------------------------------- +// Conversion to uppercase/lowercase + +// This table converts ASCII characters to lowercase and slash to backslash +unsigned char AsciiToLowerTable[256] =  +{ +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,  +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,  +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,  +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,  +    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,  +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,  +    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,  +    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,  +    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,  +    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,  +    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,  +    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,  +    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,  +    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,  +    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, +    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +// This table converts ASCII characters to uppercase and slash to backslash +unsigned char AsciiToUpperTable[256] =  +{ +    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,  +    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,  +    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C,  +    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,  +    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,  +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,  +    0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,  +    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,  +    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,  +    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,  +    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,  +    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,  +    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,  +    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,  +    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, +    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +//----------------------------------------------------------------------------- +// Storm hashing functions + +#define STORM_BUFFER_SIZE       0x500 + +static DWORD StormBuffer[STORM_BUFFER_SIZE];    // Buffer for the decryption engine +static bool  bMpqCryptographyInitialized = false; + +DWORD HashString(const char * szFileName, DWORD dwHashType) +{ +    LPBYTE pbKey   = (BYTE *)szFileName; +    DWORD  dwSeed1 = 0x7FED7FED; +    DWORD  dwSeed2 = 0xEEEEEEEE; +    DWORD  ch; + +    while(*pbKey != 0) +    { +        ch = AsciiToUpperTable[*pbKey++]; + +        dwSeed1 = StormBuffer[dwHashType + ch] ^ (dwSeed1 + dwSeed2); +        dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; +    } + +    return dwSeed1; +} + +void InitializeMpqCryptography() +{ +    DWORD dwSeed = 0x00100001; +    DWORD index1 = 0; +    DWORD index2 = 0; +    int   i; + +    // Initialize the decryption buffer. +    // Do nothing if already done. +    if(bMpqCryptographyInitialized == false) +    { +        for(index1 = 0; index1 < 0x100; index1++) +        { +            for(index2 = index1, i = 0; i < 5; i++, index2 += 0x100) +            { +                DWORD temp1, temp2; + +                dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB; +                temp1  = (dwSeed & 0xFFFF) << 0x10; + +                dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB; +                temp2  = (dwSeed & 0xFFFF); + +                StormBuffer[index2] = (temp1 | temp2); +            } +        } + +        // Also register both MD5 and SHA1 hash algorithms +        register_hash(&md5_desc); +        register_hash(&sha1_desc); + +        // Use LibTomMath as support math library for LibTomCrypt +        ltc_mp = ltm_desc;     + +        // Don't do that again +        bMpqCryptographyInitialized = true; +    } +} + +//----------------------------------------------------------------------------- +// Calculates the hash table size for a given amount of files + +DWORD GetHashTableSizeForFileCount(DWORD dwFileCount) +{ +    DWORD dwPowerOfTwo; +     +    // Round the hash table size up to the nearest power of two +    for(dwPowerOfTwo = HASH_TABLE_SIZE_MIN; dwPowerOfTwo < HASH_TABLE_SIZE_MAX; dwPowerOfTwo <<= 1) +    { +        if(dwPowerOfTwo >= dwFileCount) +        { +            return dwPowerOfTwo; +        } +    } + +    // Don't allow the hash table size go over allowed maximum +    return HASH_TABLE_SIZE_MAX; +} + +//----------------------------------------------------------------------------- +// Calculates a Jenkin's Encrypting and decrypting MPQ file data + +ULONGLONG HashStringJenkins(const char * szFileName) +{ +    LPBYTE pbFileName = (LPBYTE)szFileName; +    char * szTemp; +    char szLocFileName[0x108]; +    size_t nLength = 0; +    unsigned int primary_hash = 1; +    unsigned int secondary_hash = 2; + +    // Normalize the file name - convert to uppercase, and convert "/" to "\\". +    if(pbFileName != NULL) +    { +        szTemp = szLocFileName; +        while(*pbFileName != 0) +            *szTemp++ = (char)AsciiToLowerTable[*pbFileName++]; +        *szTemp = 0; + +        nLength = szTemp - szLocFileName; +    } + +    // Thanks Quantam for finding out what the algorithm is. +    // I am really getting old for reversing large chunks of assembly +    // that does hashing :-) +    hashlittle2(szLocFileName, nLength, &secondary_hash, &primary_hash); + +    // Combine those 2 together +    return (ULONGLONG)primary_hash * (ULONGLONG)0x100000000ULL + (ULONGLONG)secondary_hash; +} + +//----------------------------------------------------------------------------- +// This function converts the MPQ header so it always looks like version 4 + +int ConvertMpqHeaderToFormat4( +    TMPQArchive * ha, +    ULONGLONG FileSize, +    DWORD dwFlags) +{ +    ULONGLONG ByteOffset; +    TMPQHeader * pHeader = ha->pHeader; +    DWORD dwExpectedArchiveSize; +    USHORT wFormatVersion = pHeader->wFormatVersion; +    int nError = ERROR_SUCCESS; + +    // If version 1.0 is forced, then the format version is forced to be 1.0 +    // Reason: Storm.dll in Warcraft III ignores format version value +    if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) +        wFormatVersion = MPQ_FORMAT_VERSION_1; + +    // Format-specific fixes +    switch(wFormatVersion) +    { +        case MPQ_FORMAT_VERSION_1: + +            // Check for malformed MPQ header version 1.0 +            if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) +            { +                pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; +                ha->dwFlags |= MPQ_FLAG_PROTECTED; +            } + +            // +            // The value of "dwArchiveSize" member in the MPQ header +            // is ignored by Storm.dll and can contain garbage value +            // ("w3xmaster" protector). +            // + +            dwExpectedArchiveSize = (DWORD)(FileSize - ha->MpqPos); +            if(pHeader->dwArchiveSize != dwExpectedArchiveSize) +            { +                // Note: dwExpectedArchiveSize might be incorrect at this point. +                // MPQs version 1.0 can have strong digital signature appended at the end, +                // or they might just have arbitrary data there. +                // In either case, we recalculate the archive size later when +                // block table is loaded and positions of all files is known. +                pHeader->dwArchiveSize = dwExpectedArchiveSize; +                ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; +            } + +            // Zero the fields in 2.0 part of the MPQ header +            pHeader->HiBlockTablePos64 = 0; +            pHeader->wHashTablePosHi = 0; +            pHeader->wBlockTablePosHi = 0; +            // No break here !!! + +        case MPQ_FORMAT_VERSION_2: +        case MPQ_FORMAT_VERSION_3: + +            // In MPQ format 3.0, the entire header is optional +            // and the size of the header can actually be identical +            // to size of header 2.0 +            if(pHeader->dwHeaderSize < MPQ_HEADER_SIZE_V3) +            { +                ULONGLONG ArchiveSize64 = pHeader->dwArchiveSize; + +                // In format 2.0, the archive size is obsolete and is calculated +                // as the highest offset of hash table, block table or hi-block table. +                // However, we can still rely on it, if the size of the archive is under 4 GB +                if((FileSize - ha->MpqPos) >> 32) +                { +                    ByteOffset = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + (pHeader->dwHashTableSize * sizeof(TMPQHash)); +                    if(ByteOffset > ArchiveSize64) +                        ArchiveSize64 = ByteOffset; + +                    ByteOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); +                    if(ByteOffset > ArchiveSize64) +                        ArchiveSize64 = ByteOffset; + +                    // Only if we actually have a hi-block table +                    if(pHeader->HiBlockTablePos64) +                    { +                        ByteOffset = pHeader->HiBlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(USHORT)); +                        if(ByteOffset > ArchiveSize64) +                            ArchiveSize64 = ByteOffset; +                    } + +                    // We need to recalculate archive size later, +                    // when block table is loaded and the position of files is known +                    ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; +                } + +                // Initialize the rest of the 3.0 header +                pHeader->ArchiveSize64 = ArchiveSize64; +                pHeader->HetTablePos64 = 0; +                pHeader->BetTablePos64 = 0; +            } + +            // +            // Calculate compressed size of each table. We assume the following order: +            // 1) HET table +            // 2) BET table +            // 3) Classic hash table +            // 4) Classic block table +            // 5) Hi-block table +            // + +            // Set all sizes to zero +            pHeader->HetTableSize64 = 0; +            pHeader->BetTableSize64 = 0; + +            // Either both HET and BET table exist or none of them does. +            if(pHeader->HetTablePos64) +            { +                // Compressed size of the HET and BET tables +                pHeader->HetTableSize64 = pHeader->BetTablePos64 - pHeader->HetTablePos64; +                pHeader->BetTableSize64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) - pHeader->HetTablePos64; +            } + +            // Compressed size of hash and block table +            if(wFormatVersion >= MPQ_FORMAT_VERSION_2) +            { +                // Compressed size of the hash table +                pHeader->HashTableSize64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) - MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + +                // Block and hi-block table +                if(pHeader->HiBlockTablePos64) +                { +                    pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); +                    pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; +                } +                else +                { +                    pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); +                    pHeader->HiBlockTableSize64 = 0; +                } +            } +            else +            { +                // No known MPQ in format 1.0 has any of the tables compressed +                pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); +                pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); +                pHeader->HiBlockTableSize64 = 0; +            } + +            // Set the data chunk size for MD5 to zero +            pHeader->dwRawChunkSize = 0; + +            // Fill the MD5's +            memset(pHeader->MD5_BlockTable,   0, MD5_DIGEST_SIZE); +            memset(pHeader->MD5_HashTable,    0, MD5_DIGEST_SIZE); +            memset(pHeader->MD5_HiBlockTable, 0, MD5_DIGEST_SIZE); +            memset(pHeader->MD5_BetTable,     0, MD5_DIGEST_SIZE); +            memset(pHeader->MD5_HetTable,     0, MD5_DIGEST_SIZE); +            memset(pHeader->MD5_MpqHeader,    0, MD5_DIGEST_SIZE); +            // No break here !!!! + +        case MPQ_FORMAT_VERSION_4: + +            // Verify header MD5. Header MD5 is calculated from the MPQ header since the 'MPQ\x1A' +            // signature until the position of header MD5 at offset 0xC0 +            if(!VerifyDataBlockHash(ha->pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, ha->pHeader->MD5_MpqHeader)) +                nError = ERROR_FILE_CORRUPT; +            break; +    } + +    return nError; +} + +//----------------------------------------------------------------------------- +// Default flags for (attributes) and (listfile) + +DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize) +{ +    // Fixed for format 1.0 +    if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) +        return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY; + +    // Size-dependent for formats 2.0-4.0 +    return (dwFileSize > 0x4000) ? (MPQ_FILE_COMPRESS | MPQ_FILE_SECTOR_CRC) : (MPQ_FILE_COMPRESS | MPQ_FILE_SINGLE_UNIT); +} + + +//----------------------------------------------------------------------------- +// Encrypting and decrypting MPQ file data + +void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) +{ +    LPDWORD block = (LPDWORD)pvFileBlock; +    DWORD dwSeed2 = 0xEEEEEEEE; +    DWORD ch; + +    // Round to DWORDs +    dwLength >>= 2; + +    while(dwLength-- > 0) +    { +        dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; +        ch     = *block; +        *block++ = ch ^ (dwSeed1 + dwSeed2); + +        dwSeed1  = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); +        dwSeed2  = ch + dwSeed2 + (dwSeed2 << 5) + 3; +    } +} + +void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) +{ +    LPDWORD block = (LPDWORD)pvFileBlock; +    DWORD dwSeed2 = 0xEEEEEEEE; +    DWORD ch; + +    // Round to DWORDs +    dwLength >>= 2; + +    while(dwLength-- > 0) +    { +        dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; +        ch     = *block ^ (dwSeed1 + dwSeed2); + +        dwSeed1  = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); +        dwSeed2  = ch + dwSeed2 + (dwSeed2 << 5) + 3; +        *block++ = ch; +    } +} + +/* +void EncryptMpqTable(void * pvMpqTable, DWORD dwLength, const char * szKey) +{ +    EncryptMpqBlock(pvMpqTable, dwLength, HashString(szKey, MPQ_HASH_FILE_KEY)); +} + +void DecryptMpqTable(void * pvMpqTable, DWORD dwLength, const char * szKey) +{ +    DecryptMpqBlock(pvMpqTable, dwLength, HashString(szKey, MPQ_HASH_FILE_KEY)); +} +*/ + +/** + * Functions tries to get file decryption key. The trick comes from sector + * positions which are stored at the begin of each compressed file. We know the + * file size, that means we know number of sectors that means we know the first + * DWORD value in sector position. And if we know encrypted and decrypted value, + * we can find the decryption key !!! + * + * hf            - MPQ file handle + * SectorOffsets - DWORD array of sector positions + * ch            - Decrypted value of the first sector pos + */ + +DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted) +{ +    DWORD saveKey1; +    DWORD temp = *SectorOffsets ^ decrypted;    // temp = seed1 + seed2 +    temp -= 0xEEEEEEEE;                 // temp = seed1 + StormBuffer[0x400 + (seed1 & 0xFF)] + +    for(int i = 0; i < 0x100; i++)      // Try all 255 possibilities +    { +        DWORD seed1; +        DWORD seed2 = 0xEEEEEEEE; +        DWORD ch; + +        // Try the first DWORD (We exactly know the value) +        seed1  = temp - StormBuffer[0x400 + i]; +        seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; +        ch     = SectorOffsets[0] ^ (seed1 + seed2); + +        if(ch != decrypted) +            continue; + +        // Add 1 because we are decrypting sector positions +        saveKey1 = seed1 + 1; + +        // If OK, continue and test the second value. We don't know exactly the value, +        // but we know that the second one has lower 16 bits set to zero +        // (no compressed sector is larger than 0xFFFF bytes) +        seed1  = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); +        seed2  = ch + seed2 + (seed2 << 5) + 3; + +        seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; +        ch     = SectorOffsets[1] ^ (seed1 + seed2); + +        if((ch & 0xFFFF0000) == 0) +            return saveKey1; +    } +    return 0; +} + +// Function tries to detect file encryption key. It expectes at least two uncompressed bytes +DWORD DetectFileKeyByKnownContent(void * pvFileContent, DWORD nDwords, ...) +{ +    LPDWORD pdwContent = (LPDWORD)pvFileContent; +    va_list argList; +    DWORD dwDecrypted[0x10]; +    DWORD saveKey1; +    DWORD dwTemp; +    DWORD i, j; +     +    // We need at least two DWORDS to detect the file key +    if(nDwords < 0x02 || nDwords > 0x10) +        return 0; +     +    va_start(argList, nDwords); +    for(i = 0; i < nDwords; i++) +        dwDecrypted[i] = va_arg(argList, DWORD); +    va_end(argList); +     +    dwTemp = (*pdwContent ^ dwDecrypted[0]) - 0xEEEEEEEE; +    for(i = 0; i < 0x100; i++)      // Try all 256 possibilities +    { +        DWORD seed1; +        DWORD seed2 = 0xEEEEEEEE; +        DWORD ch; + +        // Try the first DWORD +        seed1  = dwTemp - StormBuffer[0x400 + i]; +        seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; +        ch     = pdwContent[0] ^ (seed1 + seed2); + +        if(ch != dwDecrypted[0]) +            continue; + +        saveKey1 = seed1; + +        // If OK, continue and test all bytes. +        for(j = 1; j < nDwords; j++) +        { +            seed1  = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); +            seed2  = ch + seed2 + (seed2 << 5) + 3; + +            seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; +            ch     = pdwContent[j] ^ (seed1 + seed2); + +            if(ch == dwDecrypted[j] && j == nDwords - 1) +                return saveKey1; +        } +    } +    return 0; +} + +DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize) +{ +    DWORD dwFileKey; + +    // Try to break the file encryption key as if it was a WAVE file +    if(dwFileSize >= 0x0C) +    { +        dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 3, 0x46464952, dwFileSize - 8, 0x45564157); +        if(dwFileKey != 0) +            return dwFileKey; +    } + +    // Try to break the encryption key as if it was an EXE file +    if(dwFileSize > 0x40) +    { +        dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x00905A4D, 0x00000003); +        if(dwFileKey != 0) +            return dwFileKey; +    } + +    // Try to break the encryption key as if it was a XML file +    if(dwFileSize > 0x04) +    { +        dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x6D783F3C, 0x6576206C); +        if(dwFileKey != 0) +            return dwFileKey; +    } + +    // Not detected, sorry +    return 0; +} + +DWORD DecryptFileKey( +    const char * szFileName, +    ULONGLONG MpqPos, +    DWORD dwFileSize, +    DWORD dwFlags) +{ +    DWORD dwFileKey; +    DWORD dwMpqPos = (DWORD)MpqPos; + +    // File key is calculated from plain name +    szFileName = GetPlainFileNameA(szFileName); +    dwFileKey = HashString(szFileName, MPQ_HASH_FILE_KEY); + +    // Fix the key, if needed +    if(dwFlags & MPQ_FILE_FIX_KEY) +        dwFileKey = (dwFileKey + dwMpqPos) ^ dwFileSize; + +    // Return the key +    return dwFileKey; +} + +//----------------------------------------------------------------------------- +// Handle validation functions + +bool IsValidMpqHandle(TMPQArchive * ha) +{ +    if(ha == NULL) +        return false; +    if(ha->pHeader == NULL || ha->pHeader->dwID != ID_MPQ) +        return false; +     +    return (bool)(ha->pHeader->dwID == ID_MPQ); +} + +bool IsValidFileHandle(TMPQFile * hf) +{ +    if(hf == NULL) +        return false; + +    if(hf->dwMagic != ID_MPQ_FILE) +        return false; + +    if(hf->pStream != NULL) +        return true; + +    return IsValidMpqHandle(hf->ha); +} + +//----------------------------------------------------------------------------- +// Hash table and block table manipulation + +// Retrieves the first hash entry for the given file. +// Every locale version of a file has its own hash entry +TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName) +{ +    TMPQHash * pStartHash;                  // File hash entry (start) +    TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; +    TMPQHash * pHash;                       // File hash entry (current) +    DWORD dwHashTableSizeMask; +    DWORD dwIndex = HashString(szFileName, MPQ_HASH_TABLE_INDEX); +    DWORD dwName1 = HashString(szFileName, MPQ_HASH_NAME_A); +    DWORD dwName2 = HashString(szFileName, MPQ_HASH_NAME_B); + +    // Get the first possible has entry that might be the one +    dwHashTableSizeMask = ha->pHeader->dwHashTableSize ? (ha->pHeader->dwHashTableSize - 1) : 0; +    pStartHash = pHash = ha->pHashTable + (dwIndex & dwHashTableSizeMask); + +    // There might be deleted entries in the hash table prior to our desired entry. +    while(pHash->dwBlockIndex != HASH_ENTRY_FREE) +    { +        // If the entry agrees, we found it. +        if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->dwFileTableSize) +            return pHash; + +        // Move to the next hash entry. Stop searching +        // if we got reached the original hash entry +        if(++pHash >= pHashEnd) +            pHash = ha->pHashTable; +        if(pHash == pStartHash) +            break; +    } + +    // The apropriate hash entry was not found +    return NULL; +} + +TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash) +{ +    TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; +    TMPQHash * pHash = pPrevHash; +    DWORD dwName1 = pPrevHash->dwName1; +    DWORD dwName2 = pPrevHash->dwName2; + +    // Now go for any next entry that follows the pPrevHash, +    // until either free hash entry was found, or the start entry was reached +    for(;;) +    { +        // Move to the next hash entry. Stop searching +        // if we got reached the original hash entry +        if(++pHash >= pHashEnd) +            pHash = ha->pHashTable; +        if(pHash == pFirstHash) +            break; + +        // If the entry is a free entry, stop search +        if(pHash->dwBlockIndex == HASH_ENTRY_FREE) +            break; + +        // If the entry is not free and the name agrees, we found it +        if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->pHeader->dwBlockTableSize) +            return pHash; +    } + +    // No next entry +    return NULL; +} + +// Allocates an entry in the hash table +DWORD AllocateHashEntry( +    TMPQArchive * ha, +    TFileEntry * pFileEntry) +{ +    TMPQHash * pStartHash;                  // File hash entry (start) +    TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; +    TMPQHash * pHash;                       // File hash entry (current) +    DWORD dwHashTableSizeMask; +    DWORD dwIndex = HashString(pFileEntry->szFileName, MPQ_HASH_TABLE_INDEX); +    DWORD dwName1 = HashString(pFileEntry->szFileName, MPQ_HASH_NAME_A); +    DWORD dwName2 = HashString(pFileEntry->szFileName, MPQ_HASH_NAME_B); + +    // Get the first possible has entry that might be the one +    dwHashTableSizeMask = ha->pHeader->dwHashTableSize ? (ha->pHeader->dwHashTableSize - 1) : 0; +    pStartHash = pHash = ha->pHashTable + (dwIndex & dwHashTableSizeMask); + +    // There might be deleted entries in the hash table prior to our desired entry. +    while(pHash->dwBlockIndex < HASH_ENTRY_DELETED) +    { +        // If there already is an existing entry, reuse it. +        if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->lcLocale == pFileEntry->lcLocale) +            break; + +        // Move to the next hash entry. +        // If we reached the starting entry, it's failure. +        if(++pHash >= pHashEnd) +            pHash = ha->pHashTable; +        if(pHash == pStartHash) +            return HASH_ENTRY_FREE; +    } + +    // Fill the free hash entry +    pHash->dwName1      = dwName1; +    pHash->dwName2      = dwName2; +    pHash->lcLocale     = pFileEntry->lcLocale; +    pHash->wPlatform    = pFileEntry->wPlatform; +    pHash->dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); + +    // Fill the hash index in the file entry +    pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); +    return pFileEntry->dwHashIndex; +} + +// Finds a free space in the MPQ where to store next data +// The free space begins beyond the file that is stored at the fuhrtest +// position in the MPQ. +void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos) +{ +    TMPQHeader * pHeader = ha->pHeader; +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pFileEntry = ha->pFileTable; +    ULONGLONG FreeSpacePos = ha->pHeader->dwHeaderSize; +    DWORD dwChunkCount; + +    // Parse the entire block table +    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +    { +        // Only take existing files +        if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) +        { +            // If the end of the file is bigger than current MPQ table pos, update it +            if((pFileEntry->ByteOffset + pFileEntry->dwCmpSize) > FreeSpacePos) +            { +                // Get the end of the file data +                FreeSpacePos = pFileEntry->ByteOffset + pFileEntry->dwCmpSize; + +                // Add the MD5 chunks, if present +                if(pHeader->dwRawChunkSize != 0 && pFileEntry->dwCmpSize != 0) +                { +                    dwChunkCount = ((pFileEntry->dwCmpSize - 1) / pHeader->dwRawChunkSize) + 1; +                    FreeSpacePos += dwChunkCount * MD5_DIGEST_SIZE; +                } +            } +        } +    } + +    // Give the free space position to the caller +    if(pFreeSpacePos != NULL) +        *pFreeSpacePos = FreeSpacePos; +} + +//----------------------------------------------------------------------------- +// Common functions - MPQ File + +TMPQFile * CreateMpqFile(TMPQArchive * ha) +{ +    TMPQFile * hf; + +    // Allocate space for TMPQFile +    hf = STORM_ALLOC(TMPQFile, 1); +    if(hf != NULL) +    { +        // Fill the file structure +        memset(hf, 0, sizeof(TMPQFile)); +        hf->ha = ha; +        hf->pStream = NULL; +        hf->dwMagic = ID_MPQ_FILE; +    } + +    return hf; +} + +// Loads a table from MPQ. +// Can be used for hash table, block table, sector offset table or sector checksum table +int LoadMpqTable( +    TMPQArchive * ha, +    ULONGLONG ByteOffset, +    void * pvTable, +    DWORD dwCompressedSize, +    DWORD dwRealSize, +    DWORD dwKey) +{ +    LPBYTE pbCompressed = NULL; +    LPBYTE pbToRead = (LPBYTE)pvTable; +    int nError = ERROR_SUCCESS; + +    // "interface.MPQ.part" in trial version of World of Warcraft +    // has block table and hash table compressed. +    if(dwCompressedSize < dwRealSize) +    { +        // Allocate temporary buffer for holding compressed data +        pbCompressed = STORM_ALLOC(BYTE, dwCompressedSize); +        if(pbCompressed == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Assign the temporary buffer as target for read operation +        pbToRead = pbCompressed; +    } + +    // Read the table +    if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwCompressedSize)) +    { +        // First of all, decrypt the table +        if(dwKey != 0) +        { +            BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); +            DecryptMpqBlock(pbToRead, dwCompressedSize, dwKey); +            BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); +        } + +        // If the table is compressed, decompress it +        if(dwCompressedSize < dwRealSize) +        { +            int cbOutBuffer = (int)dwRealSize; +            int cbInBuffer = (int)dwCompressedSize; + +            if(!SCompDecompress2(pvTable, &cbOutBuffer, pbCompressed, cbInBuffer)) +                nError = GetLastError(); + +            // Free the temporary buffer +            STORM_FREE(pbCompressed); +        } +    } +    else +    { +        nError = GetLastError(); +    } + +    BSWAP_ARRAY32_UNSIGNED(pvTable, dwRealSize); +    return nError; +} + +void CalculateRawSectorOffset( +    ULONGLONG & RawFilePos,  +    TMPQFile * hf, +    DWORD dwSectorOffset) +{ +    // +    // Some MPQ protectors place the sector offset table after the actual file data. +    // Sector offsets in the sector offset table are negative. When added +    // to MPQ file offset from the block table entry, the result is a correct +    // position of the file data in the MPQ. +    // +    // The position of sector table must be always within the MPQ, however. +    // When a negative sector offset is found, we make sure that we make the addition +    // just in 32-bits, and then add the MPQ offset. +    // + +    if(dwSectorOffset & 0x80000000) +    { +        RawFilePos = hf->ha->MpqPos + ((DWORD)hf->pFileEntry->ByteOffset + dwSectorOffset); +    } +    else +    { +        RawFilePos = hf->RawFilePos + dwSectorOffset; +    } + +    // We also have to add patch header size, if patch header is present +    if(hf->pPatchInfo != NULL) +        RawFilePos += hf->pPatchInfo->dwLength; +} + +unsigned char * AllocateMd5Buffer( +    DWORD dwRawDataSize, +    DWORD dwChunkSize, +    LPDWORD pcbMd5Size) +{ +    unsigned char * md5_array; +    DWORD cbMd5Size; + +    // Sanity check +    assert(dwRawDataSize != 0); +    assert(dwChunkSize != 0); + +    // Calculate how many MD5's we will calculate +    cbMd5Size = (((dwRawDataSize - 1) / dwChunkSize) + 1) * MD5_DIGEST_SIZE; + +    // Allocate space for array or MD5s +    md5_array = STORM_ALLOC(BYTE, cbMd5Size); + +    // Give the size of the MD5 array +    if(pcbMd5Size != NULL) +        *pcbMd5Size = cbMd5Size; +    return md5_array; +} + +// Allocates sector buffer and sector offset table +int AllocateSectorBuffer(TMPQFile * hf) +{ +    TMPQArchive * ha = hf->ha; + +    // Caller of AllocateSectorBuffer must ensure these +    assert(hf->pbFileSector == NULL); +    assert(hf->pFileEntry != NULL); +    assert(hf->ha != NULL); + +    // Don't allocate anything if the file has zero size +    if(hf->pFileEntry->dwFileSize == 0 || hf->dwDataSize == 0) +        return ERROR_SUCCESS; + +    // Determine the file sector size and allocate buffer for it +    hf->dwSectorSize = (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) ? hf->dwDataSize : ha->dwSectorSize; +    hf->pbFileSector = STORM_ALLOC(BYTE, hf->dwSectorSize); +    hf->dwSectorOffs = SFILE_INVALID_POS; + +    // Return result +    return (hf->pbFileSector != NULL) ? (int)ERROR_SUCCESS : (int)ERROR_NOT_ENOUGH_MEMORY; +} + +// Allocates sector offset table +int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) +{ +    TMPQArchive * ha = hf->ha; +    DWORD dwLength = sizeof(TPatchInfo); + +    // The following conditions must be true +    assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE); +    assert(hf->pPatchInfo == NULL); + +__AllocateAndLoadPatchInfo: + +    // Allocate space for patch header. Start with default size, +    // and if its size if bigger, then we reload them +    hf->pPatchInfo = (TPatchInfo *)STORM_ALLOC(BYTE, dwLength); +    if(hf->pPatchInfo == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Do we have to load the patch header from the file ? +    if(bLoadFromFile) +    { +        // Load the patch header +        if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength)) +        { +            // Free the patch info +            STORM_FREE(hf->pPatchInfo); +            hf->pPatchInfo = NULL; +            return GetLastError(); +        } + +        // Perform necessary swapping +        hf->pPatchInfo->dwLength = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwLength); +        hf->pPatchInfo->dwFlags = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwFlags); +        hf->pPatchInfo->dwDataSize = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwDataSize); + +        // Verify the size of the patch header +        // If it's not default size, we have to reload them +        if(hf->pPatchInfo->dwLength > dwLength) +        { +            // Free the patch info +            dwLength = hf->pPatchInfo->dwLength; +            STORM_FREE(hf->pPatchInfo); +            hf->pPatchInfo = NULL; + +            // If the length is out of all possible ranges, fail the operation +            if(dwLength > 0x400) +                return ERROR_FILE_CORRUPT; +            goto __AllocateAndLoadPatchInfo; +        } + +        // Patch file data size according to the patch header +        hf->dwDataSize = hf->pPatchInfo->dwDataSize; +    } +    else +    { +        memset(hf->pPatchInfo, 0, dwLength); +    } + +    // Save the final length to the patch header +    hf->pPatchInfo->dwLength = dwLength; +    hf->pPatchInfo->dwFlags  = 0x80000000; +    return ERROR_SUCCESS; +} + +// Allocates sector offset table +int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) +{ +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    DWORD dwSectorOffsLen; +    bool bSectorOffsetTableCorrupt = false; + +    // Caller of AllocateSectorOffsets must ensure these +    assert(hf->SectorOffsets == NULL); +    assert(hf->pFileEntry != NULL); +    assert(hf->dwDataSize != 0); +    assert(hf->ha != NULL); + +    // If the file is stored as single unit, just set number of sectors to 1 +    if(pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) +    { +        hf->dwSectorCount = 1; +        return ERROR_SUCCESS; +    } + +    // Calculate the number of data sectors +    // Note that this doesn't work if the file size is zero +    hf->dwSectorCount = ((hf->dwDataSize - 1) / hf->dwSectorSize) + 1; + +    // Calculate the number of file sectors +    dwSectorOffsLen = (hf->dwSectorCount + 1) * sizeof(DWORD); +     +    // If MPQ_FILE_SECTOR_CRC flag is set, there will either be extra DWORD +    // or an array of MD5's. Either way, we read at least 4 bytes more +    // in order to save additional read from the file. +    if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) +        dwSectorOffsLen += sizeof(DWORD); + +    // Only allocate and load the table if the file is compressed +    if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +    { +        __LoadSectorOffsets: + +        // Allocate the sector offset table +        hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen); +        if(hf->SectorOffsets == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Only read from the file if we are supposed to do so +        if(bLoadFromFile) +        { +            ULONGLONG RawFilePos = hf->RawFilePos; + +            if(hf->pPatchInfo != NULL) +                RawFilePos += hf->pPatchInfo->dwLength; + +            // Load the sector offsets from the file +            if(!FileStream_Read(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) +            { +                // Free the sector offsets +                STORM_FREE(hf->SectorOffsets); +                hf->SectorOffsets = NULL; +                return GetLastError(); +            } + +            // Swap the sector positions +            BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); + +            // Decrypt loaded sector positions if necessary +            if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +            { +                // If we don't know the file key, try to find it. +                if(hf->dwFileKey == 0) +                { +                    hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, dwSectorOffsLen); +                    if(hf->dwFileKey == 0) +                    { +                        STORM_FREE(hf->SectorOffsets); +                        hf->SectorOffsets = NULL; +                        return ERROR_UNKNOWN_FILE_KEY; +                    } +                } + +                // Decrypt sector positions +                DecryptMpqBlock(hf->SectorOffsets, dwSectorOffsLen, hf->dwFileKey - 1); +            } + +            // +            // Validate the sector offset table +            // +            // Note: Some MPQ protectors put the actual file data before the sector offset table. +            // In this case, the sector offsets are negative (> 0x80000000). +            // + +            for(DWORD i = 0; i < hf->dwSectorCount; i++) +            { +                DWORD dwSectorOffset1 = hf->SectorOffsets[i+1]; +                DWORD dwSectorOffset0 = hf->SectorOffsets[i]; + +                // Every following sector offset must be bigger than the previous one +                if(dwSectorOffset1 <= dwSectorOffset0) +                { +                    bSectorOffsetTableCorrupt = true; +                    break; +                } + +                // The sector size must not be bigger than compressed file size +                if((dwSectorOffset1 - dwSectorOffset0) > pFileEntry->dwCmpSize) +                { +                    bSectorOffsetTableCorrupt = true; +                    break; +                } +            } + +            // If data corruption detected, free the sector offset table +            if(bSectorOffsetTableCorrupt) +            { +                STORM_FREE(hf->SectorOffsets); +                hf->SectorOffsets = NULL; +                return ERROR_FILE_CORRUPT; +            } + +            // +            // There may be various extra DWORDs loaded after the sector offset table. +            // They are mostly empty on WoW release MPQs, but on MPQs from PTR, +            // they contain random non-zero data. Their meaning is unknown. +            // +            // These extra values are, however, include in the dwCmpSize in the file +            // table. We cannot ignore them, because compacting archive would fail +            //  + +            if(hf->SectorOffsets[0] > dwSectorOffsLen) +            { +                dwSectorOffsLen = hf->SectorOffsets[0]; +                STORM_FREE(hf->SectorOffsets); +                hf->SectorOffsets = NULL; +                goto __LoadSectorOffsets; +            } +        } +        else +        { +            memset(hf->SectorOffsets, 0, dwSectorOffsLen); +            hf->SectorOffsets[0] = dwSectorOffsLen; +        } +    } + +    return ERROR_SUCCESS; +} + +int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) +{ +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    ULONGLONG RawFilePos; +    DWORD dwCompressedSize = 0; +    DWORD dwExpectedSize; +    DWORD dwCrcOffset;                      // Offset of the CRC table, relative to file offset in the MPQ +    DWORD dwCrcSize; + +    // Caller of AllocateSectorChecksums must ensure these +    assert(hf->SectorChksums == NULL); +    assert(hf->SectorOffsets != NULL); +    assert(hf->pFileEntry != NULL); +    assert(hf->ha != NULL); + +    // Single unit files don't have sector checksums +    if(pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) +        return ERROR_SUCCESS; + +    // Caller must ensure that we are only called when we have sector checksums +    assert(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC); + +    //  +    // Older MPQs store an array of CRC32's after +    // the raw file data in the MPQ. +    // +    // In newer MPQs, the (since Cataclysm BETA) the (attributes) file +    // contains additional 32-bit values beyond the sector table. +    // Their number depends on size of the (attributes), but their +    // meaning is unknown. They are usually zeroed in retail game files, +    // but contain some sort of checksum in BETA MPQs +    // + +    // Does the size of the file table match with the CRC32-based checksums? +    dwExpectedSize = (hf->dwSectorCount + 2) * sizeof(DWORD); +    if(hf->SectorOffsets[0] == dwExpectedSize) +    { +        // Is there valid size of the sector checksums? +        if(hf->SectorOffsets[hf->dwSectorCount + 1] >= hf->SectorOffsets[hf->dwSectorCount]) +            dwCompressedSize = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount]; + +        // Ignore cases when the length is too small or too big. +        if(dwCompressedSize < sizeof(DWORD) || dwCompressedSize > hf->dwSectorSize) +            return ERROR_SUCCESS; + +        // Allocate the array for the sector checksums +        hf->SectorChksums = STORM_ALLOC(DWORD, hf->dwSectorCount); +        if(hf->SectorChksums == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // If we are not supposed to load it from the file, allocate empty buffer +        if(bLoadFromFile == false) +        { +            memset(hf->SectorChksums, 0, hf->dwSectorCount * sizeof(DWORD)); +            return ERROR_SUCCESS; +        } + +        // Calculate offset of the CRC table +        dwCrcSize = hf->dwSectorCount * sizeof(DWORD); +        dwCrcOffset = hf->SectorOffsets[hf->dwSectorCount]; +        CalculateRawSectorOffset(RawFilePos, hf, dwCrcOffset);  + +        // Now read the table from the MPQ +        return LoadMpqTable(ha, RawFilePos, hf->SectorChksums, dwCompressedSize, dwCrcSize, 0); +    } + +    // If the size doesn't match, we ignore sector checksums +//  assert(false); +    return ERROR_SUCCESS; +} + +int WritePatchInfo(TMPQFile * hf) +{ +    TMPQArchive * ha = hf->ha; +    TPatchInfo * pPatchInfo = hf->pPatchInfo; + +    // The caller must make sure that this function is only called +    // when the following is true. +    assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE); +    assert(pPatchInfo != NULL); + +    BSWAP_ARRAY32_UNSIGNED(pPatchInfo, 3 * sizeof(DWORD)); +    if(!FileStream_Write(ha->pStream, &hf->RawFilePos, pPatchInfo, sizeof(TPatchInfo))) +        return GetLastError(); + +    return ERROR_SUCCESS; +} + +int WriteSectorOffsets(TMPQFile * hf) +{ +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    ULONGLONG RawFilePos = hf->RawFilePos; +    DWORD dwSectorOffsLen; + +    // The caller must make sure that this function is only called +    // when the following is true. +    assert(hf->pFileEntry->dwFlags & MPQ_FILE_COMPRESSED); +    assert(hf->SectorOffsets != NULL); +    dwSectorOffsLen = hf->SectorOffsets[0]; + +    // If file is encrypted, sector positions are also encrypted +    if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +        EncryptMpqBlock(hf->SectorOffsets, dwSectorOffsLen, hf->dwFileKey - 1); +    BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); + +    // Adjust sector offset table position, if we also have patch info +    if(hf->pPatchInfo != NULL) +        RawFilePos += hf->pPatchInfo->dwLength; + +    // Write sector offsets to the archive +    if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) +        return GetLastError(); +     +    // Not necessary, as the sector checksums +    // are going to be freed when this is done. +//  BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); +    return ERROR_SUCCESS; +} + + +int WriteSectorChecksums(TMPQFile * hf) +{ +    TMPQArchive * ha = hf->ha; +    ULONGLONG RawFilePos; +    TFileEntry * pFileEntry = hf->pFileEntry; +    LPBYTE pbCompressed; +    DWORD dwCompressedSize = 0; +    DWORD dwCrcSize; +    int nOutSize; +    int nError = ERROR_SUCCESS; + +    // The caller must make sure that this function is only called +    // when the following is true. +    assert(hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC); +    assert(hf->SectorOffsets != NULL); +    assert(hf->SectorChksums != NULL); + +    // If the MPQ has MD5 of each raw data chunk, +    // we leave sector offsets empty +    if(ha->pHeader->dwRawChunkSize != 0) +    { +        hf->SectorOffsets[hf->dwSectorCount + 1] = hf->SectorOffsets[hf->dwSectorCount]; +        return ERROR_SUCCESS; +    } + +    // Calculate size of the checksum array +    dwCrcSize = hf->dwSectorCount * sizeof(DWORD); + +    // Allocate buffer for compressed sector CRCs. +    pbCompressed = STORM_ALLOC(BYTE, dwCrcSize); +    if(pbCompressed == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Perform the compression +    BSWAP_ARRAY32_UNSIGNED(hf->SectorChksums, dwCrcSize); + +    nOutSize = (int)dwCrcSize; +    SCompCompress(pbCompressed, &nOutSize, hf->SectorChksums, (int)dwCrcSize, MPQ_COMPRESSION_ZLIB, 0, 0); +    dwCompressedSize = (DWORD)nOutSize; + +    // Write the sector CRCs to the archive +    RawFilePos = hf->RawFilePos + hf->SectorOffsets[hf->dwSectorCount]; +    if(hf->pPatchInfo != NULL) +        RawFilePos += hf->pPatchInfo->dwLength; +    if(!FileStream_Write(ha->pStream, &RawFilePos, pbCompressed, dwCompressedSize)) +        nError = GetLastError(); + +    // Not necessary, as the sector checksums +    // are going to be freed when this is done. +//  BSWAP_ARRAY32_UNSIGNED(hf->SectorChksums, dwCrcSize); + +    // Store the sector CRCs  +    hf->SectorOffsets[hf->dwSectorCount + 1] = hf->SectorOffsets[hf->dwSectorCount] + dwCompressedSize; +    pFileEntry->dwCmpSize += dwCompressedSize; +    STORM_FREE(pbCompressed); +    return nError; +} + +int WriteMemDataMD5( +    TFileStream * pStream, +    ULONGLONG RawDataOffs, +    void * pvRawData, +    DWORD dwRawDataSize, +    DWORD dwChunkSize, +    LPDWORD pcbTotalSize) +{ +    unsigned char * md5_array; +    unsigned char * md5; +    LPBYTE pbRawData = (LPBYTE)pvRawData; +    DWORD dwBytesRemaining = dwRawDataSize; +    DWORD dwMd5ArraySize = 0; +    int nError = ERROR_SUCCESS; + +    // Allocate buffer for array of MD5 +    md5_array = md5 = AllocateMd5Buffer(dwRawDataSize, dwChunkSize, &dwMd5ArraySize); +    if(md5_array == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // For every file chunk, calculate MD5 +    while(dwBytesRemaining != 0) +    { +        // Get the remaining number of bytes to read +        dwChunkSize = STORMLIB_MIN(dwBytesRemaining, dwChunkSize); + +        // Calculate MD5 +        CalculateDataBlockHash(pbRawData, dwChunkSize, md5); +        md5 += MD5_DIGEST_SIZE; + +        // Move offset and size +        dwBytesRemaining -= dwChunkSize; +        pbRawData += dwChunkSize; +    } + +    // Write the array od MD5's to the file +    RawDataOffs += dwRawDataSize; +    if(!FileStream_Write(pStream, &RawDataOffs, md5_array, dwMd5ArraySize)) +        nError = GetLastError(); + +    // Give the caller the size of the MD5 array +    if(pcbTotalSize != NULL) +        *pcbTotalSize = dwRawDataSize + dwMd5ArraySize; + +    // Free buffers and exit +    STORM_FREE(md5_array); +    return nError; +} + + +// Writes the MD5 for each chunk of the raw file data +int WriteMpqDataMD5( +    TFileStream * pStream, +    ULONGLONG RawDataOffs, +    DWORD dwRawDataSize, +    DWORD dwChunkSize) +{ +    unsigned char * md5_array; +    unsigned char * md5; +    LPBYTE pbFileChunk; +    DWORD dwMd5ArraySize = 0; +    DWORD dwToRead = dwRawDataSize; +    int nError = ERROR_SUCCESS; + +    // Allocate buffer for array of MD5 +    md5_array = md5 = AllocateMd5Buffer(dwRawDataSize, dwChunkSize, &dwMd5ArraySize); +    if(md5_array == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Allocate space for file chunk +    pbFileChunk = STORM_ALLOC(BYTE, dwChunkSize); +    if(pbFileChunk == NULL) +    { +        STORM_FREE(md5_array); +        return ERROR_NOT_ENOUGH_MEMORY; +    } + +    // For every file chunk, calculate MD5 +    while(dwRawDataSize != 0) +    { +        // Get the remaining number of bytes to read +        dwToRead = STORMLIB_MIN(dwRawDataSize, dwChunkSize); + +        // Read the chunk +        if(!FileStream_Read(pStream, &RawDataOffs, pbFileChunk, dwToRead)) +        { +            nError = GetLastError(); +            break; +        } + +        // Calculate MD5 +        CalculateDataBlockHash(pbFileChunk, dwToRead, md5); +        md5 += MD5_DIGEST_SIZE; + +        // Move offset and size +        RawDataOffs += dwToRead; +        dwRawDataSize -= dwToRead; +    } + +    // Write the array od MD5's to the file +    if(nError == ERROR_SUCCESS) +    { +        if(!FileStream_Write(pStream, NULL, md5_array, dwMd5ArraySize)) +            nError = GetLastError(); +    } + +    // Free buffers and exit +    STORM_FREE(pbFileChunk); +    STORM_FREE(md5_array); +    return nError; +} + +// Frees the structure for MPQ file +void FreeMPQFile(TMPQFile *& hf) +{ +    if(hf != NULL) +    { +        // If we have patch file attached to this one, free it first +        if(hf->hfPatchFile != NULL) +            FreeMPQFile(hf->hfPatchFile); + +        // Then free all buffers allocated in the file structure +        if(hf->pPatchHeader != NULL) +            STORM_FREE(hf->pPatchHeader); +        if(hf->pbFileData != NULL) +            STORM_FREE(hf->pbFileData); +        if(hf->pPatchInfo != NULL) +            STORM_FREE(hf->pPatchInfo); +        if(hf->SectorOffsets != NULL) +            STORM_FREE(hf->SectorOffsets); +        if(hf->SectorChksums != NULL) +            STORM_FREE(hf->SectorChksums); +        if(hf->pbFileSector != NULL) +            STORM_FREE(hf->pbFileSector); +        FileStream_Close(hf->pStream); +        STORM_FREE(hf); +        hf = NULL; +    } +} + +// Frees the MPQ archive +void FreeMPQArchive(TMPQArchive *& ha) +{ +    if(ha != NULL) +    { +        // First of all, free the patch archive, if any +        if(ha->haPatch != NULL) +            FreeMPQArchive(ha->haPatch); + +        // Close the file stream +        FileStream_Close(ha->pStream); +        ha->pStream = NULL; + +        // Free the file names from the file table +        if(ha->pFileTable != NULL) +        { +            for(DWORD i = 0; i < ha->dwFileTableSize; i++) +            { +                if(ha->pFileTable[i].szFileName != NULL) +                    STORM_FREE(ha->pFileTable[i].szFileName); +                ha->pFileTable[i].szFileName = NULL; +            } + +            // Then free all buffers allocated in the archive structure +            STORM_FREE(ha->pFileTable); +        } + +        if(ha->pBitmap != NULL) +            STORM_FREE(ha->pBitmap); +        if(ha->pHashTable != NULL) +            STORM_FREE(ha->pHashTable); +        if(ha->pHetTable != NULL) +            FreeHetTable(ha->pHetTable); +        STORM_FREE(ha); +        ha = NULL; +    } +} + +const char * GetPlainFileNameA(const char * szFileName) +{ +    const char * szPlainName = szFileName; + +    while(*szFileName != 0) +    { +        if(*szFileName == '\\' || *szFileName == '/') +            szPlainName = szFileName + 1; +        szFileName++; +    } + +    return szPlainName; +} + +const TCHAR * GetPlainFileNameT(const TCHAR * szFileName) +{ +    const TCHAR * szPlainName = szFileName; + +    while(*szFileName != 0) +    { +        if(*szFileName == '\\' || *szFileName == '/') +            szPlainName = szFileName + 1; +        szFileName++; +    } + +    return szPlainName; +} + +bool IsInternalMpqFileName(const char * szFileName) +{ +    if(szFileName != NULL && szFileName[0] == '(') +    { +        if(!_stricmp(szFileName, LISTFILE_NAME) || +           !_stricmp(szFileName, ATTRIBUTES_NAME) || +           !_stricmp(szFileName, SIGNATURE_NAME)) +        { +            return true; +        } +    } + +    return false; +} + +// Verifies if the file name is a pseudo-name +bool IsPseudoFileName(const char * szFileName, DWORD * pdwFileIndex) +{ +    DWORD dwFileIndex = 0; + +    if(szFileName != NULL) +    { +        // Must be "File########.ext" +        if(!_strnicmp(szFileName, "File", 4)) +        { +            // Check 8 digits +            for(int i = 4; i < 4+8; i++) +            { +                if(szFileName[i] < '0' || szFileName[i] > '9') +                    return false; +                dwFileIndex = (dwFileIndex * 10) + (szFileName[i] - '0'); +            } + +            // An extension must follow +            if(szFileName[12] == '.') +            { +                if(pdwFileIndex != NULL) +                    *pdwFileIndex = dwFileIndex; +                return true; +            } +        } +    } + +    // Not a pseudo-name +    return false; +} + +//----------------------------------------------------------------------------- +// Functions calculating and verifying the MD5 signature + +bool IsValidMD5(LPBYTE pbMd5) +{ +    BYTE BitSummary = 0; +     +    // The MD5 is considered invalid of it is zeroed +    BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07]; +    BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F]; +    return (BitSummary != 0); +} + +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) +{ +    hash_state md5_state; +    BYTE md5_digest[MD5_DIGEST_SIZE]; + +    // Don't verify the block if the MD5 is not valid. +    if(!IsValidMD5(expected_md5)) +        return true; + +    // Calculate the MD5 of the data block +    md5_init(&md5_state); +    md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); +    md5_done(&md5_state, md5_digest); + +    // Does the MD5's match? +    return (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); +} + +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) +{ +    hash_state md5_state; + +    md5_init(&md5_state); +    md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); +    md5_done(&md5_state, md5_hash); +} + + +//----------------------------------------------------------------------------- +// Swapping functions + +#ifndef PLATFORM_LITTLE_ENDIAN + +// +// Note that those functions are implemented for Mac operating system, +// as this is the only supported platform that uses big endian. +// + +// Swaps a signed 16-bit integer +int16_t SwapInt16(uint16_t data) +{ +	return (int16_t)CFSwapInt16(data); +} + +// Swaps an unsigned 16-bit integer +uint16_t SwapUInt16(uint16_t data) +{ +	return CFSwapInt16(data); +} + +// Swaps signed 32-bit integer +int32_t SwapInt32(uint32_t data) +{ +	return (int32_t)CFSwapInt32(data); +} + +// Swaps an unsigned 32-bit integer +uint32_t SwapUInt32(uint32_t data) +{ +	return CFSwapInt32(data); +} + +// Swaps signed 64-bit integer +int64_t SwapInt64(int64_t data) +{ +       return (int64_t)CFSwapInt64(data); +} + +// Swaps an unsigned 64-bit integer +uint64_t SwapUInt64(uint64_t data) +{ +       return CFSwapInt64(data); +} + +// Swaps array of unsigned 16-bit integers +void ConvertUInt16Buffer(void * ptr, size_t length) +{ +    uint16_t * buffer = (uint16_t *)ptr; +    uint32_t nElements = (uint32_t)(length / sizeof(uint16_t)); + +    while(nElements-- > 0) +	{ +		*buffer = SwapUInt16(*buffer); +		buffer++; +	} +} + +// Swaps array of unsigned 32-bit integers +void ConvertUInt32Buffer(void * ptr, size_t length) +{ +    uint32_t * buffer = (uint32_t *)ptr; +    uint32_t nElements = (uint32_t)(length / sizeof(uint32_t)); + +	while(nElements-- > 0) +	{ +		*buffer = SwapUInt32(*buffer); +		buffer++; +	} +} + +// Swaps array of unsigned 64-bit integers +void ConvertUInt64Buffer(void * ptr, size_t length) +{ +    uint64_t * buffer = (uint64_t *)ptr; +    uint32_t nElements = (uint32_t)(length / sizeof(uint64_t)); + +	while(nElements-- > 0) +	{ +		*buffer = SwapUInt64(*buffer); +		buffer++; +	} +} + +// Swaps the TMPQUserData structure +void ConvertTMPQUserData(void *userData) +{ +	TMPQUserData * theData = (TMPQUserData *)userData; + +	theData->dwID = SwapUInt32(theData->dwID); +	theData->cbUserDataSize = SwapUInt32(theData->cbUserDataSize); +	theData->dwHeaderOffs = SwapUInt32(theData->dwHeaderOffs); +	theData->cbUserDataHeader = SwapUInt32(theData->cbUserDataHeader); +} + +// Swaps the TMPQHeader structure +void ConvertTMPQHeader(void *header) +{ +	TMPQHeader * theHeader = (TMPQHeader *)header; +	 +	theHeader->dwID = SwapUInt32(theHeader->dwID); +	theHeader->dwHeaderSize = SwapUInt32(theHeader->dwHeaderSize); +	theHeader->dwArchiveSize = SwapUInt32(theHeader->dwArchiveSize); +	theHeader->wFormatVersion = SwapUInt16(theHeader->wFormatVersion); +	theHeader->wSectorSize = SwapUInt16(theHeader->wSectorSize); +	theHeader->dwHashTablePos = SwapUInt32(theHeader->dwHashTablePos); +	theHeader->dwBlockTablePos = SwapUInt32(theHeader->dwBlockTablePos); +	theHeader->dwHashTableSize = SwapUInt32(theHeader->dwHashTableSize); +	theHeader->dwBlockTableSize = SwapUInt32(theHeader->dwBlockTableSize); + +	if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) +	{ +		// Swap the hi-block table position +		theHeader->HiBlockTablePos64 = SwapUInt64(theHeader->HiBlockTablePos64); +		 +        theHeader->wHashTablePosHi = SwapUInt16(theHeader->wHashTablePosHi); +		theHeader->wBlockTablePosHi = SwapUInt16(theHeader->wBlockTablePosHi); + +        if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) +    	{ +            theHeader->ArchiveSize64 = SwapUInt64(theHeader->ArchiveSize64); +            theHeader->BetTablePos64 = SwapUInt64(theHeader->BetTablePos64); +            theHeader->HetTablePos64 = SwapUInt64(theHeader->HetTablePos64); + +            if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4) +        	{ +                theHeader->HashTableSize64    = SwapUInt64(theHeader->HashTableSize64); +                theHeader->BlockTableSize64   = SwapUInt64(theHeader->BlockTableSize64); +                theHeader->HiBlockTableSize64 = SwapUInt64(theHeader->HiBlockTableSize64); +                theHeader->HetTableSize64     = SwapUInt64(theHeader->HetTableSize64); +                theHeader->BetTableSize64     = SwapUInt64(theHeader->BetTableSize64); +            } +        } +	} +} + +#endif  // PLATFORM_LITTLE_ENDIAN diff --git a/src/SBaseDumpData.cpp b/src/SBaseDumpData.cpp new file mode 100644 index 0000000..7056d8b --- /dev/null +++ b/src/SBaseDumpData.cpp @@ -0,0 +1,144 @@ +/*****************************************************************************/ +/* SBaseDumpData.cpp                      Copyright (c) Ladislav Zezula 2011 */ +/*---------------------------------------------------------------------------*/ +/* Description :                                                             */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 26.01.11  1.00  Lad  The first version of SBaseDumpData.cpp               */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +#ifdef __STORMLIB_DUMP_DATA__ + +void DumpMpqHeader(TMPQHeader * pHeader) +{ +    printf("== MPQ Header =================================\n"); +    printf("DWORD dwID                   = %08X\n",    pHeader->dwID); +    printf("DWORD dwHeaderSize           = %08X\n",    pHeader->dwHeaderSize); +    printf("DWORD dwArchiveSize          = %08X\n",    pHeader->dwArchiveSize); +    printf("USHORT wFormatVersion        = %04X\n",    pHeader->wFormatVersion); +    printf("USHORT wSectorSize           = %04X\n",    pHeader->wSectorSize); +    printf("DWORD dwHashTablePos         = %08X\n",    pHeader->dwHashTablePos); +    printf("DWORD dwBlockTablePos        = %08X\n",    pHeader->dwBlockTablePos); +    printf("DWORD dwHashTableSize        = %08X\n",    pHeader->dwHashTableSize); +    printf("DWORD dwBlockTableSize       = %08X\n",    pHeader->dwBlockTableSize); +    printf("ULONGLONG HiBlockTablePos64  = %016llX\n", pHeader->HiBlockTablePos64); +    printf("USHORT wHashTablePosHi       = %04X\n",    pHeader->wHashTablePosHi); +    printf("USHORT wBlockTablePosHi      = %04X\n",    pHeader->wBlockTablePosHi); +    printf("ULONGLONG ArchiveSize64      = %016llX\n", pHeader->ArchiveSize64); +    printf("ULONGLONG BetTablePos64      = %016llX\n", pHeader->BetTablePos64); +    printf("ULONGLONG HetTablePos64      = %016llX\n", pHeader->HetTablePos64); +    printf("ULONGLONG HashTableSize64    = %016llX\n", pHeader->HashTableSize64); +    printf("ULONGLONG BlockTableSize64   = %016llX\n", pHeader->BlockTableSize64); +    printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64); +    printf("ULONGLONG HetTableSize64     = %016llX\n", pHeader->HetTableSize64); +    printf("ULONGLONG BetTableSize64     = %016llX\n", pHeader->BetTableSize64); +    printf("DWORD dwRawChunkSize         = %08X\n",     pHeader->dwRawChunkSize); +    printf("-----------------------------------------------\n\n"); +} + +void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) +{ +    DWORD i; + +    if(pHetTable == NULL || pBetTable == NULL) +        return; + +    printf("== HET Header =================================\n"); +    printf("ULONGLONG  AndMask64         = %016llX\n", pHetTable->AndMask64);        +    printf("ULONGLONG  OrMask64          = %016llX\n", pHetTable->OrMask64);         +    printf("DWORD      dwIndexSizeTotal  = %08X\n",     pHetTable->dwIndexSizeTotal); +    printf("DWORD      dwIndexSizeExtra  = %08X\n",     pHetTable->dwIndexSizeExtra); +    printf("DWORD      dwIndexSize       = %08X\n",     pHetTable->dwIndexSize);      +    printf("DWORD      dwMaxFileCount    = %08X\n",     pHetTable->dwMaxFileCount);   +    printf("DWORD      dwHashTableSize   = %08X\n",     pHetTable->dwHashTableSize);  +    printf("DWORD      dwHashBitSize     = %08X\n",     pHetTable->dwHashBitSize);    +    printf("-----------------------------------------------\n\n"); + +    printf("== BET Header =================================\n"); +    printf("DWORD dwTableEntrySize       = %08X\n",     pBetTable->dwTableEntrySize); +    printf("DWORD dwBitIndex_FilePos     = %08X\n",     pBetTable->dwBitIndex_FilePos); +    printf("DWORD dwBitIndex_FileSize    = %08X\n",     pBetTable->dwBitIndex_FileSize); +    printf("DWORD dwBitIndex_CmpSize     = %08X\n",     pBetTable->dwBitIndex_CmpSize); +    printf("DWORD dwBitIndex_FlagIndex   = %08X\n",     pBetTable->dwBitIndex_FlagIndex); +    printf("DWORD dwBitIndex_Unknown     = %08X\n",     pBetTable->dwBitIndex_Unknown); +    printf("DWORD dwBitCount_FilePos     = %08X\n",     pBetTable->dwBitCount_FilePos); +    printf("DWORD dwBitCount_FileSize    = %08X\n",     pBetTable->dwBitCount_FileSize); +    printf("DWORD dwBitCount_CmpSize     = %08X\n",     pBetTable->dwBitCount_CmpSize); +    printf("DWORD dwBitCount_FlagIndex   = %08X\n",     pBetTable->dwBitCount_FlagIndex);    +    printf("DWORD dwBitCount_Unknown     = %08X\n",     pBetTable->dwBitCount_Unknown); +    printf("DWORD dwBetHashSizeTotal     = %08X\n",     pBetTable->dwBetHashSizeTotal); +    printf("DWORD dwBetHashSizeExtra     = %08X\n",     pBetTable->dwBetHashSizeExtra); +    printf("DWORD dwBetHashSize          = %08X\n",     pBetTable->dwBetHashSize); +    printf("DWORD dwMaxFileCount         = %08X\n",     pBetTable->dwMaxFileCount); +    printf("DWORD dwFlagCount            = %08X\n",     pBetTable->dwFlagCount); +    printf("-----------------------------------------------\n\n"); + +    printf("== HET & Bet Table ======================================================================\n\n"); +    printf("HetIdx HetHash BetIdx BetHash          ByteOffset       FileSize CmpSize  FlgIdx Flags   \n"); +    printf("------ ------- ------ ---------------- ---------------- -------- -------- ------ --------\n"); +    for(i = 0; i < pHetTable->dwHashTableSize; i++) +    { +        ULONGLONG ByteOffset = 0; +        ULONGLONG BetHash = 0; +        DWORD dwFileSize = 0; +        DWORD dwCmpSize = 0; +        DWORD dwFlagIndex = 0; +        DWORD dwFlags = 0; +        DWORD dwBetIndex = 0; + +        GetBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal, +                                        pHetTable->dwIndexSize, +                                       &dwBetIndex, +                                        4); +         +        if(dwBetIndex < pHetTable->dwMaxFileCount) +        { +            DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex; + +            GetBits(pBetTable->pBetHashes, dwBetIndex * pBetTable->dwBetHashSizeTotal, +                                           pBetTable->dwBetHashSize, +                                          &BetHash, +                                           8); + +            GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FilePos, +                                           pBetTable->dwBitCount_FilePos, +                                          &ByteOffset, +                                           8); + +            GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FileSize, +                                           pBetTable->dwBitCount_FileSize, +                                          &dwFileSize, +                                           4); + +            GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_CmpSize, +                                           pBetTable->dwBitCount_CmpSize, +                                          &dwCmpSize, +                                           4); + +            GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FlagIndex, +                                           pBetTable->dwBitCount_FlagIndex, +                                          &dwFlagIndex, +                                           4); + +            dwFlags = pBetTable->pFileFlags[dwFlagIndex]; +        } + +        printf(" %04X    %02lX     %04X  %016llX %016llX %08X %08X  %04X  %08X\n", i, +                                                         pHetTable->pHetHashes[i], +                                                         dwBetIndex, +                                                         BetHash, +                                                         ByteOffset, +                                                         dwFileSize, +                                                         dwCmpSize, +                                                         dwFlagIndex, +                                                         dwFlags); +    } +    printf("-----------------------------------------------------------------------------------------\n"); +} + +#endif  // __STORMLIB_DUMP_DATA__ diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp new file mode 100644 index 0000000..c77e71d --- /dev/null +++ b/src/SBaseFileTable.cpp @@ -0,0 +1,2552 @@ +/*****************************************************************************/ +/* SBaseFileTable.cpp                     Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* Description: Common handler for classic and new hash&block tables         */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 06.09.10  1.00  Lad  The first version of SBaseFileTable.cpp              */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local defines + +#define INVALID_FLAG_VALUE 0xCCCCCCCC +#define MAX_FLAG_INDEX     512 + +//----------------------------------------------------------------------------- +// Local structures + +// Structure for HET table header +typedef struct _HET_TABLE_HEADER +{ +    DWORD dwTableSize;                      // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) +    DWORD dwMaxFileCount;                   // Maximum number of files in the MPQ +    DWORD dwHashTableSize;                  // Size of the hash table (in bytes) +    DWORD dwHashEntrySize;                  // Effective size of the hash entry (in bits) +    DWORD dwIndexSizeTotal;                 // Total size of file index (in bits) +    DWORD dwIndexSizeExtra;                 // Extra bits in the file index +    DWORD dwIndexSize;                      // Effective size of the file index (in bits) +    DWORD dwIndexTableSize;                 // Size of the block index subtable (in bytes) + +} HET_TABLE_HEADER, *PHET_TABLE_HEADER; + +// Structure for BET table header +typedef struct _BET_TABLE_HEADER +{ +    DWORD dwTableSize;                      // Size of the entire BET table, including the header (in bytes) +    DWORD dwFileCount;                      // Number of files in the BET table +    DWORD dwUnknown08; +    DWORD dwTableEntrySize;                 // Size of one table entry (in bits) +    DWORD dwBitIndex_FilePos;               // Bit index of the file position (within the entry record) +    DWORD dwBitIndex_FileSize;              // Bit index of the file size (within the entry record) +    DWORD dwBitIndex_CmpSize;               // Bit index of the compressed size (within the entry record) +    DWORD dwBitIndex_FlagIndex;             // Bit index of the flag index (within the entry record) +    DWORD dwBitIndex_Unknown;               // Bit index of the ??? (within the entry record) +    DWORD dwBitCount_FilePos;               // Bit size of file position (in the entry record) +    DWORD dwBitCount_FileSize;              // Bit size of file size (in the entry record) +    DWORD dwBitCount_CmpSize;               // Bit size of compressed file size (in the entry record) +    DWORD dwBitCount_FlagIndex;             // Bit size of flags index (in the entry record) +    DWORD dwBitCount_Unknown;               // Bit size of ??? (in the entry record) +    DWORD dwBetHashSizeTotal;               // Total size of the BET hash +    DWORD dwBetHashSizeExtra;               // Extra bits in the BET hash +    DWORD dwBetHashSize;                    // Effective size of BET hash (in bits) +    DWORD dwBetHashArraySize;               // Size of BET hashes array, in bytes +    DWORD dwFlagCount;                      // Number of flags in the following array + +} BET_TABLE_HEADER, *PBET_TABLE_HEADER; + +//----------------------------------------------------------------------------- +// Support for calculating bit sizes + +static void InitFileFlagArray(LPDWORD FlagArray) +{ +    for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++) +        FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE; +} + +static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags) +{ +    // Find free or equal entry in the flag array +    for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++) +    { +        if(FlagArray[dwFlagIndex] == INVALID_FLAG_VALUE || FlagArray[dwFlagIndex] == dwFlags) +        { +            FlagArray[dwFlagIndex] = dwFlags; +            return dwFlagIndex; +        } +    } + +    // This should never happen +    assert(false); +    return 0xFFFFFFFF; +} + +static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) +{ +    DWORD dwBitCount = 0; + +    while(MaxValue > 0) +    { +        MaxValue >>= 1; +        dwBitCount++; +    } +     +    return dwBitCount; +} + +//----------------------------------------------------------------------------- +// Support functions for BIT_ARRAY + +static USHORT SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; + +static TBitArray * CreateBitArray( +    DWORD NumberOfBits, +    BYTE FillValue) +{ +    TBitArray * pBitArray; +    size_t nSize = sizeof(TBitArray) + (NumberOfBits + 7) / 8; + +    // Allocate the bit array +    pBitArray = (TBitArray *)STORM_ALLOC(BYTE, nSize); +    if(pBitArray != NULL) +    { +        memset(pBitArray, FillValue, nSize); +        pBitArray->NumberOfBits = NumberOfBits; +    } + +    return pBitArray; +} + +void GetBits( +    TBitArray * pArray, +    unsigned int nBitPosition, +    unsigned int nBitLength, +    void * pvBuffer, +    int nResultByteSize) +{ +    unsigned char * pbBuffer = (unsigned char *)pvBuffer; +    unsigned int nBytePosition0 = (nBitPosition / 8); +    unsigned int nBytePosition1 = nBytePosition0 + 1; +    unsigned int nByteLength = (nBitLength / 8); +    unsigned int nBitOffset = (nBitPosition & 0x07); +    unsigned char BitBuffer; + +    // Keep compiler happy for platforms where nResultByteSize is not used +    nResultByteSize = nResultByteSize; + +#ifdef _DEBUG +    // Check if the target is properly zeroed +    for(int i = 0; i < nResultByteSize; i++) +        assert(pbBuffer[i] == 0); +#endif + +#ifndef PLATFORM_LITTLE_ENDIAN +    // Adjust the buffer pointer for big endian platforms +    pbBuffer += (nResultByteSize - 1); +#endif     + +    // Copy whole bytes, if any +    while(nByteLength > 0) +    { +        // Is the current position in the Elements byte-aligned? +        if(nBitOffset != 0) +        { +            BitBuffer = (unsigned char)((pArray->Elements[nBytePosition0] >> nBitOffset) | (pArray->Elements[nBytePosition1] << (0x08 - nBitOffset))); +        } +        else +        { +            BitBuffer = pArray->Elements[nBytePosition0]; +        } + +#ifdef PLATFORM_LITTLE_ENDIAN +        *pbBuffer++ = BitBuffer; +#else +        *pbBuffer-- = BitBuffer; +#endif + +        // Move byte positions and lengths +        nBytePosition1++; +        nBytePosition0++; +        nByteLength--; +    } + +    // Get the rest of the bits +    nBitLength = (nBitLength & 0x07); +    if(nBitLength != 0) +    { +        *pbBuffer = (unsigned char)(pArray->Elements[nBytePosition0] >> nBitOffset); + +        if(nBitLength > (8 - nBitOffset)) +            *pbBuffer = (unsigned char)((pArray->Elements[nBytePosition1] << (8 - nBitOffset)) | (pArray->Elements[nBytePosition0] >> nBitOffset)); + +        *pbBuffer &= (0x01 << nBitLength) - 1; +    } +} + +void SetBits( +    TBitArray * pArray, +    unsigned int nBitPosition, +    unsigned int nBitLength, +    void * pvBuffer, +    int nResultByteSize) +{ +    unsigned char * pbBuffer = (unsigned char *)pvBuffer; +    unsigned int nBytePosition = (nBitPosition / 8); +    unsigned int nBitOffset = (nBitPosition & 0x07); +    unsigned short BitBuffer = 0; +    unsigned short AndMask = 0; +    unsigned short OneByte = 0; + +    // Keep compiler happy for platforms where nResultByteSize is not used +    nResultByteSize = nResultByteSize; + +#ifndef PLATFORM_LITTLE_ENDIAN +    // Adjust the buffer pointer for big endian platforms +    pbBuffer += (nResultByteSize - 1); +#endif     + +    // Copy whole bytes, if any +    while(nBitLength > 8) +    { +        // Reload the bit buffer +#ifdef PLATFORM_LITTLE_ENDIAN +        OneByte = *pbBuffer++; +#else +        OneByte = *pbBuffer--; +#endif +        // Update the BitBuffer and AndMask for the bit array +        BitBuffer = (BitBuffer >> 0x08) | (OneByte << nBitOffset); +        AndMask = (AndMask >> 0x08) | (0x00FF << nBitOffset); + +        // Update the byte in the array +        pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); + +        // Move byte positions and lengths +        nBytePosition++; +        nBitLength -= 0x08; +    } + +    if(nBitLength != 0) +    { +        // Reload the bit buffer +        OneByte = *pbBuffer; + +        // Update the AND mask for the last bit +        BitBuffer = (BitBuffer >> 0x08) | (OneByte << nBitOffset); +        AndMask = (AndMask >> 0x08) | (SetBitsMask[nBitLength] << nBitOffset); + +        // Update the byte in the array +        pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); + +        // Update the next byte, if needed +        if(AndMask & 0xFF00) +        { +            nBytePosition++; +            BitBuffer >>= 0x08; +            AndMask >>= 0x08; + +            pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); +        } +    } +} + + +//----------------------------------------------------------------------------- +// Support for hash table + +// Returns a hash table entry in the following order: +// 1) A hash table entry with the neutral locale +// 2) A hash table entry with any other locale +// 3) NULL +static TMPQHash * GetHashEntryAny(TMPQArchive * ha, const char * szFileName) +{ +    TMPQHash * pHashNeutral = NULL; +    TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); +    TMPQHash * pHashAny = NULL; +    TMPQHash * pHash = pFirstHash; + +    // Parse the found hashes +    while(pHash != NULL) +    { +        // If we found neutral hash, remember it +        if(pHash->lcLocale == 0) +            pHashNeutral = pHash; +        if(pHashAny == NULL) +            pHashAny = pHash; + +        // Get the next hash entry for that file +        pHash = GetNextHashEntry(ha, pFirstHash, pHash);  +    } + +    // At the end, return neutral hash (if found), otherwise NULL +    return (pHashNeutral != NULL) ? pHashNeutral : pHashAny; +} + +// Returns a hash table entry in the following order: +// 1) A hash table entry with the preferred locale +// 2) A hash table entry with the neutral locale +// 3) NULL +static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ +    TMPQHash * pHashNeutral = NULL; +    TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); +    TMPQHash * pHash = pFirstHash; + +    // Parse the found hashes +    while(pHash != NULL) +    { +        // If the locales match, return it +        if(pHash->lcLocale == lcLocale) +            return pHash; +         +        // If we found neutral hash, remember it +        if(pHash->lcLocale == 0) +            pHashNeutral = pHash; + +        // Get the next hash entry for that file +        pHash = GetNextHashEntry(ha, pFirstHash, pHash);  +    } + +    // At the end, return neutral hash (if found), otherwise NULL +    return pHashNeutral; +} + +// Returns a hash table entry in the following order: +// 1) A hash table entry with the preferred locale +// 2) NULL +static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ +    TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); +    TMPQHash * pHash = pFirstHash; + +    // Parse the found hashes +    while(pHash != NULL) +    { +        // If the locales match, return it +        if(pHash->lcLocale == lcLocale) +            return pHash; +         +        // Get the next hash entry for that file +        pHash = GetNextHashEntry(ha, pFirstHash, pHash);  +    } + +    // Not found +    return NULL; +} + +static TMPQHash * TranslateHashTable( +    TMPQArchive * ha, +    ULONGLONG * pcbTableSize) +{ +    TMPQHash * pHashTable; +    size_t HashTableSize; + +    // Allocate copy of the hash table +    pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize); +    if(pHashTable != NULL) +    { +        // Copy the hash table +        HashTableSize = sizeof(TMPQHash) * ha->pHeader->dwHashTableSize; +        memcpy(pHashTable, ha->pHashTable, HashTableSize); + +        // Give the size to the caller +        if(pcbTableSize != NULL) +        { +            *pcbTableSize = (ULONGLONG)HashTableSize; +        } +    } + +    return pHashTable; +} + +static TMPQBlock * TranslateBlockTable( +    TMPQArchive * ha, +    ULONGLONG * pcbTableSize, +    bool * pbNeedHiBlockTable) +{ +    TFileEntry * pFileEntry = ha->pFileTable; +    TMPQBlock * pBlockTable; +    TMPQBlock * pBlock; +    size_t BlockTableSize; +    bool bNeedHiBlockTable = false; + +    // Allocate copy of the hash table +    pBlockTable = pBlock = STORM_ALLOC(TMPQBlock, ha->dwFileTableSize); +    if(pBlockTable != NULL) +    { +        // Copy the block table +        BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize; +        for(DWORD i = 0; i < ha->dwFileTableSize; i++) +        { +            bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false; +            pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset; +            pBlock->dwFSize   = pFileEntry->dwFileSize; +            pBlock->dwCSize   = pFileEntry->dwCmpSize; +            pBlock->dwFlags   = pFileEntry->dwFlags; + +            pFileEntry++; +            pBlock++; +        } + +        // Give the size to the caller +        if(pcbTableSize != NULL) +            *pcbTableSize = (ULONGLONG)BlockTableSize; + +        if(pbNeedHiBlockTable != NULL) +            *pbNeedHiBlockTable = bNeedHiBlockTable; +    } + +    return pBlockTable; +} + +static USHORT * TranslateHiBlockTable( +    TMPQArchive * ha, +    ULONGLONG * pcbTableSize) +{ +    TFileEntry * pFileEntry = ha->pFileTable; +    USHORT * pHiBlockTable; +    USHORT * pHiBlock; +    size_t HiBlockTableSize; + +    // Allocate copy of the hash table +    pHiBlockTable = pHiBlock = STORM_ALLOC(USHORT, ha->dwFileTableSize); +    if(pHiBlockTable != NULL) +    { +        // Copy the block table +        HiBlockTableSize = sizeof(USHORT) * ha->dwFileTableSize; +        for(DWORD i = 0; i < ha->dwFileTableSize; i++) +            pHiBlock[i] = (USHORT)(pFileEntry[i].ByteOffset >> 0x20); + +        // Give the size to the caller +        if(pcbTableSize != NULL) +            *pcbTableSize = (ULONGLONG)HiBlockTableSize; +    } + +    return pHiBlockTable; +} + +//----------------------------------------------------------------------------- +// General EXT table functions + +TMPQExtTable * LoadExtTable( +    TMPQArchive * ha, +    ULONGLONG ByteOffset, +    size_t Size, +    DWORD dwSignature, +    DWORD dwKey) +{ +    TMPQExtTable * pCompressed = NULL;      // Compressed table +    TMPQExtTable * pExtTable = NULL;        // Uncompressed table + +    // Do nothing if the size is zero +    if(ByteOffset != 0 && Size != 0) +    { +        // Allocate size for the compressed table +        pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, Size); +        if(pExtTable != NULL) +        { +            // Load the table from the MPQ +            ByteOffset += ha->MpqPos; +            if(!FileStream_Read(ha->pStream, &ByteOffset, pExtTable, (DWORD)Size)) +            { +                STORM_FREE(pExtTable); +                return NULL; +            } + +            // Swap the ext table header +            BSWAP_ARRAY32_UNSIGNED(pExtTable, sizeof(TMPQExtTable)); +            if(pExtTable->dwSignature != dwSignature) +            { +                STORM_FREE(pExtTable); +                return NULL; +            } + +            // Decrypt the block +            BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); +            DecryptMpqBlock(pExtTable + 1, (DWORD)(Size - sizeof(TMPQExtTable)), dwKey); +            BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); + +            // If the table is compressed, decompress it +            if((pExtTable->dwDataSize + sizeof(TMPQExtTable)) > Size) +            { +                pCompressed = pExtTable; +                pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + pCompressed->dwDataSize); +                if(pExtTable != NULL) +                { +                    int cbOutBuffer = (int)pCompressed->dwDataSize; +                    int cbInBuffer = (int)Size; + +                    // Decompress the extended table +                    pExtTable->dwSignature = pCompressed->dwSignature; +                    pExtTable->dwVersion   = pCompressed->dwVersion; +                    pExtTable->dwDataSize  = pCompressed->dwDataSize; +                    if(!SCompDecompress2(pExtTable + 1, &cbOutBuffer, pCompressed + 1, cbInBuffer)) +                    { +                        STORM_FREE(pExtTable); +                        pExtTable = NULL; +                    } +                } + +                // Free the compressed block +                STORM_FREE(pCompressed); +            } +        } +    } + +    // Return the decompressed table to the caller +    return pExtTable; +} + +// Used in MPQ Editor +void FreeMpqBuffer(void * pvBuffer) +{ +    STORM_FREE(pvBuffer); +} + +static int SaveMpqTable( +    TMPQArchive * ha, +    void * pMpqTable, +    ULONGLONG ByteOffset, +    size_t Size, +    unsigned char * md5, +    DWORD dwKey, +    bool bCompress) +{ +    ULONGLONG FileOffset; +    void * pCompressed = NULL; +    int nError = ERROR_SUCCESS; + +    // Do we have to compress the table? +    if(bCompress) +    { +        int cbOutBuffer = (int)Size; +        int cbInBuffer = (int)Size; + +        // Allocate extra space for compressed table +        pCompressed = STORM_ALLOC(BYTE, Size); +        if(pCompressed == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Compress the table +        SCompCompress(pCompressed, &cbOutBuffer, pMpqTable, cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); + +        // If the compression failed, revert it. Otherwise, swap the tables +        if(cbOutBuffer >= cbInBuffer) +        { +            STORM_FREE(pCompressed); +            pCompressed = NULL; +        } +        else +        { +            pMpqTable = pCompressed; +        } +    } + +    // Encrypt the table +    if(dwKey != 0) +    { +        BSWAP_ARRAY32_UNSIGNED(pMpqTable, Size); +        EncryptMpqBlock(pMpqTable, (DWORD)Size, dwKey); +        BSWAP_ARRAY32_UNSIGNED(pMpqTable, Size); +    } + +    // Calculate the MD5 +    if(md5 != NULL) +    { +        CalculateDataBlockHash(pMpqTable, (DWORD)Size, md5); +    } + +    // Save the table to the MPQ +    BSWAP_ARRAY32_UNSIGNED(pMpqTable, Size); +    FileOffset = ha->MpqPos + ByteOffset; +    if(!FileStream_Write(ha->pStream, &FileOffset, pMpqTable, (DWORD)Size)) +        nError = GetLastError(); + +    // Free the compressed table, if any +    if(pCompressed != NULL) +        STORM_FREE(pCompressed); +    return nError; +} + +static int SaveExtTable( +    TMPQArchive * ha, +    TMPQExtTable * pExtTable, +    ULONGLONG ByteOffset, +    DWORD dwTableSize, +    unsigned char * md5, +    DWORD dwKey, +    bool bCompress, +    LPDWORD pcbTotalSize) +{ +    ULONGLONG FileOffset; +    TMPQExtTable * pCompressed = NULL; +    DWORD cbTotalSize = 0; +    int nError = ERROR_SUCCESS; + +    // Do we have to compress the table? +    if(bCompress) +    { +        int cbOutBuffer = (int)dwTableSize; +        int cbInBuffer = (int)dwTableSize; + +        // Allocate extra space for compressed table +        pCompressed = (TMPQExtTable *)STORM_ALLOC(BYTE, dwTableSize); +        if(pCompressed == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Compress the table +        pCompressed->dwSignature = pExtTable->dwSignature; +        pCompressed->dwVersion   = pExtTable->dwVersion; +        pCompressed->dwDataSize  = pExtTable->dwDataSize; +        SCompCompress((pCompressed + 1), &cbOutBuffer, (pExtTable + 1), cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); + +        // If the compression failed, revert it. Otherwise, swap the tables +        if(cbOutBuffer >= cbInBuffer) +        { +            STORM_FREE(pCompressed); +            pCompressed = NULL; +        } +        else +        { +            pExtTable = pCompressed; +        } +    } + +    // Encrypt the table +    if(dwKey != 0) +    { +        BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); +        EncryptMpqBlock(pExtTable + 1, (DWORD)(dwTableSize - sizeof(TMPQExtTable)), dwKey); +        BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); +    } + +    // Calculate the MD5 of the table after +    if(md5 != NULL) +    { +        CalculateDataBlockHash(pExtTable, dwTableSize, md5); +    } + +    // Save the table to the MPQ +    FileOffset = ha->MpqPos + ByteOffset; +    if(FileStream_Write(ha->pStream, &FileOffset, pExtTable, dwTableSize)) +        cbTotalSize += dwTableSize; +    else +        nError = GetLastError(); + +    // We have to write raw data MD5 +    if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) +    { +        nError = WriteMemDataMD5(ha->pStream, +                                 FileOffset, +                                 pExtTable, +                                 dwTableSize, +                                 ha->pHeader->dwRawChunkSize, +                                &cbTotalSize); +    } + +    // Give the total written size, if needed +    if(pcbTotalSize != NULL) +        *pcbTotalSize = cbTotalSize; + +    // Free the compressed table, if any +    if(pCompressed != NULL) +        STORM_FREE(pCompressed); +    return nError; +} + +//----------------------------------------------------------------------------- +// Support for HET table + +static void CreateHetHeader( +    TMPQHetTable * pHetTable, +    PHET_TABLE_HEADER pHetHeader) +{ +    // Fill the BET header +    pHetHeader->dwMaxFileCount   = pHetTable->dwMaxFileCount; +    pHetHeader->dwHashTableSize  = pHetTable->dwHashTableSize; +    pHetHeader->dwHashEntrySize  = pHetTable->dwHashBitSize; +    pHetHeader->dwIndexSizeTotal = GetNecessaryBitCount(pHetTable->dwMaxFileCount); +    pHetHeader->dwIndexSizeExtra = 0; +    pHetHeader->dwIndexSize      = pHetHeader->dwIndexSizeTotal; +    pHetHeader->dwIndexTableSize = ((pHetHeader->dwIndexSizeTotal * pHetTable->dwHashTableSize) + 7) / 8; + +    // Calculate the total size needed for holding HET table +    pHetHeader->dwTableSize = sizeof(HET_TABLE_HEADER) + +                              pHetHeader->dwHashTableSize + +                              pHetHeader->dwIndexTableSize; +} + +TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty) +{ +    TMPQHetTable * pHetTable; + +    pHetTable = STORM_ALLOC(TMPQHetTable, 1); +    if(pHetTable != NULL) +    { +        pHetTable->dwIndexSizeTotal = 0; +        pHetTable->dwIndexSizeExtra = 0; +        pHetTable->dwIndexSize      = pHetTable->dwIndexSizeTotal; +        pHetTable->dwMaxFileCount   = dwMaxFileCount; +        pHetTable->dwHashTableSize  = (dwMaxFileCount * 4 / 3); +        pHetTable->dwHashBitSize    = dwHashBitSize; + +        // Size of one index is calculated from max file count +        pHetTable->dwIndexSizeTotal = GetNecessaryBitCount(dwMaxFileCount); +        pHetTable->dwIndexSizeExtra = 0; +        pHetTable->dwIndexSize      = pHetTable->dwIndexSizeTotal; + +        // Allocate hash table +        pHetTable->pHetHashes = STORM_ALLOC(BYTE, pHetTable->dwHashTableSize); +        memset(pHetTable->pHetHashes, 0, pHetTable->dwHashTableSize); + +        // If we shall create empty HET table, we have to allocate empty block index table as well +        if(bCreateEmpty) +            pHetTable->pBetIndexes = CreateBitArray(pHetTable->dwHashTableSize * pHetTable->dwIndexSizeTotal, 0xFF); + +        // Calculate masks +        pHetTable->AndMask64 = 0; +        if(dwHashBitSize != 0x40) +            pHetTable->AndMask64 = (ULONGLONG)1 << dwHashBitSize; +        pHetTable->AndMask64--; + +        pHetTable->OrMask64 = (ULONGLONG)1 << (dwHashBitSize - 1); +    } + +    return pHetTable; +} + +static TMPQHetTable * TranslateHetTable(TMPQExtTable * pExtTable) +{ +    HET_TABLE_HEADER HetHeader; +    TMPQHetTable * pHetTable = NULL; +    LPBYTE pbSrcData = (LPBYTE)(pExtTable + 1); + +    // Sanity check +    assert(pExtTable->dwSignature == HET_TABLE_SIGNATURE); +    assert(pExtTable->dwVersion == 1); + +    // Verify size of the HET table +    if(pExtTable != NULL && pExtTable->dwDataSize >= sizeof(HET_TABLE_HEADER)) +    { +        // Copy the table header in order to have it aligned and swapped +        memcpy(&HetHeader, pbSrcData, sizeof(HET_TABLE_HEADER)); +        BSWAP_ARRAY32_UNSIGNED(&HetHeader, sizeof(HET_TABLE_HEADER)); +        pbSrcData += sizeof(HET_TABLE_HEADER); + +        // Verify the size of the table in the header +        if(HetHeader.dwTableSize == pExtTable->dwDataSize) +        { +            // Create translated table +            pHetTable = CreateHetTable(HetHeader.dwMaxFileCount, HetHeader.dwHashEntrySize, false); +            if(pHetTable != NULL) +            { +                // Copy the hash table size, index size and extra bits from the HET header +                pHetTable->dwHashTableSize  = HetHeader.dwHashTableSize; +                pHetTable->dwIndexSizeTotal = HetHeader.dwIndexSizeTotal; +                pHetTable->dwIndexSizeExtra = HetHeader.dwIndexSizeExtra; + +                // Fill the hash table +                if(pHetTable->pHetHashes != NULL) +                    memcpy(pHetTable->pHetHashes, pbSrcData, pHetTable->dwHashTableSize); +                pbSrcData += pHetTable->dwHashTableSize; + +                // Copy the block index table +                pHetTable->pBetIndexes = CreateBitArray(HetHeader.dwIndexTableSize * 8, 0xFF); +                if(pHetTable->pBetIndexes != NULL) +                    memcpy(pHetTable->pBetIndexes->Elements, pbSrcData, HetHeader.dwIndexTableSize); +                pbSrcData += HetHeader.dwIndexTableSize; +            } +        } +    } + +    return pHetTable; +} + +static TMPQExtTable * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pcbHetTable) +{ +    TMPQExtTable * pExtTable = NULL; +    HET_TABLE_HEADER HetHeader; +    LPBYTE pbLinearTable = NULL; +    LPBYTE pbTrgData; +    size_t HetTableSize; + +    // Prepare header of the HET table +    CreateHetHeader(pHetTable, &HetHeader); + +    // Calculate the total size needed for holding the encrypted HET table +    HetTableSize = HetHeader.dwTableSize; + +    // Allocate space for the linear table +    pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + HetTableSize); +    if(pbLinearTable != NULL) +    { +        // Create the common ext table header +        pExtTable = (TMPQExtTable *)pbLinearTable; +        pExtTable->dwSignature = HET_TABLE_SIGNATURE; +        pExtTable->dwVersion   = 1; +        pExtTable->dwDataSize  = (DWORD)HetTableSize; +        pbTrgData = (LPBYTE)(pExtTable + 1); + +        // Copy the HET table header +        memcpy(pbTrgData, &HetHeader, sizeof(HET_TABLE_HEADER)); +        BSWAP_ARRAY32_UNSIGNED(pbTrgData, sizeof(HET_TABLE_HEADER)); +        pbTrgData += sizeof(HET_TABLE_HEADER); + +        // Copy the array of HET hashes +        memcpy(pbTrgData, pHetTable->pHetHashes, pHetTable->dwHashTableSize); +        pbTrgData += pHetTable->dwHashTableSize; + +        // Copy the bit array of BET indexes +        memcpy(pbTrgData, pHetTable->pBetIndexes->Elements, HetHeader.dwIndexTableSize); + +        // Calculate the total size of the table, including the TMPQExtTable +        if(pcbHetTable != NULL) +        { +            *pcbHetTable = (ULONGLONG)(sizeof(TMPQExtTable) + HetTableSize); +        } +    } + +    return pExtTable; +} + +DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) +{ +    TMPQHetTable * pHetTable = ha->pHetTable; +    ULONGLONG FileNameHash; +    ULONGLONG AndMask64; +    ULONGLONG OrMask64; +    ULONGLONG BetHash; +    DWORD StartIndex; +    DWORD Index; +    BYTE HetHash;                   // Upper 8 bits of the masked file name hash + +    // Do nothing if the MPQ has no HET table +    assert(ha->pHetTable != NULL); + +    // Calculate 64-bit hash of the file name +    AndMask64 = pHetTable->AndMask64; +    OrMask64 = pHetTable->OrMask64; +    FileNameHash = (HashStringJenkins(szFileName) & AndMask64) | OrMask64; + +    // Split the file name hash into two parts: +    // Part 1: The highest 8 bits of the name hash +    // Part 2: The rest of the name hash (without the highest 8 bits) +    HetHash = (BYTE)(FileNameHash >> (pHetTable->dwHashBitSize - 8)); +    BetHash = FileNameHash & (AndMask64 >> 0x08); + +    // Calculate the starting index to the hash table +    StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwHashTableSize); + +    // Go through HET table until we find a terminator +    while(pHetTable->pHetHashes[Index] != HET_ENTRY_FREE) +    { +        // Did we find match ? +        if(pHetTable->pHetHashes[Index] == HetHash) +        { +            DWORD dwFileIndex = 0; + +            // Get the index of the BetHash +            GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, +                                            pHetTable->dwIndexSize, +                                           &dwFileIndex, +                                            4); + +            // +            // TODO: This condition only happens when we are opening a MPQ +            // where some files were deleted by StormLib. Perhaps  +            // we should not allow shrinking of the file table in MPQs v 4.0? +            // assert(dwFileIndex <= ha->dwFileTableSize); +            // + +            // Verify the BetHash against the entry in the table of BET hashes +            if(dwFileIndex <= ha->dwFileTableSize && ha->pFileTable[dwFileIndex].BetHash == BetHash) +                return dwFileIndex; +        } + +        // Move to the next entry in the primary search table +        // If we came to the start index again, we are done +        Index = (Index + 1) % pHetTable->dwHashTableSize; +        if(Index == StartIndex) +            break; +    } + +    // File not found +    return HASH_ENTRY_FREE; +} + +DWORD AllocateHetEntry( +    TMPQArchive * ha, +    TFileEntry * pFileEntry) +{ +    TMPQHetTable * pHetTable = ha->pHetTable; +    ULONGLONG FileNameHash; +    ULONGLONG AndMask64; +    ULONGLONG OrMask64; +    ULONGLONG BetHash; +    DWORD FreeHetIndex = HASH_ENTRY_FREE; +    DWORD dwFileIndex; +    DWORD StartIndex; +    DWORD Index; +    BYTE HetHash;                   // Upper 8 bits of the masked file name hash + +    // Do nothing if the MPQ has no HET table +    assert(ha->pHetTable != NULL); + +    // Calculate 64-bit hash of the file name +    AndMask64 = pHetTable->AndMask64; +    OrMask64 = pHetTable->OrMask64; +    FileNameHash = (HashStringJenkins(pFileEntry->szFileName) & AndMask64) | OrMask64; + +    // Calculate the starting index to the hash table +    StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwHashTableSize); + +    // Split the file name hash into two parts: +    // Part 1: The highest 8 bits of the name hash +    // Part 2: The rest of the name hash (without the highest 8 bits) +    HetHash = (BYTE)(FileNameHash >> (pHetTable->dwHashBitSize - 8)); +    BetHash = FileNameHash & (AndMask64 >> 0x08); + +    // Go through HET table until we find a terminator +    for(;;) +    { +        // Check for entries that might have been deleted +        if(pHetTable->pHetHashes[Index] == HET_ENTRY_DELETED) +        { +            DWORD dwInvalidBetIndex = (1 << pHetTable->dwIndexSizeTotal) - 1; +            DWORD dwBetIndex = 0; + +            // Verify the BET index. If it's really free, we can use it +            dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); +            GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, +                                            pHetTable->dwIndexSize, +                                           &dwBetIndex, +                                            4); +             +            if(dwBetIndex == dwInvalidBetIndex) +            { +                FreeHetIndex = Index; +                break; +            } +        } + +        // Is that entry free ? +        if(pHetTable->pHetHashes[Index] == HET_ENTRY_FREE) +        { +            FreeHetIndex = Index; +            break; +        } + +        // Move to the next entry in the primary search table +        // If we came to the start index again, we are done +        Index = (Index + 1) % pHetTable->dwHashTableSize; +        if(Index == StartIndex) +            return HASH_ENTRY_FREE; +    } + +    // Fill the HET table entry +    dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); +    pHetTable->pHetHashes[FreeHetIndex] = HetHash; +    SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * FreeHetIndex, +                                    pHetTable->dwIndexSize, +                                   &dwFileIndex, +                                    4); +    // Fill the file entry +    pFileEntry->BetHash    = BetHash; +    pFileEntry->dwHetIndex = FreeHetIndex; +    return FreeHetIndex; +} + +void FreeHetTable(TMPQHetTable * pHetTable) +{ +    if(pHetTable != NULL) +    { +        if(pHetTable->pHetHashes != NULL) +            STORM_FREE(pHetTable->pHetHashes); +        if(pHetTable->pBetIndexes != NULL) +            STORM_FREE(pHetTable->pBetIndexes); + +        STORM_FREE(pHetTable); +    } +} + +//----------------------------------------------------------------------------- +// Support for BET table + +static void CreateBetHeader( +    TMPQArchive * ha, +    PBET_TABLE_HEADER pBetHeader) +{ +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pFileEntry; +    ULONGLONG MaxByteOffset = 0; +    DWORD FlagArray[MAX_FLAG_INDEX]; +    DWORD dwMaxFlagIndex = 0; +    DWORD dwMaxFileSize = 0; +    DWORD dwMaxCmpSize = 0; +    DWORD dwFlagIndex; + +    // Initialize array of flag combinations +    InitFileFlagArray(FlagArray); + +    // Get the maximum values for the BET table +    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +    { +        // Highest file position in the MPQ +        if(pFileEntry->ByteOffset > MaxByteOffset) +            MaxByteOffset = pFileEntry->ByteOffset; + +        // Biggest file size +        if(pFileEntry->dwFileSize > dwMaxFileSize) +            dwMaxFileSize = pFileEntry->dwFileSize; + +        // Biggest compressed size +        if(pFileEntry->dwCmpSize > dwMaxCmpSize) +            dwMaxCmpSize = pFileEntry->dwCmpSize; + +        // Check if this flag was there before +        dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); +        if(dwFlagIndex > dwMaxFlagIndex) +            dwMaxFlagIndex = dwFlagIndex; +    } + +    // Now save bit count for every piece of file information +    pBetHeader->dwBitIndex_FilePos   = 0; +    pBetHeader->dwBitCount_FilePos   = GetNecessaryBitCount(MaxByteOffset); + +    pBetHeader->dwBitIndex_FileSize  = pBetHeader->dwBitIndex_FilePos + pBetHeader->dwBitCount_FilePos; +    pBetHeader->dwBitCount_FileSize  = GetNecessaryBitCount(dwMaxFileSize); + +    pBetHeader->dwBitIndex_CmpSize   = pBetHeader->dwBitIndex_FileSize + pBetHeader->dwBitCount_FileSize; +    pBetHeader->dwBitCount_CmpSize   = GetNecessaryBitCount(dwMaxCmpSize); + +    pBetHeader->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_CmpSize + pBetHeader->dwBitCount_CmpSize; +    pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwMaxFlagIndex + 1); + +    pBetHeader->dwBitIndex_Unknown   = pBetHeader->dwBitIndex_FlagIndex + pBetHeader->dwBitCount_FlagIndex; +    pBetHeader->dwBitCount_Unknown   = 0; + +    // Calculate the total size of one entry +    pBetHeader->dwTableEntrySize     = pBetHeader->dwBitCount_FilePos + +                                       pBetHeader->dwBitCount_FileSize + +                                       pBetHeader->dwBitCount_CmpSize + +                                       pBetHeader->dwBitCount_FlagIndex + +                                       pBetHeader->dwBitCount_Unknown; + +    // Save the file count and flag count +    pBetHeader->dwFileCount          = ha->dwFileTableSize; +    pBetHeader->dwFlagCount          = dwMaxFlagIndex + 1; +    pBetHeader->dwUnknown08          = 0x10; + +    // Save the total size of the BET hash +    pBetHeader->dwBetHashSizeTotal   = ha->pHetTable->dwHashBitSize - 0x08; +    pBetHeader->dwBetHashSizeExtra   = 0; +    pBetHeader->dwBetHashSize        = pBetHeader->dwBetHashSizeTotal; +    pBetHeader->dwBetHashArraySize   = ((pBetHeader->dwBetHashSizeTotal * pBetHeader->dwFileCount) + 7) / 8; + +    // Save the total table size +    pBetHeader->dwTableSize          = sizeof(BET_TABLE_HEADER) + +                                       pBetHeader->dwFlagCount * sizeof(DWORD) + +                                     ((pBetHeader->dwTableEntrySize * pBetHeader->dwFileCount) + 7) / 8 + +                                      pBetHeader->dwBetHashArraySize; +} + +TMPQBetTable * CreateBetTable(DWORD dwFileCount) +{ +    TMPQBetTable * pBetTable; + +    // Allocate BET table +    pBetTable = STORM_ALLOC(TMPQBetTable, 1); +    if(pBetTable != NULL) +    { +        memset(pBetTable, 0, sizeof(TMPQBetTable)); +        pBetTable->dwFileCount = dwFileCount; +    } + +    return pBetTable; +} + +static TMPQBetTable * TranslateBetTable( +    TMPQArchive * ha, +    TMPQExtTable * pExtTable) +{ +    BET_TABLE_HEADER BetHeader; +    TMPQBetTable * pBetTable = NULL; +    LPBYTE pbSrcData = (LPBYTE)(pExtTable + 1); +    DWORD LengthInBytes; + +    // Sanity check +    assert(pExtTable->dwSignature == BET_TABLE_SIGNATURE); +    assert(pExtTable->dwVersion == 1); +    assert(ha->pHetTable != NULL); +    ha = ha; + +    // Verify size of the HET table +    if(pExtTable != NULL && pExtTable->dwDataSize >= sizeof(BET_TABLE_HEADER)) +    { +        // Copy the table header in order to have it aligned and swapped +        memcpy(&BetHeader, pbSrcData, sizeof(BET_TABLE_HEADER)); +        BSWAP_ARRAY32_UNSIGNED(&BetHeader, sizeof(BET_TABLE_HEADER)); +        pbSrcData += sizeof(BET_TABLE_HEADER); + +        // Some MPQs affected by a bug in StormLib have pBetTable->dwFileCount +        // greater than ha->dwMaxFileCount +        if(BetHeader.dwFileCount > ha->dwMaxFileCount) +            return NULL; + +        // Verify the size of the table in the header +        if(BetHeader.dwTableSize == pExtTable->dwDataSize) +        { +            // Create translated table +            pBetTable = CreateBetTable(BetHeader.dwFileCount); +            if(pBetTable != NULL) +            { +                // Copy the variables from the header to the BetTable +                pBetTable->dwTableEntrySize     = BetHeader.dwTableEntrySize; +                pBetTable->dwBitIndex_FilePos   = BetHeader.dwBitIndex_FilePos; +                pBetTable->dwBitIndex_FileSize  = BetHeader.dwBitIndex_FileSize; +                pBetTable->dwBitIndex_CmpSize   = BetHeader.dwBitIndex_CmpSize; +                pBetTable->dwBitIndex_FlagIndex = BetHeader.dwBitIndex_FlagIndex; +                pBetTable->dwBitIndex_Unknown   = BetHeader.dwBitIndex_Unknown; +                pBetTable->dwBitCount_FilePos   = BetHeader.dwBitCount_FilePos; +                pBetTable->dwBitCount_FileSize  = BetHeader.dwBitCount_FileSize; +                pBetTable->dwBitCount_CmpSize   = BetHeader.dwBitCount_CmpSize; +                pBetTable->dwBitCount_FlagIndex = BetHeader.dwBitCount_FlagIndex; +                pBetTable->dwBitCount_Unknown   = BetHeader.dwBitCount_Unknown; + +                // Since we don't know what the "unknown" is, we'll assert when it's nonzero +                assert(pBetTable->dwBitCount_Unknown == 0); + +                // Allocate array for flags +                if(BetHeader.dwFlagCount != 0) +                { +                    // Allocate array for file flags and load it +                    pBetTable->pFileFlags = STORM_ALLOC(DWORD, BetHeader.dwFlagCount); +                    if(pBetTable->pFileFlags != NULL) +                    { +                        LengthInBytes = BetHeader.dwFlagCount * sizeof(DWORD); +                        memcpy(pBetTable->pFileFlags, pbSrcData, LengthInBytes); +                        BSWAP_ARRAY32_UNSIGNED(pBetTable->pFileFlags, LengthInBytes); +                        pbSrcData += LengthInBytes; +                    } + +                    // Save the number of flags +                    pBetTable->dwFlagCount = BetHeader.dwFlagCount; +                } + +                // Load the bit-based file table +                pBetTable->pFileTable = CreateBitArray(pBetTable->dwTableEntrySize * BetHeader.dwFileCount, 0); +                LengthInBytes = (pBetTable->pFileTable->NumberOfBits + 7) / 8; +                if(pBetTable->pFileTable != NULL) +                    memcpy(pBetTable->pFileTable->Elements, pbSrcData, LengthInBytes); +                pbSrcData += LengthInBytes; + +                // Fill the sizes of BET hash +                pBetTable->dwBetHashSizeTotal = BetHeader.dwBetHashSizeTotal; +                pBetTable->dwBetHashSizeExtra = BetHeader.dwBetHashSizeExtra; +                pBetTable->dwBetHashSize      = BetHeader.dwBetHashSize; +                 +                // Create and load the array of BET hashes +                pBetTable->pBetHashes = CreateBitArray(pBetTable->dwBetHashSizeTotal * BetHeader.dwFileCount, 0); +                LengthInBytes = (pBetTable->pBetHashes->NumberOfBits + 7) / 8; +                if(pBetTable->pBetHashes != NULL) +                    memcpy(pBetTable->pBetHashes->Elements, pbSrcData, LengthInBytes); +                pbSrcData += BetHeader.dwBetHashArraySize; + +                // Dump both tables +//              DumpHetAndBetTable(ha->pHetTable, pBetTable); +            } +        } +    } + +    return pBetTable; +} + +TMPQExtTable * TranslateBetTable( +    TMPQArchive * ha, +    ULONGLONG * pcbBetTable) +{ +    TMPQExtTable * pExtTable = NULL; +    BET_TABLE_HEADER BetHeader; +    TBitArray * pBitArray = NULL; +    LPBYTE pbLinearTable = NULL; +    LPBYTE pbTrgData; +    size_t BetTableSize; +    DWORD LengthInBytes; +    DWORD FlagArray[MAX_FLAG_INDEX]; +    DWORD i; + +    // Calculate the bit sizes of various entries +    InitFileFlagArray(FlagArray); +    CreateBetHeader(ha, &BetHeader); + +    // Calculate the size of the BET table +    BetTableSize = sizeof(BET_TABLE_HEADER) + +                   BetHeader.dwFlagCount * sizeof(DWORD) + +                 ((BetHeader.dwTableEntrySize * BetHeader.dwFileCount) + 7) / 8 + +                   BetHeader.dwBetHashArraySize; + +    // Allocate space +    pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + BetTableSize); +    if(pbLinearTable != NULL) +    { +        // Create the common ext table header +        pExtTable = (TMPQExtTable *)pbLinearTable; +        pExtTable->dwSignature = BET_TABLE_SIGNATURE; +        pExtTable->dwVersion   = 1; +        pExtTable->dwDataSize  = (DWORD)BetTableSize; +        pbTrgData = (LPBYTE)(pExtTable + 1); + +        // Copy the BET table header +        memcpy(pbTrgData, &BetHeader, sizeof(BET_TABLE_HEADER)); +        BSWAP_ARRAY32_UNSIGNED(pbTrgData, sizeof(BET_TABLE_HEADER)); +        pbTrgData += sizeof(BET_TABLE_HEADER); + +        // Save the bit-based block table +        pBitArray = CreateBitArray(BetHeader.dwFileCount * BetHeader.dwTableEntrySize, 0); +        if(pBitArray != NULL) +        { +            TFileEntry * pFileEntry = ha->pFileTable; +            DWORD dwFlagIndex = 0; +            DWORD nBitOffset = 0; + +            // Construct the array of flag values and bit-based file table +            for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++) +            { +                // +                // Note: Blizzard MPQs contain valid values even for non-existant files +                // (FilePos, FileSize, CmpSize and FlagIndex) +                // Note: If flags is zero, it must be in the flag table too !!! +                // +                 +                // Save the byte offset +                SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FilePos, +                                   BetHeader.dwBitCount_FilePos, +                                  &pFileEntry->ByteOffset, +                                   8); +                SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FileSize, +                                   BetHeader.dwBitCount_FileSize, +                                  &pFileEntry->dwFileSize, +                                   4); +                SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_CmpSize, +                                   BetHeader.dwBitCount_CmpSize, +                                  &pFileEntry->dwCmpSize, +                                   4); + +                // Save the flag index +                dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); +                SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FlagIndex, +                                   BetHeader.dwBitCount_FlagIndex, +                                  &dwFlagIndex, +                                   4); + +                // Move the bit offset +                nBitOffset += BetHeader.dwTableEntrySize; +            } + +            // Write the array of flags +            LengthInBytes = BetHeader.dwFlagCount * sizeof(DWORD); +            memcpy(pbTrgData, FlagArray, LengthInBytes); +            BSWAP_ARRAY32_UNSIGNED(pbTrgData, LengthInBytes); +            pbTrgData += LengthInBytes; + +            // Write the bit-based block table +            LengthInBytes = (pBitArray->NumberOfBits + 7) / 8; +            memcpy(pbTrgData, pBitArray->Elements, LengthInBytes); +            pbTrgData += LengthInBytes; + +            // Free the bit array +            STORM_FREE(pBitArray); +        } + +        // Create bit array for BET hashes +        pBitArray = CreateBitArray(BetHeader.dwBetHashSizeTotal * BetHeader.dwFileCount, 0); +        if(pBitArray != NULL) +        { +            TFileEntry * pFileEntry = ha->pFileTable; +            ULONGLONG AndMask64 = ha->pHetTable->AndMask64; +            ULONGLONG OrMask64 = ha->pHetTable->OrMask64; + +            for(i = 0; i < BetHeader.dwFileCount; i++) +            { +                ULONGLONG FileNameHash = 0; + +                // Calculate 64-bit hash of the file name +                if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) +                { +                    FileNameHash = (HashStringJenkins(pFileEntry->szFileName) & AndMask64) | OrMask64; +                    FileNameHash = FileNameHash & (AndMask64 >> 0x08); +                } + +                // Insert the name hash to the bit array +                SetBits(pBitArray, BetHeader.dwBetHashSizeTotal * i, +                                   BetHeader.dwBetHashSize, +                                  &FileNameHash, +                                   8); + +                // Move to the next file entry +                pFileEntry++; +            } + +            // Write the array of BET hashes +            LengthInBytes = (pBitArray->NumberOfBits + 7) / 8; +            memcpy(pbTrgData, pBitArray->Elements, LengthInBytes); +            pbTrgData += LengthInBytes; + +            // Free the bit array +            STORM_FREE(pBitArray); +        } + +        // Write the size of the BET table in the MPQ +        if(pcbBetTable != NULL) +        { +            *pcbBetTable = (ULONGLONG)(sizeof(TMPQExtTable) + BetTableSize); +        } +    } + +    return pExtTable; +} + +void FreeBetTable(TMPQBetTable * pBetTable) +{ +    if(pBetTable != NULL) +    { +        if(pBetTable->pFileTable != NULL) +            STORM_FREE(pBetTable->pFileTable); +        if(pBetTable->pFileFlags != NULL) +            STORM_FREE(pBetTable->pFileFlags); +        if(pBetTable->pBetHashes != NULL) +            STORM_FREE(pBetTable->pBetHashes); + +        STORM_FREE(pBetTable); +    } +} + +//----------------------------------------------------------------------------- +// Support for file table + +TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName) +{ +    TMPQHash * pHash; +    DWORD dwFileIndex; + +    // If we have HET table in the MPQ, try to find the file in HET table +    if(ha->pHetTable != NULL) +    { +        dwFileIndex = GetFileIndex_Het(ha, szFileName); +        if(dwFileIndex != HASH_ENTRY_FREE) +            return ha->pFileTable + dwFileIndex; +    } + +    // Otherwise, perform the file search in the classic hash table +    if(ha->pHashTable != NULL) +    { +        pHash = GetHashEntryAny(ha, szFileName); +        if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) +            return ha->pFileTable + pHash->dwBlockIndex; +    } +     +    // Not found +    return NULL; +} + +TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ +    TMPQHash * pHash; +    DWORD dwFileIndex; + +    // If we have HET table in the MPQ, try to find the file in HET table +    if(ha->pHetTable != NULL) +    { +        dwFileIndex = GetFileIndex_Het(ha, szFileName); +        if(dwFileIndex != HASH_ENTRY_FREE) +            return ha->pFileTable + dwFileIndex; +    } + +    // Otherwise, perform the file search in the classic hash table +    if(ha->pHashTable != NULL) +    { +        pHash = GetHashEntryLocale(ha, szFileName, lcLocale); +        if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) +            return ha->pFileTable + pHash->dwBlockIndex; +    } +     +    // Not found +    return NULL; +} + +TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ +    TMPQHash * pHash; +    DWORD dwFileIndex; + +    // If we have HET table in the MPQ, try to find the file in HET table +    if(ha->pHetTable != NULL) +    { +        dwFileIndex = GetFileIndex_Het(ha, szFileName); +        if(dwFileIndex != HASH_ENTRY_FREE) +            return ha->pFileTable + dwFileIndex; +    } + +    // Otherwise, perform the file search in the classic hash table +    if(ha->pHashTable != NULL) +    { +        pHash = GetHashEntryExact(ha, szFileName, lcLocale); +        if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) +            return ha->pFileTable + pHash->dwBlockIndex; +    } +     +    // Not found +    return NULL; +} + +TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex) +{ +    // For MPQs with classic hash table +    if(dwIndex < ha->dwFileTableSize) +        return ha->pFileTable + dwIndex; +    return NULL; +} + +void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName) +{ +    // Sanity check +    assert(pFileEntry != NULL); + +    // If the file name is pseudo file name, free it at this point +    if(IsPseudoFileName(pFileEntry->szFileName, NULL)) +    { +        if(pFileEntry->szFileName != NULL) +            STORM_FREE(pFileEntry->szFileName); +        pFileEntry->szFileName = NULL; +    } + +    // Only allocate new file name if it's not there yet +    if(pFileEntry->szFileName == NULL) +    { +        pFileEntry->szFileName = STORM_ALLOC(char, strlen(szFileName) + 1); +        if(pFileEntry->szFileName != NULL) +            strcpy(pFileEntry->szFileName, szFileName); +    } +} + + +// Finds a free file entry. Does NOT increment table size. +TFileEntry * FindFreeFileEntry(TMPQArchive * ha) +{ +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pFreeEntry = NULL; +    TFileEntry * pFileEntry; + +    // Try to find a free entry +    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +    { +        // If that entry is free, we reuse it +        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) +        { +            pFreeEntry = pFileEntry; +            break; +        } + +        // +        // Note: Files with "delete marker" are not deleted. +        // Don't consider them free entries +        // +    } + +    // Do we have a deleted entry? +    if(pFreeEntry != NULL) +    { +        ClearFileEntry(ha, pFreeEntry); +        return pFreeEntry; +    } + +    // If no file entry within the existing file table is free, +    // we try the reserve space after current file table +    if(ha->dwFileTableSize < ha->dwMaxFileCount) +        return ha->pFileTable + ha->dwFileTableSize; + +    // If we reached maximum file count, we cannot add more files to the MPQ +    assert(ha->dwFileTableSize == ha->dwMaxFileCount); +    return NULL; +} + + +TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +{ +    TFileEntry * pFileEntry = NULL; +    TMPQHash * pHash; +    DWORD dwHashIndex; +    DWORD dwFileIndex; +    bool bHashEntryExists = false; +    bool bHetEntryExists = false; + +    // If the archive has classic hash table, we try to +    // find the file in the hash table +    if(ha->pHashTable != NULL) +    { +        // If the hash entry is already there, we reuse the file entry +        pHash = GetHashEntryExact(ha, szFileName, lcLocale); +        if(pHash != NULL) +        { +            pFileEntry = ha->pFileTable + pHash->dwBlockIndex; +            bHashEntryExists = true; +        } +    } + +    // If the archive has HET table, try to use it for +    // finding the file +    if(ha->pHetTable != NULL) +    { +        dwFileIndex = GetFileIndex_Het(ha, szFileName); +        if(dwFileIndex != HASH_ENTRY_FREE) +        { +            pFileEntry = ha->pFileTable + dwFileIndex; +            bHetEntryExists = true; +        } +    } + +    // If still haven't found the file entry, we allocate new one +    if(pFileEntry == NULL) +    { +        pFileEntry = FindFreeFileEntry(ha); +        if(pFileEntry == NULL) +            return NULL; +    } + +    // Fill the rest of the file entry +    pFileEntry->ByteOffset = 0; +    pFileEntry->FileTime   = 0; +    pFileEntry->dwFileSize = 0; +    pFileEntry->dwCmpSize  = 0; +    pFileEntry->dwFlags    = 0; +    pFileEntry->lcLocale   = (USHORT)lcLocale; +    pFileEntry->wPlatform  = 0; +    pFileEntry->dwCrc32    = 0; +    memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); + +    // Allocate space for file name, if it's not there yet +    AllocateFileName(pFileEntry, szFileName); + +    // If the free file entry is at the end of the file table, +    // we have to increment file table size +    if(pFileEntry == ha->pFileTable + ha->dwFileTableSize) +    { +        assert(ha->dwFileTableSize < ha->dwMaxFileCount); +        ha->pHeader->dwBlockTableSize++; +        ha->dwFileTableSize++; +    } + +    // If the MPQ has hash table, we have to insert the new entry into the hash table +    if(ha->pHashTable != NULL && bHashEntryExists == false) +    { +        dwHashIndex = AllocateHashEntry(ha, pFileEntry); +        assert(dwHashIndex != HASH_ENTRY_FREE); +    } + +    // If the MPQ has HET table, we have to insert it to the HET table as well +    if(ha->pHetTable != NULL && bHetEntryExists == false) +    { +        // TODO: Does HET table even support locales? +        // Most probably, Blizzard gave up that silly idea long ago. +        dwHashIndex = AllocateHetEntry(ha, pFileEntry); +        assert(dwHashIndex != HASH_ENTRY_FREE); +    } + +    // Return the file entry +    return pFileEntry; +} + +int RenameFileEntry( +    TMPQArchive * ha, +    TFileEntry * pFileEntry, +    const char * szNewFileName) +{ +    TMPQHash * pHash; +    DWORD dwFileIndex; +    int nError = ERROR_SUCCESS; + +    // If the MPQ has classic hash table, clear the entry there +    if(ha->pHashTable != NULL) +    { +        assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); + +        pHash = ha->pHashTable + pFileEntry->dwHashIndex; +        memset(pHash, 0xFF, sizeof(TMPQHash)); +        pHash->dwBlockIndex = HASH_ENTRY_DELETED; +    } + +    // If the MPQ has HET table, clear the entry there as well +    if(ha->pHetTable != NULL) +    { +        TMPQHetTable * pHetTable = ha->pHetTable; +        DWORD dwInvalidFileIndex = (1 << pHetTable->dwIndexSizeTotal) - 1; + +        assert(pFileEntry->dwHetIndex < pHetTable->dwHashTableSize); + +        // Clear the entry in the HET hash array +        pHetTable->pHetHashes[pFileEntry->dwHetIndex] = HET_ENTRY_DELETED; + +        // Set the BET index to invalid index +        SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, +                                        pHetTable->dwIndexSize, +                                       &dwInvalidFileIndex, +                                        4); +    } + +    // Free the old file name +    if(pFileEntry->szFileName != NULL) +        STORM_FREE(pFileEntry->szFileName); +    pFileEntry->szFileName = NULL; + +    // Allocate new file name +    AllocateFileName(pFileEntry, szNewFileName); + +    // Now find a hash entry for the new file name +    if(ha->pHashTable != NULL) +    { +        // Try to find the hash table entry for the new file name +        // Note: If this fails, we leave the MPQ in a corrupt state +        dwFileIndex = AllocateHashEntry(ha, pFileEntry); +        if(dwFileIndex == HASH_ENTRY_FREE) +            nError = ERROR_FILE_CORRUPT; +    } + +    // If the archive has HET table, we have to allocate HET table for the file as well +    // finding the file +    if(ha->pHetTable != NULL) +    { +        dwFileIndex = AllocateHetEntry(ha, pFileEntry); +        if(dwFileIndex == HASH_ENTRY_FREE) +            nError = ERROR_FILE_CORRUPT; +    } + +    // Invalidate the entries for (listfile) and (attributes) +    // After we are done with MPQ changes, we need to re-create them +    InvalidateInternalFiles(ha); +    return nError; +} + +void ClearFileEntry( +    TMPQArchive * ha, +    TFileEntry * pFileEntry) +{ +    TMPQHash * pHash = NULL; + +    // If the MPQ has classic hash table, clear the entry there +    if(ha->pHashTable != NULL) +    { +        assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); + +        pHash = ha->pHashTable + pFileEntry->dwHashIndex; +        memset(pHash, 0xFF, sizeof(TMPQHash)); +        pHash->dwBlockIndex = HASH_ENTRY_DELETED; +    } + +    // If the MPQ has HET table, clear the entry there as well +    if(ha->pHetTable != NULL) +    { +        TMPQHetTable * pHetTable = ha->pHetTable; +        DWORD dwInvalidFileIndex = (1 << pHetTable->dwIndexSizeTotal) - 1; + +        assert(pFileEntry->dwHetIndex < pHetTable->dwHashTableSize); + +        // Clear the entry in the HET hash array +        pHetTable->pHetHashes[pFileEntry->dwHetIndex] = HET_ENTRY_DELETED; + +        // Set the BET index to invalid index +        SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, +                                        pHetTable->dwIndexSize, +                                       &dwInvalidFileIndex, +                                        4); +    } + +    // Free the file name, and set the file entry as deleted +    if(pFileEntry->szFileName != NULL) +        STORM_FREE(pFileEntry->szFileName); + +    // Invalidate the file entry +    memset(pFileEntry, 0, sizeof(TFileEntry)); +} + +int FreeFileEntry( +    TMPQArchive * ha, +    TFileEntry * pFileEntry) +{ +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pTempEntry; +    int nError = ERROR_SUCCESS; + +    // +    // If we have HET table, we cannot just get rid of the file +    // Doing so would lead to empty gaps in the HET table +    // We have to keep BET hash, hash index, HET index, locale, platform and file name +    // + +    if(ha->pHetTable == NULL) +    { +        TFileEntry * pLastFileEntry = ha->pFileTable + ha->dwFileTableSize - 1; +        TFileEntry * pLastUsedEntry = pLastFileEntry; + +        // Zero the file entry +        ClearFileEntry(ha, pFileEntry); + +        // Now there is a chance that we created a chunk of free +        // file entries at the end of the file table. We check this +        // and eventually free all deleted file entries at the end +        for(pTempEntry = ha->pFileTable; pTempEntry < pFileTableEnd; pTempEntry++) +        { +            // Is that an occupied file entry? +            if(pTempEntry->dwFlags & MPQ_FILE_EXISTS) +                pLastUsedEntry = pTempEntry; +        } + +        // Can we free some entries at the end?                  +        if(pLastUsedEntry < pLastFileEntry) +        { +            // Fix the size of the file table entry +            ha->dwFileTableSize = (DWORD)(pLastUsedEntry - ha->pFileTable) + 1; +            ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; +        } +    } +    else +    { +        // Note: Deleted entries in Blizzard MPQs version 4.0 +        // normally contain valid byte offset and length +        pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS; +        nError = ERROR_SUCCESS; +    } + +    return nError; +} + +void InvalidateInternalFiles(TMPQArchive * ha) +{ +    TFileEntry * pFileEntry; + +    // Invalidate the (listfile), if not done yet +    if(!(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)) +    { +        pFileEntry = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); +        if(pFileEntry != NULL) +            FreeFileEntry(ha, pFileEntry); +        ha->dwFlags |= MPQ_FLAG_INV_LISTFILE; +    } + +    // Invalidate the (attributes), if not done yet +    if(!(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)) +    { +        pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); +        if(pFileEntry != NULL) +            FreeFileEntry(ha, pFileEntry); +        ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES; +    } + +    // Remember that the MPQ has been changed and it will be necessary +    // to update the tables +    ha->dwFlags |= MPQ_FLAG_CHANGED; +} + +//----------------------------------------------------------------------------- +// Functions that loads and verify MPQ data bitmap + +int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete) +{ +    TMPQBitmap * pBitmap = NULL; +    TMPQBitmap DataBitmap; +    ULONGLONG BitmapOffset; +    ULONGLONG EndOfMpq; +    DWORD DataBlockCount = 0; +    DWORD BitmapByteSize; +    DWORD WholeByteCount; +    DWORD ExtraBitsCount; + +    // Is there enough space for a MPQ bitmap? +    EndOfMpq = ha->MpqPos + ha->pHeader->ArchiveSize64; +    FileSize = FileSize - sizeof(TMPQBitmap); +    if(FileSize > EndOfMpq) +    { +        // Try to load the data bitmap from the end of the file +        if(FileStream_Read(ha->pStream, &FileSize, &DataBitmap, sizeof(TMPQBitmap))) +        { +            // Is it a valid data bitmap? +            BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap)); +            if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE) +            { +                // We assume that MPQs with data bitmap begin at position 0 +                assert(ha->MpqPos == 0); + +                // Calculate the number of extra bytes for data bitmap +                DataBlockCount = (DWORD)(((ha->pHeader->ArchiveSize64 - 1) / DataBitmap.dwBlockSize) + 1); +                BitmapByteSize = ((DataBlockCount - 1) / 8) + 1; + +                // Verify the data block size +                BitmapOffset = ((ULONGLONG)DataBitmap.dwMapOffsetHi << 32) | DataBitmap.dwMapOffsetLo; +                assert((DWORD)(FileSize - BitmapOffset) == BitmapByteSize); + +                // Allocate space for the data bitmap +                pBitmap = (TMPQBitmap *)STORM_ALLOC(BYTE, sizeof(TMPQBitmap) + BitmapByteSize); +                if(pBitmap != NULL) +                { +                    // Copy the bitmap header +                    memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap)); + +                    // Read the remaining part +                    if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize)) +                    { +                        STORM_FREE(pBitmap); +                        pBitmap = NULL; +                    } +                } +            } +        } +    } + +    // If the caller asks for file completeness, check it +    if(pBitmap != NULL && pbFileIsComplete != NULL) +    { +        LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); +        DWORD i; +        bool bFileIsComplete = true; + +        // Calculate the number of whole bytes and extra bits of the bitmap +        WholeByteCount = (DataBlockCount / 8); +        ExtraBitsCount = (DataBlockCount & 7); + +        // Verify the whole bytes - their value must be 0xFF +        for(i = 0; i < WholeByteCount; i++) +        { +            if(pbBitmap[i] != 0xFF) +                bFileIsComplete = false; +        } + +        // If there are extra bits, calculate the mask +        if(ExtraBitsCount != 0) +        { +            BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); +             +            if(pbBitmap[i] != ExpectedValue) +                bFileIsComplete = false; +        } + +        // Give the result to the caller +        *pbFileIsComplete = bFileIsComplete; +    } + +    ha->pBitmap = pBitmap; +    return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Support for file tables - hash table, block table, hi-block table + +int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) +{ +    TMPQHash * pHashTable; + +    // Sanity checks +    assert((dwHashTableSize & (dwHashTableSize - 1)) == 0); +    assert(ha->pHashTable == NULL); + +    // Create the hash table +    pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize); +    if(pHashTable == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Fill it +    memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash)); +    ha->pHashTable = pHashTable; + +    // Set the max file count, if needed +    if(ha->pHetTable == NULL) +        ha->dwMaxFileCount = dwHashTableSize; +    return ERROR_SUCCESS; +} + +TMPQHash * LoadHashTable(TMPQArchive * ha) +{ +    TMPQHeader * pHeader = ha->pHeader; +    ULONGLONG ByteOffset; +    TMPQHash * pHashTable; +    DWORD dwTableSize; +    DWORD dwCmpSize; +    int nError; + +    // If the MPQ has no hash table, do nothing +    if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) +        return NULL; + +    // If the hash table size is zero, do nothing +    if(pHeader->dwHashTableSize == 0) +        return NULL; + +    // Allocate buffer for the hash table +    dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); +    pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize); +    if(pHashTable == NULL) +        return NULL; + +    // Compressed size of the hash table +    dwCmpSize = (DWORD)pHeader->HashTableSize64; + +    //  +    // Load the table from the MPQ, with decompression +    // +    // Note: We will NOT check if the hash table is properly decrypted. +    // Some MPQ protectors corrupt the hash table by rewriting part of it. +    // Hash table, the way how it works, allows arbitrary values for unused entries. +    //  + +    ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); +    nError = LoadMpqTable(ha, ByteOffset, pHashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE); +    if(nError != ERROR_SUCCESS) +    { +        STORM_FREE(pHashTable); +        pHashTable = NULL; +    } + +    // Return the hash table +    return pHashTable; +} + +static void FixBlockTableSize( +    TMPQArchive * ha, +    TMPQBlock * pBlockTable, +    DWORD dwClaimedSize) +{ +    TMPQHeader * pHeader = ha->pHeader; +    ULONGLONG BlockTableStart; +    ULONGLONG BlockTableEnd; +    ULONGLONG FileDataStart; + +    // Only perform this check on MPQs version 1.0 +    if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1) +    { +        // Calculate claimed block table begin and end +        BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); +        BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + +        for(DWORD i = 0; i < dwClaimedSize; i++) +        { +            // If the block table end goes into that file, fix the block table end +            FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos; +            if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart) +            { +                dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock)); +                BlockTableEnd = FileDataStart; +            } +        } +    } + +    // Fix the block table size +    pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock); +    pHeader->dwBlockTableSize = dwClaimedSize; +} + +TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize) +{ +    TMPQHeader * pHeader = ha->pHeader; +    TMPQBlock * pBlockTable; +    ULONGLONG ByteOffset; +    DWORD dwTableSize; +    DWORD dwCmpSize; +    int nError; + +    // Do nothing if the block table position is zero +    if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0) +        return NULL; + +    // Do nothing if the block table size is zero +    if(pHeader->dwBlockTableSize == 0) +        return NULL; + +    // Sanity check, enforced by LoadAnyHashTable +    assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize); + +    // Calculate sizes of both tables +    ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); +    dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); +    dwCmpSize = (DWORD)pHeader->BlockTableSize64; + +    // Allocate space for the block table +    // Note: pHeader->dwBlockTableSize can be zero !!! +    pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount); +    if(pBlockTable == NULL) +        return NULL; + +    // Fill the block table with zeros +    memset(pBlockTable, 0, dwTableSize); + +    // I found a MPQ which claimed 0x200 entries in the block table, +    // but the file was cut and there was only 0x1A0 entries. +    // We will handle this case properly. +    if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize) +    { +        pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock)); +        pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); +        dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); +    } + +    // +    // One of the first cracked versions of Diablo I had block table unencrypted  +    // StormLib does NOT support such MPQs anymore, as they are incompatible +    // with compressed block table feature +    // + +    // Load the block table +    nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE); +    if(nError != ERROR_SUCCESS) +    { +        // Failed, sorry +        STORM_FREE(pBlockTable); +        return NULL; +    } + +    // Defense against MPQs that claim block table to be bigger than it really is +    FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize); +    return pBlockTable; +} + +int LoadHetTable(TMPQArchive * ha) +{ +    TMPQExtTable * pExtTable; +    TMPQHeader * pHeader = ha->pHeader; +    int nError = ERROR_SUCCESS; + +    // If the HET table position is not NULL, we expect +    // both HET and BET tables to be present. +    if(pHeader->HetTablePos64 != 0) +    { +        // Attempt to load the HET table (Hash Extended Table) +        pExtTable = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); +        if(pExtTable != NULL) +        { +            // If succeeded, we have to limit the maximum file count +            // to the values saved in the HET table +            // If loading HET table fails, we ignore the result. +            ha->pHetTable = TranslateHetTable(pExtTable); +            if(ha->pHetTable != NULL) +                ha->dwMaxFileCount = ha->pHetTable->dwMaxFileCount; + +            STORM_FREE(pExtTable); +        } + +        // If the HET hable failed to load, it's corrupt. +        if(ha->pHetTable == NULL) +            nError = ERROR_FILE_CORRUPT; +    } + +    return nError; +} + +TMPQBetTable * LoadBetTable(TMPQArchive * ha) +{ +    TMPQExtTable * pExtTable; +    TMPQBetTable * pBetTable = NULL; +    TMPQHeader * pHeader = ha->pHeader; + +    // If the HET table position is not NULL, we expect +    // both HET and BET tables to be present. +    if(pHeader->BetTablePos64 != 0) +    { +        // Attempt to load the HET table (Hash Extended Table) +        pExtTable = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); +        if(pExtTable != NULL) +        { +            // If succeeded, we translate the BET table +            // to more readable form +            pBetTable = TranslateBetTable(ha, pExtTable); +            STORM_FREE(pExtTable); +        } +    } + +    return pBetTable; +} + +int LoadAnyHashTable(TMPQArchive * ha) +{ +    TMPQHeader * pHeader = ha->pHeader; + +    // If the MPQ archive is empty, don't bother trying to load anything +    if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0) +        return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT); + +    // Try to load HET and/or classic hash table +    LoadHetTable(ha); + +    // Load the HASH table +    ha->pHashTable = LoadHashTable(ha); + +    // Set the maximum file count to the size of the hash table +    // In case there is HET table, we have to keep the file limit +    if(ha->pHetTable == NULL) +        ha->dwMaxFileCount = pHeader->dwHashTableSize; + +    // Did at least one succeed? +    if(ha->pHetTable == NULL && ha->pHashTable == NULL) +        return ERROR_FILE_CORRUPT; + +    // In theory, a MPQ could have bigger block table than hash table +    if(ha->pHeader->dwBlockTableSize > ha->dwMaxFileCount) +    { +        ha->dwMaxFileCount = ha->pHeader->dwBlockTableSize; +        ha->dwFlags |= MPQ_FLAG_READ_ONLY; +    } +         +    return ERROR_SUCCESS; +} + +int BuildFileTable_Classic( +    TMPQArchive * ha, +    TFileEntry * pFileTable, +    ULONGLONG FileSize) +{ +    TFileEntry * pFileEntry; +    TMPQHeader * pHeader = ha->pHeader; +    TMPQBlock * pBlockTable; +    TMPQBlock * pBlock; +    int nError = ERROR_SUCCESS; + +    // Sanity checks +    assert(ha->pHashTable != NULL); + +    // Load the block table +    pBlockTable = LoadBlockTable(ha, FileSize); +    if(pBlockTable != NULL) +    { +        TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize; +        TMPQHash * pHash; + +        // If we don't have HET table, we build the file entries from the hash&block tables +        if(ha->pHetTable == NULL) +        { +            for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) +            { +                if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) +                { +                    pFileEntry = pFileTable + pHash->dwBlockIndex; +                    pBlock = pBlockTable + pHash->dwBlockIndex; + +                    // +                    // Yet another silly map protector: For each valid file, +                    // there are 4 items in the hash table, that appears to be valid: +                    // +                    //   a6d79af0 e61a0932 001e0000 0000770b <== Fake valid +                    //   a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid +                    //   a6d79af0 e61a0932 00000000 0000002f <== Real file entry +                    //   a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid +                    //  + +                    if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS)) +                    { +                        // Fill the entry +                        pFileEntry->ByteOffset  = pBlock->dwFilePos; +                        pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); +                        pFileEntry->dwFileSize  = pBlock->dwFSize; +                        pFileEntry->dwCmpSize   = pBlock->dwCSize; +                        pFileEntry->dwFlags     = pBlock->dwFlags; +                        pFileEntry->lcLocale    = pHash->lcLocale; +                        pFileEntry->wPlatform   = pHash->wPlatform; +                    } +                    else +                    { +                        // If the hash table entry doesn't point to the valid file item, +                        // we invalidate the entire hash table entry +                        pHash->dwName1      = 0xFFFFFFFF; +                        pHash->dwName2      = 0xFFFFFFFF; +                        pHash->lcLocale     = 0xFFFF; +                        pHash->wPlatform    = 0xFFFF; +                        pHash->dwBlockIndex = HASH_ENTRY_DELETED; +                    } +                } +            } +        } +        else +        { +            for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) +            { +                if(pHash->dwBlockIndex < ha->dwFileTableSize) +                { +                    pFileEntry = pFileTable + pHash->dwBlockIndex; +                    if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) +                    { +                        pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); +                        pFileEntry->lcLocale    = pHash->lcLocale; +                        pFileEntry->wPlatform   = pHash->wPlatform; +                    } +                } +            } +        } + +        // Free the block table +        STORM_FREE(pBlockTable); +    } +    else +    { +        nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Load the hi-block table +    if(nError == ERROR_SUCCESS && pHeader->HiBlockTablePos64 != 0) +    { +        ULONGLONG ByteOffset; +        USHORT * pHiBlockTable = NULL; +        DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(USHORT); + +        // Allocate space for the hi-block table +        // Note: pHeader->dwBlockTableSize can be zero !!! +        pHiBlockTable = STORM_ALLOC(USHORT, pHeader->dwBlockTableSize + 1); +        if(pHiBlockTable != NULL) +        { +            // Load the hi-block table. It is not encrypted, nor compressed +            ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; +            if(!FileStream_Read(ha->pStream, &ByteOffset, pHiBlockTable, dwTableSize)) +                nError = GetLastError(); + +            // Now merge the hi-block table to the file table +            if(nError == ERROR_SUCCESS) +            { +                pFileEntry = pFileTable; + +                // Add the high file offset to the base file offset. +                // We also need to swap it during the process. +                for(DWORD i = 0; i < pHeader->dwBlockTableSize; i++) +                { +                    pFileEntry->ByteOffset |= ((ULONGLONG)BSWAP_INT16_UNSIGNED(pHiBlockTable[i]) << 32); +                    pFileEntry++; +                } +            } + +            // Free the hi-block table +            STORM_FREE(pHiBlockTable); +        } +        else +        { +            nError = ERROR_NOT_ENOUGH_MEMORY; +        } +    } + +    // Set the current size of the file table +    ha->dwFileTableSize = pHeader->dwBlockTableSize; +    return nError; +} + +int BuildFileTable_HetBet( +    TMPQArchive * ha, +    TFileEntry * pFileTable) +{ +    TMPQHetTable * pHetTable = ha->pHetTable; +    TMPQBetTable * pBetTable; +    TFileEntry * pFileEntry = pFileTable; +    TBitArray * pBitArray; +    DWORD dwBitPosition = 0; +    DWORD i; +    int nError = ERROR_FILE_CORRUPT; + +    // Load the BET table from the MPQ +    pBetTable = LoadBetTable(ha); +    if(pBetTable != NULL) +    { +        // Step one: Fill the indexes to the HET table +        for(i = 0; i < pHetTable->dwHashTableSize; i++) +        { +            DWORD dwFileIndex = 0; + +            // Is the entry in the HET table occupied? +            if(pHetTable->pHetHashes[i] != 0) +            { +                // Load the index to the BET table +                GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * i, +                                                pHetTable->dwIndexSize, +                                               &dwFileIndex, +                                                4); +                // Overflow test +                if(dwFileIndex < pBetTable->dwFileCount) +                { +                    // Get the file entry and save HET index +                    pFileEntry = pFileTable + dwFileIndex; +                    pFileEntry->dwHetIndex = i; + +                    // Load the BET hash +                    GetBits(pBetTable->pBetHashes, pBetTable->dwBetHashSizeTotal * dwFileIndex, +                                                   pBetTable->dwBetHashSize, +                                                  &pFileEntry->BetHash, +                                                   8); +                } +            } +        } + +        // Go through the entire BET table and convert it to the file table. +        pFileEntry = pFileTable; +        pBitArray = pBetTable->pFileTable;  +        for(i = 0; i < pBetTable->dwFileCount; i++) +        { +            DWORD dwFlagIndex = 0; + +            // Read the file position +            GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FilePos, +                               pBetTable->dwBitCount_FilePos, +                              &pFileEntry->ByteOffset, +                               8); + +            // Read the file size +            GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FileSize, +                               pBetTable->dwBitCount_FileSize, +                              &pFileEntry->dwFileSize, +                               4); + +            // Read the compressed size +            GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_CmpSize, +                               pBetTable->dwBitCount_CmpSize, +                              &pFileEntry->dwCmpSize, +                               4); + + +            // Read the flag index +            if(pBetTable->dwFlagCount != 0) +            { +                GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FlagIndex, +                                   pBetTable->dwBitCount_FlagIndex, +                                  &dwFlagIndex, +                                   4); + +                pFileEntry->dwFlags = pBetTable->pFileFlags[dwFlagIndex]; +            } + +            // +            // TODO: Locale (?) +            // + +            // Move the current bit position +            dwBitPosition += pBetTable->dwTableEntrySize; +            pFileEntry++; +        } + +        // Set the current size of the file table +        ha->dwFileTableSize = pBetTable->dwFileCount; +        FreeBetTable(pBetTable); +        nError = ERROR_SUCCESS; +    } +    else +    { +        nError = ERROR_FILE_CORRUPT; +    } + +    return nError; +} + +int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) +{ +    TFileEntry * pFileTable; +    bool bFileTableCreated = false; + +    // Sanity checks +    assert(ha->dwFileTableSize == 0); +    assert(ha->dwMaxFileCount != 0); + +    // Allocate the file table with size determined before +    pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); +    if(pFileTable == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Fill the table with zeros +    memset(pFileTable, 0, ha->dwMaxFileCount * sizeof(TFileEntry)); + +    // If we have HET table, we load file table from the BET table +    // Note: If BET table is corrupt or missing, we set the archive as read only +    if(ha->pHetTable != NULL) +    { +        if(BuildFileTable_HetBet(ha, pFileTable) != ERROR_SUCCESS) +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; +        else +            bFileTableCreated = true; +    } + +    // If we have hash table, we load the file table from the block table +    // Note: If block table is corrupt or missing, we set the archive as read only +    if(ha->pHashTable != NULL) +    { +        if(BuildFileTable_Classic(ha, pFileTable, FileSize) != ERROR_SUCCESS) +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; +        else +            bFileTableCreated = true; +    } +     +    // If something failed, we free the file table entry +    if(bFileTableCreated == false) +    { +        STORM_FREE(pFileTable); +        return ERROR_FILE_CORRUPT; +    } + +    // Assign it to the archive structure +    ha->pFileTable = pFileTable; +    return ERROR_SUCCESS; +} + +// Saves MPQ header, hash table, block table and hi-block table. +int SaveMPQTables(TMPQArchive * ha) +{ +    TMPQExtTable * pHetTable = NULL; +    TMPQExtTable * pBetTable = NULL; +    TMPQHeader * pHeader = ha->pHeader; +    TMPQBlock * pBlockTable = NULL; +    TMPQHash * pHashTable = NULL; +    ULONGLONG HetTableSize64 = 0; +    ULONGLONG BetTableSize64 = 0; +    ULONGLONG HashTableSize64 = 0; +    ULONGLONG BlockTableSize64 = 0; +    ULONGLONG HiBlockTableSize64 = 0; +    ULONGLONG TablePos = 0;             // A table position, relative to the begin of the MPQ +    USHORT * pHiBlockTable = NULL; +    DWORD cbTotalSize; +    bool bNeedHiBlockTable = false; +    int nError = ERROR_SUCCESS; + +    // We expect this function to be called only when tables have been changed +    assert(ha->dwFlags & MPQ_FLAG_CHANGED); + +    // Find the space where the MPQ tables will be saved  +    FindFreeMpqSpace(ha, &TablePos); + +    // If the MPQ has HET table, we prepare a ready-to-save version +    if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) +    { +        pHetTable = TranslateHetTable(ha->pHetTable, &HetTableSize64); +        if(pHetTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // If the MPQ has HET table, we also must create BET table to be saved +    if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) +    { +        pBetTable = TranslateBetTable(ha, &BetTableSize64); +        if(pBetTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Now create hash table +    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) +    { +        pHashTable = TranslateHashTable(ha, &HashTableSize64); +        if(pHashTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Create block table +    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) +    { +        pBlockTable = TranslateBlockTable(ha, &BlockTableSize64, &bNeedHiBlockTable); +        if(pBlockTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Create hi-block table, if needed +    if(nError == ERROR_SUCCESS && bNeedHiBlockTable) +    { +        pHiBlockTable = TranslateHiBlockTable(ha, &HiBlockTableSize64); +        if(pHiBlockTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Write the HET table, if any +    if(nError == ERROR_SUCCESS && pHetTable != NULL) +    { +        pHeader->HetTableSize64 = HetTableSize64; +        pHeader->HetTablePos64  = TablePos; +        nError = SaveExtTable(ha, pHetTable, TablePos, (DWORD)HetTableSize64, pHeader->MD5_HetTable, MPQ_KEY_HASH_TABLE, false, &cbTotalSize); +        TablePos += cbTotalSize; +    } + +    // Write the BET table, if any +    if(nError == ERROR_SUCCESS && pBetTable != NULL) +    { +        pHeader->BetTableSize64 = BetTableSize64; +        pHeader->BetTablePos64  = TablePos; +        nError = SaveExtTable(ha, pBetTable, TablePos, (DWORD)BetTableSize64, pHeader->MD5_BetTable, MPQ_KEY_BLOCK_TABLE, false, &cbTotalSize); +        TablePos += cbTotalSize; +    } + +    // Write the hash table, if we have any +    if(nError == ERROR_SUCCESS && pHashTable != NULL) +    { +        pHeader->HashTableSize64 = HashTableSize64; +        pHeader->wHashTablePosHi = (USHORT)(TablePos >> 32); +        pHeader->dwHashTableSize = (DWORD)(HashTableSize64 / sizeof(TMPQHash)); +        pHeader->dwHashTablePos = (DWORD)TablePos; +        nError = SaveMpqTable(ha, pHashTable, TablePos, (size_t)HashTableSize64, pHeader->MD5_HashTable, MPQ_KEY_HASH_TABLE, false); +        TablePos += HashTableSize64; +    } + +    // Write the block table, if we have any +    if(nError == ERROR_SUCCESS && pBlockTable != NULL) +    { +        pHeader->BlockTableSize64 = BlockTableSize64; +        pHeader->wBlockTablePosHi = (USHORT)(TablePos >> 32); +        pHeader->dwBlockTableSize = (DWORD)(BlockTableSize64 / sizeof(TMPQBlock)); +        pHeader->dwBlockTablePos = (DWORD)TablePos; +        nError = SaveMpqTable(ha, pBlockTable, TablePos, (size_t)BlockTableSize64, pHeader->MD5_BlockTable, MPQ_KEY_BLOCK_TABLE, false); +        TablePos += BlockTableSize64; +    } + +    // Write the hi-block table, if we have any +    if(nError == ERROR_SUCCESS && pHiBlockTable != NULL) +    { +        ULONGLONG ByteOffset = ha->MpqPos + TablePos; + +        pHeader->HiBlockTableSize64 = HiBlockTableSize64; +        pHeader->HiBlockTablePos64 = TablePos; +        BSWAP_ARRAY16_UNSIGNED(pHiBlockTable, HiBlockTableSize64); +         +        if(!FileStream_Write(ha->pStream, &ByteOffset, pHiBlockTable, (DWORD)HiBlockTableSize64)) +            nError = GetLastError(); +        TablePos += HiBlockTableSize64; +    } + +    // Cut the MPQ +    if(nError == ERROR_SUCCESS) +    { +        ULONGLONG FileSize = ha->MpqPos + TablePos; + +        if(!FileStream_SetSize(ha->pStream, FileSize)) +            nError = GetLastError(); +    } + +    // Write the MPQ header +    if(nError == ERROR_SUCCESS) +    { +        // Update the size of the archive +        pHeader->ArchiveSize64 = TablePos; +        pHeader->dwArchiveSize = (DWORD)TablePos; +         +        // Update the MD5 of the archive header +        CalculateDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader); + +        // Write the MPQ header to the file +        BSWAP_TMPQHEADER(pHeader); +        if(!FileStream_Write(ha->pStream, &ha->MpqPos, pHeader, pHeader->dwHeaderSize)) +            nError = GetLastError(); +        BSWAP_TMPQHEADER(pHeader); +    } + +    // Clear the changed flag +    if(nError == ERROR_SUCCESS) +        ha->dwFlags &= ~MPQ_FLAG_CHANGED; + +    // Cleanup and exit +    if(pHetTable != NULL) +        STORM_FREE(pHetTable); +    if(pBetTable != NULL) +        STORM_FREE(pBetTable); +    if(pHashTable != NULL) +        STORM_FREE(pHashTable); +    if(pBlockTable != NULL) +        STORM_FREE(pBlockTable); +    if(pHiBlockTable != NULL) +        STORM_FREE(pHiBlockTable); +    return nError; +} diff --git a/src/SCompression.cpp b/src/SCompression.cpp new file mode 100644 index 0000000..e3b99e0 --- /dev/null +++ b/src/SCompression.cpp @@ -0,0 +1,1065 @@ +/*****************************************************************************/ +/* SCompression.cpp                       Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module serves as a bridge between StormLib code and (de)compression  */ +/* functions. All (de)compression calls go (and should only go) through this */    +/* module. No system headers should be included in this module to prevent    */ +/* compile-time problems.                                                    */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 01.04.03  1.00  Lad  The first version of SCompression.cpp                */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +// Information about the input and output buffers for pklib +typedef struct +{ +    unsigned char * pbInBuff;           // Pointer to input data buffer +    unsigned char * pbInBuffEnd;        // End of the input buffer +    unsigned char * pbOutBuff;          // Pointer to output data buffer +    unsigned char * pbOutBuffEnd;       // Pointer to output data buffer +} TDataInfo; + +// Prototype of the compression function +// Function doesn't return an error. A success means that the size of compressed buffer +// is lower than size of uncompressed buffer. +typedef void (*COMPRESS)( +    void * pvOutBuffer,                 // [out] Pointer to the buffer where the compressed data will be stored +    int  * pcbOutBuffer,                // [in]  Pointer to length of the buffer pointed by pvOutBuffer +    void * pvInBuffer,                  // [in]  Pointer to the buffer with data to compress +    int cbInBuffer,                     // [in]  Length of the buffer pointer by pvInBuffer +    int * pCmpType,                     // [in]  Compression-method specific value. ADPCM Setups this for the following Huffman compression +    int nCmpLevel);                     // [in]  Compression specific value. ADPCM uses this. Should be set to zero. + +// Prototype of the decompression function +// Returns 1 if success, 0 if failure +typedef int (*DECOMPRESS)( +    void * pvOutBuffer,                 // [out] Pointer to the buffer where to store decompressed data +    int  * pcbOutBuffer,                // [in]  Pointer to total size of the buffer pointed by pvOutBuffer +                                        // [out] Contains length of the decompressed data +    void * pvInBuffer,                  // [in]  Pointer to data to be decompressed   +    int cbInBuffer);                    // [in]  Length of the data to be decompressed + +// Table of compression functions +typedef struct   +{ +    unsigned long uMask;                // Compression mask +    COMPRESS Compress;                  // Compression function +} TCompressTable; + +// Table of decompression functions +typedef struct +{ +    unsigned long uMask;                // Decompression bit +    DECOMPRESS    Decompress;           // Decompression function +} TDecompressTable; + + +/*****************************************************************************/ +/*                                                                           */ +/*  Support for Huffman compression (0x01)                                   */ +/*                                                                           */ +/*****************************************************************************/ + +void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    THuffmannTree ht(true); +    TOutputStream os(pvOutBuffer, *pcbOutBuffer); + +    STORMLIB_UNUSED(nCmpLevel); +    *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType); +}                  + +int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    THuffmannTree ht(false); +    TInputStream is(pvInBuffer, cbInBuffer); + +    *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is); +    return (*pcbOutBuffer == 0) ? 0 : 1; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support for ZLIB compression (0x02)                                       */ +/*                                                                            */ +/******************************************************************************/ + +void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    z_stream z;                        // Stream information for zlib +    int windowBits; +    int nResult; + +    // Keep compilers happy +    STORMLIB_UNUSED(pCmpType); +    STORMLIB_UNUSED(nCmpLevel); + +    // Fill the stream structure for zlib +    z.next_in   = (Bytef *)pvInBuffer; +    z.avail_in  = (uInt)cbInBuffer; +    z.total_in  = cbInBuffer; +    z.next_out  = (Bytef *)pvOutBuffer; +    z.avail_out = *pcbOutBuffer; +    z.total_out = 0; +    z.zalloc    = NULL; +    z.zfree     = NULL; + +    // Determine the proper window bits (WoW.exe build 12694) +    if(cbInBuffer <= 0x100) +        windowBits = 8; +    else if(cbInBuffer <= 0x200) +        windowBits = 9; +    else if(cbInBuffer <= 0x400) +        windowBits = 10; +    else if(cbInBuffer <= 0x800) +        windowBits = 11; +    else if(cbInBuffer <= 0x1000) +        windowBits = 12; +    else if(cbInBuffer <= 0x2000) +        windowBits = 13; +    else if(cbInBuffer <= 0x4000) +        windowBits = 14; +    else +        windowBits = 15; + +    // Initialize the compression. +    // Storm.dll uses zlib version 1.1.3 +    // Wow.exe uses zlib version 1.2.3 +    nResult = deflateInit2(&z, +                            6,                  // Compression level used by WoW MPQs +                            Z_DEFLATED, +                            windowBits, +                            8, +                            Z_DEFAULT_STRATEGY); +    if(nResult == Z_OK) +    { +        // Call zlib to compress the data +        nResult = deflate(&z, Z_FINISH); +         +        if(nResult == Z_OK || nResult == Z_STREAM_END) +            *pcbOutBuffer = z.total_out; + +        deflateEnd(&z); +    } +} + +int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    z_stream z;                        // Stream information for zlib +    int nResult; + +    // Fill the stream structure for zlib +    z.next_in   = (Bytef *)pvInBuffer; +    z.avail_in  = (uInt)cbInBuffer; +    z.total_in  = cbInBuffer; +    z.next_out  = (Bytef *)pvOutBuffer; +    z.avail_out = *pcbOutBuffer; +    z.total_out = 0; +    z.zalloc    = NULL; +    z.zfree     = NULL; + +    // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 +    if((nResult = inflateInit(&z)) == 0) +    { +        // Call zlib to decompress the data +        nResult = inflate(&z, Z_FINISH); +        *pcbOutBuffer = z.total_out; +        inflateEnd(&z); +    } +    return nResult; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support functions for PKWARE Data Compression Library compression (0x08)  */ +/*                                                                            */ +/******************************************************************************/ + +// Function loads data from the input buffer. Used by Pklib's "implode" +// and "explode" function as user-defined callback +// Returns number of bytes loaded +//     +//   char * buf          - Pointer to a buffer where to store loaded data +//   unsigned int * size - Max. number of bytes to read +//   void * param        - Custom pointer, parameter of implode/explode + +static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) +{ +    TDataInfo * pInfo = (TDataInfo *)param; +    unsigned int nMaxAvail = (unsigned int)(pInfo->pbInBuffEnd - pInfo->pbInBuff); +    unsigned int nToRead = *size; + +    // Check the case when not enough data available +    if(nToRead > nMaxAvail) +        nToRead = nMaxAvail; +     +    // Load data and increment offsets +    memcpy(buf, pInfo->pbInBuff, nToRead); +    pInfo->pbInBuff += nToRead; +    assert(pInfo->pbInBuff <= pInfo->pbInBuffEnd); +    return nToRead; +} + +// Function for store output data. Used by Pklib's "implode" and "explode" +// as user-defined callback +//     +//   char * buf          - Pointer to data to be written +//   unsigned int * size - Number of bytes to write +//   void * param        - Custom pointer, parameter of implode/explode + +static void WriteOutputData(char * buf, unsigned int * size, void * param) +{ +    TDataInfo * pInfo = (TDataInfo *)param; +    unsigned int nMaxWrite = (unsigned int)(pInfo->pbOutBuffEnd - pInfo->pbOutBuff); +    unsigned int nToWrite = *size; + +    // Check the case when not enough space in the output buffer +    if(nToWrite > nMaxWrite) +        nToWrite = nMaxWrite; + +    // Write output data and increments offsets +    memcpy(pInfo->pbOutBuff, buf, nToWrite); +    pInfo->pbOutBuff += nToWrite; +    assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd); +} + +static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    TDataInfo Info;                                      // Data information +    char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer +    unsigned int dict_size;                              // Dictionary size +    unsigned int ctype = CMP_BINARY;                     // Compression type + +    // Keep compilers happy +    STORMLIB_UNUSED(pCmpType); +    STORMLIB_UNUSED(nCmpLevel); + +    // Fill data information structure +    memset(work_buf, 0, CMP_BUFFER_SIZE); +    Info.pbInBuff     = (unsigned char *)pvInBuffer; +    Info.pbInBuffEnd  = (unsigned char *)pvInBuffer + cbInBuffer; +    Info.pbOutBuff    = (unsigned char *)pvOutBuffer; +    Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + +    // +    // Set the dictionary size +    // +    // Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 +    // Starcraft uses the variable dictionary size based on algorithm below +    // + +    if (cbInBuffer < 0x600) +        dict_size = CMP_IMPLODE_DICT_SIZE1; +    else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) +        dict_size = CMP_IMPLODE_DICT_SIZE2; +    else +        dict_size = CMP_IMPLODE_DICT_SIZE3; + +    // Do the compression +    if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) +        *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); + +    STORM_FREE(work_buf); +} + +static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    TDataInfo Info;                             // Data information +    char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer + +    // Fill data information structure +    memset(work_buf, 0, EXP_BUFFER_SIZE); +    Info.pbInBuff     = (unsigned char *)pvInBuffer; +    Info.pbInBuffEnd  = (unsigned char *)pvInBuffer + cbInBuffer; +    Info.pbOutBuff    = (unsigned char *)pvOutBuffer; +    Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + +    // Do the decompression +    explode(ReadInputData, WriteOutputData, work_buf, &Info); +     +    // If PKLIB is unable to decompress the data, return 0; +    if(Info.pbOutBuff == pvOutBuffer) +        return 0; + +    // Give away the number of decompressed bytes +    *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); +    STORM_FREE(work_buf); +    return 1; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support for Bzip2 compression (0x10)                                      */ +/*                                                                            */ +/******************************************************************************/ + +static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    bz_stream strm; +    int blockSize100k = 9; +    int workFactor = 30; +    int bzError; + +    // Keep compilers happy +    STORMLIB_UNUSED(pCmpType); +    STORMLIB_UNUSED(nCmpLevel); + +    // Initialize the BZIP2 compression +    strm.bzalloc = NULL; +    strm.bzfree  = NULL; + +    // Blizzard uses 9 as blockSize100k, (0x30 as workFactor) +    // Last checked on Starcraft II +    if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK) +    { +        strm.next_in   = (char *)pvInBuffer; +        strm.avail_in  = cbInBuffer; +        strm.next_out  = (char *)pvOutBuffer; +        strm.avail_out = *pcbOutBuffer; + +        // Perform the compression +        for(;;) +        { +            bzError = BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH); +            if(bzError == BZ_STREAM_END || bzError < 0) +                break; +        } + +        // Put the stream into idle state +        BZ2_bzCompressEnd(&strm); + +        if(bzError > 0) +            *pcbOutBuffer = strm.total_out_lo32; +    } +} + +static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    bz_stream strm; +    int nResult = BZ_OK; + +    // Initialize the BZIP2 decompression +    strm.bzalloc = NULL; +    strm.bzfree  = NULL; +    if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK) +    { +        strm.next_in   = (char *)pvInBuffer; +        strm.avail_in  = cbInBuffer; +        strm.next_out  = (char *)pvOutBuffer; +        strm.avail_out = *pcbOutBuffer; + +        // Perform the decompression +        while(nResult != BZ_STREAM_END) +        { +            nResult = BZ2_bzDecompress(&strm); +             +            // If any error there, break the loop  +            if(nResult < BZ_OK) +                break; +        } + +        // Put the stream into idle state +        BZ2_bzDecompressEnd(&strm); + +        // If all succeeded, set the number of output bytes +        if(nResult >= BZ_OK) +        { +            *pcbOutBuffer = strm.total_out_lo32; +            return 1; +        } +    } + +    // Something failed, so set number of output bytes to zero +    *pcbOutBuffer = 0; +    return 1; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support functions for LZMA compression (0x12)                             */ +/*                                                                            */ +/******************************************************************************/ + +#define LZMA_HEADER_SIZE (1 + LZMA_PROPS_SIZE + 8) + +static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 /* outSize */) +{ +    return SZ_OK; +} + +static void * LZMA_Callback_Alloc(void *p, size_t size) +{ +    p = p; +    return STORM_ALLOC(BYTE, size); +} + +/* address can be 0 */ +static void LZMA_Callback_Free(void *p, void *address) +{ +    p = p; +    if(address != NULL) +        STORM_FREE(address); +} + +// +// Note: So far, I haven't seen any files compressed by LZMA. +// This code haven't been verified against code ripped from Starcraft II Beta, +// but we know that Starcraft LZMA decompression code is able to decompress +// the data compressed by StormLib. +// + +/*static */ void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    ICompressProgress Progress; +    CLzmaEncProps props; +    ISzAlloc SzAlloc; +    Byte * pbOutBuffer = (Byte *)pvOutBuffer; +    Byte * destBuffer; +    SizeT destLen = *pcbOutBuffer; +    SizeT srcLen = cbInBuffer; +    Byte encodedProps[LZMA_PROPS_SIZE]; +    size_t encodedPropsSize = LZMA_PROPS_SIZE; +    SRes nResult; + +    // Keep compilers happy +    STORMLIB_UNUSED(pCmpType); +    STORMLIB_UNUSED(nCmpLevel); + +    // Fill the callbacks in structures +    Progress.Progress = LZMA_Callback_Progress; +    SzAlloc.Alloc = LZMA_Callback_Alloc; +    SzAlloc.Free = LZMA_Callback_Free; + +    // Initialize properties +    LzmaEncProps_Init(&props); + +    // Perform compression +    destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE; +    destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; +    nResult = LzmaEncode(destBuffer, +                        &destLen, +                 (Byte *)pvInBuffer, +                         srcLen, +                        &props, +                         encodedProps, +                        &encodedPropsSize, +                         0, +                        &Progress, +                        &SzAlloc, +                        &SzAlloc); +    if(nResult != SZ_OK) +        return; + +    // If we failed to compress the data +    if(destLen >= (SizeT)(*pcbOutBuffer - LZMA_HEADER_SIZE)) +        return; + +    // Write "useFilter" variable. Blizzard MPQ must not use filter. +    *pbOutBuffer++ = 0; + +    // Copy the encoded properties to the output buffer +    memcpy(pvOutBuffer, encodedProps, encodedPropsSize); +    pbOutBuffer += encodedPropsSize; + +    // Copy the size of the data +    *pbOutBuffer++ = (unsigned char)(srcLen >> 0x00); +    *pbOutBuffer++ = (unsigned char)(srcLen >> 0x08); +    *pbOutBuffer++ = (unsigned char)(srcLen >> 0x10); +    *pbOutBuffer++ = (unsigned char)(srcLen >> 0x18); +    *pbOutBuffer++ = 0; +    *pbOutBuffer++ = 0; +    *pbOutBuffer++ = 0; +    *pbOutBuffer++ = 0; + +    // Give the size of the data to the caller +    *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE); +} + +static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    ELzmaStatus LzmaStatus; +    ISzAlloc SzAlloc; +    Byte * destBuffer = (Byte *)pvOutBuffer; +    Byte * srcBuffer = (Byte *)pvInBuffer; +    SizeT destLen = *pcbOutBuffer; +    SizeT srcLen = cbInBuffer; +    SRes nResult; + +    // There must be at least 0x0E bytes in the buffer +    if(srcLen <= LZMA_HEADER_SIZE)  +        return 0; + +    // We only accept blocks that have no filter used +    if(*srcBuffer != 0) +        return 0; + +    // Fill the callbacks in structures +    SzAlloc.Alloc = LZMA_Callback_Alloc; +    SzAlloc.Free = LZMA_Callback_Free; + +    // Perform compression +    srcLen = cbInBuffer - LZMA_HEADER_SIZE; +    nResult = LzmaDecode(destBuffer, +                        &destLen, +                         srcBuffer + LZMA_HEADER_SIZE, +                        &srcLen, +                         srcBuffer + 1,  +                         LZMA_PROPS_SIZE, +                         LZMA_FINISH_END, +                        &LzmaStatus, +                        &SzAlloc); +    if(nResult != SZ_OK) +        return 0; + +    *pcbOutBuffer = (unsigned int)destLen; +    return 1; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support functions for SPARSE compression (0x20)                           */ +/*                                                                            */ +/******************************************************************************/ + +void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    // Keep compilers happy +    STORMLIB_UNUSED(pCmpType); +    STORMLIB_UNUSED(nCmpLevel); + +    CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support for ADPCM mono compression (0x40)                                 */ +/*                                                                            */ +/******************************************************************************/ + +static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    // Prepare the compression level for Huffmann compression, +    // which will be called as next step +    if(0 < nCmpLevel && nCmpLevel <= 2) +    { +        nCmpLevel = 4; +        *pCmpType = 6; +    } +    else if(nCmpLevel == 3) +    { +        nCmpLevel = 6; +        *pCmpType = 8; +    } +    else +    { +        nCmpLevel = 5; +        *pCmpType = 7; +    } +    *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel); +} + +static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); +    return 1; +} + +/******************************************************************************/ +/*                                                                            */ +/*  Support for ADPCM stereo compression (0x80)                               */ +/*                                                                            */ +/******************************************************************************/ + +static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ +    // Prepare the compression level for Huffmann compression, +    // which will be called as next step +    if(0 < nCmpLevel && nCmpLevel <= 2) +    { +        nCmpLevel = 4; +        *pCmpType = 6; +    } +    else if(nCmpLevel == 3) +    { +        nCmpLevel = 6; +        *pCmpType = 8; +    } +    else +    { +        nCmpLevel = 5; +        *pCmpType = 7; +    } +    *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel); +} + +static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); +    return 1; +} + +/*****************************************************************************/ +/*                                                                           */ +/*   SCompImplode                                                            */ +/*                                                                           */ +/*****************************************************************************/ + +int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    int cbOutBuffer = *pcbOutBuffer; + +    // Check for valid parameters +    if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return 0; +    } + +    // Perform the compression +    Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0); + +    // If the compression was unsuccessful, copy the data as-is +    if(cbOutBuffer >= *pcbOutBuffer) +    { +        memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); +        cbOutBuffer = *pcbOutBuffer; +    } + +    *pcbOutBuffer = cbOutBuffer; +    return 1; +} + +/*****************************************************************************/ +/*                                                                           */ +/*   SCompExplode                                                            */ +/*                                                                           */ +/*****************************************************************************/ + +int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    int cbOutBuffer = *pcbOutBuffer; + +    // Check for valid parameters +    if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return 0; +    } + +    // If the input length is the same as output length, do nothing. +    if(cbInBuffer == cbOutBuffer) +    { +        // If the buffers are equal, don't copy anything. +        if(pvInBuffer == pvOutBuffer) +            return 1; + +        memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); +        return 1; +    } +     +    // Perform decompression +    if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer)) +    { +        SetLastError(ERROR_FILE_CORRUPT); +        return false; +    } + +    *pcbOutBuffer = cbOutBuffer; +    return 1; +} + +/*****************************************************************************/ +/*                                                                           */ +/*   SCompCompress                                                           */ +/*                                                                           */ +/*****************************************************************************/ + +// This table contains compress functions which can be applied to +// uncompressed data. Each bit means the corresponding +// compression method/function must be applied. +// +//   WAVes compression            Data compression +//   ------------------           ------------------- +//   1st sector   - 0x08          0x08 (D, HF, W2, SC, D2) +//   Next sectors - 0x81          0x02 (W3) + +static TCompressTable cmp_table[] = +{ +    {MPQ_COMPRESSION_SPARSE,       Compress_SPARSE},        // Sparse compression +    {MPQ_COMPRESSION_ADPCM_MONO,   Compress_ADPCM_mono},    // IMA ADPCM mono compression +    {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo},  // IMA ADPCM stereo compression +    {MPQ_COMPRESSION_HUFFMANN,     Compress_huff},          // Huffmann compression +    {MPQ_COMPRESSION_ZLIB,         Compress_ZLIB},          // Compression with the "zlib" library +    {MPQ_COMPRESSION_PKWARE,       Compress_PKLIB},         // Compression with Pkware DCL +    {MPQ_COMPRESSION_BZIP2,        Compress_BZIP2}          // Compression Bzip2 library +}; + +int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel) +{ +    COMPRESS CompressFuncArray[0x10];                       // Array of compression functions, applied sequentially +    unsigned char CompressByte[0x10];                       // CompressByte for each method in the CompressFuncArray array +    unsigned char * pbWorkBuffer = NULL;                    // Temporary storage for decompressed data +    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; +    unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer +    unsigned char * pbInput = (unsigned char *)pvInBuffer;  // Current input buffer +    int nCompressCount = 0; +    int nCompressIndex = 0; +    int nAtLeastOneCompressionDone = 0; +    int cbOutBuffer = 0; +    int cbInLength = cbInBuffer; +    int nResult = 1; + +    // Check for valid parameters +    if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return 0; +    } + +    // Zero input length brings zero output length +    if(cbInBuffer == 0) +    { +        *pcbOutBuffer = 0; +        return true; +    } + +    // Setup the compression function array +    if(uCompressionMask == MPQ_COMPRESSION_LZMA) +    { +        CompressFuncArray[0] = Compress_LZMA; +        CompressByte[0] = (char)uCompressionMask; +        nCompressCount = 1; +    } +    else +    { +        // Fill the compressions array +        for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++) +        { +            // If the mask agrees, insert the compression function to the array +            if(uCompressionMask & cmp_table[i].uMask) +            { +                CompressFuncArray[nCompressCount] = cmp_table[i].Compress; +                CompressByte[nCompressCount] = (unsigned char)cmp_table[i].uMask; +                uCompressionMask &= ~cmp_table[i].uMask; +                nCompressCount++; +            } +        } + +        // If at least one of the compressions remaing unknown, return an error +        if(uCompressionMask != 0) +        { +            SetLastError(ERROR_NOT_SUPPORTED); +            return 0; +        } +    } + +    // If there is at least one compression, do it +    if(nCompressCount > 0) +    { +        // If we need to do more than 1 compression, allocate intermediate buffer +        if(nCompressCount > 1) +        { +            pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); +            if(pbWorkBuffer == NULL) +            { +                SetLastError(ERROR_NOT_ENOUGH_MEMORY); +                return 0; +            } +        } + +        // Get the current compression index +        nCompressIndex = nCompressCount - 1; + +        // Perform all compressions in the array +        for(int i = 0; i < nCompressCount; i++) +        { +            // Choose the proper output buffer +            pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; +            nCompressIndex--; + +            // Perform the (next) compression +            // Note that if the compression method is unable to compress the input data block +            // by at least 2 bytes, we consider it as failure and will use source data instead +            cbOutBuffer = *pcbOutBuffer - 1; +            CompressFuncArray[i](pbOutput + 1, &cbOutBuffer, pbInput, cbInLength, &nCmpType, nCmpLevel); + +            // If the compression failed, we copy the input buffer as-is. +            // Note that there is one extra byte at the end of the intermediate buffer, so it should be OK +            if(cbOutBuffer > (cbInLength - 2)) +            { +                memcpy(pbOutput + nAtLeastOneCompressionDone, pbInput, cbInLength); +                cbOutBuffer = cbInLength; +            } +            else +            { +                // Remember that we have done at least one compression +                nAtLeastOneCompressionDone = 1; +                uCompressionMask |= CompressByte[i]; +            } + +            // Now point input buffer to the output buffer +            pbInput = pbOutput + nAtLeastOneCompressionDone; +            cbInLength = cbOutBuffer; +        } + +        // If at least one compression succeeded, put the compression +        // mask to the begin of the output buffer +        if(nAtLeastOneCompressionDone) +            *pbOutBuffer  = (unsigned char)uCompressionMask; +        *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone; +    } +    else +    { +        memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); +        *pcbOutBuffer = cbInBuffer; +    } + +    // Cleanup and return +    if(pbWorkBuffer != NULL) +        STORM_FREE(pbWorkBuffer); +    return nResult; +} + +/*****************************************************************************/ +/*                                                                           */ +/*   SCompDecompress                                                         */ +/*                                                                           */ +/*****************************************************************************/ + +// This table contains decompress functions which can be applied to +// uncompressed data. The compression mask is stored in the first byte +// of compressed data +static TDecompressTable dcmp_table[] = +{ +    {MPQ_COMPRESSION_BZIP2,        Decompress_BZIP2},        // Decompression with Bzip2 library +    {MPQ_COMPRESSION_PKWARE,       Decompress_PKLIB},        // Decompression with Pkware Data Compression Library +    {MPQ_COMPRESSION_ZLIB,         Decompress_ZLIB},         // Decompression with the "zlib" library +    {MPQ_COMPRESSION_HUFFMANN,     Decompress_huff},         // Huffmann decompression +    {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression +    {MPQ_COMPRESSION_ADPCM_MONO,   Decompress_ADPCM_mono},   // IMA ADPCM mono decompression +    {MPQ_COMPRESSION_SPARSE,       Decompress_SPARSE}        // Sparse decompression +}; + +int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    unsigned char * pbWorkBuffer = NULL; +    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; +    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; +    unsigned char * pbOutput = (unsigned char *)pvOutBuffer; +    unsigned char * pbInput; +    unsigned uCompressionMask;              // Decompressions applied to the data +    unsigned uCompressionCopy;              // Decompressions applied to the data +    int      cbOutBuffer = *pcbOutBuffer;   // Current size of the output buffer +    int      cbInLength;                    // Current size of the input buffer +    int      nCompressCount = 0;            // Number of compressions to be applied +    int      nCompressIndex = 0; +    int      nResult = 1; + +    // Verify buffer sizes +    if(cbOutBuffer < cbInBuffer || cbInBuffer < 1) +        return 0; + +    // If the input length is the same as output length, do nothing. +    if(cbOutBuffer == cbInBuffer) +    { +        // If the buffers are equal, don't copy anything. +        if(pvInBuffer != pvOutBuffer) +            memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); +        return 1; +    } + +    // Get applied compression types and decrement data length +    uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; +    cbInBuffer--; + +    // Get current compressed data and length of it +    pbInput = pbInBuffer; +    cbInLength = cbInBuffer; + +    // This compression function doesn't support LZMA +    assert(uCompressionMask != MPQ_COMPRESSION_LZMA); + +    // Parse the compression mask +    for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) +    { +        // If the mask agrees, insert the compression function to the array +        if(uCompressionMask & dcmp_table[i].uMask) +        { +            uCompressionCopy &= ~dcmp_table[i].uMask; +            nCompressCount++; +        } +    } + +    // If at least one of the compressions remaing unknown, return an error +    if(nCompressCount == 0 || uCompressionCopy != 0) +    { +        SetLastError(ERROR_NOT_SUPPORTED); +        return 0; +    } + +    // If there is more than one compression, we have to allocate extra buffer +    if(nCompressCount > 1) +    { +        pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer); +        if(pbWorkBuffer == NULL) +        { +            SetLastError(ERROR_NOT_ENOUGH_MEMORY); +            return 0; +        } +    } + +    // Get the current compression index +    nCompressIndex = nCompressCount - 1; + +    // Apply all decompressions +    for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) +    { +        // Perform the (next) decompression +        if(uCompressionMask & dcmp_table[i].uMask) +        { +            // Get the correct output buffer +            pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; +            nCompressIndex--; +         +            // Perform the decompression +            cbOutBuffer = *pcbOutBuffer; +            nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); +            if(nResult == 0 || cbOutBuffer == 0) +            { +                SetLastError(ERROR_FILE_CORRUPT); +                nResult = 0; +                break; +            } + +            // Switch buffers +            cbInLength = cbOutBuffer; +            pbInput = pbOutput; +        } +    } + +    // Put the length of the decompressed data to the output buffer +    *pcbOutBuffer = cbOutBuffer; + +    // Cleanup and return +    if(pbWorkBuffer != NULL) +        STORM_FREE(pbWorkBuffer); +    return nResult; +} + +int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    DECOMPRESS pfnDecompress1 = NULL; +    DECOMPRESS pfnDecompress2 = NULL; +    unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer; +    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; +    int cbWorkBuffer = *pcbOutBuffer; +    int nResult; +    char CompressionMethod; + +    // Verify buffer sizes +    if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) +        return 0; + +    // If the outputbuffer is as big as input buffer, just copy the block +    if(*pcbOutBuffer == cbInBuffer) +    { +        if(pvOutBuffer != pvInBuffer) +            memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); +        return 1; +    } + +    // Get the compression methods +    CompressionMethod = *pbInBuffer++; +    cbInBuffer--; + +    // We only recognize a fixed set of compression methods +    switch((unsigned char)CompressionMethod) +    { +        case MPQ_COMPRESSION_ZLIB: +            pfnDecompress1 = Decompress_ZLIB; +            break; + +        case MPQ_COMPRESSION_PKWARE: +            pfnDecompress1 = Decompress_PKLIB; +            break; + +        case MPQ_COMPRESSION_BZIP2: +            pfnDecompress1 = Decompress_BZIP2; +            break; + +        case MPQ_COMPRESSION_LZMA: +            pfnDecompress1 = Decompress_LZMA; +            break; + +        case MPQ_COMPRESSION_SPARSE: +            pfnDecompress1 = Decompress_SPARSE; +            break; + +        case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB): +            pfnDecompress1 = Decompress_ZLIB; +            pfnDecompress2 = Decompress_SPARSE; +            break; + +        case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2): +            pfnDecompress1 = Decompress_BZIP2; +            pfnDecompress2 = Decompress_SPARSE; +            break; + +        // +        // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO, +        // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN +        // is not supported by newer MPQs. +        // + +        default: +            SetLastError(ERROR_FILE_CORRUPT); +            return 0; +    } + +    // If we have to use two decompressions, allocate temporary buffer +    if(pfnDecompress2 != NULL) +    { +        pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); +        if(pbWorkBuffer == NULL) +        { +            SetLastError(ERROR_NOT_ENOUGH_MEMORY); +            return 0; +        } +    } + +    // Apply the first decompression method +    nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pvInBuffer, cbInBuffer); + +    // Apply the second decompression method, if any +    if(pfnDecompress2 != NULL && nResult != 0) +    { +        cbInBuffer   = cbWorkBuffer; +        cbWorkBuffer = *pcbOutBuffer; +        nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); +    } + +    // Supply the output buffer size +    *pcbOutBuffer = cbWorkBuffer; + +    // Free temporary buffer +    if(pbWorkBuffer != pvOutBuffer) +        STORM_FREE(pbWorkBuffer); + +    if(nResult == 0) +        SetLastError(ERROR_FILE_CORRUPT); +    return nResult; +} diff --git a/src/SFileAddFile.cpp b/src/SFileAddFile.cpp new file mode 100644 index 0000000..3441b26 --- /dev/null +++ b/src/SFileAddFile.cpp @@ -0,0 +1,1277 @@ +/*****************************************************************************/ +/* SFileAddFile.cpp                       Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* MPQ Editing functions                                                     */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 27.03.10  1.00  Lad  Splitted from SFileCreateArchiveEx.cpp               */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +#define FILE_SIGNATURE_RIFF     0x46464952 +#define FILE_SIGNATURE_WAVE     0x45564157 +#define FILE_SIGNATURE_FMT      0x20746D66 +#define AUDIO_FORMAT_PCM                 1 + +typedef struct _WAVE_FILE_HEADER +{ +    DWORD dwChunkId;                        // 0x52494646 ("RIFF") +    DWORD dwChunkSize;                      // Size of that chunk, in bytes +    DWORD dwFormat;                         // Must be 0x57415645 ("WAVE") +    +    // Format sub-chunk  +    DWORD dwSubChunk1Id;                    // 0x666d7420 ("fmt ") +    DWORD dwSubChunk1Size;                  // 0x16 for PCM +    USHORT wAudioFormat;                    // 1 = PCM. Other value means some sort of compression +    USHORT wChannels;                       // Number of channels +    DWORD dwSampleRate;                     // 8000, 44100, etc. +    DWORD dwBytesRate;                      // SampleRate * NumChannels * BitsPerSample/8 +    USHORT wBlockAlign;                     // NumChannels * BitsPerSample/8 +    USHORT wBitsPerSample;                  // 8 bits = 8, 16 bits = 16, etc. + +    // Followed by "data" sub-chunk (we don't care) +} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; + +//----------------------------------------------------------------------------- +// Local variables + +// Data compression for SFileAddFile +// Kept here for compatibility with code that was created with StormLib version < 6.50 +static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; + +static SFILE_ADDFILE_CALLBACK AddFileCB = NULL; +static void * pvUserData = NULL; + +//----------------------------------------------------------------------------- +// MPQ write data functions + +#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) + +static int IsWaveFile( +    LPBYTE pbFileData, +    DWORD cbFileData, +    LPDWORD pdwChannels) +{ +    PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData; + +    if(cbFileData > sizeof(WAVE_FILE_HEADER)) +    { +        if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE) +        { +            if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM) +            { +                *pdwChannels = pWaveHdr->wChannels; +                return true; +            } +        } +    } + +    return false; +} + + +static int WriteDataToMpqFile( +    TMPQArchive * ha, +    TMPQFile * hf, +    LPBYTE pbFileData, +    DWORD dwDataSize, +    DWORD dwCompression) +{ +    TFileEntry * pFileEntry = hf->pFileEntry; +    ULONGLONG ByteOffset; +    LPBYTE pbCompressed = NULL;         // Compressed (target) data +    LPBYTE pbToWrite = NULL;            // Data to write to the file +    int nCompressionLevel = -1;         // ADPCM compression level (only used for wave files) +    int nError = ERROR_SUCCESS; + +    // If the caller wants ADPCM compression, we will set wave compression level to 4, +    // which corresponds to medium quality +    if(dwCompression & LOSSY_COMPRESSION_MASK) +        nCompressionLevel = 4; + +    // Make sure that the caller won't overrun the previously initiated file size +    assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize); +    assert(hf->dwSectorCount != 0); +    assert(hf->pbFileSector != NULL); +    if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize) +        return ERROR_DISK_FULL; +    pbToWrite = hf->pbFileSector; + +    // Now write all data to the file sector buffer +    if(nError == ERROR_SUCCESS) +    { +        DWORD dwBytesInSector = hf->dwFilePos % hf->dwSectorSize; +        DWORD dwSectorIndex = hf->dwFilePos / hf->dwSectorSize; +        DWORD dwBytesToCopy; + +        // Process all data.  +        while(dwDataSize != 0) +        { +            dwBytesToCopy = dwDataSize; +                 +            // Check for sector overflow +            if(dwBytesToCopy > (hf->dwSectorSize - dwBytesInSector)) +                dwBytesToCopy = (hf->dwSectorSize - dwBytesInSector); + +            // Copy the data to the file sector +            memcpy(hf->pbFileSector + dwBytesInSector, pbFileData, dwBytesToCopy); +            dwBytesInSector += dwBytesToCopy; +            pbFileData += dwBytesToCopy; +            dwDataSize -= dwBytesToCopy; + +            // Update the file position +            hf->dwFilePos += dwBytesToCopy; + +            // If the current sector is full, or if the file is already full, +            // then write the data to the MPQ +            if(dwBytesInSector >= hf->dwSectorSize || hf->dwFilePos >= pFileEntry->dwFileSize) +            { +                // Set the position in the file +                ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize; + +                // Update CRC32 and MD5 of the file +                md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector); +                hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector); + +                // Compress the file sector, if needed +                if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +                { +                    int nOutBuffer = (int)dwBytesInSector; +                    int nInBuffer = (int)dwBytesInSector; + +                    // If the file is compressed, allocate buffer for the compressed data. +                    // Note that we allocate buffer that is a bit longer than sector size, +                    // for case if the compression method performs a buffer overrun +                    if(pbCompressed == NULL) +                    { +                        pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100); +                        if(pbCompressed == NULL) +                        { +                            nError = ERROR_NOT_ENOUGH_MEMORY; +                            break; +                        } +                    } + +                    // +                    // Note that both SCompImplode and SCompCompress give original buffer, +                    // if they are unable to comperss the data. +                    // + +                    if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) +                    { +                        SCompImplode(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer); +                    } + +                    if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) +                    { +                        SCompCompress(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer, (unsigned)dwCompression, 0, nCompressionLevel); +                    } + +                    // Update sector positions +                    dwBytesInSector = nOutBuffer; +                    if(hf->SectorOffsets != NULL) +                        hf->SectorOffsets[dwSectorIndex+1] = hf->SectorOffsets[dwSectorIndex] + dwBytesInSector; + +                    // We have to calculate sector CRC, if enabled +                    if(hf->SectorChksums != NULL) +                        hf->SectorChksums[dwSectorIndex] = adler32(0, pbCompressed, nOutBuffer); +                }                  + +                // Encrypt the sector, if necessary +                if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +                { +                    BSWAP_ARRAY32_UNSIGNED(pbToWrite, dwBytesInSector); +                    EncryptMpqBlock(pbToWrite, dwBytesInSector, hf->dwFileKey + dwSectorIndex); +                    BSWAP_ARRAY32_UNSIGNED(pbToWrite, dwBytesInSector); +                } + +                // Write the file sector +                if(!FileStream_Write(ha->pStream, &ByteOffset, pbToWrite, dwBytesInSector)) +                { +                    nError = GetLastError(); +                    break; +                } + +                // Call the compact callback, if any +                if(AddFileCB != NULL) +                    AddFileCB(pvUserData, hf->dwFilePos, hf->dwDataSize, false); + +                // Update the compressed file size +                pFileEntry->dwCmpSize += dwBytesInSector; +                dwBytesInSector = 0; +                dwSectorIndex++; +            } +        } +    } + +    // Cleanup +    if(pbCompressed != NULL) +        STORM_FREE(pbCompressed); +    return nError; +} + +//----------------------------------------------------------------------------- +// Recrypts file data for file renaming + +static int RecryptFileData( +    TMPQArchive * ha, +    TMPQFile * hf, +    const char * szFileName, +    const char * szNewFileName) +{ +    ULONGLONG RawFilePos; +    TFileEntry * pFileEntry = hf->pFileEntry; +    DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize; +    DWORD dwOldKey; +    DWORD dwNewKey; +    int nError = ERROR_SUCCESS; + +    // The file must be encrypted +    assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); + +    // File decryption key is calculated from the plain name +    szNewFileName = GetPlainFileNameA(szNewFileName); +    szFileName = GetPlainFileNameA(szFileName); + +    // Calculate both file keys +    dwOldKey = DecryptFileKey(szFileName,    pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); +    dwNewKey = DecryptFileKey(szNewFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); + +    // Incase the keys are equal, don't recrypt the file +    if(dwNewKey == dwOldKey) +        return ERROR_SUCCESS; +    hf->dwFileKey = dwOldKey; + +    // Calculate the raw position of the file in the archive +    hf->MpqFilePos = pFileEntry->ByteOffset; +    hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; + +    // Allocate buffer for file transfer +    nError = AllocateSectorBuffer(hf); +    if(nError != ERROR_SUCCESS) +        return nError; + +    // Also allocate buffer for sector offsets +    // Note: Don't load sector checksums, we don't need to recrypt them +    nError = AllocateSectorOffsets(hf, true); +    if(nError != ERROR_SUCCESS) +        return nError; + +    // If we have sector offsets, recrypt these as well +    if(hf->SectorOffsets != NULL) +    { +        // Allocate secondary buffer for sectors copy +        DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); +        DWORD dwSectorOffsLen = hf->SectorOffsets[0]; + +        if(SectorOffsetsCopy == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Recrypt the array of sector offsets +        memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen); +        EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwNewKey - 1); +        BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen); + +        // Write the recrypted array back +        if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen)) +            nError = GetLastError(); +        STORM_FREE(SectorOffsetsCopy); +    } + +    // Now we have to recrypt all file sectors. We do it without +    // recompression, because recompression is not necessary in this case +    if(nError == ERROR_SUCCESS) +    { +        for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) +        { +            DWORD dwRawDataInSector = hf->dwSectorSize; +            DWORD dwRawByteOffset = dwSector * hf->dwSectorSize; + +            // Last sector: If there is not enough bytes remaining in the file, cut the raw size +            if(dwRawDataInSector > dwBytesToRecrypt) +                dwRawDataInSector = dwBytesToRecrypt; + +            // Fix the raw data length if the file is compressed +            if(hf->SectorOffsets != NULL) +            { +                dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector]; +                dwRawByteOffset = hf->SectorOffsets[dwSector]; +            } + +            // Calculate the raw file offset of the file sector +            CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); + +            // Read the file sector +            if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) +            { +                nError = GetLastError(); +                break; +            } + +            // If necessary, re-encrypt the sector +            // Note: Recompression is not necessary here. Unlike encryption,  +            // the compression does not depend on the position of the file in MPQ. +            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); +            DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector); +            EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwNewKey + dwSector); +            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); + +            // Write the sector back +            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) +            { +                nError = GetLastError(); +                break; +            } + +            // Decrement number of bytes remaining +            dwBytesToRecrypt -= hf->dwSectorSize; +        } +    } + +    return nError; +} + +//----------------------------------------------------------------------------- +// Support functions for adding files to the MPQ + +int SFileAddFile_Init( +    TMPQArchive * ha, +    const char * szFileName, +    ULONGLONG FileTime, +    DWORD dwFileSize, +    LCID lcLocale, +    DWORD dwFlags, +    TMPQFile ** phf) +{ +    TFileEntry * pFileEntry = NULL; +    ULONGLONG TempPos;                  // For various file offset calculations +    TMPQFile * hf = NULL;               // File structure for newly added file +    int nError = ERROR_SUCCESS; + +    // +    // Note: This is an internal function so no validity checks are done. +    // It is the caller's responsibility to make sure that no invalid +    // flags get to this point +    // + +    // Sestor CRC is not allowed with single unit files +    if(dwFlags & MPQ_FILE_SINGLE_UNIT) +        dwFlags &= ~MPQ_FILE_SECTOR_CRC; + +    // Sector CRC is not allowed if the file is not compressed +    if(!(dwFlags & MPQ_FILE_COMPRESSED)) +        dwFlags &= ~MPQ_FILE_SECTOR_CRC; +     +    // Fix Key is not allowed if the file is not enrypted +    if(!(dwFlags & MPQ_FILE_ENCRYPTED)) +        dwFlags &= ~MPQ_FILE_FIX_KEY; + +    // If the MPQ is of version 3.0 or higher, we ignore file locale. +    // This is because HET and BET tables have no known support for it +    if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) +        lcLocale = 0; + +    // Allocate the TMPQFile entry for newly added file +    hf = CreateMpqFile(ha); +    if(hf == NULL) +        nError = ERROR_NOT_ENOUGH_MEMORY; + +    // Find a free space in the MPQ, as well as free block table entry +    if(nError == ERROR_SUCCESS) +    { +        // Find the position where the file will be stored +        FindFreeMpqSpace(ha, &hf->MpqFilePos); +        hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; +        hf->bIsWriteHandle = true; + +        // Sanity check: The MPQ must be marked as changed at this point +        assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0); + +        // When format V1, the size of the archive cannot exceed 4 GB +        if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) +        { +            TempPos  = hf->MpqFilePos + dwFileSize; +            TempPos += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); +            TempPos += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); +            TempPos += ha->pHeader->dwBlockTableSize * sizeof(USHORT); +            if((TempPos >> 32) != 0) +                nError = ERROR_DISK_FULL; +        } +    } + +    // Allocate file entry in the MPQ +    if(nError == ERROR_SUCCESS) +    { +        // Check if the file already exists in the archive +        pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale); +        if(pFileEntry == NULL) +        { +            pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale); +            if(pFileEntry == NULL) +                nError = ERROR_DISK_FULL; +        } +        else +        { +            // If the file exists and "replace existing" is not set, fail it +            if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0) +                nError = ERROR_ALREADY_EXISTS; + +            // If the file entry already contains a file +            // and it is a pseudo-name, replace it +            if(nError == ERROR_SUCCESS) +            { +                AllocateFileName(pFileEntry, szFileName); +            } +        } +    } + +    // +    // At this point, the file name in file entry must be non-NULL +    // + +    // Create key for file encryption +    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) +    { +        hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); +    } + +    if(nError == ERROR_SUCCESS) +    { +        // Initialize the hash entry for the file +        hf->pFileEntry = pFileEntry; +        hf->dwDataSize = dwFileSize; + +        // Initialize the block table entry for the file +        pFileEntry->ByteOffset = hf->MpqFilePos; +        pFileEntry->dwFileSize = dwFileSize; +        pFileEntry->dwCmpSize = 0; +        pFileEntry->dwFlags  = dwFlags | MPQ_FILE_EXISTS; +        pFileEntry->lcLocale = (USHORT)lcLocale; + +        // Initialize the file time, CRC32 and MD5 +        assert(sizeof(hf->hctx) >= sizeof(hash_state)); +        memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); +        md5_init((hash_state *)hf->hctx); +        pFileEntry->dwCrc32 = crc32(0, Z_NULL, 0); + +        // If the caller gave us a file time, use it. +        pFileEntry->FileTime = FileTime; + +        // Call the callback, if needed +        if(AddFileCB != NULL) +            AddFileCB(pvUserData, 0, hf->dwDataSize, false); +    } + +    // If an error occured, remember it +    if(nError != ERROR_SUCCESS) +        hf->bErrorOccured = true; +    *phf = hf; +    return nError; +} + +int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression) +{ +    TMPQArchive * ha; +    TFileEntry * pFileEntry; +    int nError = ERROR_SUCCESS; + +    // Don't bother if the caller gave us zero size +    if(pvData == NULL || dwSize == 0) +        return ERROR_SUCCESS; + +    // Get pointer to the MPQ archive +    pFileEntry = hf->pFileEntry; +    ha = hf->ha; + +    // Allocate file buffers +    if(hf->pbFileSector == NULL) +    { +        ULONGLONG RawFilePos = hf->RawFilePos; + +        // Allocate buffer for file sector +        nError = AllocateSectorBuffer(hf); +        if(nError != ERROR_SUCCESS) +        { +            hf->bErrorOccured = true; +            return nError; +        } + +        // Allocate patch info, if the data is patch +        if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize)) +        { +            // Set the MPQ_FILE_PATCH_FILE flag +            hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; + +            // Allocate the patch info +            nError = AllocatePatchInfo(hf, false); +            if(nError != ERROR_SUCCESS) +            { +                hf->bErrorOccured = true; +                return nError; +            } +        } + +        // Allocate sector offsets +        if(hf->SectorOffsets == NULL) +        { +            nError = AllocateSectorOffsets(hf, false); +            if(nError != ERROR_SUCCESS) +            { +                hf->bErrorOccured = true; +                return nError; +            } +        } + +        // Create array of sector checksums +        if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)) +        { +            nError = AllocateSectorChecksums(hf, false); +            if(nError != ERROR_SUCCESS) +            { +                hf->bErrorOccured = true; +                return nError; +            } +        } + +        // Pre-save the patch info, if any +        if(hf->pPatchInfo != NULL) +        { +            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength)) +                nError = GetLastError(); +   +            pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength; +            RawFilePos += hf->pPatchInfo->dwLength; +        } + +        // Pre-save the sector offset table, just to reserve space in the file. +        // Note that we dont need to swap the sector positions, nor encrypt the table +        // at the moment, as it will be written again after writing all file sectors. +        if(hf->SectorOffsets != NULL) +        { +            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, hf->SectorOffsets[0])) +                nError = GetLastError(); + +            pFileEntry->dwCmpSize += hf->SectorOffsets[0]; +            RawFilePos += hf->SectorOffsets[0]; +        } +    } + +    // Write the MPQ data to the file +    if(nError == ERROR_SUCCESS) +        nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression); + +    // If it succeeded and we wrote all the file data, +    // we need to re-save sector offset table +    if(nError == ERROR_SUCCESS) +    { +        if(hf->dwFilePos >= pFileEntry->dwFileSize) +        { +            // Finish calculating CRC32 +            hf->pFileEntry->dwCrc32 = hf->dwCrc32; + +            // Finish calculating MD5 +            md5_done((hash_state *)hf->hctx, hf->pFileEntry->md5); + +            // If we also have sector checksums, write them to the file +            if(hf->SectorChksums != NULL) +            { +                nError = WriteSectorChecksums(hf); +                if(nError != ERROR_SUCCESS) +                    hf->bErrorOccured = true; +            } + +            // Now write patch info +            if(hf->pPatchInfo != NULL) +            { +                memcpy(hf->pPatchInfo->md5, hf->pFileEntry->md5, MD5_DIGEST_SIZE); +                hf->pPatchInfo->dwDataSize  = hf->pFileEntry->dwFileSize; +                hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize; +                nError = WritePatchInfo(hf); +                if(nError != ERROR_SUCCESS) +                    hf->bErrorOccured = true; +            } + +            // Now write sector offsets to the file +            if(hf->SectorOffsets != NULL) +            { +                nError = WriteSectorOffsets(hf); +                if(nError != ERROR_SUCCESS) +                    hf->bErrorOccured = true; +            } + +            // Write the MD5 hashes of each file chunk, if required +            if(ha->pHeader->dwRawChunkSize != 0) +            { +                nError = WriteMpqDataMD5(ha->pStream, +                                         ha->MpqPos + hf->pFileEntry->ByteOffset, +                                         hf->pFileEntry->dwCmpSize, +                                         ha->pHeader->dwRawChunkSize); +                if(nError != ERROR_SUCCESS) +                    hf->bErrorOccured = true; +            } +        } +    } +    else +    { +        hf->bErrorOccured = true; +    } + +    return nError; +} + +int SFileAddFile_Finish(TMPQFile * hf) +{ +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    int nError = ERROR_SUCCESS; + +    // If all previous operations succeeded, we can update the MPQ +    if(!hf->bErrorOccured) +    { +        // Verify if the caller wrote the file properly +        if(hf->pPatchInfo == NULL) +        { +            assert(pFileEntry != NULL); +            if(hf->dwFilePos != pFileEntry->dwFileSize) +            { +                nError = ERROR_CAN_NOT_COMPLETE; +                hf->bErrorOccured = true; +            } +        } +        else +        { +            if(hf->dwFilePos != hf->pPatchInfo->dwDataSize) +            { +                nError = ERROR_CAN_NOT_COMPLETE; +                hf->bErrorOccured = true; +            } +        } +    } + +    if(!hf->bErrorOccured) +    { +        // Call the user callback, if any +        if(AddFileCB != NULL) +            AddFileCB(pvUserData, hf->dwDataSize, hf->dwDataSize, true); + +        // Update the size of the block table +        ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; +    } +    else +    { +        // Free the file entry in MPQ tables +        if(pFileEntry != NULL) +            FreeFileEntry(ha, pFileEntry); +    } + +    // Clear the add file callback +    FreeMPQFile(hf); +    pvUserData = NULL; +    AddFileCB = NULL; +    return nError; +} + +//----------------------------------------------------------------------------- +// Adds data as file to the archive  + +bool WINAPI SFileCreateFile( +    HANDLE hMpq, +    const char * szArchivedName, +    ULONGLONG FileTime, +    DWORD dwFileSize, +    LCID lcLocale, +    DWORD dwFlags, +    HANDLE * phFile) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    int nError = ERROR_SUCCESS; + +    // Check valid parameters +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(szArchivedName == NULL || *szArchivedName == 0) +        nError = ERROR_INVALID_PARAMETER; +    if(phFile == NULL) +        nError = ERROR_INVALID_PARAMETER; +     +    // Don't allow to add file if the MPQ is open for read only +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +        nError = ERROR_ACCESS_DENIED; + +    // Don't allow to add a file under pseudo-file name +    if(IsPseudoFileName(szArchivedName, NULL)) +        nError = ERROR_INVALID_PARAMETER; + +    // Don't allow to add any of the internal files +    if(IsInternalMpqFileName(szArchivedName)) +        nError = ERROR_INTERNAL_FILE; + +    // Perform validity check of the MPQ flags +    if(nError == ERROR_SUCCESS) +    { +        // Mask all unsupported flags out +        dwFlags &= MPQ_FILE_VALID_FLAGS; + +        // Check for valid flag combinations +        if((dwFlags & (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) == (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) +            nError = ERROR_INVALID_PARAMETER; +    } + +    // Create the file in MPQ +    if(nError == ERROR_SUCCESS) +    { +        // Invalidate the entries for (listfile) and (attributes) +        // After we are done with MPQ changes, we need to re-create them anyway +        InvalidateInternalFiles(ha); + +        // Initiate the add file operation +        nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile); +    } + +    // Deal with the errors +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +bool WINAPI SFileWriteFile( +    HANDLE hFile, +    const void * pvData, +    DWORD dwSize, +    DWORD dwCompression) +{ +    TMPQFile * hf = (TMPQFile *)hFile; +    int nError = ERROR_SUCCESS; + +    // Check the proper parameters +    if(!IsValidFileHandle(hf)) +        nError = ERROR_INVALID_HANDLE; +    if(hf->bIsWriteHandle == false) +        nError = ERROR_INVALID_HANDLE; + +    // Special checks for single unit files +    if(nError == ERROR_SUCCESS && (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)) +    { +        // +        // Note: Blizzard doesn't support single unit files +        // that are stored as encrypted or imploded. We will allow them here, +        // the calling application must ensure that such flag combination doesn't get here +        // + +//      if(dwFlags & MPQ_FILE_IMPLODE) +//          nError = ERROR_INVALID_PARAMETER; +// +//      if(dwFlags & MPQ_FILE_ENCRYPTED) +//          nError = ERROR_INVALID_PARAMETER; +         +        // Lossy compression is not allowed on single unit files +        if(dwCompression & LOSSY_COMPRESSION_MASK) +            nError = ERROR_INVALID_PARAMETER; +    } + + +    // Write the data to the file +    if(nError == ERROR_SUCCESS) +        nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression); +     +    // Deal with errors +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +bool WINAPI SFileFinishFile(HANDLE hFile) +{ +    TMPQFile * hf = (TMPQFile *)hFile; +    int nError = ERROR_SUCCESS; + +    // Check the proper parameters +    if(!IsValidFileHandle(hf)) +        nError = ERROR_INVALID_HANDLE; +    if(hf->bIsWriteHandle == false) +        nError = ERROR_INVALID_HANDLE; + +    // Finish the file +    if(nError == ERROR_SUCCESS) +        nError = SFileAddFile_Finish(hf); +     +    // Deal with errors +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Adds a file to the archive  + +bool WINAPI SFileAddFileEx( +    HANDLE hMpq, +    const TCHAR * szFileName, +    const char * szArchivedName, +    DWORD dwFlags, +    DWORD dwCompression,            // Compression of the first sector +    DWORD dwCompressionNext)        // Compression of next sectors +{ +    ULONGLONG FileSize = 0; +    ULONGLONG FileTime = 0; +    TFileStream * pStream = NULL; +    HANDLE hMpqFile = NULL; +    LPBYTE pbFileData = NULL; +    DWORD dwBytesRemaining = 0; +    DWORD dwBytesToRead; +    DWORD dwSectorSize = 0x1000; +    DWORD dwChannels = 0; +    bool bIsAdpcmCompression = false; +    bool bIsFirstSector = true; +    int nError = ERROR_SUCCESS; + +    // Check parameters +    if(szFileName == NULL || *szFileName == 0) +        nError = ERROR_INVALID_PARAMETER; + +    // Open added file +    if(nError == ERROR_SUCCESS) +    { +        pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        if(pStream == NULL) +            nError = GetLastError(); +    } + +    // Get the file size and file time +    if(nError == ERROR_SUCCESS) +    { +        FileStream_GetTime(pStream, &FileTime); +        FileStream_GetSize(pStream, &FileSize); +         +        // Files bigger than 4GB cannot be added to MPQ +        if(FileSize >> 32) +            nError = ERROR_DISK_FULL; +    } + +    // Allocate data buffer for reading from the source file +    if(nError == ERROR_SUCCESS) +    { +        dwBytesRemaining = (DWORD)FileSize; +        pbFileData = STORM_ALLOC(BYTE, dwSectorSize); +        if(pbFileData == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Deal with various combination of compressions +    if(nError == ERROR_SUCCESS) +    { +        // When the compression for next blocks is set to default, +        // we will copy the compression for the first sector +        if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME) +            dwCompressionNext = dwCompression; +         +        // If the caller wants ADPCM compression, we make sure +        // that the first sector is not compressed with lossy compression +        if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) +        { +            // The first compression must not be WAVE +            if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) +                dwCompression = MPQ_COMPRESSION_PKWARE; +             +            dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); +            bIsAdpcmCompression = true; +        } + +        // Initiate adding file to the MPQ +        if(!SFileCreateFile(hMpq, szArchivedName, FileTime, (DWORD)FileSize, lcFileLocale, dwFlags, &hMpqFile)) +            nError = GetLastError(); +    } + +    // Write the file data to the MPQ +    while(nError == ERROR_SUCCESS && dwBytesRemaining != 0) +    { +        // Get the number of bytes remaining in the source file +        dwBytesToRead = dwBytesRemaining; +        if(dwBytesToRead > dwSectorSize) +            dwBytesToRead = dwSectorSize; + +        // Read data from the local file +        if(!FileStream_Read(pStream, NULL, pbFileData, dwBytesToRead)) +        { +            nError = GetLastError(); +            break; +        } + +        // If the file being added is a WAVE file, we check number of channels +        if(bIsFirstSector && bIsAdpcmCompression) +        { +            // The file must really be a wave file, otherwise it's data corruption +            if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels)) +            { +                nError = ERROR_BAD_FORMAT; +                break; +            } + +            // Setup the compression according to number of channels +            dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; +            bIsFirstSector = false; +        } + +        // Add the file sectors to the MPQ +        if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression)) +        { +            nError = GetLastError(); +            break; +        } + +        // Set the next data compression +        dwBytesRemaining -= dwBytesToRead; +        dwCompression = dwCompressionNext; +    } + +    // Finish the file writing +    if(hMpqFile != NULL) +    { +        if(!SFileFinishFile(hMpqFile)) +            nError = GetLastError(); +    } + +    // Cleanup and exit +    if(pbFileData != NULL) +        STORM_FREE(pbFileData); +    if(pStream != NULL) +        FileStream_Close(pStream); +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} +                                                                                                                                  +// Adds a data file into the archive +bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags) +{ +    return SFileAddFileEx(hMpq, +                          szFileName, +                          szArchivedName, +                          dwFlags, +                          DefaultDataCompression, +                          DefaultDataCompression); +} + +// Adds a WAVE file into the archive +bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality) +{ +    DWORD dwCompression = 0; + +    // +    // Note to wave compression level: +    // The following conversion table applied: +    // High quality:   WaveCompressionLevel = -1 +    // Medium quality: WaveCompressionLevel = 4 +    // Low quality:    WaveCompressionLevel = 2 +    // +    // Starcraft files are packed as Mono (0x41) on medium quality. +    // Because this compression is not used anymore, our compression functions +    // will default to WaveCompressionLevel = 4 when using ADPCM compression +    //  + +    // Convert quality to data compression +    switch(dwQuality) +    { +        case MPQ_WAVE_QUALITY_HIGH: +//          WaveCompressionLevel = -1; +            dwCompression = MPQ_COMPRESSION_PKWARE; +            break; + +        case MPQ_WAVE_QUALITY_MEDIUM: +//          WaveCompressionLevel = 4; +            dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; +            break; + +        case MPQ_WAVE_QUALITY_LOW: +//          WaveCompressionLevel = 2; +            dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; +            break; +    } + +    return SFileAddFileEx(hMpq, +                          szFileName, +                          szArchivedName, +                          dwFlags, +                          MPQ_COMPRESSION_PKWARE,   // First sector should be compressed as data +                          dwCompression);           // Next sectors should be compressed as WAVE +} + +//----------------------------------------------------------------------------- +// bool SFileRemoveFile(HANDLE hMpq, char * szFileName) +// +// This function removes a file from the archive. The file content +// remains there, only the entries in the hash table and in the block +// table are updated.  + +bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pFileEntry = NULL; // File entry of the file to be deleted +    DWORD dwFileIndex = 0; +    int nError = ERROR_SUCCESS; + +    // Keep compiler happy +    dwSearchScope = dwSearchScope; + +    // Check the parameters +    if(nError == ERROR_SUCCESS) +    { +        if(!IsValidMpqHandle(ha)) +            nError = ERROR_INVALID_HANDLE; +        if(szFileName == NULL || *szFileName == 0) +            nError = ERROR_INVALID_PARAMETER; +        if(IsInternalMpqFileName(szFileName)) +            nError = ERROR_INTERNAL_FILE; +    } + +    if(nError == ERROR_SUCCESS) +    { +        // Do not allow to remove files from MPQ open for read only +        if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +            nError = ERROR_ACCESS_DENIED; +    } + +    // Get hash entry belonging to this file +    if(nError == ERROR_SUCCESS) +    { +        if(!IsPseudoFileName(szFileName, &dwFileIndex)) +        { +            if((pFileEntry = GetFileEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) +                nError = ERROR_FILE_NOT_FOUND; +        } +        else +        { +            if((pFileEntry = GetFileEntryByIndex(ha, dwFileIndex)) == NULL) +                nError = ERROR_FILE_NOT_FOUND; +        } +    } + +    // Test if the file is not already deleted +    if(nError == ERROR_SUCCESS) +    { +        if(!(pFileEntry->dwFlags & MPQ_FILE_EXISTS)) +            nError = ERROR_FILE_NOT_FOUND; +    } + +    if(nError == ERROR_SUCCESS) +    { +        // Invalidate the entries for (listfile) and (attributes) +        // After we are done with MPQ changes, we need to re-create them anyway +        InvalidateInternalFiles(ha); + +        // Mark the file entry as free +        nError = FreeFileEntry(ha, pFileEntry); +    } + +    // Resolve error and exit +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +// Renames the file within the archive. +bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pFileEntry = NULL; +    ULONGLONG RawDataOffs; +    TMPQFile * hf; +    int nError = ERROR_SUCCESS; + +    // Test the valid parameters +    if(nError == ERROR_SUCCESS) +    { +        if(!IsValidMpqHandle(ha)) +            nError = ERROR_INVALID_HANDLE; +        if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) +            nError = ERROR_INVALID_PARAMETER; +    } + +    if(nError == ERROR_SUCCESS) +    { +        // Do not allow to rename files in MPQ open for read only +        if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +            nError = ERROR_ACCESS_DENIED; + +        // Do not allow renaming anything to a pseudo-file name +        if(IsPseudoFileName(szFileName, NULL) || IsPseudoFileName(szNewFileName, NULL)) +            nError = ERROR_INVALID_PARAMETER; + +        // Do not allow to rename any of the internal files +        // Also do not allow to rename any of files to an internal file +        if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) +            nError = ERROR_INTERNAL_FILE; +    } + +    // Find the current file entry. +    if(nError == ERROR_SUCCESS) +    { +        // Get the file entry +        pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); +        if(pFileEntry == NULL) +            nError = ERROR_FILE_NOT_FOUND; +    } +         +    // Also try to find file entry for the new file. +    // This verifies if we are not overwriting an existing file +    // (whose name we perhaps don't know) +    if(nError == ERROR_SUCCESS) +    { +        if(GetFileEntryLocale(ha, szNewFileName, pFileEntry->lcLocale) != NULL) +            nError = ERROR_ALREADY_EXISTS; +    } + +    // Now we rename the existing file entry. +    if(nError == ERROR_SUCCESS) +    { +        // Rename the file entry +        nError = RenameFileEntry(ha, pFileEntry, szNewFileName); +    } + +    // Now we copy the existing file entry to the new one +    if(nError == ERROR_SUCCESS) +    { +        // If the file is encrypted, we have to re-crypt the file content +        // with the new decryption key +        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +        { +            hf = CreateMpqFile(ha); +            if(hf != NULL) +            { +                // Recrypt the file data in the MPQ +                hf->pFileEntry = pFileEntry; +                hf->dwDataSize = pFileEntry->dwFileSize; +                nError = RecryptFileData(ha, hf, szFileName, szNewFileName); +                 +                // Update the MD5 +                if(ha->pHeader->dwRawChunkSize != 0) +                { +                    RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; +                    WriteMpqDataMD5(ha->pStream, +                                    RawDataOffs, +                                    pFileEntry->dwCmpSize, +                                    ha->pHeader->dwRawChunkSize); +                } +                 +                FreeMPQFile(hf); +            } +            else +            { +                nError = ERROR_NOT_ENOUGH_MEMORY; +            } +        } +    } + +    // +    // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry +    // + +    // Resolve error and return +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Sets default data compression for SFileAddFile + +bool WINAPI SFileSetDataCompression(DWORD DataCompression) +{ +    unsigned int uValidMask = (MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_PKWARE | MPQ_COMPRESSION_BZIP2 | MPQ_COMPRESSION_SPARSE); + +    if((DataCompression & uValidMask) != DataCompression) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    DefaultDataCompression = DataCompression; +    return true; +} + +//----------------------------------------------------------------------------- +// Changes locale ID of a file + +bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) +{ +    TMPQArchive * ha; +    TFileEntry * pFileEntry; +    TMPQFile * hf = (TMPQFile *)hFile; + +    // Invalid handle => do nothing +    if(!IsValidFileHandle(hf)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    // Do not allow unnamed access +    if(hf->pFileEntry->szFileName == NULL) +    { +        SetLastError(ERROR_CAN_NOT_COMPLETE); +        return false; +    } + +    // Do not allow to change locale of any internal file +    if(IsInternalMpqFileName(hf->pFileEntry->szFileName)) +    { +        SetLastError(ERROR_INTERNAL_FILE); +        return false; +    } + +    // Do not allow changing file locales in MPQs version 3 or higher +    ha = hf->ha; +    if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) +    { +        SetLastError(ERROR_NOT_SUPPORTED); +        return false; +    } + +    // Do not allow to rename files in MPQ open for read only +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED); +        return false; +    } + +    // If the file already has that locale, return OK +    if(hf->pFileEntry->lcLocale == lcNewLocale) +        return true; + +    // We have to check if the file+locale is not already there +    pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale); +    if(pFileEntry != NULL) +    { +        SetLastError(ERROR_ALREADY_EXISTS); +        return false; +    } + +    // Set the locale and return success +    pFileEntry = hf->pFileEntry; +    pFileEntry->lcLocale = (USHORT)lcNewLocale; + +    // Save the new locale to the hash table, if any +    if(ha->pHashTable != NULL) +        ha->pHashTable[pFileEntry->dwHashIndex].lcLocale = (USHORT)lcNewLocale; + +    // Remember that the MPQ tables have been changed +    ha->dwFlags |= MPQ_FLAG_CHANGED; +    return true; +} + +//----------------------------------------------------------------------------- +// Sets add file callback + +bool WINAPI SFileSetAddFileCallback(HANDLE /* hMpq */, SFILE_ADDFILE_CALLBACK aAddFileCB, void * pvData) +{ +    pvUserData = pvData; +    AddFileCB = aAddFileCB; +    return true; +} diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp new file mode 100644 index 0000000..865debc --- /dev/null +++ b/src/SFileAttributes.cpp @@ -0,0 +1,472 @@ +/*****************************************************************************/ +/* SAttrFile.cpp                          Copyright (c) Ladislav Zezula 2007 */ +/*---------------------------------------------------------------------------*/ +/* Description:                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 12.06.04  1.00  Lad  The first version of SAttrFile.cpp                   */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +typedef struct _MPQ_ATTRIBUTES_HEADER +{ +    DWORD dwVersion;                    // Version of the (attributes) file. Must be 100 (0x64) +    DWORD dwFlags;                      // See MPQ_ATTRIBUTE_XXXX + +    // Followed by an array of CRC32 +    // Followed by an array of file times +    // Followed by an array of MD5 +    // Followed by an array of patch bits +} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER; + +//----------------------------------------------------------------------------- +// Public functions (internal use by StormLib) + +int SAttrLoadAttributes(TMPQArchive * ha) +{ +    MPQ_ATTRIBUTES_HEADER AttrHeader; +    HANDLE hFile = NULL; +    DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; +    DWORD dwArraySize; +    DWORD dwBytesRead; +    DWORD i; +    int nError = ERROR_SUCCESS; + +    // File table must be initialized +    assert(ha->pFileTable != NULL); + +    // Attempt to open the "(attributes)" file. +    // If it's not there, then the archive doesn't support attributes +    if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile)) +    { +        // Load the content of the attributes file +        SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL); +        if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER)) +            nError = ERROR_FILE_CORRUPT; + +        // Verify the header of the (attributes) file +        if(nError == ERROR_SUCCESS) +        { +            AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion); +            AttrHeader.dwFlags   = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags); +            ha->dwAttrFlags      = AttrHeader.dwFlags; +            if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER)) +                nError = ERROR_FILE_CORRUPT; +        } + +        // Verify format of the attributes +        if(nError == ERROR_SUCCESS) +        { +            if(AttrHeader.dwVersion > MPQ_ATTRIBUTES_V1) +                nError = ERROR_BAD_FORMAT; +        } + +        // Load the CRC32 (if any) +        if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32)) +        { +            LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize); + +            if(pArrayCRC32 != NULL) +            { +                dwArraySize = dwBlockTableSize * sizeof(DWORD); +                SFileReadFile(hFile, pArrayCRC32, dwArraySize, &dwBytesRead, NULL); +                if(dwBytesRead == dwArraySize) +                { +                     for(i = 0; i < dwBlockTableSize; i++) +                        ha->pFileTable[i].dwCrc32 = BSWAP_INT32_UNSIGNED(pArrayCRC32[i]); +                } +                else +                    nError = ERROR_FILE_CORRUPT; + +                STORM_FREE(pArrayCRC32); +            } +            else +                nError = ERROR_NOT_ENOUGH_MEMORY; +        } + +        // Read the array of file times +        if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME)) +        { +            ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize); + +            if(pArrayFileTime != NULL) +            { +                dwArraySize = dwBlockTableSize * sizeof(ULONGLONG); +                SFileReadFile(hFile, pArrayFileTime, dwArraySize, &dwBytesRead, NULL); +                if(dwBytesRead == dwArraySize) +                { +                    for(i = 0; i < dwBlockTableSize; i++) +                        ha->pFileTable[i].FileTime = BSWAP_INT64_UNSIGNED(pArrayFileTime[i]); +                } +                else +                    nError = ERROR_FILE_CORRUPT; + +                STORM_FREE(pArrayFileTime); +            } +            else +                nError = ERROR_NOT_ENOUGH_MEMORY; +        } + +        // Read the MD5 (if any) +        // Note: MD5 array can be incomplete, if it's the last array in the (attributes) +        if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_MD5)) +        { +            unsigned char * pArrayMD5 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE)); +            unsigned char * md5; + +            if(pArrayMD5 != NULL) +            { +                dwArraySize = dwBlockTableSize * MD5_DIGEST_SIZE; +                SFileReadFile(hFile, pArrayMD5, dwArraySize, &dwBytesRead, NULL); +                if(dwBytesRead == dwArraySize) +                { +                    md5 = pArrayMD5; +                    for(i = 0; i < dwBlockTableSize; i++) +                    { +                        memcpy(ha->pFileTable[i].md5, md5, MD5_DIGEST_SIZE); +                        md5 += MD5_DIGEST_SIZE; +                    } +                } +                else +                    nError = ERROR_FILE_CORRUPT; + +                STORM_FREE(pArrayMD5); +            } +            else +                nError = ERROR_NOT_ENOUGH_MEMORY; +        } + +        // Read the patch bit for each file +        if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_PATCH_BIT)) +        { +            LPBYTE pbBitArray; +            DWORD dwByteSize = ((dwBlockTableSize - 1) / 8) + 1; + +            pbBitArray = STORM_ALLOC(BYTE, dwByteSize); +            if(pbBitArray != NULL) +            { +                SFileReadFile(hFile, pbBitArray, dwByteSize, &dwBytesRead, NULL); +                if(dwBytesRead == dwByteSize) +                { +                    for(i = 0; i < dwBlockTableSize; i++) +                    { +                        DWORD dwByteIndex = i / 8; +                        DWORD dwBitMask = 0x80 >> (i & 7); + +                        // Is the appropriate bit set? +                        if(pbBitArray[dwByteIndex] & dwBitMask) +                        { +                            // At the moment, we assume that the patch bit is present +                            // in both file table and (attributes) +                            assert((ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) != 0); +                            ha->pFileTable[i].dwFlags |= MPQ_FILE_PATCH_FILE; +                        } +                    } +                } +                else +                    nError = ERROR_FILE_CORRUPT; + +                STORM_FREE(pbBitArray); +            } +        } + +        //  +        // Note: Version 7.00 of StormLib saved the (attributes) incorrectly.  +        // Sometimes, number of entries in the (attributes) was 1 item less +        // than block table size.  +        // If we encounter such table, we will zero all three arrays +        // + +        if(nError != ERROR_SUCCESS) +            ha->dwAttrFlags = 0; + +        // Cleanup & exit +        SFileCloseFile(hFile); +    } +    return nError; +} + +int SAttrFileSaveToMpq(TMPQArchive * ha) +{ +    MPQ_ATTRIBUTES_HEADER AttrHeader; +    TFileEntry * pFileEntry; +    TMPQFile * hf = NULL; +    DWORD dwFinalBlockTableSize = ha->dwFileTableSize; +    DWORD dwFileSize = 0; +    DWORD dwToWrite; +    DWORD i; +    int nError = ERROR_SUCCESS; + +    // Now we have to check if we need patch bits in the (attributes) +    if(nError == ERROR_SUCCESS) +    { +        for(i = 0; i < ha->dwFileTableSize; i++) +        { +            if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) +            { +                ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT; +                break; +            } +        } +    } + +    // If the (attributes) is not in the file table yet, +    // we have to increase the final block table size +    pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); +    if(pFileEntry != NULL) +    { +        // If "(attributes)" file exists, and it's set to 0, then remove it +        if(ha->dwAttrFlags == 0) +        { +            FreeFileEntry(ha, pFileEntry); +            return ERROR_SUCCESS; +        } +    } +    else +    { +        // If we don't want to create file atributes, do nothing +        if(ha->dwAttrFlags == 0) +            return ERROR_SUCCESS; + +        // Check where the file entry is going to be allocated. +        // If at the end of the file table, we have to increment +        // the expected size of the (attributes) file. +        pFileEntry = FindFreeFileEntry(ha); +        if(pFileEntry == ha->pFileTable + ha->dwFileTableSize) +            dwFinalBlockTableSize++; +    } + +    // Calculate the size of the attributes file +    if(nError == ERROR_SUCCESS) +    { +        dwFileSize = sizeof(MPQ_ATTRIBUTES_HEADER);         // Header +        if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) +            dwFileSize += dwFinalBlockTableSize * sizeof(DWORD); +        if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) +            dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG); +        if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) +            dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE; +        if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) +            dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1; +    } + +    // Determine the flags for (attributes) +    if(ha->dwFileFlags2 == 0) +        ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize); + +    // Create the attributes file in the MPQ +    nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME, +                                   0, +                                   dwFileSize, +                                   LANG_NEUTRAL, +                                   ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, +                                  &hf); + +    // Write all parts of the (attributes) file +    if(nError == ERROR_SUCCESS) +    { +        assert(ha->dwFileTableSize == dwFinalBlockTableSize); + +        // Note that we don't know what the new bit (0x08) means. +        AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100); +        AttrHeader.dwFlags   = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL)); +        dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER); +        nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB); +    } + +    // Write the array of CRC32 +    if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)) +    { +        LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize); + +        if(pArrayCRC32 != NULL) +        { +            // Copy from file table +            for(i = 0; i < ha->dwFileTableSize; i++) +                pArrayCRC32[i] = BSWAP_INT32_UNSIGNED(ha->pFileTable[i].dwCrc32); + +            dwToWrite = ha->dwFileTableSize * sizeof(DWORD); +            nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB); +            STORM_FREE(pArrayCRC32); +        } +    } + +    // Write the array of file time +    if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)) +    { +        ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize); + +        if(pArrayFileTime != NULL) +        { +            // Copy from file table +            for(i = 0; i < ha->dwFileTableSize; i++) +                pArrayFileTime[i] = BSWAP_INT64_UNSIGNED(ha->pFileTable[i].FileTime); + +            dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG); +            nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB); +            STORM_FREE(pArrayFileTime); +        } +    } + +    // Write the array of MD5s +    if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)) +    { +        char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE); + +        if(pArrayMD5 != NULL) +        { +            // Copy from file table +            for(i = 0; i < ha->dwFileTableSize; i++) +                memcpy(&pArrayMD5[i * MD5_DIGEST_SIZE], ha->pFileTable[i].md5, MD5_DIGEST_SIZE); + +            dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE; +            nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB); +            STORM_FREE(pArrayMD5); +        } +    } + +    // Write the array of patch bits +    if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)) +    { +        LPBYTE pbBitArray; +        DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1; + +        pbBitArray = STORM_ALLOC(BYTE, dwByteSize); +        if(pbBitArray != NULL) +        { +            memset(pbBitArray, 0, dwByteSize); +            for(i = 0; i < ha->dwFileTableSize; i++) +            { +                DWORD dwByteIndex = i / 8; +                DWORD dwBitMask = 0x80 >> (i & 7); + +                if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) +                    pbBitArray[dwByteIndex] |= dwBitMask; +            } + +            nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB); +            STORM_FREE(pbBitArray); +        } +    } + +    // Finalize the file in the archive +    if(hf != NULL) +    { +        SFileAddFile_Finish(hf); +    } + +    if(nError == ERROR_SUCCESS) +        ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES; +    return nError; +} + +//----------------------------------------------------------------------------- +// Public functions + +DWORD WINAPI SFileGetAttributes(HANDLE hMpq) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    // Verify the parameters +    if(!IsValidMpqHandle(ha)) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return SFILE_INVALID_ATTRIBUTES; +    } + +    return ha->dwAttrFlags; +} + +bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    // Verify the parameters +    if(!IsValidMpqHandle(ha)) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    // Not allowed when the archive is read-only +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED); +        return false; +    } + +    // Set the attributes +    InvalidateInternalFiles(ha); +    ha->dwAttrFlags = (dwFlags & MPQ_ATTRIBUTE_ALL); +    return true; +} + +bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName) +{ +    hash_state md5_state; +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TMPQFile * hf; +    BYTE Buffer[0x1000]; +    HANDLE hFile = NULL; +    DWORD dwTotalBytes = 0; +    DWORD dwBytesRead; +    DWORD dwCrc32; + +    // Verify the parameters +    if(!IsValidMpqHandle(ha)) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    // Not allowed when the archive is read-only +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +    { +        SetLastError(ERROR_ACCESS_DENIED); +        return false; +    } + +    // Attempt to open the file +    if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) +        return false; + +    // Get the file size +    hf = (TMPQFile *)hFile; +    SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD), NULL); + +    // Initialize the CRC32 and MD5 contexts +    md5_init(&md5_state); +    dwCrc32 = crc32(0, Z_NULL, 0); + +    // Go through entire file and calculate both CRC32 and MD5 +    while(dwTotalBytes != 0) +    { +        // Read data from file +        SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); +        if(dwBytesRead == 0) +            break; + +        // Update CRC32 and MD5 +        dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); +        md5_process(&md5_state, Buffer, dwBytesRead); + +        // Decrement the total size +        dwTotalBytes -= dwBytesRead; +    } + +    // Update both CRC32 and MD5 +    hf->pFileEntry->dwCrc32 = dwCrc32; +    md5_done(&md5_state, hf->pFileEntry->md5); + +    // Remember that we need to save the MPQ tables +    InvalidateInternalFiles(ha); +    SFileCloseFile(hFile); +    return true; +} diff --git a/src/SFileCompactArchive.cpp b/src/SFileCompactArchive.cpp new file mode 100644 index 0000000..004ca7d --- /dev/null +++ b/src/SFileCompactArchive.cpp @@ -0,0 +1,765 @@ +/*****************************************************************************/ +/* SFileCompactArchive.cpp                Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Archive compacting function                                               */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 14.04.03  1.00  Lad  Splitted from SFileCreateArchiveEx.cpp               */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +/*****************************************************************************/ +/* Local variables                                                           */ +/*****************************************************************************/ + +static SFILE_COMPACT_CALLBACK CompactCB = NULL; +static ULONGLONG CompactBytesProcessed = 0; +static ULONGLONG CompactTotalBytes = 0; +static void * pvUserData = NULL; + +/*****************************************************************************/ +/* Local functions                                                           */ +/*****************************************************************************/ + +static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys) +{ +    TFileEntry * pFileTableEnd; +    TFileEntry * pFileEntry; +    DWORD dwBlockIndex = 0; +    int nError = ERROR_SUCCESS; + +    // Add the listfile to the MPQ +    if(nError == ERROR_SUCCESS && szListFile != NULL) +    { +        // Notify the user +        if(CompactCB != NULL) +            CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes); + +        nError = SFileAddListFile((HANDLE)ha, szListFile); +    } + +    // Verify the file table +    if(nError == ERROR_SUCCESS) +    { +        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +        for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) +        { +            if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) +            { +                if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) +                { +                    DWORD dwFileKey = 0; + +                    // Resolve the file key. Use plain file name for it +                    if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +                    { +                        dwFileKey = DecryptFileKey(pFileEntry->szFileName, +                                                   pFileEntry->ByteOffset, +                                                   pFileEntry->dwFileSize, +                                                   pFileEntry->dwFlags); +                    } + +                    // Give the key to the caller +                    if(pFileKeys != NULL) +                        pFileKeys[dwBlockIndex] = dwFileKey; +                } +                else +                { +                    nError = ERROR_CAN_NOT_COMPLETE; +                    break; +                } +            } +        } +    } + +    return nError; +} + +static int CopyNonMpqData( +    TFileStream * pSrcStream, +    TFileStream * pTrgStream, +    ULONGLONG & ByteOffset, +    ULONGLONG & ByteCount) +{ +    ULONGLONG DataSize = ByteCount; +    DWORD dwToRead; +    char DataBuffer[0x1000]; +    int nError = ERROR_SUCCESS; + +    // Copy the data +    while(DataSize > 0) +    { +        // Get the proper size of data +        dwToRead = sizeof(DataBuffer); +        if(DataSize < dwToRead) +            dwToRead = (DWORD)DataSize; + +        // Read from the source stream +        if(!FileStream_Read(pSrcStream, &ByteOffset, DataBuffer, dwToRead)) +        { +            nError = GetLastError(); +            break; +        } + +        // Write to the target stream +        if(!FileStream_Write(pTrgStream, NULL, DataBuffer, dwToRead)) +        { +            nError = GetLastError(); +            break; +        } + +        // Update the progress +        if(CompactCB != NULL) +        { +            CompactBytesProcessed += dwToRead; +            CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes); +        } + +        // Decrement the number of data to be copied +        ByteOffset += dwToRead; +        DataSize -= dwToRead; +    } + +    return ERROR_SUCCESS; +} + +// Copies all file sectors into another archive. +static int CopyMpqFileSectors( +    TMPQArchive * ha, +    TMPQFile * hf, +    TFileStream * pNewStream) +{ +    TFileEntry * pFileEntry = hf->pFileEntry; +    ULONGLONG RawFilePos;               // Used for calculating sector offset in the old MPQ archive +    ULONGLONG MpqFilePos;               // MPQ file position in the new archive +    DWORD dwBytesToCopy = pFileEntry->dwCmpSize; +    DWORD dwPatchSize = 0;              // Size of patch header +    DWORD dwFileKey1 = 0;               // File key used for decryption +    DWORD dwFileKey2 = 0;               // File key used for encryption +    DWORD dwCmpSize = 0;                // Compressed file size, including patch header +    int nError = ERROR_SUCCESS; + +    // Remember the position in the destination file +    FileStream_GetPos(pNewStream, &MpqFilePos); +    MpqFilePos -= ha->MpqPos; + +    // Resolve decryption keys. Note that the file key given  +    // in the TMPQFile structure also includes the key adjustment +    if(nError == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) +    { +        dwFileKey2 = dwFileKey1 = hf->dwFileKey; +        if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) +        { +            dwFileKey2 = (dwFileKey1 ^ pFileEntry->dwFileSize) - (DWORD)pFileEntry->ByteOffset; +            dwFileKey2 = (dwFileKey2 + (DWORD)MpqFilePos) ^ pFileEntry->dwFileSize; +        } +    } + +    // If we have to save patch header, do it +    if(nError == ERROR_SUCCESS && hf->pPatchInfo != NULL) +    { +        BSWAP_ARRAY32_UNSIGNED(hf->pPatchInfo, sizeof(DWORD) * 3); +        if(!FileStream_Write(pNewStream, NULL, hf->pPatchInfo, hf->pPatchInfo->dwLength)) +            nError = GetLastError(); + +        // Save the size of the patch info +        dwPatchSize = hf->pPatchInfo->dwLength; +    } + +    // If we have to save sector offset table, do it. +    if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL) +    { +        DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); +        DWORD dwSectorOffsLen = hf->SectorOffsets[0]; + +        assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0); +        assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED); + +        if(SectorOffsetsCopy == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; + +        // Encrypt the secondary sector offset table and write it to the target file +        if(nError == ERROR_SUCCESS) +        { +            memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen); +            if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +                EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwFileKey2 - 1); + +            BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen); +            if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen)) +                nError = GetLastError(); + +            dwBytesToCopy -= dwSectorOffsLen; +            dwCmpSize += dwSectorOffsLen; +        } + +        // Update compact progress +        if(CompactCB != NULL) +        { +            CompactBytesProcessed += dwSectorOffsLen; +            CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); +        } + +        STORM_FREE(SectorOffsetsCopy); +    } + +    // Now we have to copy all file sectors. We do it without +    // recompression, because recompression is not necessary in this case +    if(nError == ERROR_SUCCESS) +    { +        for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) +        { +            DWORD dwRawDataInSector = hf->dwSectorSize; +            DWORD dwRawByteOffset = dwSector * hf->dwSectorSize; + +            // Fix the raw data length if the file is compressed +            if(hf->SectorOffsets != NULL) +            { +                dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector]; +                dwRawByteOffset = hf->SectorOffsets[dwSector]; +            } + +            // Last sector: If there is not enough bytes remaining in the file, cut the raw size +            if(dwRawDataInSector > dwBytesToCopy) +                dwRawDataInSector = dwBytesToCopy; + +            // Calculate the raw file offset of the file sector +            CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); +             +            // Read the file sector +            if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) +            { +                nError = GetLastError(); +                break; +            } + +            // If necessary, re-encrypt the sector +            // Note: Recompression is not necessary here. Unlike encryption,  +            // the compression does not depend on the position of the file in MPQ. +            if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2) +            { +                BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); +                DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey1 + dwSector); +                EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey2 + dwSector); +                BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); +            } + +            // Now write the sector back to the file +            if(!FileStream_Write(pNewStream, NULL, hf->pbFileSector, dwRawDataInSector)) +            { +                nError = GetLastError(); +                break; +            } + +            // Update compact progress +            if(CompactCB != NULL) +            { +                CompactBytesProcessed += dwRawDataInSector; +                CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); +            } + +            // Adjust byte counts +            dwBytesToCopy -= dwRawDataInSector; +            dwCmpSize += dwRawDataInSector; +        } +    } + +    // Copy the sector CRCs, if any +    // Sector CRCs are always compressed (not imploded) and unencrypted +    if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL && hf->SectorChksums != NULL) +    { +        DWORD dwCrcLength; + +        dwCrcLength = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount]; +        if(dwCrcLength != 0) +        { +            if(!FileStream_Read(ha->pStream, NULL, hf->SectorChksums, dwCrcLength)) +                nError = GetLastError(); + +            if(!FileStream_Write(pNewStream, NULL, hf->SectorChksums, dwCrcLength)) +                nError = GetLastError(); + +            // Update compact progress +            if(CompactCB != NULL) +            { +                CompactBytesProcessed += dwCrcLength; +                CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); +            } + +            // Size of the CRC block is also included in the compressed file size +            dwBytesToCopy -= dwCrcLength; +            dwCmpSize += dwCrcLength; +        } +    } + +    // There might be extra data beyond sector checksum table +    // Sometimes, these data are even part of sector offset table +    // Examples: +    // 2012 - WoW\15354\locale-enGB.MPQ:DBFilesClient\SpellLevels.dbc +    // 2012 - WoW\15354\locale-enGB.MPQ:Interface\AddOns\Blizzard_AuctionUI\Blizzard_AuctionUI.xml +    if(nError == ERROR_SUCCESS && dwBytesToCopy != 0) +    { +        LPBYTE pbExtraData; + +        // Allocate space for the extra data +        pbExtraData = STORM_ALLOC(BYTE, dwBytesToCopy); +        if(pbExtraData != NULL) +        { +            if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy)) +                nError = GetLastError(); + +            if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy)) +                nError = GetLastError(); + +            // Include these extra data in the compressed size +            dwCmpSize += dwBytesToCopy; +            dwBytesToCopy = 0; +            STORM_FREE(pbExtraData); +        } +        else +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Write the MD5's of the raw file data, if needed +    if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) +    { +        nError = WriteMpqDataMD5(pNewStream,  +                                 ha->MpqPos + MpqFilePos, +                                 pFileEntry->dwCmpSize, +                                 ha->pHeader->dwRawChunkSize); +    } + +    // Update file position in the block table +    if(nError == ERROR_SUCCESS) +    { +        // At this point, number of bytes written should be exactly +        // the same like the compressed file size. If it isn't, +        // there's something wrong (an unknown archive version, MPQ protection, ...) +        //  +        // Note: Diablo savegames have very weird layout, and the file "hero" +        // seems to have improper compressed size. Instead of real compressed size, +        // the "dwCmpSize" member of the block table entry contains +        // uncompressed size of file data + size of the sector table. +        // If we compact the archive, Diablo will refuse to load the game +        // Seems like some sort of protection to me. +        // +        // Note: Some patch files in WOW patches don't count the patch header +        // into compressed size +        // + +        if(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize) +        { +            // Note: DO NOT update the compressed size in the file entry, no matter how bad it is. +            pFileEntry->ByteOffset = MpqFilePos; +        } +        else +        { +            nError = ERROR_FILE_CORRUPT; +            assert(false); +        } +    } + +    return nError; +} + +static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream) +{ +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pFileEntry; +    TMPQFile * hf = NULL; +    int nError = ERROR_SUCCESS; + +    // Walk through all files and write them to the destination MPQ archive +    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +    { +        // Copy all the file sectors +        // Only do that when the file has nonzero size +        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0) +        { +            // Allocate structure for the MPQ file +            hf = CreateMpqFile(ha); +            if(hf == NULL) +                return ERROR_NOT_ENOUGH_MEMORY; + +            // Store file entry +            hf->pFileEntry = pFileEntry; + +            // Set the raw file position +            hf->MpqFilePos = pFileEntry->ByteOffset; +            hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; + +            // Set the file decryption key +            hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable]; +            hf->dwDataSize = pFileEntry->dwFileSize; + +            // If the file is a patch file, load the patch header +            if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) +            { +                nError = AllocatePatchInfo(hf, true); +                if(nError != ERROR_SUCCESS) +                    break; +            } + +            // Allocate buffers for file sector and sector offset table +            nError = AllocateSectorBuffer(hf); +            if(nError != ERROR_SUCCESS) +                break; + +            // Also allocate sector offset table and sector checksum table +            nError = AllocateSectorOffsets(hf, true); +            if(nError != ERROR_SUCCESS) +                break; + +            // Also load sector checksums, if any +            if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) +            { +                nError = AllocateSectorChecksums(hf, false); +                if(nError != ERROR_SUCCESS) +                    break; +            } + +            // Copy all file sectors +            nError = CopyMpqFileSectors(ha, hf, pNewStream); +            if(nError != ERROR_SUCCESS) +                break; + +            // Free buffers. This also sets "hf" to NULL. +            FreeMPQFile(hf); +        } +    } + +    // Cleanup and exit +    if(hf != NULL) +        FreeMPQFile(hf); +    return nError; +} + + +/*****************************************************************************/ +/* Public functions                                                          */ +/*****************************************************************************/ + +bool WINAPI SFileSetCompactCallback(HANDLE /* hMpq */, SFILE_COMPACT_CALLBACK aCompactCB, void * pvData) +{ +    CompactCB = aCompactCB; +    pvUserData = pvData; +    return true; +} + +//----------------------------------------------------------------------------- +// Archive compacting + +bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */) +{ +    TFileStream * pTempStream = NULL; +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    ULONGLONG ByteOffset; +    ULONGLONG ByteCount; +    LPDWORD pFileKeys = NULL; +    TCHAR szTempFile[MAX_PATH] = _T(""); +    TCHAR * szTemp = NULL; +    int nError = ERROR_SUCCESS; + +    // Test the valid parameters +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +        nError = ERROR_ACCESS_DENIED; + +    // If the MPQ is changed at this moment, we have to flush the archive +    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED)) +    { +        SFileFlushArchive(hMpq); +    } + +    // Create the table with file keys +    if(nError == ERROR_SUCCESS) +    { +        if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL) +            memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize); +        else +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // First of all, we have to check of we are able to decrypt all files. +    // If not, sorry, but the archive cannot be compacted. +    if(nError == ERROR_SUCCESS) +    { +        // Initialize the progress variables for compact callback +        FileStream_GetSize(ha->pStream, &CompactTotalBytes); +        CompactBytesProcessed = 0; +        nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys); +    } + +    // Get the temporary file name and create it +    if(nError == ERROR_SUCCESS) +    { +        _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream)); +        if((szTemp = _tcsrchr(szTempFile, '.')) != NULL) +            _tcscpy(szTemp + 1, _T("mp_")); +        else +            _tcscat(szTempFile, _T("_")); + +        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        if(pTempStream == NULL) +            nError = GetLastError(); +    } + +    // Write the data before MPQ user data (if any) +    if(nError == ERROR_SUCCESS && ha->UserDataPos != 0) +    { +        // Inform the application about the progress +        if(CompactCB != NULL) +            CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes); + +        ByteOffset = 0; +        ByteCount = ha->UserDataPos; +        nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount); +    } + +    // Write the MPQ user data (if any) +    if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos) +    { +        // At this point, we assume that the user data size is equal +        // to pUserData->dwHeaderOffs. +        // If this assumption doesn't work, then we have an unknown version of MPQ +        ByteOffset = ha->UserDataPos; +        ByteCount = ha->MpqPos - ha->UserDataPos; + +        assert(ha->pUserData != NULL); +        assert(ha->pUserData->dwHeaderOffs == ByteCount); +        nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount); +    } + +    // Write the MPQ header +    if(nError == ERROR_SUCCESS) +    { +        // Remember the header size before swapping +        DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize; + +        BSWAP_TMPQHEADER(ha->pHeader); +        if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite)) +            nError = GetLastError(); +        BSWAP_TMPQHEADER(ha->pHeader); + +        // Update the progress +        CompactBytesProcessed += ha->pHeader->dwHeaderSize; +    } + +    // Now copy all files +    if(nError == ERROR_SUCCESS) +    { +        nError = CopyMpqFiles(ha, pFileKeys, pTempStream); +        ha->dwFlags |= MPQ_FLAG_CHANGED; +    } + +    // If succeeded, switch the streams +    if(nError == ERROR_SUCCESS) +    { +        if(FileStream_Switch(ha->pStream, pTempStream)) +            pTempStream = NULL; +        else +            nError = ERROR_CAN_NOT_COMPLETE; +    } + +    // If all succeeded, save the MPQ tables +    if(nError == ERROR_SUCCESS) +    { +        // +        // Note: We don't recalculate position of the MPQ tables at this point. +        // SaveMPQTables does it automatically. +        //  + +        nError = SaveMPQTables(ha); +        if(nError == ERROR_SUCCESS && CompactCB != NULL) +        { +            CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); +            CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock)); +            CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes); +        } +    } + +    // Invalidate the compact callback +    pvUserData = NULL; +    CompactCB = NULL; + +    // Cleanup and return +    if(pTempStream != NULL) +        FileStream_Close(pTempStream); +    if(pFileKeys != NULL) +        STORM_FREE(pFileKeys); +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Changing hash table size + +DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    return ha->dwMaxFileCount; +} + +bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) +{ +    TMPQHetTable * pOldHetTable = NULL; +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pOldFileTable = NULL; +    TFileEntry * pOldFileEntry; +    TFileEntry * pFileEntry; +    TMPQHash * pOldHashTable = NULL; +    DWORD dwOldHashTableSize = 0; +    DWORD dwOldFileTableSize = 0; +    int nError = ERROR_SUCCESS; + +    // Test the valid parameters +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(ha->dwFlags & MPQ_FLAG_READ_ONLY) +        nError = ERROR_ACCESS_DENIED; + +    // The new limit must not be lower than the index of the last file entry in the table +    if(nError == ERROR_SUCCESS && ha->dwFileTableSize > dwMaxFileCount) +        nError = ERROR_DISK_FULL; + +    // ALL file names must be known in order to be able +    // to rebuild hash table size +    if(nError == ERROR_SUCCESS) +    { +        nError = CheckIfAllFilesKnown(ha, NULL, NULL); +    } + +    // If the MPQ has a hash table, then we relocate the hash table +    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) +    { +        // Save parameters for the current hash table +        dwOldHashTableSize = ha->pHeader->dwHashTableSize; +        pOldHashTable = ha->pHashTable; + +        // Allocate new hash table +        ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); +        ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize); +        if(ha->pHashTable != NULL) +            memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); +        else +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // If the MPQ has HET table, allocate new one as well +    if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) +    { +        // Save the original HET table +        pOldHetTable = ha->pHetTable; + +        // Create new one +        ha->pHetTable = CreateHetTable(dwMaxFileCount, 0x40, true); +        if(ha->pHetTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Now reallocate the file table +    if(nError == ERROR_SUCCESS) +    { +        // Save the current file table +        dwOldFileTableSize = ha->dwFileTableSize; +        pOldFileTable = ha->pFileTable; + +        // Create new one +        ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount); +        if(ha->pFileTable != NULL) +            memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry)); +        else +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Now we have to build both classic hash table and HET table. +    if(nError == ERROR_SUCCESS) +    { +        DWORD dwFileIndex = 0; +        DWORD dwHashIndex = 0; + +        // Create new hash and HET entry for each file +        pFileEntry = ha->pFileTable; +        for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++) +        { +            if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS) +            { +                // Copy the old file entry to the new one +                memcpy(pFileEntry, pOldFileEntry, sizeof(TFileEntry)); +                assert(pFileEntry->szFileName != NULL); +                 +                // Create new entry in the hash table +                if(ha->pHashTable != NULL) +                { +                    dwHashIndex = AllocateHashEntry(ha, pFileEntry); +                    if(dwHashIndex == HASH_ENTRY_FREE) +                    { +                        nError = ERROR_CAN_NOT_COMPLETE; +                        break; +                    } +                } + +                // Create new entry in the HET table, if needed +                if(ha->pHetTable != NULL) +                { +                    dwHashIndex = AllocateHetEntry(ha, pFileEntry); +                    if(dwHashIndex == HASH_ENTRY_FREE) +                    { +                        nError = ERROR_CAN_NOT_COMPLETE; +                        break; +                    } +                } + +                // Move to the next file entry in the new table +                pFileEntry++; +                dwFileIndex++; +            } +        } +    } + +    // Mark the archive as changed +    // Note: We always have to rebuild the (attributes) file due to file table change +    if(nError == ERROR_SUCCESS) +    { +        ha->dwMaxFileCount = dwMaxFileCount; +        InvalidateInternalFiles(ha); +    } +    else +    { +        // Revert the hash table +        if(ha->pHashTable != NULL && pOldHashTable != NULL) +        { +            STORM_FREE(ha->pHashTable); +            ha->pHeader->dwHashTableSize = dwOldHashTableSize; +            ha->pHashTable = pOldHashTable; +        } + +        // Revert the HET table +        if(ha->pHetTable != NULL && pOldHetTable != NULL) +        { +            FreeHetTable(ha->pHetTable); +            ha->pHetTable = pOldHetTable; +        } + +        // Revert the file table +        if(pOldFileTable != NULL) +        { +            STORM_FREE(ha->pFileTable); +            ha->pFileTable = pOldFileTable; +        } + +        SetLastError(nError); +    } + +    // Return the result +    return (nError == ERROR_SUCCESS); +} diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp new file mode 100644 index 0000000..84109d3 --- /dev/null +++ b/src/SFileCreateArchive.cpp @@ -0,0 +1,255 @@ +/*****************************************************************************/ +/* SFileCreateArchive.cpp                 Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* MPQ Editing functions                                                     */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 24.03.03  1.00  Lad  Splitted from SFileOpenArchive.cpp                   */ +/* 08.06.10  1.00  Lad  Renamed to SFileCreateArchive.cpp                    */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local variables + +static const DWORD MpqHeaderSizes[] = +{ +    MPQ_HEADER_SIZE_V1, +    MPQ_HEADER_SIZE_V2, +    MPQ_HEADER_SIZE_V3, +    MPQ_HEADER_SIZE_V4 +}; + +//----------------------------------------------------------------------------- +// Local functions + +static USHORT GetSectorSizeShift(DWORD dwSectorSize) +{ +    USHORT wSectorSizeShift = 0; + +    while(dwSectorSize > 0x200) +    { +        dwSectorSize >>= 1; +        wSectorSizeShift++; +    } + +    return wSectorSizeShift; +} + +static int WriteNakedMPQHeader(TMPQArchive * ha) +{ +    TMPQHeader * pHeader = ha->pHeader; +    TMPQHeader Header; +    DWORD dwBytesToWrite = pHeader->dwHeaderSize; +    int nError = ERROR_SUCCESS; + +    // Prepare the naked MPQ header +    memset(&Header, 0, sizeof(TMPQHeader)); +    Header.dwID           = pHeader->dwID; +    Header.dwHeaderSize   = pHeader->dwHeaderSize; +    Header.dwArchiveSize  = pHeader->dwHeaderSize; +    Header.wFormatVersion = pHeader->wFormatVersion; +    Header.wSectorSize    = pHeader->wSectorSize; + +    // Write it to the file +    BSWAP_TMPQHEADER(&Header); +    if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite)) +        nError = GetLastError(); + +    return nError; +} + +//----------------------------------------------------------------------------- +// Creates a new MPQ archive. + +bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +{ +    SFILE_CREATE_MPQ CreateInfo; + +    // Fill the create structure +    memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ)); +    CreateInfo.cbSize         = sizeof(SFILE_CREATE_MPQ); +    CreateInfo.dwMpqVersion   = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; +    CreateInfo.dwStreamFlags  = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE; +    CreateInfo.dwAttrFlags    = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0; +    CreateInfo.dwSectorSize   = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; +    CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0; +    CreateInfo.dwMaxFileCount = dwMaxFileCount; +    return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq); +} + +bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq) +{ +    TFileStream * pStream = NULL;           // File stream +    TMPQArchive * ha = NULL;                // MPQ archive handle +    ULONGLONG MpqPos = 0;                   // Position of MPQ header in the file +    HANDLE hMpq = NULL; +    DWORD dwBlockTableSize = 0;             // Initial block table size +    DWORD dwHashTableSize = 0; +    DWORD dwMaxFileCount; +    int nError = ERROR_SUCCESS; + +    // Check the parameters, if they are valid +    if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    // Verify if all variables in SFILE_CREATE_MPQ are correct +    if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) || +       (pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4)                        || +       (pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0)            || +       (pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL)                              || +       (pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1))                || +       (pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1))            || +       (pCreateInfo->dwMaxFileCount < 4)) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    // One time initialization of MPQ cryptography +    InitializeMpqCryptography(); + +    // We verify if the file already exists and if it's a MPQ archive. +    // If yes, we won't allow to overwrite it. +    if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq)) +    { +        SFileCloseArchive(hMpq); +        SetLastError(ERROR_ALREADY_EXISTS); +        return false; +    } + +    // +    // At this point, we have to create the archive. +    // - If the file exists, convert it to MPQ archive. +    // - If the file doesn't exist, create new empty file +    // + +    pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags); +    if(pStream == NULL) +    { +        pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags); +        if(pStream == NULL) +            return false; +    } + +    // Increment the maximum amount of files to have space +    // for listfile and attributes file +    dwMaxFileCount = pCreateInfo->dwMaxFileCount; +    if(pCreateInfo->dwAttrFlags != 0) +        dwMaxFileCount++; +    dwMaxFileCount++; + +    // If file count is not zero, initialize the hash table size +    dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); + +    // Retrieve the file size and round it up to 0x200 bytes +    FileStream_GetSize(pStream, &MpqPos); +    MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL; +    if(!FileStream_SetSize(pStream, MpqPos)) +        nError = GetLastError(); + +#ifdef _DEBUG     +    // Debug code, used for testing StormLib +//  dwBlockTableSize = dwHashTableSize * 2; +#endif + +    // Create the archive handle +    if(nError == ERROR_SUCCESS) +    { +        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Fill the MPQ archive handle structure +    if(nError == ERROR_SUCCESS) +    { +        memset(ha, 0, sizeof(TMPQArchive)); +        ha->pStream         = pStream; +        ha->dwSectorSize    = pCreateInfo->dwSectorSize; +        ha->UserDataPos     = MpqPos; +        ha->MpqPos          = MpqPos; +        ha->pHeader         = (TMPQHeader *)ha->HeaderData; +        ha->dwMaxFileCount  = dwMaxFileCount; +        ha->dwFileTableSize = 0; +        ha->dwFileFlags1    = pCreateInfo->dwFileFlags1; +        ha->dwFileFlags2    = pCreateInfo->dwFileFlags2; +        ha->dwFlags         = 0; + +        // Setup the attributes +        ha->dwAttrFlags     = pCreateInfo->dwAttrFlags; +        pStream = NULL; +    } + +    // Fill the MPQ header +    if(nError == ERROR_SUCCESS) +    { +        TMPQHeader * pHeader = ha->pHeader; + +        // Fill the MPQ header +        memset(pHeader, 0, sizeof(ha->HeaderData)); +        pHeader->dwID             = ID_MPQ; +        pHeader->dwHeaderSize     = MpqHeaderSizes[pCreateInfo->dwMpqVersion]; +        pHeader->dwArchiveSize    = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash); +        pHeader->wFormatVersion   = (USHORT)pCreateInfo->dwMpqVersion; +        pHeader->wSectorSize      = GetSectorSizeShift(ha->dwSectorSize); +        pHeader->dwHashTablePos   = pHeader->dwHeaderSize; +        pHeader->dwHashTableSize  = dwHashTableSize; +        pHeader->dwBlockTablePos  = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash); +        pHeader->dwBlockTableSize = dwBlockTableSize; + +        // For MPQs version 4 and higher, we set the size of raw data block +        // for calculating MD5 +        if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4) +            pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize; + +        // Write the naked MPQ header +        nError = WriteNakedMPQHeader(ha); + +        // Remember that the (listfile) and (attributes) need to be saved +        ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES; +    } + +    // Create initial HET table, if the caller required an MPQ format 3.0 or newer +    if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3) +    { +        ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true); +        if(ha->pHetTable == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Create initial hash table +    if(nError == ERROR_SUCCESS) +    { +        nError = CreateHashTable(ha, dwHashTableSize); +    } + +    // Create initial file table +    if(nError == ERROR_SUCCESS) +    { +        ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); +        if(ha->pFileTable != NULL) +            memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount); +        else +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Cleanup : If an error, delete all buffers and return +    if(nError != ERROR_SUCCESS) +    { +        FileStream_Close(pStream); +        FreeMPQArchive(ha); +        SetLastError(nError); +        ha = NULL; +    } +     +    // Return the values +    *phMpq = (HANDLE)ha; +    return (nError == ERROR_SUCCESS); +} diff --git a/src/SFileExtractFile.cpp b/src/SFileExtractFile.cpp new file mode 100644 index 0000000..c8053ed --- /dev/null +++ b/src/SFileExtractFile.cpp @@ -0,0 +1,67 @@ +/*****************************************************************************/ +/* SFileExtractFile.cpp                   Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Simple extracting utility                                                 */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 20.06.03  1.00  Lad  The first version of SFileExtractFile.cpp            */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope) +{ +    TFileStream * pLocalFile = NULL; +    HANDLE hMpqFile = NULL; +    int nError = ERROR_SUCCESS; + +    // Open the MPQ file +    if(nError == ERROR_SUCCESS) +    { +        if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile)) +            nError = GetLastError(); +    } + +    // Create the local file +    if(nError == ERROR_SUCCESS) +    { +        pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +        if(pLocalFile == NULL) +            nError = GetLastError(); +    } + +    // Copy the file's content +    if(nError == ERROR_SUCCESS) +    { +        char  szBuffer[0x1000]; +        DWORD dwTransferred; + +        for(;;) +        { +            // dwTransferred is only set to nonzero if something has been read. +            // nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF +            if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL)) +                nError = GetLastError(); +            if(nError == ERROR_HANDLE_EOF) +                nError = ERROR_SUCCESS; +            if(dwTransferred == 0) +                break; + +            // If something has been actually read, write it +            if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred)) +                nError = GetLastError(); +        } +    } + +    // Close the files +    if(hMpqFile != NULL) +        SFileCloseFile(hMpqFile); +    if(pLocalFile != NULL) +        FileStream_Close(pLocalFile); +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} diff --git a/src/SFileFindFile.cpp b/src/SFileFindFile.cpp new file mode 100644 index 0000000..80aa6e1 --- /dev/null +++ b/src/SFileFindFile.cpp @@ -0,0 +1,446 @@ +/*****************************************************************************/ +/* SFileFindFile.cpp                      Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* A module for file searching within MPQs                                   */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 25.03.03  1.00  Lad  The first version of SFileFindFile.cpp               */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Defines + +#define LISTFILE_CACHE_SIZE 0x1000 + +//----------------------------------------------------------------------------- +// Private structure used for file search (search handle) + +struct TMPQSearch; +typedef int (*MPQSEARCH)(TMPQSearch *, SFILE_FIND_DATA *); + +// Used by searching in MPQ archives +struct TMPQSearch +{ +    TMPQArchive * ha;                   // Handle to MPQ, where the search runs +    TFileEntry ** pSearchTable;         // Table for files that have been already found +    DWORD  dwSearchTableItems;          // Number of items in the search table +    DWORD  dwNextIndex;                 // Next file index to be checked +    DWORD  dwFlagMask;                  // For checking flag mask +    char   szSearchMask[1];             // Search mask (variable length) +}; + +//----------------------------------------------------------------------------- +// Local functions + +static bool IsValidSearchHandle(TMPQSearch * hs) +{ +    if(hs == NULL) +        return false; + +    return IsValidMpqHandle(hs->ha); +} + +bool CheckWildCard(const char * szString, const char * szWildCard) +{ +    const char * szSubString; +    int nSubStringLength; +    int nMatchCount = 0; + +    // When the mask is empty, it never matches +    if(szWildCard == NULL || *szWildCard == 0) +        return false; + +    // If the wildcard contains just "*", then it always matches +    if(szWildCard[0] == '*' && szWildCard[1] == 0) +        return true; + +    // Do normal test +    for(;;) +    { +        // If there is '?' in the wildcard, we skip one char +        while(*szWildCard == '?') +        { +            szWildCard++; +            szString++; +        } + +        // If there is '*', means zero or more chars. We have to  +        // find the sequence after '*' +        if(*szWildCard == '*') +        { +            // More stars is equal to one star +            while(*szWildCard == '*' || *szWildCard == '?') +                szWildCard++; + +            // If we found end of the wildcard, it's a match +            if(*szWildCard == 0) +                return true; + +            // Determine the length of the substring in szWildCard +            szSubString = szWildCard; +            while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*') +                szSubString++; +            nSubStringLength = (int)(szSubString - szWildCard); +            nMatchCount = 0; + +            // Now we have to find a substring in szString, +            // that matches the substring in szWildCard +            while(*szString != 0) +            { +                // Calculate match count +                while(nMatchCount < nSubStringLength) +                { +                    if(AsciiToUpperTable[(BYTE)szString[nMatchCount]] != AsciiToUpperTable[(BYTE)szWildCard[nMatchCount]]) +                        break; +                    if(szString[nMatchCount] == 0) +                        break; +                    nMatchCount++; +                } + +                // If the match count has reached substring length, we found a match +                if(nMatchCount == nSubStringLength) +                { +                    szWildCard += nMatchCount; +                    szString += nMatchCount; +                    break; +                } + +                // No match, move to the next char in szString +                nMatchCount = 0; +                szString++; +            } +        } +        else +        { +            // If we came to the end of the string, compare it to the wildcard +            if(AsciiToUpperTable[(BYTE)*szString] != AsciiToUpperTable[(BYTE)*szWildCard]) +                return false; + +            // If we arrived to the end of the string, it's a match +            if(*szString == 0) +                return true; + +            // Otherwise, continue in comparing +            szWildCard++; +            szString++; +        } +    } +} + +static DWORD GetSearchTableItems(TMPQArchive * ha) +{ +    DWORD dwMergeItems = 0; + +    // Loop over all patches +    while(ha != NULL) +    { +        // Append the number of files +        dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount +                                                : ha->pHeader->dwBlockTableSize; +        // Move to the patched archive +        ha = ha->haPatch; +    } + +    // Return the double size of number of items +    return (dwMergeItems | 1); +} + +static bool FileWasFoundBefore( +    TMPQArchive * ha, +    TMPQSearch * hs, +    TFileEntry * pFileEntry) +{ +    TFileEntry * pEntry; +    char * szRealFileName = pFileEntry->szFileName; +    DWORD dwStartIndex; +    DWORD dwNameHash; +    DWORD dwIndex; + +    if(hs->pSearchTable != NULL && szRealFileName != NULL) +    { +        // If we are in patch MPQ, we check if patch prefix matches +        // and then trim the patch prefix +        if(ha->cchPatchPrefix != 0) +        { +            // If the patch prefix doesn't fit, we pretend that the file +            // was there before and it will be skipped +            if(_strnicmp(szRealFileName, ha->szPatchPrefix, ha->cchPatchPrefix)) +                return true; + +            szRealFileName += ha->cchPatchPrefix; +        } + +        // Calculate the hash to the table +        dwNameHash = HashString(szRealFileName, MPQ_HASH_NAME_A); +        dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems); + +        // The file might have been found before +        // only if this is not the first MPQ being searched +        if(ha->haBase != NULL) +        { +            // Enumerate all entries in the search table +            for(;;) +            { +                // Get the file entry at that position +                pEntry = hs->pSearchTable[dwIndex]; +                if(pEntry == NULL) +                    break; + +                if(pEntry->szFileName != NULL) +                { +                    // Does the name match? +                    if(!_stricmp(pEntry->szFileName, szRealFileName)) +                        return true; +                } + +                // Move to the next entry +                dwIndex = (dwIndex + 1) % hs->dwSearchTableItems; +                if(dwIndex == dwStartIndex) +                    break; +            } +        } + +        // Put the entry to the table for later use +        hs->pSearchTable[dwIndex] = pFileEntry; +    } +    return false; +} + +static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry) +{ +    TFileEntry * pPatchEntry = NULL; +    TFileEntry * pTempEntry; +    char szFileName[MAX_PATH]; +    LCID lcLocale = pFileEntry->lcLocale; + +    // Go while there are patches +    while(ha->haPatch != NULL) +    { +        // Move to the patch archive +        ha = ha->haPatch; + +        // Prepare the prefix for the file name +        strcpy(szFileName, ha->szPatchPrefix); +        strcat(szFileName, pFileEntry->szFileName); + +        // Try to find the file there +        pTempEntry = GetFileEntryExact(ha, szFileName, lcLocale); +        if(pTempEntry != NULL) +            pPatchEntry = pTempEntry; +    } + +    // Return the found patch entry +    return pPatchEntry; +} + +// Performs one MPQ search +static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) +{ +    TMPQArchive * ha = hs->ha; +    TFileEntry * pFileTableEnd; +    TFileEntry * pPatchEntry; +    TFileEntry * pFileEntry; +    const char * szFileName; +    HANDLE hFile; +    char szPseudoName[20]; +    DWORD dwBlockIndex; +    size_t nPrefixLength; + +    // Start searching with base MPQ +    while(ha != NULL) +    { +        // Now parse the file entry table in order to get all files. +        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +        pFileEntry = ha->pFileTable + hs->dwNextIndex; + +        // Get the length of the patch prefix (0 if none) +        nPrefixLength = strlen(ha->szPatchPrefix); + +        // Parse the file table +        while(pFileEntry < pFileTableEnd) +        { +            // Increment the next index for subsequent search +            hs->dwNextIndex++; + +            // Is it a file and not a patch file? +            if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) +            { +                // Now we have to check if this file was not enumerated before +                if(!FileWasFoundBefore(ha, hs, pFileEntry)) +                { +                    // Find a patch to this file +                    pPatchEntry = FindPatchEntry(ha, pFileEntry); +                    if(pPatchEntry == NULL) +                        pPatchEntry = pFileEntry; + +                    // Prepare the block index +                    dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); + +                    // Get the file name. If it's not known, we will create pseudo-name +                    szFileName = pFileEntry->szFileName; +                    if(szFileName == NULL) +                    { +                        // Open the file by its pseudo-name. +                        // This also generates the file name with a proper extension +                        sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex); +                        if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile)) +                        { +                            szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName; +                            SFileCloseFile(hFile); +                        } +                    } + +                    // Check the file name against the wildcard +                    if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) +                    { +                        // Fill the found entry +                        lpFindFileData->dwHashIndex  = pPatchEntry->dwHashIndex; +                        lpFindFileData->dwBlockIndex = dwBlockIndex; +                        lpFindFileData->dwFileSize   = pPatchEntry->dwFileSize; +                        lpFindFileData->dwFileFlags  = pPatchEntry->dwFlags; +                        lpFindFileData->dwCompSize   = pPatchEntry->dwCmpSize; +                        lpFindFileData->lcLocale     = pPatchEntry->lcLocale; + +                        // Fill the filetime +                        lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32); +                        lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime); + +                        // Fill the file name and plain file name +                        strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength); +                        lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName); +                        return ERROR_SUCCESS; +                    } + +                } +            } + +            pFileEntry++; +        } + +        // Move to the next patch in the patch chain +        hs->ha = ha = ha->haPatch; +        hs->dwNextIndex = 0; +    } + +    // No more files found, return error +    return ERROR_NO_MORE_FILES; +} + +static void FreeMPQSearch(TMPQSearch *& hs) +{ +    if(hs != NULL) +    { +        if(hs->pSearchTable != NULL) +            STORM_FREE(hs->pSearchTable); +        STORM_FREE(hs); +        hs = NULL; +    } +} + +//----------------------------------------------------------------------------- +// Public functions + +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TMPQSearch * hs = NULL; +    size_t nSize  = 0; +    int nError = ERROR_SUCCESS; + +    // Check for the valid parameters +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(szMask == NULL || lpFindFileData == NULL) +        nError = ERROR_INVALID_PARAMETER; + +    // Include the listfile into the MPQ's internal listfile +    // Note that if the listfile name is NULL, do nothing because the +    // internal listfile is always included. +    if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0) +        nError = SFileAddListFile((HANDLE)ha, szListFile); + +    // Allocate the structure for MPQ search +    if(nError == ERROR_SUCCESS) +    { +        nSize = sizeof(TMPQSearch) + strlen(szMask) + 1; +        if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Perform the first search +    if(nError == ERROR_SUCCESS) +    { +        memset(hs, 0, sizeof(TMPQSearch)); +        strcpy(hs->szSearchMask, szMask); +        hs->dwFlagMask = MPQ_FILE_EXISTS; +        hs->ha = ha; + +        // If the archive is patched archive, we have to create a merge table +        // to prevent files being repeated +        if(ha->haPatch != NULL) +        { +            hs->dwSearchTableItems = GetSearchTableItems(ha); +            hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems); +            hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; +            if(hs->pSearchTable != NULL) +                memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *)); +            else +                nError = ERROR_NOT_ENOUGH_MEMORY; +        } +    } + +    // Perform first item searching +    if(nError == ERROR_SUCCESS) +    { +        nError = DoMPQSearch(hs, lpFindFileData); +    } + +    // Cleanup +    if(nError != ERROR_SUCCESS) +    { +        FreeMPQSearch(hs); +        SetLastError(nError); +    } +     +    // Return the result value +    return (HANDLE)hs; +} + +bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) +{ +    TMPQSearch * hs = (TMPQSearch *)hFind; +    int nError = ERROR_SUCCESS; + +    // Check the parameters +    if(!IsValidSearchHandle(hs)) +        nError = ERROR_INVALID_HANDLE; +    if(lpFindFileData == NULL) +        nError = ERROR_INVALID_PARAMETER; + +    if(nError == ERROR_SUCCESS) +        nError = DoMPQSearch(hs, lpFindFileData); + +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +bool WINAPI SFileFindClose(HANDLE hFind) +{ +    TMPQSearch * hs = (TMPQSearch *)hFind; + +    // Check the parameters +    if(!IsValidSearchHandle(hs)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    FreeMPQSearch(hs); +    return true; +} diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp new file mode 100644 index 0000000..2293403 --- /dev/null +++ b/src/SFileListFile.cpp @@ -0,0 +1,636 @@ +/*****************************************************************************/ +/* SListFile.cpp                          Copyright (c) Ladislav Zezula 2004 */ +/*---------------------------------------------------------------------------*/ +/* Description:                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 12.06.04  1.00  Lad  The first version of SListFile.cpp                   */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" +#include <assert.h> + +//----------------------------------------------------------------------------- +// Listfile entry structure + +#define CACHE_BUFFER_SIZE  0x1000       // Size of the cache buffer + +struct TListFileCache +{ +    HANDLE  hFile;                      // Stormlib file handle +    char  * szMask;                     // File mask +    DWORD   dwFileSize;                 // Total size of the cached file +    DWORD   dwFilePos;                  // Position of the cache in the file +    BYTE  * pBegin;                     // The begin of the listfile cache +    BYTE  * pPos; +    BYTE  * pEnd;                       // The last character in the file cache + +    BYTE Buffer[CACHE_BUFFER_SIZE];     // Listfile cache itself +}; + +//----------------------------------------------------------------------------- +// Local functions (cache) + +static bool FreeListFileCache(TListFileCache * pCache) +{ +    // Valid parameter check +    if(pCache == NULL) +        return false; + +    // Free all allocated buffers +    if(pCache->hFile != NULL) +        SFileCloseFile(pCache->hFile); +    if(pCache->szMask != NULL) +        STORM_FREE(pCache->szMask); +    STORM_FREE(pCache); +    return true; +} + +static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask) +{ +    TListFileCache * pCache = NULL; +    DWORD dwBytesRead = 0; +    int nError = ERROR_SUCCESS; + +    // Allocate cache for one file block +    pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1); +    if(pCache == NULL) +        nError = ERROR_NOT_ENOUGH_MEMORY; + +    // Clear the entire structure +    if(nError == ERROR_SUCCESS) +    { +        memset(pCache, 0, sizeof(TListFileCache)); +        pCache->hFile = hListFile; + +        // Shall we allocate a mask? +        if(szMask != NULL) +        { +            pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1); +            if(pCache->szMask != NULL) +                strcpy(pCache->szMask, szMask); +            else +                nError = ERROR_NOT_ENOUGH_MEMORY; +        } +    } + +    // Initialize the file cache +    if(nError == ERROR_SUCCESS) +    { +        pCache->dwFileSize = SFileGetFileSize(pCache->hFile, NULL); + +        // Fill the cache +        SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); +        if(dwBytesRead == 0) +            nError = GetLastError(); +    } + +    // Allocate pointers +    if(nError == ERROR_SUCCESS) +    { +        pCache->pBegin = +        pCache->pPos = &pCache->Buffer[0]; +        pCache->pEnd = pCache->pBegin + dwBytesRead; +    } +    else +    { +        FreeListFileCache(pCache); +        SetLastError(nError); +        pCache = NULL; +    } + +    // Return the cache +    return pCache; +} + +// Reloads the cache. Returns number of characters +// that has been loaded into the cache. +static DWORD ReloadListFileCache(TListFileCache * pCache) +{ +    DWORD dwBytesToRead; +    DWORD dwBytesRead = 0; + +    // Only do something if the cache is empty +    if(pCache->pPos >= pCache->pEnd) +    { +//      __TryReadBlock: + +        // Move the file position forward +        pCache->dwFilePos += CACHE_BUFFER_SIZE; +        if(pCache->dwFilePos >= pCache->dwFileSize) +            return 0; + +        // Get the number of bytes remaining +        dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos; +        if(dwBytesToRead > CACHE_BUFFER_SIZE) +            dwBytesToRead = CACHE_BUFFER_SIZE; + +        // Load the next data chunk to the cache +        SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN); +        SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); + +        // If we didn't read anything, it might mean that the block +        // of the file is not available (in case of partial MPQs). +        // We stop reading the file at this point, because the rest +        // of the listfile is unreliable +        if(dwBytesRead == 0) +            return 0; + +        // Set the buffer pointers +        pCache->pBegin = +        pCache->pPos = &pCache->Buffer[0]; +        pCache->pEnd = pCache->pBegin + dwBytesRead; +    } + +    return dwBytesRead; +} + +static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxChars) +{ +    char * szLineBegin = szLine; +    char * szLineEnd = szLine + nMaxChars - 1; +    char * szExtraString = NULL; +     +    // Skip newlines, spaces, tabs and another non-printable stuff +    for(;;) +    { +        // If we need to reload the cache, do it +        if(pCache->pPos == pCache->pEnd) +        { +            if(ReloadListFileCache(pCache) == 0) +                break; +        } + +        // If we found a non-whitespace character, stop +        if(*pCache->pPos > 0x20) +            break; + +        // Skip the character +        pCache->pPos++; +    } + +    // Copy the remaining characters +    while(szLine < szLineEnd) +    { +        // If we need to reload the cache, do it now and resume copying +        if(pCache->pPos == pCache->pEnd) +        { +            if(ReloadListFileCache(pCache) == 0) +                break; +        } + +        // If we have found a newline, stop loading +        if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A) +            break; + +        // Blizzard listfiles can also contain information about patch: +        // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326) +        if(*pCache->pPos == '~') +            szExtraString = szLine; + +        // Copy the character +        *szLine++ = *pCache->pPos++; +    } + +    // Terminate line with zero +    *szLine = 0; + +    // If there was extra string after the file name, clear it +    if(szExtraString != NULL) +    { +        if(szExtraString[0] == '~' && szExtraString[1] == 'P') +        { +            szLine = szExtraString; +            *szExtraString = 0; +        } +    } + +    // Return the length of the line +    return (szLine - szLineBegin); +} + +static int CompareFileNodes(const void * p1, const void * p2)  +{ +    char * szFileName1 = *(char **)p1; +    char * szFileName2 = *(char **)p2; + +    return _stricmp(szFileName1, szFileName2); +} + +static int WriteListFileLine( +    TMPQFile * hf, +    const char * szLine) +{ +    char szNewLine[2] = {0x0D, 0x0A}; +    size_t nLength = strlen(szLine); +    int nError; + +    nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB); +    if(nError != ERROR_SUCCESS) +        return nError; + +    return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB); +} + +//----------------------------------------------------------------------------- +// Local functions (listfile nodes) + +// Adds a name into the list of all names. For each locale in the MPQ, +// one entry will be created +// If the file name is already there, does nothing. +static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) +{ +    TMPQHeader * pHeader = ha->pHeader; +    TFileEntry * pFileEntry; +    TMPQHash * pFirstHash; +    TMPQHash * pHash; +    bool bNameEntryCreated = false; + +    // If we have HET table, use that one +    if(ha->pHetTable != NULL) +    { +        pFileEntry = GetFileEntryAny(ha, szFileName); +        if(pFileEntry != NULL) +        { +            // Allocate file name for the file entry +            AllocateFileName(pFileEntry, szFileName); +            bNameEntryCreated = true; +        } + +        return ERROR_SUCCESS; +    } + +    // If we have hash table, we use it +    if(bNameEntryCreated == false && ha->pHashTable != NULL) +    { +        // Look for the first hash table entry for the file +        pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); + +        // Go while we found something +        while(pHash != NULL) +        { +            // Is it a valid file table index ? +            if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) +            { +                // Allocate file name for the file entry +                AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName); +                bNameEntryCreated = true; +            } + +            // Now find the next language version of the file +            pHash = GetNextHashEntry(ha, pFirstHash, pHash); +        } +    } + +    return ERROR_CAN_NOT_COMPLETE; +} + +// Saves the whole listfile into the MPQ. +int SListFileSaveToMpq(TMPQArchive * ha) +{ +    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +    TFileEntry * pFileEntry; +    TMPQFile * hf = NULL; +    char * szPrevItem; +    char ** SortTable = NULL; +    DWORD dwFileSize = 0; +    size_t nFileNodes = 0; +    size_t i; +    int nError = ERROR_SUCCESS; + +    // Allocate the table for sorting listfile +    SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); +    if(SortTable == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Construct the sort table +    // Note: in MPQs with multiple locale versions of the same file, +    // this code causes adding multiple listfile entries. +    // Since those MPQs were last time used in Starcraft, +    // we leave it as it is. +    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +    { +        // Only take existing items +        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) +        { +            // Ignore pseudo-names +            if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName)) +            { +                SortTable[nFileNodes++] = pFileEntry->szFileName; +            } +        } +    } + +    // Sort the table +    qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes); + +    // Now parse the table of file names again - remove duplicates +    // and count file size. +    if(nFileNodes != 0) +    { +        // Count the 0-th item +        dwFileSize += (DWORD)strlen(SortTable[0]) + 2; +        szPrevItem = SortTable[0]; +         +        // Count all next items +        for(i = 1; i < nFileNodes; i++) +        { +            // If the item is the same like the last one, skip it +            if(_stricmp(SortTable[i], szPrevItem)) +            { +                dwFileSize += (DWORD)strlen(SortTable[i]) + 2; +                szPrevItem = SortTable[i]; +            } +        } + +        // Determine the flags for (listfile) +        if(ha->dwFileFlags1 == 0) +            ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize); + +        // Create the listfile in the MPQ +        nError = SFileAddFile_Init(ha, LISTFILE_NAME, +                                       0, +                                       dwFileSize, +                                       LANG_NEUTRAL, +                                       ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, +                                      &hf); +        // Add all file names +        if(nError == ERROR_SUCCESS) +        { +            // Each name is followed by newline ("\x0D\x0A") +            szPrevItem = SortTable[0]; +            nError = WriteListFileLine(hf, SortTable[0]); + +            // Count all next items +            for(i = 1; i < nFileNodes; i++) +            { +                // If the item is the same like the last one, skip it +                if(_stricmp(SortTable[i], szPrevItem)) +                { +                    WriteListFileLine(hf, SortTable[i]); +                    szPrevItem = SortTable[i]; +                } +            } +        } +    } +    else +    { +        // Create the listfile in the MPQ +        dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2; +        nError = SFileAddFile_Init(ha, LISTFILE_NAME, +                                       0, +                                       dwFileSize, +                                       LANG_NEUTRAL, +                                       MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, +                                      &hf); + +        // Just add "(listfile)" there +        if(nError == ERROR_SUCCESS) +        { +            WriteListFileLine(hf, LISTFILE_NAME); +        } +    } + +    // Finalize the file in the MPQ +    if(hf != NULL) +    { +        SFileAddFile_Finish(hf); +    } +     +    // Free buffers +    if(nError == ERROR_SUCCESS) +        ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE; +    if(SortTable != NULL) +        STORM_FREE(SortTable); +    return nError; +} + +static int SFileAddArbitraryListFile( +    TMPQArchive * ha, +    HANDLE hListFile) +{ +    TListFileCache * pCache = NULL; +    size_t nLength; +    char szFileName[MAX_PATH]; +    int nError = ERROR_SUCCESS; + +    // Create the listfile cache for that file +    pCache = CreateListFileCache(hListFile, NULL); +    if(pCache == NULL) +        nError = GetLastError(); + +    // Load the node list. Add the node for every locale in the archive +    if(nError == ERROR_SUCCESS) +    { +        while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0) +            SListFileCreateNodeForAllLocales(ha, szFileName); +        pCache->hFile = NULL; +    } + +    // Delete the cache +    if(pCache != NULL) +        FreeListFileCache(pCache); +    return nError; +} + +static int SFileAddExternalListFile( +    TMPQArchive * ha, +    HANDLE hMpq, +    const char * szListFile) +{ +    HANDLE hListFile; +    int nError = ERROR_SUCCESS; + +    // Open the external list file +    if(SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile)) +    { +        // Add the data from the listfile to MPQ +        nError = SFileAddArbitraryListFile(ha, hListFile); +        SFileCloseFile(hListFile); +    } +    return nError; +} + +static int SFileAddInternalListFile( +    TMPQArchive * ha, +    HANDLE hMpq) +{ +    TMPQArchive * haMpq = (TMPQArchive *)hMpq; +    TMPQHash * pFirstHash; +    TMPQHash * pHash; +    HANDLE hListFile; +    LCID lcSaveLocale = lcFileLocale; +    int nError = ERROR_SUCCESS; + +    // If there is hash table, we need to support multiple listfiles +    // with different locales (BrooDat.mpq) +    if(haMpq->pHashTable != NULL) +    { +        pFirstHash = pHash = GetFirstHashEntry(haMpq, LISTFILE_NAME); +        while(nError == ERROR_SUCCESS && pHash != NULL) +        { +            // Set the prefered locale to that from list file +            SFileSetLocale(pHash->lcLocale); +            if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile)) +            { +                // Add the data from the listfile to MPQ +                nError = SFileAddArbitraryListFile(ha, hListFile); +                SFileCloseFile(hListFile); +            } +             +            // Restore the original locale +            SFileSetLocale(lcSaveLocale); + +            // Move to the next hash +            pHash = GetNextHashEntry(haMpq, pFirstHash, pHash); +        } +    } +    else +    { +        // Open the external list file +        if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile)) +        { +            // Add the data from the listfile to MPQ +            // The function also closes the listfile handle +            nError = SFileAddArbitraryListFile(ha, hListFile); +            SFileCloseFile(hListFile); +        } +    } + +    // Return the result of the operation +    return nError; +} + +//----------------------------------------------------------------------------- +// File functions + +// Adds a listfile into the MPQ archive. +int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    int nError = ERROR_SUCCESS; + +    // Add the listfile for each MPQ in the patch chain +    while(ha != NULL) +    { +        if(szListFile != NULL) +            SFileAddExternalListFile(ha, hMpq, szListFile); +        else +            SFileAddInternalListFile(ha, hMpq); + +        // Also, add three special files to the listfile: +        // (listfile) itself, (attributes) and (signature) +        SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME); +        SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME); +        SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME); + +        // Move to the next archive in the chain +        ha = ha->haPatch; +    } + +    return nError; +} + +//----------------------------------------------------------------------------- +// Enumerating files in listfile + +HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) +{ +    TListFileCache * pCache = NULL; +    HANDLE hListFile; +    size_t nLength = 0; +    DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; +    int nError = ERROR_SUCCESS; + +    // Initialize the structure with zeros +    memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); + +    // If the szListFile is NULL, it means we have to open internal listfile +    if(szListFile == NULL) +    { +        // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load +        // the listfile even if there is only non-neutral version of the listfile in the MPQ +        dwSearchScope = SFILE_OPEN_ANY_LOCALE; +        szListFile = LISTFILE_NAME; +    } + +    // Open the local/internal listfile +    if(!SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile)) +        nError = GetLastError(); + +    // Load the listfile to cache +    if(nError == ERROR_SUCCESS) +    { +        pCache = CreateListFileCache(hListFile, szMask); +        if(pCache == NULL) +            nError = GetLastError(); +    } + +    // Perform file search +    if(nError == ERROR_SUCCESS) +    { +        for(;;) +        { +            // Read the (next) line +            nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); +            if(nLength == 0) +            { +                nError = ERROR_NO_MORE_FILES; +                break; +            } + +            // If some mask entered, check it +            if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) +                break;                 +        } +    } + +    // Cleanup & exit +    if(nError != ERROR_SUCCESS) +    { +        memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); +        FreeListFileCache(pCache); +        SetLastError(nError); +        pCache = NULL; +    } +    return (HANDLE)pCache; +} + +bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) +{ +    TListFileCache * pCache = (TListFileCache *)hFind; +    size_t nLength; +    bool bResult = false; +    int nError = ERROR_SUCCESS; + +    for(;;) +    { +        // Read the (next) line +        nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); +        if(nLength == 0) +        { +            nError = ERROR_NO_MORE_FILES; +            break; +        } + +        // If some mask entered, check it +        if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) +        { +            bResult = true; +            break; +        } +    } + +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return bResult; +} + +bool WINAPI SListFileFindClose(HANDLE hFind) +{ +    return FreeListFileCache((TListFileCache *)hFind); +} + diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp new file mode 100644 index 0000000..c385019 --- /dev/null +++ b/src/SFileOpenArchive.cpp @@ -0,0 +1,480 @@ +/*****************************************************************************/ +/* SFileOpenArchive.cpp                       Copyright Ladislav Zezula 1999 */ +/*                                                                           */ +/* Author : Ladislav Zezula                                                  */ +/* E-mail : ladik@zezula.net                                                 */ +/* WWW    : www.zezula.net                                                   */ +/*---------------------------------------------------------------------------*/ +/*                       Archive functions of Storm.dll                      */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of SFileOpenArchive.cpp            */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +/*****************************************************************************/ +/* Local functions                                                           */ +/*****************************************************************************/ + +static bool IsAviFile(void * pvFileBegin) +{ +    LPDWORD AviHeader = (DWORD *)pvFileBegin; +    DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]); +    DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]); +    DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]); + +    // Test for 'RIFF', 'AVI ' or 'LIST' +    return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C); +} + +static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete) +{ +    TFileBitmap * pBitmap; +    size_t nLength; + +    // Calculate the length of the bitmap in blocks and in bytes +    nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1); +    nLength = (size_t)(((nLength - 1) / 8) + 1); + +    // Allocate the file bitmap +    pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength); +    if(pBitmap != NULL) +    { +        // Fill the structure +        pBitmap->StartOffset = ha->MpqPos; +        pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64; +        pBitmap->IsComplete = bFileIsComplete ? 1 : 0; +        pBitmap->BitmapSize = (DWORD)nLength; +        pBitmap->BlockSize = pMpqBitmap->dwBlockSize; +        pBitmap->Reserved = 0; + +        // Copy the file bitmap +        memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength); +    } + +    return pBitmap; +} + +// This function gets the right positions of the hash table and the block table. +static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) +{ +    TMPQHeader * pHeader = ha->pHeader; +    ULONGLONG ByteOffset; + +    // Check the begin of HET table +    if(pHeader->HetTablePos64) +    { +        ByteOffset = ha->MpqPos + pHeader->HetTablePos64; +        if(ByteOffset > FileSize) +            return ERROR_BAD_FORMAT; +    } + +    // Check the begin of BET table +    if(pHeader->BetTablePos64) +    { +        ByteOffset = ha->MpqPos + pHeader->BetTablePos64; +        if(ByteOffset > FileSize) +            return ERROR_BAD_FORMAT; +    } + +    // Check the begin of hash table +    if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) +    { +        ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); +        if(ByteOffset > FileSize) +            return ERROR_BAD_FORMAT; +    } + +    // Check the begin of block table +    if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos) +    { +        ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); +        if(ByteOffset > FileSize) +            return ERROR_BAD_FORMAT; +    } + +    // Check the begin of hi-block table +    if(pHeader->HiBlockTablePos64 != 0) +    { +        ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; +        if(ByteOffset > FileSize) +            return ERROR_BAD_FORMAT; +    } + +    // All OK. +    return ERROR_SUCCESS; +} + + +/*****************************************************************************/ +/* Public functions                                                          */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// SFileGetLocale and SFileSetLocale +// Set the locale for all newly opened files + +LCID WINAPI SFileGetLocale() +{ +    return lcFileLocale; +} + +LCID WINAPI SFileSetLocale(LCID lcNewLocale) +{ +    lcFileLocale = lcNewLocale; +    return lcFileLocale; +} + +//----------------------------------------------------------------------------- +// SFileOpenArchive +// +//   szFileName - MPQ archive file name to open +//   dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives +//   dwFlags    - See MPQ_OPEN_XXX in StormLib.h +//   phMpq      - Pointer to store open archive handle + +bool WINAPI SFileOpenArchive( +    const TCHAR * szMpqName, +    DWORD dwPriority, +    DWORD dwFlags, +    HANDLE * phMpq) +{ +    TFileStream * pStream = NULL;       // Open file stream +    TMPQArchive * ha = NULL;            // Archive handle +    TFileEntry * pFileEntry; +    ULONGLONG FileSize = 0;             // Size of the file +    int nError = ERROR_SUCCESS;    + +    // Verify the parameters +    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL) +        nError = ERROR_INVALID_PARAMETER; + +    // One time initialization of MPQ cryptography +    InitializeMpqCryptography(); +    dwPriority = dwPriority; + +    // Open the MPQ archive file +    if(nError == ERROR_SUCCESS) +    { +        // Initialize the stream +        pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK)); +        if(pStream == NULL) +            nError = GetLastError(); +    } +     +    // Allocate the MPQhandle +    if(nError == ERROR_SUCCESS) +    { +        FileStream_GetSize(pStream, &FileSize); +        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Initialize handle structure and allocate structure for MPQ header +    if(nError == ERROR_SUCCESS) +    { +        memset(ha, 0, sizeof(TMPQArchive)); +        ha->pStream = pStream; +        pStream = NULL; + +        // Remember if the archive is open for write +        if(FileStream_IsReadOnly(ha->pStream)) +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; + +        // Also remember if we shall check sector CRCs when reading file +        if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) +            ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC; +    } + +    // Find the offset of MPQ header within the file +    if(nError == ERROR_SUCCESS) +    { +        ULONGLONG SearchPos = 0; +        DWORD dwHeaderID; + +        while(SearchPos < FileSize) +        { +            DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4; + +            // Cut the bytes available, if needed +            if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4) +                dwBytesAvailable = (DWORD)(FileSize - SearchPos); + +            // Read the eventual MPQ header +            if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable)) +            { +                nError = GetLastError(); +                break; +            } + +            // There are AVI files from Warcraft III with 'MPQ' extension. +            if(SearchPos == 0 && IsAviFile(ha->HeaderData)) +            { +                nError = ERROR_AVI_FILE; +                break; +            } + +            // If there is the MPQ user data signature, process it +            dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData); +            if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL) +            { +                // Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0 +                if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0) +                { +                    // Fill the user data header +                    ha->pUserData = &ha->UserData; +                    memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData)); +                    BSWAP_TMPQUSERDATA(ha->pUserData); + +                    // Remember the position of the user data and continue search +                    ha->UserDataPos = SearchPos; +                    SearchPos += ha->pUserData->dwHeaderOffs; +                    continue; +                } +            } + +            // There must be MPQ header signature +            if(dwHeaderID == ID_MPQ) +            { +                // Save the position where the MPQ header has been found +                if(ha->pUserData == NULL) +                    ha->UserDataPos = SearchPos; +                ha->pHeader = (TMPQHeader *)ha->HeaderData; +                ha->MpqPos = SearchPos; + +                // Now convert the header to version 4 +                BSWAP_TMPQHEADER(ha->pHeader); +                nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags); +                break; +            } + +            // Move to the next possible offset +            SearchPos += 0x200; +        } + +        // If we haven't found MPQ header in the file, it's an error +        if(ha->pHeader == NULL) +            nError = ERROR_BAD_FORMAT; +    } + +    // Fix table positions according to format +    if(nError == ERROR_SUCCESS) +    { +        // Dump the header +//      DumpMpqHeader(ha->pHeader); + +        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data, +        // and probably ignores the MPQ format version as well. The trick is to +        // fake MPQ format 2, with an improper hi-word position of hash table and block table +        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0 +        if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) +        { +            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; +            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; +            ha->pUserData = NULL; +        } + +        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode +        if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; + +        // Set the size of file sector +        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize); + +        // Verify if any of the tables doesn't start beyond the end of the file +        nError = VerifyMpqTablePositions(ha, FileSize); +    } + +    // Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete +    if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4) +    { +        TFileBitmap * pBitmap; +        bool bFileIsComplete = true; + +        LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete); +        if(ha->pBitmap != NULL && bFileIsComplete == false) +        { +            // Convert the MPQ bitmap to the file bitmap +            pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete); + +            // Set the data bitmap into the file stream for additional checks +            FileStream_SetBitmap(ha->pStream, pBitmap); +            ha->dwFlags |= MPQ_FLAG_READ_ONLY; +        } +    } + +    // Read the hash table. Ignore the result, as hash table is no longer required +    // Read HET table. Ignore the result, as HET table is no longer required +    if(nError == ERROR_SUCCESS) +    { +        nError = LoadAnyHashTable(ha); +    } + +    // Now, build the file table. It will be built by combining +    // the block table, BET table, hi-block table, (attributes) and (listfile). +    if(nError == ERROR_SUCCESS) +    { +        nError = BuildFileTable(ha, FileSize); +    } + +    // Verify the file table, if no kind of protection was detected +    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0) +    { +        TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize; +//      ULONGLONG ArchiveSize = 0; +        ULONGLONG RawFilePos; + +        // Parse all file entries +        for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +        { +            // If that file entry is valid, check the file position +            if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) +            { +                // Get the 64-bit file position, +                // relative to the begin of the file +                RawFilePos = ha->MpqPos + pFileEntry->ByteOffset; + +                // Begin of the file must be within range +                if(RawFilePos > FileSize) +                { +                    nError = ERROR_FILE_CORRUPT; +                    break; +                } + +                // End of the file must be within range +                RawFilePos += pFileEntry->dwCmpSize; +                if(RawFilePos > FileSize) +                { +                    nError = ERROR_FILE_CORRUPT; +                    break; +                } + +                // Also, we remember end of the file +//              if(RawFilePos > ArchiveSize) +//                  ArchiveSize = RawFilePos; +            } +        } +    } + +    // Load the internal listfile and include it to the file table +    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) +    { +        // Save the flags for (listfile) +        pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL); +        if(pFileEntry != NULL) +            ha->dwFileFlags1 = pFileEntry->dwFlags; + +        // Ignore result of the operation. (listfile) is optional. +        SFileAddListFile((HANDLE)ha, NULL); +    } + +    // Load the "(attributes)" file and merge it to the file table +    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0) +    { +        // Save the flags for (attributes) +        pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); +        if(pFileEntry != NULL) +            ha->dwFileFlags2 = pFileEntry->dwFlags; + +        // Ignore result of the operation. (attributes) is optional. +        SAttrLoadAttributes(ha); +    } + +    // Cleanup and exit +    if(nError != ERROR_SUCCESS) +    { +        FileStream_Close(pStream); +        FreeMPQArchive(ha); +        SetLastError(nError); +        ha = NULL; +    } + +    *phMpq = ha; +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// SFileGetArchiveBitmap + +bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded); +} + +//----------------------------------------------------------------------------- +// bool SFileFlushArchive(HANDLE hMpq) +// +// Saves all dirty data into MPQ archive. +// Has similar effect like SFileCloseArchive, but the archive is not closed. +// Use on clients who keep MPQ archive open even for write operations, +// and terminating without calling SFileCloseArchive might corrupt the archive. +// + +bool WINAPI SFileFlushArchive(HANDLE hMpq) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    int nResultError = ERROR_SUCCESS; +    int nError; + +    // Do nothing if 'hMpq' is bad parameter +    if(!IsValidMpqHandle(ha)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    // If the (listfile) has been invalidated, save it +    if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE) +    { +        nError = SListFileSaveToMpq(ha); +        if(nError != ERROR_SUCCESS) +            nResultError = nError; +    } + +    // If the (attributes) has been invalidated, save it +    if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES) +    { +        nError = SAttrFileSaveToMpq(ha); +        if(nError != ERROR_SUCCESS) +            nResultError = nError; +    } + +    // Save HET table, BET table, hash table, block table, hi-block table +    if(ha->dwFlags & MPQ_FLAG_CHANGED) +    { +        nError = SaveMPQTables(ha); +        if(nError != ERROR_SUCCESS) +            nResultError = nError; +    } + +    // Return the error +    if(nResultError != ERROR_SUCCESS) +        SetLastError(nResultError); +    return (nResultError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// bool SFileCloseArchive(HANDLE hMpq); +// + +bool WINAPI SFileCloseArchive(HANDLE hMpq) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    bool bResult; + +    // Flush all unsaved data to the storage +    bResult = SFileFlushArchive(hMpq); + +    // Free all memory used by MPQ archive +    FreeMPQArchive(ha); +    return bResult; +} + diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp new file mode 100644 index 0000000..9fe77a7 --- /dev/null +++ b/src/SFileOpenFileEx.cpp @@ -0,0 +1,473 @@ +/*****************************************************************************/ +/* SFileOpenFileEx.cpp                    Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description :                                                             */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.99  1.00  Lad  The first version of SFileOpenFileEx.cpp             */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +/*****************************************************************************/ +/* Local functions                                                           */ +/*****************************************************************************/ + +static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) +{ +    TFileStream * pStream; +    TMPQFile * hf = NULL; + +    // We have to convert the local file name to UNICODE, if needed +#ifdef _UNICODE +    TCHAR szFileNameT[MAX_PATH]; +    int i; + +    for(i = 0; szFileName[i] != 0; i++) +        szFileNameT[i] = szFileName[i]; +    szFileNameT[i] = 0; +    pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + +#else +    pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +#endif + +    if(pStream != NULL) +    { +        // Allocate and initialize file handle +        hf = CreateMpqFile(NULL); +        if(hf != NULL) +        { +            hf->pStream = pStream; +            *phFile = hf; +            return true; +        } +        else +        { +            FileStream_Close(pStream); +            SetLastError(ERROR_NOT_ENOUGH_MEMORY); +        } +    } +    *phFile = NULL; +    return false; +} + +bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TMPQFile * hfPatch;                     // Pointer to patch file +    TMPQFile * hfBase = NULL;               // Pointer to base open file +    TMPQFile * hfLast = NULL;               // The highest file in the chain that is not patch file +    TMPQFile * hf = NULL; +    HANDLE hPatchFile; +    char szPatchFileName[MAX_PATH]; + +    // Keep this flag here for future updates +    dwReserved = dwReserved; + +    // First of all, try to open the original version of the file in any of the patch chain +    while(ha != NULL) +    { +        // Construct the name of the patch file +        strcpy(szPatchFileName, ha->szPatchPrefix); +        strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); +        if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase)) +        { +            // The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE +            if((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) +            { +                hf = hfLast = hfBase; +                break; +            } + +            SFileCloseFile((HANDLE)hfBase); +        } + +        // Move to the next file in the patch chain +        ha = ha->haPatch; +    } + +    // If we couldn't find the file in any of the patches, it doesn't exist +    if(hf == NULL) +    { +        SetLastError(ERROR_FILE_NOT_FOUND); +        return false; +    } + +    // Now keep going in the patch chain and open every patch file that is there +    for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch) +    { +        // Construct patch file name +        strcpy(szPatchFileName, ha->szPatchPrefix); +        strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); +        if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_BASE_FILE, &hPatchFile)) +        { +            // Remember the new version +            hfPatch = (TMPQFile *)hPatchFile; + +            // If we encountered a full replacement of the file,  +            // we have to remember the highest full file +            if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) +                hfLast = hfPatch; + +            // Set current patch to base file and move on +            hf->hfPatchFile = hfPatch; +            hf = hfPatch; +        } +    } + +    // Now we need to free all files that are below the highest unpatched version +    while(hfBase != hfLast) +    { +        TMPQFile * hfNext = hfBase->hfPatchFile; + +        // Free the file below +        hfBase->hfPatchFile = NULL; +        FreeMPQFile(hfBase); + +        // Move the base to the next file +        hfBase = hfNext; +    } + +    // Give the updated base MPQ +    if(phFile != NULL) +        *phFile = (HANDLE)hfBase; +    return true; +} + +/*****************************************************************************/ +/* Public functions                                                          */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// SFileEnumLocales enums all locale versions within MPQ.  +// Functions fills all available language identifiers on a file into the buffer +// pointed by plcLocales. There must be enough entries to copy the localed, +// otherwise the function returns ERROR_INSUFFICIENT_BUFFER. + +int WINAPI SFileEnumLocales( +    HANDLE hMpq, +    const char * szFileName, +    LCID * plcLocales, +    LPDWORD pdwMaxLocales, +    DWORD dwSearchScope) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pFileEntry; +    TMPQHash * pFirstHash; +    TMPQHash * pHash; +    DWORD dwFileIndex = 0; +    DWORD dwLocales = 0; + +    // Test the parameters +    if(!IsValidMpqHandle(ha)) +        return ERROR_INVALID_HANDLE; +    if(szFileName == NULL || *szFileName == 0) +        return ERROR_INVALID_PARAMETER; +    if(pdwMaxLocales == NULL) +        return ERROR_INVALID_PARAMETER; +     +    // Keep compiler happy +    dwSearchScope = dwSearchScope; + +    // Parse hash table entries for all locales +    if(!IsPseudoFileName(szFileName, &dwFileIndex)) +    { +        // Calculate the number of locales +        pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); +        while(pHash != NULL) +        { +            dwLocales++; +            pHash = GetNextHashEntry(ha, pFirstHash, pHash); +        } + +        // Test if there is enough space to copy the locales +        if(*pdwMaxLocales < dwLocales) +        { +            *pdwMaxLocales = dwLocales; +            return ERROR_INSUFFICIENT_BUFFER; +        } + +        // Enum the locales +        pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); +        while(pHash != NULL) +        { +            *plcLocales++ = pHash->lcLocale; +            pHash = GetNextHashEntry(ha, pFirstHash, pHash); +        } +    } +    else +    { +        // There must be space for 1 locale +        if(*pdwMaxLocales < 1) +        { +            *pdwMaxLocales = 1; +            return ERROR_INSUFFICIENT_BUFFER; +        } + +        // For nameless access, always return 1 locale +        pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); +        pHash = ha->pHashTable + pFileEntry->dwHashIndex; +        *plcLocales = pHash->lcLocale; +        dwLocales = 1; +    } + +    // Give the caller the total number of found locales +    *pdwMaxLocales = dwLocales; +    return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// SFileHasFile +// +//   hMpq          - Handle of opened MPQ archive +//   szFileName    - Name of file to look for + +bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pFileEntry; +    DWORD dwFlagsToCheck = MPQ_FILE_EXISTS; +    DWORD dwFileIndex = 0; +    char szPatchFileName[MAX_PATH]; +    bool bIsPseudoName; +    int nError = ERROR_SUCCESS; + +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(szFileName == NULL || *szFileName == 0) +        nError = ERROR_INVALID_PARAMETER; + +    // Prepare the file opening +    if(nError == ERROR_SUCCESS) +    { +        // Different processing for pseudo-names +        bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex); + +        // Walk through the MPQ and all patches +        while(ha != NULL) +        { +            // Verify presence of the file +            pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale) +                                                  : GetFileEntryByIndex(ha, dwFileIndex); +            // Verify the file flags +            if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS) +                return true; + +            // If this is patched archive, go to the patch +            dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; +            ha = ha->haPatch; + +            // Prepare the patched file name +            if(ha != NULL) +            { +                strcpy(szPatchFileName, ha->szPatchPrefix); +                strcat(szPatchFileName, szFileName); +                szFileName = szPatchFileName; +            } +        } + +        // Not found, sorry +        nError = ERROR_FILE_NOT_FOUND; +    } + +    // Cleanup +    SetLastError(nError); +    return false; +} + + +//----------------------------------------------------------------------------- +// SFileOpenFileEx +// +//   hMpq          - Handle of opened MPQ archive +//   szFileName    - Name of file to open +//   dwSearchScope - Where to search +//   phFile        - Pointer to store opened file handle + +bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry  * pFileEntry = NULL; +    TMPQFile    * hf = NULL; +    DWORD dwFileIndex = 0; +    bool bOpenByIndex = false; +    int nError = ERROR_SUCCESS; + +    // Don't accept NULL pointer to file handle +    if(phFile == NULL) +        nError = ERROR_INVALID_PARAMETER; + +    // Prepare the file opening +    if(nError == ERROR_SUCCESS) +    { +        switch(dwSearchScope) +        { +            case SFILE_OPEN_FROM_MPQ: +            case SFILE_OPEN_BASE_FILE: +                 +                if(!IsValidMpqHandle(ha)) +                { +                    nError = ERROR_INVALID_HANDLE; +                    break; +                } + +                if(szFileName == NULL || *szFileName == 0) +                { +                    nError = ERROR_INVALID_PARAMETER; +                    break; +                } + +                // Check the pseudo-file name +                if(IsPseudoFileName(szFileName, &dwFileIndex)) +                { +                    pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); +                    bOpenByIndex = true; +                    if(pFileEntry == NULL) +                        nError = ERROR_FILE_NOT_FOUND; +                } +                else +                { +                    // If this MPQ is a patched archive, open the file as patched +                    if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) +                    { +                        // Otherwise, open the file from *this* MPQ +                        pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); +                        if(pFileEntry == NULL) +                            nError = ERROR_FILE_NOT_FOUND; +                    } +                    else +                    { +                        return OpenPatchedFile(hMpq, szFileName, 0, phFile); +                    } +                } +                break; + +            case SFILE_OPEN_ANY_LOCALE: + +                // This open option is reserved for opening MPQ internal listfile. +                // No argument validation. Tries to open file with neutral locale first, +                // then any other available. +                pFileEntry = GetFileEntryAny(ha, szFileName); +                if(pFileEntry == NULL) +                    nError = ERROR_FILE_NOT_FOUND; +                break; + +            case SFILE_OPEN_LOCAL_FILE: + +                if(szFileName == NULL || *szFileName == 0) +                { +                    nError = ERROR_INVALID_PARAMETER; +                    break; +                } + +                return OpenLocalFile(szFileName, phFile);  + +            default: + +                // Don't accept any other value +                nError = ERROR_INVALID_PARAMETER; +                break; +        } + +        // Quick return if something failed +        if(nError != ERROR_SUCCESS) +        { +            SetLastError(nError); +            return false; +        } +    } + +    // Test if the file was not already deleted. +    if(nError == ERROR_SUCCESS) +    { +        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) +            nError = ERROR_FILE_NOT_FOUND; +        if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) +            nError = ERROR_NOT_SUPPORTED; +    } + +    // Allocate file handle +    if(nError == ERROR_SUCCESS) +    { +        if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Initialize file handle +    if(nError == ERROR_SUCCESS) +    { +        memset(hf, 0, sizeof(TMPQFile)); +        hf->pFileEntry = pFileEntry; +        hf->dwMagic = ID_MPQ_FILE; +        hf->ha = ha; + +        hf->MpqFilePos   = pFileEntry->ByteOffset; +        hf->RawFilePos   = ha->MpqPos + hf->MpqFilePos; +        hf->dwDataSize   = pFileEntry->dwFileSize; + +        // If the MPQ has sector CRC enabled, enable if for the file +        if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) +            hf->bCheckSectorCRCs = true; + +        // If we know the real file name, copy it to the file entry +        if(bOpenByIndex == false) +        { +            // If there is no file name yet, allocate it +            AllocateFileName(pFileEntry, szFileName); + +            // If the file is encrypted, we should detect the file key +            if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +            { +                hf->dwFileKey = DecryptFileKey(szFileName, +                                               pFileEntry->ByteOffset, +                                               pFileEntry->dwFileSize, +                                               pFileEntry->dwFlags); +            } +        } +        else +        { +            // Try to auto-detect the file name +            if(!SFileGetFileName(hf, NULL)) +                nError = GetLastError(); +        } +    } + +    // If the file is actually a patch file, we have to load the patch file header +    if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) +    { +        assert(hf->pPatchInfo == NULL); +        nError = AllocatePatchInfo(hf, true); +    } + +    // Cleanup +    if(nError != ERROR_SUCCESS) +    { +        SetLastError(nError); +        FreeMPQFile(hf); +    } + +    *phFile = hf; +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// bool WINAPI SFileCloseFile(HANDLE hFile); + +bool WINAPI SFileCloseFile(HANDLE hFile) +{ +    TMPQFile * hf = (TMPQFile *)hFile; +     +    if(!IsValidFileHandle(hf)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    // Free the structure +    FreeMPQFile(hf); +    return true; +} diff --git a/src/SFilePatchArchives.cpp b/src/SFilePatchArchives.cpp new file mode 100644 index 0000000..8f259f4 --- /dev/null +++ b/src/SFilePatchArchives.cpp @@ -0,0 +1,587 @@ +/*****************************************************************************/ +/* SFilePatchArchives.cpp                 Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* Description:                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 18.08.10  1.00  Lad  The first version of SFilePatchArchives.cpp          */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +typedef struct _BLIZZARD_BSDIFF40_FILE +{ +    ULONGLONG Signature; +    ULONGLONG CtrlBlockSize; +    ULONGLONG DataBlockSize; +    ULONGLONG NewFileSize; +} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE; + +//----------------------------------------------------------------------------- +// Local functions + +static bool GetDefaultPatchPrefix( +    const TCHAR * szBaseMpqName, +    char * szBuffer) +{ +    const TCHAR * szExtension; +    const TCHAR * szDash; + +    // Ensure that both names are plain names +    szBaseMpqName = GetPlainFileNameT(szBaseMpqName); + +    // Patch prefix is for the Cataclysm MPQs, whose names +    // are like "locale-enGB.MPQ" or "speech-enGB.MPQ" +    szExtension = _tcsrchr(szBaseMpqName, _T('.')); +    szDash = _tcsrchr(szBaseMpqName, _T('-')); +    strcpy(szBuffer, "Base"); + +    // If the length of the prefix doesn't match, use default one +    if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5) +    { +        // Copy the prefix +        szBuffer[0] = (char)szDash[1]; +        szBuffer[1] = (char)szDash[2]; +        szBuffer[2] = (char)szDash[3]; +        szBuffer[3] = (char)szDash[4]; +        szBuffer[4] = 0; +    } + +    return true; +} + +static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed) +{ +    LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed; +    LPBYTE pbCompressedEnd = pbCompressed + cbCompressed; +    BYTE RepeatCount;  +    BYTE OneByte; + +    // Cut the initial DWORD from the compressed chunk +    pbCompressed += sizeof(DWORD); +    cbCompressed -= sizeof(DWORD); + +    // Pre-fill decompressed buffer with zeros +    memset(pbDecompressed, 0, cbDecompressed); + +    // Unpack +    while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd) +    { +        OneByte = *pbCompressed++; +         +        // Is it a repetition byte ? +        if(OneByte & 0x80) +        { +            RepeatCount = (OneByte & 0x7F) + 1; +            for(BYTE i = 0; i < RepeatCount; i++) +            { +                if(pbDecompressed == pbDecompressedEnd || pbCompressed == pbCompressedEnd) +                    break; + +                *pbDecompressed++ = *pbCompressed++; +            } +        } +        else +        { +            pbDecompressed += (OneByte + 1); +        } +    } +} + +static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader) +{ +    int nError = ERROR_SUCCESS; + +    // Allocate space for patch header and compressed data +    hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); +    if(hf->pPatchHeader == NULL) +        nError = ERROR_NOT_ENOUGH_MEMORY; + +    // Load the patch data and decide if they are compressed or not +    if(nError == ERROR_SUCCESS) +    { +        LPBYTE pbPatchFile = (LPBYTE)hf->pPatchHeader; + +        // Copy the patch header itself +        memcpy(pbPatchFile, pPatchHeader, sizeof(TPatchHeader)); +        pbPatchFile += sizeof(TPatchHeader); + +        // Load the rest of the patch +        if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader), NULL, NULL)) +            nError = GetLastError(); +    } + +    return nError; +} + +static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) +{ +    LPBYTE pbDecompressed = NULL; +    LPBYTE pbCompressed = NULL; +    DWORD cbDecompressed = 0; +    DWORD cbCompressed = 0; +    DWORD dwBytesRead = 0; +    int nError = ERROR_SUCCESS; + +    // Allocate space for compressed data +    cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; +    pbCompressed = STORM_ALLOC(BYTE, cbCompressed); +    if(pbCompressed == NULL) +        nError = ERROR_SUCCESS; + +    // Read the compressed patch data +    if(nError == ERROR_SUCCESS) +    { +        // Load the rest of the header +        SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead, NULL); +        if(dwBytesRead != cbCompressed) +            nError = ERROR_FILE_CORRUPT; +    } + +    // Get the uncompressed size of the patch +    if(nError == ERROR_SUCCESS) +    { +        cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader); +        hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); +        if(hf->pPatchHeader == NULL) +            nError = ERROR_NOT_ENOUGH_MEMORY; +    } + +    // Now decompress the patch data +    if(nError == ERROR_SUCCESS) +    { +        // Copy the patch header +        memcpy(hf->pPatchHeader, pPatchHeader, sizeof(TPatchHeader)); +        pbDecompressed = (LPBYTE)hf->pPatchHeader + sizeof(TPatchHeader); + +        // Uncompress or copy the patch data +        if(cbCompressed < cbDecompressed) +        { +            Decompress_RLE(pbDecompressed, cbDecompressed, pbCompressed, cbCompressed); +        } +        else +        { +            assert(cbCompressed == cbDecompressed); +            memcpy(pbDecompressed, pbCompressed, cbCompressed); +        } +    } + +    // Free buffers and exit +    if(pbCompressed != NULL) +        STORM_FREE(pbCompressed); +    return nError; +} + +static int ApplyMpqPatch_COPY( +    TMPQFile * hf, +    TPatchHeader * pPatchHeader) +{ +    LPBYTE pbNewFileData; +    DWORD cbNewFileData; + +    // Allocate space for new file data +    cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; +    pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData); +    if(pbNewFileData == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Copy the patch data as-is +    memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData); + +    // Free the old file data +    STORM_FREE(hf->pbFileData); + +    // Put the new file data there +    hf->pbFileData = pbNewFileData; +    hf->cbFileData = cbNewFileData; +    return ERROR_SUCCESS; +} + +static int ApplyMpqPatch_BSD0( +    TMPQFile * hf, +    TPatchHeader * pPatchHeader) +{ +    PBLIZZARD_BSDIFF40_FILE pBsdiff; +    LPDWORD pCtrlBlock; +    LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader); +    LPBYTE pDataBlock; +    LPBYTE pExtraBlock; +    LPBYTE pbNewData = NULL; +    LPBYTE pbOldData = (LPBYTE)hf->pbFileData; +    DWORD dwNewOffset = 0;                          // Current position to patch +    DWORD dwOldOffset = 0;                          // Current source position +    DWORD dwNewSize;                                // Patched file size +    DWORD dwOldSize = hf->cbFileData;               // File size before patch + +    // Get pointer to the patch header +    // Format of BSDIFF header corresponds to original BSDIFF, which is: +    // 0000   8 bytes   signature "BSDIFF40" +    // 0008   8 bytes   size of the control block +    // 0010   8 bytes   size of the data block +    // 0018   8 bytes   new size of the patched file +    pBsdiff = (PBLIZZARD_BSDIFF40_FILE)pbPatchData; +    pbPatchData += sizeof(BLIZZARD_BSDIFF40_FILE); + +    // Get pointer to the 32-bit BSDIFF control block +    // The control block follows immediately after the BSDIFF header +    // and consists of three 32-bit integers +    // 0000   4 bytes   Length to copy from the BSDIFF data block the new file +    // 0004   4 bytes   Length to copy from the BSDIFF extra block +    // 0008   4 bytes   Size to increment source file offset +    pCtrlBlock = (LPDWORD)pbPatchData; +    pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->CtrlBlockSize); + +    // Get the pointer to the data block +    pDataBlock = (LPBYTE)pbPatchData; +    pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->DataBlockSize); + +    // Get the pointer to the extra block +    pExtraBlock = (LPBYTE)pbPatchData; +    dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize); + +    // Allocate new buffer +    pbNewData = STORM_ALLOC(BYTE, dwNewSize); +    if(pbNewData == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Now patch the file +    while(dwNewOffset < dwNewSize) +    { +        DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[0]); +        DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[1]); +        DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[2]); +        DWORD i; + +        // Sanity check +        if((dwNewOffset + dwAddDataLength) > dwNewSize) +        { +            STORM_FREE(pbNewData); +            return ERROR_FILE_CORRUPT; +        } + +        // Read the diff string to the target buffer +        memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength); +        pDataBlock += dwAddDataLength; + +        // Now combine the patch data with the original file +        for(i = 0; i < dwAddDataLength; i++) +        { +            if(dwOldOffset < dwOldSize) +                pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset]; + +            dwNewOffset++; +            dwOldOffset++; +        } + +        // Sanity check +        if((dwNewOffset + dwMovDataLength) > dwNewSize) +        { +            STORM_FREE(pbNewData); +            return ERROR_FILE_CORRUPT; +        } + +        // Copy the data from the extra block in BSDIFF patch +        memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength); +        pExtraBlock += dwMovDataLength; +        dwNewOffset += dwMovDataLength; + +        // Move the old offset +        if(dwOldMoveLength & 0x80000000) +            dwOldMoveLength = 0x80000000 - dwOldMoveLength; +        dwOldOffset += dwOldMoveLength; +        pCtrlBlock += 3; +    } + +    // Free the old file data +    STORM_FREE(hf->pbFileData); + +    // Put the new data to the fil structure +    hf->pbFileData = pbNewData; +    hf->cbFileData = dwNewSize; +    return ERROR_SUCCESS; +} + + +static int LoadMpqPatch(TMPQFile * hf) +{ +    TPatchHeader PatchHeader; +    DWORD dwBytesRead; +    int nError = ERROR_SUCCESS; + +    // Read the patch header +    SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead, NULL); +    if(dwBytesRead != sizeof(TPatchHeader)) +        nError = ERROR_FILE_CORRUPT; + +    // Verify the signatures in the patch header +    if(nError == ERROR_SUCCESS) +    { +        // BSWAP the entire header, if needed +        BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); +        PatchHeader.dwXFRM          = BSWAP_INT32_UNSIGNED(PatchHeader.dwXFRM); +        PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize); +        PatchHeader.dwPatchType     = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType); + +        if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658) +            nError = ERROR_FILE_CORRUPT; +    } + +    // Read the patch, depending on patch type +    if(nError == ERROR_SUCCESS) +    { +        switch(PatchHeader.dwPatchType) +        { +            case 0x59504f43:    // 'COPY' +                nError = LoadMpqPatch_COPY(hf, &PatchHeader); +                break; + +            case 0x30445342:    // 'BSD0' +                nError = LoadMpqPatch_BSD0(hf, &PatchHeader); +                break; + +            default: +                nError = ERROR_FILE_CORRUPT; +                break; +        } +    } + +    return nError; +} + +static int ApplyMpqPatch( +    TMPQFile * hf, +    TPatchHeader * pPatchHeader) +{ +    int nError = ERROR_SUCCESS; + +    // Verify the original file before patching +    if(pPatchHeader->dwSizeBeforePatch != 0) +    { +        if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch)) +            nError = ERROR_FILE_CORRUPT; +    } + +    // Apply the patch +    if(nError == ERROR_SUCCESS) +    { +        switch(pPatchHeader->dwPatchType) +        { +            case 0x59504f43:    // 'COPY' +                nError = ApplyMpqPatch_COPY(hf, pPatchHeader); +                break; + +            case 0x30445342:    // 'BSD0' +                nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); +                break; + +            default: +                nError = ERROR_FILE_CORRUPT; +                break; +        } +    } + +    // Verify MD5 after patch +    if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) +    { +        // Verify the patched file +        if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) +            nError = ERROR_FILE_CORRUPT; +    } + +    return nError; +} + +//----------------------------------------------------------------------------- +// Public functions (StormLib internals) + +bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) +{ +    TPatchHeader * pPatchHeader = (TPatchHeader *)pvData; +    BLIZZARD_BSDIFF40_FILE DiffFile; +    DWORD dwPatchType; + +    if(cbData >= sizeof(TPatchHeader) + sizeof(BLIZZARD_BSDIFF40_FILE)) +    { +        dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType); +        if(dwPatchType == 0x30445342) +        { +            // Give the caller the patch file size +            if(pdwPatchedFileSize != NULL) +            { +                Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE)); +                DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize); +                *pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize; +                return true; +            } +        } +    } + +    return false; +} + +int PatchFileData(TMPQFile * hf) +{ +    TMPQFile * hfBase = hf; +    int nError = ERROR_SUCCESS; + +    // Move to the first patch +    hf = hf->hfPatchFile; + +    // Now go through all patches and patch the original data +    while(hf != NULL) +    { +        // This must be true +        assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE); + +        // Make sure that the patch data is loaded +        nError = LoadMpqPatch(hf); +        if(nError != ERROR_SUCCESS) +            break; + +        // Apply the patch +        nError = ApplyMpqPatch(hfBase, hf->pPatchHeader); +        if(nError != ERROR_SUCCESS) +            break; + +        // Move to the next patch +        hf = hf->hfPatchFile; +    } + +    return nError; +} + +//----------------------------------------------------------------------------- +// Public functions + +// +// Patch prefix is the path subdirectory where the patched files are within MPQ. +// +// Example 1: +// Main MPQ:  locale-enGB.MPQ +// Patch MPQ: wow-update-12694.MPQ +// File in main MPQ: DBFilesClient\Achievement.dbc +// File in patch MPQ: enGB\DBFilesClient\Achievement.dbc +// Path prefix: enGB +// +// Example 2: +// Main MPQ:  expansion1.MPQ +// Patch MPQ: wow-update-12694.MPQ +// File in main MPQ: DBFilesClient\Achievement.dbc +// File in patch MPQ: Base\DBFilesClient\Achievement.dbc +// Path prefix: Base +// + +bool WINAPI SFileOpenPatchArchive( +    HANDLE hMpq, +    const TCHAR * szPatchMpqName, +    const char * szPatchPathPrefix, +    DWORD dwFlags) +{ +    TMPQArchive * haPatch; +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    HANDLE hPatchMpq = NULL; +    char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN]; +    int nError = ERROR_SUCCESS; + +    // Keep compiler happy +    dwFlags = dwFlags; + +    // Verify input parameters +    if(!IsValidMpqHandle(ha)) +        nError = ERROR_INVALID_HANDLE; +    if(szPatchMpqName == NULL || *szPatchMpqName == 0) +        nError = ERROR_INVALID_PARAMETER; + +    // If the user didn't give the patch prefix, get default one +    if(szPatchPathPrefix != NULL) +    { +        // Save length of the patch prefix +        if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2) +            nError = ERROR_INVALID_PARAMETER; +    } + +    // +    // We don't allow adding patches to archives that have been open for write +    // +    // Error scenario: +    // +    // 1) Open archive for writing +    // 2) Modify or replace a file +    // 3) Add patch archive to the opened MPQ +    // 4) Read patched file +    // 5) Now what ? +    // + +    if(nError == ERROR_SUCCESS) +    { +        if(!FileStream_IsReadOnly(ha->pStream)) +            nError = ERROR_ACCESS_DENIED; +    } + +    // Open the archive like it is normal archive +    if(nError == ERROR_SUCCESS) +    { +        if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY, &hPatchMpq)) +            return false; +        haPatch = (TMPQArchive *)hPatchMpq; + +        // Older WoW patches (build 13914) used to have +        // several language versions in one patch file +        // Those patches needed to have a path prefix +        // We can distinguish such patches by not having the (patch_metadata) file +        if(szPatchPathPrefix == NULL) +        { +            if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME)) +            { +                GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff); +                szPatchPathPrefix = szPatchPrefixBuff; +            } +        } + +        // Save the prefix for patch file names. +        // Make sure that there is backslash after it +        if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0) +        { +            strcpy(haPatch->szPatchPrefix, szPatchPathPrefix); +            strcat(haPatch->szPatchPrefix, "\\"); +            haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix); +        } + +        // Now add the patch archive to the list of patches to the original MPQ +        while(ha != NULL) +        { +            if(ha->haPatch == NULL) +            { +                haPatch->haBase = ha; +                ha->haPatch = haPatch; +                return true; +            } + +            // Move to the next archive +            ha = ha->haPatch; +        } + +        // Should never happen +        nError = ERROR_CAN_NOT_COMPLETE; +    } + +    SetLastError(nError); +    return false; +} + +bool WINAPI SFileIsPatchedArchive(HANDLE hMpq) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    // Verify input parameters +    if(!IsValidMpqHandle(ha)) +        return false; + +    return (ha->haPatch != NULL); +} diff --git a/src/SFileReadFile.cpp b/src/SFileReadFile.cpp new file mode 100644 index 0000000..164b646 --- /dev/null +++ b/src/SFileReadFile.cpp @@ -0,0 +1,1186 @@ +/*****************************************************************************/ +/* SFileReadFile.cpp                      Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description :                                                             */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.99  1.00  Lad  The first version of SFileReadFile.cpp               */ +/* 24.03.99  1.00  Lad  Added the SFileGetFileInfo function                  */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +struct TFileHeader2Ext +{ +    DWORD dwOffset00Data;               // Required data at offset 00 (32-bits) +    DWORD dwOffset00Mask;               // Mask for data at offset 00 (32 bits). 0 = data are ignored +    DWORD dwOffset04Data;               // Required data at offset 04 (32-bits) +    DWORD dwOffset04Mask;               // Mask for data at offset 04 (32 bits). 0 = data are ignored +    const char * szExt;                 // Supplied extension, if the condition is true +}; + +//----------------------------------------------------------------------------- +// Local functions + +static void CopyFileName(char * szTarget, const TCHAR * szSource) +{ +    while(*szSource != 0) +        *szTarget++ = (char)*szSource++; +    *szTarget = 0; +} + +static DWORD GetMpqFileCount(TMPQArchive * ha) +{ +    TFileEntry * pFileTableEnd; +    TFileEntry * pFileEntry; +    DWORD dwFileCount = 0; + +    // Go through all open MPQs, including patches +    while(ha != NULL) +    { +        // Only count files that are not patch files +        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; +        for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) +        { +            // If the file is patch file and this is not primary archive, skip it +            // BUGBUG: This errorneously counts non-patch files that are in both +            // base MPQ and in patches, and increases the number of files by cca 50% +            if((pFileEntry->dwFlags & (MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE)) == MPQ_FILE_EXISTS) +                dwFileCount++; +        } + +        // Move to the next patch archive +        ha = ha->haPatch; +    } + +    return dwFileCount; +} + +static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) +{ +    TMPQFile * hfTemp; +    TCHAR * szPatchChain = (TCHAR *)pvFileInfo; +    TCHAR * szFileName; +    size_t cchCharsNeeded = 1; +    size_t nLength; +    DWORD cbLengthNeeded; + +    // Check if the "hf" is a MPQ file +    if(hf->pStream != NULL) +    { +        // Calculate the length needed +        szFileName = FileStream_GetFileName(hf->pStream); +        cchCharsNeeded += _tcslen(szFileName) + 1; +        cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); +         +        // If we have enough space, copy the file name +        if(cbFileInfo >= cbLengthNeeded) +        { +            nLength = _tcslen(szFileName) + 1; +            memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); +            szPatchChain += nLength; + +            // Terminate the multi-string +            *szPatchChain = 0; +        } +    } +    else +    { +        // Calculate number of characters needed +        for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) +            cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; +        cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); + +        // If we have enough space, the copy the patch chain +        if(cbFileInfo >= cbLengthNeeded) +        { +            for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) +            { +                szFileName = FileStream_GetFileName(hfTemp->ha->pStream); +                nLength = _tcslen(szFileName) + 1; +                memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); +                szPatchChain += nLength; +            } + +            // Terminate the multi-string +            *szPatchChain = 0; +        } +    } + +    // Give result length, terminate multi-string and return +    *pcbLengthNeeded = cbLengthNeeded; +    return true; +} + +//  hf            - MPQ File handle. +//  pbBuffer      - Pointer to target buffer to store sectors. +//  dwByteOffset  - Position of sector in the file (relative to file begin) +//  dwBytesToRead - Number of bytes to read. Must be multiplier of sector size. +//  pdwBytesRead  - Stored number of bytes loaded +static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +{ +    ULONGLONG RawFilePos; +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    LPBYTE pbRawSector = NULL; +    LPBYTE pbOutSector = pbBuffer; +    LPBYTE pbInSector = pbBuffer; +    DWORD dwRawBytesToRead; +    DWORD dwRawSectorOffset = dwByteOffset; +    DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize; +    DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize; +    DWORD dwSectorsDone = 0; +    DWORD dwBytesRead = 0; +    int nError = ERROR_SUCCESS; + +    // Note that dwByteOffset must be aligned to size of one sector +    // Note that dwBytesToRead must be a multiplier of one sector size +    // This is local function, so we won't check if that's true. +    // Note that files stored in single units are processed by a separate function + +    // If there is not enough bytes remaining, cut dwBytesToRead +    if((dwByteOffset + dwBytesToRead) > hf->dwDataSize) +        dwBytesToRead = hf->dwDataSize - dwByteOffset; +    dwRawBytesToRead = dwBytesToRead; + +    // Perform all necessary work to do with compressed files +    if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +    { +        // If the sector positions are not loaded yet, do it +        if(hf->SectorOffsets == NULL) +        { +            nError = AllocateSectorOffsets(hf, true); +            if(nError != ERROR_SUCCESS) +                return nError; +        } + +        // If the sector checksums are not loaded yet, load them now. +        if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) +        { +            // +            // Sector CRCs is plain crap feature. It is almost never present, +            // often it's empty, or the end offset of sector CRCs is zero. +            // We only try to load sector CRCs once, and regardless if it fails +            // or not, we won't try that again for the given file. +            // + +            AllocateSectorChecksums(hf, true); +            hf->bLoadedSectorCRCs = true; +        } + +        // TODO: If the raw data MD5s are not loaded yet, load them now +        // Only do it if the MPQ is of format 4.0 +//      if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0) +//      { +//          nError = AllocateRawMD5s(hf, true); +//          if(nError != ERROR_SUCCESS) +//              return nError; +//      } + +        // If the file is compressed, also allocate secondary buffer +        pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwBytesToRead); +        if(pbRawSector == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Assign the temporary buffer as target for read operation +        dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex]; +        dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset; +    } + +    // Calculate raw file offset where the sector(s) are stored. +    CalculateRawSectorOffset(RawFilePos, hf, dwRawSectorOffset); + +    // Set file pointer and read all required sectors +    if(!FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) +        return GetLastError(); +    dwBytesRead = 0; + +    // Now we have to decrypt and decompress all file sectors that have been loaded +    for(DWORD i = 0; i < dwSectorsToRead; i++) +    { +        DWORD dwRawBytesInThisSector = ha->dwSectorSize; +        DWORD dwBytesInThisSector = ha->dwSectorSize; +        DWORD dwIndex = dwSectorIndex + i; + +        // If there is not enough bytes in the last sector, +        // cut the number of bytes in this sector +        if(dwRawBytesInThisSector > dwBytesToRead) +            dwRawBytesInThisSector = dwBytesToRead; +        if(dwBytesInThisSector > dwBytesToRead) +            dwBytesInThisSector = dwBytesToRead; + +        // If the file is compressed, we have to adjust the raw sector size +        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +            dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; + +        // If the file is encrypted, we have to decrypt the sector +        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +        { +            BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); + +            // If we don't know the key, try to detect it by file content +            if(hf->dwFileKey == 0) +            { +                hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector); +                if(hf->dwFileKey == 0) +                { +                    nError = ERROR_UNKNOWN_FILE_KEY; +                    break; +                } +            } + +            DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); +            BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); +        } + +        // If the file has sector CRC check turned on, perform it +        if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) +        { +            DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; +            DWORD dwAdlerValue = 0; + +            // We can only check sector CRC when it's not zero +            // Neither can we check it if it's 0xFFFFFFFF. +            if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) +            { +                dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); +                if(dwAdlerValue != dwAdlerExpected) +                { +                    nError = ERROR_CHECKSUM_ERROR; +                    break; +                } +            } +        } + +        // If the sector is really compressed, decompress it. +        // WARNING : Some sectors may not be compressed, it can be determined only +        // by comparing uncompressed and compressed size !!! +        if(dwRawBytesInThisSector < dwBytesInThisSector) +        { +            int cbOutSector = dwBytesInThisSector; +            int cbInSector = dwRawBytesInThisSector; +            int nResult = 0; + +            // Is the file compressed by Blizzard's multiple compression ? +            if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) +            { +                if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) +                    nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector); +                else +                    nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector); +            } + +            // Is the file compressed by PKWARE Data Compression Library ? +            else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) +            { +                nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); +            } + +            // Did the decompression fail ? +            if(nResult == 0) +            { +                nError = ERROR_FILE_CORRUPT; +                break; +            } +        } +        else +        { +            if(pbOutSector != pbInSector) +                memcpy(pbOutSector, pbInSector, dwBytesInThisSector); +        } + +        // Move pointers +        dwBytesToRead -= dwBytesInThisSector; +        dwByteOffset += dwBytesInThisSector; +        dwBytesRead += dwBytesInThisSector; +        pbOutSector += dwBytesInThisSector; +        pbInSector += dwRawBytesInThisSector; +        dwSectorsDone++; +    } + +    // Free all used buffers +    if(pbRawSector != NULL) +        STORM_FREE(pbRawSector); +     +    // Give the caller thenumber of bytes read +    *pdwBytesRead = dwBytesRead; +    return nError;  +} + +static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ +    ULONGLONG RawFilePos = hf->RawFilePos; +    TMPQArchive * ha = hf->ha; +    TFileEntry * pFileEntry = hf->pFileEntry; +    LPBYTE pbCompressed = NULL; +    LPBYTE pbRawData = NULL; +    int nError = ERROR_SUCCESS; + +    // If the file buffer is not allocated yet, do it. +    if(hf->pbFileSector == NULL) +    { +        nError = AllocateSectorBuffer(hf); +        if(nError != ERROR_SUCCESS) +            return nError; +        pbRawData = hf->pbFileSector; +    } + +    // If the file is a patch file, adjust raw data offset +    if(hf->pPatchInfo != NULL) +        RawFilePos += hf->pPatchInfo->dwLength; + +    // If the file sector is not loaded yet, do it +    if(hf->dwSectorOffs != 0) +    { +        // Is the file compressed? +        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +        { +            // Allocate space for compressed data +            pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); +            if(pbCompressed == NULL) +                return ERROR_NOT_ENOUGH_MEMORY; +            pbRawData = pbCompressed; +        } +         +        // Load the raw (compressed, encrypted) data +        if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) +        { +            STORM_FREE(pbCompressed); +            return GetLastError(); +        } + +        // If the file is encrypted, we have to decrypt the data first +        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) +        { +            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); +            DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); +            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); +        } + +        // If the file is compressed, we have to decompress it now +        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) +        { +            int cbOutBuffer = (int)hf->dwDataSize; +            int cbInBuffer = (int)pFileEntry->dwCmpSize; +            int nResult = 0; + +            // +            // If the file is an incremental patch, the size of compressed data +            // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo) +            // +            // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: +            // +            // File                                    CmprSize   DcmpSize DataSize Compressed? +            // --------------------------------------  ---------- -------- -------- --------------- +            // esES\DBFilesClient\LightSkyBox.dbc      0xBE->0xA2  0xBC     0xBC     Yes +            // deDE\DBFilesClient\MountCapability.dbc  0x93->0x77  0x77     0x77     No +            //  + +            if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) +                cbInBuffer = cbInBuffer - sizeof(TPatchInfo); + +            // Is the file compressed by Blizzard's multiple compression ? +            if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) +            { +                if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) +                    nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); +                else +                    nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); +            } + +            // Is the file compressed by PKWARE Data Compression Library ? +            // Note: Single unit files compressed with IMPLODE are not supported by Blizzard +            else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) +                nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); + +            nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; +        } +        else +        { +            if(pbRawData != hf->pbFileSector) +                memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); +        } + +        // Free the decompression buffer. +        if(pbCompressed != NULL) +            STORM_FREE(pbCompressed); + +        // The file sector is now properly loaded +        hf->dwSectorOffs = 0; +    } + +    // At this moment, we have the file loaded into the file buffer. +    // Copy as much as the caller wants +    if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) +    { +        // File position is greater or equal to file size ? +        if(dwFilePos >= hf->dwDataSize) +        { +            *pdwBytesRead = 0; +            return ERROR_SUCCESS; +        } + +        // If not enough bytes remaining in the file, cut them +        if((hf->dwDataSize - dwFilePos) < dwToRead) +            dwToRead = (hf->dwDataSize - dwFilePos); + +        // Copy the bytes +        memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); + +        // Give the number of bytes read +        *pdwBytesRead = dwToRead; +        return ERROR_SUCCESS; +    } + +    // An error, sorry +    return ERROR_CAN_NOT_COMPLETE; +} + +static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +{ +    TMPQArchive * ha = hf->ha; +    LPBYTE pbBuffer = (BYTE *)pvBuffer; +    DWORD dwTotalBytesRead = 0;                         // Total bytes read in all three parts +    DWORD dwSectorSizeMask = ha->dwSectorSize - 1;      // Mask for block size, usually 0x0FFF +    DWORD dwFileSectorPos;                              // File offset of the loaded sector +    DWORD dwBytesRead;                                  // Number of bytes read (temporary variable) +    int nError; + +    // If the file position is at or beyond end of file, do nothing +    if(dwFilePos >= hf->dwDataSize) +    { +        *pdwBytesRead = 0; +        return ERROR_SUCCESS; +    } + +    // If not enough bytes in the file remaining, cut them +    if(dwBytesToRead > (hf->dwDataSize - dwFilePos)) +        dwBytesToRead = (hf->dwDataSize - dwFilePos); + +    // Compute sector position in the file +    dwFileSectorPos = dwFilePos & ~dwSectorSizeMask;  // Position in the block + +    // If the file sector buffer is not allocated yet, do it now +    if(hf->pbFileSector == NULL) +    { +        nError = AllocateSectorBuffer(hf); +        if(nError != ERROR_SUCCESS) +            return nError; +    } + +    // Load the first (incomplete) file sector +    if(dwFilePos & dwSectorSizeMask) +    { +        DWORD dwBytesInSector = ha->dwSectorSize; +        DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; +        DWORD dwToCopy;                                      + +        // Is the file sector already loaded ? +        if(hf->dwSectorOffs != dwFileSectorPos) +        { +            // Load one MPQ sector into archive buffer +            nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); +            if(nError != ERROR_SUCCESS) +                return nError; + +            // Remember that the data loaded to the sector have new file offset +            hf->dwSectorOffs = dwFileSectorPos; +        } +        else +        { +            if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize) +                dwBytesInSector = hf->dwDataSize - dwFileSectorPos; +        } + +        // Copy the data from the offset in the loaded sector to the end of the sector +        dwToCopy = dwBytesInSector - dwBufferOffs; +        if(dwToCopy > dwBytesToRead) +            dwToCopy = dwBytesToRead; + +        // Copy data from sector buffer into target buffer +        memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy); + +        // Update pointers and byte counts +        dwTotalBytesRead += dwToCopy; +        dwFileSectorPos  += dwBytesInSector; +        pbBuffer         += dwToCopy; +        dwBytesToRead    -= dwToCopy; +    } + +    // Load the whole ("middle") sectors only if there is at least one full sector to be read +    if(dwBytesToRead >= ha->dwSectorSize) +    { +        DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask; + +        // Load all sectors to the output buffer +        nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); +        if(nError != ERROR_SUCCESS) +            return nError; + +        // Update pointers +        dwTotalBytesRead += dwBytesRead; +        dwFileSectorPos  += dwBytesRead; +        pbBuffer         += dwBytesRead; +        dwBytesToRead    -= dwBytesRead; +    } + +    // Read the terminating sector +    if(dwBytesToRead > 0) +    { +        DWORD dwToCopy = ha->dwSectorSize; + +        // Is the file sector already loaded ? +        if(hf->dwSectorOffs != dwFileSectorPos) +        { +            // Load one MPQ sector into archive buffer +            nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); +            if(nError != ERROR_SUCCESS) +                return nError; + +            // Remember that the data loaded to the sector have new file offset +            hf->dwSectorOffs = dwFileSectorPos; +        } + +        // Check number of bytes read +        if(dwToCopy > dwBytesToRead) +            dwToCopy = dwBytesToRead; + +        // Copy the data from the cached last sector to the caller's buffer +        memcpy(pbBuffer, hf->pbFileSector, dwToCopy); +         +        // Update pointers +        dwTotalBytesRead += dwToCopy; +    } + +    // Store total number of bytes read to the caller +    *pdwBytesRead = dwTotalBytesRead; +    return ERROR_SUCCESS; +} + +static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ +    DWORD dwBytesToRead = dwToRead; +    DWORD dwBytesRead = 0; +    int nError = ERROR_SUCCESS; + +    // Make sure that the patch file is loaded completely +    if(hf->pbFileData == NULL) +    { +        // Load the original file and store its content to "pbOldData" +        hf->pbFileData = STORM_ALLOC(BYTE, hf->pFileEntry->dwFileSize); +        hf->cbFileData = hf->pFileEntry->dwFileSize; +        if(hf->pbFileData == NULL) +            return ERROR_NOT_ENOUGH_MEMORY; + +        // Read the file data +        if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) +            nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); +        else +            nError = ReadMpqFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); + +        // Fix error code +        if(nError == ERROR_SUCCESS && dwBytesRead != hf->cbFileData) +            nError = ERROR_FILE_CORRUPT; + +        // Patch the file data +        if(nError == ERROR_SUCCESS) +            nError = PatchFileData(hf); + +        // Reset number of bytes read to zero +        dwBytesRead = 0; +    } + +    // If there is something to read, do it +    if(nError == ERROR_SUCCESS) +    { +        if(dwFilePos < hf->cbFileData) +        { +            // Make sure we don't copy more than file size +            if((dwFilePos + dwToRead) > hf->cbFileData) +                dwToRead = hf->cbFileData - dwFilePos; + +            // Copy the appropriate amount of the file data to the caller's buffer +            memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead); +            dwBytesRead = dwToRead; +        } + +        // Set the proper error code +        nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; +    } + +    // Give the result to the caller +    if(pdwBytesRead != NULL) +        *pdwBytesRead = dwBytesRead; +    return nError; +} + +//----------------------------------------------------------------------------- +// SFileReadFile + +bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped) +{ +    TMPQFile * hf = (TMPQFile *)hFile; +    DWORD dwBytesRead = 0;                      // Number of bytes read +    int nError = ERROR_SUCCESS; + +    // Keep compilers happy +    lpOverlapped = lpOverlapped; + +    // Check valid parameters +    if(!IsValidFileHandle(hf)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return false; +    } + +    if(pvBuffer == NULL) +    { +        SetLastError(ERROR_INVALID_PARAMETER); +        return false; +    } + +    // If the file is local file, read the data directly from the stream +    if(hf->pStream != NULL) +    { +        ULONGLONG FilePosition1; +        ULONGLONG FilePosition2; + +        // Because stream I/O functions are designed to read +        // "all or nothing", we compare file position before and after, +        // and if they differ, we assume that number of bytes read +        // is the difference between them + +        FileStream_GetPos(hf->pStream, &FilePosition1); +        if(!FileStream_Read(hf->pStream, NULL, pvBuffer, dwToRead)) +        { +            // If not all bytes have been read, then return the number +            // of bytes read +            if((nError = GetLastError()) == ERROR_HANDLE_EOF) +            { +                FileStream_GetPos(hf->pStream, &FilePosition2); +                dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); +            } +            else +            { +                nError = GetLastError(); +            } +        } +        else +        { +            dwBytesRead = dwToRead; +        } +    } +    else +    { +        // If the file is a patch file, we have to read it special way +        if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) +        { +            nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); +        } + +        // If the file is single unit file, redirect it to read file  +        else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) +        { +            nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); +        } + +        // Otherwise read it as sector based MPQ file +        else +        {                                                                    +            nError = ReadMpqFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); +        } + +        // Increment the file position +        hf->dwFilePos += dwBytesRead; +    } + +    // Give the caller the number of bytes read +    if(pdwRead != NULL) +        *pdwRead = dwBytesRead; + +    // If the read operation succeeded, but not full number of bytes was read, +    // set the last error to ERROR_HANDLE_EOF +    if(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead)) +        nError = ERROR_HANDLE_EOF; + +    // If something failed, set the last error value +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// SFileGetFileSize + +DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) +{ +    ULONGLONG FileSize; +    TMPQFile * hf = (TMPQFile *)hFile; + +    // Validate the file handle before we go on +    if(IsValidFileHandle(hf)) +    { +        // Make sure that the variable is initialized +        FileSize = 0; + +        // If the file is patched file, we have to get the size of the last version +        if(hf->hfPatchFile != NULL) +        { +            // Walk through the entire patch chain, take the last version +            while(hf != NULL) +            { +                // Get the size of the currently pointed version +                FileSize = hf->pFileEntry->dwFileSize; + +                // Move to the next patch file in the hierarchy +                hf = hf->hfPatchFile; +            } +        } +        else +        { +            // Is it a local file ? +            if(hf->pStream != NULL) +            { +                FileStream_GetSize(hf->pStream, &FileSize); +            } +            else +            { +                FileSize = hf->dwDataSize; +            } +        } + +        // If opened from archive, return file size +        if(pdwFileSizeHigh != NULL) +            *pdwFileSizeHigh = (DWORD)(FileSize >> 32); +        return (DWORD)FileSize; +    } + +    SetLastError(ERROR_INVALID_HANDLE); +    return SFILE_INVALID_SIZE; +} + +DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) +{ +    TMPQFile * hf = (TMPQFile *)hFile; +    ULONGLONG FilePosition; +    ULONGLONG MoveOffset; +    DWORD dwFilePosHi; + +    // If the hFile is not a valid file handle, return an error. +    if(!IsValidFileHandle(hf)) +    { +        SetLastError(ERROR_INVALID_HANDLE); +        return SFILE_INVALID_POS; +    } + +    // Get the relative point where to move from +    switch(dwMoveMethod) +    { +        case FILE_BEGIN: +            FilePosition = 0; +            break; + +        case FILE_CURRENT: +            if(hf->pStream != NULL) +            { +                FileStream_GetPos(hf->pStream, &FilePosition); +            } +            else +            { +                FilePosition = hf->dwFilePos; +            } +            break; + +        case FILE_END: +            if(hf->pStream != NULL) +            { +                FileStream_GetSize(hf->pStream, &FilePosition); +            } +            else +            { +                FilePosition = SFileGetFileSize(hFile, NULL); +            } +            break; + +        default: +            SetLastError(ERROR_INVALID_PARAMETER); +            return SFILE_INVALID_POS; +    } + +    // Now get the move offset. Note that both values form +    // a signed 64-bit value (a file pointer can be moved backwards) +    if(plFilePosHigh != NULL) +        dwFilePosHi = *plFilePosHigh; +    else +        dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0; +    MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos); + +    // Now calculate the new file pointer +    // Do not allow the file pointer to go before the begin of the file +    FilePosition += MoveOffset; +    if(FilePosition < 0) +        FilePosition = 0; + +    // Now apply the file pointer to the file +    if(hf->pStream != NULL) +    { +        // Apply the new file position +        if(!FileStream_Read(hf->pStream, &FilePosition, NULL, 0)) +            return SFILE_INVALID_POS; + +        // Return the new file position +        if(plFilePosHigh != NULL) +            *plFilePosHigh = (LONG)(FilePosition >> 32); +        return (DWORD)FilePosition; +    } +    else +    { +        // Files in MPQ can't be bigger than 4 GB. +        // We don't allow to go past 4 GB +        if(FilePosition >> 32) +        { +            SetLastError(ERROR_INVALID_PARAMETER); +            return SFILE_INVALID_POS; +        } + +        // Change the file position +        hf->dwFilePos = (DWORD)FilePosition; + +        // Return the new file position +        if(plFilePosHigh != NULL) +            *plFilePosHigh = 0; +        return (DWORD)FilePosition; +    } +} + +//----------------------------------------------------------------------------- +// Tries to retrieve the file name + +static TFileHeader2Ext data2ext[] =  +{ +    {0x00005A4D, 0x0000FFFF, 0x00000000, 0x00000000, "exe"},    // EXE files +    {0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, "dc6"},    // EXE files +    {0x1A51504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mpq"},    // MPQ archive header ID ('MPQ\x1A') +    {0x46464952, 0xFFFFFFFF, 0x00000000, 0x00000000, "wav"},    // WAVE header 'RIFF' +    {0x324B4D53, 0xFFFFFFFF, 0x00000000, 0x00000000, "smk"},    // Old "Smacker Video" files 'SMK2' +    {0x694B4942, 0xFFFFFFFF, 0x00000000, 0x00000000, "bik"},    // Bink video files (new) +    {0x0801050A, 0xFFFFFFFF, 0x00000000, 0x00000000, "pcx"},    // PCX images used in Diablo I +    {0x544E4F46, 0xFFFFFFFF, 0x00000000, 0x00000000, "fnt"},    // Font files used in Diablo II +    {0x6D74683C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"},   // HTML '<htm' +    {0x4D54483C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"},   // HTML '<HTM +    {0x216F6F57, 0xFFFFFFFF, 0x00000000, 0x00000000, "tbl"},    // Table files +    {0x31504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"},    // BLP textures +    {0x32504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"},    // BLP textures (v2) +    {0x584C444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mdx"},    // MDX files +    {0x45505954, 0xFFFFFFFF, 0x00000000, 0x00000000, "pud"},    // Warcraft II maps +    {0x38464947, 0xFFFFFFFF, 0x00000000, 0x00000000, "gif"},    // GIF images 'GIF8' +    {0x3032444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "m2"},     // WoW ??? .m2 +    {0x43424457, 0xFFFFFFFF, 0x00000000, 0x00000000, "dbc"},    // ??? .dbc +    {0x47585053, 0xFFFFFFFF, 0x00000000, 0x00000000, "bls"},    // WoW pixel shaders +    {0xE0FFD8FF, 0xFFFFFFFF, 0x00000000, 0x00000000, "jpg"},    // JPEG image +    {0x00000000, 0x00000000, 0x00000000, 0x00000000, "xxx"},    // Default extension +    {0, 0, 0, 0, NULL}                                          // Terminator  +}; + +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) +{ +    TFileEntry * pFileEntry; +    TMPQFile * hf = (TMPQFile *)hFile;  // MPQ File handle +    char szPseudoName[20];     +    DWORD FirstBytes[2];                // The first 4 bytes of the file +    DWORD dwFilePos;                    // Saved file position +    int nError = ERROR_SUCCESS; +    int i; + +    // Pre-zero the output buffer +    if(szFileName != NULL) +        *szFileName = 0; + +    // Check valid parameters +    if(!IsValidFileHandle(hf)) +        nError = ERROR_INVALID_HANDLE; +    pFileEntry = hf->pFileEntry; +     +    // Only do something if the file name is not filled +    if(nError == ERROR_SUCCESS && pFileEntry != NULL && pFileEntry->szFileName == NULL) +    { +        // Read the first 2 DWORDs bytes from the file +        FirstBytes[0] = FirstBytes[1] = 0; +        dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT);    +        SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), NULL, NULL); +        BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); +        SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN); + +        // Try to guess file extension from those 2 DWORDs +        for(i = 0; data2ext[i].szExt != NULL; i++) +        { +            if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && +               (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) +            { +                sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); +                break; +            } +        } + +        // Put the file name to the file table +        AllocateFileName(pFileEntry, szPseudoName); +    }  + +    // Now put the file name to the file structure +    if(nError == ERROR_SUCCESS && szFileName != NULL) +    { +        if(pFileEntry != NULL && pFileEntry->szFileName != NULL) +            strcpy(szFileName, pFileEntry->szFileName); +        else if(hf->pStream != NULL) +            CopyFileName(szFileName, FileStream_GetFileName(hf->pStream)); +    } +    return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Retrieves an information about an archive or about a file within the archive +// +//  hMpqOrFile - Handle to an MPQ archive or to a file +//  dwInfoType - Information to obtain + +#define VERIFY_MPQ_HANDLE(h)                \ +    if(!IsValidMpqHandle(h))                \ +    {                                       \ +        nError = ERROR_INVALID_HANDLE;      \ +        break;                              \ +    } + +#define VERIFY_FILE_HANDLE(h)               \ +    if(!IsValidFileHandle(h))               \ +    {                                       \ +        nError = ERROR_INVALID_HANDLE;      \ +        break;                              \ +    } + +bool WINAPI SFileGetFileInfo( +    HANDLE hMpqOrFile, +    DWORD dwInfoType, +    void * pvFileInfo, +    DWORD cbFileInfo, +    LPDWORD pcbLengthNeeded) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpqOrFile; +    TMPQBlock * pBlock; +    TMPQFile * hf = (TMPQFile *)hMpqOrFile; +    void * pvSrcFileInfo = NULL; +    DWORD cbLengthNeeded = 0; +    DWORD dwIsReadOnly; +    DWORD dwFileCount = 0; +    DWORD dwFileIndex; +    DWORD dwFileKey; +    DWORD i; +    int nError = ERROR_SUCCESS; + +    switch(dwInfoType) +    { +        case SFILE_INFO_ARCHIVE_NAME: +            VERIFY_MPQ_HANDLE(ha); +             +            // pvFileInfo receives the name of the archive, terminated by 0 +            pvSrcFileInfo = FileStream_GetFileName(ha->pStream); +            cbLengthNeeded = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); +            break; + +        case SFILE_INFO_ARCHIVE_SIZE:       // Size of the archive +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &ha->pHeader->dwArchiveSize; +            break; + +        case SFILE_INFO_MAX_FILE_COUNT:     // Max. number of files in the MPQ +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &ha->dwMaxFileCount; +            break; + +        case SFILE_INFO_HASH_TABLE_SIZE:    // Size of the hash table +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &ha->pHeader->dwHashTableSize; +            break; + +        case SFILE_INFO_BLOCK_TABLE_SIZE:   // Size of the block table +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; +            break; + +        case SFILE_INFO_SECTOR_SIZE: +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &ha->dwSectorSize; +            break; + +        case SFILE_INFO_HASH_TABLE: +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); +            pvSrcFileInfo = ha->pHashTable; +            break; + +        case SFILE_INFO_BLOCK_TABLE: +            VERIFY_MPQ_HANDLE(ha); +            cbLengthNeeded = ha->dwFileTableSize * sizeof(TMPQBlock); +            if(cbFileInfo < cbLengthNeeded) +            { +                nError = ERROR_INSUFFICIENT_BUFFER; +                break; +            } + +            // Construct block table from file table size +            pBlock = (TMPQBlock *)pvFileInfo; +            for(i = 0; i < ha->dwFileTableSize; i++) +            { +                pBlock->dwFilePos = (DWORD)ha->pFileTable[i].ByteOffset; +                pBlock->dwFSize   = ha->pFileTable[i].dwFileSize; +                pBlock->dwCSize   = ha->pFileTable[i].dwCmpSize; +                pBlock->dwFlags   = ha->pFileTable[i].dwFlags; +                pBlock++; +            } +            break; + +        case SFILE_INFO_NUM_FILES: +            VERIFY_MPQ_HANDLE(ha); +            dwFileCount = GetMpqFileCount(ha); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &dwFileCount; +            break; + +        case SFILE_INFO_STREAM_FLAGS: +            VERIFY_MPQ_HANDLE(ha); +            FileStream_GetFlags(ha->pStream, &dwFileKey); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &dwFileKey; +            break; + +        case SFILE_INFO_IS_READ_ONLY: +            VERIFY_MPQ_HANDLE(ha); +            dwIsReadOnly = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &dwIsReadOnly; +            break; + +        case SFILE_INFO_HASH_INDEX: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; +            break; + +        case SFILE_INFO_CODENAME1: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; +            if(ha->pHashTable != NULL) +                pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1; +            break; + +        case SFILE_INFO_CODENAME2: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            if(ha->pHashTable != NULL) +                pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2; +            break; + +        case SFILE_INFO_LOCALEID: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->lcLocale; +            break; + +        case SFILE_INFO_BLOCKINDEX: +            VERIFY_FILE_HANDLE(hf); +            dwFileIndex = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &dwFileIndex; +            break; + +        case SFILE_INFO_FILE_SIZE: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->dwFileSize; +            break; + +        case SFILE_INFO_COMPRESSED_SIZE: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; +            break; + +        case SFILE_INFO_FLAGS: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->pFileEntry->dwFlags; +            break; + +        case SFILE_INFO_POSITION: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(ULONGLONG); +            pvSrcFileInfo = &hf->pFileEntry->ByteOffset; +            break; + +        case SFILE_INFO_KEY: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &hf->dwFileKey; +            break; + +        case SFILE_INFO_KEY_UNFIXED: +            VERIFY_FILE_HANDLE(hf); +            dwFileKey = hf->dwFileKey; +            if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) +                dwFileKey = (dwFileKey ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; +            cbLengthNeeded = sizeof(DWORD); +            pvSrcFileInfo = &dwFileKey; +            break; + +        case SFILE_INFO_FILETIME: +            VERIFY_FILE_HANDLE(hf); +            cbLengthNeeded = sizeof(ULONGLONG); +            pvSrcFileInfo = &hf->pFileEntry->FileTime; +            break; + +        case SFILE_INFO_PATCH_CHAIN: +            VERIFY_FILE_HANDLE(hf); +            GetFilePatchChain(hf, pvFileInfo, cbFileInfo, &cbLengthNeeded); +            break; + +        default: +            nError = ERROR_INVALID_PARAMETER; +            break; +    } + +    // If everything is OK so far, copy the information +    if(nError == ERROR_SUCCESS) +    { +        // Is the output buffer large enough? +        if(cbFileInfo >= cbLengthNeeded) +        { +            // Copy the data +            if(pvSrcFileInfo != NULL) +                memcpy(pvFileInfo, pvSrcFileInfo, cbLengthNeeded); +        } +        else +        { +            nError = ERROR_INSUFFICIENT_BUFFER; +        } + +        // Give the size to the caller +        if(pcbLengthNeeded != NULL) +            *pcbLengthNeeded = cbLengthNeeded; +    } + +    // Set the last error value, if needed +    if(nError != ERROR_SUCCESS) +        SetLastError(nError); +    return (nError == ERROR_SUCCESS); +} diff --git a/src/SFileVerify.cpp b/src/SFileVerify.cpp new file mode 100644 index 0000000..1354bfc --- /dev/null +++ b/src/SFileVerify.cpp @@ -0,0 +1,922 @@ +/*****************************************************************************/ +/* SFileVerify.cpp                        Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* MPQ files and MPQ archives verification.                                  */ +/*                                                                           */ +/* The MPQ signature verification has been written by Jean-Francois Roy      */ +/* <bahamut@macstorm.org> and Justin Olbrantz (Quantam).                     */ +/* The MPQ public keys have been created by MPQKit, using OpenSSL library.   */ +/*                                                                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 04.05.10  1.00  Lad  The first version of SFileVerify.cpp                 */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local defines + +#define SIGNATURE_TYPE_NONE             0 +#define SIGNATURE_TYPE_WEAK             1 +#define SIGNATURE_TYPE_STRONG           2 + +#define MPQ_DIGEST_UNIT_SIZE      0x10000 + +typedef struct _MPQ_SIGNATURE_INFO +{ +    ULONGLONG BeginMpqData;                 // File offset where the hashing starts +    ULONGLONG BeginExclude;                 // Begin of the excluded area (used for (signature) file) +    ULONGLONG EndExclude;                   // End of the excluded area (used for (signature) file) +    ULONGLONG EndMpqData;                   // File offset where the hashing ends +    ULONGLONG EndOfFile;                    // Size of the entire file +    BYTE  Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10]; +    DWORD cbSignatureSize;                  // Length of the signature +    int nSignatureType;                     // See SIGNATURE_TYPE_XXX + +} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO; + +//----------------------------------------------------------------------------- +// Known Blizzard public keys +// Created by Jean-Francois Roy using OpenSSL + +static const char * szBlizzardWeakPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe" +    "2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ==" +    "-----END PUBLIC KEY-----"; + +static const char * szBlizzardStrongPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd" +    "tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD" +    "Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp" +    "kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm" +    "Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW" +    "lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk" +    "dwIDAQAB" +    "-----END PUBLIC KEY-----"; + +static const char * szWarcraft3MapPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5" +    "yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3" +    "iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0" +    "1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS" +    "gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b" +    "heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74" +    "6QIDAQAB" +    "-----END PUBLIC KEY-----"; + +static const char * szWowPatchPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9" +    "6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa" +    "5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ" +    "bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c" +    "yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y" +    "UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv" +    "TwIDAQAB" +    "-----END PUBLIC KEY-----"; + +static const char * szWowSurveyPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe" +    "MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c" +    "63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU" +    "BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt" +    "zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a" +    "vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr" +    "nQIDAQAB" +    "-----END PUBLIC KEY-----"; + +static const char * szStarcraft2MapPublicKey = +    "-----BEGIN PUBLIC KEY-----" +    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB" +    "q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq" +    "2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT" +    "E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ" +    "7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0" +    "31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z" +    "nQIDAQAB" +    "-----END PUBLIC KEY-----"; + +//----------------------------------------------------------------------------- +// Local functions + +static void memrev(unsigned char *buf, size_t count) +{ +    unsigned char *r; + +    for (r = buf + count - 1; buf < r; buf++, r--) +    { +        *buf ^= *r; +        *r   ^= *buf; +        *buf ^= *r; +    } +} + +static bool is_valid_md5(void * pvMd5) +{ +    LPDWORD Md5 = (LPDWORD)pvMd5; + +    return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false; +} + +static bool decode_base64_key(const char * szKeyBase64, rsa_key * key) +{ +    unsigned char decoded_key[0x200]; +    const char * szBase64Begin; +    const char * szBase64End; +    unsigned long decoded_length = sizeof(decoded_key); +    unsigned long length; + +    // Find out the begin of the BASE64 data +    szBase64Begin = szKeyBase64 + strlen("-----BEGIN PUBLIC KEY-----"); +    szBase64End   = szBase64Begin + strlen(szBase64Begin) - strlen("-----END PUBLIC KEY-----"); +    if(szBase64End[0] != '-') +        return false; + +    // decode the base64 string +    length = (unsigned long)(szBase64End - szBase64Begin); +    if(base64_decode((unsigned char *)szBase64Begin, length, decoded_key, &decoded_length) != CRYPT_OK) +        return false; + +    // Create RSA key +    if(rsa_import(decoded_key, decoded_length, key) != CRYPT_OK) +        return false; + +    return true; +} + +static void GetPlainAnsiFileName( +    const TCHAR * szFileName, +    char * szPlainName) +{ +    const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName); + +    // Convert the plain name to ANSI +    while(*szPlainNameT != 0) +        *szPlainName++ = (char)*szPlainNameT++; +    *szPlainName = 0; +} + +// Calculate begin and end of the MPQ archive +static void CalculateArchiveRange( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI) +{ +    ULONGLONG TempPos = 0; +    char szMapHeader[0x200]; + +    // Get the MPQ begin +    pSI->BeginMpqData = ha->MpqPos; + +    // Warcraft III maps are signed from the map header to the end +    if(FileStream_Read(ha->pStream, &TempPos, szMapHeader, sizeof(szMapHeader))) +    { +        // Is it a map header ? +        if(szMapHeader[0] == 'H' && szMapHeader[1] == 'M' && szMapHeader[2] == '3' && szMapHeader[3] == 'W') +        { +            // We will have to hash since the map header +            pSI->BeginMpqData = 0; +        } +    } + +    // Get the MPQ data end. This is stored in our MPQ header, +    // and it's been already prepared by SFileOpenArchive, +    pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64; + +    // Get the size of the entire file +    FileStream_GetSize(ha->pStream, &pSI->EndOfFile); +} + +static bool QueryMpqSignatureInfo( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI) +{ +    ULONGLONG ExtraBytes; +    TMPQFile * hf; +    HANDLE hFile; +    DWORD dwFileSize; + +    // Calculate the range of the MPQ +    CalculateArchiveRange(ha, pSI); + +    // If there is "(signature)" file in the MPQ, it has a weak signature +    if(SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_BASE_FILE, &hFile)) +    { +        // Get the content of the signature +        SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize, NULL); + +        // Verify the size of the signature +        hf = (TMPQFile *)hFile; + +        // We have to exclude the signature file from the digest +        pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset; +        pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize; +        dwFileSize = hf->dwDataSize; + +        // Close the file +        SFileCloseFile(hFile); +        pSI->nSignatureType = SIGNATURE_TYPE_WEAK; +        return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false; +    } + +    // If there is extra bytes beyond the end of the archive, +    // it's the strong signature +    ExtraBytes = pSI->EndOfFile - pSI->EndMpqData; +    if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4)) +    { +        // Read the strong signature +        if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4))) +            return false; + +        // Check the signature header "NGIS" +        if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S') +            return false; + +        pSI->nSignatureType = SIGNATURE_TYPE_STRONG; +        return true; +    } + +    // Succeeded, but no known signature found +    return true; +} + +static bool CalculateMpqHashMd5( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI, +    LPBYTE pMd5Digest) +{ +    hash_state md5_state; +    ULONGLONG BeginBuffer; +    ULONGLONG EndBuffer; +    LPBYTE pbDigestBuffer = NULL; + +    // Allocate buffer for creating the MPQ digest. +    pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE); +    if(pbDigestBuffer == NULL) +        return false; + +    // Initialize the MD5 hash state +    md5_init(&md5_state); + +    // Set the byte offset of begin of the data +    BeginBuffer = pSI->BeginMpqData; + +    // Create the digest +    for(;;) +    { +        ULONGLONG BytesRemaining; +        LPBYTE pbSigBegin = NULL; +        LPBYTE pbSigEnd = NULL; +        DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE; + +        // Check the number of bytes remaining +        BytesRemaining = pSI->EndMpqData - BeginBuffer; +        if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE) +            dwToRead = (DWORD)BytesRemaining; +        if(dwToRead == 0) +            break; + +        // Read the next chunk  +        if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) +        { +            STORM_FREE(pbDigestBuffer); +            return false; +        } + +        // Move the current byte offset +        EndBuffer = BeginBuffer + dwToRead; + +        // Check if the signature is within the loaded digest +        if(BeginBuffer <= pSI->BeginExclude && pSI->BeginExclude < EndBuffer) +            pbSigBegin = pbDigestBuffer + (size_t)(pSI->BeginExclude - BeginBuffer); +        if(BeginBuffer <= pSI->EndExclude && pSI->EndExclude < EndBuffer) +            pbSigEnd = pbDigestBuffer + (size_t)(pSI->EndExclude - BeginBuffer); + +        // Zero the part that belongs to the signature +        if(pbSigBegin != NULL || pbSigEnd != NULL) +        { +            if(pbSigBegin == NULL) +                pbSigBegin = pbDigestBuffer; +            if(pbSigEnd == NULL) +                pbSigEnd = pbDigestBuffer + dwToRead; + +            memset(pbSigBegin, 0, (pbSigEnd - pbSigBegin)); +        } + +        // Pass the buffer to the hashing function +        md5_process(&md5_state, pbDigestBuffer, dwToRead); + +        // Move pointers +        BeginBuffer += dwToRead; +    } + +    // Finalize the MD5 hash +    md5_done(&md5_state, pMd5Digest); +    STORM_FREE(pbDigestBuffer); +    return true; +} + +static void AddTailToSha1( +    hash_state * psha1_state, +    const char * szTail) +{ +    unsigned char * pbTail = (unsigned char *)szTail; +    unsigned char szUpperCase[0x200]; +    unsigned long nLength = 0; + +    // Convert the tail to uppercase +    // Note that we don't need to terminate the string with zero +    while(*pbTail != 0) +    { +        szUpperCase[nLength++] = AsciiToUpperTable[*pbTail++]; +    } + +    // Append the tail to the SHA1 +    sha1_process(psha1_state, szUpperCase, nLength); +} + +static bool CalculateMpqHashSha1( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI, +    unsigned char * sha1_tail0, +    unsigned char * sha1_tail1, +    unsigned char * sha1_tail2) +{ +    ULONGLONG BeginBuffer; +    hash_state sha1_state_temp; +    hash_state sha1_state; +    LPBYTE pbDigestBuffer = NULL; +    char szPlainName[MAX_PATH]; + +    // Allocate buffer for creating the MPQ digest. +    pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE); +    if(pbDigestBuffer == NULL) +        return false; + +    // Initialize SHA1 state structure +    sha1_init(&sha1_state); + +    // Calculate begin of data to be hashed +    BeginBuffer = pSI->BeginMpqData; + +    // Create the digest +    for(;;) +    { +        ULONGLONG BytesRemaining; +        DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE; + +        // Check the number of bytes remaining +        BytesRemaining = pSI->EndMpqData - BeginBuffer; +        if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE) +            dwToRead = (DWORD)BytesRemaining; +        if(dwToRead == 0) +            break; + +        // Read the next chunk  +        if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) +        { +            STORM_FREE(pbDigestBuffer); +            return false; +        } + +        // Pass the buffer to the hashing function +        sha1_process(&sha1_state, pbDigestBuffer, dwToRead); + +        // Move pointers +        BeginBuffer += dwToRead; +    } + +    // Add all three known tails and generate three hashes +    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); +    sha1_done(&sha1_state_temp, sha1_tail0); + +    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); +    GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName); +    AddTailToSha1(&sha1_state_temp, szPlainName); +    sha1_done(&sha1_state_temp, sha1_tail1); + +    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); +    AddTailToSha1(&sha1_state_temp, "ARCHIVE"); +    sha1_done(&sha1_state_temp, sha1_tail2); + +    // Finalize the MD5 hash +    STORM_FREE(pbDigestBuffer); +    return true; +} + +static int VerifyRawMpqData( +    TMPQArchive * ha, +    ULONGLONG ByteOffset, +    DWORD dwDataSize) +{ +    ULONGLONG DataOffset = ha->MpqPos + ByteOffset; +    LPBYTE pbDataChunk; +    LPBYTE pbMD5Array1;                 // Calculated MD5 array +    LPBYTE pbMD5Array2;                 // MD5 array loaded from the MPQ +    DWORD dwBytesInChunk; +    DWORD dwChunkCount; +    DWORD dwChunkSize = ha->pHeader->dwRawChunkSize; +    DWORD dwMD5Size; +    int nError = ERROR_SUCCESS; + +    // Don't verify zero-sized blocks +    if(dwDataSize == 0) +        return ERROR_SUCCESS; + +    // Get the number of data chunks to calculate MD5 +    assert(dwChunkSize != 0); +    dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1; +    dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE; + +    // Allocate space for data chunk and for the MD5 array +    pbDataChunk = STORM_ALLOC(BYTE, dwChunkSize); +    if(pbDataChunk == NULL) +        return ERROR_NOT_ENOUGH_MEMORY; + +    // Allocate space for MD5 array +    pbMD5Array1 = STORM_ALLOC(BYTE, dwMD5Size); +    pbMD5Array2 = STORM_ALLOC(BYTE, dwMD5Size); +    if(pbMD5Array1 == NULL || pbMD5Array2 == NULL) +        nError = ERROR_NOT_ENOUGH_MEMORY; + +    // Calculate MD5 of each data chunk +    if(nError == ERROR_SUCCESS) +    { +        LPBYTE pbMD5 = pbMD5Array1; + +        for(DWORD i = 0; i < dwChunkCount; i++) +        { +            // Get the number of bytes in the chunk +            dwBytesInChunk = STORMLIB_MIN(dwChunkSize, dwDataSize); + +            // Read the data chunk +            if(!FileStream_Read(ha->pStream, &DataOffset, pbDataChunk, dwBytesInChunk)) +            { +                nError = ERROR_FILE_CORRUPT; +                break; +            } + +            // Calculate MD5 +            CalculateDataBlockHash(pbDataChunk, dwBytesInChunk, pbMD5); + +            // Move pointers and offsets +            DataOffset += dwBytesInChunk; +            dwDataSize -= dwBytesInChunk; +            pbMD5 += MD5_DIGEST_SIZE; +        } +    } + +    // Read the MD5 array +    if(nError == ERROR_SUCCESS) +    { +        // Read the array of MD5 +        if(!FileStream_Read(ha->pStream, &DataOffset, pbMD5Array2, dwMD5Size)) +            nError = GetLastError(); +    } + +    // Compare the array of MD5 +    if(nError == ERROR_SUCCESS) +    { +        // Compare the MD5 +        if(memcmp(pbMD5Array1, pbMD5Array2, dwMD5Size)) +            nError = ERROR_FILE_CORRUPT; +    } + +    // Free memory and return result +    if(pbMD5Array2 != NULL) +        STORM_FREE(pbMD5Array2); +    if(pbMD5Array1 != NULL) +        STORM_FREE(pbMD5Array1); +    if(pbDataChunk != NULL) +        STORM_FREE(pbDataChunk); +    return nError; +} + +static DWORD VerifyWeakSignature( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI) +{ +    BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE]; +    BYTE Md5Digest[MD5_DIGEST_SIZE]; +    rsa_key key; +    int hash_idx = find_hash("md5"); +    int result = 0; + +    // Calculate hash of the entire archive, skipping the (signature) file +    if(!CalculateMpqHashMd5(ha, pSI, Md5Digest)) +        return ERROR_VERIFY_FAILED; + +    // Import the Blizzard key in OpenSSL format +    if(!decode_base64_key(szBlizzardWeakPublicKey, &key)) +        return ERROR_VERIFY_FAILED; + +    // Verify the signature +    memcpy(RevSignature, &pSI->Signature[8], MPQ_WEAK_SIGNATURE_SIZE); +    memrev(RevSignature, MPQ_WEAK_SIGNATURE_SIZE); +    rsa_verify_hash_ex(RevSignature, MPQ_WEAK_SIGNATURE_SIZE, Md5Digest, sizeof(Md5Digest), LTC_LTC_PKCS_1_V1_5, hash_idx, 0, &result, &key); +    rsa_free(&key); + +    // Return the result +    return result ? ERROR_WEAK_SIGNATURE_OK : ERROR_WEAK_SIGNATURE_ERROR; +} + +static DWORD VerifyStrongSignatureWithKey( +    unsigned char * reversed_signature, +    unsigned char * padded_digest, +    const char * szPublicKey) +{ +    rsa_key key; +    int result = 0; + +    // Import the Blizzard key in OpenSSL format +    if(!decode_base64_key(szPublicKey, &key)) +    { +        assert(false); +        return ERROR_VERIFY_FAILED; +    } + +    // Verify the signature +    if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK) +        return ERROR_VERIFY_FAILED; +     +    // Free the key and return result +    rsa_free(&key); +    return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR; +} + +static DWORD VerifyStrongSignature( +    TMPQArchive * ha, +    PMPQ_SIGNATURE_INFO pSI) +{ +    unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE]; +    unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE]; +    unsigned char Sha1Digest_tail1[SHA1_DIGEST_SIZE]; +    unsigned char Sha1Digest_tail2[SHA1_DIGEST_SIZE]; +    unsigned char padded_digest[MPQ_STRONG_SIGNATURE_SIZE]; +    DWORD dwResult; +    size_t digest_offset; + +    // Calculate SHA1 hash of the archive +    if(!CalculateMpqHashSha1(ha, pSI, Sha1Digest_tail0, Sha1Digest_tail1, Sha1Digest_tail2)) +        return ERROR_VERIFY_FAILED; + +    // Prepare the signature for decryption +    memcpy(reversed_signature, &pSI->Signature[4], MPQ_STRONG_SIGNATURE_SIZE); +    memrev(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE); + +    // Prepare the padded digest for comparison +    digest_offset = sizeof(padded_digest) - SHA1_DIGEST_SIZE; +    memset(padded_digest, 0xbb, digest_offset); +    padded_digest[0] = 0x0b; + +    // Try Blizzard Strong public key with no SHA1 tail +    memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE); +    memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE); +    dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szBlizzardStrongPublicKey); +    if(dwResult == ERROR_STRONG_SIGNATURE_OK) +        return dwResult; + +    // Try War 3 map public key with plain file name as SHA1 tail +    memcpy(padded_digest + digest_offset, Sha1Digest_tail1, SHA1_DIGEST_SIZE); +    memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE); +    dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWarcraft3MapPublicKey); +    if(dwResult == ERROR_STRONG_SIGNATURE_OK) +        return dwResult; + +    // Try WoW-TBC public key with "ARCHIVE" as SHA1 tail +    memcpy(padded_digest + digest_offset, Sha1Digest_tail2, SHA1_DIGEST_SIZE); +    memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE); +    dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowPatchPublicKey); +    if(dwResult == ERROR_STRONG_SIGNATURE_OK) +        return dwResult; + +    // Try Survey public key with no SHA1 tail +    memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE); +    memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE); +    dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowSurveyPublicKey); +    if(dwResult == ERROR_STRONG_SIGNATURE_OK) +        return dwResult; + +    // Try Starcraft II public key with no SHA1 tail +    memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE); +    memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE); +    dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szStarcraft2MapPublicKey); +    if(dwResult == ERROR_STRONG_SIGNATURE_OK) +        return dwResult; + +    return ERROR_STRONG_SIGNATURE_ERROR; +} + +static DWORD VerifyFile( +    HANDLE hMpq, +    const char * szFileName, +    LPDWORD pdwCrc32, +    char * pMD5, +    DWORD dwFlags) +{ +    hash_state md5_state; +    unsigned char * pFileMd5; +    unsigned char md5[MD5_DIGEST_SIZE]; +    TFileEntry * pFileEntry; +    TMPQFile * hf; +    BYTE Buffer[0x1000]; +    HANDLE hFile = NULL; +    DWORD dwVerifyResult = 0; +    DWORD dwTotalBytes = 0; +    DWORD dwBytesRead; +    DWORD dwCrc32 = 0; + +    // +    // Note: When the MPQ is patched, it will +    // automatically check the patched version of the file +    // + +    // If we have to verify raw data MD5, do it before file open +    if(dwFlags & SFILE_VERIFY_RAW_MD5) +    { +        TMPQArchive * ha = (TMPQArchive *)hMpq; + +        // Parse the base MPQ and all patches +        while(ha != NULL) +        { +            // Does the archive have support for raw MD5? +            if(ha->pHeader->dwRawChunkSize != 0) +            { +                // The file has raw MD5 if the archive supports it +                dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5; + +                // Find file entry for the file +                pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); +                if(pFileEntry != NULL) +                { +                    // If the file's raw MD5 doesn't match, don't bother with more checks +                    if(VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS) +                        return dwVerifyResult | VERIFY_FILE_RAW_MD5_ERROR; +                } +            } + +            // Move to the next patch +            ha = ha->haPatch; +        } +    } + +    // Attempt to open the file +    if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile)) +    { +        // Get the file size +        hf = (TMPQFile *)hFile; +        pFileEntry = hf->pFileEntry; +        dwTotalBytes = SFileGetFileSize(hFile, NULL); + +        // Initialize the CRC32 and MD5 contexts +        md5_init(&md5_state); +        dwCrc32 = crc32(0, Z_NULL, 0); + +        // Also turn on sector checksum verification +        if(dwFlags & SFILE_VERIFY_SECTOR_CRC) +            hf->bCheckSectorCRCs = true; + +        // Go through entire file and update both CRC32 and MD5 +        for(;;) +        { +            // Read data from file +            SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); +            if(dwBytesRead == 0) +            { +                if(GetLastError() == ERROR_CHECKSUM_ERROR) +                    dwVerifyResult |= VERIFY_FILE_SECTOR_CRC_ERROR; +                break; +            } + +            // Update CRC32 value +            if(dwFlags & SFILE_VERIFY_FILE_CRC) +                dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); +             +            // Update MD5 value +            if(dwFlags & SFILE_VERIFY_FILE_MD5) +                md5_process(&md5_state, Buffer, dwBytesRead); + +            // Decrement the total size +            dwTotalBytes -= dwBytesRead; +        } + +        // If the file has sector checksums, indicate it in the flags +        if(dwFlags & SFILE_VERIFY_SECTOR_CRC) +        { +            if((hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->SectorChksums != NULL && hf->SectorChksums[0] != 0) +                dwVerifyResult |= VERIFY_FILE_HAS_SECTOR_CRC; +        } + +        // Check if the entire file has been read +        // No point in checking CRC32 and MD5 if not +        // Skip checksum checks if the file has patches +        if(dwTotalBytes == 0) +        { +            // Check CRC32 and MD5 only if there is no patches +            if(hf->hfPatchFile == NULL) +            { +                // Check if the CRC32 matches. +                if(dwFlags & SFILE_VERIFY_FILE_CRC) +                { +                    // Only check the CRC32 if it is valid +                    if(pFileEntry->dwCrc32 != 0) +                    { +                        dwVerifyResult |= VERIFY_FILE_HAS_CHECKSUM; +                        if(dwCrc32 != pFileEntry->dwCrc32) +                            dwVerifyResult |= VERIFY_FILE_CHECKSUM_ERROR; +                    } +                } + +                // Check if MD5 matches +                if(dwFlags & SFILE_VERIFY_FILE_MD5) +                { +                    // Patch files have their MD5 saved in the patch info +                    pFileMd5 = (hf->pPatchInfo != NULL) ? hf->pPatchInfo->md5 : pFileEntry->md5; +                    md5_done(&md5_state, md5); + +                    // Only check the MD5 if it is valid +                    if(is_valid_md5(pFileMd5)) +                    { +                        dwVerifyResult |= VERIFY_FILE_HAS_MD5; +                        if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE)) +                            dwVerifyResult |= VERIFY_FILE_MD5_ERROR; +                    } +                } +            } +            else +            { +                // Patched files are MD5-checked automatically +                dwVerifyResult |= VERIFY_FILE_HAS_MD5; +            } +        } +        else +        { +            dwVerifyResult |= VERIFY_READ_ERROR; +        } + +        SFileCloseFile(hFile); +    } +    else +    { +        // Remember that the file couldn't be open +        dwVerifyResult |= VERIFY_OPEN_ERROR; +    } + +    // If the caller required CRC32 and/or MD5, give it to him +    if(pdwCrc32 != NULL) +        *pdwCrc32 = dwCrc32; +    if(pMD5 != NULL) +        memcpy(pMD5, md5, MD5_DIGEST_SIZE);  + +    return dwVerifyResult; +} + +//----------------------------------------------------------------------------- +// Public (exported) functions + +bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5) +{ +    DWORD dwVerifyResult; +    DWORD dwVerifyFlags = 0; + +    if(pdwCrc32 != NULL) +        dwVerifyFlags |= SFILE_VERIFY_FILE_CRC; +    if(pMD5 != NULL) +        dwVerifyFlags |= SFILE_VERIFY_FILE_MD5; + +    dwVerifyResult = VerifyFile(hMpq, +                                szFileName, +                                pdwCrc32, +                                pMD5, +                                dwVerifyFlags); + +    // If verification failed, return zero +    if(dwVerifyResult & VERIFY_FILE_ERROR_MASK) +    { +        SetLastError(ERROR_FILE_CORRUPT); +        return false; +    } + +    return true; +} + + +DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags) +{ +    return VerifyFile(hMpq, +                      szFileName, +                      NULL, +                      NULL, +                      dwFlags); +} + +// Verifies raw data of the archive Only works for MPQs version 4 or newer +int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName) +{ +    TMPQArchive * ha = (TMPQArchive *)hMpq; +    TFileEntry * pFileEntry; +    TMPQHeader * pHeader; + +    // Verify input parameters +    if(!IsValidMpqHandle(ha)) +        return ERROR_INVALID_PARAMETER; +    pHeader = ha->pHeader; + +    // If the archive doesn't have raw data MD5, report it as OK +    if(pHeader->dwRawChunkSize == 0) +        return ERROR_SUCCESS; + +    // If we have to verify MPQ header, do it +    switch(dwWhatToVerify) +    { +        case SFILE_VERIFY_MPQ_HEADER: +             +            // Only if the header is of version 4 or newer +            if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE)) +                return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE); +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_HET_TABLE: + +            // Only if we have HET table +            if(pHeader->HetTablePos64 && pHeader->HetTableSize64) +                return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64); +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_BET_TABLE: + +            // Only if we have BET table +            if(pHeader->BetTablePos64 && pHeader->BetTableSize64) +                return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64); +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_HASH_TABLE: + +            // Hash table is not protected by MD5 +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_BLOCK_TABLE: + +            // Block table is not protected by MD5 +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_HIBLOCK_TABLE: + +            // It is unknown if the hi-block table is protected my MD5 or not. +            return ERROR_SUCCESS; + +        case SFILE_VERIFY_FILE: + +            // Verify parameters +            if(szFileName == NULL || *szFileName == 0) +                return ERROR_INVALID_PARAMETER; + +            // Get the offset of a file +            pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); +            if(pFileEntry == NULL) +                return ERROR_FILE_NOT_FOUND; + +            return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize); +    } + +    return ERROR_INVALID_PARAMETER; +} + + +// Verifies the archive against the signature +DWORD WINAPI SFileVerifyArchive(HANDLE hMpq) +{ +    MPQ_SIGNATURE_INFO si; +    TMPQArchive * ha = (TMPQArchive *)hMpq; + +    // Verify input parameters +    if(!IsValidMpqHandle(ha)) +        return ERROR_VERIFY_FAILED; + +    // Get the MPQ signature and signature type +    memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO)); +    if(!QueryMpqSignatureInfo(ha, &si)) +        return ERROR_VERIFY_FAILED; + +    // Verify the signature +    switch(si.nSignatureType) +    { +        case SIGNATURE_TYPE_NONE: +            return ERROR_NO_SIGNATURE; + +        case SIGNATURE_TYPE_WEAK: +            return VerifyWeakSignature(ha, &si); + +        case SIGNATURE_TYPE_STRONG: +            return VerifyStrongSignature(ha, &si); +    } + +    return ERROR_VERIFY_FAILED; +} diff --git a/src/StormCommon.h b/src/StormCommon.h new file mode 100644 index 0000000..2d5c2ab --- /dev/null +++ b/src/StormCommon.h @@ -0,0 +1,281 @@ +/*****************************************************************************/ +/* SCommon.h                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Common functions for encryption/decryption from Storm.dll. Included by    */ +/* SFile*** functions, do not include and do not use this file directly      */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 24.03.03  1.00  Lad  The first version of SFileCommon.h                   */ +/* 12.06.04  1.00  Lad  Renamed to SCommon.h                                 */ +/* 06.09.10  1.00  Lad  Renamed to StormCommon.h                             */ +/*****************************************************************************/ + +#ifndef __STORMCOMMON_H__ +#define __STORMCOMMON_H__ + +//----------------------------------------------------------------------------- +// Compression support + +// Include functions from Pkware Data Compression Library +#include "pklib/pklib.h" + +// Include functions from Huffmann compression +#include "huffman/huff.h" + +// Include functions from IMA ADPCM compression +#include "adpcm/adpcm.h" + +// Include functions from SPARSE compression +#include "sparse/sparse.h" + +// Include functions from LZMA compression +#include "lzma/C/LzmaEnc.h" +#include "lzma/C/LzmaDec.h" + +// Include functions from zlib +#ifndef __SYS_ZLIB +  #include "zlib/zlib.h" +#else +  #include <zlib.h> +#endif + +// Include functions from bzlib +#ifndef __SYS_BZLIB +  #include "bzip2/bzlib.h" +#else +  #include <bzlib.h> +#endif + +//----------------------------------------------------------------------------- +// Cryptography support + +// Headers from LibTomCrypt +#include "libtomcrypt/src/headers/tomcrypt.h" + +// For HashStringJenkins +#include "jenkins/lookup.h" + +//----------------------------------------------------------------------------- +// StormLib private defines + +#define ID_MPQ_FILE            0x46494c45     // Used internally for checking TMPQFile ('FILE') + +#define MPQ_WEAK_SIGNATURE_SIZE        64 +#define MPQ_STRONG_SIGNATURE_SIZE     256  + +// Prevent problems with CRT "min" and "max" functions, +// as they are not defined on all platforms +#define STORMLIB_MIN(a, b) ((a < b) ? a : b) +#define STORMLIB_MAX(a, b) ((a > b) ? a : b) +#define STORMLIB_UNUSED(p) ((void)(p)) + +// Macro for building 64-bit file offset from two 32-bit +#define MAKE_OFFSET64(hi, lo)      (((ULONGLONG)hi << 32) | lo) + +//----------------------------------------------------------------------------- +// Memory management +// +// We use our own macros for allocating/freeing memory. If you want +// to redefine them, please keep the following rules +// +//  - The memory allocation must return NULL if not enough memory +//    (i.e not to throw exception) +//  - It is not necessary to fill the allocated buffer with zeros +//  - Memory freeing function doesn't have to test the pointer to NULL. +// + +#if defined(_MSC_VER) && defined(_DEBUG) +__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize) +{ +//  return new BYTE[nSize]; +    return HeapAlloc(GetProcessHeap(), 0, nSize); +} + +__inline void DebugFree(void * ptr) +{ +//  delete [] ptr; +    HeapFree(GetProcessHeap(), 0, ptr); +} + +#define STORM_ALLOC(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type)) +#define STORM_FREE(ptr)           DebugFree(ptr) +#else + +#define STORM_ALLOC(type, nitems)   (type *)malloc((nitems) * sizeof(type)) +#define STORM_FREE(ptr) free(ptr) + +#endif + +//----------------------------------------------------------------------------- +// StormLib internal global variables + +extern LCID lcFileLocale;                       // Preferred file locale + +//----------------------------------------------------------------------------- +// Conversion to uppercase/lowercase (and "/" to "\") + +extern unsigned char AsciiToLowerTable[256]; +extern unsigned char AsciiToUpperTable[256]; + +//----------------------------------------------------------------------------- +// Encryption and decryption functions + +#define MPQ_HASH_TABLE_INDEX    0x000 +#define MPQ_HASH_NAME_A         0x100 +#define MPQ_HASH_NAME_B         0x200 +#define MPQ_HASH_FILE_KEY       0x300 + +DWORD HashString(const char * szFileName, DWORD dwHashType); + +void  InitializeMpqCryptography(); + +DWORD GetHashTableSizeForFileCount(DWORD dwFileCount); + +bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex); +ULONGLONG HashStringJenkins(const char * szFileName); + +int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); + +DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize); + +void  EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); +void  DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); + +DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted); +DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize); +DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags); + +bool IsValidMD5(LPBYTE pbMd5); +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); + +//----------------------------------------------------------------------------- +// Handle validation functions + +bool IsValidMpqHandle(TMPQArchive * ha); +bool IsValidFileHandle(TMPQFile * hf); + +//----------------------------------------------------------------------------- +// Hash table and block table manipulation + +TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName); +TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash); +DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry); + +void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos); + +// Functions that loads and verifies MPQ data bitmap +int  LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete); + +// Functions that load the HET and BET tables +int  CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize); +int  LoadAnyHashTable(TMPQArchive * ha); +int  BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize); +int  SaveMPQTables(TMPQArchive * ha); + +TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty); +void FreeHetTable(TMPQHetTable * pHetTable); + +TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount); +void FreeBetTable(TMPQBetTable * pBetTable); + +// Functions for finding files in the file table +TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName); +TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale); +TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale); +TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex); + +// Allocates file name in the file entry +void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName); + +// Allocates new file entry in the MPQ tables. Reuses existing, if possible +TFileEntry * FindFreeFileEntry(TMPQArchive * ha); +TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale); +int  RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName); +void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +int  FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); + +// Invalidates entries for (listfile) and (attributes) +void InvalidateInternalFiles(TMPQArchive * ha); + +//----------------------------------------------------------------------------- +// Common functions - MPQ File + +TMPQFile * CreateMpqFile(TMPQArchive * ha); +int  LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey); +int  AllocateSectorBuffer(TMPQFile * hf); +int  AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); +int  AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); +int  AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile); +void CalculateRawSectorOffset(ULONGLONG & RawFilePos, TMPQFile * hf, DWORD dwSectorOffset); +int  WritePatchInfo(TMPQFile * hf); +int  WriteSectorOffsets(TMPQFile * hf); +int  WriteSectorChecksums(TMPQFile * hf); +int  WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize); +int  WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize); +void FreeMPQFile(TMPQFile *& hf); + +bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize); +int  PatchFileData(TMPQFile * hf); + +void FreeMPQArchive(TMPQArchive *& ha); + +//----------------------------------------------------------------------------- +// Utility functions + +bool CheckWildCard(const char * szString, const char * szWildCard); +const char * GetPlainFileNameA(const char * szFileName); +const TCHAR * GetPlainFileNameT(const TCHAR * szFileName); +bool IsInternalMpqFileName(const char * szFileName); + +//----------------------------------------------------------------------------- +// Support for adding files to the MPQ + +int SFileAddFile_Init( +    TMPQArchive * ha, +    const char * szArchivedName, +    ULONGLONG ft, +    DWORD dwFileSize, +    LCID lcLocale, +    DWORD dwFlags, +    TMPQFile ** phf +    ); + +int SFileAddFile_Write( +    TMPQFile * hf, +    const void * pvData, +    DWORD dwSize, +    DWORD dwCompression +    ); + +int SFileAddFile_Finish( +    TMPQFile * hf +    ); + +//----------------------------------------------------------------------------- +// Attributes support + +int  SAttrLoadAttributes(TMPQArchive * ha); +int  SAttrFileSaveToMpq(TMPQArchive * ha); + +//----------------------------------------------------------------------------- +// Listfile functions + +int  SListFileSaveToMpq(TMPQArchive * ha); + +//----------------------------------------------------------------------------- +// Dump data support + +#ifdef __STORMLIB_DUMP_DATA__ +void DumpMpqHeader(TMPQHeader * pHeader); +void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable); + +#else +#define DumpMpqHeader(h)           /* */ +#define DumpHetAndBetTable(h, b)   /* */ +#endif + +#endif // __STORMCOMMON_H__ + diff --git a/src/StormLib.h b/src/StormLib.h new file mode 100644 index 0000000..db813d3 --- /dev/null +++ b/src/StormLib.h @@ -0,0 +1,988 @@ +/*****************************************************************************/ +/* StormLib.h                        Copyright (c) Ladislav Zezula 1999-2010 */ +/*---------------------------------------------------------------------------*/ +/* StormLib library v 7.02                                                   */ +/*                                                                           */ +/* Author : Ladislav Zezula                                                  */ +/* E-mail : ladik@zezula.net                                                 */ +/* WWW    : http://www.zezula.net                                            */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.99  1.00  Lad  Created                                              */ +/* 24.03.03  2.50  Lad  Version 2.50                                         */ +/* 02.04.03  3.00  Lad  Version 3.00 with compression                        */ +/* 11.04.03  3.01  Lad  Renamed to StormLib.h for compatibility with         */ +/*                      original headers for Storm.dll                       */ +/* 10.05.03  3.02  Lad  Added Pkware DCL compression                         */ +/* 26.05.03  4.00  Lad  Completed all compressions                           */ +/* 18.06.03  4.01  Lad  Added SFileSetFileLocale                             */ +/*                      Added SFileExtractFile                               */ +/* 26.07.03  4.02  Lad  Implemented nameless rename and delete               */ +/* 26.07.03  4.03  Lad  Added support for protected MPQs                     */ +/* 28.08.03  4.10  Lad  Fixed bugs that caused StormLib incorrectly work     */ +/*                      with Diablo I savegames and with files having full   */ +/*                      hash table                                           */ +/* 08.12.03  4.11  DCH  Fixed bug in reading file sector larger than 0x1000  */ +/*                      on certain files.                                    */ +/*                      Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING  */ +/*                      (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/ +/* 21.12.03  4.50  Lad  Completed port for Mac                               */ +/*                      Fixed bug in compacting (if fsize is mul of 0x1000)  */ +/*                      Fixed bug in SCompCompress                           */ +/* 27.05.04  4.51  Lad  Changed memory management from new/delete to our     */ +/*                      own macros                                           */ +/* 22.06.04  4.60  Lad  Optimized search. Support for multiple listfiles.    */ +/* 30.09.04  4.61  Lad  Fixed some bugs (Aaargh !!!)                         */ +/*                      Correctly works if HashTableSize > BlockTableSize    */ +/* 29.12.04  4.70  Lad  Fixed compatibility problem with MPQs from WoW       */ +/* 14.07.05  5.00  Lad  Added the BZLIB compression support                  */ +/*                      Added suport of files stored as single unit          */ +/* 17.04.06  5.01  Lad  Converted to MS Visual Studio 8.0                    */ +/*                      Fixed issue with protected Warcraft 3 protected maps */ +/* 15.05.06  5.02  Lad  Fixed issue with WoW 1.10+                           */ +/* 07.09.06  5.10  Lad  Fixed processing files longer than 2GB               */ +/* 22.11.06  6.00  Lad  Support for MPQ archives V2                          */ +/* 12.06.07  6.10  Lad  Support for (attributes) file                        */ +/* 10.09.07  6.12  Lad  Support for MPQs protected by corrupting hash table  */ +/* 03.12.07  6.13  Lad  Support for MPQs with hash tbl size > block tbl size */ +/* 07.04.08  6.20  Lad  Added SFileFlushArchive                              */ +/* 09.04.08        Lad  Removed FilePointer variable from MPQ handle         */ +/*                      structure, as it caused more problems than benefits  */ +/* 12.05.08  6.22  Lad  Support for w3xMaster map protector                  */ +/* 05.10.08  6.23  Lad  Support for protectors who set negative values in    */ +/*                      the table of file blocks                             */ +/* 26.05.09  6.24  Lad  Fixed search for multiple lang files with deleted    */ +/*                      entries                                              */ +/* 03.09.09  6.25  Lad  Fixed decompression bug in huffmann decompression    */ +/* 22.03.10  6.50  Lad  New compressions in Starcraft II (LZMA, sparse)      */ +/*                      Fixed compacting MPQs that contain single unit files */ +/* 26.04.10  7.00  Lad  Major rewrite                                        */ +/* 08.06.10  7.10  Lad  Support for partial MPQs                             */ +/* 08.07.10  7.11  Lad  Support for MPQs v 3.0                               */ +/* 20.08.10  7.20  Lad  Support for opening multiple MPQs in patch mode      */ +/* 20.09.10  8.00  Lad  MPQs v 4, HET and BET tables                         */ +/* 07.01.11  8.01  Lad  Write support for MPQs v 3 and 4                     */ +/* 15.09.11  8.04  Lad  Bug fixes, testing for Diablo III MPQs               */ +/* 26.04.12  8.10  Lad  Support for data map, added SFileGetArchiveBitmap    */ +/* 29.05.12  8.20  Lad  C-only interface                                     */ +/*****************************************************************************/ + +#ifndef __STORMLIB_H__ +#define __STORMLIB_H__ + +#ifdef _MSC_VER +#pragma warning(disable:4668)       // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'  +#pragma warning(disable:4820)       // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' +#endif                                              + +#include "StormPort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------------- +// Use the apropriate library +// +// The library type is encoded in the library name as the following +// StormLibXYZ.lib +//  +//  X - D for Debug version, R for Release version +//  Y - A for ANSI version, U for Unicode version +//  Z - S for static-linked CRT library, D for multithreaded DLL CRT library +// + +#if defined(_MSC_VER) && !defined(__STORMLIB_SELF__) +   +  #ifdef _DEBUG                                 // DEBUG VERSIONS +    #ifndef _UNICODE                             +      #ifdef _DLL                                +        #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version +      #else         +        #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version +      #endif +    #else +      #ifdef _DLL                                +        #pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version +      #else         +        #pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version +      #endif +    #endif +  #else                                         // RELEASE VERSIONS +    #ifndef _UNICODE                             +      #ifdef _DLL +        #pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version +      #else         +        #pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version +      #endif +    #else +      #ifdef _DLL +        #pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version +      #else         +        #pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version +      #endif +    #endif +  #endif + +#endif + +//----------------------------------------------------------------------------- +// Defines + +#define ID_MPQ                      0x1A51504D  // MPQ archive header ID ('MPQ\x1A') +#define ID_MPQ_USERDATA             0x1B51504D  // MPQ userdata entry ('MPQ\x1B') + +#define ERROR_AVI_FILE                   10000  // No MPQ file, but AVI file. +#define ERROR_UNKNOWN_FILE_KEY           10001  // Returned by SFileReadFile when can't find file key +#define ERROR_CHECKSUM_ERROR             10002  // Returned by SFileReadFile when sector CRC doesn't match +#define ERROR_INTERNAL_FILE              10003  // The given operation is not allowed on internal file +#define ERROR_BASE_FILE_MISSING          10004  // The file is present as incremental patch file, but base file is missing +#define ERROR_MARKED_FOR_DELETE          10005  // The file was marked as "deleted" in the MPQ + +// Values for SFileCreateArchive +#define HASH_TABLE_SIZE_MIN         0x00000004  // Minimum acceptable hash table size +#define HASH_TABLE_SIZE_DEFAULT     0x00001000  // Default hash table size for empty MPQs +#define HASH_TABLE_SIZE_MAX         0x00080000  // Maximum acceptable hash table size + +#define HASH_ENTRY_DELETED          0xFFFFFFFE  // Block index for deleted entry in the hash table +#define HASH_ENTRY_FREE             0xFFFFFFFF  // Block index for free entry in the hash table + +#define HET_ENTRY_DELETED                 0x80  // HET hash value for a deleted entry +#define HET_ENTRY_FREE                    0x00  // HET hash value for free entry + +#define HASH_STATE_SIZE                   0x60  // Size of LibTomCrypt's hash_state structure + +#define MPQ_PATCH_PREFIX_LEN              0x20  // Maximum length of the patch prefix + +// Values for SFileOpenArchive +#define SFILE_OPEN_HARD_DISK_FILE            2  // Open the archive on HDD +#define SFILE_OPEN_CDROM_FILE                3  // Open the archive only if it is on CDROM + +// Values for SFileOpenFile +#define SFILE_OPEN_FROM_MPQ         0x00000000  // Open the file from the MPQ archive +#define SFILE_OPEN_BASE_FILE        0xFFFFFFFD  // Reserved for StormLib internal use +#define SFILE_OPEN_ANY_LOCALE       0xFFFFFFFE  // Reserved for StormLib internal use +#define SFILE_OPEN_LOCAL_FILE       0xFFFFFFFF  // Open a local file + +// Flags for TMPQArchive::dwFlags +#define MPQ_FLAG_READ_ONLY          0x00000001  // If set, the MPQ has been open for read-only access +#define MPQ_FLAG_CHANGED            0x00000002  // If set, the MPQ tables have been changed +#define MPQ_FLAG_PROTECTED          0x00000004  // Set on protected MPQs (like W3M maps) +#define MPQ_FLAG_CHECK_SECTOR_CRC   0x00000008  // Checking sector CRC when reading files +#define MPQ_FLAG_NEED_FIX_SIZE      0x00000010  // Used during opening the archive +#define MPQ_FLAG_INV_LISTFILE       0x00000020  // If set, it means that the (listfile) has been invalidated +#define MPQ_FLAG_INV_ATTRIBUTES     0x00000040  // If set, it means that the (attributes) has been invalidated + +// Return value for SFileGetFileSize and SFileSetFilePointer +#define SFILE_INVALID_SIZE          0xFFFFFFFF +#define SFILE_INVALID_POS           0xFFFFFFFF +#define SFILE_INVALID_ATTRIBUTES    0xFFFFFFFF + +// Flags for SFileAddFile +#define MPQ_FILE_IMPLODE            0x00000100  // Implode method (By PKWARE Data Compression Library) +#define MPQ_FILE_COMPRESS           0x00000200  // Compress methods (By multiple methods) +#define MPQ_FILE_COMPRESSED         0x0000FF00  // File is compressed +#define MPQ_FILE_ENCRYPTED          0x00010000  // Indicates whether file is encrypted  +#define MPQ_FILE_FIX_KEY            0x00020000  // File decryption key has to be fixed +#define MPQ_FILE_PATCH_FILE         0x00100000  // The file is a patch file. Raw file data begin with TPatchInfo structure +#define MPQ_FILE_SINGLE_UNIT        0x01000000  // File is stored as a single unit, rather than split into sectors (Thx, Quantam) +#define MPQ_FILE_DELETE_MARKER      0x02000000  // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists. +#define MPQ_FILE_SECTOR_CRC         0x04000000  // File has checksums for each sector. +                                                // Ignored if file is not compressed or imploded. +#define MPQ_FILE_EXISTS             0x80000000  // Set if file exists, reset when the file was deleted +#define MPQ_FILE_REPLACEEXISTING    0x80000000  // Replace when the file exist (SFileAddFile) + +#define MPQ_FILE_VALID_FLAGS     (MPQ_FILE_IMPLODE       |  \ +                                  MPQ_FILE_COMPRESS      |  \ +                                  MPQ_FILE_ENCRYPTED     |  \ +                                  MPQ_FILE_FIX_KEY       |  \ +                                  MPQ_FILE_PATCH_FILE    |  \ +                                  MPQ_FILE_SINGLE_UNIT   |  \ +                                  MPQ_FILE_DELETE_MARKER |  \ +                                  MPQ_FILE_SECTOR_CRC    |  \ +                                  MPQ_FILE_EXISTS) + +// Compression types for multiple compressions +#define MPQ_COMPRESSION_HUFFMANN          0x01  // Huffmann compression (used on WAVE files only) +#define MPQ_COMPRESSION_ZLIB              0x02  // ZLIB compression +#define MPQ_COMPRESSION_PKWARE            0x08  // PKWARE DCL compression +#define MPQ_COMPRESSION_BZIP2             0x10  // BZIP2 compression (added in Warcraft III) +#define MPQ_COMPRESSION_SPARSE            0x20  // Sparse compression (added in Starcraft 2) +#define MPQ_COMPRESSION_ADPCM_MONO        0x40  // IMA ADPCM compression (mono) +#define MPQ_COMPRESSION_ADPCM_STEREO      0x80  // IMA ADPCM compression (stereo) +#define MPQ_COMPRESSION_LZMA              0x12  // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags. +#define MPQ_COMPRESSION_NEXT_SAME   0xFFFFFFFF  // Same compression + +// Constants for SFileAddWave +#define MPQ_WAVE_QUALITY_HIGH                0  // Best quality, the worst compression +#define MPQ_WAVE_QUALITY_MEDIUM              1  // Medium quality, medium compression +#define MPQ_WAVE_QUALITY_LOW                 2  // Low quality, the best compression + +// Signatures for HET and BET table +#define HET_TABLE_SIGNATURE         0x1A544548  // 'HET\x1a' +#define BET_TABLE_SIGNATURE         0x1A544542  // 'BET\x1a' + +// Decryption keys for MPQ tables +#define MPQ_KEY_HASH_TABLE          0xC3AF3770  // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY) +#define MPQ_KEY_BLOCK_TABLE         0xEC83B3A3  // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY) + +// Block map defines +#define MPQ_DATA_BITMAP_SIGNATURE   0x33767470  // Signature of the MPQ data bitmap ('ptv3') + +// Constants for SFileGetFileInfo +#define SFILE_INFO_ARCHIVE_NAME              1  // MPQ size (value from header) +#define SFILE_INFO_ARCHIVE_SIZE              2  // MPQ size (value from header) +#define SFILE_INFO_MAX_FILE_COUNT            3  // Max number of files in the MPQ +#define SFILE_INFO_HASH_TABLE_SIZE           4  // Size of hash table, in entries +#define SFILE_INFO_BLOCK_TABLE_SIZE          5  // Number of entries in the block table +#define SFILE_INFO_SECTOR_SIZE               6  // Size of file sector (in bytes) +#define SFILE_INFO_HASH_TABLE                7  // Pointer to Hash table (TMPQHash *) +#define SFILE_INFO_BLOCK_TABLE               8  // Pointer to Block Table (TMPQBlock *) +#define SFILE_INFO_NUM_FILES                 9  // Real number of files within archive +#define SFILE_INFO_STREAM_FLAGS             10  // Stream flags for the MPQ. See STREAM_FLAG_XXX +#define SFILE_INFO_IS_READ_ONLY             11  // TRUE of the MPQ was open as read only +//------                                  +#define SFILE_INFO_HASH_INDEX              100  // Hash index of file in MPQ +#define SFILE_INFO_CODENAME1               101  // The first codename of the file +#define SFILE_INFO_CODENAME2               102  // The second codename of the file +#define SFILE_INFO_LOCALEID                103  // Locale ID of file in MPQ +#define SFILE_INFO_BLOCKINDEX              104  // Index to Block Table +#define SFILE_INFO_FILE_SIZE               105  // Original file size (from the block table) +#define SFILE_INFO_COMPRESSED_SIZE         106  // Compressed file size (from the block table) +#define SFILE_INFO_FLAGS                   107  // File flags +#define SFILE_INFO_POSITION                108  // File position within archive +                                                // Note: for current pointer in open MPQ file, +                                                // use SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT); +#define SFILE_INFO_KEY                     109  // File decryption key +#define SFILE_INFO_KEY_UNFIXED             110  // Decryption key not fixed to file pos and size +#define SFILE_INFO_FILETIME                111  // TMPQFileTime +#define SFILE_INFO_PATCH_CHAIN             112  // Chain of patches + +#define LISTFILE_NAME             "(listfile)"  // Name of internal listfile +#define SIGNATURE_NAME           "(signature)"  // Name of internal signature +#define ATTRIBUTES_NAME         "(attributes)"  // Name of internal attributes file +#define PATCH_METADATA_NAME "(patch_metadata)" + +#define STORMLIB_VERSION                0x0814  // Current version of StormLib (8.10) +#define STORMLIB_VERSION_STRING         "8.20" + +#define MPQ_FORMAT_VERSION_1                 0  // Up to The Burning Crusade +#define MPQ_FORMAT_VERSION_2                 1  // The Burning Crusade and newer  +#define MPQ_FORMAT_VERSION_3                 2  // WoW Cataclysm Beta +#define MPQ_FORMAT_VERSION_4                 3  // WoW Cataclysm and newer + +// Flags for MPQ attributes +#define MPQ_ATTRIBUTE_CRC32         0x00000001  // The "(attributes)" contains CRC32 for each file +#define MPQ_ATTRIBUTE_FILETIME      0x00000002  // The "(attributes)" contains file time for each file +#define MPQ_ATTRIBUTE_MD5           0x00000004  // The "(attributes)" contains MD5 for each file +#define MPQ_ATTRIBUTE_PATCH_BIT     0x00000008  // The "(attributes)" contains a patch bit for each file +#define MPQ_ATTRIBUTE_ALL           0x0000000F  // Summary mask + +#define MPQ_ATTRIBUTES_V1                  100  // (attributes) format version 1.00 + +// Flags for SFileOpenArchive +#define BASE_PROVIDER_FILE          0x00000000  // Base data source is a file +#define BASE_PROVIDER_MAP           0x00000001  // Base data source is memory-mapped file +#define BASE_PROVIDER_HTTP          0x00000002  // Base data source is a file on web server +#define BASE_PROVIDER_MASK          0x0000000F  // Mask for base provider value + +#define STREAM_PROVIDER_LINEAR      0x00000000  // Stream is linear with no offset mapping +#define STREAM_PROVIDER_PARTIAL     0x00000010  // Stream is partial file (.part) +#define STREAM_PROVIDER_ENCRYPTED   0x00000020  // Stream is an encrypted MPQ +#define STREAM_PROVIDER_MASK        0x000000F0  // Mask for stream provider value + +#define STREAM_FLAG_READ_ONLY       0x00000100  // Stream is read only +#define STREAM_FLAG_WRITE_SHARE     0x00000200  // Allow write sharing when open for write +#define STREAM_FLAG_MASK            0x0000FF00  // Mask for stream flags +#define STREAM_OPTIONS_MASK         0x0000FFFF  // Mask for all stream options + +#define MPQ_OPEN_NO_LISTFILE        0x00010000  // Don't load the internal listfile +#define MPQ_OPEN_NO_ATTRIBUTES      0x00020000  // Don't open the attributes +#define MPQ_OPEN_FORCE_MPQ_V1       0x00040000  // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header +#define MPQ_OPEN_CHECK_SECTOR_CRC   0x00080000  // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file + +// Deprecated +#define MPQ_OPEN_READ_ONLY          STREAM_FLAG_READ_ONLY +#define MPQ_OPEN_ENCRYPTED          STREAM_PROVIDER_ENCRYPTED + +// Flags for SFileCreateArchive +#define MPQ_CREATE_ATTRIBUTES       0x00100000  // Also add the (attributes) file +#define MPQ_CREATE_ARCHIVE_V1       0x00000000  // Creates archive of version 1 (size up to 4GB) +#define MPQ_CREATE_ARCHIVE_V2       0x01000000  // Creates archive of version 2 (larger than 4 GB) +#define MPQ_CREATE_ARCHIVE_V3       0x02000000  // Creates archive of version 3 +#define MPQ_CREATE_ARCHIVE_V4       0x03000000  // Creates archive of version 4 +#define MPQ_CREATE_ARCHIVE_VMASK    0x0F000000  // Mask for archive version + +#define FLAGS_TO_FORMAT_SHIFT               24  // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4 + +// Flags for SFileVerifyFile +#define SFILE_VERIFY_SECTOR_CRC     0x00000001  // Verify sector checksum for the file, if available +#define SFILE_VERIFY_FILE_CRC       0x00000002  // Verify file CRC, if available +#define SFILE_VERIFY_FILE_MD5       0x00000004  // Verify file MD5, if available +#define SFILE_VERIFY_RAW_MD5        0x00000008  // Verify raw file MD5, if available +#define SFILE_VERIFY_ALL            0x0000000F  // Verify every checksum possible + +// Return values for SFileVerifyFile +#define VERIFY_OPEN_ERROR               0x0001  // Failed to open the file +#define VERIFY_READ_ERROR               0x0002  // Failed to read all data from the file +#define VERIFY_FILE_HAS_SECTOR_CRC      0x0004  // File has sector CRC +#define VERIFY_FILE_SECTOR_CRC_ERROR    0x0008  // Sector CRC check failed +#define VERIFY_FILE_HAS_CHECKSUM        0x0010  // File has CRC32 +#define VERIFY_FILE_CHECKSUM_ERROR      0x0020  // CRC32 check failed +#define VERIFY_FILE_HAS_MD5             0x0040  // File has data MD5 +#define VERIFY_FILE_MD5_ERROR           0x0080  // MD5 check failed +#define VERIFY_FILE_HAS_RAW_MD5         0x0100  // File has raw data MD5 +#define VERIFY_FILE_RAW_MD5_ERROR       0x0200  // Raw MD5 check failed +#define VERIFY_FILE_ERROR_MASK      (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR) + +// Flags for SFileVerifyRawData (for MPQs version 4.0 or higher) +#define SFILE_VERIFY_MPQ_HEADER         0x0001  // Verify raw MPQ header +#define SFILE_VERIFY_HET_TABLE          0x0002  // Verify raw data of the HET table +#define SFILE_VERIFY_BET_TABLE          0x0003  // Verify raw data of the BET table +#define SFILE_VERIFY_HASH_TABLE         0x0004  // Verify raw data of the hash table +#define SFILE_VERIFY_BLOCK_TABLE        0x0005  // Verify raw data of the block table +#define SFILE_VERIFY_HIBLOCK_TABLE      0x0006  // Verify raw data of the hi-block table +#define SFILE_VERIFY_FILE               0x0007  // Verify raw data of a file + +// Return values for SFileVerifyArchive +#define ERROR_NO_SIGNATURE                   0  // There is no signature in the MPQ +#define ERROR_VERIFY_FAILED                  1  // There was an error during verifying signature (like no memory) +#define ERROR_WEAK_SIGNATURE_OK              2  // There is a weak signature and sign check passed +#define ERROR_WEAK_SIGNATURE_ERROR           3  // There is a weak signature but sign check failed +#define ERROR_STRONG_SIGNATURE_OK            4  // There is a strong signature and sign check passed +#define ERROR_STRONG_SIGNATURE_ERROR         5  // There is a strong signature but sign check failed +                                            +#ifndef MD5_DIGEST_SIZE +#define MD5_DIGEST_SIZE                   0x10 +#endif + +#ifndef SHA1_DIGEST_SIZE +#define SHA1_DIGEST_SIZE                  0x14  // 160 bits +#endif + +#ifndef LANG_NEUTRAL +#define LANG_NEUTRAL                      0x00  // Neutral locale +#endif + +//----------------------------------------------------------------------------- +// Callback functions + +// Values for compact callback +#define CCB_CHECKING_FILES                   1  // Checking archive (dwParam1 = current, dwParam2 = total) +#define CCB_CHECKING_HASH_TABLE              2  // Checking hash table (dwParam1 = current, dwParam2 = total) +#define CCB_COPYING_NON_MPQ_DATA             3  // Copying non-MPQ data: No params used +#define CCB_COMPACTING_FILES                 4  // Compacting archive (dwParam1 = current, dwParam2 = total) +#define CCB_CLOSING_ARCHIVE                  5  // Closing archive: No params used +                                       +typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall); +typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); + +typedef struct TFileStream TFileStream; + +//----------------------------------------------------------------------------- +// Structure for bit arrays used for HET and BET tables + +typedef struct _TBitArray +{ +    DWORD NumberOfBits;                         // Total number of bits that are available +    BYTE Elements[1];                           // Array of elements (variable length) +} TBitArray; + +void GetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); +void SetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); + +// Structure for file bitmap. Used by SFileGetArchiveBitmap +typedef struct _TFileBitmap +{ +    ULONGLONG StartOffset;                      // Starting offset of the file, covered by bitmap +    ULONGLONG EndOffset;                        // Ending offset of the file, covered by bitmap +    DWORD IsComplete;                           // If nonzero, no blocks are missing +    DWORD BitmapSize;                           // Size of the file bitmap (in bytes) +    DWORD BlockSize;                            // Size of one block, in bytes +    DWORD Reserved;                             // Alignment + +    // Followed by file bitmap (variable length), array of BYTEs) +} TFileBitmap; + +//----------------------------------------------------------------------------- +// Structures related to MPQ format +// +// Note: All structures in this header file are supposed to remain private +// to StormLib. The structures may (and will) change over time, as the MPQ +// file format evolves. Programmers directly using these structures need to +// be aware of this. And the last, but not least, NEVER do any modifications +// to those structures directly, always use SFile* functions. +// + +#define MPQ_HEADER_SIZE_V1    0x20 +#define MPQ_HEADER_SIZE_V2    0x2C +#define MPQ_HEADER_SIZE_V3    0x44 +#define MPQ_HEADER_SIZE_V4    0xD0 + +typedef struct _TMPQUserData +{ +    // The ID_MPQ_USERDATA ('MPQ\x1B') signature +    DWORD dwID; + +    // Maximum size of the user data +    DWORD cbUserDataSize; + +    // Offset of the MPQ header, relative to the begin of this header +    DWORD dwHeaderOffs; + +    // Appears to be size of user data header (Starcraft II maps) +    DWORD cbUserDataHeader; +} TMPQUserData; + +// MPQ file header +// +// We have to make sure that the header is packed OK. +// Reason: A 64-bit integer at the beginning of 3.0 part, +// which is offset 0x2C +#pragma pack(push, 1) +typedef struct _TMPQHeader +{ +    // The ID_MPQ ('MPQ\x1A') signature +    DWORD dwID;                          + +    // Size of the archive header +    DWORD dwHeaderSize;                    + +    // 32-bit size of MPQ archive +    // This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive +    // is calculated as the size from the beginning of the archive to the end of the hash table, +    // block table, or hi-block table (whichever is largest). +    DWORD dwArchiveSize; + +    // 0 = Format 1 (up to The Burning Crusade) +    // 1 = Format 2 (The Burning Crusade and newer) +    // 2 = Format 3 (WoW - Cataclysm beta or newer) +    // 3 = Format 4 (WoW - Cataclysm beta or newer) +    USHORT wFormatVersion; + +    // Power of two exponent specifying the number of 512-byte disk sectors in each file sector +    // in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize. +    USHORT wSectorSize; + +    // Offset to the beginning of the hash table, relative to the beginning of the archive. +    DWORD dwHashTablePos; +     +    // Offset to the beginning of the block table, relative to the beginning of the archive. +    DWORD dwBlockTablePos; +     +    // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for +    // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. +    DWORD dwHashTableSize; +     +    // Number of entries in the block table +    DWORD dwBlockTableSize; + +    //-- MPQ HEADER v 2 ------------------------------------------- + +    // Offset to the beginning of array of 16-bit high parts of file offsets. +    ULONGLONG HiBlockTablePos64; + +    // High 16 bits of the hash table offset for large archives. +    USHORT wHashTablePosHi; + +    // High 16 bits of the block table offset for large archives. +    USHORT wBlockTablePosHi; + +    //-- MPQ HEADER v 3 ------------------------------------------- + +    // 64-bit version of the archive size +    ULONGLONG ArchiveSize64; + +    // 64-bit position of the BET table +    ULONGLONG BetTablePos64; + +    // 64-bit position of the HET table +    ULONGLONG HetTablePos64; + +    //-- MPQ HEADER v 4 ------------------------------------------- + +    // Compressed size of the hash table +    ULONGLONG HashTableSize64; + +    // Compressed size of the block table +    ULONGLONG BlockTableSize64; + +    // Compressed size of the hi-block table +    ULONGLONG HiBlockTableSize64; + +    // Compressed size of the HET block +    ULONGLONG HetTableSize64; + +    // Compressed size of the BET block +    ULONGLONG BetTableSize64; + +    // Size of raw data chunk to calculate MD5. +    // MD5 of each data chunk follows the raw file data. +    DWORD dwRawChunkSize;                                  + +    // MD5 of MPQ tables +    unsigned char MD5_BlockTable[MD5_DIGEST_SIZE];      // MD5 of the block table before decryption +    unsigned char MD5_HashTable[MD5_DIGEST_SIZE];       // MD5 of the hash table before decryption +    unsigned char MD5_HiBlockTable[MD5_DIGEST_SIZE];    // MD5 of the hi-block table +    unsigned char MD5_BetTable[MD5_DIGEST_SIZE];        // MD5 of the BET table before decryption +    unsigned char MD5_HetTable[MD5_DIGEST_SIZE];        // MD5 of the HET table before decryption +    unsigned char MD5_MpqHeader[MD5_DIGEST_SIZE];       // MD5 of the MPQ header from signature to (including) MD5_HetTable +} TMPQHeader; +#pragma pack(pop) + + +// Hash entry. All files in the archive are searched by their hashes. +typedef struct _TMPQHash +{ +    // The hash of the file path, using method A. +    DWORD dwName1; +     +    // The hash of the file path, using method B. +    DWORD dwName2; + +#ifdef PLATFORM_LITTLE_ENDIAN + +    // The language of the file. This is a Windows LANGID data type, and uses the same values. +    // 0 indicates the default language (American English), or that the file is language-neutral. +    USHORT lcLocale; + +    // The platform the file is used for. 0 indicates the default platform. +    // No other values have been observed. +    // Note: wPlatform is actually just BYTE, but since it has never been used, we don't care. +    USHORT wPlatform; + +#else + +    USHORT wPlatform; +    USHORT lcLocale; + +#endif + +    // If the hash table entry is valid, this is the index into the block table of the file. +    // Otherwise, one of the following two values: +    //  - FFFFFFFFh: Hash table entry is empty, and has always been empty. +    //               Terminates searches for a given file. +    //  - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file). +    //               Does not terminate searches for a given file. +    DWORD dwBlockIndex; +} TMPQHash; + + +// File description block contains informations about the file +typedef struct _TMPQBlock +{ +    // Offset of the beginning of the file, relative to the beginning of the archive. +    DWORD dwFilePos; +     +    // Compressed file size +    DWORD dwCSize; +     +    // Only valid if the block is a file; otherwise meaningless, and should be 0. +    // If the file is compressed, this is the size of the uncompressed file data. +    DWORD dwFSize;                       +     +    // Flags for the file. See MPQ_FILE_XXXX constants +    DWORD dwFlags;                       +} TMPQBlock; + +// Patch file information, preceding the sector offset table +typedef struct _TPatchInfo +{ +	DWORD dwLength;                     // Length of patch info header, in bytes +	DWORD dwFlags;                      // Flags. 0x80000000 = MD5 (?) +	DWORD dwDataSize;                   // Uncompressed size of the patch file +	BYTE  md5[0x10];                    // MD5 of the entire patch file after decompression + +    // Followed by the sector table (variable length) +} TPatchInfo; + +// Header for PTCH files  +typedef struct _TPatchHeader +{ +    //-- PATCH header ----------------------------------- +    DWORD dwSignature;                  // 'PTCH' +    DWORD dwSizeOfPatchData;            // Size of the entire patch (decompressed) +    DWORD dwSizeBeforePatch;            // Size of the file before patch +    DWORD dwSizeAfterPatch;             // Size of file after patch +     +    //-- MD5 block -------------------------------------- +    DWORD dwMD5;                        // 'MD5_' +    DWORD dwMd5BlockSize;               // Size of the MD5 block, including the signature and size itself +    BYTE md5_before_patch[0x10];        // MD5 of the original (unpached) file +    BYTE md5_after_patch[0x10];         // MD5 of the patched file + +    //-- XFRM block ------------------------------------- +    DWORD dwXFRM;                       // 'XFRM' +    DWORD dwXfrmBlockSize;              // Size of the XFRM block, includes XFRM header and patch data +    DWORD dwPatchType;                  // Type of patch ('BSD0' or 'COPY') + +    // Followed by the patch data +} TPatchHeader; + +#define SIZE_OF_XFRM_HEADER  0x0C + +// This is the combined file entry for maintaining file list in the MPQ. +// This structure is combined from block table, hi-block table, +// (attributes) file and from (listfile). +typedef struct _TFileEntry +{ +    ULONGLONG ByteOffset;               // Position of the file content in the MPQ, relative to the MPQ header +    ULONGLONG FileTime;                 // FileTime from the (attributes) file. 0 if not present. +    ULONGLONG BetHash;                  // Lower part of the file name hash. Only used when the MPQ has BET table. +    DWORD     dwHashIndex;              // Index to the hash table. Only used when the MPQ has classic hash table +    DWORD     dwHetIndex;               // Index to the HET table. Only used when the MPQ has HET table +    DWORD     dwFileSize;               // Decompressed size of the file +    DWORD     dwCmpSize;                // Compressed size of the file (i.e., size of the file data in the MPQ) +    DWORD     dwFlags;                  // File flags (from block table) +    USHORT    lcLocale;                 // Locale ID for the file +    USHORT    wPlatform;                // Platform ID for the file +    DWORD     dwCrc32;                  // CRC32 from (attributes) file. 0 if not present. +    unsigned char md5[MD5_DIGEST_SIZE]; // File MD5 from the (attributes) file. 0 if not present. +    char * szFileName;                  // File name. NULL if not known. +} TFileEntry; + +// Common header for HET and BET tables +typedef struct _TMPQExtTable +{ +    DWORD dwSignature;                  // 'HET\x1A' or 'BET\x1A' +    DWORD dwVersion;                    // Version. Seems to be always 1 +    DWORD dwDataSize;                   // Size of the contained table + +    // Followed by the table header +    // Followed by the table data + +} TMPQExtTable; + +// +// MPQ data bitmap, can be found at (FileSize - sizeof(TMPQBlockMap)) +// +// There is bit map of the entire MPQ before TMPQBitmap. Each 0x4000-byte +// block is represented by one bit (including the last, eventually incomplete block). +//  +typedef struct _TMPQBitmap +{ +    DWORD dwSignature;                  // 'ptv3' (MPQ_BLOCK_MAP_SIGNATURE) +    DWORD dwAlways3;                    // Unknown, seems to always have value of 3 +    DWORD dwBuildNumber;                // Game build number for that MPQ +    DWORD dwMapOffsetLo;                // Low 32-bits of the offset of the bit map +    DWORD dwMapOffsetHi;                // High 32-bits of the offset of the bit map +    DWORD dwBlockSize;                  // Size of one block (usually 0x4000 bytes) +} TMPQBitmap; + +// Structure for parsed HET table +typedef struct _TMPQHetTable +{ +    TBitArray * pBetIndexes;            // Bit array of indexes to BET tables +    LPBYTE     pHetHashes;              // Array of HET hashes. Each entry has size of 1 byte +    ULONGLONG  AndMask64;               // AND mask used for calculating file name hash +    ULONGLONG  OrMask64;                // OR mask used for setting the highest bit of the file name hash + +    DWORD      dwIndexSizeTotal;        // Total size of one entry in pBetIndexes (in bits) +    DWORD      dwIndexSizeExtra;        // Extra bits in the entry in pBetIndexes +    DWORD      dwIndexSize;             // Effective size of one entry in pBetIndexes (in bits) +    DWORD      dwMaxFileCount;          // Maximum number of files in the MPQ +    DWORD      dwHashTableSize;         // Number of entries in pBetHashes +    DWORD      dwHashBitSize;           // Effective number of bits in the hash +} TMPQHetTable; + +// Structure for parsed BET table +typedef struct _TMPQBetTable +{ +    TBitArray * pBetHashes;             // Array of BET hashes +    TBitArray * pFileTable;             // Bit-based file table +    LPDWORD pFileFlags;                 // Array of file flags + +    DWORD dwTableEntrySize;             // Size of one table entry, in bits +    DWORD dwBitIndex_FilePos;           // Bit index of the file position in the table entry +    DWORD dwBitIndex_FileSize;          // Bit index of the file size in the table entry +    DWORD dwBitIndex_CmpSize;           // Bit index of the compressed size in the table entry +    DWORD dwBitIndex_FlagIndex;         // Bit index of the flag index in the table entry +    DWORD dwBitIndex_Unknown;           // Bit index of ??? in the table entry +    DWORD dwBitCount_FilePos;           // Size of file offset (in bits) within table entry +    DWORD dwBitCount_FileSize;          // Size of file size (in bits) within table entry +    DWORD dwBitCount_CmpSize;           // Size of compressed file size (in bits) within table entry +    DWORD dwBitCount_FlagIndex;         // Size of flag index (in bits) within table entry +    DWORD dwBitCount_Unknown;           // Size of ??? (in bits) within table entry +    DWORD dwBetHashSizeTotal;           // Total size of bet hash +    DWORD dwBetHashSizeExtra;           // Extra bits in the bet hash +    DWORD dwBetHashSize;                // Effective size of the bet hash +    DWORD dwFileCount;                  // Number of files (usually equal to maximum number of files) +    DWORD dwFlagCount;                  // Number of entries in pFileFlags +} TMPQBetTable; + +// Archive handle structure +typedef struct _TMPQArchive +{ +    TFileStream  * pStream;             // Open stream for the MPQ + +    ULONGLONG      UserDataPos;         // Position of user data (relative to the begin of the file) +    ULONGLONG      MpqPos;              // MPQ header offset (relative to the begin of the file) + +    struct _TMPQArchive * haPatch;      // Pointer to patch archive, if any +    struct _TMPQArchive * haBase;       // Pointer to base ("previous version") archive, if any +    char szPatchPrefix[MPQ_PATCH_PREFIX_LEN]; // Prefix for file names in patch MPQs +    size_t         cchPatchPrefix;      // Length of the patch prefix, in characters + +    TMPQUserData * pUserData;           // MPQ user data (NULL if not present in the file) +    TMPQHeader   * pHeader;             // MPQ file header +    TMPQBitmap   * pBitmap;             // MPQ bitmap +    TMPQHash     * pHashTable;          // Hash table +    TMPQHetTable * pHetTable;           // Het table +    TFileEntry   * pFileTable;          // File table +     +    TMPQUserData   UserData;            // MPQ user data. Valid only when ID_MPQ_USERDATA has been found +    BYTE           HeaderData[MPQ_HEADER_SIZE_V4];  // Storage for MPQ header + +    DWORD          dwHETBlockSize; +    DWORD          dwBETBlockSize; +    DWORD          dwFileTableSize;     // Current size of the file table, e.g. index of the entry past the last occupied one +    DWORD          dwMaxFileCount;      // Maximum number of files in the MPQ +    DWORD          dwSectorSize;        // Default size of one file sector +    DWORD          dwFileFlags1;        // Flags for (listfile) +    DWORD          dwFileFlags2;        // Flags for (attributes) +    DWORD          dwAttrFlags;         // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX +    DWORD          dwFlags;             // See MPQ_FLAG_XXXXX +} TMPQArchive;                                       + +// File handle structure +typedef struct _TMPQFile +{ +    TFileStream  * pStream;             // File stream. Only used on local files +    TMPQArchive  * ha;                  // Archive handle +    TFileEntry   * pFileEntry;          // File entry for the file +    DWORD          dwFileKey;           // Decryption key +    DWORD          dwFilePos;           // Current file position +    ULONGLONG      RawFilePos;          // Offset in MPQ archive (relative to file begin) +    ULONGLONG      MpqFilePos;          // Offset in MPQ archive (relative to MPQ header) +    DWORD          dwMagic;             // 'FILE' + +    struct _TMPQFile * hfPatchFile;     // Pointer to opened patch file +    TPatchHeader * pPatchHeader;        // Patch header. Only used if the file is a patch file +    LPBYTE         pbFileData;          // Loaded and patched file data. Only used if the file is a patch file +    DWORD          cbFileData;          // Size of loaded patched data + +    TPatchInfo   * pPatchInfo;          // Patch info block, preceding the sector table +    DWORD        * SectorOffsets;       // Position of each file sector, relative to the begin of the file. Only for compressed files. +    DWORD        * SectorChksums;       // Array of sector checksums (either ADLER32 or MD5) values for each file sector +    DWORD          dwSectorCount;       // Number of sectors in the file +    DWORD          dwPatchedFileSize;   // Size of patched file. Used when saving patch file to the MPQ +    DWORD          dwDataSize;          // Size of data in the file (on patch files, this differs from file size in block table entry) + +    LPBYTE         pbFileSector;        // Last loaded file sector. For single unit files, entire file content +    DWORD          dwSectorOffs;        // File position of currently loaded file sector +    DWORD          dwSectorSize;        // Size of the file sector. For single unit files, this is equal to the file size + +    unsigned char  hctx[HASH_STATE_SIZE];// Hash state for MD5. Used when saving file to MPQ +    DWORD          dwCrc32;             // CRC32 value, used when saving file to MPQ + +    bool           bLoadedSectorCRCs;   // If true, we already tried to load sector CRCs +    bool           bCheckSectorCRCs;    // If true, then SFileReadFile will check sector CRCs when reading the file +    bool           bIsWriteHandle;      // If true, this handle has been created by SFileCreateFile +    bool           bErrorOccured;       // If true, then at least one error occured during saving the file to the archive +} TMPQFile; + +// Structure for SFileFindFirstFile and SFileFindNextFile +typedef struct _SFILE_FIND_DATA +{ +    char   cFileName[MAX_PATH];         // Full name of the found file +    char * szPlainName;                 // Plain name of the found file +    DWORD  dwHashIndex;                 // Hash table index for the file +    DWORD  dwBlockIndex;                // Block table index for the file +    DWORD  dwFileSize;                  // File size in bytes +    DWORD  dwFileFlags;                 // MPQ file flags +    DWORD  dwCompSize;                  // Compressed file size +    DWORD  dwFileTimeLo;                // Low 32-bits of the file time (0 if not present) +    DWORD  dwFileTimeHi;                // High 32-bits of the file time (0 if not present) +    LCID   lcLocale;                    // Locale version + +} SFILE_FIND_DATA, *PSFILE_FIND_DATA; + +typedef struct _SFILE_CREATE_MPQ +{ +    DWORD cbSize;                       // Size of this structure, in bytes +    DWORD dwMpqVersion;                 // Version of the MPQ to be created +    void *pvUserData;                   // Reserved, must be NULL +    DWORD cbUserData;                   // Reserved, must be 0 +    DWORD dwStreamFlags;                // Stream flags for creating the MPQ +    DWORD dwFileFlags1;                 // File flags for (listfile). 0 = default +    DWORD dwFileFlags2;                 // File flags for (attributes). 0 = default +    DWORD dwAttrFlags;                  // Flags for the (attributes) file. If 0, no attributes will be created +    DWORD dwSectorSize;                 // Sector size for compressed files +    DWORD dwRawChunkSize;               // Size of raw data chunk +    DWORD dwMaxFileCount;               // File limit for the MPQ + +} SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ; + +//----------------------------------------------------------------------------- +// Stream support - functions + +TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags); +TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags); +TCHAR * FileStream_GetFileName(TFileStream * pStream); +bool FileStream_IsReadOnly(TFileStream * pStream); +bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); +bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); +bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset); +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); +bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); +bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags); +bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream); +bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap); +bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded); +void FileStream_Close(TFileStream * pStream); + +//----------------------------------------------------------------------------- +// Functions prototypes for Storm.dll + +// Typedefs for functions exported by Storm.dll +typedef LCID  (WINAPI * SFILESETLOCALE)(LCID); +typedef bool  (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *); +typedef bool  (WINAPI * SFILECLOSEARCHIVE)(HANDLE); +typedef bool  (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *); +typedef bool  (WINAPI * SFILECLOSEFILE)(HANDLE); +typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD); +typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD); +typedef bool  (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED); + +//----------------------------------------------------------------------------- +// Functions for manipulation with StormLib global flags + +LCID   WINAPI SFileGetLocale(); +LCID   WINAPI SFileSetLocale(LCID lcNewLocale); + +//----------------------------------------------------------------------------- +// Functions for archive manipulation + +bool   WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); +bool   WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq); +bool   WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); + +bool   WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded); +bool   WINAPI SFileFlushArchive(HANDLE hMpq); +bool   WINAPI SFileCloseArchive(HANDLE hMpq); + +// Adds another listfile into MPQ. The currently added listfile(s) remain, +// so you can use this API to combining more listfiles. +// Note that this function is internally called by SFileFindFirstFile +int    WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile); + +// Archive compacting +bool   WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvData); +bool   WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool bReserved); + +// Changing the maximum file count +DWORD  WINAPI SFileGetMaxFileCount(HANDLE hMpq); +bool   WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount); + +// Changing (attributes) file +DWORD  WINAPI SFileGetAttributes(HANDLE hMpq); +bool   WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags); +bool   WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName); + +//----------------------------------------------------------------------------- +// Functions for manipulation with patch archives + +bool   WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); +bool   WINAPI SFileIsPatchedArchive(HANDLE hMpq); + +//----------------------------------------------------------------------------- +// Functions for file manipulation + +// Reading from MPQ file +bool   WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); +DWORD  WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh); +DWORD  WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); +bool   WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped); +bool   WINAPI SFileCloseFile(HANDLE hFile); + +// Retrieving info about the file +bool   WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName); +bool   WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); +bool   WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); + +// High-level extract function +bool   WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope); + +//----------------------------------------------------------------------------- +// Functions for file and archive verification + +// Generates file CRC32 +bool   WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5); + +// Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags). +// For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5 +DWORD  WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags); + +// Verifies raw data of the archive. Only works for MPQs version 4 or newer +int    WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName); + +// Verifies the signature, if present +DWORD  WINAPI SFileVerifyArchive(HANDLE hMpq); + +//----------------------------------------------------------------------------- +// Functions for file searching + +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile); +bool   WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +bool   WINAPI SFileFindClose(HANDLE hFind); + +HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); +bool   WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +bool   WINAPI SListFileFindClose(HANDLE hFind); + +// Locale support +int    WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope); + +//----------------------------------------------------------------------------- +// Support for adding files to the MPQ + +bool   WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); +bool   WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression); +bool   WINAPI SFileFinishFile(HANDLE hFile); + +bool   WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext); +bool   WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags);  +bool   WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality);  +bool   WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope); +bool   WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName); +bool   WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); +bool   WINAPI SFileSetDataCompression(DWORD DataCompression); + +bool   WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvData); + +//----------------------------------------------------------------------------- +// Compression and decompression + +int    WINAPI SCompImplode    (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int    WINAPI SCompExplode    (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int    WINAPI SCompCompress   (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); +int    WINAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int    WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); + +//----------------------------------------------------------------------------- +// Non-Windows support for SetLastError/GetLastError + +#ifndef PLATFORM_WINDOWS + +void  SetLastError(int err); +int   GetLastError(); + +#endif + +//----------------------------------------------------------------------------- +// Functions from Storm.dll. They use slightly different names for keeping +// possibility to use them together with StormLib (StormXXX instead of SFileXXX) + +#ifdef __LINK_STORM_DLL__ +  #define STORM_ALTERNATE_NAMES         // Force storm_dll.h to use alternate fnc names +  #include "..\storm_dll\storm_dll.h" +#endif // __LINK_STORM_DLL__ + +#ifdef __cplusplus +}   // extern "C" +#endif + +#endif  // __STORMLIB_H__ diff --git a/src/StormPort.h b/src/StormPort.h new file mode 100644 index 0000000..069033a --- /dev/null +++ b/src/StormPort.h @@ -0,0 +1,243 @@ +/*****************************************************************************/ +/* StormPort.h                           Copyright (c) Marko Friedemann 2001 */ +/*---------------------------------------------------------------------------*/ +/* Portability module for the StormLib library. Contains a wrapper symbols   */ +/* to make the compilation under Linux work                                  */ +/*                                                                           */ +/* Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de>               */ +/* Created at: Mon Jan 29 18:26:01 CEST 2001                                 */ +/* Computer: whiplash.flachland-chemnitz.de                                  */ +/* System: Linux 2.4.0 on i686                                               */ +/*                                                                           */ +/* Author: Sam Wilkins <swilkins1337@gmail.com>                              */ +/* System: Mac OS X and port to big endian processor                         */ +/*                                                                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 29.01.01  1.00  Mar  Created                                              */ +/* 24.03.03  1.01  Lad  Some cosmetic changes                                */ +/* 12.11.03  1.02  Dan  Macintosh compatibility                              */ +/* 24.07.04  1.03  Sam  Mac OS X compatibility                               */ +/* 22.11.06  1.04  Sam  Mac OS X compatibility (for StormLib 6.0)            */ +/* 31.12.06  1.05  XPinguin  Full GNU/Linux compatibility		             */ +/* 17.10.12  1.05  Lad  Moved error codes so they don't overlap with errno.h */ +/*****************************************************************************/ + +#ifndef __STORMPORT_H__ +#define __STORMPORT_H__ + +#ifndef __cplusplus +  #define bool char +  #define true 1 +  #define false 0 +#endif + +// Defines for Windows +#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64)) + +  // In MSVC 8.0, there are some functions declared as deprecated. +  #if _MSC_VER >= 1400 +  #define _CRT_SECURE_NO_DEPRECATE +  #define _CRT_NON_CONFORMING_SWPRINTFS +  #endif + +  #include <tchar.h> +  #include <assert.h> +  #include <ctype.h> +  #include <stdio.h> +  #include <windows.h> +  #include <wininet.h> +  #define PLATFORM_LITTLE_ENDIAN + +  #ifdef WIN64 +    #define PLATFORM_64BIT +  #else +    #define PLATFORM_32BIT +  #endif + +  #define PLATFORM_WINDOWS +  #define PLATFORM_DEFINED                  // The platform is known now + +#endif + +// Defines for Mac  +#if !defined(PLATFORM_DEFINED) && defined(__APPLE__)  // Mac BSD API + +  // Macintosh +  #include <sys/types.h> +  #include <sys/stat.h> +  #include <sys/mman.h> +  #include <unistd.h> +  #include <fcntl.h> +  #include <stdlib.h> +  #include <errno.h> +   +  #define    PKEXPORT +  #define    __SYS_ZLIB +  #define    __SYS_BZLIB + +  #ifndef __BIG_ENDIAN__ +    #define PLATFORM_LITTLE_ENDIAN +  #endif + +  #define PLATFORM_MAC +  #define PLATFORM_DEFINED                  // The platform is known now + +#endif + +// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* +#if !defined(PLATFORM_DEFINED) + +  #include <sys/types.h> +  #include <sys/stat.h> +  #include <sys/mman.h> +  #include <fcntl.h> +  #include <unistd.h> +  #include <stdint.h> +  #include <stdlib.h> +  #include <stdio.h> +  #include <stdarg.h> +  #include <string.h> +  #include <ctype.h> +  #include <assert.h> +  #include <errno.h> + +  #define PLATFORM_LITTLE_ENDIAN +  #define PLATFORM_LINUX +  #define PLATFORM_DEFINED + +#endif + +// Definition of Windows-specific structures for non-Windows platforms +#ifndef PLATFORM_WINDOWS +  #if __LP64__ +    #define PLATFORM_64BIT +  #else +    #define PLATFORM_32BIT +  #endif + +  // Typedefs for ANSI C +  typedef unsigned char  BYTE; +  typedef unsigned short USHORT; +  typedef int            LONG; +  typedef unsigned int   DWORD; +  typedef unsigned long  DWORD_PTR; +  typedef long           LONG_PTR; +  typedef long           INT_PTR; +  typedef long long      LONGLONG; +  typedef unsigned long long ULONGLONG; +  typedef void         * HANDLE; +  typedef void         * LPOVERLAPPED; // Unsupported on Linux and Mac +  typedef char           TCHAR; +  typedef unsigned int   LCID; +  typedef LONG         * PLONG; +  typedef DWORD        * LPDWORD; +  typedef BYTE         * LPBYTE; + +  #ifdef PLATFORM_32BIT +    #define _LZMA_UINT32_IS_ULONG +  #endif + +  // Some Windows-specific defines +  #ifndef MAX_PATH +    #define MAX_PATH 1024 +  #endif + +  #define WINAPI  + +  #define FILE_BEGIN    SEEK_SET +  #define FILE_CURRENT  SEEK_CUR +  #define FILE_END      SEEK_END + +  #define _T(x)     x +  #define _tcslen   strlen +  #define _tcscpy   strcpy +  #define _tcscat   strcat +  #define _tcsrchr  strrchr +  #define _tprintf  printf +  #define _stprintf sprintf +  #define _tremove  remove + +  #define _stricmp  strcasecmp +  #define _strnicmp strncasecmp +  #define _tcsnicmp strncasecmp + +#endif // !WIN32 + +// 64-bit calls are supplied by "normal" calls on Mac +#if defined(PLATFORM_MAC) +  #define stat64  stat +  #define fstat64 fstat +  #define lseek64 lseek +  #define off64_t off_t +  #define O_LARGEFILE 0 +#endif +                                                 +// Platform-specific error codes for UNIX-based platforms +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +  #define ERROR_SUCCESS                  0 +  #define ERROR_FILE_NOT_FOUND           ENOENT +  #define ERROR_ACCESS_DENIED            EPERM +  #define ERROR_INVALID_HANDLE           EBADF +  #define ERROR_NOT_ENOUGH_MEMORY        ENOMEM +  #define ERROR_NOT_SUPPORTED            ENOTSUP +  #define ERROR_INVALID_PARAMETER        EINVAL +  #define ERROR_DISK_FULL                ENOSPC +  #define ERROR_ALREADY_EXISTS           EEXIST +  #define ERROR_INSUFFICIENT_BUFFER      ENOBUFS +  #define ERROR_BAD_FORMAT               1000        // No such error code under Linux +  #define ERROR_NO_MORE_FILES            1001        // No such error code under Linux +  #define ERROR_HANDLE_EOF               1002        // No such error code under Linux +  #define ERROR_CAN_NOT_COMPLETE         1003        // No such error code under Linux +  #define ERROR_FILE_CORRUPT             1004        // No such error code under Linux +#endif + +#ifdef PLATFORM_LITTLE_ENDIAN +    #define    BSWAP_INT16_UNSIGNED(a)          (a) +    #define    BSWAP_INT16_SIGNED(a)            (a) +    #define    BSWAP_INT32_UNSIGNED(a)          (a) +    #define    BSWAP_INT32_SIGNED(a)            (a) +    #define    BSWAP_INT64_SIGNED(a)            (a) +    #define    BSWAP_INT64_UNSIGNED(a)          (a) +    #define    BSWAP_ARRAY16_UNSIGNED(a,b)      {} +    #define    BSWAP_ARRAY32_UNSIGNED(a,b)      {} +    #define    BSWAP_ARRAY64_UNSIGNED(a,b)      {} +    #define    BSWAP_PART_HEADER(a)             {} +    #define    BSWAP_TMPQUSERDATA(a)            {} +    #define    BSWAP_TMPQHEADER(a)              {} +#else + +#ifdef __cplusplus +  extern "C" { +#endif +    int16_t  SwapInt16(uint16_t); +    uint16_t SwapUInt16(uint16_t); +    int32_t  SwapInt32(uint32_t); +    uint32_t SwapUInt32(uint32_t); +    int64_t  SwapInt64(uint64_t); +    uint64_t SwapUInt64(uint64_t); +    void ConvertUInt16Buffer(void * ptr, size_t length); +    void ConvertUInt32Buffer(void * ptr, size_t length); +    void ConvertUInt64Buffer(void * ptr, size_t length); +    void ConvertPartHeader(void * partHeader); +    void ConvertTMPQUserData(void *userData); +    void ConvertTMPQHeader(void *header); +#ifdef __cplusplus +  } +#endif +    #define    BSWAP_INT16_SIGNED(a)            SwapInt16((a)) +    #define    BSWAP_INT16_UNSIGNED(a)          SwapUInt16((a)) +    #define    BSWAP_INT32_SIGNED(a)            SwapInt32((a)) +    #define    BSWAP_INT32_UNSIGNED(a)          SwapUInt32((a)) +    #define    BSWAP_INT64_SIGNED(a)            SwapInt64((a)) +    #define    BSWAP_INT64_UNSIGNED(a)          SwapUInt64((a)) +    #define    BSWAP_ARRAY16_UNSIGNED(a,b)      ConvertUInt16Buffer((a),(b)) +    #define    BSWAP_ARRAY32_UNSIGNED(a,b)      ConvertUInt32Buffer((a),(b)) +    #define    BSWAP_ARRAY64_UNSIGNED(a,b)      ConvertUInt64Buffer((a),(b)) +    #define    BSWAP_PART_HEADER(a)             ConvertPartHeader(a) +    #define    BSWAP_TMPQUSERDATA(a)            ConvertTMPQUserData((a)) +    #define    BSWAP_TMPQHEADER(a)              ConvertTMPQHeader((a)) +#endif + +#endif // __STORMPORT_H__ diff --git a/src/adpcm/adpcm.cpp b/src/adpcm/adpcm.cpp new file mode 100644 index 0000000..d05fca6 --- /dev/null +++ b/src/adpcm/adpcm.cpp @@ -0,0 +1,398 @@ +/*****************************************************************************/ +/* adpcm.cpp                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains implementation of adpcm decompression method used by */ +/* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing     */ +/* his sources.                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 11.03.03  1.00  Lad  Splitted from Pkware.cpp                             */ +/* 20.05.03  2.00  Lad  Added compression                                    */ +/* 19.11.03  2.01  Dan  Big endian handling                                  */ +/* 10.01.13  3.00  Lad  Refactored, beautified, documented :-)               */ +/*****************************************************************************/ + +#include "../StormPort.h" +#include "adpcm.h" + +//----------------------------------------------------------------------------- +// Tables necessary dor decompression + +static int NextStepTable[] = +{ +    -1, 0, -1, 4, -1, 2, -1, 6, +    -1, 1, -1, 5, -1, 3, -1, 7, +    -1, 1, -1, 5, -1, 3, -1, 7, +    -1, 2, -1, 4, -1, 6, -1, 8 +}; + +static int StepSizeTable[] = +{ +        7,     8,     9,    10,     11,    12,    13,    14, +       16,    17,    19,    21,     23,    25,    28,    31, +       34,    37,    41,    45,     50,    55,    60,    66, +       73,    80,    88,    97,    107,   118,   130,   143, +      157,   173,   190,   209,    230,   253,   279,   307, +      337,   371,   408,   449,    494,   544,   598,   658, +      724,   796,   876,   963,   1060,  1166,  1282,  1411, +     1552,  1707,  1878,  2066,   2272,  2499,  2749,  3024, +     3327,  3660,  4026,  4428,   4871,  5358,  5894,  6484, +     7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899, +     15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, +     32767 +}; + +//----------------------------------------------------------------------------- +// Helper class for writing output ADPCM data + +class TADPCMStream +{ +    public: + +    TADPCMStream(void * pvBuffer, size_t cbBuffer) +    { +        pbBufferEnd = (unsigned char *)pvBuffer + cbBuffer; +        pbBuffer = (unsigned char *)pvBuffer; +    } + +    bool ReadByteSample(unsigned char & ByteSample) +    { +        // Check if there is enough space in the buffer +        if(pbBuffer >= pbBufferEnd) +            return false; + +        ByteSample = *pbBuffer++; +        return true; +    } + +    bool WriteByteSample(unsigned char ByteSample) +    { +        // Check if there is enough space in the buffer +        if(pbBuffer >= pbBufferEnd) +            return false; + +        *pbBuffer++ = ByteSample; +        return true; +    } + +    bool ReadWordSample(short & OneSample) +    { +        // Check if we have enough space in the output buffer +        if((pbBufferEnd - pbBuffer) < sizeof(short)) +            return false; + +        // Write the sample +        OneSample = pbBuffer[0] + (((short)pbBuffer[1]) << 0x08); +        pbBuffer += sizeof(short); +        return true; +    } + +    bool WriteWordSample(short OneSample) +    { +        // Check if we have enough space in the output buffer +        if((pbBufferEnd - pbBuffer) < sizeof(short)) +            return false; + +        // Write the sample +        *pbBuffer++ = (unsigned char)(OneSample & 0xFF); +        *pbBuffer++ = (unsigned char)(OneSample >> 0x08); +        return true; +    } + +    int LengthProcessed(void * pvBuffer) +    { +        return pbBuffer - (unsigned char *)pvBuffer; +    } + +    unsigned char * pbBufferEnd; +    unsigned char * pbBuffer; +};                     + +//---------------------------------------------------------------------------- +// Local functions + +static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample) +{ +    // Get the next step index +    StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F]; + +    // Don't make the step index overflow +    if(StepIndex < 0) +        StepIndex = 0; +    else if(StepIndex > 88) +        StepIndex = 88; + +    return (short)StepIndex; +} + +static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference) +{ +    // Is the sign bit set? +    if(EncodedSample & 0x40) +    { +        PredictedSample -= Difference; +        if(PredictedSample <= -32768) +            PredictedSample = -32768; +    } +    else +    { +        PredictedSample += Difference; +        if(PredictedSample >= 32767) +            PredictedSample = 32767; +    } + +    return PredictedSample; +} + +static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference) +{ +    if(EncodedSample & 0x01) +        Difference += (StepSize >> 0); + +    if(EncodedSample & 0x02) +        Difference += (StepSize >> 1); + +    if(EncodedSample & 0x04) +        Difference += (StepSize >> 2); + +    if(EncodedSample & 0x08) +        Difference += (StepSize >> 3); + +    if(EncodedSample & 0x10) +        Difference += (StepSize >> 4); + +    if(EncodedSample & 0x20) +        Difference += (StepSize >> 5); + +    return UpdatePredictedSample(PredictedSample, EncodedSample, Difference); +} + +//---------------------------------------------------------------------------- +// Compression routine + +int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel) +{ +    TADPCMStream os(pvOutBuffer, cbOutBuffer);      // The output stream +    TADPCMStream is(pvInBuffer, cbInBuffer);        // The input stream +    unsigned char BitShift = (unsigned char)(CompressionLevel - 1); +    short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel +    short StepIndexes[MAX_ADPCM_CHANNEL_COUNT];     // Step indexes for each channel +    short InputSample;                              // Input sample for the current channel +    int TotalStepSize; +    int ChannelIndex; +    int AbsDifference; +    int Difference; +    int MaxBitMask; +    int StepSize; + +//  _tprintf(_T("== CMPR Started ==============\n")); + +    // First byte in the output stream contains zero. The second one contains the compression level +    os.WriteByteSample(0); +    if(!os.WriteByteSample(BitShift)) +        return 2; + +    // Set the initial step index for each channel +    StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; + +    // Next, InitialSample value for each channel follows +    for(int i = 0; i < ChannelCount; i++) +    { +        // Get the initial sample from the input stream +        if(!is.ReadWordSample(InputSample)) +            return os.LengthProcessed(pvOutBuffer); + +        // Store the initial sample to our sample array +        PredictedSamples[i] = InputSample; + +        // Also store the loaded sample to the output stream +        if(!os.WriteWordSample(InputSample)) +            return os.LengthProcessed(pvOutBuffer); +    } + +    // Get the initial index +    ChannelIndex = ChannelCount - 1; +     +    // Now keep reading the input data as long as there is something in the input buffer +    while(is.ReadWordSample(InputSample)) +    { +        int EncodedSample = 0; + +        // If we have two channels, we need to flip the channel index +        ChannelIndex = (ChannelIndex + 1) % ChannelCount; + +        // Get the difference from the previous sample. +        // If the difference is negative, set the sign bit to the encoded sample +        AbsDifference = InputSample - PredictedSamples[ChannelIndex]; +        if(AbsDifference < 0) +        { +            AbsDifference = -AbsDifference; +            EncodedSample |= 0x40; +        } + +        // If the difference is too low (higher that difference treshold), +        // write a step index modifier marker +        StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; +        if(AbsDifference < (StepSize >> CompressionLevel)) +        { +            if(StepIndexes[ChannelIndex] != 0) +                StepIndexes[ChannelIndex]--; +             +            os.WriteByteSample(0x80); +        } +        else +        { +            // If the difference is too high, write marker that +            // indicates increase in step size +            while(AbsDifference > (StepSize << 1)) +            { +                if(StepIndexes[ChannelIndex] >= 0x58) +                    break; + +                // Modify the step index +                StepIndexes[ChannelIndex] += 8; +                if(StepIndexes[ChannelIndex] > 0x58) +                    StepIndexes[ChannelIndex] = 0x58; + +                // Write the "modify step index" marker +                StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; +                os.WriteByteSample(0x81); +            } + +            // Get the limit bit value +            MaxBitMask = (1 << (BitShift - 1)); +            MaxBitMask = (MaxBitMask > 0x20) ? 0x20 : MaxBitMask; +            Difference = StepSize >> BitShift; +            TotalStepSize = 0; + +            for(int BitVal = 0x01; BitVal <= MaxBitMask; BitVal <<= 1) +            { +                if((TotalStepSize + StepSize) <= AbsDifference) +                { +                    TotalStepSize += StepSize; +                    EncodedSample |= BitVal; +                } +                StepSize >>= 1; +            } + +            PredictedSamples[ChannelIndex] = (short)UpdatePredictedSample(PredictedSamples[ChannelIndex], +                                                                          EncodedSample, +                                                                          Difference + TotalStepSize); +            // Write the encoded sample to the output stream +            if(!os.WriteByteSample((unsigned char)EncodedSample)) +                break; +             +            // Calculates the step index to use for the next encode +            StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample); +        } +    } + +//  _tprintf(_T("== CMPR Ended ================\n")); +    return os.LengthProcessed(pvOutBuffer); +} + +//---------------------------------------------------------------------------- +// Decompression routine + +int DecompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount) +{ +    TADPCMStream os(pvOutBuffer, cbOutBuffer);          // Output stream +    TADPCMStream is(pvInBuffer, cbInBuffer);            // Input stream +    unsigned char EncodedSample; +    unsigned char BitShift; +    short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];    // Predicted sample for each channel +    short StepIndexes[MAX_ADPCM_CHANNEL_COUNT];         // Predicted step index for each channel +    int ChannelIndex;                                   // Current channel index + +    // Initialize the StepIndex for each channel +    StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; + +//  _tprintf(_T("== DCMP Started ==============\n")); + +    // The first byte is always zero, the second one contains bit shift (compression level - 1) +    is.ReadByteSample(BitShift); +    is.ReadByteSample(BitShift); +//  _tprintf(_T("DCMP: BitShift = %u\n"), (unsigned int)(unsigned char)BitShift); + +    // Next, InitialSample value for each channel follows +    for(int i = 0; i < ChannelCount; i++) +    { +        // Get the initial sample from the input stream +        short InitialSample; + +        // Attempt to read the initial sample +        if(!is.ReadWordSample(InitialSample)) +            return os.LengthProcessed(pvOutBuffer); + +//      _tprintf(_T("DCMP: Loaded InitialSample[%u]: %04X\n"), i, (unsigned int)(unsigned short)InitialSample); + +        // Store the initial sample to our sample array +        PredictedSamples[i] = InitialSample; + +        // Also store the loaded sample to the output stream +        if(!os.WriteWordSample(InitialSample)) +            return os.LengthProcessed(pvOutBuffer); +    } + +    // Get the initial index +    ChannelIndex = ChannelCount - 1; + +    // Keep reading as long as there is something in the input buffer +    while(is.ReadByteSample(EncodedSample)) +    { +//      _tprintf(_T("DCMP: Loaded Encoded Sample: %02X\n"), (unsigned int)(unsigned char)EncodedSample); + +        // If we have two channels, we need to flip the channel index +        ChannelIndex = (ChannelIndex + 1) % ChannelCount; + +        if(EncodedSample == 0x80) +        { +            if(StepIndexes[ChannelIndex] != 0) +                StepIndexes[ChannelIndex]--; + +//          _tprintf(_T("DCMP: Writing Decoded Sample: %04lX\n"), (unsigned int)(unsigned short)PredictedSamples[ChannelIndex]); +            if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) +                return os.LengthProcessed(pvOutBuffer); +        } +        else if(EncodedSample == 0x81) +        { +            // Modify the step index +            StepIndexes[ChannelIndex] += 8; +            if(StepIndexes[ChannelIndex] > 0x58) +                StepIndexes[ChannelIndex] = 0x58; + +//          _tprintf(_T("DCMP: New value of StepIndex: %04lX\n"), (unsigned int)(unsigned short)StepIndexes[ChannelIndex]); + +            // Next pass, keep going on the same channel +            ChannelIndex = (ChannelIndex + 1) % ChannelCount; +        } +        else +        { +            int StepIndex = StepIndexes[ChannelIndex]; +            int StepSize = StepSizeTable[StepIndex]; + +            // Encode one sample +            PredictedSamples[ChannelIndex] = (short)DecodeSample(PredictedSamples[ChannelIndex], +                                                                 EncodedSample,  +                                                                 StepSize, +                                                                 StepSize >> BitShift); + +//          _tprintf(_T("DCMP: Writing decoded sample: %04X\n"), (unsigned int)(unsigned short)PredictedSamples[ChannelIndex]); + +            // Write the decoded sample to the output stream +            if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) +                break; + +            // Calculates the step index to use for the next encode +            StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndex, EncodedSample); +//          _tprintf(_T("DCMP: New step index: %04X\n"), (unsigned int)(unsigned short)StepIndexes[ChannelIndex]); +        } +    } + +//  _tprintf(_T("DCMP: Total length written: %u\n"), (unsigned int)os.LengthProcessed(pvOutBuffer)); +//  _tprintf(_T("== DCMP Ended ================\n")); + +    // Return total bytes written since beginning of the output buffer +    return os.LengthProcessed(pvOutBuffer); +} diff --git a/src/adpcm/adpcm.h b/src/adpcm/adpcm.h new file mode 100644 index 0000000..b1bf361 --- /dev/null +++ b/src/adpcm/adpcm.h @@ -0,0 +1,26 @@ +/*****************************************************************************/ +/* adpcm.h                                Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Header file for adpcm decompress functions                                */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 31.03.03  1.00  Lad  The first version of adpcm.h                         */ +/*****************************************************************************/ + +#ifndef __ADPCM_H__ +#define __ADPCM_H__ + +//----------------------------------------------------------------------------- +// Defines + +#define MAX_ADPCM_CHANNEL_COUNT   2 +#define INITIAL_ADPCM_STEP_INDEX  0x2C + +//----------------------------------------------------------------------------- +// Public functions + +int  CompressADPCM  (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int nCmpType, int ChannelCount); +int  DecompressADPCM(void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount); + +#endif // __ADPCM_H__ diff --git a/src/adpcm/adpcm_old.cpp b/src/adpcm/adpcm_old.cpp new file mode 100644 index 0000000..916fa38 --- /dev/null +++ b/src/adpcm/adpcm_old.cpp @@ -0,0 +1,358 @@ +/*****************************************************************************/ +/* adpcm.cpp                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains implementation of adpcm decompression method used by */ +/* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing     */ +/* his sources.                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 11.03.03  1.00  Lad  Splitted from Pkware.cpp                             */ +/* 20.05.03  2.00  Lad  Added compression                                    */ +/* 19.11.03  2.01  Dan  Big endian handling                                  */ +/*****************************************************************************/ + +#include "adpcm.h" + +//------------------------------------------------------------------------------ +// Structures + +typedef union _BYTE_AND_WORD_PTR +{ +    short * pw; +    unsigned char * pb; +} BYTE_AND_WORD_PTR; + +typedef union _WORD_AND_BYTE_ARRAY +{ +    short w; +    unsigned char b[2]; +} WORD_AND_BYTE_ARRAY; + +//----------------------------------------------------------------------------- +// Tables necessary dor decompression + +static long Table1503F120[] = +{ +    0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006, +    0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, +    0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,   +    0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008   +}; + +static long step_table[] = +{ +    0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E, +    0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F, +    0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042, +    0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F, +    0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133, +    0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292, +    0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583, +    0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0, +    0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954, +    0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B, +    0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462, +    0x00007FFF +}; + +//---------------------------------------------------------------------------- +// CompressWave + +// 1500EF70 +int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel) +//                ECX                          EDX +{ +    WORD_AND_BYTE_ARRAY Wcmp; +    BYTE_AND_WORD_PTR out;                    // Pointer to the output buffer +    long SInt32Array1[2]; +    long SInt32Array2[2]; +    long SInt32Array3[2]; +    long nBytesRemains = dwOutLength;       // Number of bytes remaining +    long nWordsRemains;                     // Number of words remaining +//  unsigned char * pbSaveOutBuffer;        // Copy of output buffer (actually not used) +    unsigned long dwBitBuff; +    unsigned long dwStopBit; +    unsigned long dwBit; +    unsigned long ebx; +    unsigned long esi; +    long nTableValue; +    long nOneWord; +    long var_1C; +    long var_2C; +    int nLength; +    int nIndex; +    int nValue; +    int i, chnl; + +    // If less than 2 bytes remain, don't decompress anything +//  pbSaveOutBuffer = pbOutBuffer; +    out.pb = pbOutBuffer; +    if(nBytesRemains < 2) +        return 2; + +    Wcmp.b[1] = (unsigned char)(nCmpLevel - 1); +    Wcmp.b[0] = (unsigned char)0; + +    *out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w); +    if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains) +        return (int)(out.pb - pbOutBuffer + (nChannels * 2)); + +    SInt32Array1[0] = SInt32Array1[1] = 0x2C; + +    for(i = 0; i < nChannels; i++) +    { +        nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); +        *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); +        SInt32Array2[i] = nOneWord; +    } + +    // Weird. But it's there +    nLength = dwInLength; +    if(nLength < 0)                     // mov eax, dwInLength; cdq; sub eax, edx; +        nLength++; + +    nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer); +    nLength = (nLength < 0) ? 0 : nLength; +     +    nIndex  = nChannels - 1;            // edi +    nWordsRemains = dwInLength / 2;     // eax +     +    // ebx - nChannels +    // ecx - pwOutPos +    for(chnl = nChannels; chnl < nWordsRemains; chnl++) +    { +        // 1500F030 +        if((out.pb - pbOutBuffer + 2) > nBytesRemains) +            return (int)(out.pb - pbOutBuffer + 2); + +        // Switch index +        if(nChannels == 2) +            nIndex = (nIndex == 0) ? 1 : 0; + +        // Load one word from the input stream +        nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);   // ecx - nOneWord +        SInt32Array3[nIndex] = nOneWord; + +        // esi - SInt32Array2[nIndex] +        // eax - nValue +        nValue = nOneWord - SInt32Array2[nIndex]; +        nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue; + +        ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40; + +        // esi - SInt32Array2[nIndex] +        // edx - step_table[SInt32Array2[nIndex]] +        // edi - (step_table[SInt32Array1[nIndex]] >> nCmpLevel) +        nTableValue = step_table[SInt32Array1[nIndex]]; +        dwStopBit = (unsigned long)nCmpLevel; + +        // edi - nIndex; +        if(nValue < (nTableValue >> nCmpLevel)) +        { +            if(SInt32Array1[nIndex] != 0) +                SInt32Array1[nIndex]--; +            *out.pb++ = 0x80; +        } +        else +        { +            while(nValue > nTableValue * 2) +            { +                if(SInt32Array1[nIndex] >= 0x58 || nLength == 0) +                    break; + +                SInt32Array1[nIndex] += 8; +                if(SInt32Array1[nIndex] > 0x58) +                    SInt32Array1[nIndex] = 0x58; + +                nTableValue = step_table[SInt32Array1[nIndex]]; +                *out.pb++ = 0x81; +                nLength--; +            } + +            var_2C = nTableValue >> Wcmp.b[1]; +            dwBitBuff = 0; + +            esi = (1 << (dwStopBit - 2)); +            dwStopBit = (esi <= 0x20) ? esi : 0x20; + +            for(var_1C = 0, dwBit = 1; ; dwBit <<= 1) +            { +//              esi = var_1C + nTableValue; +                if((var_1C + nTableValue) <= nValue) +                { +                    var_1C += nTableValue; +                    dwBitBuff |= dwBit; +                } +                if(dwBit == dwStopBit) +                    break; +                +                nTableValue >>= 1; +            } + +            nValue = SInt32Array2[nIndex]; +            if(ebx != 0) +            { +                nValue -= (var_1C + var_2C); +                if(nValue < -32768) +                    nValue = -32768; +            } +            else +            { +                nValue += (var_1C + var_2C); +                if(nValue > 32767) +                    nValue = 32767; +            } + +            SInt32Array2[nIndex]  = nValue; +            *out.pb++ = (unsigned char)(dwBitBuff | ebx); +            nTableValue = Table1503F120[dwBitBuff & 0x1F]; +            SInt32Array1[nIndex]  = SInt32Array1[nIndex] + nTableValue;  +            if(SInt32Array1[nIndex] < 0) +                SInt32Array1[nIndex] = 0; +            else if(SInt32Array1[nIndex] > 0x58) +                SInt32Array1[nIndex] = 0x58; +        } +    } + +    return (int)(out.pb - pbOutBuffer); +} + +//---------------------------------------------------------------------------- +// DecompressADPCM + +// 1500F230 +int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels) +{ +    BYTE_AND_WORD_PTR out;                // Output buffer +    BYTE_AND_WORD_PTR in; +    unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength); +    long SInt32Array1[2]; +    long SInt32Array2[2]; +    long nOneWord; +    int nIndex; +    int i; + +    SInt32Array1[0] = SInt32Array1[1] = 0x2C; +    out.pb = pbOutBuffer; +    in.pb = pbInBuffer; +    in.pw++; + +    // Fill the Uint32Array2 array by channel values. +    for(i = 0; i < nChannels; i++) +    { +        nOneWord = BSWAP_INT16_SIGNED(*in.pw++); +        SInt32Array2[i] = nOneWord; +        if(dwOutLength < 2) +            return (int)(out.pb - pbOutBuffer); + +        *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); +        dwOutLength -= sizeof(short); +    } + +    // Get the initial index +    nIndex = nChannels - 1; + +    // Perform the decompression +    while(in.pb < pbInBufferEnd) +    { +        unsigned char nOneByte = *in.pb++; + +        // Switch index +        if(nChannels == 2) +            nIndex = (nIndex == 0) ? 1 : 0; + +        // 1500F2A2: Get one byte from input buffer +        if(nOneByte & 0x80) +        { +            switch(nOneByte & 0x7F) +            { +                case 0:     // 1500F315 +                    if(SInt32Array1[nIndex] != 0) +                        SInt32Array1[nIndex]--; + +                    if(dwOutLength < 2) +                        return (int)(out.pb - pbOutBuffer); + +                    *out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]); +                    dwOutLength -= sizeof(unsigned short); +                    break; + +                case 1:     // 1500F2E8 +                    SInt32Array1[nIndex] += 8; +                    if(SInt32Array1[nIndex] > 0x58) +                        SInt32Array1[nIndex] = 0x58; +                     +                    if(nChannels == 2) +                        nIndex = (nIndex == 0) ? 1 : 0; +                    break; + +                case 2:     // 1500F41E +                    break; + +                default:    // 1500F2C4 +                    SInt32Array1[nIndex] -= 8; +                    if(SInt32Array1[nIndex] < 0) +                        SInt32Array1[nIndex] = 0; + +                    if(nChannels == 2) +                        nIndex = (nIndex == 0) ? 1 : 0; +                    break; +            } +        } +        else +        { +            // 1500F349 +            long temp1 = step_table[SInt32Array1[nIndex]];     // EDI +            long temp2 = temp1 >> pbInBuffer[1];               // ESI +            long temp3 = SInt32Array2[nIndex];                 // ECX + +            if(nOneByte & 0x01)          // EBX = nOneByte +                temp2 += (temp1 >> 0); + +            if(nOneByte & 0x02) +                temp2 += (temp1 >> 1); + +            if(nOneByte & 0x04) +                temp2 += (temp1 >> 2); + +            if(nOneByte & 0x08) +                temp2 += (temp1 >> 3); + +            if(nOneByte & 0x10) +                temp2 += (temp1 >> 4); + +            if(nOneByte & 0x20) +                temp2 += (temp1 >> 5); + +            if(nOneByte & 0x40) +            { +                temp3 = temp3 - temp2; +                if(temp3 <= -32768) +                    temp3 = -32768; +            } +            else +            { +                temp3 = temp3 + temp2; +                if(temp3 >= 32767) +                    temp3 = 32767; +            } + +            SInt32Array2[nIndex] = temp3; +            if(dwOutLength < 2) +                break; + +            // Store the output 16-bit value +            *out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]); +            dwOutLength -= 2; + +            SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F]; + +            if(SInt32Array1[nIndex] < 0) +                SInt32Array1[nIndex] = 0; +            else if(SInt32Array1[nIndex] > 0x58) +                SInt32Array1[nIndex] = 0x58; +        } +    } +    return (int)(out.pb - pbOutBuffer); +} diff --git a/src/adpcm/adpcm_old.h b/src/adpcm/adpcm_old.h new file mode 100644 index 0000000..beb9615 --- /dev/null +++ b/src/adpcm/adpcm_old.h @@ -0,0 +1,22 @@ +/*****************************************************************************/ +/* adpcm.h                                Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Header file for adpcm decompress functions                                */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 31.03.03  1.00  Lad  The first version of adpcm.h                         */ +/*****************************************************************************/ + +#ifndef __ADPCM_H__ +#define __ADPCM_H__ + +//----------------------------------------------------------------------------- +// Functions + +#include "../StormPort.h" + +int  CompressADPCM  (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels); +int  DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels); + +#endif // __ADPCM_H__ diff --git a/src/bzip2/blocksort.c b/src/bzip2/blocksort.c new file mode 100644 index 0000000..bd2dec1 --- /dev/null +++ b/src/bzip2/blocksort.c @@ -0,0 +1,1094 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery                               ---*/ +/*---                                           blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting        ---*/ +/*--- algorithm, for repetitive blocks      ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static  +__inline__ +void fallbackSimpleSort ( UInt32* fmap,  +                          UInt32* eclass,  +                          Int32   lo,  +                          Int32   hi ) +{ +   Int32 i, j, tmp; +   UInt32 ec_tmp; + +   if (lo == hi) return; + +   if (hi - lo > 3) { +      for ( i = hi-4; i >= lo; i-- ) { +         tmp = fmap[i]; +         ec_tmp = eclass[tmp]; +         for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) +            fmap[j-4] = fmap[j]; +         fmap[j-4] = tmp; +      } +   } + +   for ( i = hi-1; i >= lo; i-- ) { +      tmp = fmap[i]; +      ec_tmp = eclass[tmp]; +      for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) +         fmap[j-1] = fmap[j]; +      fmap[j-1] = tmp; +   } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ +   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn)       \ +{                                     \ +   Int32 yyp1 = (zzp1);               \ +   Int32 yyp2 = (zzp2);               \ +   Int32 yyn  = (zzn);                \ +   while (yyn > 0) {                  \ +      fswap(fmap[yyp1], fmap[yyp2]);  \ +      yyp1++; yyp2++; yyn--;          \ +   }                                  \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ +                       stackHi[sp] = hz; \ +                       sp++; } + +#define fpop(lz,hz) { sp--;              \ +                      lz = stackLo[sp];  \ +                      hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE   100 + + +static +void fallbackQSort3 ( UInt32* fmap,  +                      UInt32* eclass, +                      Int32   loSt,  +                      Int32   hiSt ) +{ +   Int32 unLo, unHi, ltLo, gtHi, n, m; +   Int32 sp, lo, hi; +   UInt32 med, r, r3; +   Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; +   Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + +   r = 0; + +   sp = 0; +   fpush ( loSt, hiSt ); + +   while (sp > 0) { + +      AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + +      fpop ( lo, hi ); +      if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { +         fallbackSimpleSort ( fmap, eclass, lo, hi ); +         continue; +      } + +      /* Random partitioning.  Median of 3 sometimes fails to +         avoid bad cases.  Median of 9 seems to help but  +         looks rather expensive.  This too seems to work but +         is cheaper.  Guidance for the magic constants  +         7621 and 32768 is taken from Sedgewick's algorithms +         book, chapter 35. +      */ +      r = ((r * 7621) + 1) % 32768; +      r3 = r % 3; +      if (r3 == 0) med = eclass[fmap[lo]]; else +      if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else +                   med = eclass[fmap[hi]]; + +      unLo = ltLo = lo; +      unHi = gtHi = hi; + +      while (1) { +         while (1) { +            if (unLo > unHi) break; +            n = (Int32)eclass[fmap[unLo]] - (Int32)med; +            if (n == 0) {  +               fswap(fmap[unLo], fmap[ltLo]);  +               ltLo++; unLo++;  +               continue;  +            }; +            if (n > 0) break; +            unLo++; +         } +         while (1) { +            if (unLo > unHi) break; +            n = (Int32)eclass[fmap[unHi]] - (Int32)med; +            if (n == 0) {  +               fswap(fmap[unHi], fmap[gtHi]);  +               gtHi--; unHi--;  +               continue;  +            }; +            if (n < 0) break; +            unHi--; +         } +         if (unLo > unHi) break; +         fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; +      } + +      AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + +      if (gtHi < ltLo) continue; + +      n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); +      m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + +      n = lo + unLo - ltLo - 1; +      m = hi - (gtHi - unHi) + 1; + +      if (n - lo > hi - m) { +         fpush ( lo, n ); +         fpush ( m, hi ); +      } else { +         fpush ( m, hi ); +         fpush ( lo, n ); +      } +   } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: +      nblock > 0 +      eclass exists for [0 .. nblock-1] +      ((UChar*)eclass) [0 .. nblock-1] holds block +      ptr exists for [0 .. nblock-1] + +   Post: +      ((UChar*)eclass) [0 .. nblock-1] holds block +      All other areas of eclass destroyed +      fmap [0 .. nblock-1] holds sorted order +      bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define       SET_BH(zz)  bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define     CLEAR_BH(zz)  bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define     ISSET_BH(zz)  (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define      WORD_BH(zz)  bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz)  ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap,  +                    UInt32* eclass,  +                    UInt32* bhtab, +                    Int32   nblock, +                    Int32   verb ) +{ +   Int32 ftab[257]; +   Int32 ftabCopy[256]; +   Int32 H, i, j, k, l, r, cc, cc1; +   Int32 nNotDone; +   Int32 nBhtab; +   UChar* eclass8 = (UChar*)eclass; + +   /*-- +      Initial 1-char radix sort to generate +      initial fmap and initial BH bits. +   --*/ +   if (verb >= 4) +      VPrintf0 ( "        bucket sorting ...\n" ); +   for (i = 0; i < 257;    i++) ftab[i] = 0; +   for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; +   for (i = 0; i < 256;    i++) ftabCopy[i] = ftab[i]; +   for (i = 1; i < 257;    i++) ftab[i] += ftab[i-1]; + +   for (i = 0; i < nblock; i++) { +      j = eclass8[i]; +      k = ftab[j] - 1; +      ftab[j] = k; +      fmap[k] = i; +   } + +   nBhtab = 2 + (nblock / 32); +   for (i = 0; i < nBhtab; i++) bhtab[i] = 0; +   for (i = 0; i < 256; i++) SET_BH(ftab[i]); + +   /*-- +      Inductively refine the buckets.  Kind-of an +      "exponential radix sort" (!), inspired by the +      Manber-Myers suffix array construction algorithm. +   --*/ + +   /*-- set sentinel bits for block-end detection --*/ +   for (i = 0; i < 32; i++) {  +      SET_BH(nblock + 2*i); +      CLEAR_BH(nblock + 2*i + 1); +   } + +   /*-- the log(N) loop --*/ +   H = 1; +   while (1) { + +      if (verb >= 4)  +         VPrintf1 ( "        depth %6d has ", H ); + +      j = 0; +      for (i = 0; i < nblock; i++) { +         if (ISSET_BH(i)) j = i; +         k = fmap[i] - H; if (k < 0) k += nblock; +         eclass[k] = j; +      } + +      nNotDone = 0; +      r = -1; +      while (1) { + +	 /*-- find the next non-singleton bucket --*/ +         k = r + 1; +         while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; +         if (ISSET_BH(k)) { +            while (WORD_BH(k) == 0xffffffff) k += 32; +            while (ISSET_BH(k)) k++; +         } +         l = k - 1; +         if (l >= nblock) break; +         while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; +         if (!ISSET_BH(k)) { +            while (WORD_BH(k) == 0x00000000) k += 32; +            while (!ISSET_BH(k)) k++; +         } +         r = k - 1; +         if (r >= nblock) break; + +         /*-- now [l, r] bracket current bucket --*/ +         if (r > l) { +            nNotDone += (r - l + 1); +            fallbackQSort3 ( fmap, eclass, l, r ); + +            /*-- scan bucket and generate header bits-- */ +            cc = -1; +            for (i = l; i <= r; i++) { +               cc1 = eclass[fmap[i]]; +               if (cc != cc1) { SET_BH(i); cc = cc1; }; +            } +         } +      } + +      if (verb >= 4)  +         VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + +      H *= 2; +      if (H > nblock || nNotDone == 0) break; +   } + +   /*--  +      Reconstruct the original block in +      eclass8 [0 .. nblock-1], since the +      previous phase destroyed it. +   --*/ +   if (verb >= 4) +      VPrintf0 ( "        reconstructing block ...\n" ); +   j = 0; +   for (i = 0; i < nblock; i++) { +      while (ftabCopy[j] == 0) j++; +      ftabCopy[j]--; +      eclass8[fmap[i]] = (UChar)j; +   } +   AssertH ( j < 256, 1005 ); +} + +#undef       SET_BH +#undef     CLEAR_BH +#undef     ISSET_BH +#undef      WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting       ---*/ +/*--- algorithm.  Faster for "normal"       ---*/ +/*--- non-repetitive blocks.                ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32  i1,  +               UInt32  i2, +               UChar*  block,  +               UInt16* quadrant, +               UInt32  nblock, +               Int32*  budget ) +{ +   Int32  k; +   UChar  c1, c2; +   UInt16 s1, s2; + +   AssertD ( i1 != i2, "mainGtU" ); +   /* 1 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 2 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 3 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 4 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 5 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 6 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 7 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 8 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 9 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 10 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 11 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; +   /* 12 */ +   c1 = block[i1]; c2 = block[i2]; +   if (c1 != c2) return (c1 > c2); +   i1++; i2++; + +   k = nblock + 8; + +   do { +      /* 1 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 2 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 3 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 4 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 5 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 6 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 7 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; +      /* 8 */ +      c1 = block[i1]; c2 = block[i2]; +      if (c1 != c2) return (c1 > c2); +      s1 = quadrant[i1]; s2 = quadrant[i2]; +      if (s1 != s2) return (s1 > s2); +      i1++; i2++; + +      if (i1 >= nblock) i1 -= nblock; +      if (i2 >= nblock) i2 -= nblock; + +      k -= 8; +      (*budget)--; +   } +      while (k >= 0); + +   return False; +} + + +/*---------------------------------------------*/ +/*-- +   Knuth's increments seem to work better +   than Incerpi-Sedgewick here.  Possibly +   because the number of elems to sort is +   usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, +                   9841, 29524, 88573, 265720, +                   797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, +                      UChar*  block, +                      UInt16* quadrant, +                      Int32   nblock, +                      Int32   lo,  +                      Int32   hi,  +                      Int32   d, +                      Int32*  budget ) +{ +   Int32 i, j, h, bigN, hp; +   UInt32 v; + +   bigN = hi - lo + 1; +   if (bigN < 2) return; + +   hp = 0; +   while (incs[hp] < bigN) hp++; +   hp--; + +   for (; hp >= 0; hp--) { +      h = incs[hp]; + +      i = lo + h; +      while (True) { + +         /*-- copy 1 --*/ +         if (i > hi) break; +         v = ptr[i]; +         j = i; +         while ( mainGtU (  +                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget  +                 ) ) { +            ptr[j] = ptr[j-h]; +            j = j - h; +            if (j <= (lo + h - 1)) break; +         } +         ptr[j] = v; +         i++; + +         /*-- copy 2 --*/ +         if (i > hi) break; +         v = ptr[i]; +         j = i; +         while ( mainGtU (  +                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget  +                 ) ) { +            ptr[j] = ptr[j-h]; +            j = j - h; +            if (j <= (lo + h - 1)) break; +         } +         ptr[j] = v; +         i++; + +         /*-- copy 3 --*/ +         if (i > hi) break; +         v = ptr[i]; +         j = i; +         while ( mainGtU (  +                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget  +                 ) ) { +            ptr[j] = ptr[j-h]; +            j = j - h; +            if (j <= (lo + h - 1)) break; +         } +         ptr[j] = v; +         i++; + +         if (*budget < 0) return; +      } +   } +} + + +/*---------------------------------------------*/ +/*-- +   The following is an implementation of +   an elegant 3-way quicksort for strings, +   described in a paper "Fast Algorithms for +   Sorting and Searching Strings", by Robert +   Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ +   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn)       \ +{                                     \ +   Int32 yyp1 = (zzp1);               \ +   Int32 yyp2 = (zzp2);               \ +   Int32 yyn  = (zzn);                \ +   while (yyn > 0) {                  \ +      mswap(ptr[yyp1], ptr[yyp2]);    \ +      yyp1++; yyp2++; yyn--;          \ +   }                                  \ +} + +static  +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ +   UChar t; +   if (a > b) { t = a; a = b; b = t; }; +   if (b > c) {  +      b = c; +      if (a > b) b = a; +   } +   return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ +                          stackHi[sp] = hz; \ +                          stackD [sp] = dz; \ +                          sp++; } + +#define mpop(lz,hz,dz) { sp--;             \ +                         lz = stackLo[sp]; \ +                         hz = stackHi[sp]; \ +                         dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz)                                        \ +   { Int32 tz;                                                  \ +     tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ +     tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ +     tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, +                  UChar*  block, +                  UInt16* quadrant, +                  Int32   nblock, +                  Int32   loSt,  +                  Int32   hiSt,  +                  Int32   dSt, +                  Int32*  budget ) +{ +   Int32 unLo, unHi, ltLo, gtHi, n, m, med; +   Int32 sp, lo, hi, d; + +   Int32 stackLo[MAIN_QSORT_STACK_SIZE]; +   Int32 stackHi[MAIN_QSORT_STACK_SIZE]; +   Int32 stackD [MAIN_QSORT_STACK_SIZE]; + +   Int32 nextLo[3]; +   Int32 nextHi[3]; +   Int32 nextD [3]; + +   sp = 0; +   mpush ( loSt, hiSt, dSt ); + +   while (sp > 0) { + +      AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + +      mpop ( lo, hi, d ); +      if (hi - lo < MAIN_QSORT_SMALL_THRESH ||  +          d > MAIN_QSORT_DEPTH_THRESH) { +         mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); +         if (*budget < 0) return; +         continue; +      } + +      med = (Int32)  +            mmed3 ( block[ptr[ lo         ]+d], +                    block[ptr[ hi         ]+d], +                    block[ptr[ (lo+hi)>>1 ]+d] ); + +      unLo = ltLo = lo; +      unHi = gtHi = hi; + +      while (True) { +         while (True) { +            if (unLo > unHi) break; +            n = ((Int32)block[ptr[unLo]+d]) - med; +            if (n == 0) {  +               mswap(ptr[unLo], ptr[ltLo]);  +               ltLo++; unLo++; continue;  +            }; +            if (n >  0) break; +            unLo++; +         } +         while (True) { +            if (unLo > unHi) break; +            n = ((Int32)block[ptr[unHi]+d]) - med; +            if (n == 0) {  +               mswap(ptr[unHi], ptr[gtHi]);  +               gtHi--; unHi--; continue;  +            }; +            if (n <  0) break; +            unHi--; +         } +         if (unLo > unHi) break; +         mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; +      } + +      AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + +      if (gtHi < ltLo) { +         mpush(lo, hi, d+1 ); +         continue; +      } + +      n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); +      m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + +      n = lo + unLo - ltLo - 1; +      m = hi - (gtHi - unHi) + 1; + +      nextLo[0] = lo;  nextHi[0] = n;   nextD[0] = d; +      nextLo[1] = m;   nextHi[1] = hi;  nextD[1] = d; +      nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + +      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); +      if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); +      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + +      AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); +      AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + +      mpush (nextLo[0], nextHi[0], nextD[0]); +      mpush (nextLo[1], nextHi[1], nextD[1]); +      mpush (nextLo[2], nextHi[2], nextD[2]); +   } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: +      nblock > N_OVERSHOOT +      block32 exists for [0 .. nblock-1 +N_OVERSHOOT] +      ((UChar*)block32) [0 .. nblock-1] holds block +      ptr exists for [0 .. nblock-1] + +   Post: +      ((UChar*)block32) [0 .. nblock-1] holds block +      All other areas of block32 destroyed +      ftab [0 .. 65536 ] destroyed +      ptr [0 .. nblock-1] holds sorted order +      if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr,  +                UChar*  block, +                UInt16* quadrant,  +                UInt32* ftab, +                Int32   nblock, +                Int32   verb, +                Int32*  budget ) +{ +   Int32  i, j, k, ss, sb; +   Int32  runningOrder[256]; +   Bool   bigDone[256]; +   Int32  copyStart[256]; +   Int32  copyEnd  [256]; +   UChar  c1; +   Int32  numQSorted; +   UInt16 s; +   if (verb >= 4) VPrintf0 ( "        main sort initialise ...\n" ); + +   /*-- set up the 2-byte frequency table --*/ +   for (i = 65536; i >= 0; i--) ftab[i] = 0; + +   j = block[0] << 8; +   i = nblock-1; +   for (; i >= 3; i -= 4) { +      quadrant[i] = 0; +      j = (j >> 8) | ( ((UInt16)block[i]) << 8); +      ftab[j]++; +      quadrant[i-1] = 0; +      j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); +      ftab[j]++; +      quadrant[i-2] = 0; +      j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); +      ftab[j]++; +      quadrant[i-3] = 0; +      j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); +      ftab[j]++; +   } +   for (; i >= 0; i--) { +      quadrant[i] = 0; +      j = (j >> 8) | ( ((UInt16)block[i]) << 8); +      ftab[j]++; +   } + +   /*-- (emphasises close relationship of block & quadrant) --*/ +   for (i = 0; i < BZ_N_OVERSHOOT; i++) { +      block   [nblock+i] = block[i]; +      quadrant[nblock+i] = 0; +   } + +   if (verb >= 4) VPrintf0 ( "        bucket sorting ...\n" ); + +   /*-- Complete the initial radix sort --*/ +   for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + +   s = block[0] << 8; +   i = nblock-1; +   for (; i >= 3; i -= 4) { +      s = (s >> 8) | (block[i] << 8); +      j = ftab[s] -1; +      ftab[s] = j; +      ptr[j] = i; +      s = (s >> 8) | (block[i-1] << 8); +      j = ftab[s] -1; +      ftab[s] = j; +      ptr[j] = i-1; +      s = (s >> 8) | (block[i-2] << 8); +      j = ftab[s] -1; +      ftab[s] = j; +      ptr[j] = i-2; +      s = (s >> 8) | (block[i-3] << 8); +      j = ftab[s] -1; +      ftab[s] = j; +      ptr[j] = i-3; +   } +   for (; i >= 0; i--) { +      s = (s >> 8) | (block[i] << 8); +      j = ftab[s] -1; +      ftab[s] = j; +      ptr[j] = i; +   } + +   /*-- +      Now ftab contains the first loc of every small bucket. +      Calculate the running order, from smallest to largest +      big bucket. +   --*/ +   for (i = 0; i <= 255; i++) { +      bigDone     [i] = False; +      runningOrder[i] = i; +   } + +   { +      Int32 vv; +      Int32 h = 1; +      do h = 3 * h + 1; while (h <= 256); +      do { +         h = h / 3; +         for (i = h; i <= 255; i++) { +            vv = runningOrder[i]; +            j = i; +            while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { +               runningOrder[j] = runningOrder[j-h]; +               j = j - h; +               if (j <= (h - 1)) goto zero; +            } +            zero: +            runningOrder[j] = vv; +         } +      } while (h != 1); +   } + +   /*-- +      The main sorting loop. +   --*/ + +   numQSorted = 0; + +   for (i = 0; i <= 255; i++) { + +      /*-- +         Process big buckets, starting with the least full. +         Basically this is a 3-step process in which we call +         mainQSort3 to sort the small buckets [ss, j], but +         also make a big effort to avoid the calls if we can. +      --*/ +      ss = runningOrder[i]; + +      /*-- +         Step 1: +         Complete the big bucket [ss] by quicksorting +         any unsorted small buckets [ss, j], for j != ss.   +         Hopefully previous pointer-scanning phases have already +         completed many of the small buckets [ss, j], so +         we don't have to sort them at all. +      --*/ +      for (j = 0; j <= 255; j++) { +         if (j != ss) { +            sb = (ss << 8) + j; +            if ( ! (ftab[sb] & SETMASK) ) { +               Int32 lo = ftab[sb]   & CLEARMASK; +               Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; +               if (hi > lo) { +                  if (verb >= 4) +                     VPrintf4 ( "        qsort [0x%x, 0x%x]   " +                                "done %d   this %d\n", +                                ss, j, numQSorted, hi - lo + 1 ); +                  mainQSort3 (  +                     ptr, block, quadrant, nblock,  +                     lo, hi, BZ_N_RADIX, budget  +                  );    +                  numQSorted += (hi - lo + 1); +                  if (*budget < 0) return; +               } +            } +            ftab[sb] |= SETMASK; +         } +      } + +      AssertH ( !bigDone[ss], 1006 ); + +      /*-- +         Step 2: +         Now scan this big bucket [ss] so as to synthesise the +         sorted order for small buckets [t, ss] for all t, +         including, magically, the bucket [ss,ss] too. +         This will avoid doing Real Work in subsequent Step 1's. +      --*/ +      { +         for (j = 0; j <= 255; j++) { +            copyStart[j] =  ftab[(j << 8) + ss]     & CLEARMASK; +            copyEnd  [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; +         } +         for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { +            k = ptr[j]-1; if (k < 0) k += nblock; +            c1 = block[k]; +            if (!bigDone[c1]) +               ptr[ copyStart[c1]++ ] = k; +         } +         for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { +            k = ptr[j]-1; if (k < 0) k += nblock; +            c1 = block[k]; +            if (!bigDone[c1])  +               ptr[ copyEnd[c1]-- ] = k; +         } +      } + +      AssertH ( (copyStart[ss]-1 == copyEnd[ss]) +                ||  +                /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. +                   Necessity for this case is demonstrated by compressing  +                   a sequence of approximately 48.5 million of character  +                   251; 1.0.0/1.0.1 will then die here. */ +                (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), +                1007 ) + +      for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + +      /*-- +         Step 3: +         The [ss] big bucket is now done.  Record this fact, +         and update the quadrant descriptors.  Remember to +         update quadrants in the overshoot area too, if +         necessary.  The "if (i < 255)" test merely skips +         this updating for the last bucket processed, since +         updating for the last bucket is pointless. + +         The quadrant array provides a way to incrementally +         cache sort orderings, as they appear, so as to  +         make subsequent comparisons in fullGtU() complete +         faster.  For repetitive blocks this makes a big +         difference (but not big enough to be able to avoid +         the fallback sorting mechanism, exponential radix sort). + +         The precise meaning is: at all times: + +            for 0 <= i < nblock and 0 <= j <= nblock + +            if block[i] != block[j],  + +               then the relative values of quadrant[i] and  +                    quadrant[j] are meaningless. + +               else { +                  if quadrant[i] < quadrant[j] +                     then the string starting at i lexicographically +                     precedes the string starting at j + +                  else if quadrant[i] > quadrant[j] +                     then the string starting at j lexicographically +                     precedes the string starting at i + +                  else +                     the relative ordering of the strings starting +                     at i and j has not yet been determined. +               } +      --*/ +      bigDone[ss] = True; + +      if (i < 255) { +         Int32 bbStart  = ftab[ss << 8] & CLEARMASK; +         Int32 bbSize   = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; +         Int32 shifts   = 0; + +         while ((bbSize >> shifts) > 65534) shifts++; + +         for (j = bbSize-1; j >= 0; j--) { +            Int32 a2update     = ptr[bbStart + j]; +            UInt16 qVal        = (UInt16)(j >> shifts); +            quadrant[a2update] = qVal; +            if (a2update < BZ_N_OVERSHOOT) +               quadrant[a2update + nblock] = qVal; +         } +         AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); +      } + +   } + +   if (verb >= 4) +      VPrintf3 ( "        %d pointers, %d sorted, %d scanned\n", +                 nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: +      nblock > 0 +      arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] +      ((UChar*)arr2)  [0 .. nblock-1] holds block +      arr1 exists for [0 .. nblock-1] + +   Post: +      ((UChar*)arr2) [0 .. nblock-1] holds block +      All other areas of block destroyed +      ftab [ 0 .. 65536 ] destroyed +      arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ +   UInt32* ptr    = s->ptr;  +   UChar*  block  = s->block; +   UInt32* ftab   = s->ftab; +   Int32   nblock = s->nblock; +   Int32   verb   = s->verbosity; +   Int32   wfact  = s->workFactor; +   UInt16* quadrant; +   Int32   budget; +   Int32   budgetInit; +   Int32   i; + +   if (nblock < 10000) { +      fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); +   } else { +      /* Calculate the location for quadrant, remembering to get +         the alignment right.  Assumes that &(block[0]) is at least +         2-byte aligned -- this should be ok since block is really +         the first section of arr2. +      */ +      i = nblock+BZ_N_OVERSHOOT; +      if (i & 1) i++; +      quadrant = (UInt16*)(&(block[i])); + +      /* (wfact-1) / 3 puts the default-factor-30 +         transition point at very roughly the same place as  +         with v0.1 and v0.9.0.   +         Not that it particularly matters any more, since the +         resulting compressed stream is now the same regardless +         of whether or not we use the main sort or fallback sort. +      */ +      if (wfact < 1  ) wfact = 1; +      if (wfact > 100) wfact = 100; +      budgetInit = nblock * ((wfact-1) / 3); +      budget = budgetInit; + +      mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); +      if (verb >= 3)  +         VPrintf3 ( "      %d work, %d block, ratio %5.2f\n", +                    budgetInit - budget, +                    nblock,  +                    (float)(budgetInit - budget) / +                    (float)(nblock==0 ? 1 : nblock) );  +      if (budget < 0) { +         if (verb >= 2)  +            VPrintf0 ( "    too repetitive; using fallback" +                       " sorting algorithm\n" ); +         fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); +      } +   } + +   s->origPtr = -1; +   for (i = 0; i < s->nblock; i++) +      if (ptr[i] == 0) +         { s->origPtr = i; break; }; + +   AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end                                       blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/bzlib.c b/src/bzip2/bzlib.c new file mode 100644 index 0000000..b98f3e5 --- /dev/null +++ b/src/bzip2/bzlib.c @@ -0,0 +1,1573 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions.                          ---*/ +/*---                                               bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + +/* CHANGES +   0.9.0    -- original version. +   0.9.0a/b -- no changes in this file. +   0.9.0c   -- made zero-length BZ_FLUSH work correctly in bzCompress(). +     fixed bzWrite/bzRead to ignore zero-length requests. +     fixed bzread to correctly handle read requests after EOF. +     wrong parameter order in call to bzDecompressInit in +     bzBuffToBuffDecompress.  Fixed. +*/ + +#define _CRT_SECURE_NO_WARNINGS +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff                           ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ +   fprintf(stderr,  +      "\n\nbzip2/libbzip2: internal error number %d.\n" +      "This is a bug in bzip2/libbzip2, %s.\n" +      "Please report it to me at: jseward@bzip.org.  If this happened\n" +      "when you were using some program which uses libbzip2 as a\n" +      "component, you should also report this bug to the author(s)\n" +      "of that program.  Please make an effort to report this bug;\n" +      "timely and accurate bug reports eventually lead to higher\n" +      "quality software.  Thanks.  Julian Seward, 10 December 2007.\n\n", +      errcode, +      BZ2_bzlibVersion() +   ); + +   if (errcode == 1007) { +   fprintf(stderr, +      "\n*** A special note about internal error number 1007 ***\n" +      "\n" +      "Experience suggests that a common cause of i.e. 1007\n" +      "is unreliable memory or other hardware.  The 1007 assertion\n" +      "just happens to cross-check the results of huge numbers of\n" +      "memory reads/writes, and so acts (unintendedly) as a stress\n" +      "test of your memory system.\n" +      "\n" +      "I suggest the following: try compressing the file again,\n" +      "possibly monitoring progress in detail with the -vv flag.\n" +      "\n" +      "* If the error cannot be reproduced, and/or happens at different\n" +      "  points in compression, you may have a flaky memory system.\n" +      "  Try a memory-test program.  I have used Memtest86\n" +      "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n" +      "  Memtest86 tests memory much more thorougly than your BIOSs\n" +      "  power-on test, and may find failures that the BIOS doesn't.\n" +      "\n" +      "* If the error can be repeatably reproduced, this is a bug in\n" +      "  bzip2, and I would very much like to hear about it.  Please\n" +      "  let me know, and, ideally, save a copy of the file causing the\n" +      "  problem -- without which I will be unable to investigate it.\n" +      "\n" +   ); +   } + +   exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ +   if (sizeof(int)   != 4) return 0; +   if (sizeof(short) != 2) return 0; +   if (sizeof(char)  != 1) return 0; +   return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ +   void* v = malloc ( items * size ); +   return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ +   if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ +   Int32 i; +   s->nblock = 0; +   s->numZ = 0; +   s->state_out_pos = 0; +   BZ_INITIALISE_CRC ( s->blockCRC ); +   for (i = 0; i < 256; i++) s->inUse[i] = False; +   s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ +   s->state_in_ch  = 256; +   s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ +   if (s->state_in_ch < 256 && s->state_in_len > 0) +      return False; else +      return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit)  +                    ( bz_stream* strm,  +                     int        blockSize100k, +                     int        verbosity, +                     int        workFactor ) +{ +   Int32   n; +   EState* s; + +   if (!bz_config_ok()) return BZ_CONFIG_ERROR; + +   if (strm == NULL ||  +       blockSize100k < 1 || blockSize100k > 9 || +       workFactor < 0 || workFactor > 250) +     return BZ_PARAM_ERROR; + +   if (workFactor == 0) workFactor = 30; +   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; +   if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + +   s = BZALLOC( sizeof(EState) ); +   if (s == NULL) return BZ_MEM_ERROR; +   s->strm = strm; + +   s->arr1 = NULL; +   s->arr2 = NULL; +   s->ftab = NULL; + +   n       = 100000 * blockSize100k; +   s->arr1 = BZALLOC( n                  * sizeof(UInt32) ); +   s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); +   s->ftab = BZALLOC( 65537              * sizeof(UInt32) ); + +   if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { +      if (s->arr1 != NULL) BZFREE(s->arr1); +      if (s->arr2 != NULL) BZFREE(s->arr2); +      if (s->ftab != NULL) BZFREE(s->ftab); +      if (s       != NULL) BZFREE(s); +      return BZ_MEM_ERROR; +   } + +   s->blockNo           = 0; +   s->state             = BZ_S_INPUT; +   s->mode              = BZ_M_RUNNING; +   s->combinedCRC       = 0; +   s->blockSize100k     = blockSize100k; +   s->nblockMAX         = 100000 * blockSize100k - 19; +   s->verbosity         = verbosity; +   s->workFactor        = workFactor; + +   s->block             = (UChar*)s->arr2; +   s->mtfv              = (UInt16*)s->arr1; +   s->zbits             = NULL; +   s->ptr               = (UInt32*)s->arr1; + +   strm->state          = s; +   strm->total_in_lo32  = 0; +   strm->total_in_hi32  = 0; +   strm->total_out_lo32 = 0; +   strm->total_out_hi32 = 0; +   init_RL ( s ); +   prepare_new_block ( s ); +   return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ +   Int32 i; +   UChar ch = (UChar)(s->state_in_ch); +   for (i = 0; i < s->state_in_len; i++) { +      BZ_UPDATE_CRC( s->blockCRC, ch ); +   } +   s->inUse[s->state_in_ch] = True; +   switch (s->state_in_len) { +      case 1: +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         break; +      case 2: +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         break; +      case 3: +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         break; +      default: +         s->inUse[s->state_in_len-4] = True; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = (UChar)ch; s->nblock++; +         s->block[s->nblock] = ((UChar)(s->state_in_len-4)); +         s->nblock++; +         break; +   } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ +   if (s->state_in_ch < 256) add_pair_to_block ( s ); +   init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0)               \ +{                                                 \ +   UInt32 zchh = (UInt32)(zchh0);                 \ +   /*-- fast track the common case --*/           \ +   if (zchh != zs->state_in_ch &&                 \ +       zs->state_in_len == 1) {                   \ +      UChar ch = (UChar)(zs->state_in_ch);        \ +      BZ_UPDATE_CRC( zs->blockCRC, ch );          \ +      zs->inUse[zs->state_in_ch] = True;          \ +      zs->block[zs->nblock] = (UChar)ch;          \ +      zs->nblock++;                               \ +      zs->state_in_ch = zchh;                     \ +   }                                              \ +   else                                           \ +   /*-- general, uncommon cases --*/              \ +   if (zchh != zs->state_in_ch ||                 \ +      zs->state_in_len == 255) {                  \ +      if (zs->state_in_ch < 256)                  \ +         add_pair_to_block ( zs );                \ +      zs->state_in_ch = zchh;                     \ +      zs->state_in_len = 1;                       \ +   } else {                                       \ +      zs->state_in_len++;                         \ +   }                                              \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ +   Bool progress_in = False; + +   if (s->mode == BZ_M_RUNNING) { + +      /*-- fast track the common case --*/ +      while (True) { +         /*-- block full? --*/ +         if (s->nblock >= s->nblockMAX) break; +         /*-- no input? --*/ +         if (s->strm->avail_in == 0) break; +         progress_in = True; +         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) );  +         s->strm->next_in++; +         s->strm->avail_in--; +         s->strm->total_in_lo32++; +         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; +      } + +   } else { + +      /*-- general, uncommon case --*/ +      while (True) { +         /*-- block full? --*/ +         if (s->nblock >= s->nblockMAX) break; +         /*-- no input? --*/ +         if (s->strm->avail_in == 0) break; +         /*-- flush/finish end? --*/ +         if (s->avail_in_expect == 0) break; +         progress_in = True; +         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) );  +         s->strm->next_in++; +         s->strm->avail_in--; +         s->strm->total_in_lo32++; +         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; +         s->avail_in_expect--; +      } +   } +   return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ +   Bool progress_out = False; + +   while (True) { + +      /*-- no output space? --*/ +      if (s->strm->avail_out == 0) break; + +      /*-- block done? --*/ +      if (s->state_out_pos >= s->numZ) break; + +      progress_out = True; +      *(s->strm->next_out) = s->zbits[s->state_out_pos]; +      s->state_out_pos++; +      s->strm->avail_out--; +      s->strm->next_out++; +      s->strm->total_out_lo32++; +      if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; +   } + +   return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ +   Bool progress_in  = False; +   Bool progress_out = False; +   EState* s = strm->state; +    +   while (True) { + +      if (s->state == BZ_S_OUTPUT) { +         progress_out |= copy_output_until_stop ( s ); +         if (s->state_out_pos < s->numZ) break; +         if (s->mode == BZ_M_FINISHING &&  +             s->avail_in_expect == 0 && +             isempty_RL(s)) break; +         prepare_new_block ( s ); +         s->state = BZ_S_INPUT; +         if (s->mode == BZ_M_FLUSHING &&  +             s->avail_in_expect == 0 && +             isempty_RL(s)) break; +      } + +      if (s->state == BZ_S_INPUT) { +         progress_in |= copy_input_until_stop ( s ); +         if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { +            flush_RL ( s ); +            BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); +            s->state = BZ_S_OUTPUT; +         } +         else +         if (s->nblock >= s->nblockMAX) { +            BZ2_compressBlock ( s, False ); +            s->state = BZ_S_OUTPUT; +         } +         else +         if (s->strm->avail_in == 0) { +            break; +         } +      } + +   } + +   return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ +   Bool progress; +   EState* s; +   if (strm == NULL) return BZ_PARAM_ERROR; +   s = strm->state; +   if (s == NULL) return BZ_PARAM_ERROR; +   if (s->strm != strm) return BZ_PARAM_ERROR; + +   preswitch: +   switch (s->mode) { + +      case BZ_M_IDLE: +         return BZ_SEQUENCE_ERROR; + +      case BZ_M_RUNNING: +         if (action == BZ_RUN) { +            progress = handle_compress ( strm ); +            return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; +         }  +         else +	 if (action == BZ_FLUSH) { +            s->avail_in_expect = strm->avail_in; +            s->mode = BZ_M_FLUSHING; +            goto preswitch; +         } +         else +         if (action == BZ_FINISH) { +            s->avail_in_expect = strm->avail_in; +            s->mode = BZ_M_FINISHING; +            goto preswitch; +         } +         else  +            return BZ_PARAM_ERROR; + +      case BZ_M_FLUSHING: +         if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; +         if (s->avail_in_expect != s->strm->avail_in)  +            return BZ_SEQUENCE_ERROR; +         progress = handle_compress ( strm ); +         if (s->avail_in_expect > 0 || !isempty_RL(s) || +             s->state_out_pos < s->numZ) return BZ_FLUSH_OK; +         s->mode = BZ_M_RUNNING; +         return BZ_RUN_OK; + +      case BZ_M_FINISHING: +         if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; +         if (s->avail_in_expect != s->strm->avail_in)  +            return BZ_SEQUENCE_ERROR; +         progress = handle_compress ( strm ); +         if (!progress) return BZ_SEQUENCE_ERROR; +         if (s->avail_in_expect > 0 || !isempty_RL(s) || +             s->state_out_pos < s->numZ) return BZ_FINISH_OK; +         s->mode = BZ_M_IDLE; +         return BZ_STREAM_END; +   } +   return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd)  ( bz_stream *strm ) +{ +   EState* s; +   if (strm == NULL) return BZ_PARAM_ERROR; +   s = strm->state; +   if (s == NULL) return BZ_PARAM_ERROR; +   if (s->strm != strm) return BZ_PARAM_ERROR; + +   if (s->arr1 != NULL) BZFREE(s->arr1); +   if (s->arr2 != NULL) BZFREE(s->arr2); +   if (s->ftab != NULL) BZFREE(s->ftab); +   BZFREE(strm->state); + +   strm->state = NULL;    + +   return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff                         ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit)  +                     ( bz_stream* strm,  +                       int        verbosity, +                       int        small ) +{ +   DState* s; + +   if (!bz_config_ok()) return BZ_CONFIG_ERROR; + +   if (strm == NULL) return BZ_PARAM_ERROR; +   if (small != 0 && small != 1) return BZ_PARAM_ERROR; +   if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + +   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; +   if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + +   s = BZALLOC( sizeof(DState) ); +   if (s == NULL) return BZ_MEM_ERROR; +   s->strm                  = strm; +   strm->state              = s; +   s->state                 = BZ_X_MAGIC_1; +   s->bsLive                = 0; +   s->bsBuff                = 0; +   s->calculatedCombinedCRC = 0; +   strm->total_in_lo32      = 0; +   strm->total_in_hi32      = 0; +   strm->total_out_lo32     = 0; +   strm->total_out_hi32     = 0; +   s->smallDecompress       = (Bool)small; +   s->ll4                   = NULL; +   s->ll16                  = NULL; +   s->tt                    = NULL; +   s->currBlockNo           = 0; +   s->verbosity             = verbosity; + +   return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return  True iff data corruption is discovered. +   Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ +   UChar k1; + +   if (s->blockRandomised) { + +      while (True) { +         /* try to finish existing run */ +         while (True) { +            if (s->strm->avail_out == 0) return False; +            if (s->state_out_len == 0) break; +            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; +            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); +            s->state_out_len--; +            s->strm->next_out++; +            s->strm->avail_out--; +            s->strm->total_out_lo32++; +            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; +         } + +         /* can a new run be started? */ +         if (s->nblock_used == s->save_nblock+1) return False; +                +         /* Only caused by corrupt data stream? */ +         if (s->nblock_used > s->save_nblock+1) +            return True; +    +         s->state_out_len = 1; +         s->state_out_ch = s->k0; +         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 2; +         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 3; +         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         s->state_out_len = ((Int32)k1) + 4; +         BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;  +         s->k0 ^= BZ_RAND_MASK; s->nblock_used++; +      } + +   } else { + +      /* restore */ +      UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC; +      UChar         c_state_out_ch       = s->state_out_ch; +      Int32         c_state_out_len      = s->state_out_len; +      Int32         c_nblock_used        = s->nblock_used; +      Int32         c_k0                 = s->k0; +      UInt32*       c_tt                 = s->tt; +      UInt32        c_tPos               = s->tPos; +      char*         cs_next_out          = s->strm->next_out; +      unsigned int  cs_avail_out         = s->strm->avail_out; +      Int32         ro_blockSize100k     = s->blockSize100k; +      /* end restore */ + +      UInt32       avail_out_INIT = cs_avail_out; +      Int32        s_save_nblockPP = s->save_nblock+1; +      unsigned int total_out_lo32_old; + +      while (True) { + +         /* try to finish existing run */ +         if (c_state_out_len > 0) { +            while (True) { +               if (cs_avail_out == 0) goto return_notr; +               if (c_state_out_len == 1) break; +               *( (UChar*)(cs_next_out) ) = c_state_out_ch; +               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); +               c_state_out_len--; +               cs_next_out++; +               cs_avail_out--; +            } +            s_state_out_len_eq_one: +            { +               if (cs_avail_out == 0) {  +                  c_state_out_len = 1; goto return_notr; +               }; +               *( (UChar*)(cs_next_out) ) = c_state_out_ch; +               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); +               cs_next_out++; +               cs_avail_out--; +            } +         }    +         /* Only caused by corrupt data stream? */ +         if (c_nblock_used > s_save_nblockPP) +            return True; + +         /* can a new run be started? */ +         if (c_nblock_used == s_save_nblockPP) { +            c_state_out_len = 0; goto return_notr; +         };    +         c_state_out_ch = c_k0; +         BZ_GET_FAST_C(k1); c_nblock_used++; +         if (k1 != c_k0) {  +            c_k0 = k1; goto s_state_out_len_eq_one;  +         }; +         if (c_nblock_used == s_save_nblockPP)  +            goto s_state_out_len_eq_one; +    +         c_state_out_len = 2; +         BZ_GET_FAST_C(k1); c_nblock_used++; +         if (c_nblock_used == s_save_nblockPP) continue; +         if (k1 != c_k0) { c_k0 = k1; continue; }; +    +         c_state_out_len = 3; +         BZ_GET_FAST_C(k1); c_nblock_used++; +         if (c_nblock_used == s_save_nblockPP) continue; +         if (k1 != c_k0) { c_k0 = k1; continue; }; +    +         BZ_GET_FAST_C(k1); c_nblock_used++; +         c_state_out_len = ((Int32)k1) + 4; +         BZ_GET_FAST_C(c_k0); c_nblock_used++; +      } + +      return_notr: +      total_out_lo32_old = s->strm->total_out_lo32; +      s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); +      if (s->strm->total_out_lo32 < total_out_lo32_old) +         s->strm->total_out_hi32++; + +      /* save */ +      s->calculatedBlockCRC = c_calculatedBlockCRC; +      s->state_out_ch       = c_state_out_ch; +      s->state_out_len      = c_state_out_len; +      s->nblock_used        = c_nblock_used; +      s->k0                 = c_k0; +      s->tt                 = c_tt; +      s->tPos               = c_tPos; +      s->strm->next_out     = cs_next_out; +      s->strm->avail_out    = cs_avail_out; +      /* end save */ +   } +   return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ +   Int32 nb, na, mid; +   nb = 0; +   na = 256; +   do { +      mid = (nb + na) >> 1; +      if (indx >= cftab[mid]) nb = mid; else na = mid; +   } +   while (na - nb != 1); +   return nb; +} + + +/*---------------------------------------------------*/ +/* Return  True iff data corruption is discovered. +   Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ +   UChar k1; + +   if (s->blockRandomised) { + +      while (True) { +         /* try to finish existing run */ +         while (True) { +            if (s->strm->avail_out == 0) return False; +            if (s->state_out_len == 0) break; +            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; +            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); +            s->state_out_len--; +            s->strm->next_out++; +            s->strm->avail_out--; +            s->strm->total_out_lo32++; +            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; +         } +    +         /* can a new run be started? */ +         if (s->nblock_used == s->save_nblock+1) return False; + +         /* Only caused by corrupt data stream? */ +         if (s->nblock_used > s->save_nblock+1) +            return True; +    +         s->state_out_len = 1; +         s->state_out_ch = s->k0; +         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 2; +         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 3; +         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;  +         k1 ^= BZ_RAND_MASK; s->nblock_used++; +         s->state_out_len = ((Int32)k1) + 4; +         BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;  +         s->k0 ^= BZ_RAND_MASK; s->nblock_used++; +      } + +   } else { + +      while (True) { +         /* try to finish existing run */ +         while (True) { +            if (s->strm->avail_out == 0) return False; +            if (s->state_out_len == 0) break; +            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; +            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); +            s->state_out_len--; +            s->strm->next_out++; +            s->strm->avail_out--; +            s->strm->total_out_lo32++; +            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; +         } +    +         /* can a new run be started? */ +         if (s->nblock_used == s->save_nblock+1) return False; + +         /* Only caused by corrupt data stream? */ +         if (s->nblock_used > s->save_nblock+1) +            return True; +    +         s->state_out_len = 1; +         s->state_out_ch = s->k0; +         BZ_GET_SMALL(k1); s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 2; +         BZ_GET_SMALL(k1); s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         s->state_out_len = 3; +         BZ_GET_SMALL(k1); s->nblock_used++; +         if (s->nblock_used == s->save_nblock+1) continue; +         if (k1 != s->k0) { s->k0 = k1; continue; }; +    +         BZ_GET_SMALL(k1); s->nblock_used++; +         s->state_out_len = ((Int32)k1) + 4; +         BZ_GET_SMALL(s->k0); s->nblock_used++; +      } + +   } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ +   Bool    corrupt; +   DState* s; +   if (strm == NULL) return BZ_PARAM_ERROR; +   s = strm->state; +   if (s == NULL) return BZ_PARAM_ERROR; +   if (s->strm != strm) return BZ_PARAM_ERROR; + +   while (True) { +      if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; +      if (s->state == BZ_X_OUTPUT) { +         if (s->smallDecompress) +            corrupt = unRLE_obuf_to_output_SMALL ( s ); else +            corrupt = unRLE_obuf_to_output_FAST  ( s ); +         if (corrupt) return BZ_DATA_ERROR; +         if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { +            BZ_FINALISE_CRC ( s->calculatedBlockCRC ); +            if (s->verbosity >= 3)  +               VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC,  +                          s->calculatedBlockCRC ); +            if (s->verbosity >= 2) VPrintf0 ( "]" ); +            if (s->calculatedBlockCRC != s->storedBlockCRC) +               return BZ_DATA_ERROR; +            s->calculatedCombinedCRC  +               = (s->calculatedCombinedCRC << 1) |  +                    (s->calculatedCombinedCRC >> 31); +            s->calculatedCombinedCRC ^= s->calculatedBlockCRC; +            s->state = BZ_X_BLKHDR_1; +         } else { +            return BZ_OK; +         } +      } +      if (s->state >= BZ_X_MAGIC_1) { +         Int32 r = BZ2_decompress ( s ); +         if (r == BZ_STREAM_END) { +            if (s->verbosity >= 3) +               VPrintf2 ( "\n    combined CRCs: stored = 0x%08x, computed = 0x%08x",  +                          s->storedCombinedCRC, s->calculatedCombinedCRC ); +            if (s->calculatedCombinedCRC != s->storedCombinedCRC) +               return BZ_DATA_ERROR; +            return r; +         } +         if (s->state != BZ_X_OUTPUT) return r; +      } +   } + +   AssertH ( 0, 6001 ); + +   return 0;  /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm ) +{ +   DState* s; +   if (strm == NULL) return BZ_PARAM_ERROR; +   s = strm->state; +   if (s == NULL) return BZ_PARAM_ERROR; +   if (s->strm != strm) return BZ_PARAM_ERROR; + +   if (s->tt   != NULL) BZFREE(s->tt); +   if (s->ll16 != NULL) BZFREE(s->ll16); +   if (s->ll4  != NULL) BZFREE(s->ll4); + +   BZFREE(strm->state); +   strm->state = NULL; + +   return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff                              ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee)                    \ +{                                         \ +   if (bzerror != NULL) *bzerror = eee;   \ +   if (bzf != NULL) bzf->lastErr = eee;   \ +} + +typedef  +   struct { +      FILE*     handle; +      Char      buf[BZ_MAX_UNUSED]; +      Int32     bufN; +      Bool      writing; +      bz_stream strm; +      Int32     lastErr; +      Bool      initialisedOk; +   } +   bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ +   Int32 c = fgetc ( f ); +   if (c == EOF) return True; +   ungetc ( c, f ); +   return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen)  +                    ( int*  bzerror,       +                      FILE* f,  +                      int   blockSize100k,  +                      int   verbosity, +                      int   workFactor ) +{ +   Int32   ret; +   bzFile* bzf = NULL; + +   BZ_SETERR(BZ_OK); + +   if (f == NULL || +       (blockSize100k < 1 || blockSize100k > 9) || +       (workFactor < 0 || workFactor > 250) || +       (verbosity < 0 || verbosity > 4)) +      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + +   if (ferror(f)) +      { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + +   bzf = malloc ( sizeof(bzFile) ); +   if (bzf == NULL) +      { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + +   BZ_SETERR(BZ_OK); +   bzf->initialisedOk = False; +   bzf->bufN          = 0; +   bzf->handle        = f; +   bzf->writing       = True; +   bzf->strm.bzalloc  = NULL; +   bzf->strm.bzfree   = NULL; +   bzf->strm.opaque   = NULL; + +   if (workFactor == 0) workFactor = 30; +   ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k,  +                              verbosity, workFactor ); +   if (ret != BZ_OK) +      { BZ_SETERR(ret); free(bzf); return NULL; }; + +   bzf->strm.avail_in = 0; +   bzf->initialisedOk = True; +   return bzf;    +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) +             ( int*    bzerror,  +               BZFILE* b,  +               void*   buf,  +               int     len ) +{ +   Int32 n, n2, ret; +   bzFile* bzf = (bzFile*)b; + +   BZ_SETERR(BZ_OK); +   if (bzf == NULL || buf == NULL || len < 0) +      { BZ_SETERR(BZ_PARAM_ERROR); return; }; +   if (!(bzf->writing)) +      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; +   if (ferror(bzf->handle)) +      { BZ_SETERR(BZ_IO_ERROR); return; }; + +   if (len == 0) +      { BZ_SETERR(BZ_OK); return; }; + +   bzf->strm.avail_in = len; +   bzf->strm.next_in  = buf; + +   while (True) { +      bzf->strm.avail_out = BZ_MAX_UNUSED; +      bzf->strm.next_out = bzf->buf; +      ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); +      if (ret != BZ_RUN_OK) +         { BZ_SETERR(ret); return; }; + +      if (bzf->strm.avail_out < BZ_MAX_UNUSED) { +         n = BZ_MAX_UNUSED - bzf->strm.avail_out; +         n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar),  +                       n, bzf->handle ); +         if (n != n2 || ferror(bzf->handle)) +            { BZ_SETERR(BZ_IO_ERROR); return; }; +      } + +      if (bzf->strm.avail_in == 0) +         { BZ_SETERR(BZ_OK); return; }; +   } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) +                  ( int*          bzerror,  +                    BZFILE*       b,  +                    int           abandon, +                    unsigned int* nbytes_in, +                    unsigned int* nbytes_out ) +{ +   BZ2_bzWriteClose64 ( bzerror, b, abandon,  +                        nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) +                  ( int*          bzerror,  +                    BZFILE*       b,  +                    int           abandon, +                    unsigned int* nbytes_in_lo32, +                    unsigned int* nbytes_in_hi32, +                    unsigned int* nbytes_out_lo32, +                    unsigned int* nbytes_out_hi32 ) +{ +   Int32   n, n2, ret; +   bzFile* bzf = (bzFile*)b; + +   if (bzf == NULL) +      { BZ_SETERR(BZ_OK); return; }; +   if (!(bzf->writing)) +      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; +   if (ferror(bzf->handle)) +      { BZ_SETERR(BZ_IO_ERROR); return; }; + +   if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; +   if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; +   if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; +   if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + +   if ((!abandon) && bzf->lastErr == BZ_OK) { +      while (True) { +         bzf->strm.avail_out = BZ_MAX_UNUSED; +         bzf->strm.next_out = bzf->buf; +         ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); +         if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) +            { BZ_SETERR(ret); return; }; + +         if (bzf->strm.avail_out < BZ_MAX_UNUSED) { +            n = BZ_MAX_UNUSED - bzf->strm.avail_out; +            n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar),  +                          n, bzf->handle ); +            if (n != n2 || ferror(bzf->handle)) +               { BZ_SETERR(BZ_IO_ERROR); return; }; +         } + +         if (ret == BZ_STREAM_END) break; +      } +   } + +   if ( !abandon && !ferror ( bzf->handle ) ) { +      fflush ( bzf->handle ); +      if (ferror(bzf->handle)) +         { BZ_SETERR(BZ_IO_ERROR); return; }; +   } + +   if (nbytes_in_lo32 != NULL) +      *nbytes_in_lo32 = bzf->strm.total_in_lo32; +   if (nbytes_in_hi32 != NULL) +      *nbytes_in_hi32 = bzf->strm.total_in_hi32; +   if (nbytes_out_lo32 != NULL) +      *nbytes_out_lo32 = bzf->strm.total_out_lo32; +   if (nbytes_out_hi32 != NULL) +      *nbytes_out_hi32 = bzf->strm.total_out_hi32; + +   BZ_SETERR(BZ_OK); +   BZ2_bzCompressEnd ( &(bzf->strm) ); +   free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen)  +                   ( int*  bzerror,  +                     FILE* f,  +                     int   verbosity, +                     int   small, +                     void* unused, +                     int   nUnused ) +{ +   bzFile* bzf = NULL; +   int     ret; + +   BZ_SETERR(BZ_OK); + +   if (f == NULL ||  +       (small != 0 && small != 1) || +       (verbosity < 0 || verbosity > 4) || +       (unused == NULL && nUnused != 0) || +       (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) +      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + +   if (ferror(f)) +      { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + +   bzf = malloc ( sizeof(bzFile) ); +   if (bzf == NULL)  +      { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + +   BZ_SETERR(BZ_OK); + +   bzf->initialisedOk = False; +   bzf->handle        = f; +   bzf->bufN          = 0; +   bzf->writing       = False; +   bzf->strm.bzalloc  = NULL; +   bzf->strm.bzfree   = NULL; +   bzf->strm.opaque   = NULL; +    +   while (nUnused > 0) { +      bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; +      unused = ((void*)( 1 + ((UChar*)(unused))  )); +      nUnused--; +   } + +   ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); +   if (ret != BZ_OK) +      { BZ_SETERR(ret); free(bzf); return NULL; }; + +   bzf->strm.avail_in = bzf->bufN; +   bzf->strm.next_in  = bzf->buf; + +   bzf->initialisedOk = True; +   return bzf;    +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ +   bzFile* bzf = (bzFile*)b; + +   BZ_SETERR(BZ_OK); +   if (bzf == NULL) +      { BZ_SETERR(BZ_OK); return; }; + +   if (bzf->writing) +      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + +   if (bzf->initialisedOk) +      (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); +   free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead)  +           ( int*    bzerror,  +             BZFILE* b,  +             void*   buf,  +             int     len ) +{ +   Int32   n, ret; +   bzFile* bzf = (bzFile*)b; + +   BZ_SETERR(BZ_OK); + +   if (bzf == NULL || buf == NULL || len < 0) +      { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + +   if (bzf->writing) +      { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + +   if (len == 0) +      { BZ_SETERR(BZ_OK); return 0; }; + +   bzf->strm.avail_out = len; +   bzf->strm.next_out = buf; + +   while (True) { + +      if (ferror(bzf->handle))  +         { BZ_SETERR(BZ_IO_ERROR); return 0; }; + +      if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { +         n = fread ( bzf->buf, sizeof(UChar),  +                     BZ_MAX_UNUSED, bzf->handle ); +         if (ferror(bzf->handle)) +            { BZ_SETERR(BZ_IO_ERROR); return 0; }; +         bzf->bufN = n; +         bzf->strm.avail_in = bzf->bufN; +         bzf->strm.next_in = bzf->buf; +      } + +      ret = BZ2_bzDecompress ( &(bzf->strm) ); + +      if (ret != BZ_OK && ret != BZ_STREAM_END) +         { BZ_SETERR(ret); return 0; }; + +      if (ret == BZ_OK && myfeof(bzf->handle) &&  +          bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) +         { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + +      if (ret == BZ_STREAM_END) +         { BZ_SETERR(BZ_STREAM_END); +           return len - bzf->strm.avail_out; }; +      if (bzf->strm.avail_out == 0) +         { BZ_SETERR(BZ_OK); return len; }; +       +   } + +   return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused)  +                     ( int*    bzerror,  +                       BZFILE* b,  +                       void**  unused,  +                       int*    nUnused ) +{ +   bzFile* bzf = (bzFile*)b; +   if (bzf == NULL) +      { BZ_SETERR(BZ_PARAM_ERROR); return; }; +   if (bzf->lastErr != BZ_STREAM_END) +      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; +   if (unused == NULL || nUnused == NULL) +      { BZ_SETERR(BZ_PARAM_ERROR); return; }; + +   BZ_SETERR(BZ_OK); +   *nUnused = bzf->strm.avail_in; +   *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff                      ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress)  +                         ( char*         dest,  +                           unsigned int* destLen, +                           char*         source,  +                           unsigned int  sourceLen, +                           int           blockSize100k,  +                           int           verbosity,  +                           int           workFactor ) +{ +   bz_stream strm; +   int ret; + +   if (dest == NULL || destLen == NULL ||  +       source == NULL || +       blockSize100k < 1 || blockSize100k > 9 || +       verbosity < 0 || verbosity > 4 || +       workFactor < 0 || workFactor > 250)  +      return BZ_PARAM_ERROR; + +   if (workFactor == 0) workFactor = 30; +   strm.bzalloc = NULL; +   strm.bzfree = NULL; +   strm.opaque = NULL; +   ret = BZ2_bzCompressInit ( &strm, blockSize100k,  +                              verbosity, workFactor ); +   if (ret != BZ_OK) return ret; + +   strm.next_in = source; +   strm.next_out = dest; +   strm.avail_in = sourceLen; +   strm.avail_out = *destLen; + +   ret = BZ2_bzCompress ( &strm, BZ_FINISH ); +   if (ret == BZ_FINISH_OK) goto output_overflow; +   if (ret != BZ_STREAM_END) goto errhandler; + +   /* normal termination */ +   *destLen -= strm.avail_out;    +   BZ2_bzCompressEnd ( &strm ); +   return BZ_OK; + +   output_overflow: +   BZ2_bzCompressEnd ( &strm ); +   return BZ_OUTBUFF_FULL; + +   errhandler: +   BZ2_bzCompressEnd ( &strm ); +   return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress)  +                           ( char*         dest,  +                             unsigned int* destLen, +                             char*         source,  +                             unsigned int  sourceLen, +                             int           small, +                             int           verbosity ) +{ +   bz_stream strm; +   int ret; + +   if (dest == NULL || destLen == NULL ||  +       source == NULL || +       (small != 0 && small != 1) || +       verbosity < 0 || verbosity > 4)  +          return BZ_PARAM_ERROR; + +   strm.bzalloc = NULL; +   strm.bzfree = NULL; +   strm.opaque = NULL; +   ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); +   if (ret != BZ_OK) return ret; + +   strm.next_in = source; +   strm.next_out = dest; +   strm.avail_in = sourceLen; +   strm.avail_out = *destLen; + +   ret = BZ2_bzDecompress ( &strm ); +   if (ret == BZ_OK) goto output_overflow_or_eof; +   if (ret != BZ_STREAM_END) goto errhandler; + +   /* normal termination */ +   *destLen -= strm.avail_out; +   BZ2_bzDecompressEnd ( &strm ); +   return BZ_OK; + +   output_overflow_or_eof: +   if (strm.avail_out > 0) { +      BZ2_bzDecompressEnd ( &strm ); +      return BZ_UNEXPECTED_EOF; +   } else { +      BZ2_bzDecompressEnd ( &strm ); +      return BZ_OUTBUFF_FULL; +   };       + +   errhandler: +   BZ2_bzDecompressEnd ( &strm ); +   return ret;  +} + + +/*---------------------------------------------------*/ +/*-- +   Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) +   to support better zlib compatibility. +   This code is not _officially_ part of libbzip2 (yet); +   I haven't tested it, documented it, or considered the +   threading-safeness of it. +   If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- +   return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ +   return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +#   include <fcntl.h> +#   include <io.h> +#   define SET_BINARY_MODE(file) _setmode(_fileno(file),O_BINARY) +#else +#   define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen +               ( const char *path,   /* no use when bzdopen */ +                 int fd,             /* no use when bzdopen */ +                 const char *mode, +                 int open_mode)      /* bzopen: 0, bzdopen:1 */ +{ +   int    bzerr; +   char   unused[BZ_MAX_UNUSED]; +   int    blockSize100k = 9; +   int    writing       = 0; +   char   mode2[10]     = ""; +   FILE   *fp           = NULL; +   BZFILE *bzfp         = NULL; +   int    verbosity     = 0; +   int    workFactor    = 30; +   int    smallMode     = 0; +   int    nUnused       = 0;  + +   if (mode == NULL) return NULL; +   while (*mode) { +      switch (*mode) { +      case 'r': +         writing = 0; break; +      case 'w': +         writing = 1; break; +      case 's': +         smallMode = 1; break; +      default: +         if (isdigit((int)(*mode))) { +            blockSize100k = *mode-BZ_HDR_0; +         } +      } +      mode++; +   } +   strcat(mode2, writing ? "w" : "r" ); +   strcat(mode2,"b");   /* binary mode */ + +   if (open_mode==0) { +      if (path==NULL || strcmp(path,"")==0) { +        fp = (writing ? stdout : stdin); +        SET_BINARY_MODE(fp); +      } else { +        fp = fopen(path,mode2); +      } +   } else { +#ifdef BZ_STRICT_ANSI +      fp = NULL; +#else +      fp = _fdopen(fd,mode2); +#endif +   } +   if (fp == NULL) return NULL; + +   if (writing) { +      /* Guard against total chaos and anarchy -- JRS */ +      if (blockSize100k < 1) blockSize100k = 1; +      if (blockSize100k > 9) blockSize100k = 9;  +      bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, +                             verbosity,workFactor); +   } else { +      bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, +                            unused,nUnused); +   } +   if (bzfp == NULL) { +      if (fp != stdin && fp != stdout) fclose(fp); +      return NULL; +   } +   return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- +   open file for read or write. +      ex) bzopen("file","w9") +      case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) +               ( const char *path, +                 const char *mode ) +{ +   return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) +               ( int fd, +                 const char *mode ) +{ +   return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ +   int bzerr, nread; +   if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; +   nread = BZ2_bzRead(&bzerr,b,buf,len); +   if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { +      return nread; +   } else { +      return -1; +   } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ +   int bzerr; + +   BZ2_bzWrite(&bzerr,b,buf,len); +   if(bzerr == BZ_OK){ +      return len; +   }else{ +      return -1; +   } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ +   /* do nothing now... */ +   return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ +   int bzerr; +   FILE *fp; +    +   if (b==NULL) {return;} +   fp = ((bzFile *)b)->handle; +   if(((bzFile*)b)->writing){ +      BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); +      if(bzerr != BZ_OK){ +         BZ2_bzWriteClose(NULL,b,1,NULL,NULL); +      } +   }else{ +      BZ2_bzReadClose(&bzerr,b); +   } +   if(fp!=stdin && fp!=stdout){ +      fclose(fp); +   } +} + + +/*---------------------------------------------------*/ +/*-- +   return last error code  +--*/ +static const char *bzerrorstrings[] = { +       "OK" +      ,"SEQUENCE_ERROR" +      ,"PARAM_ERROR" +      ,"MEM_ERROR" +      ,"DATA_ERROR" +      ,"DATA_ERROR_MAGIC" +      ,"IO_ERROR" +      ,"UNEXPECTED_EOF" +      ,"OUTBUFF_FULL" +      ,"CONFIG_ERROR" +      ,"???"   /* for future */ +      ,"???"   /* for future */ +      ,"???"   /* for future */ +      ,"???"   /* for future */ +      ,"???"   /* for future */ +      ,"???"   /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ +   int err = ((bzFile *)b)->lastErr; + +   if(err>0) err = 0; +   *errnum = err; +   return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end                                           bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/bzlib.h b/src/bzip2/bzlib.h new file mode 100644 index 0000000..c5b75d6 --- /dev/null +++ b/src/bzip2/bzlib.h @@ -0,0 +1,282 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library.                   ---*/ +/*---                                               bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN               0 +#define BZ_FLUSH             1 +#define BZ_FINISH            2 + +#define BZ_OK                0 +#define BZ_RUN_OK            1 +#define BZ_FLUSH_OK          2 +#define BZ_FINISH_OK         3 +#define BZ_STREAM_END        4 +#define BZ_SEQUENCE_ERROR    (-1) +#define BZ_PARAM_ERROR       (-2) +#define BZ_MEM_ERROR         (-3) +#define BZ_DATA_ERROR        (-4) +#define BZ_DATA_ERROR_MAGIC  (-5) +#define BZ_IO_ERROR          (-6) +#define BZ_UNEXPECTED_EOF    (-7) +#define BZ_OUTBUFF_FULL      (-8) +#define BZ_CONFIG_ERROR      (-9) + +typedef  +   struct { +      char *next_in; +      unsigned int avail_in; +      unsigned int total_in_lo32; +      unsigned int total_in_hi32; + +      char *next_out; +      unsigned int avail_out; +      unsigned int total_out_lo32; +      unsigned int total_out_hi32; + +      void *state; + +      void *(*bzalloc)(void *,int,int); +      void (*bzfree)(void *,void *); +      void *opaque; +   }  +   bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include <stdio.h> +#endif + +#ifdef _WIN32 +#   include <windows.h> +#   ifdef small +      /* windows.h define small to char */ +#      undef small +#   endif +#   ifdef BZ_EXPORT +#   define BZ_API(func) WINAPI func +#   define BZ_EXTERN extern +#   else +   /* import windows dll dynamically */ +#   define BZ_API(func) (WINAPI * func) +#   define BZ_EXTERN +#   endif +#else +#   define BZ_API(func) func +#   define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (  +      bz_stream* strm,  +      int        blockSize100k,  +      int        verbosity,  +      int        workFactor  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) (  +      bz_stream* strm,  +      int action  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (  +      bz_stream* strm  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (  +      bz_stream *strm,  +      int       verbosity,  +      int       small +   ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) (  +      bz_stream* strm  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (  +      bz_stream *strm  +   ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (  +      int*  bzerror,    +      FILE* f,  +      int   verbosity,  +      int   small, +      void* unused,     +      int   nUnused  +   ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) (  +      int*    bzerror,  +      BZFILE* b  +   ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (  +      int*    bzerror,  +      BZFILE* b,  +      void**  unused,   +      int*    nUnused  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) (  +      int*    bzerror,  +      BZFILE* b,  +      void*   buf,  +      int     len  +   ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (  +      int*  bzerror,       +      FILE* f,  +      int   blockSize100k,  +      int   verbosity,  +      int   workFactor  +   ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) (  +      int*    bzerror,  +      BZFILE* b,  +      void*   buf,  +      int     len  +   ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (  +      int*          bzerror,  +      BZFILE*       b,  +      int           abandon,  +      unsigned int* nbytes_in,  +      unsigned int* nbytes_out  +   ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (  +      int*          bzerror,  +      BZFILE*       b,  +      int           abandon,  +      unsigned int* nbytes_in_lo32,  +      unsigned int* nbytes_in_hi32,  +      unsigned int* nbytes_out_lo32,  +      unsigned int* nbytes_out_hi32 +   ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (  +      char*         dest,  +      unsigned int* destLen, +      char*         source,  +      unsigned int  sourceLen, +      int           blockSize100k,  +      int           verbosity,  +      int           workFactor  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (  +      char*         dest,  +      unsigned int* destLen, +      char*         source,  +      unsigned int  sourceLen, +      int           small,  +      int           verbosity  +   ); + + +/*-- +   Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) +   to support better zlib compatibility. +   This code is not _officially_ part of libbzip2 (yet); +   I haven't tested it, documented it, or considered the +   threading-safeness of it. +   If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( +      void +   ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( +      const char *path, +      const char *mode +   ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( +      int        fd, +      const char *mode +   ); +          +BZ_EXTERN int BZ_API(BZ2_bzread) ( +      BZFILE* b,  +      void* buf,  +      int len  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( +      BZFILE* b,  +      void*   buf,  +      int     len  +   ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( +      BZFILE* b +   ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( +      BZFILE* b +   ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( +      BZFILE *b,  +      int    *errnum +   ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end                                           bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/bzlib_private.h b/src/bzip2/bzlib_private.h new file mode 100644 index 0000000..2342787 --- /dev/null +++ b/src/bzip2/bzlib_private.h @@ -0,0 +1,509 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library.                  ---*/ +/*---                                       bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include <stdlib.h> + +#ifndef BZ_NO_STDIO +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION  "1.0.5, 10-Dec-2007" + +typedef char            Char; +typedef unsigned char   Bool; +typedef unsigned char   UChar; +typedef int             Int32; +typedef unsigned int    UInt32; +typedef short           Int16; +typedef unsigned short  UInt16; + +#define True  ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__  /* */ +#endif  + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ +   { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ +   { if (!(cond)) {       \ +      fprintf ( stderr,   \ +        "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ +      exit(1); \ +   }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ +   fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ +   fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ +   fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ +   fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ +   fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ +   fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ +   { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg)                do { } while (0) +#define VPrintf0(zf)                     do { } while (0) +#define VPrintf1(zf,za1)                 do { } while (0) +#define VPrintf2(zf,za1,za2)             do { } while (0) +#define VPrintf3(zf,za1,za2,za3)         do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4)     do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp)  (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42   /* 'B' */ +#define BZ_HDR_Z 0x5a   /* 'Z' */ +#define BZ_HDR_h 0x68   /* 'h' */ +#define BZ_HDR_0 0x30   /* '0' */ +   +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN    23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE   50 +#define BZ_N_ITERS  4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS                          \ +   Int32 rNToGo;                               \ +   Int32 rTPos                                 \ + +#define BZ_RAND_INIT_MASK                      \ +   s->rNToGo = 0;                              \ +   s->rTPos  = 0                               \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK                       \ +   if (s->rNToGo == 0) {                       \ +      s->rNToGo = BZ2_rNums[s->rTPos];         \ +      s->rTPos++;                              \ +      if (s->rTPos == 512) s->rTPos = 0;       \ +   }                                           \ +   s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar)              \ +{                                              \ +   crcVar = 0xffffffffL;                       \ +} + +#define BZ_FINALISE_CRC(crcVar)                \ +{                                              \ +   crcVar = ~(crcVar);                         \ +} + +#define BZ_UPDATE_CRC(crcVar,cha)              \ +{                                              \ +   crcVar = (crcVar << 8) ^                    \ +            BZ2_crc32Table[(crcVar >> 24) ^    \ +                           ((UChar)cha)];      \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE      1 +#define BZ_M_RUNNING   2 +#define BZ_M_FLUSHING  3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT    1 +#define BZ_S_INPUT     2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef +   struct { +      /* pointer back to the struct bz_stream */ +      bz_stream* strm; + +      /* mode this stream is in, and whether inputting */ +      /* or outputting data */ +      Int32    mode; +      Int32    state; + +      /* remembers avail_in when flush/finish requested */ +      UInt32   avail_in_expect; + +      /* for doing the block sorting */ +      UInt32*  arr1; +      UInt32*  arr2; +      UInt32*  ftab; +      Int32    origPtr; + +      /* aliases for arr1 and arr2 */ +      UInt32*  ptr; +      UChar*   block; +      UInt16*  mtfv; +      UChar*   zbits; + +      /* for deciding when to use the fallback sorting algorithm */ +      Int32    workFactor; + +      /* run-length-encoding of the input */ +      UInt32   state_in_ch; +      Int32    state_in_len; +      BZ_RAND_DECLS; + +      /* input and output limits and current posns */ +      Int32    nblock; +      Int32    nblockMAX; +      Int32    numZ; +      Int32    state_out_pos; + +      /* map of bytes used in block */ +      Int32    nInUse; +      Bool     inUse[256]; +      UChar    unseqToSeq[256]; + +      /* the buffer for bit stream creation */ +      UInt32   bsBuff; +      Int32    bsLive; + +      /* block and combined CRCs */ +      UInt32   blockCRC; +      UInt32   combinedCRC; + +      /* misc administratium */ +      Int32    verbosity; +      Int32    blockNo; +      Int32    blockSize100k; + +      /* stuff for coding the MTF values */ +      Int32    nMTF; +      Int32    mtfFreq    [BZ_MAX_ALPHA_SIZE]; +      UChar    selector   [BZ_MAX_SELECTORS]; +      UChar    selectorMtf[BZ_MAX_SELECTORS]; + +      UChar    len     [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      Int32    code    [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      Int32    rfreq   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      /* second dimension: only 3 needed; 4 makes index calculations faster */ +      UInt32   len_pack[BZ_MAX_ALPHA_SIZE][4]; + +   } +   EState; + + + +/*-- externs for compression. --*/ + +extern void  +BZ2_blockSort ( EState* ); + +extern void  +BZ2_compressBlock ( EState*, Bool ); + +extern void  +BZ2_bsInitWrite ( EState* ); + +extern void  +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void  +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE        1 +#define BZ_X_OUTPUT      2 + +#define BZ_X_MAGIC_1     10 +#define BZ_X_MAGIC_2     11 +#define BZ_X_MAGIC_3     12 +#define BZ_X_MAGIC_4     13 +#define BZ_X_BLKHDR_1    14 +#define BZ_X_BLKHDR_2    15 +#define BZ_X_BLKHDR_3    16 +#define BZ_X_BLKHDR_4    17 +#define BZ_X_BLKHDR_5    18 +#define BZ_X_BLKHDR_6    19 +#define BZ_X_BCRC_1      20 +#define BZ_X_BCRC_2      21 +#define BZ_X_BCRC_3      22 +#define BZ_X_BCRC_4      23 +#define BZ_X_RANDBIT     24 +#define BZ_X_ORIGPTR_1   25 +#define BZ_X_ORIGPTR_2   26 +#define BZ_X_ORIGPTR_3   27 +#define BZ_X_MAPPING_1   28 +#define BZ_X_MAPPING_2   29 +#define BZ_X_SELECTOR_1  30 +#define BZ_X_SELECTOR_2  31 +#define BZ_X_SELECTOR_3  32 +#define BZ_X_CODING_1    33 +#define BZ_X_CODING_2    34 +#define BZ_X_CODING_3    35 +#define BZ_X_MTF_1       36 +#define BZ_X_MTF_2       37 +#define BZ_X_MTF_3       38 +#define BZ_X_MTF_4       39 +#define BZ_X_MTF_5       40 +#define BZ_X_MTF_6       41 +#define BZ_X_ENDHDR_2    42 +#define BZ_X_ENDHDR_3    43 +#define BZ_X_ENDHDR_4    44 +#define BZ_X_ENDHDR_5    45 +#define BZ_X_ENDHDR_6    46 +#define BZ_X_CCRC_1      47 +#define BZ_X_CCRC_2      48 +#define BZ_X_CCRC_3      49 +#define BZ_X_CCRC_4      50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef +   struct { +      /* pointer back to the struct bz_stream */ +      bz_stream* strm; + +      /* state indicator for this stream */ +      Int32    state; + +      /* for doing the final run-length decoding */ +      UChar    state_out_ch; +      Int32    state_out_len; +      Bool     blockRandomised; +      BZ_RAND_DECLS; + +      /* the buffer for bit stream reading */ +      UInt32   bsBuff; +      Int32    bsLive; + +      /* misc administratium */ +      Int32    blockSize100k; +      Bool     smallDecompress; +      Int32    currBlockNo; +      Int32    verbosity; + +      /* for undoing the Burrows-Wheeler transform */ +      Int32    origPtr; +      UInt32   tPos; +      Int32    k0; +      Int32    unzftab[256]; +      Int32    nblock_used; +      Int32    cftab[257]; +      Int32    cftabCopy[257]; + +      /* for undoing the Burrows-Wheeler transform (FAST) */ +      UInt32   *tt; + +      /* for undoing the Burrows-Wheeler transform (SMALL) */ +      UInt16   *ll16; +      UChar    *ll4; + +      /* stored and calculated CRCs */ +      UInt32   storedBlockCRC; +      UInt32   storedCombinedCRC; +      UInt32   calculatedBlockCRC; +      UInt32   calculatedCombinedCRC; + +      /* map of bytes used in block */ +      Int32    nInUse; +      Bool     inUse[256]; +      Bool     inUse16[16]; +      UChar    seqToUnseq[256]; + +      /* for decoding the MTF values */ +      UChar    mtfa   [MTFA_SIZE]; +      Int32    mtfbase[256 / MTFL_SIZE]; +      UChar    selector   [BZ_MAX_SELECTORS]; +      UChar    selectorMtf[BZ_MAX_SELECTORS]; +      UChar    len  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + +      Int32    limit  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      Int32    base   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      Int32    perm   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +      Int32    minLens[BZ_N_GROUPS]; + +      /* save area for scalars in the main decompress code */ +      Int32    save_i; +      Int32    save_j; +      Int32    save_t; +      Int32    save_alphaSize; +      Int32    save_nGroups; +      Int32    save_nSelectors; +      Int32    save_EOB; +      Int32    save_groupNo; +      Int32    save_groupPos; +      Int32    save_nextSym; +      Int32    save_nblockMAX; +      Int32    save_nblock; +      Int32    save_es; +      Int32    save_N; +      Int32    save_curr; +      Int32    save_zt; +      Int32    save_zn;  +      Int32    save_zvec; +      Int32    save_zj; +      Int32    save_gSel; +      Int32    save_gMinlen; +      Int32*   save_gLimit; +      Int32*   save_gBase; +      Int32*   save_gPerm; + +   } +   DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc)                     \ +    /* c_tPos is unsigned, hence test < 0 is pointless. */ \ +    if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ +    s->tPos = s->tt[s->tPos];                 \ +    cccc = (UChar)(s->tPos & 0xff);           \ +    s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc)                   \ +    /* c_tPos is unsigned, hence test < 0 is pointless. */ \ +    if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ +    c_tPos = c_tt[c_tPos];                    \ +    cccc = (UChar)(c_tPos & 0xff);            \ +    c_tPos >>= 8; + +#define SET_LL4(i,n)                                          \ +   { if (((i) & 0x1) == 0)                                    \ +        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else    \ +        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4);  \ +   } + +#define GET_LL4(i)                             \ +   ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n)                          \ +   { s->ll16[i] = (UInt16)(n & 0x0000ffff);  \ +     SET_LL4(i, n >> 16);                    \ +   } + +#define GET_LL(i) \ +   (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc)                            \ +    /* c_tPos is unsigned, hence test < 0 is pointless. */ \ +    if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ +    cccc = BZ2_indexIntoF ( s->tPos, s->cftab );    \ +    s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32  +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32  +BZ2_decompress ( DState* ); + +extern void  +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, +                           Int32,  Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end                                   bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/compress.c b/src/bzip2/compress.c new file mode 100644 index 0000000..8c80a07 --- /dev/null +++ b/src/bzip2/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting)        ---*/ +/*---                                            compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +/* CHANGES +    0.9.0    -- original version. +    0.9.0a/b -- no changes in this file. +    0.9.0c   -- changed setting of nGroups in sendMTFValues()  +                so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O                              ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ +   s->bsLive = 0; +   s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ +   while (s->bsLive > 0) { +      s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); +      s->numZ++; +      s->bsBuff <<= 8; +      s->bsLive -= 8; +   } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz)                           \ +{                                             \ +   while (s->bsLive >= 8) {                   \ +      s->zbits[s->numZ]                       \ +         = (UChar)(s->bsBuff >> 24);          \ +      s->numZ++;                              \ +      s->bsBuff <<= 8;                        \ +      s->bsLive -= 8;                         \ +   }                                          \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ +   bsNEEDW ( n ); +   s->bsBuff |= (v << (32 - s->bsLive - n)); +   s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ +   bsW ( s, 8, (u >> 24) & 0xffL ); +   bsW ( s, 8, (u >> 16) & 0xffL ); +   bsW ( s, 8, (u >>  8) & 0xffL ); +   bsW ( s, 8,  u        & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ +   bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper                         ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ +   Int32 i; +   s->nInUse = 0; +   for (i = 0; i < 256; i++) +      if (s->inUse[i]) { +         s->unseqToSeq[i] = s->nInUse; +         s->nInUse++; +      } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ +   UChar   yy[256]; +   Int32   i, j; +   Int32   zPend; +   Int32   wr; +   Int32   EOB; + +   /*  +      After sorting (eg, here), +         s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, +         and +         ((UChar*)s->arr2) [ 0 .. s->nblock-1 ]  +         holds the original block data. + +      The first thing to do is generate the MTF values, +      and put them in +         ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. +      Because there are strictly fewer or equal MTF values +      than block values, ptr values in this area are overwritten +      with MTF values only when they are no longer needed. + +      The final compressed bitstream is generated into the +      area starting at +         (UChar*) (&((UChar*)s->arr2)[s->nblock]) + +      These storage aliases are set up in bzCompressInit(), +      except for the last one, which is arranged in  +      compressBlock(). +   */ +   UInt32* ptr   = s->ptr; +   UChar* block  = s->block; +   UInt16* mtfv  = s->mtfv; + +   makeMaps_e ( s ); +   EOB = s->nInUse+1; + +   for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + +   wr = 0; +   zPend = 0; +   for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + +   for (i = 0; i < s->nblock; i++) { +      UChar ll_i; +      AssertD ( wr <= i, "generateMTFValues(1)" ); +      j = ptr[i]-1; if (j < 0) j += s->nblock; +      ll_i = s->unseqToSeq[block[j]]; +      AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + +      if (yy[0] == ll_i) {  +         zPend++; +      } else { + +         if (zPend > 0) { +            zPend--; +            while (True) { +               if (zPend & 1) { +                  mtfv[wr] = BZ_RUNB; wr++;  +                  s->mtfFreq[BZ_RUNB]++;  +               } else { +                  mtfv[wr] = BZ_RUNA; wr++;  +                  s->mtfFreq[BZ_RUNA]++;  +               } +               if (zPend < 2) break; +               zPend = (zPend - 2) / 2; +            }; +            zPend = 0; +         } +         { +            register UChar  rtmp; +            register UChar* ryy_j; +            register UChar  rll_i; +            rtmp  = yy[1]; +            yy[1] = yy[0]; +            ryy_j = &(yy[1]); +            rll_i = ll_i; +            while ( rll_i != rtmp ) { +               register UChar rtmp2; +               ryy_j++; +               rtmp2  = rtmp; +               rtmp   = *ryy_j; +               *ryy_j = rtmp2; +            }; +            yy[0] = rtmp; +            j = ryy_j - &(yy[0]); +            mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; +         } + +      } +   } + +   if (zPend > 0) { +      zPend--; +      while (True) { +         if (zPend & 1) { +            mtfv[wr] = BZ_RUNB; wr++;  +            s->mtfFreq[BZ_RUNB]++;  +         } else { +            mtfv[wr] = BZ_RUNA; wr++;  +            s->mtfFreq[BZ_RUNA]++;  +         } +         if (zPend < 2) break; +         zPend = (zPend - 2) / 2; +      }; +      zPend = 0; +   } + +   mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + +   s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST  0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ +   Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; +   Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; +   Int32 nGroups, nBytes; + +   /*-- +   UChar  len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +   is a global since the decoder also needs it. + +   Int32  code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +   Int32  rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; +   are also globals only used in this proc. +   Made global to keep stack frame size small. +   --*/ + + +   UInt16 cost[BZ_N_GROUPS]; +   Int32  fave[BZ_N_GROUPS]; + +   UInt16* mtfv = s->mtfv; + +   if (s->verbosity >= 3) +      VPrintf3( "      %d in block, %d after MTF & 1-2 coding, " +                "%d+2 syms in use\n",  +                s->nblock, s->nMTF, s->nInUse ); + +   alphaSize = s->nInUse+2; +   for (t = 0; t < BZ_N_GROUPS; t++) +      for (v = 0; v < alphaSize; v++) +         s->len[t][v] = BZ_GREATER_ICOST; + +   /*--- Decide how many coding tables to use ---*/ +   AssertH ( s->nMTF > 0, 3001 ); +   if (s->nMTF < 200)  nGroups = 2; else +   if (s->nMTF < 600)  nGroups = 3; else +   if (s->nMTF < 1200) nGroups = 4; else +   if (s->nMTF < 2400) nGroups = 5; else +                       nGroups = 6; + +   /*--- Generate an initial set of coding tables ---*/ +   {  +      Int32 nPart, remF, tFreq, aFreq; + +      nPart = nGroups; +      remF  = s->nMTF; +      gs = 0; +      while (nPart > 0) { +         tFreq = remF / nPart; +         ge = gs-1; +         aFreq = 0; +         while (aFreq < tFreq && ge < alphaSize-1) { +            ge++; +            aFreq += s->mtfFreq[ge]; +         } + +         if (ge > gs  +             && nPart != nGroups && nPart != 1  +             && ((nGroups-nPart) % 2 == 1)) { +            aFreq -= s->mtfFreq[ge]; +            ge--; +         } + +         if (s->verbosity >= 3) +            VPrintf5( "      initial group %d, [%d .. %d], " +                      "has %d syms (%4.1f%%)\n", +                      nPart, gs, ge, aFreq,  +                      (100.0 * (float)aFreq) / (float)(s->nMTF) ); +  +         for (v = 0; v < alphaSize; v++) +            if (v >= gs && v <= ge)  +               s->len[nPart-1][v] = BZ_LESSER_ICOST; else +               s->len[nPart-1][v] = BZ_GREATER_ICOST; +  +         nPart--; +         gs = ge+1; +         remF -= aFreq; +      } +   } + +   /*---  +      Iterate up to BZ_N_ITERS times to improve the tables. +   ---*/ +   for (iter = 0; iter < BZ_N_ITERS; iter++) { + +      for (t = 0; t < nGroups; t++) fave[t] = 0; + +      for (t = 0; t < nGroups; t++) +         for (v = 0; v < alphaSize; v++) +            s->rfreq[t][v] = 0; + +      /*--- +        Set up an auxiliary length table which is used to fast-track +	the common case (nGroups == 6).  +      ---*/ +      if (nGroups == 6) { +         for (v = 0; v < alphaSize; v++) { +            s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; +            s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; +            s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; +	 } +      } + +      nSelectors = 0; +      totc = 0; +      gs = 0; +      while (True) { + +         /*--- Set group start & end marks. --*/ +         if (gs >= s->nMTF) break; +         ge = gs + BZ_G_SIZE - 1;  +         if (ge >= s->nMTF) ge = s->nMTF-1; + +         /*--  +            Calculate the cost of this group as coded +            by each of the coding tables. +         --*/ +         for (t = 0; t < nGroups; t++) cost[t] = 0; + +         if (nGroups == 6 && 50 == ge-gs+1) { +            /*--- fast track the common case ---*/ +            register UInt32 cost01, cost23, cost45; +            register UInt16 icv; +            cost01 = cost23 = cost45 = 0; + +#           define BZ_ITER(nn)                \ +               icv = mtfv[gs+(nn)];           \ +               cost01 += s->len_pack[icv][0]; \ +               cost23 += s->len_pack[icv][1]; \ +               cost45 += s->len_pack[icv][2]; \ + +            BZ_ITER(0);  BZ_ITER(1);  BZ_ITER(2);  BZ_ITER(3);  BZ_ITER(4); +            BZ_ITER(5);  BZ_ITER(6);  BZ_ITER(7);  BZ_ITER(8);  BZ_ITER(9); +            BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); +            BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); +            BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); +            BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); +            BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); +            BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); +            BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); +            BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +#           undef BZ_ITER + +            cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; +            cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; +            cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + +         } else { +	    /*--- slow version which correctly handles all situations ---*/ +            for (i = gs; i <= ge; i++) {  +               UInt16 icv = mtfv[i]; +               for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; +            } +         } +  +         /*--  +            Find the coding table which is best for this group, +            and record its identity in the selector table. +         --*/ +         bc = 999999999; bt = -1; +         for (t = 0; t < nGroups; t++) +            if (cost[t] < bc) { bc = cost[t]; bt = t; }; +         totc += bc; +         fave[bt]++; +         s->selector[nSelectors] = bt; +         nSelectors++; + +         /*--  +            Increment the symbol frequencies for the selected table. +          --*/ +         if (nGroups == 6 && 50 == ge-gs+1) { +            /*--- fast track the common case ---*/ + +#           define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + +            BZ_ITUR(0);  BZ_ITUR(1);  BZ_ITUR(2);  BZ_ITUR(3);  BZ_ITUR(4); +            BZ_ITUR(5);  BZ_ITUR(6);  BZ_ITUR(7);  BZ_ITUR(8);  BZ_ITUR(9); +            BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); +            BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); +            BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); +            BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); +            BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); +            BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); +            BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); +            BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +#           undef BZ_ITUR + +         } else { +	    /*--- slow version which correctly handles all situations ---*/ +            for (i = gs; i <= ge; i++) +               s->rfreq[bt][ mtfv[i] ]++; +         } + +         gs = ge+1; +      } +      if (s->verbosity >= 3) { +         VPrintf2 ( "      pass %d: size is %d, grp uses are ",  +                   iter+1, totc/8 ); +         for (t = 0; t < nGroups; t++) +            VPrintf1 ( "%d ", fave[t] ); +         VPrintf0 ( "\n" ); +      } + +      /*-- +        Recompute the tables based on the accumulated frequencies. +      --*/ +      /* maxLen was changed from 20 to 17 in bzip2-1.0.3.  See  +         comment in huffman.c for details. */ +      for (t = 0; t < nGroups; t++) +         BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]),  +                                 alphaSize, 17 /*20*/ ); +   } + + +   AssertH( nGroups < 8, 3002 ); +   AssertH( nSelectors < 32768 && +            nSelectors <= (2 + (900000 / BZ_G_SIZE)), +            3003 ); + + +   /*--- Compute MTF values for the selectors. ---*/ +   { +      UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; +      for (i = 0; i < nGroups; i++) pos[i] = i; +      for (i = 0; i < nSelectors; i++) { +         ll_i = s->selector[i]; +         j = 0; +         tmp = pos[j]; +         while ( ll_i != tmp ) { +            j++; +            tmp2 = tmp; +            tmp = pos[j]; +            pos[j] = tmp2; +         }; +         pos[0] = tmp; +         s->selectorMtf[i] = j; +      } +   }; + +   /*--- Assign actual codes for the tables. --*/ +   for (t = 0; t < nGroups; t++) { +      minLen = 32; +      maxLen = 0; +      for (i = 0; i < alphaSize; i++) { +         if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; +         if (s->len[t][i] < minLen) minLen = s->len[t][i]; +      } +      AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); +      AssertH ( !(minLen < 1),  3005 ); +      BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]),  +                          minLen, maxLen, alphaSize ); +   } + +   /*--- Transmit the mapping table. ---*/ +   {  +      Bool inUse16[16]; +      for (i = 0; i < 16; i++) { +          inUse16[i] = False; +          for (j = 0; j < 16; j++) +             if (s->inUse[i * 16 + j]) inUse16[i] = True; +      } +      +      nBytes = s->numZ; +      for (i = 0; i < 16; i++) +         if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + +      for (i = 0; i < 16; i++) +         if (inUse16[i]) +            for (j = 0; j < 16; j++) { +               if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); +            } + +      if (s->verbosity >= 3)  +         VPrintf1( "      bytes: mapping %d, ", s->numZ-nBytes ); +   } + +   /*--- Now the selectors. ---*/ +   nBytes = s->numZ; +   bsW ( s, 3, nGroups ); +   bsW ( s, 15, nSelectors ); +   for (i = 0; i < nSelectors; i++) {  +      for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); +      bsW(s,1,0); +   } +   if (s->verbosity >= 3) +      VPrintf1( "selectors %d, ", s->numZ-nBytes ); + +   /*--- Now the coding tables. ---*/ +   nBytes = s->numZ; + +   for (t = 0; t < nGroups; t++) { +      Int32 curr = s->len[t][0]; +      bsW ( s, 5, curr ); +      for (i = 0; i < alphaSize; i++) { +         while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; +         while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; +         bsW ( s, 1, 0 ); +      } +   } + +   if (s->verbosity >= 3) +      VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + +   /*--- And finally, the block data proper ---*/ +   nBytes = s->numZ; +   selCtr = 0; +   gs = 0; +   while (True) { +      if (gs >= s->nMTF) break; +      ge = gs + BZ_G_SIZE - 1;  +      if (ge >= s->nMTF) ge = s->nMTF-1; +      AssertH ( s->selector[selCtr] < nGroups, 3006 ); + +      if (nGroups == 6 && 50 == ge-gs+1) { +            /*--- fast track the common case ---*/ +            UInt16 mtfv_i; +            UChar* s_len_sel_selCtr  +               = &(s->len[s->selector[selCtr]][0]); +            Int32* s_code_sel_selCtr +               = &(s->code[s->selector[selCtr]][0]); + +#           define BZ_ITAH(nn)                      \ +               mtfv_i = mtfv[gs+(nn)];              \ +               bsW ( s,                             \ +                     s_len_sel_selCtr[mtfv_i],      \ +                     s_code_sel_selCtr[mtfv_i] ) + +            BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4); +            BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9); +            BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); +            BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); +            BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); +            BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); +            BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); +            BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); +            BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); +            BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +#           undef BZ_ITAH + +      } else { +	 /*--- slow version which correctly handles all situations ---*/ +         for (i = gs; i <= ge; i++) { +            bsW ( s,  +                  s->len  [s->selector[selCtr]] [mtfv[i]], +                  s->code [s->selector[selCtr]] [mtfv[i]] ); +         } +      } + + +      gs = ge+1; +      selCtr++; +   } +   AssertH( selCtr == nSelectors, 3007 ); + +   if (s->verbosity >= 3) +      VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ +   if (s->nblock > 0) { + +      BZ_FINALISE_CRC ( s->blockCRC ); +      s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); +      s->combinedCRC ^= s->blockCRC; +      if (s->blockNo > 1) s->numZ = 0; + +      if (s->verbosity >= 2) +         VPrintf4( "    block %d: crc = 0x%08x, " +                   "combined CRC = 0x%08x, size = %d\n", +                   s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + +      BZ2_blockSort ( s ); +   } + +   s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + +   /*-- If this is the first block, create the stream header. --*/ +   if (s->blockNo == 1) { +      BZ2_bsInitWrite ( s ); +      bsPutUChar ( s, BZ_HDR_B ); +      bsPutUChar ( s, BZ_HDR_Z ); +      bsPutUChar ( s, BZ_HDR_h ); +      bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); +   } + +   if (s->nblock > 0) { + +      bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); +      bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); +      bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + +      /*-- Now the block's CRC, so it is in a known place. --*/ +      bsPutUInt32 ( s, s->blockCRC ); + +      /*--  +         Now a single bit indicating (non-)randomisation.  +         As of version 0.9.5, we use a better sorting algorithm +         which makes randomisation unnecessary.  So always set +         the randomised bit to 'no'.  Of course, the decoder +         still needs to be able to handle randomised blocks +         so as to maintain backwards compatibility with +         older versions of bzip2. +      --*/ +      bsW(s,1,0); + +      bsW ( s, 24, s->origPtr ); +      generateMTFValues ( s ); +      sendMTFValues ( s ); +   } + + +   /*-- If this is the last block, add the stream trailer. --*/ +   if (is_last_block) { + +      bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); +      bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); +      bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); +      bsPutUInt32 ( s, s->combinedCRC ); +      if (s->verbosity >= 2) +         VPrintf1( "    final combined CRC = 0x%08x\n   ", s->combinedCRC ); +      bsFinishWrite ( s ); +   } +} + + +/*-------------------------------------------------------------*/ +/*--- end                                        compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/crctable.c b/src/bzip2/crctable.c new file mode 100644 index 0000000..215687b --- /dev/null +++ b/src/bzip2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs                                  ---*/ +/*---                                            crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- +  I think this is an implementation of the AUTODIN-II, +  Ethernet & FDDI 32-bit CRC standard.  Vaguely derived +  from code by Rob Warnock, in Section 51 of the +  comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + +   /*-- Ugly, innit? --*/ + +   0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, +   0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, +   0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, +   0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, +   0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, +   0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, +   0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, +   0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, +   0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, +   0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, +   0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, +   0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, +   0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, +   0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, +   0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, +   0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, +   0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, +   0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, +   0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, +   0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, +   0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, +   0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, +   0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, +   0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, +   0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, +   0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, +   0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, +   0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, +   0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, +   0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, +   0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, +   0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, +   0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, +   0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, +   0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, +   0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, +   0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, +   0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, +   0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, +   0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, +   0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, +   0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, +   0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, +   0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, +   0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, +   0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, +   0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, +   0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, +   0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, +   0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, +   0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, +   0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, +   0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, +   0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, +   0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, +   0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, +   0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, +   0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, +   0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, +   0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, +   0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, +   0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, +   0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, +   0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end                                        crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/decompress.c b/src/bzip2/decompress.c new file mode 100644 index 0000000..bba5e0f --- /dev/null +++ b/src/bzip2/decompress.c @@ -0,0 +1,626 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery                               ---*/ +/*---                                          decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ +   Int32 i; +   s->nInUse = 0; +   for (i = 0; i < 256; i++) +      if (s->inUse[i]) { +         s->seqToUnseq[s->nInUse] = i; +         s->nInUse++; +      } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr)                               \ +   { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn)                     \ +   case lll: s->state = lll;                      \ +   while (True) {                                 \ +      if (s->bsLive >= nnn) {                     \ +         UInt32 v;                                \ +         v = (s->bsBuff >>                        \ +             (s->bsLive-nnn)) & ((1 << nnn)-1);   \ +         s->bsLive -= nnn;                        \ +         vvv = v;                                 \ +         break;                                   \ +      }                                           \ +      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \ +      s->bsBuff                                   \ +         = (s->bsBuff << 8) |                     \ +           ((UInt32)                              \ +              (*((UChar*)(s->strm->next_in))));   \ +      s->bsLive += 8;                             \ +      s->strm->next_in++;                         \ +      s->strm->avail_in--;                        \ +      s->strm->total_in_lo32++;                   \ +      if (s->strm->total_in_lo32 == 0)            \ +         s->strm->total_in_hi32++;                \ +   } + +#define GET_UCHAR(lll,uuu)                        \ +   GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu)                          \ +   GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval)           \ +{                                                 \ +   if (groupPos == 0) {                           \ +      groupNo++;                                  \ +      if (groupNo >= nSelectors)                  \ +         RETURN(BZ_DATA_ERROR);                   \ +      groupPos = BZ_G_SIZE;                       \ +      gSel = s->selector[groupNo];                \ +      gMinlen = s->minLens[gSel];                 \ +      gLimit = &(s->limit[gSel][0]);              \ +      gPerm = &(s->perm[gSel][0]);                \ +      gBase = &(s->base[gSel][0]);                \ +   }                                              \ +   groupPos--;                                    \ +   zn = gMinlen;                                  \ +   GET_BITS(label1, zvec, zn);                    \ +   while (1) {                                    \ +      if (zn > 20 /* the longest code */)         \ +         RETURN(BZ_DATA_ERROR);                   \ +      if (zvec <= gLimit[zn]) break;              \ +      zn++;                                       \ +      GET_BIT(label2, zj);                        \ +      zvec = (zvec << 1) | zj;                    \ +   };                                             \ +   if (zvec - gBase[zn] < 0                       \ +       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \ +      RETURN(BZ_DATA_ERROR);                      \ +   lval = gPerm[zvec - gBase[zn]];                \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ +   UChar      uc; +   Int32      retVal; +   Int32      minLen, maxLen; +   bz_stream* strm = s->strm; + +   /* stuff that needs to be saved/restored */ +   Int32  i; +   Int32  j; +   Int32  t; +   Int32  alphaSize; +   Int32  nGroups; +   Int32  nSelectors; +   Int32  EOB; +   Int32  groupNo; +   Int32  groupPos; +   Int32  nextSym; +   Int32  nblockMAX; +   Int32  nblock; +   Int32  es; +   Int32  N; +   Int32  curr; +   Int32  zt; +   Int32  zn;  +   Int32  zvec; +   Int32  zj; +   Int32  gSel; +   Int32  gMinlen; +   Int32* gLimit; +   Int32* gBase; +   Int32* gPerm; + +   if (s->state == BZ_X_MAGIC_1) { +      /*initialise the save area*/ +      s->save_i           = 0; +      s->save_j           = 0; +      s->save_t           = 0; +      s->save_alphaSize   = 0; +      s->save_nGroups     = 0; +      s->save_nSelectors  = 0; +      s->save_EOB         = 0; +      s->save_groupNo     = 0; +      s->save_groupPos    = 0; +      s->save_nextSym     = 0; +      s->save_nblockMAX   = 0; +      s->save_nblock      = 0; +      s->save_es          = 0; +      s->save_N           = 0; +      s->save_curr        = 0; +      s->save_zt          = 0; +      s->save_zn          = 0; +      s->save_zvec        = 0; +      s->save_zj          = 0; +      s->save_gSel        = 0; +      s->save_gMinlen     = 0; +      s->save_gLimit      = NULL; +      s->save_gBase       = NULL; +      s->save_gPerm       = NULL; +   } + +   /*restore from the save area*/ +   i           = s->save_i; +   j           = s->save_j; +   t           = s->save_t; +   alphaSize   = s->save_alphaSize; +   nGroups     = s->save_nGroups; +   nSelectors  = s->save_nSelectors; +   EOB         = s->save_EOB; +   groupNo     = s->save_groupNo; +   groupPos    = s->save_groupPos; +   nextSym     = s->save_nextSym; +   nblockMAX   = s->save_nblockMAX; +   nblock      = s->save_nblock; +   es          = s->save_es; +   N           = s->save_N; +   curr        = s->save_curr; +   zt          = s->save_zt; +   zn          = s->save_zn;  +   zvec        = s->save_zvec; +   zj          = s->save_zj; +   gSel        = s->save_gSel; +   gMinlen     = s->save_gMinlen; +   gLimit      = s->save_gLimit; +   gBase       = s->save_gBase; +   gPerm       = s->save_gPerm; + +   retVal = BZ_OK; + +   switch (s->state) { + +      GET_UCHAR(BZ_X_MAGIC_1, uc); +      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + +      GET_UCHAR(BZ_X_MAGIC_2, uc); +      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + +      GET_UCHAR(BZ_X_MAGIC_3, uc) +      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + +      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) +      if (s->blockSize100k < (BZ_HDR_0 + 1) ||  +          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); +      s->blockSize100k -= BZ_HDR_0; + +      if (s->smallDecompress) { +         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); +         s->ll4  = BZALLOC(  +                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)  +                   ); +         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); +      } else { +         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); +         if (s->tt == NULL) RETURN(BZ_MEM_ERROR); +      } + +      GET_UCHAR(BZ_X_BLKHDR_1, uc); + +      if (uc == 0x17) goto endhdr_2; +      if (uc != 0x31) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_BLKHDR_2, uc); +      if (uc != 0x41) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_BLKHDR_3, uc); +      if (uc != 0x59) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_BLKHDR_4, uc); +      if (uc != 0x26) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_BLKHDR_5, uc); +      if (uc != 0x53) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_BLKHDR_6, uc); +      if (uc != 0x59) RETURN(BZ_DATA_ERROR); + +      s->currBlockNo++; +      if (s->verbosity >= 2) +         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo ); +  +      s->storedBlockCRC = 0; +      GET_UCHAR(BZ_X_BCRC_1, uc); +      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_BCRC_2, uc); +      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_BCRC_3, uc); +      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_BCRC_4, uc); +      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + +      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + +      s->origPtr = 0; +      GET_UCHAR(BZ_X_ORIGPTR_1, uc); +      s->origPtr = (s->origPtr << 8) | ((Int32)uc); +      GET_UCHAR(BZ_X_ORIGPTR_2, uc); +      s->origPtr = (s->origPtr << 8) | ((Int32)uc); +      GET_UCHAR(BZ_X_ORIGPTR_3, uc); +      s->origPtr = (s->origPtr << 8) | ((Int32)uc); + +      if (s->origPtr < 0) +         RETURN(BZ_DATA_ERROR); +      if (s->origPtr > 10 + 100000*s->blockSize100k)  +         RETURN(BZ_DATA_ERROR); + +      /*--- Receive the mapping table ---*/ +      for (i = 0; i < 16; i++) { +         GET_BIT(BZ_X_MAPPING_1, uc); +         if (uc == 1)  +            s->inUse16[i] = True; else  +            s->inUse16[i] = False; +      } + +      for (i = 0; i < 256; i++) s->inUse[i] = False; + +      for (i = 0; i < 16; i++) +         if (s->inUse16[i]) +            for (j = 0; j < 16; j++) { +               GET_BIT(BZ_X_MAPPING_2, uc); +               if (uc == 1) s->inUse[i * 16 + j] = True; +            } +      makeMaps_d ( s ); +      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); +      alphaSize = s->nInUse+2; + +      /*--- Now the selectors ---*/ +      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); +      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); +      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); +      if (nSelectors < 1) RETURN(BZ_DATA_ERROR); +      for (i = 0; i < nSelectors; i++) { +         j = 0; +         while (True) { +            GET_BIT(BZ_X_SELECTOR_3, uc); +            if (uc == 0) break; +            j++; +            if (j >= nGroups) RETURN(BZ_DATA_ERROR); +         } +         s->selectorMtf[i] = j; +      } + +      /*--- Undo the MTF values for the selectors. ---*/ +      { +         UChar pos[BZ_N_GROUPS], tmp, v; +         for (v = 0; v < nGroups; v++) pos[v] = v; +    +         for (i = 0; i < nSelectors; i++) { +            v = s->selectorMtf[i]; +            tmp = pos[v]; +            while (v > 0) { pos[v] = pos[v-1]; v--; } +            pos[0] = tmp; +            s->selector[i] = tmp; +         } +      } + +      /*--- Now the coding tables ---*/ +      for (t = 0; t < nGroups; t++) { +         GET_BITS(BZ_X_CODING_1, curr, 5); +         for (i = 0; i < alphaSize; i++) { +            while (True) { +               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); +               GET_BIT(BZ_X_CODING_2, uc); +               if (uc == 0) break; +               GET_BIT(BZ_X_CODING_3, uc); +               if (uc == 0) curr++; else curr--; +            } +            s->len[t][i] = curr; +         } +      } + +      /*--- Create the Huffman decoding tables ---*/ +      for (t = 0; t < nGroups; t++) { +         minLen = 32; +         maxLen = 0; +         for (i = 0; i < alphaSize; i++) { +            if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; +            if (s->len[t][i] < minLen) minLen = s->len[t][i]; +         } +         BZ2_hbCreateDecodeTables (  +            &(s->limit[t][0]),  +            &(s->base[t][0]),  +            &(s->perm[t][0]),  +            &(s->len[t][0]), +            minLen, maxLen, alphaSize +         ); +         s->minLens[t] = minLen; +      } + +      /*--- Now the MTF values ---*/ + +      EOB      = s->nInUse+1; +      nblockMAX = 100000 * s->blockSize100k; +      groupNo  = -1; +      groupPos = 0; + +      for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + +      /*-- MTF init --*/ +      { +         Int32 ii, jj, kk; +         kk = MTFA_SIZE-1; +         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { +            for (jj = MTFL_SIZE-1; jj >= 0; jj--) { +               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); +               kk--; +            } +            s->mtfbase[ii] = kk + 1; +         } +      } +      /*-- end MTF init --*/ + +      nblock = 0; +      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + +      while (True) { + +         if (nextSym == EOB) break; + +         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + +            es = -1; +            N = 1; +            do { +               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else +               if (nextSym == BZ_RUNB) es = es + (1+1) * N; +               N = N * 2; +               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); +            } +               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + +            es++; +            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; +            s->unzftab[uc] += es; + +            if (s->smallDecompress) +               while (es > 0) { +                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); +                  s->ll16[nblock] = (UInt16)uc; +                  nblock++; +                  es--; +               } +            else +               while (es > 0) { +                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); +                  s->tt[nblock] = (UInt32)uc; +                  nblock++; +                  es--; +               }; + +            continue; + +         } else { + +            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + +            /*-- uc = MTF ( nextSym-1 ) --*/ +            { +               Int32 ii, jj, kk, pp, lno, off; +               UInt32 nn; +               nn = (UInt32)(nextSym - 1); + +               if (nn < MTFL_SIZE) { +                  /* avoid general-case expense */ +                  pp = s->mtfbase[0]; +                  uc = s->mtfa[pp+nn]; +                  while (nn > 3) { +                     Int32 z = pp+nn; +                     s->mtfa[(z)  ] = s->mtfa[(z)-1]; +                     s->mtfa[(z)-1] = s->mtfa[(z)-2]; +                     s->mtfa[(z)-2] = s->mtfa[(z)-3]; +                     s->mtfa[(z)-3] = s->mtfa[(z)-4]; +                     nn -= 4; +                  } +                  while (nn > 0) {  +                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;  +                  }; +                  s->mtfa[pp] = uc; +               } else {  +                  /* general case */ +                  lno = nn / MTFL_SIZE; +                  off = nn % MTFL_SIZE; +                  pp = s->mtfbase[lno] + off; +                  uc = s->mtfa[pp]; +                  while (pp > s->mtfbase[lno]) {  +                     s->mtfa[pp] = s->mtfa[pp-1]; pp--;  +                  }; +                  s->mtfbase[lno]++; +                  while (lno > 0) { +                     s->mtfbase[lno]--; +                     s->mtfa[s->mtfbase[lno]]  +                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; +                     lno--; +                  } +                  s->mtfbase[0]--; +                  s->mtfa[s->mtfbase[0]] = uc; +                  if (s->mtfbase[0] == 0) { +                     kk = MTFA_SIZE-1; +                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { +                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) { +                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; +                           kk--; +                        } +                        s->mtfbase[ii] = kk + 1; +                     } +                  } +               } +            } +            /*-- end uc = MTF ( nextSym-1 ) --*/ + +            s->unzftab[s->seqToUnseq[uc]]++; +            if (s->smallDecompress) +               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else +               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]); +            nblock++; + +            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); +            continue; +         } +      } + +      /* Now we know what nblock is, we can do a better sanity +         check on s->origPtr. +      */ +      if (s->origPtr < 0 || s->origPtr >= nblock) +         RETURN(BZ_DATA_ERROR); + +      /*-- Set up cftab to facilitate generation of T^(-1) --*/ +      s->cftab[0] = 0; +      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; +      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; +      for (i = 0; i <= 256; i++) { +         if (s->cftab[i] < 0 || s->cftab[i] > nblock) { +            /* s->cftab[i] can legitimately be == nblock */ +            RETURN(BZ_DATA_ERROR); +         } +      } + +      s->state_out_len = 0; +      s->state_out_ch  = 0; +      BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); +      s->state = BZ_X_OUTPUT; +      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + +      if (s->smallDecompress) { + +         /*-- Make a copy of cftab, used in generation of T --*/ +         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + +         /*-- compute the T vector --*/ +         for (i = 0; i < nblock; i++) { +            uc = (UChar)(s->ll16[i]); +            SET_LL(i, s->cftabCopy[uc]); +            s->cftabCopy[uc]++; +         } + +         /*-- Compute T^(-1) by pointer reversal on T --*/ +         i = s->origPtr; +         j = GET_LL(i); +         do { +            Int32 tmp = GET_LL(j); +            SET_LL(j, i); +            i = j; +            j = tmp; +         } +            while (i != s->origPtr); + +         s->tPos = s->origPtr; +         s->nblock_used = 0; +         if (s->blockRandomised) { +            BZ_RAND_INIT_MASK; +            BZ_GET_SMALL(s->k0); s->nblock_used++; +            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;  +         } else { +            BZ_GET_SMALL(s->k0); s->nblock_used++; +         } + +      } else { + +         /*-- compute the T^(-1) vector --*/ +         for (i = 0; i < nblock; i++) { +            uc = (UChar)(s->tt[i] & 0xff); +            s->tt[s->cftab[uc]] |= (i << 8); +            s->cftab[uc]++; +         } + +         s->tPos = s->tt[s->origPtr] >> 8; +         s->nblock_used = 0; +         if (s->blockRandomised) { +            BZ_RAND_INIT_MASK; +            BZ_GET_FAST(s->k0); s->nblock_used++; +            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;  +         } else { +            BZ_GET_FAST(s->k0); s->nblock_used++; +         } + +      } + +      RETURN(BZ_OK); + + + +    endhdr_2: + +      GET_UCHAR(BZ_X_ENDHDR_2, uc); +      if (uc != 0x72) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_ENDHDR_3, uc); +      if (uc != 0x45) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_ENDHDR_4, uc); +      if (uc != 0x38) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_ENDHDR_5, uc); +      if (uc != 0x50) RETURN(BZ_DATA_ERROR); +      GET_UCHAR(BZ_X_ENDHDR_6, uc); +      if (uc != 0x90) RETURN(BZ_DATA_ERROR); + +      s->storedCombinedCRC = 0; +      GET_UCHAR(BZ_X_CCRC_1, uc); +      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_CCRC_2, uc); +      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_CCRC_3, uc); +      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); +      GET_UCHAR(BZ_X_CCRC_4, uc); +      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + +      s->state = BZ_X_IDLE; +      RETURN(BZ_STREAM_END); + +      default: AssertH ( False, 4001 ); +   } + +   AssertH ( False, 4002 ); + +   save_state_and_return: + +   s->save_i           = i; +   s->save_j           = j; +   s->save_t           = t; +   s->save_alphaSize   = alphaSize; +   s->save_nGroups     = nGroups; +   s->save_nSelectors  = nSelectors; +   s->save_EOB         = EOB; +   s->save_groupNo     = groupNo; +   s->save_groupPos    = groupPos; +   s->save_nextSym     = nextSym; +   s->save_nblockMAX   = nblockMAX; +   s->save_nblock      = nblock; +   s->save_es          = es; +   s->save_N           = N; +   s->save_curr        = curr; +   s->save_zt          = zt; +   s->save_zn          = zn; +   s->save_zvec        = zvec; +   s->save_zj          = zj; +   s->save_gSel        = gSel; +   s->save_gMinlen     = gMinlen; +   s->save_gLimit      = gLimit; +   s->save_gBase       = gBase; +   s->save_gPerm       = gPerm; + +   return retVal;    +} + + +/*-------------------------------------------------------------*/ +/*--- end                                      decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/huffman.c b/src/bzip2/huffman.c new file mode 100644 index 0000000..87e79e3 --- /dev/null +++ b/src/bzip2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff                        ---*/ +/*---                                             huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00) +#define DEPTHOF(zz1)   ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2)                           \ +   (WEIGHTOF(zw1)+WEIGHTOF(zw2)) |                    \ +   (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z)                                     \ +{                                                     \ +   Int32 zz, tmp;                                     \ +   zz = z; tmp = heap[zz];                            \ +   while (weight[tmp] < weight[heap[zz >> 1]]) {      \ +      heap[zz] = heap[zz >> 1];                       \ +      zz >>= 1;                                       \ +   }                                                  \ +   heap[zz] = tmp;                                    \ +} + +#define DOWNHEAP(z)                                   \ +{                                                     \ +   Int32 zz, yy, tmp;                                 \ +   zz = z; tmp = heap[zz];                            \ +   while (True) {                                     \ +      yy = zz << 1;                                   \ +      if (yy > nHeap) break;                          \ +      if (yy < nHeap &&                               \ +          weight[heap[yy+1]] < weight[heap[yy]])      \ +         yy++;                                        \ +      if (weight[tmp] < weight[heap[yy]]) break;      \ +      heap[zz] = heap[yy];                            \ +      zz = yy;                                        \ +   }                                                  \ +   heap[zz] = tmp;                                    \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len,  +                             Int32 *freq, +                             Int32 alphaSize, +                             Int32 maxLen ) +{ +   /*-- +      Nodes and heap entries run from 1.  Entry 0 +      for both the heap and nodes is a sentinel. +   --*/ +   Int32 nNodes, nHeap, n1, n2, i, j, k; +   Bool  tooLong; + +   Int32 heap   [ BZ_MAX_ALPHA_SIZE + 2 ]; +   Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; +   Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ];  + +   for (i = 0; i < alphaSize; i++) +      weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + +   while (True) { + +      nNodes = alphaSize; +      nHeap = 0; + +      heap[0] = 0; +      weight[0] = 0; +      parent[0] = -2; + +      for (i = 1; i <= alphaSize; i++) { +         parent[i] = -1; +         nHeap++; +         heap[nHeap] = i; +         UPHEAP(nHeap); +      } + +      AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); +    +      while (nHeap > 1) { +         n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); +         n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); +         nNodes++; +         parent[n1] = parent[n2] = nNodes; +         weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); +         parent[nNodes] = -1; +         nHeap++; +         heap[nHeap] = nNodes; +         UPHEAP(nHeap); +      } + +      AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + +      tooLong = False; +      for (i = 1; i <= alphaSize; i++) { +         j = 0; +         k = i; +         while (parent[k] >= 0) { k = parent[k]; j++; } +         len[i-1] = j; +         if (j > maxLen) tooLong = True; +      } +       +      if (! tooLong) break; + +      /* 17 Oct 04: keep-going condition for the following loop used +         to be 'i < alphaSize', which missed the last element, +         theoretically leading to the possibility of the compressor +         looping.  However, this count-scaling step is only needed if +         one of the generated Huffman code words is longer than +         maxLen, which up to and including version 1.0.2 was 20 bits, +         which is extremely unlikely.  In version 1.0.3 maxLen was +         changed to 17 bits, which has minimal effect on compression +         ratio, but does mean this scaling step is used from time to +         time, enough to verify that it works. + +         This means that bzip2-1.0.3 and later will only produce +         Huffman codes with a maximum length of 17 bits.  However, in +         order to preserve backwards compatibility with bitstreams +         produced by versions pre-1.0.3, the decompressor must still +         handle lengths of up to 20. */ + +      for (i = 1; i <= alphaSize; i++) { +         j = weight[i] >> 8; +         j = 1 + (j / 2); +         weight[i] = j << 8; +      } +   } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, +                         UChar *length, +                         Int32 minLen, +                         Int32 maxLen, +                         Int32 alphaSize ) +{ +   Int32 n, vec, i; + +   vec = 0; +   for (n = minLen; n <= maxLen; n++) { +      for (i = 0; i < alphaSize; i++) +         if (length[i] == n) { code[i] = vec; vec++; }; +      vec <<= 1; +   } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, +                                Int32 *base, +                                Int32 *perm, +                                UChar *length, +                                Int32 minLen, +                                Int32 maxLen, +                                Int32 alphaSize ) +{ +   Int32 pp, i, j, vec; + +   pp = 0; +   for (i = minLen; i <= maxLen; i++) +      for (j = 0; j < alphaSize; j++) +         if (length[j] == i) { perm[pp] = j; pp++; }; + +   for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; +   for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + +   for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + +   for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; +   vec = 0; + +   for (i = minLen; i <= maxLen; i++) { +      vec += (base[i+1] - base[i]); +      limit[i] = vec-1; +      vec <<= 1; +   } +   for (i = minLen + 1; i <= maxLen; i++) +      base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end                                         huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/bzip2/randtable.c b/src/bzip2/randtable.c new file mode 100644 index 0000000..068b763 --- /dev/null +++ b/src/bzip2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks               ---*/ +/*---                                           randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ +   This file is part of bzip2/libbzip2, a program and library for +   lossless, block-sorting data compression. + +   bzip2/libbzip2 version 1.0.5 of 10 December 2007 +   Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + +   Please read the WARNING, DISCLAIMER and PATENTS sections in the  +   README file. + +   This program is released under the terms of the license contained +   in the file LICENSE. +   ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = {  +   619, 720, 127, 481, 931, 816, 813, 233, 566, 247,  +   985, 724, 205, 454, 863, 491, 741, 242, 949, 214,  +   733, 859, 335, 708, 621, 574, 73, 654, 730, 472,  +   419, 436, 278, 496, 867, 210, 399, 680, 480, 51,  +   878, 465, 811, 169, 869, 675, 611, 697, 867, 561,  +   862, 687, 507, 283, 482, 129, 807, 591, 733, 623,  +   150, 238, 59, 379, 684, 877, 625, 169, 643, 105,  +   170, 607, 520, 932, 727, 476, 693, 425, 174, 647,  +   73, 122, 335, 530, 442, 853, 695, 249, 445, 515,  +   909, 545, 703, 919, 874, 474, 882, 500, 594, 612,  +   641, 801, 220, 162, 819, 984, 589, 513, 495, 799,  +   161, 604, 958, 533, 221, 400, 386, 867, 600, 782,  +   382, 596, 414, 171, 516, 375, 682, 485, 911, 276,  +   98, 553, 163, 354, 666, 933, 424, 341, 533, 870,  +   227, 730, 475, 186, 263, 647, 537, 686, 600, 224,  +   469, 68, 770, 919, 190, 373, 294, 822, 808, 206,  +   184, 943, 795, 384, 383, 461, 404, 758, 839, 887,  +   715, 67, 618, 276, 204, 918, 873, 777, 604, 560,  +   951, 160, 578, 722, 79, 804, 96, 409, 713, 940,  +   652, 934, 970, 447, 318, 353, 859, 672, 112, 785,  +   645, 863, 803, 350, 139, 93, 354, 99, 820, 908,  +   609, 772, 154, 274, 580, 184, 79, 626, 630, 742,  +   653, 282, 762, 623, 680, 81, 927, 626, 789, 125,  +   411, 521, 938, 300, 821, 78, 343, 175, 128, 250,  +   170, 774, 972, 275, 999, 639, 495, 78, 352, 126,  +   857, 956, 358, 619, 580, 124, 737, 594, 701, 612,  +   669, 112, 134, 694, 363, 992, 809, 743, 168, 974,  +   944, 375, 748, 52, 600, 747, 642, 182, 862, 81,  +   344, 805, 988, 739, 511, 655, 814, 334, 249, 515,  +   897, 955, 664, 981, 649, 113, 974, 459, 893, 228,  +   433, 837, 553, 268, 926, 240, 102, 654, 459, 51,  +   686, 754, 806, 760, 493, 403, 415, 394, 687, 700,  +   946, 670, 656, 610, 738, 392, 760, 799, 887, 653,  +   978, 321, 576, 617, 626, 502, 894, 679, 243, 440,  +   680, 879, 194, 572, 640, 724, 926, 56, 204, 700,  +   707, 151, 457, 449, 797, 195, 791, 558, 945, 679,  +   297, 59, 87, 824, 713, 663, 412, 693, 342, 606,  +   134, 108, 571, 364, 631, 212, 174, 643, 304, 329,  +   343, 97, 430, 751, 497, 314, 983, 374, 822, 928,  +   140, 206, 73, 263, 980, 736, 876, 478, 430, 305,  +   170, 514, 364, 692, 829, 82, 855, 953, 676, 246,  +   369, 970, 294, 750, 807, 827, 150, 790, 288, 923,  +   804, 378, 215, 828, 592, 281, 565, 555, 710, 82,  +   896, 831, 547, 261, 524, 462, 293, 465, 502, 56,  +   661, 821, 976, 991, 658, 869, 905, 758, 745, 193,  +   768, 550, 608, 933, 378, 286, 215, 979, 792, 961,  +   61, 688, 793, 644, 986, 403, 106, 366, 905, 644,  +   372, 567, 466, 434, 645, 210, 389, 550, 919, 135,  +   780, 773, 635, 389, 707, 100, 626, 958, 165, 504,  +   920, 176, 193, 713, 857, 265, 203, 50, 668, 108,  +   645, 990, 626, 197, 510, 357, 358, 850, 858, 364,  +   936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end                                       randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/src/huffman/HuffTree.xls b/src/huffman/HuffTree.xls Binary files differnew file mode 100644 index 0000000..68e0664 --- /dev/null +++ b/src/huffman/HuffTree.xls diff --git a/src/huffman/huff.cpp b/src/huffman/huff.cpp new file mode 100644 index 0000000..cf5ae05 --- /dev/null +++ b/src/huffman/huff.cpp @@ -0,0 +1,869 @@ +/*****************************************************************************/ +/* huffman.cpp                       Copyright (c) Ladislav Zezula 1998-2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains Huffmann (de)compression methods                     */ +/*                                                                           */ +/* Authors : Ladislav Zezula (ladik@zezula.net)                              */ +/*           ShadowFlare     (BlakFlare@hotmail.com)                         */ +/*                                                                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of dcmp.cpp                        */ +/* 03.05.03  1.00  Lad  Added compression methods                            */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */ +/* 09.01.13  3.00  Lad  Refactored, beautified, documented :-)               */ +/*****************************************************************************/ +  +#include <assert.h> +#include <string.h> +  +#include "../StormPort.h" +#include "huff.h" + +//----------------------------------------------------------------------------- +// Table of byte-to-weight values + +// Table for (de)compression. Every compression type has 258 entries +static unsigned char ByteToWeight_00[] = +{ +    0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +    0x00, 0x00 +}; + +// Data for compression type 0x01 +static unsigned char ByteToWeight_01[] = +{ +    0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, +    0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, +    0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, +    0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, +    0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, +    0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, +    0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03, +    0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, +    0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, +    0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, +    0x00, 0x00 +}; +    +// Data for compression type 0x02 +static unsigned char ByteToWeight_02[] = +{ +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04, +    0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01, +    0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, +    0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, +    0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A, +    0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; +    +// Data for compression type 0x03 +static unsigned char ByteToWeight_03[] = +{ +    0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, +    0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, +    0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, +    0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, +    0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03, +    0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01, +    0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, +    0x00, 0x00 +}; +    +// Data for compression type 0x04 +static unsigned char ByteToWeight_04[] = +{ +    0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; +    +// Data for compression type 0x05 +static unsigned char ByteToWeight_05[] = +{ +    0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, +    0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, +    0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D, +    0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; +    +    // Data for compression type 0x06 +static unsigned char ByteToWeight_06[] = +{ +    0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; +    +// Data for compression type 0x07 +static unsigned char ByteToWeight_07[] = +{ +    0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; +    +// Data for compression type 0x08 +static unsigned char ByteToWeight_08[] = +{ +    0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, +    0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11, +    0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; + +static unsigned char * WeightTables[0x09] = +{ +    ByteToWeight_00, +    ByteToWeight_01, +    ByteToWeight_02, +    ByteToWeight_03, +    ByteToWeight_04, +    ByteToWeight_05, +    ByteToWeight_06, +    ByteToWeight_07, +    ByteToWeight_08 +}; + +//-----------------------------------------------------------------------------  +// Debug/diagnostics + +#ifdef _DEBUG +void DumpHuffmannTree(THTreeItem * pItem) +{ +    THTreeItem * pChildLo;                          // Item with the lower weight +    THTreeItem * pChildHi;                          // Item with the higher weight + +    // Get the lower-weight branch +    pChildLo = pItem->pChildLo; +    if(pChildLo != NULL) +    { +        // Get the higher-weight branch +        pChildHi = pChildLo->pPrev; + +        // Parse the lower-weight branch +        DumpHuffmannTree(pChildHi); +        DumpHuffmannTree(pChildLo); +    } +} +#endif + +//----------------------------------------------------------------------------- +// TInputStream functions + +TInputStream::TInputStream(void * pvInBuffer, size_t cbInBuffer) +{ +    pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; +    pbInBuffer = (unsigned char *)pvInBuffer; +    BitBuffer = 0; +    BitCount = 0; +} + +// Gets 7 bits from the stream. DOES NOT remove the bits from input stream +unsigned int TInputStream::Peek7Bits() +{ +    unsigned int dwReloadByte = 0; + +    // If there is not enough bits to get the value, +    // we have to add 8 more bits from the input buffer +    if(BitCount < 7) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Return the first available 7 bits. DO NOT remove them from the input stream +    return (BitBuffer & 0x7F); +} + +// Gets one bit from input stream +unsigned int TInputStream::Get1Bit() +{ +    unsigned int OneBit = 0; + +    // Ensure that the input stream is reloaded, if there are no bits left +    if(BitCount == 0) +    { +        // Refill the bit buffer +        BitBuffer = *pbInBuffer++; +        BitCount = 8; +    } + +    // Copy the bit from bit buffer to the variable +    OneBit = (BitBuffer & 0x01); +    BitBuffer >>= 1; +    BitCount--; + +    return OneBit; +}    + +// Gets the whole byte from the input stream. +unsigned int TInputStream::Get8Bits() +{ +    unsigned int dwReloadByte = 0; +    unsigned int dwOneByte = 0; + +    // If there is not enough bits to get the value, +    // we have to add 8 more bits from the input buffer +    if(BitCount < 8) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Return the lowest 8 its +    dwOneByte = (BitBuffer & 0xFF); +    BitBuffer >>= 8; +    BitCount -= 8; +    return dwOneByte; +} + +void TInputStream::SkipBits(unsigned int dwBitsToSkip) +{ +    unsigned int dwReloadByte = 0; + +    // If there is not enough bits in the buffer, +    // we have to add 8 more bits from the input buffer +    if(BitCount < dwBitsToSkip) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Skip the remaining bits +    BitBuffer >>= dwBitsToSkip; +    BitCount -= dwBitsToSkip; +} + +//----------------------------------------------------------------------------- +// TOutputStream functions + +TOutputStream::TOutputStream(void * pvOutBuffer, size_t cbOutLength) +{ +    pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength; +    pbOutBuffer = (unsigned char *)pvOutBuffer; +    BitBuffer = 0; +    BitCount = 0; +} + +void TOutputStream::PutBits(unsigned int dwValue, unsigned int nBitCount) +{ +    BitBuffer |= (dwValue << BitCount); +    BitCount += nBitCount; +  +    // Flush completed bytes +    while(BitCount >= 8) +    { +        if(pbOutBuffer < pbOutBufferEnd) +            *pbOutBuffer++ = (unsigned char)BitBuffer; +  +        BitBuffer >>= 8; +        BitCount -= 8; +    } +} + +void TOutputStream::Flush() +{ +    while(BitCount != 0) +    { +        if(pbOutBuffer < pbOutBufferEnd) +            *pbOutBuffer++ = (unsigned char)BitBuffer; + +        BitBuffer >>= 8; +        BitCount -= ((BitCount > 8) ? 8 : BitCount); +    } +} + +//----------------------------------------------------------------------------- +// Methods of the THTreeItem struct + +void THTreeItem::RemoveItem() +{ +    if(pNext != NULL) +    { +        pPrev->pNext = pNext; +        pNext->pPrev = pPrev; +        pNext = pPrev = NULL; +    } +} + +//----------------------------------------------------------------------------- +// THuffmannTree class functions +  +THuffmannTree::THuffmannTree(bool bCompression) +{ +    // TODO: Obsolete, delete this!! +//  InitializeHTListHead(&ItemLinks); +    pFirst = pLast = LIST_HEAD(); +  +    MinValidValue = 1; +    ItemsUsed = 0; +  +    // If we are going to decompress data, we need to invalidate all item links +    // We do so by zeroing their ValidValue, so it becomes lower MinValidValue +    if(bCompression == false) +    { +        memset(QuickLinks, 0, sizeof(QuickLinks)); +    } +} + +THuffmannTree::~THuffmannTree() +{ +    // Our Huffmann tree does not use any memory allocations, +    // so we don't need to do eny code in the destructor +} + +void THuffmannTree::LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2) +{ +    pItem2->pNext = pItem1->pNext; +    pItem2->pPrev = pItem1->pNext->pPrev; +    pItem1->pNext->pPrev = pItem2; +    pItem1->pNext = pItem2; +} + +// Inserts item into the tree (?) +void THuffmannTree::InsertItem(THTreeItem * pNewItem, TInsertPoint InsertPoint, THTreeItem * pInsertPoint) +{ +    // Remove the item from the tree +    pNewItem->RemoveItem(); +  +    if(pInsertPoint == NULL) +        pInsertPoint = LIST_HEAD(); + +    switch(InsertPoint) +    { +        case InsertAfter: +            LinkTwoItems(pInsertPoint, pNewItem); +            return; +        +        case InsertBefore: +            pNewItem->pNext = pInsertPoint;             // Set next item (or pointer to pointer to first item) +            pNewItem->pPrev = pInsertPoint->pPrev;      // Set prev item (or last item in the tree) +            pInsertPoint->pPrev->pNext = pNewItem; +            pInsertPoint->pPrev = pNewItem;             // Set the next/last item +            return; +    } +} + +THTreeItem * THuffmannTree::FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight) +{ +    // Parse all existing items +    if(pItem != NULL) +    { +        while(pItem != LIST_HEAD()) +        { +            if(pItem->Weight >= Weight) +                return pItem; + +            pItem = pItem->pPrev; +        } +    } + +    // If not found, we just get the first item +    return LIST_HEAD(); +} + +THTreeItem * THuffmannTree::CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint) +{ +    THTreeItem * pNewItem; + +    // Allocate new item from the item pool +    pNewItem = &ItemBuffer[ItemsUsed++]; + +    // Insert this item to the top of the tree +    InsertItem(pNewItem, InsertPoint, NULL); + +    // Fill the rest of the item +    pNewItem->DecompressedValue = DecompressedValue; +    pNewItem->Weight = Weight; +    pNewItem->pParent = NULL; +    pNewItem->pChildLo = NULL; +    return pNewItem; +} + +unsigned int THuffmannTree::FixupItemPosByWeight(THTreeItem * pNewItem, unsigned int MaxWeight) +{ +    THTreeItem * pHigherItem; + +    if(pNewItem->Weight < MaxWeight) +    { +        // Find an item that has higher weight than this one +        pHigherItem = FindHigherOrEqualItem(pLast, pNewItem->Weight); + +        // Remove the item and put it to the new position +        pNewItem->RemoveItem(); +        LinkTwoItems(pHigherItem, pNewItem); +    } +    else +    { +        MaxWeight = pNewItem->Weight; +    } + +    // Return the (updated) maximum weight +    return MaxWeight; +} + +// Builds Huffman tree. Called with the first 8 bits loaded from input stream +void THuffmannTree::BuildTree(unsigned int CompressionType) +{ +    THTreeItem * pNewItem; +    THTreeItem * pChildLo; +    THTreeItem * pChildHi; +    unsigned char * WeightTable; +    unsigned int MaxWeight;                     // [ESP+10] - The greatest character found in table +  +    // Clear all pointers in HTree item array +    memset(ItemsByByte, 0, sizeof(ItemsByByte)); +    MaxWeight = 0; +  +    // Ensure that the compression type is in range +    assert((CompressionType & 0x0F) <= 0x08); +    WeightTable  = WeightTables[CompressionType & 0x0F]; +  +    // Build the linear list of entries that is sorted by byte weight +    for(unsigned int i = 0; i < 0x100; i++) +    { +        // Skip all the bytes which are zero. +        if(WeightTable[i] != 0) +        { +            // Create new tree item +            ItemsByByte[i] = pNewItem = CreateNewItem(i, WeightTable[i], InsertAfter); + +            // We need to put the item to the right place in the list +            MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight); +        } +    } +  +    // Insert termination entries at the end of the list +    ItemsByByte[0x100] = CreateNewItem(0x100, 1, InsertBefore); +    ItemsByByte[0x101] = CreateNewItem(0x101, 1, InsertBefore); +  +    // Now we need to build the tree. We start at the last entry +    // and go backwards to the first one +    pChildLo = pLast; + +    // Work as long as both children are valid +    // pChildHi is child with higher weight, pChildLo is the one with lower weight +    while(pChildLo != LIST_HEAD()) +    { +        // Also get and verify the higher-weight child +        pChildHi = pChildLo->pPrev; +        if(pChildHi == LIST_HEAD()) +            break; + +        // Create new parent item for the children +        pNewItem = CreateNewItem(0, pChildHi->Weight + pChildLo->Weight, InsertAfter); + +        // Link both child items to their new parent +        pChildLo->pParent = pNewItem; +        pChildHi->pParent = pNewItem; +        pNewItem->pChildLo = pChildLo; + +        // Fixup the item's position by its weight +        MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight); + +        // Get the previous lower-weight child +        pChildLo = pChildHi->pPrev; +    } + +    // Initialize the MinValidValue to 1, which invalidates all quick-link items +    MinValidValue = 1; +} + +void THuffmannTree::IncWeightsAndRebalance(THTreeItem * pItem) +{ +    THTreeItem * pHigherItem;           // A previous item with greater or equal weight +    THTreeItem * pChildHi;              // The higher-weight child +    THTreeItem * pChildLo;              // The lower-weight child +    THTreeItem * pParent; +  +    // Climb up the tree and increment weight of each tree item +    for(; pItem != NULL; pItem = pItem->pParent) +    { +        // Increment the item's weight +        pItem->Weight++; + +        // Find a previous item with equal or greater weight, which is not equal to this item +        pHigherItem = FindHigherOrEqualItem(pItem->pPrev, pItem->Weight); +        pChildHi = pHigherItem->pNext; + +        // If the item is not equal to the tree item, we need to rebalance the tree +        if(pChildHi != pItem) +        { +            // Move the previous item to the RIGHT from the given item +            pChildHi->RemoveItem(); +            LinkTwoItems(pItem, pChildHi); +             +            // Move the given item AFTER the greater-weight tree item +            pItem->RemoveItem(); +            LinkTwoItems(pHigherItem, pItem); +      +            // We need to maintain the tree so that pChildHi->Weight is >= pChildLo->Weight. +            // Rebalance the tree accordingly. +            pChildLo = pChildHi->pParent->pChildLo; +            pParent = pItem->pParent; +            if(pParent->pChildLo == pItem) +                pParent->pChildLo = pChildHi; +            if(pChildLo == pChildHi) +                pChildHi->pParent->pChildLo = pItem; +            pParent = pItem->pParent; +            pItem->pParent = pChildHi->pParent; +            pChildHi->pParent = pParent; + +            // Increment the global valid value. This invalidates all quick-link items. +            MinValidValue++; +        } +    } +} + +void THuffmannTree::InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2) +{ +    THTreeItem * pLastItem = pLast; +    THTreeItem * pChildHi; +    THTreeItem * pChildLo; + +    // Create higher-weight child +    pChildHi = CreateNewItem(Value1, pLastItem->Weight, InsertBefore); +    pChildHi->pParent = pLastItem; +    ItemsByByte[Value1] = pChildHi; + +    // Create lower-weight child +    pChildLo = CreateNewItem(Value2, 0, InsertBefore); +    pChildLo->pParent = pLastItem; +    pLastItem->pChildLo = pChildLo; +    ItemsByByte[Value2] = pChildLo; + +    IncWeightsAndRebalance(pChildLo); +} + +void THuffmannTree::EncodeOneByte(TOutputStream * os, THTreeItem * pItem) +{ +    THTreeItem * pParent = pItem->pParent; +    unsigned int BitBuffer = 0; +    unsigned int BitCount = 0; + +    // Put 1's as long as there is parent +    while(pParent != NULL) +    { +        // Fill the bit buffer +        BitBuffer = (BitBuffer << 1) | ((pParent->pChildLo != pItem) ? 1 : 0); +        BitCount++; + +        // Move to the parent +        pItem = pParent; +        pParent = pParent->pParent; +    } + +    // Write the bits to the output stream +    os->PutBits(BitBuffer, BitCount); +} + +unsigned int THuffmannTree::DecodeOneByte(TInputStream * is) +{ +    THTreeItem * pItemLink = NULL; +    THTreeItem * pItem; +    unsigned int ItemLinkIndex; +    unsigned int BitCount = 0; + +    // Check for the end of the input stream +    if(is->pbInBuffer >= is->pbInBufferEnd && is->BitCount < 7) +        return 0x1FF; + +    // Get the eventual quick-link index +    ItemLinkIndex = is->Peek7Bits(); +     +    // Is the quick-link item valid? +    if(QuickLinks[ItemLinkIndex].ValidValue > MinValidValue) +    { +        // If that item needs less than 7 bits, we can get decompressed value directly +        if(QuickLinks[ItemLinkIndex].ValidBits <= 7) +        { +            is->SkipBits(QuickLinks[ItemLinkIndex].ValidBits); +            return QuickLinks[ItemLinkIndex].DecompressedValue; +        } + +        // Otherwise we cannot get decompressed value directly +        // but we can skip 7 levels of tree parsing +        pItem = QuickLinks[ItemLinkIndex].pItem; +        is->SkipBits(7); +    } +    else +    { +        // Just a sanity check +        if(pFirst == LIST_HEAD()) +            return 0x1FF; + +        // We don't have the quick-link item, we need to parse the tree from its root +        pItem = pFirst; +    } + +    // Step down the tree until we find a terminal item +    while(pItem->pChildLo != NULL) +    { +        // If the next bit in the compressed stream is set, we get the higher-weight +        // child. Otherwise, get the lower-weight child. +        pItem = is->Get1Bit() ? pItem->pChildLo->pPrev : pItem->pChildLo; +        BitCount++; + +        // If the number of loaded bits reached 7, +        // remember the current item for storing into quick-link item array +        if(BitCount == 7) +            pItemLink = pItem; +    } + +    // If we didn't get the item from the quick-link array, +    // set the entry in it +    if(QuickLinks[ItemLinkIndex].ValidValue < MinValidValue) +    { +        // If the current compressed byte was more than 7 bits, +        // set a quick-link item with pointer to tree item +        if(BitCount > 7) +        { +            QuickLinks[ItemLinkIndex].ValidValue = MinValidValue; +            QuickLinks[ItemLinkIndex].ValidBits = BitCount; +            QuickLinks[ItemLinkIndex].pItem = pItemLink; +        } +        else +        { +            // Limit the quick-decompress item to lower amount of bits +            ItemLinkIndex &= (0xFFFFFFFF >> (32 - BitCount)); +            while(ItemLinkIndex < LINK_ITEM_COUNT) +            { +                // Fill the quick-decompress item +                QuickLinks[ItemLinkIndex].ValidValue = MinValidValue; +                QuickLinks[ItemLinkIndex].ValidBits  = BitCount; +                QuickLinks[ItemLinkIndex].DecompressedValue = pItem->DecompressedValue; + +                // Increment the index +                ItemLinkIndex += (1 << BitCount); +            } +        } +    } + +    // Return the decompressed value from the found item +    return pItem->DecompressedValue; +} + +unsigned int THuffmannTree::Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int CompressionType) +{ +    unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; +    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; +    unsigned char * pbOutBuff = os->pbOutBuffer; +    unsigned char InputByte; +  +    BuildTree(CompressionType); +    bIsCmp0 = (CompressionType == 0); + +    // Store the compression type into output buffer +    os->PutBits(CompressionType, 8); +  +    // Process the entire input buffer +    while(pbInBuffer < pbInBufferEnd) +    { +        // Get the (next) byte from the input buffer +        InputByte = *pbInBuffer++; + +        // Do we have an item for such input value? +        if(ItemsByByte[InputByte] == NULL) +        { +            // Encode the relationship +            EncodeOneByte(os, ItemsByByte[0x101]); +  +            // Store the loaded byte into output stream +            os->PutBits(InputByte, 8); +  +            InsertNewBranchAndRebalance(pLast->DecompressedValue, InputByte); + +            if(bIsCmp0) +            { +                IncWeightsAndRebalance(ItemsByByte[InputByte]); +                continue; +            } +   +            IncWeightsAndRebalance(ItemsByByte[InputByte]); +        } +        else +        { +            EncodeOneByte(os, ItemsByByte[InputByte]); +        } +  +        if(bIsCmp0) +        { +            IncWeightsAndRebalance(ItemsByByte[InputByte]); +        } +    } +  +    // Put the termination mark to the compressed stream +    EncodeOneByte(os, ItemsByByte[0x100]); +  +    // Flush the remaining bits +    os->Flush(); +    return (unsigned int)(os->pbOutBuffer - pbOutBuff); +} +  +// Decompression using Huffman tree (1500E450) +unsigned int THuffmannTree::Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is) +{ +    unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength; +    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; +    unsigned int DecompressedValue = 0; +    unsigned int CompressionType = 0; +    +    // Test the output length. Must not be NULL. +    if(cbOutLength == 0) +        return 0; +  +    // Get the compression type from the input stream +    CompressionType = is->Get8Bits(); +    bIsCmp0 = (CompressionType == 0) ? 1 : 0; +  +    // Build the Huffman tree +    BuildTree(CompressionType);     +  +    // Process the entire input buffer until end of the stream +    while((DecompressedValue = DecodeOneByte(is)) != 0x100) +    { +        // Did an error occur? +        if(DecompressedValue == 0x1FF)          // An error occurred +            return 0; + +        // Huffman tree needs to be modified +        if(DecompressedValue == 0x101) +        { +            // The decompressed byte is stored in the next 8 bits +            DecompressedValue = is->Get8Bits(); + +            InsertNewBranchAndRebalance(pLast->DecompressedValue, DecompressedValue); + +            if(bIsCmp0 == 0) +                IncWeightsAndRebalance(ItemsByByte[DecompressedValue]); +        } +  +        // A byte successfully decoded - store it in the output stream +        *pbOutBuffer++ = (unsigned char)DecompressedValue; +        if(pbOutBuffer >= pbOutBufferEnd) +            break; +  +        if(bIsCmp0) +        { +            IncWeightsAndRebalance(ItemsByByte[DecompressedValue]); +        } +    } +  +    return (unsigned int)(pbOutBuffer - (unsigned char *)pvOutBuffer); +} + diff --git a/src/huffman/huff.h b/src/huffman/huff.h new file mode 100644 index 0000000..b0a54ee --- /dev/null +++ b/src/huffman/huff.h @@ -0,0 +1,143 @@ +/*****************************************************************************/ +/* huffman.h                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description :                                                             */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of huffman.h                       */ +/* 03.05.03  2.00  Lad  Added compression                                    */ +/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */ +/*****************************************************************************/ +  +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +//----------------------------------------------------------------------------- +// Defines +  +#define HUFF_ITEM_COUNT    0x203        // Number of items in the item pool +#define LINK_ITEM_COUNT    0x80         // Maximum number of quick-link items + +//----------------------------------------------------------------------------- +// Structures and classes + +// Input stream for Huffmann decompression +class TInputStream +{ +    public: + +    TInputStream(void * pvInBuffer, size_t cbInBuffer); +    unsigned int Get1Bit(); +    unsigned int Peek7Bits(); +    unsigned int Get8Bits(); +    void SkipBits(unsigned int BitCount); +  +    unsigned char * pbInBufferEnd;      // End position in the the input buffer +    unsigned char * pbInBuffer;         // Current position in the the input buffer +    unsigned int BitBuffer;             // Input bit buffer +    unsigned int BitCount;              // Number of bits remaining in 'dwBitBuff' +}; +  + +// Output stream for Huffmann compression +class TOutputStream +{ +    public: +  +    TOutputStream(void * pvOutBuffer, size_t cbOutLength); +    void PutBits(unsigned int dwValue, unsigned int nBitCount); +    void Flush(); +  +    unsigned char * pbOutBufferEnd;     // End position in the output buffer +    unsigned char * pbOutBuffer;        // Current position in the output buffer +    unsigned int BitBuffer;             // Bit buffer +    unsigned int BitCount;              // Number of bits in the bit buffer +}; + +// A virtual tree item that represents the head of the item list +#define LIST_HEAD()  ((THTreeItem *)(&pFirst)) + +enum TInsertPoint +{ +    InsertAfter = 1, +    InsertBefore = 2 +}; + +// Huffmann tree item +struct THTreeItem +{ +    THTreeItem()    { pPrev = pNext = NULL;} +//  ~THTreeItem()   { RemoveItem(); } + +    void         RemoveItem(); +//  void         RemoveEntry(); +  +    THTreeItem  * pNext;                // Pointer to lower-weight tree item +    THTreeItem  * pPrev;                // Pointer to higher-weight item +    unsigned int  DecompressedValue;    // 08 - Decompressed byte value (also index in the array) +    unsigned int  Weight;               // 0C - Weight +    THTreeItem  * pParent;              // 10 - Pointer to parent item (NULL if none) +    THTreeItem  * pChildLo;             // 14 - Pointer to the child with lower-weight child ("left child") +}; + + +// Structure used for quick navigating in the huffmann tree. +// Allows skipping up to 7 bits in the compressed stream, thus +// decompressing a bit faster. Sometimes it can even get the decompressed +// byte directly. +struct TQuickLink +{       +    unsigned int ValidValue;            // If greater than THuffmannTree::MinValidValue, the entry is valid +    unsigned int ValidBits;             // Number of bits that are valid for this item link +    union +    { +        THTreeItem  * pItem;            // Pointer to the item within the Huffmann tree +        unsigned int DecompressedValue; // Value for direct decompression +    }; +}; +                                            + +// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert +// for the decompression, I do not know actually if the class is really a Hufmann +// tree. If someone knows the decompression details, please let me know +class THuffmannTree +{ +    public: +     +    THuffmannTree(bool bCompression); +    ~THuffmannTree(); + +    void  LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2); +    void  InsertItem(THTreeItem * item, TInsertPoint InsertPoint, THTreeItem * item2); + +    THTreeItem * FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight); +    THTreeItem * CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint); + +    unsigned int FixupItemPosByWeight(THTreeItem * pItem, unsigned int MaxWeight); +    void  BuildTree(unsigned int CompressionType); + +    void  IncWeightsAndRebalance(THTreeItem * pItem); +    void  InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2); + +    void  EncodeOneByte(TOutputStream * os, THTreeItem * pItem); +    unsigned int DecodeOneByte(TInputStream * is); + +    unsigned int Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int nCmpType); +    unsigned int Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is); +  +    THTreeItem   ItemBuffer[HUFF_ITEM_COUNT];   // Buffer for tree items. No memory allocation is needed +    unsigned int ItemsUsed;                     // Number of tree items used from ItemBuffer +  +    // Head of the linear item list +    THTreeItem * pFirst;                        // Pointer to the highest weight item +    THTreeItem * pLast;                         // Pointer to the lowest weight item + +    THTreeItem * ItemsByByte[0x102];            // Array of item pointers, one for each possible byte value +    TQuickLink   QuickLinks[LINK_ITEM_COUNT];   // Array of quick-link items +     +    unsigned int MinValidValue;                 // A minimum value of TQDecompress::ValidValue to be considered valid +    unsigned int bIsCmp0;                       // 1 if compression type 0 +}; + +#endif // __HUFFMAN_H__ diff --git a/src/huffman/huff_old.cpp b/src/huffman/huff_old.cpp new file mode 100644 index 0000000..66a46b3 --- /dev/null +++ b/src/huffman/huff_old.cpp @@ -0,0 +1,1303 @@ +/*****************************************************************************/ +/* huffman.cpp                       Copyright (c) Ladislav Zezula 1998-2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains Huffmann (de)compression methods                     */ +/*                                                                           */ +/* Authors : Ladislav Zezula (ladik@zezula.net)                              */ +/*           ShadowFlare     (BlakFlare@hotmail.com)                         */ +/*                                                                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of dcmp.cpp                        */ +/* 03.05.03  1.00  Lad  Added compression methods                            */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */ +/*****************************************************************************/ +  +#include <assert.h> +#include <string.h> +  +#include "huff.h" + +// Special for Mac - we have to know if normal pointer greater or less +// than 0x80000000. This variable is used in the PTR_VALID and PTR_INVALID +// macros +static long mul = 1; + +#define PTR_VALID(ptr)           (((LONG_PTR)(ptr) * mul) > 0) +#define PTR_INVALID(ptr)         (((LONG_PTR)(ptr) * mul) < 0) +#define PTR_INVALID_OR_NULL(ptr) (((LONG_PTR)(ptr) * mul) <= 0) + +  +//----------------------------------------------------------------------------- +// Methods of the THTreeItem struct +  +// 1501DB70 +THTreeItem * THTreeItem::Call1501DB70(THTreeItem * pLast) +{ +    if(pLast == NULL) +        pLast = this + 1; +    return pLast; +} +  +// Gets previous Huffman tree item (?) +THTreeItem * THTreeItem::GetPrevItem(LONG_PTR value) +{ +    if(PTR_INVALID(prev)) +        return PTR_NOT(prev); +  +    if(value == -1 || PTR_INVALID(value)) +        value = (LONG_PTR)(this - next->prev); +    return prev + value; +  +// OLD VERSION +//  if(PTR_INT(value) < 0) +//      value = PTR_INT((item - item->next->prev)); +//  return (THTreeItem *)((char *)prev + value); +} +  +// 1500F5E0 +void THTreeItem::ClearItemLinks() +{ +    next = prev = NULL; +} +  +// 1500BC90 +void THTreeItem::RemoveItem() +{ +    THTreeItem * pTemp;                // EDX +  +    if(next != NULL) +    { +        pTemp = prev; +        +        if(PTR_INVALID_OR_NULL(pTemp)) +            pTemp = PTR_NOT(pTemp); +        else +            pTemp += (this - next->prev); +  +        pTemp->next = next; +        next->prev  = prev; +        next = prev = NULL; +    } +} +  +/* +// OLD VERSION : Removes item from the tree (?) +static void RemoveItem(THTreeItem * item) +{ +    THTreeItem * next = item->next;     // ESI +    THTreeItem * prev = item->prev;     // EDX +  +    if(next == NULL) +        return; +  +    if(PTR_INT(prev) < 0) +        prev = PTR_NOT(prev); +    else +        // ??? usually item == next->prev, so what is it ? +        prev = (THTreeItem *)((unsigned char *)prev + (unsigned long)((unsigned char *)item - (unsigned char *)(next->prev))); +  +    // Remove HTree item from the chain +    prev->next = next;                  // Sets the 'first' pointer +    next->prev = item->prev; +  +    // Invalidate pointers +    item->next = NULL; +    item->prev = NULL; +} +*/ +  +//----------------------------------------------------------------------------- +// TOutputStream functions +  +void TOutputStream::PutBits(unsigned long dwBuff, unsigned int nPutBits) +{ +    dwBitBuff |= (dwBuff << nBits); +    nBits     += nPutBits; +  +    // Flush completed bytes +    while(nBits >= 8) +    { +        if(cbOutSize != 0) +        { +            *pbOutPos++ = (unsigned char)dwBitBuff; +            cbOutSize--; +        } +  +        dwBitBuff >>= 8; +        nBits      -= 8; +    } +} +  +//----------------------------------------------------------------------------- +// TInputStream functions + +// Gets one bit from input stream +unsigned long TInputStream::GetBit() +{ +    unsigned long dwOneBit = 0; + +    // Ensure that the input stream is reloaded, if there are no bits left +    if(BitCount == 0) +    { +        // Refill the bit buffer +        BitBuffer = *pbInBuffer++; +        BitCount = 8; +    } + +    // Copy the bit from bit buffer to the variable +    dwOneBit = (BitBuffer & 0x01); +    BitBuffer >>= 1; +    BitCount--; + +    return dwOneBit; +}    +  +// Gets 7 bits from the stream. DOES NOT remove the bits from input stream +unsigned long TInputStream::Get7Bits() +{ +    unsigned long dwReloadByte = 0; + +    // If there is not enough bits to get the value, +    // we have to add 8 more bits from the input buffer +    if(BitCount < 7) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Return the first available 7 bits. DO NOT remove them from the input stream +    return (BitBuffer & 0x7F); +} +  +// Gets the whole byte from the input stream. +unsigned long TInputStream::Get8Bits() +{ +    unsigned long dwReloadByte = 0; +    unsigned long dwOneByte = 0; + +    // If there is not enough bits to get the value, +    // we have to add 8 more bits from the input buffer +    if(BitCount < 8) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Return the lowest 8 its +    dwOneByte = (BitBuffer & 0xFF); +    BitBuffer >>= 8; +    BitCount -= 8; +    return dwOneByte; +} + +void TInputStream::SkipBits(unsigned int dwBitsToSkip) +{ +    unsigned long dwReloadByte = 0; + +    // If there is not enough bits in the buffer, +    // we have to add 8 more bits from the input buffer +    if(BitCount < dwBitsToSkip) +    { +        dwReloadByte = *pbInBuffer++; +        BitBuffer |= dwReloadByte << BitCount; +        BitCount += 8; +    } + +    // Skip the remaining bits +    BitBuffer >>= dwBitsToSkip; +    BitCount -= dwBitsToSkip; +} + +//----------------------------------------------------------------------------- +// Functions for huffmann tree items +  +// Inserts item into the tree (?) +static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long nWhere, THTreeItem * item2) +{ +    THTreeItem * next = item->next;     // EDI - next to the first item +    THTreeItem * prev = item->prev;     // ESI - prev to the first item +    THTreeItem * prev2;                 // Pointer to previous item +    LONG_PTR next2;                     // Pointer to the next item +    +    // The same code like in RemoveItem(item); +    if(next != 0)                       // If the first item already has next one +    { +        if(PTR_INVALID(prev)) +            prev = PTR_NOT(prev); +        else +            prev += (item - next->prev); +  +        // 150083C1 +        // Remove the item from the tree +        prev->next = next; +        next->prev = prev; +  +        // Invalidate 'prev' and 'next' pointer +        item->next = 0; +        item->prev = 0; +    } +  +    if(item2 == NULL)                   // EDX - If the second item is not entered, +        item2 = PTR_PTR(&itemPtr[1]);   // take the first tree item +  +    switch(nWhere) +    { +        case SWITCH_ITEMS :             // Switch the two items +            item->next  = item2->next;  // item2->next (Pointer to pointer to first) +            item->prev  = item2->next->prev; +            item2->next->prev = item; +            item2->next = item;         // Set the first item +            return; +        +        case INSERT_ITEM:               // Insert as the last item +            item->next = item2;         // Set next item (or pointer to pointer to first item) +            item->prev = item2->prev;   // Set prev item (or last item in the tree) +  +            next2 = PTR_INT(itemPtr[0]);// Usually NULL +            prev2 = item2->prev;        // Prev item to the second (or last tree item) +            +            if(PTR_INVALID(prev2)) +            { +                if(prev != NULL) +                { +                    prev2 = PTR_NOT(prev); +                    if(prev2 != NULL) +                    { +                        prev2->next = item; +                        item2->prev = item;     // Next after last item +                    } +                } +                return; +            } +  +            if(PTR_INVALID(next2)) +                next2 = (LONG_PTR)(item2 - item2->next->prev); +//              next2 = (THTreeItem *)(unsigned long)((unsigned char *)item2 - (unsigned char *)(item2->next->prev)); +  +//          prev2 = (THTreeItem *)((char *)prev2 + (unsigned long)next2);// ??? +            prev2 += next2; +            prev2->next = item; +            item2->prev = item;         // Set the next/last item +            return; +  +        default: +            return; +    } +} +  +//----------------------------------------------------------------------------- +// THuffmannTree class functions +  +THuffmannTree::THuffmannTree() +{ +    // We have to check if the "this" pointer is less than zero +    if((LONG_PTR)this < 0) +        mul = -1; +} +  +void THuffmannTree::InitTree(bool bCompression) +{ +    THTreeItem * pItem; +    unsigned int nCount; +  +    // Clear links for all the items in the tree +    for(pItem = items0008, nCount = 0x203; nCount != 0; pItem++, nCount--) +        pItem->ClearItemLinks(); +  +    pItem3050 = NULL; +    pItem3054 = PTR_PTR(&pItem3054); +    pItem3058 = PTR_NOT(pItem3054); +    +    pItem305C = NULL; +    pFirst    = PTR_PTR(&pFirst); +    pLast     = PTR_NOT(pFirst); +  +    offs0004  = 1; +    nItems    = 0; +  +    // Clear all TQDecompress items. Do this only if preparing for decompression +    if(bCompression == false) +    { +        for(nCount = 0; nCount < sizeof(qd3474) / sizeof(TQDecompress); nCount++) +            qd3474[nCount].offs00 = 0; +    } +} +  +// Builds Huffman tree. Called with the first 8 bits loaded from input stream +void THuffmannTree::BuildTree(unsigned int nCmpType) +{ +    unsigned long   maxByte;                // [ESP+10] - The greatest character found in table +    THTreeItem   ** itemPtr;                // [ESP+14] - Pointer to Huffman tree item pointer array +    unsigned char * byteArray;              // [ESP+1C] - Pointer to unsigned char in Table1502A630 +    THTreeItem    * child1; +    unsigned long   i;                      // egcs in linux doesn't like multiple for loops without an explicit i +  +    // Loop while pointer has a valid value +    while(PTR_VALID(pLast))                 // ESI - Last entry +    { +        THTreeItem * temp;                  // EAX +  +        if(pLast->next != NULL)             // ESI->next +            pLast->RemoveItem(); +                                            // EDI = &offs3054 +        pItem3058   = PTR_PTR(&pItem3054);  // [EDI+4] +        pLast->prev = pItem3058;            // EAX +  +        temp = PTR_PTR(&pItem3054)->GetPrevItem(PTR_INT(&pItem3050)); +  +        temp->next = pLast; +        pItem3054  = pLast; +    } +  +    // Clear all pointers in HTree item array +    memset(items306C, 0, sizeof(items306C)); +  +    maxByte = 0;                            // Greatest character found init to zero. +    itemPtr = (THTreeItem **)&items306C;    // Pointer to current entry in HTree item pointer array +  +    // Ensure we have low 8 bits only +    nCmpType &= 0xFF; +    byteArray  = Table1502A630 + nCmpType * 258; // EDI also +  +    for(i = 0; i < 0x100; i++, itemPtr++) +    { +        THTreeItem * item   = pItem3058;    // Item to be created +        THTreeItem * pItem3 = pItem3058; +        unsigned char         oneByte = byteArray[i]; +  +        // Skip all the bytes which are zero. +        if(byteArray[i] == 0) +            continue; +  +        // If not valid pointer, take the first available item in the array +        if(PTR_INVALID_OR_NULL(item)) +            item = &items0008[nItems++]; +  +        // Insert this item as the top of the tree +        InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); +  +        item->parent    = NULL;                 // Invalidate child and parent +        item->child     = NULL; +        *itemPtr        = item;                 // Store pointer into pointer array +  +        item->dcmpByte  = i;                    // Store counter +        item->byteValue = oneByte;              // Store byte value +        if(oneByte >= maxByte) +        { +            maxByte = oneByte; +            continue; +        } +  +        // Find the first item which has byte value greater than current one byte +        if(PTR_VALID(pItem3 = pLast))           // EDI - Pointer to the last item +        { +            // 15006AF7 +            if(pItem3 != NULL) +            { +                do  // 15006AFB +                { +                    if(pItem3->byteValue >= oneByte) +                        goto _15006B09; +                    pItem3 = pItem3->prev; +                } +                while(PTR_VALID(pItem3)); +            } +        } +        pItem3 = NULL; +  +        // 15006B09 +        _15006B09: +        if(item->next != NULL) +            item->RemoveItem(); +  +        // 15006B15 +        if(pItem3 == NULL) +            pItem3 = PTR_PTR(&pFirst); +  +        // 15006B1F +        item->next = pItem3->next; +        item->prev = pItem3->next->prev; +        pItem3->next->prev = item; +        pItem3->next = item; +    } +  +    // 15006B4A +    for(; i < 0x102; i++) +    { +        THTreeItem ** itemPtr = &items306C[i];  // EDI +  +        // 15006B59 +        THTreeItem * item = pItem3058;          // ESI +        if(PTR_INVALID_OR_NULL(item)) +            item = &items0008[nItems++]; +  +        InsertItem(&pItem305C, item, INSERT_ITEM, NULL); +  +        // 15006B89 +        item->dcmpByte   = i; +        item->byteValue  = 1; +        item->parent     = NULL; +        item->child      = NULL; +        *itemPtr++ = item; +    } +  +    // 15006BAA +    if(PTR_VALID(child1 = pLast))                   // EDI - last item (first child to item +    { +        THTreeItem * child2;                        // EBP +        THTreeItem * item;                          // ESI +  +        // 15006BB8 +        while(PTR_VALID(child2 = child1->prev)) +        { +            if(PTR_INVALID_OR_NULL(item = pItem3058)) +                item = &items0008[nItems++]; +  +            // 15006BE3 +            InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); +  +            // 15006BF3 +            item->parent = NULL; +            item->child  = NULL; +  +            //EDX = child2->byteValue + child1->byteValue; +            //EAX = child1->byteValue; +            //ECX = maxByte;                        // The greatest character (0xFF usually) +  +            item->byteValue = child1->byteValue + child2->byteValue; // 0x02 +            item->child     = child1;                                // Prev item in the +            child1->parent  = item; +            child2->parent  = item; +  +            // EAX = item->byteValue; +            if(item->byteValue >= maxByte) +                maxByte = item->byteValue; +            else +            { +                THTreeItem * pItem2 = child2->prev;   // EDI +  +                // 15006C2D +                while(PTR_VALID(pItem2)) +                { +                    if(pItem2->byteValue >= item->byteValue) +                        goto _15006C3B; +                    pItem2 = pItem2->prev; +                } +                pItem2 = NULL; +  +                _15006C3B: +                if(item->next != 0) +                { +                    THTreeItem * temp4 = item->GetPrevItem(-1); +                                                                    +                    temp4->next      = item->next;                 // The first item changed +                    item->next->prev = item->prev;                 // First->prev changed to negative value +                    item->next = NULL; +                    item->prev = NULL; +                } +  +                // 15006C62 +                if(pItem2 == NULL) +                    pItem2 = PTR_PTR(&pFirst); +  +                item->next = pItem2->next;                           // Set item with 0x100 byte value +                item->prev = pItem2->next->prev;                     // Set item with 0x17 byte value +                pItem2->next->prev = item;                           // Changed prev of item with +                pItem2->next = item; +            } + +            // 15006C7B +            if(PTR_INVALID_OR_NULL(child1 = child2->prev)) +                break; +        } +    } +    // 15006C88 +    offs0004 = 1; +} +/* +// Modifies Huffman tree. Adds new item and changes +void THuffmannTree::ModifyTree(unsigned long dwIndex) +{ +    THTreeItem * pItem1 = pItem3058;                              // ESI +    THTreeItem * pSaveLast = (PTR_INT(pLast) <= 0) ? NULL : pLast;  // EBX +    THTreeItem * temp;                                              // EAX +  +    // Prepare the first item to insert to the tree +    if(PTR_INT(pItem1) <= 0) +        pItem1 = &items0008[nItems++]; +  +    // If item has any next item, remove it from the chain +    if(pItem1->next != NULL) +    { +        THTreeItem * temp = pItem1->GetPrevItem(-1);                  // EAX +  +        temp->next = pItem1->next; +        pItem1->next->prev = pItem1->prev; +        pItem1->next = NULL; +        pItem1->prev = NULL; +    } +  +    pItem1->next = PTR_PTR(&pFirst); +    pItem1->prev = pLast; +    temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); +  +    // 150068E9 +    temp->next = pItem1; +    pLast  = pItem1; +  +    pItem1->parent = NULL; +    pItem1->child  = NULL; +  +    // 150068F6 +    pItem1->dcmpByte  = pSaveLast->dcmpByte;   // Copy item index +    pItem1->byteValue = pSaveLast->byteValue;  // Copy item byte value +    pItem1->parent    = pSaveLast;             // Set parent to last item +    items306C[pSaveLast->dcmpByte] = pItem1;  // Insert item into item pointer array +  +    // Prepare the second item to insert into the tree +    if(PTR_INT((pItem1 = pItem3058)) <= 0) +        pItem1 = &items0008[nItems++]; +  +    // 1500692E +    if(pItem1->next != NULL) +    { +        temp = pItem1->GetPrevItem(-1);   // EAX +  +        temp->next = pItem1->next; +        pItem1->next->prev = pItem1->prev; +        pItem1->next = NULL; +        pItem1->prev = NULL; +    } +    // 1500694C +    pItem1->next = PTR_PTR(&pFirst); +    pItem1->prev = pLast; +    temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); +  +    // 15006968 +    temp->next = pItem1; +    pLast      = pItem1; +  +    // 1500696E +    pItem1->child     = NULL; +    pItem1->dcmpByte  = dwIndex; +    pItem1->byteValue = 0; +    pItem1->parent    = pSaveLast; +    pSaveLast->child   = pItem1; +    items306C[dwIndex] = pItem1; +  +    do +    { +        THTreeItem  * pItem2 = pItem1; +        THTreeItem  * pItem3; +        unsigned long byteValue; +  +        // 15006993 +        byteValue = ++pItem1->byteValue; +  +        // Pass through all previous which have its value greater than byteValue +        while(PTR_INT((pItem3 = pItem2->prev)) > 0)  // EBX +        { +            if(pItem3->byteValue >= byteValue) +                goto _150069AE; +  +            pItem2 = pItem2->prev; +        } +        // 150069AC +        pItem3 = NULL; +  +        _150069AE: +        if(pItem2 == pItem1) +            continue; +  +        // 150069B2 +        // Switch pItem2 with item +        InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); +        InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); +  +        // 150069D0 +        // Switch parents of pItem1 and pItem2 +        temp = pItem2->parent->child; +        if(pItem1 == pItem1->parent->child) +            pItem1->parent->child = pItem2; +  +        if(pItem2 == temp) +            pItem2->parent->child = pItem1; +  +        // 150069ED +        // Switch parents of pItem1 and pItem3 +        temp = pItem1->parent; +        pItem1 ->parent = pItem2->parent; +        pItem2->parent = temp; +        offs0004++; +    } +    while(PTR_INT((pItem1 = pItem1->parent)) > 0); +} +  +void THuffmannTree::UninitTree() +{ +    while(PTR_INT(pLast) > 0) +    { +        pItem = pItem305C->Call1501DB70(pLast); +        pItem->RemoveItem(); +    } +  +    for(pItem = pFirst; PTR_INT(pItem3058) > 0; pItem = pItem3058) +        pItem->RemoveItem(); +    PTR_PTR(&pItem3054)->RemoveItem(); +  +    for(pItem = items0008 + 0x203, nCount = 0x203; nCount != 0; nCount--) +    { +        pItem--; +        pItem->RemoveItem(); +        pItem->RemoveItem(); +    } +} +*/ +  +THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) +{ +    THTreeItem * pItem1 = pItem3058;    // EDX +    THTreeItem * pItem2;                // EAX +    THTreeItem * pNext; +    THTreeItem * pPrev; +    THTreeItem ** ppItem; +  +    if(PTR_INVALID_OR_NULL(pItem1) || (pItem2 = pItem1) == NULL) +    { +        if((pItem2 = &items0008[nItems++]) != NULL) +            pItem1 = pItem2; +        else +            pItem1 = pFirst; +    } +    else +        pItem1 = pItem2; +  +    pNext = pItem1->next; +    if(pNext != NULL) +    { +        pPrev = pItem1->prev; +        if(PTR_INVALID_OR_NULL(pPrev)) +            pPrev = PTR_NOT(pPrev); +        else +            pPrev += (pItem1 - pItem1->next->prev); +  +        pPrev->next = pNext; +        pNext->prev = pPrev; +        pItem1->next = NULL; +        pItem1->prev = NULL; +    } +  +    ppItem = &pFirst;       // esi +    if(nValue > 1) +    { +        // ecx = pFirst->next; +        pItem1->next = *ppItem; +        pItem1->prev = (*ppItem)->prev; +  +        (*ppItem)->prev = pItem2; +        *ppItem = pItem1; +  +        pItem2->parent = NULL; +        pItem2->child  = NULL; +    } +    else +    { +        pItem1->next = (THTreeItem *)ppItem; +        pItem1->prev = ppItem[1]; +        // edi = pItem305C; +        pPrev = ppItem[1];      // ecx +        if(PTR_INVALID_OR_NULL(pPrev)) +        { +            pPrev = PTR_NOT(pPrev); +            pPrev->next = pItem1; +            pPrev->prev = pItem2; +  +            pItem2->parent = NULL; +            pItem2->child  = NULL; +        } +        else +        { +            if(PTR_INVALID(pItem305C)) +                pPrev += (THTreeItem *)ppItem - (*ppItem)->prev; +            else +                pPrev += PTR_INT(pItem305C); +  +            pPrev->next    = pItem1; +            ppItem[1]      = pItem2; +            pItem2->parent = NULL; +            pItem2->child  = NULL; +        } +    } +    return pItem2; +} +  +void THuffmannTree::Call1500E820(THTreeItem * pItem) +{ +    THTreeItem * pItem1;                // edi +    THTreeItem * pItem2 = NULL;         // eax +    THTreeItem * pItem3;                // edx +    THTreeItem * pPrev;                 // ebx +  +    for(; pItem != NULL; pItem = pItem->parent) +    { +        pItem->byteValue++; +        +        for(pItem1 = pItem; ; pItem1 = pPrev) +        { +            pPrev = pItem1->prev; +            if(PTR_INVALID_OR_NULL(pPrev)) +            { +                pPrev = NULL; +                break; +            } +  +            if(pPrev->byteValue >= pItem->byteValue) +                break; +        } +  +        if(pItem1 == pItem) +            continue; +  +        if(pItem1->next != NULL) +        { +            pItem2 = pItem1->GetPrevItem(-1); +            pItem2->next = pItem1->next; +            pItem1->next->prev = pItem1->prev; +            pItem1->next = NULL; +            pItem1->prev = NULL; +        } +  +        pItem2 = pItem->next; +        pItem1->next = pItem2; +        pItem1->prev = pItem2->prev; +        pItem2->prev = pItem1; +        pItem->next = pItem1; +        if((pItem2 = pItem1) != NULL) +        { +            pItem2 = pItem->GetPrevItem(-1); +            pItem2->next = pItem->next; +            pItem->next->prev = pItem->prev; +            pItem->next = NULL; +            pItem->prev = NULL; +        } +  +        if(pPrev == NULL) +            pPrev = PTR_PTR(&pFirst); +  +        pItem2       = pPrev->next; +        pItem->next  = pItem2; +        pItem->prev  = pItem2->prev; +        pItem2->prev = pItem; +        pPrev->next  = pItem; +  +        pItem3 = pItem1->parent->child; +        pItem2 = pItem->parent; +        if(pItem2->child == pItem) +            pItem2->child = pItem1; +        if(pItem3 == pItem1) +            pItem1->parent->child = pItem; +  +        pItem2 = pItem->parent; +        pItem->parent  = pItem1->parent; +        pItem1->parent = pItem2; +        offs0004++; +    } +} +  +// 1500E920 +unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType) +{ +    THTreeItem  * pItem1; +    THTreeItem  * pItem2; +    THTreeItem  * pItem3; +    THTreeItem  * pTemp; +    unsigned long dwBitBuff; +    unsigned int  nBits; +    unsigned int  nBit; +  +    BuildTree(nCmpType); +    bIsCmp0 = (nCmpType == 0); +  +    // Store the compression type into output buffer +    os->dwBitBuff |= (nCmpType << os->nBits); +    os->nBits     += 8; +  +    // Flush completed bytes +    while(os->nBits >= 8) +    { +        if(os->cbOutSize != 0) +        { +            *os->pbOutPos++ = (unsigned char)os->dwBitBuff; +            os->cbOutSize--; +        } +  +        os->dwBitBuff >>= 8; +        os->nBits      -= 8; +    } +  +    for(; nInLength != 0; nInLength--) +    { +        unsigned char bOneByte = *pbInBuffer++; +  +        if((pItem1 = items306C[bOneByte]) == NULL) +        { +            pItem2    = items306C[0x101];  // ecx +            pItem3    = pItem2->parent;    // eax +            dwBitBuff = 0; +            nBits     = 0; +  +            for(; pItem3 != NULL; pItem3 = pItem3->parent) +            { +                nBit      = (pItem3->child != pItem2) ? 1 : 0; +                dwBitBuff = (dwBitBuff << 1) | nBit; +                nBits++; +                pItem2  = pItem3; +            } +            os->PutBits(dwBitBuff, nBits); +  +            // Store the loaded byte into output stream +            os->dwBitBuff |= (bOneByte << os->nBits); +            os->nBits     += 8; +  +            // Flush the whole byte(s) +            while(os->nBits >= 8) +            { +                if(os->cbOutSize != 0) +                { +                    *os->pbOutPos++ = (unsigned char)os->dwBitBuff; +                    os->cbOutSize--; +                } +                os->dwBitBuff >>= 8; +                os->nBits -= 8; +            } +  +            pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; +            pItem2 = Call1500E740(1); +            pItem2->dcmpByte  = pItem1->dcmpByte; +            pItem2->byteValue = pItem1->byteValue; +            pItem2->parent    = pItem1; +            items306C[pItem2->dcmpByte] = pItem2; +  +            pItem2 = Call1500E740(1); +            pItem2->dcmpByte  = bOneByte; +            pItem2->byteValue = 0; +            pItem2->parent    = pItem1; +            items306C[pItem2->dcmpByte] = pItem2; +            pItem1->child = pItem2; +  +            Call1500E820(pItem2); +  +            if(bIsCmp0 != 0) +            { +                Call1500E820(items306C[bOneByte]); +                continue; +            } +  +            for(pItem1 = items306C[bOneByte]; pItem1 != NULL; pItem1 = pItem1->parent) +            { +                pItem1->byteValue++; +                pItem2 = pItem1; +  +                for(;;) +                { +                    pItem3 = pItem2->prev; +                    if(PTR_INVALID_OR_NULL(pItem3)) +                    { +                        pItem3 = NULL; +                        break; +                    } +                    if(pItem3->byteValue >= pItem1->byteValue) +                        break; +                    pItem2 = pItem3; +                } +  +                if(pItem2 != pItem1) +                { +                    InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); +                    InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); +  +                    pItem3 = pItem2->parent->child; +                    if(pItem1->parent->child == pItem1) +                        pItem1->parent->child = pItem2; +  +                    if(pItem3 == pItem2) +                        pItem2->parent->child = pItem1; +  +                    pTemp = pItem1->parent; +                    pItem1->parent = pItem2->parent; +                    pItem2->parent = pTemp; +                    offs0004++; +                } +            } +        } +// 1500EB62 +        else +        { +            dwBitBuff = 0; +            nBits = 0; +            for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) +            { +                nBit      = (pItem2->child != pItem1) ? 1 : 0; +                dwBitBuff = (dwBitBuff << 1) | nBit; +                nBits++; +                pItem1    = pItem2; +            } +            os->PutBits(dwBitBuff, nBits); +        } +  +// 1500EB98 +        if(bIsCmp0 != 0) +            Call1500E820(items306C[bOneByte]);  // 1500EB9D +// 1500EBAF +    } // for(; nInLength != 0; nInLength--) +  +// 1500EBB8 +    pItem1 = items306C[0x100]; +    dwBitBuff = 0; +    nBits = 0; +    for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) +    { +        nBit      = (pItem2->child != pItem1) ? 1 : 0; +        dwBitBuff = (dwBitBuff << 1) | nBit; +        nBits++; +        pItem1    = pItem2; +    } +  +// 1500EBE6 +    os->PutBits(dwBitBuff, nBits); +  +// 1500EBEF +    // Flush the remaining bits +    while(os->nBits != 0) +    { +        if(os->cbOutSize != 0) +        { +            *os->pbOutPos++ = (unsigned char)os->dwBitBuff; +            os->cbOutSize--; +        } +        os->dwBitBuff >>= 8; +        os->nBits -= ((os->nBits > 8) ? 8 : os->nBits); +    } +  +    return (unsigned int)(os->pbOutPos - os->pbOutBuffer); +} +  +// Decompression using Huffman tree (1500E450) +unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is) +{ +    TQDecompress  * qd; +    THTreeItem    * pItem1; +    THTreeItem    * pItem2; +    unsigned char * pbOutPos = pbOutBuffer; +    unsigned long nBitCount; +    unsigned int nDcmpByte = 0; +    unsigned int n8Bits;                // 8 bits loaded from input stream +    unsigned int n7Bits;                // 7 bits loaded from input stream +    bool bHasQdEntry; +    +    // Test the output length. Must not be NULL. +    if(dwOutLength == 0) +        return 0; +  +    // Get the compression type from the input stream +    n8Bits = is->Get8Bits(); +  +    // Build the Huffman tree +    BuildTree(n8Bits);     +    bIsCmp0 = (n8Bits == 0) ? 1 : 0; +  +    for(;;) +    { +        // Security check: If we are at the end of the input buffer, +        // it means that the data is corrupt +        if(is->BitCount == 0 && is->pbInBuffer >= is->pbInBufferEnd) +            return 0; + +        // Get 7 bits from input stream +        n7Bits = is->Get7Bits(); +  +        // Try to use quick decompression. Check TQDecompress array for corresponding item. +        // If found, ise the result byte instead. +        qd = &qd3474[n7Bits]; +  +        // If there is a quick-pass possible (ebx) +        bHasQdEntry = (qd->offs00 >= offs0004) ? true : false; +  +        // If we can use quick decompress, use it. +        if(bHasQdEntry) +        { +            if(qd->nBits > 7) +            { +                is->SkipBits(7); +                pItem1 = qd->pItem; +                goto _1500E549; +            } +            is->SkipBits(qd->nBits); +            nDcmpByte = qd->dcmpByte; +        } +        else +        { +            pItem1 = pFirst->next->prev; +            if(PTR_INVALID_OR_NULL(pItem1)) +                pItem1 = NULL; +_1500E549:            +            nBitCount = 0; +            pItem2 = NULL; +  +            do +            { +                if(pItem1 == NULL) +                    return 0; + +                pItem1 = pItem1->child;     // Move down by one level +                if(is->GetBit())            // If current bit is set, move to previous +                    pItem1 = pItem1->prev; +  +                if(++nBitCount == 7)        // If we are at 7th bit, save current HTree item. +                    pItem2 = pItem1; +            } +            while(pItem1->child != NULL);   // Walk until tree has no deeper level +  +            if(bHasQdEntry == false) +            { +                if(nBitCount > 7) +                { +                    qd->offs00 = offs0004; +                    qd->nBits  = nBitCount; +                    qd->pItem  = pItem2; +                } +                else +                { +                    unsigned long nIndex = n7Bits & (0xFFFFFFFF >> (32 - nBitCount)); +                    unsigned long nAdd   = (1 << nBitCount); +                    +                    for(qd = &qd3474[nIndex]; nIndex <= 0x7F; nIndex += nAdd, qd += nAdd) +                    { +                        qd->offs00   = offs0004; +                        qd->nBits    = nBitCount; +                        qd->dcmpByte = pItem1->dcmpByte; +                    } +                } +            } +            nDcmpByte = pItem1->dcmpByte; +        } +  +        if(nDcmpByte == 0x101)          // Huffman tree needs to be modified +        { +            n8Bits = is->Get8Bits(); +            pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; +  +            pItem2 = Call1500E740(1); +            pItem2->parent    = pItem1; +            pItem2->dcmpByte  = pItem1->dcmpByte; +            pItem2->byteValue = pItem1->byteValue; +            items306C[pItem2->dcmpByte] = pItem2; +  +            pItem2 = Call1500E740(1); +            pItem2->parent    = pItem1; +            pItem2->dcmpByte  = n8Bits; +            pItem2->byteValue = 0; +            items306C[pItem2->dcmpByte] = pItem2; +  +            pItem1->child = pItem2; +            Call1500E820(pItem2); +            if(bIsCmp0 == 0) +                Call1500E820(items306C[n8Bits]); +  +            nDcmpByte = n8Bits; +        } +  +        if(nDcmpByte == 0x100) +            break; +  +        *pbOutPos++ = (unsigned char)nDcmpByte; +        if(--dwOutLength == 0) +            break; +  +        if(bIsCmp0) +            Call1500E820(items306C[nDcmpByte]); +    } +  +    return (unsigned int)(pbOutPos - pbOutBuffer); +} + + +// Table for (de)compression. Every compression type has 258 entries +unsigned char THuffmannTree::Table1502A630[] = +{ +    // Data for compression type 0x00 +    0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, +    0x00, 0x00, +    +    // Data for compression type 0x01 +    0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, +    0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, +    0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, +    0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, +    0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, +    0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, +    0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03, +    0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, +    0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, +    0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, +    0x00, 0x00, +    +    // Data for compression type 0x02 +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04, +    0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01, +    0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, +    0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, +    0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A, +    0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, +    +    // Data for compression type 0x03 +    0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, +    0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, +    0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, +    0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, +    0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03, +    0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01, +    0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, +    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, +    0x00, 0x00, +    +    // Data for compression type 0x04 +    0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, +    +    // Data for compression type 0x05 +    0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, +    0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, +    0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D, +    0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, +    +    // Data for compression type 0x06 +    0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, +    +    // Data for compression type 0x07 +    0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, +    +    // Data for compression type 0x08 +    0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, +    0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11, +    0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +    0x00, 0x00 +}; diff --git a/src/huffman/huff_old.h b/src/huffman/huff_old.h new file mode 100644 index 0000000..83e9b2c --- /dev/null +++ b/src/huffman/huff_old.h @@ -0,0 +1,142 @@ +/*****************************************************************************/ +/* huffman.h                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description :                                                             */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of huffman.h                       */ +/* 03.05.03  2.00  Lad  Added compression                                    */ +/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */ +/*****************************************************************************/ +  +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#include "../StormPort.h" +  +//----------------------------------------------------------------------------- +// Defines +  +#define INSERT_ITEM    1                    +#define SWITCH_ITEMS   2                    // Switch the item1 and item2 +  +#define PTR_NOT(ptr)  (THTreeItem *)(~(DWORD_PTR)(ptr)) +#define PTR_PTR(ptr)  ((THTreeItem *)(ptr)) +#define PTR_INT(ptr)  (INT_PTR)(ptr) +  +#ifndef NULL +#define NULL 0 +#endif +  +//----------------------------------------------------------------------------- +// Structures and classes +  +// Input stream for Huffmann decompression +class TInputStream +{ +    public: +  +    unsigned long GetBit(); +    unsigned long Get7Bits(); +    unsigned long Get8Bits(); +    void SkipBits(unsigned int BitCount); +  +    unsigned char * pbInBuffer;         // Input data +    unsigned char * pbInBufferEnd;      // End of the input buffer +    unsigned long   BitBuffer;          // Input bit buffer +    unsigned int    BitCount;           // Number of bits remaining in 'dwBitBuff' +}; +  +// Output stream for Huffmann compression +class TOutputStream +{ +    public: +  +    void PutBits(unsigned long dwBuff, unsigned int nPutBits); +  +    unsigned char * pbOutBuffer;        // 00 : Output buffer +    unsigned long   cbOutSize;          // 04 : Size of output buffer +    unsigned char * pbOutPos;           // 08 : Current output position +    unsigned long   dwBitBuff;          // 0C : Bit buffer +    unsigned long   nBits;              // 10 : Number of bits in the bit buffer +}; +  +// Huffmann tree item (?) +struct THTreeItem +{ +    THTreeItem * Call1501DB70(THTreeItem * pLast); +    THTreeItem * GetPrevItem(LONG_PTR value); +    void         ClearItemLinks(); +    void         RemoveItem(); +  +    THTreeItem  * next;                 // 00 - Pointer to next THTreeItem +    THTreeItem  * prev;                 // 04 - Pointer to prev THTreeItem (< 0 if none) +    unsigned long dcmpByte;             // 08 - Index of this item in item pointer array, decompressed byte value +    unsigned long byteValue;            // 0C - Some byte value +    THTreeItem  * parent;               // 10 - Pointer to parent THTreeItem (NULL if none) +    THTreeItem  * child;                // 14 - Pointer to child  THTreeItem +    int           addressMultiplier;    // -1 if object on negative address (>0x80000000), +1 if positive +}; +  +// Structure used for quick decompress. The 'bitCount' contains number of bits +// and byte value contains result decompressed byte value. +// After each walk through Huffman tree are filled all entries which are +// multiplies of number of bits loaded from input stream. These entries +// contain number of bits and result value. At the next 7 bits is tested this +// structure first. If corresponding entry found, decompression routine will +// not walk through Huffman tree and directly stores output byte to output stream. +struct TQDecompress +{ +    unsigned long offs00;               // 00 - 1 if resolved +    unsigned long nBits;                // 04 - Bit count +    union +    { +        unsigned long dcmpByte;         // 08 - Byte value for decompress (if bitCount <= 7) +        THTreeItem  * pItem;            // 08 - THTreeItem (if number of bits is greater than 7 +    }; +}; +  +// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert +// for the decompression, I do not know actually if the class is really a Hufmann +// tree. If someone knows the decompression details, please let me know +class THuffmannTree +{ +    public: +     +    THuffmannTree(); +    void  InitTree(bool bCompression); +    void  BuildTree(unsigned int nCmpType); +//  void  ModifyTree(unsigned long dwIndex); +//  void  UninitTree(); +  +//  void  Call15007010(Bit32 dwInLength, THTreeItem * item); +    THTreeItem * Call1500E740(unsigned int nValue); +    void         Call1500E820(THTreeItem * pItem); +    unsigned int DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType); +    unsigned int DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is); +  +    unsigned long bIsCmp0;              // 0000 - 1 if compression type 0 +    unsigned long offs0004;             // 0004 - Some flag +    THTreeItem    items0008[0x203];     // 0008 - HTree items +  +    //- Sometimes used as HTree item ----------- +    THTreeItem  * pItem3050;            // 3050 - Always NULL (?) +    THTreeItem  * pItem3054;            // 3054 - Pointer to Huffman tree item +    THTreeItem  * pItem3058;            // 3058 - Pointer to Huffman tree item (< 0 if invalid) +  +    //- Sometimes used as HTree item ----------- +    THTreeItem  * pItem305C;            // 305C - Usually NULL +    THTreeItem  * pFirst;               // 3060 - Pointer to top (first) Huffman tree item +    THTreeItem  * pLast;                // 3064 - Pointer to bottom (last) Huffman tree item (< 0 if invalid) +    unsigned long nItems;               // 3068 - Number of used HTree items +  +    //------------------------------------------- +    THTreeItem * items306C[0x102];      // 306C - THTreeItem pointer array +    TQDecompress qd3474[0x80];          // 3474 - Array for quick decompression +    int          addressMultiplier;     // -1 if object on negative address (>0x80000000), +1 if positive +  +    static unsigned char Table1502A630[];// Some table +}; +  +#endif // __HUFFMAN_H__ diff --git a/src/jenkins/lookup.h b/src/jenkins/lookup.h new file mode 100644 index 0000000..54ccc97 --- /dev/null +++ b/src/jenkins/lookup.h @@ -0,0 +1,24 @@ +#ifndef __LOOKUP3_H__ +#define __LOOKUP3_H__ + +#ifdef WIN32 +typedef unsigned char  uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int   uint32_t; +#else +#include <stdint.h>     /* defines uint32_t etc */ +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +uint32_t hashlittle(const void *key, size_t length, uint32_t initval); +void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb); + +#ifdef __cplusplus +} +#endif + +#endif  // __LOOKUP3_H__ diff --git a/src/jenkins/lookup3.c b/src/jenkins/lookup3.c new file mode 100644 index 0000000..6af56b4 --- /dev/null +++ b/src/jenkins/lookup3.c @@ -0,0 +1,1003 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()  +are externally useful functions.  Routines to test the hash are included  +if SELF_TEST is defined.  You can use this free for any purpose.  It's in +the public domain.  It has no warranty. + +You probably want to use hashlittle().  hashlittle() and hashbig() +hash byte arrays.  hashlittle() is is faster than hashbig() on +little-endian machines.  Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one.   +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do +  a = i1;  b = i2;  c = i3; +  mix(a,b,c); +  a += i4; b += i5; c += i6; +  mix(a,b,c); +  a += i7; +  final(a,b,c); +then use c as the hash value.  If you have a variable length array of +4-byte integers to hash, use hashword().  If you have a byte array (like +a character string), use hashlittle().  If you have several byte arrays, or +a mix of things, see the comments above hashlittle().   + +Why is this so big?  I read 12 bytes at a time into 3 4-byte integers,  +then mix those integers.  This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ +//#define SELF_TEST 1 + +#include <stdio.h>      /* defines printf for tests */ +#include <time.h>       /* defines time_t for timings in the test */ + +#ifdef linux +#include <sys/param.h>  /* attempt to define endianness */ +#include <endian.h>    /* attempt to define endianness */ +#endif + +#include "lookup.h" + +/* + * My best guess at if you are big-endian or little-endian.  This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ +     __BYTE_ORDER == __LITTLE_ENDIAN) || \ +    (defined(i386) || defined(__i386__) || defined(__i486__) || \ +     defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ +       __BYTE_ORDER == __BIG_ENDIAN) || \ +      (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination +  of top bits of (a,b,c), or in any combination of bottom bits of +  (a,b,c). +* "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed +  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as +  is commonly produced by subtraction) look like a single 1-bit +  difference. +* the base values were pseudorandom, all zero but one bit set, or  +  all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are +    4  6  8 16 19  4 +    9 15  3 18 27 15 +   14  9  3  7 17  3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta.  I +used http://burtleburtle.net/bob/hash/avalanche.html to choose  +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche.  There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a.  The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism.  Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism.  I did what I could.  Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ +  a -= c;  a ^= rot(c, 4);  c += b; \ +  b -= a;  b ^= rot(a, 6);  a += c; \ +  c -= b;  c ^= rot(b, 8);  b += a; \ +  a -= c;  a ^= rot(c,16);  c += b; \ +  b -= a;  b ^= rot(a,19);  a += c; \ +  c -= b;  c ^= rot(b, 4);  b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different.  This was tested for +* pairs that differed by one bit, by two bits, in any combination +  of top bits of (a,b,c), or in any combination of bottom bits of +  (a,b,c). +* "differ" is defined as +, -, ^, or ~^.  For + and -, I transformed +  the output delta to a Gray code (a^(a>>1)) so a string of 1's (as +  is commonly produced by subtraction) look like a single 1-bit +  difference. +* the base values were pseudorandom, all zero but one bit set, or  +  all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: +  4  8 15 26 3 22 24 + 10  8 15 26 3 22 24 + 11  8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ +  c ^= b; c -= rot(b,14); \ +  a ^= c; a -= rot(c,11); \ +  b ^= a; b -= rot(a,25); \ +  c ^= b; c -= rot(b,16); \ +  a ^= c; a -= rot(c,4);  \ +  b ^= a; b -= rot(a,14); \ +  c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines.  To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes.  hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t hashword( +const uint32_t *k,                   /* the key, an array of uint32_t values */ +size_t          length,               /* the length of the key, in uint32_ts */ +uint32_t        initval)         /* the previous hash, or an arbitrary value */ +{ +  uint32_t a,b,c; + +  /* Set up the internal state */ +  a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + +  /*------------------------------------------------- handle most of the key */ +  while (length > 3) +  { +    a += k[0]; +    b += k[1]; +    c += k[2]; +    mix(a,b,c); +    length -= 3; +    k += 3; +  } + +  /*------------------------------------------- handle the last 3 uint32_t's */ +  switch(length)                     /* all the case statements fall through */ +  {  +  case 3 : c+=k[2]; +  case 2 : b+=k[1]; +  case 1 : a+=k[0]; +    final(a,b,c); +  case 0:     /* case 0: nothing left to add */ +    break; +  } +  /*------------------------------------------------------ report the result */ +  return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values.  pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds.  If you pass in (*pb)==0, the output  +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void hashword2 ( +const uint32_t *k,                   /* the key, an array of uint32_t values */ +size_t          length,               /* the length of the key, in uint32_ts */ +uint32_t       *pc,                      /* IN: seed OUT: primary hash value */ +uint32_t       *pb)               /* IN: more seed OUT: secondary hash value */ +{ +  uint32_t a,b,c; + +  /* Set up the internal state */ +  a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; +  c += *pb; + +  /*------------------------------------------------- handle most of the key */ +  while (length > 3) +  { +    a += k[0]; +    b += k[1]; +    c += k[2]; +    mix(a,b,c); +    length -= 3; +    k += 3; +  } + +  /*------------------------------------------- handle the last 3 uint32_t's */ +  switch(length)                     /* all the case statements fall through */ +  {  +  case 3 : c+=k[2]; +  case 2 : b+=k[1]; +  case 1 : a+=k[0]; +    final(a,b,c); +  case 0:     /* case 0: nothing left to add */ +    break; +  } +  /*------------------------------------------------------ report the result */ +  *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value +  k       : the key (the unaligned variable-length array of bytes) +  length  : the length of the key, counting by bytes +  initval : can be any 4-byte value +Returns a 32-bit value.  Every bit of the key affects every bit of +the return value.  Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2.  There is no need to do +mod a prime (mod is sooo slow!).  If you need less than 32 bits, +use a bitmask.  For example, if you need only 10 bits, do +  h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: +  for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006.  bob_jenkins@burtleburtle.net.  You may use this +code any way you wish, private, educational, or commercial.  It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable.  Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +uint32_t hashlittle( const void *key, size_t length, uint32_t initval) +{ +  uint32_t a,b,c;                                          /* internal state */ +  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */ + +  /* Set up the internal state */ +  a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + +  u.ptr = key; +  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { +    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */ +    const uint8_t  *k8; + +    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += k[0]; +      b += k[1]; +      c += k[2]; +      mix(a,b,c); +      length -= 12; +      k += 3; +    } + +    /*----------------------------- handle the last (probably partial) block */ +    /*  +     * "k[2]&0xffffff" actually reads beyond the end of the string, but +     * then masks off the part it's not allowed to read.  Because the +     * string is aligned, the masked-off tail is in the same word as the +     * rest of the string.  Every machine with memory protection I've seen +     * does it on word boundaries, so is OK with this.  But VALGRIND will +     * still catch it and complain.  The masking trick does make the hash +     * noticably faster for short strings (like English words). +     */ +#ifndef VALGRIND + +    switch(length) +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; +    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; +    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=k[1]&0xffffff; a+=k[0]; break; +    case 6 : b+=k[1]&0xffff; a+=k[0]; break; +    case 5 : b+=k[1]&0xff; a+=k[0]; break; +    case 4 : a+=k[0]; break; +    case 3 : a+=k[0]&0xffffff; break; +    case 2 : a+=k[0]&0xffff; break; +    case 1 : a+=k[0]&0xff; break; +    case 0 : return c;              /* zero length strings require no mixing */ +    } + +#else /* make valgrind happy */ + +    k8 = (const uint8_t *)k; +    switch(length) +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */ +    case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */ +    case 9 : c+=k8[8];                   /* fall through */ +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */ +    case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */ +    case 5 : b+=k8[4];                   /* fall through */ +    case 4 : a+=k[0]; break; +    case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */ +    case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */ +    case 1 : a+=k8[0]; break; +    case 0 : return c; +    } + +#endif /* !valgrind */ + +  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { +    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */ +    const uint8_t  *k8; + +    /*--------------- all but last block: aligned reads and different mixing */ +    while (length > 12) +    { +      a += k[0] + (((uint32_t)k[1])<<16); +      b += k[2] + (((uint32_t)k[3])<<16); +      c += k[4] + (((uint32_t)k[5])<<16); +      mix(a,b,c); +      length -= 12; +      k += 6; +    } + +    /*----------------------------- handle the last (probably partial) block */ +    k8 = (const uint8_t *)k; +    switch(length) +    { +    case 12: c+=k[4]+(((uint32_t)k[5])<<16); +             b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */ +    case 10: c+=k[4]; +             b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 9 : c+=k8[8];                      /* fall through */ +    case 8 : b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */ +    case 6 : b+=k[2]; +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 5 : b+=k8[4];                      /* fall through */ +    case 4 : a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */ +    case 2 : a+=k[0]; +             break; +    case 1 : a+=k8[0]; +             break; +    case 0 : return c;                     /* zero length requires no mixing */ +    } + +  } else {                        /* need to read the key one byte at a time */ +    const uint8_t *k = (const uint8_t *)key; + +    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += k[0]; +      a += ((uint32_t)k[1])<<8; +      a += ((uint32_t)k[2])<<16; +      a += ((uint32_t)k[3])<<24; +      b += k[4]; +      b += ((uint32_t)k[5])<<8; +      b += ((uint32_t)k[6])<<16; +      b += ((uint32_t)k[7])<<24; +      c += k[8]; +      c += ((uint32_t)k[9])<<8; +      c += ((uint32_t)k[10])<<16; +      c += ((uint32_t)k[11])<<24; +      mix(a,b,c); +      length -= 12; +      k += 12; +    } + +    /*-------------------------------- last block: affect all 32 bits of (c) */ +    switch(length)                   /* all the case statements fall through */ +    { +    case 12: c+=((uint32_t)k[11])<<24; +    case 11: c+=((uint32_t)k[10])<<16; +    case 10: c+=((uint32_t)k[9])<<8; +    case 9 : c+=k[8]; +    case 8 : b+=((uint32_t)k[7])<<24; +    case 7 : b+=((uint32_t)k[6])<<16; +    case 6 : b+=((uint32_t)k[5])<<8; +    case 5 : b+=k[4]; +    case 4 : a+=((uint32_t)k[3])<<24; +    case 3 : a+=((uint32_t)k[2])<<16; +    case 2 : a+=((uint32_t)k[1])<<8; +    case 1 : a+=k[0]; +             break; +    case 0 : return c; +    } +  } + +  final(a,b,c); +  return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one.  This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key.  *pc is better mixed than *pb, so use *pc first.  If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void hashlittle2(  +  const void *key,       /* the key to hash */ +  size_t      length,    /* length of the key */ +  uint32_t   *pc,        /* IN: primary initval, OUT: primary hash */ +  uint32_t   *pb)        /* IN: secondary initval, OUT: secondary hash */ +{ +  uint32_t a,b,c;                                          /* internal state */ +  union { const void *ptr; size_t i; } u;     /* needed for Mac Powerbook G4 */ + +  /* Set up the internal state */ +  a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; +  c += *pb; + +  u.ptr = key; +  if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { +    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */ +    const uint8_t  *k8; + +    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += k[0]; +      b += k[1]; +      c += k[2]; +      mix(a,b,c); +      length -= 12; +      k += 3; +    } + +    /*----------------------------- handle the last (probably partial) block */ +    /*  +     * "k[2]&0xffffff" actually reads beyond the end of the string, but +     * then masks off the part it's not allowed to read.  Because the +     * string is aligned, the masked-off tail is in the same word as the +     * rest of the string.  Every machine with memory protection I've seen +     * does it on word boundaries, so is OK with this.  But VALGRIND will +     * still catch it and complain.  The masking trick does make the hash +     * noticably faster for short strings (like English words). +     */ +#ifndef VALGRIND + +    switch(length) +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; +    case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; +    case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=k[1]&0xffffff; a+=k[0]; break; +    case 6 : b+=k[1]&0xffff; a+=k[0]; break; +    case 5 : b+=k[1]&0xff; a+=k[0]; break; +    case 4 : a+=k[0]; break; +    case 3 : a+=k[0]&0xffffff; break; +    case 2 : a+=k[0]&0xffff; break; +    case 1 : a+=k[0]&0xff; break; +    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */ +    } + +#else /* make valgrind happy */ + +    k8 = (const uint8_t *)k; +    switch(length) +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=((uint32_t)k8[10])<<16;  /* fall through */ +    case 10: c+=((uint32_t)k8[9])<<8;    /* fall through */ +    case 9 : c+=k8[8];                   /* fall through */ +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=((uint32_t)k8[6])<<16;   /* fall through */ +    case 6 : b+=((uint32_t)k8[5])<<8;    /* fall through */ +    case 5 : b+=k8[4];                   /* fall through */ +    case 4 : a+=k[0]; break; +    case 3 : a+=((uint32_t)k8[2])<<16;   /* fall through */ +    case 2 : a+=((uint32_t)k8[1])<<8;    /* fall through */ +    case 1 : a+=k8[0]; break; +    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */ +    } + +#endif /* !valgrind */ + +  } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { +    const uint16_t *k = (const uint16_t *)key;         /* read 16-bit chunks */ +    const uint8_t  *k8; + +    /*--------------- all but last block: aligned reads and different mixing */ +    while (length > 12) +    { +      a += k[0] + (((uint32_t)k[1])<<16); +      b += k[2] + (((uint32_t)k[3])<<16); +      c += k[4] + (((uint32_t)k[5])<<16); +      mix(a,b,c); +      length -= 12; +      k += 6; +    } + +    /*----------------------------- handle the last (probably partial) block */ +    k8 = (const uint8_t *)k; +    switch(length) +    { +    case 12: c+=k[4]+(((uint32_t)k[5])<<16); +             b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 11: c+=((uint32_t)k8[10])<<16;     /* fall through */ +    case 10: c+=k[4]; +             b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 9 : c+=k8[8];                      /* fall through */ +    case 8 : b+=k[2]+(((uint32_t)k[3])<<16); +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 7 : b+=((uint32_t)k8[6])<<16;      /* fall through */ +    case 6 : b+=k[2]; +             a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 5 : b+=k8[4];                      /* fall through */ +    case 4 : a+=k[0]+(((uint32_t)k[1])<<16); +             break; +    case 3 : a+=((uint32_t)k8[2])<<16;      /* fall through */ +    case 2 : a+=k[0]; +             break; +    case 1 : a+=k8[0]; +             break; +    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */ +    } + +  } else {                        /* need to read the key one byte at a time */ +    const uint8_t *k = (const uint8_t *)key; + +    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += k[0]; +      a += ((uint32_t)k[1])<<8; +      a += ((uint32_t)k[2])<<16; +      a += ((uint32_t)k[3])<<24; +      b += k[4]; +      b += ((uint32_t)k[5])<<8; +      b += ((uint32_t)k[6])<<16; +      b += ((uint32_t)k[7])<<24; +      c += k[8]; +      c += ((uint32_t)k[9])<<8; +      c += ((uint32_t)k[10])<<16; +      c += ((uint32_t)k[11])<<24; +      mix(a,b,c); +      length -= 12; +      k += 12; +    } + +    /*-------------------------------- last block: affect all 32 bits of (c) */ +    switch(length)                   /* all the case statements fall through */ +    { +    case 12: c+=((uint32_t)k[11])<<24; +    case 11: c+=((uint32_t)k[10])<<16; +    case 10: c+=((uint32_t)k[9])<<8; +    case 9 : c+=k[8]; +    case 8 : b+=((uint32_t)k[7])<<24; +    case 7 : b+=((uint32_t)k[6])<<16; +    case 6 : b+=((uint32_t)k[5])<<8; +    case 5 : b+=k[4]; +    case 4 : a+=((uint32_t)k[3])<<24; +    case 3 : a+=((uint32_t)k[2])<<16; +    case 2 : a+=((uint32_t)k[1])<<8; +    case 1 : a+=k[0]; +             break; +    case 0 : *pc=c; *pb=b; return;  /* zero length strings require no mixing */ +    } +  } + +  final(a,b,c); +  *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines.  It is different + * from hashlittle() on all machines.  hashbig() takes advantage of + * big-endian byte ordering.  + */ +uint32_t hashbig( const void *key, size_t length, uint32_t initval) +{ +  uint32_t a,b,c; +  union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + +  /* Set up the internal state */ +  a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + +  u.ptr = key; +  if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { +    const uint32_t *k = (const uint32_t *)key;         /* read 32-bit chunks */ +    const uint8_t  *k8; + +    /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += k[0]; +      b += k[1]; +      c += k[2]; +      mix(a,b,c); +      length -= 12; +      k += 3; +    } + +    /*----------------------------- handle the last (probably partial) block */ +    /*  +     * "k[2]<<8" actually reads beyond the end of the string, but +     * then shifts out the part it's not allowed to read.  Because the +     * string is aligned, the illegal read is in the same word as the +     * rest of the string.  Every machine with memory protection I've seen +     * does it on word boundaries, so is OK with this.  But VALGRIND will +     * still catch it and complain.  The masking trick does make the hash +     * noticably faster for short strings (like English words). +     */ +#ifndef VALGRIND + +    switch(length) +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; +    case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; +    case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; +    case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; +    case 5 : b+=k[1]&0xff000000; a+=k[0]; break; +    case 4 : a+=k[0]; break; +    case 3 : a+=k[0]&0xffffff00; break; +    case 2 : a+=k[0]&0xffff0000; break; +    case 1 : a+=k[0]&0xff000000; break; +    case 0 : return c;              /* zero length strings require no mixing */ +    } + +#else  /* make valgrind happy */ + +    k8 = (const uint8_t *)k; +    switch(length)                   /* all the case statements fall through */ +    { +    case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; +    case 11: c+=((uint32_t)k8[10])<<8;  /* fall through */ +    case 10: c+=((uint32_t)k8[9])<<16;  /* fall through */ +    case 9 : c+=((uint32_t)k8[8])<<24;  /* fall through */ +    case 8 : b+=k[1]; a+=k[0]; break; +    case 7 : b+=((uint32_t)k8[6])<<8;   /* fall through */ +    case 6 : b+=((uint32_t)k8[5])<<16;  /* fall through */ +    case 5 : b+=((uint32_t)k8[4])<<24;  /* fall through */ +    case 4 : a+=k[0]; break; +    case 3 : a+=((uint32_t)k8[2])<<8;   /* fall through */ +    case 2 : a+=((uint32_t)k8[1])<<16;  /* fall through */ +    case 1 : a+=((uint32_t)k8[0])<<24; break; +    case 0 : return c; +    } + +#endif /* !VALGRIND */ + +  } else {                        /* need to read the key one byte at a time */ +    const uint8_t *k = (const uint8_t *)key; + +    /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ +    while (length > 12) +    { +      a += ((uint32_t)k[0])<<24; +      a += ((uint32_t)k[1])<<16; +      a += ((uint32_t)k[2])<<8; +      a += ((uint32_t)k[3]); +      b += ((uint32_t)k[4])<<24; +      b += ((uint32_t)k[5])<<16; +      b += ((uint32_t)k[6])<<8; +      b += ((uint32_t)k[7]); +      c += ((uint32_t)k[8])<<24; +      c += ((uint32_t)k[9])<<16; +      c += ((uint32_t)k[10])<<8; +      c += ((uint32_t)k[11]); +      mix(a,b,c); +      length -= 12; +      k += 12; +    } + +    /*-------------------------------- last block: affect all 32 bits of (c) */ +    switch(length)                   /* all the case statements fall through */ +    { +    case 12: c+=k[11]; +    case 11: c+=((uint32_t)k[10])<<8; +    case 10: c+=((uint32_t)k[9])<<16; +    case 9 : c+=((uint32_t)k[8])<<24; +    case 8 : b+=k[7]; +    case 7 : b+=((uint32_t)k[6])<<8; +    case 6 : b+=((uint32_t)k[5])<<16; +    case 5 : b+=((uint32_t)k[4])<<24; +    case 4 : a+=k[3]; +    case 3 : a+=((uint32_t)k[2])<<8; +    case 2 : a+=((uint32_t)k[1])<<16; +    case 1 : a+=((uint32_t)k[0])<<24; +             break; +    case 0 : return c; +    } +  } + +  final(a,b,c); +  return c; +} + + +#ifdef SELF_TEST + +/* used for timings */ +void driver1() +{ +  uint8_t buf[256]; +  uint32_t i; +  uint32_t h=0; +  time_t a,z; + +  time(&a); +  for (i=0; i<256; ++i) buf[i] = 'x'; +  for (i=0; i<1; ++i)  +  { +    h = hashlittle(&buf[0],1,h); +  } +  time(&z); +  if (z-a > 0) printf("time %d %.8x\n", z-a, h); +} + +/* check that every input bit changes every output bit half the time */ +#define HASHSTATE 1 +#define HASHLEN   1 +#define MAXPAIR 60 +#define MAXLEN  70 +void driver2() +{ +  uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1]; +  uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z; +  uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE]; +  uint32_t x[HASHSTATE],y[HASHSTATE]; +  uint32_t hlen; + +  printf("No more than %d trials should ever be needed \n",MAXPAIR/2); +  for (hlen=0; hlen < MAXLEN; ++hlen) +  { +    z=0; +    for (i=0; i<hlen; ++i)  /*----------------------- for each input byte, */ +    { +      for (j=0; j<8; ++j)   /*------------------------ for each input bit, */ +      { +	for (m=1; m<8; ++m) /*------------ for serveral possible initvals, */ +	{ +	  for (l=0; l<HASHSTATE; ++l) +	    e[l]=f[l]=g[l]=h[l]=x[l]=y[l]=~((uint32_t)0); + +      	  /*---- check that every output bit is affected by that input bit */ +	  for (k=0; k<MAXPAIR; k+=2) +	  {  +	    uint32_t finished=1; +	    /* keys have one bit different */ +	    for (l=0; l<hlen+1; ++l) {a[l] = b[l] = (uint8_t)0;} +	    /* have a and b be two keys differing in only one bit */ +	    a[i] ^= (k<<j); +	    a[i] ^= (k>>(8-j)); +	     c[0] = hashlittle(a, hlen, m); +	    b[i] ^= ((k+1)<<j); +	    b[i] ^= ((k+1)>>(8-j)); +	     d[0] = hashlittle(b, hlen, m); +	    /* check every bit is 1, 0, set, and not set at least once */ +	    for (l=0; l<HASHSTATE; ++l) +	    { +	      e[l] &= (c[l]^d[l]); +	      f[l] &= ~(c[l]^d[l]); +	      g[l] &= c[l]; +	      h[l] &= ~c[l]; +	      x[l] &= d[l]; +	      y[l] &= ~d[l]; +	      if (e[l]|f[l]|g[l]|h[l]|x[l]|y[l]) finished=0; +	    } +	    if (finished) break; +	  } +	  if (k>z) z=k; +	  if (k==MAXPAIR)  +	  { +	     printf("Some bit didn't change: "); +	     printf("%.8x %.8x %.8x %.8x %.8x %.8x  ", +	            e[0],f[0],g[0],h[0],x[0],y[0]); +	     printf("i %d j %d m %d len %d\n", i, j, m, hlen); +	  } +	  if (z==MAXPAIR) goto done; +	} +      } +    } +   done: +    if (z < MAXPAIR) +    { +      printf("Mix success  %2d bytes  %2d initvals  ",i,m); +      printf("required  %d  trials\n", z/2); +    } +  } +  printf("\n"); +} + +/* Check for reading beyond the end of the buffer and alignment problems */ +void driver3() +{ +  uint8_t buf[MAXLEN+20], *b; +  uint32_t len; +  uint8_t q[] = "This is the time for all good men to come to the aid of their country..."; +  uint32_t h; +  uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country..."; +  uint32_t i; +  uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country..."; +  uint32_t j; +  uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country..."; +  uint32_t ref,x,y; +  uint8_t *p; + +  printf("Endianness.  These lines should all be the same (for values filled in):\n"); +  printf("%.8x                            %.8x                            %.8x\n", +         hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13), +         hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13), +         hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13)); +  p = q; +  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", +         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), +         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), +         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), +         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), +         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), +         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); +  p = &qq[1]; +  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", +         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), +         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), +         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), +         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), +         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), +         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); +  p = &qqq[2]; +  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", +         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), +         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), +         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), +         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), +         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), +         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); +  p = &qqqq[3]; +  printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", +         hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13), +         hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13), +         hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13), +         hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13), +         hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13), +         hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13)); +  printf("\n"); + +  /* check that hashlittle2 and hashlittle produce the same results */ +  i=47; j=0; +  hashlittle2(q, sizeof(q), &i, &j); +  if (hashlittle(q, sizeof(q), 47) != i) +    printf("hashlittle2 and hashlittle mismatch\n"); + +  /* check that hashword2 and hashword produce the same results */ +  len = 0xdeadbeef; +  i=47, j=0; +  hashword2(&len, 1, &i, &j); +  if (hashword(&len, 1, 47) != i) +    printf("hashword2 and hashword mismatch %x %x\n",  +	   i, hashword(&len, 1, 47)); + +  /* check hashlittle doesn't read before or after the ends of the string */ +  for (h=0, b=buf+1; h<8; ++h, ++b) +  { +    for (i=0; i<MAXLEN; ++i) +    { +      len = i; +      for (j=0; j<i; ++j) *(b+j)=0; + +      /* these should all be equal */ +      ref = hashlittle(b, len, (uint32_t)1); +      *(b+i)=(uint8_t)~0; +      *(b-1)=(uint8_t)~0; +      x = hashlittle(b, len, (uint32_t)1); +      y = hashlittle(b, len, (uint32_t)1); +      if ((ref != x) || (ref != y))  +      { +	printf("alignment error: %.8x %.8x %.8x %d %d\n",ref,x,y, +               h, i); +      } +    } +  } +} + +/* check for problems with nulls */ + void driver4() +{ +  uint8_t buf[1]; +  uint32_t h,i,state[HASHSTATE]; + + +  buf[0] = ~0; +  for (i=0; i<HASHSTATE; ++i) state[i] = 1; +  printf("These should all be different\n"); +  for (i=0, h=0; i<8; ++i) +  { +    h = hashlittle(buf, 0, h); +    printf("%2ld  0-byte strings, hash is  %.8x\n", i, h); +  } +} + +void driver5() +{ +  uint32_t b,c; +  b=0, c=0, hashlittle2("", 0, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* deadbeef deadbeef */ +  b=0xdeadbeef, c=0, hashlittle2("", 0, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* bd5b7dde deadbeef */ +  b=0xdeadbeef, c=0xdeadbeef, hashlittle2("", 0, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* 9c093ccd bd5b7dde */ +  b=0, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* 17770551 ce7226e6 */ +  b=1, c=0, hashlittle2("Four score and seven years ago", 30, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* e3607cae bd371de4 */ +  b=0, c=1, hashlittle2("Four score and seven years ago", 30, &c, &b); +  printf("hash is %.8lx %.8lx\n", c, b);   /* cd628161 6cbea4b3 */ +  c = hashlittle("Four score and seven years ago", 30, 0); +  printf("hash is %.8lx\n", c);   /* 17770551 */ +  c = hashlittle("Four score and seven years ago", 30, 1); +  printf("hash is %.8lx\n", c);   /* cd628161 */ +} + + +int main() +{ +  driver1();   /* test that the key is hashed: used for timings */ +  driver2();   /* test that whole key is hashed thoroughly */ +  driver3();   /* test that nothing but the key is hashed */ +  driver4();   /* test hashing multiple buffers (all buffers are null) */ +  driver5();   /* test the hash against known vectors */ +  return 1; +} + +#endif  /* SELF_TEST */ diff --git a/src/libtomcrypt/src/hashes/hash_memory.c b/src/libtomcrypt/src/hashes/hash_memory.c new file mode 100644 index 0000000..1daf0bf --- /dev/null +++ b/src/libtomcrypt/src/hashes/hash_memory.c @@ -0,0 +1,69 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file hash_memory.c +  Hash memory helper, Tom St Denis +*/ + +/** +  Hash a block of memory and store the digest. +  @param hash   The index of the hash you wish to use +  @param in     The data you wish to hash +  @param inlen  The length of the data to hash (octets) +  @param out    [out] Where to store the digest +  @param outlen [in/out] Max size and resulting size of the digest +  @return CRYPT_OK if successful +*/ +int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) +{ +    hash_state *md; +    int err; + +    LTC_ARGCHK(in     != NULL); +    LTC_ARGCHK(out    != NULL); +    LTC_ARGCHK(outlen != NULL); + +    if ((err = hash_is_valid(hash)) != CRYPT_OK) { +        return err; +    } + +    if (*outlen < hash_descriptor[hash].hashsize) { +       *outlen = hash_descriptor[hash].hashsize; +       return CRYPT_BUFFER_OVERFLOW; +    } + +    md = XMALLOC(sizeof(hash_state)); +    if (md == NULL) { +       return CRYPT_MEM; +    } + +    if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { +       goto LBL_ERR; +    } +    if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) { +       goto LBL_ERR; +    } +    err = hash_descriptor[hash].done(md, out); +    *outlen = hash_descriptor[hash].hashsize; +LBL_ERR: +#ifdef LTC_CLEAN_STACK +    zeromem(md, sizeof(hash_state)); +#endif +    XFREE(md); + +    return err; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ diff --git a/src/libtomcrypt/src/hashes/md5.c b/src/libtomcrypt/src/hashes/md5.c new file mode 100644 index 0000000..4cbd000 --- /dev/null +++ b/src/libtomcrypt/src/hashes/md5.c @@ -0,0 +1,368 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + + +/** +  @file md5.c +  LTC_MD5 hash function by Tom St Denis  +*/ + +#ifdef LTC_MD5 + +const struct ltc_hash_descriptor md5_desc = +{ +    "md5", +    3, +    16, +    64, + +    /* OID */ +   { 1, 2, 840, 113549, 2, 5,  }, +   6, + +    &md5_init, +    &md5_process, +    &md5_done, +    &md5_test, +    NULL +}; + +#define F(x,y,z)  (z ^ (x & (y ^ z))) +#define G(x,y,z)  (y ^ (z & (y ^ x))) +#define H(x,y,z)  (x^y^z) +#define I(x,y,z)  (y^(x|(~z))) + +#ifdef LTC_SMALL_CODE + +#define FF(a,b,c,d,M,s,t) \ +    a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ +    a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ +    a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ +    a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; + +static const unsigned char Worder[64] = { +   0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, +   1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, +   5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, +   0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 +}; + +static const unsigned char Rorder[64] = { +   7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, +   5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, +   4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, +   6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const ulong32 Korder[64] = { +0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, +0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, +0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, +0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, +0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, +0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, +0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, +0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL +}; + +#else + +#define FF(a,b,c,d,M,s,t) \ +    a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define GG(a,b,c,d,M,s,t) \ +    a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define HH(a,b,c,d,M,s,t) \ +    a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; + +#define II(a,b,c,d,M,s,t) \ +    a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; + + +#endif    + +#ifdef LTC_CLEAN_STACK +static int _md5_compress(hash_state *md, unsigned char *buf) +#else +static int  md5_compress(hash_state *md, unsigned char *buf) +#endif +{ +    ulong32 i, W[16], a, b, c, d; +#ifdef LTC_SMALL_CODE +    ulong32 t; +#endif + +    /* copy the state into 512-bits into W[0..15] */ +    for (i = 0; i < 16; i++) { +        LOAD32L(W[i], buf + (4*i)); +    } +  +    /* copy state */ +    a = md->md5.state[0]; +    b = md->md5.state[1]; +    c = md->md5.state[2]; +    d = md->md5.state[3]; + +#ifdef LTC_SMALL_CODE +    for (i = 0; i < 16; ++i) { +        FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); +        t = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 32; ++i) { +        GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); +        t = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 48; ++i) { +        HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); +        t = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 64; ++i) { +        II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); +        t = d; d = c; c = b; b = a; a = t; +    } + +#else +    FF(a,b,c,d,W[0],7,0xd76aa478UL) +    FF(d,a,b,c,W[1],12,0xe8c7b756UL) +    FF(c,d,a,b,W[2],17,0x242070dbUL) +    FF(b,c,d,a,W[3],22,0xc1bdceeeUL) +    FF(a,b,c,d,W[4],7,0xf57c0fafUL) +    FF(d,a,b,c,W[5],12,0x4787c62aUL) +    FF(c,d,a,b,W[6],17,0xa8304613UL) +    FF(b,c,d,a,W[7],22,0xfd469501UL) +    FF(a,b,c,d,W[8],7,0x698098d8UL) +    FF(d,a,b,c,W[9],12,0x8b44f7afUL) +    FF(c,d,a,b,W[10],17,0xffff5bb1UL) +    FF(b,c,d,a,W[11],22,0x895cd7beUL) +    FF(a,b,c,d,W[12],7,0x6b901122UL) +    FF(d,a,b,c,W[13],12,0xfd987193UL) +    FF(c,d,a,b,W[14],17,0xa679438eUL) +    FF(b,c,d,a,W[15],22,0x49b40821UL) +    GG(a,b,c,d,W[1],5,0xf61e2562UL) +    GG(d,a,b,c,W[6],9,0xc040b340UL) +    GG(c,d,a,b,W[11],14,0x265e5a51UL) +    GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) +    GG(a,b,c,d,W[5],5,0xd62f105dUL) +    GG(d,a,b,c,W[10],9,0x02441453UL) +    GG(c,d,a,b,W[15],14,0xd8a1e681UL) +    GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) +    GG(a,b,c,d,W[9],5,0x21e1cde6UL) +    GG(d,a,b,c,W[14],9,0xc33707d6UL) +    GG(c,d,a,b,W[3],14,0xf4d50d87UL) +    GG(b,c,d,a,W[8],20,0x455a14edUL) +    GG(a,b,c,d,W[13],5,0xa9e3e905UL) +    GG(d,a,b,c,W[2],9,0xfcefa3f8UL) +    GG(c,d,a,b,W[7],14,0x676f02d9UL) +    GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) +    HH(a,b,c,d,W[5],4,0xfffa3942UL) +    HH(d,a,b,c,W[8],11,0x8771f681UL) +    HH(c,d,a,b,W[11],16,0x6d9d6122UL) +    HH(b,c,d,a,W[14],23,0xfde5380cUL) +    HH(a,b,c,d,W[1],4,0xa4beea44UL) +    HH(d,a,b,c,W[4],11,0x4bdecfa9UL) +    HH(c,d,a,b,W[7],16,0xf6bb4b60UL) +    HH(b,c,d,a,W[10],23,0xbebfbc70UL) +    HH(a,b,c,d,W[13],4,0x289b7ec6UL) +    HH(d,a,b,c,W[0],11,0xeaa127faUL) +    HH(c,d,a,b,W[3],16,0xd4ef3085UL) +    HH(b,c,d,a,W[6],23,0x04881d05UL) +    HH(a,b,c,d,W[9],4,0xd9d4d039UL) +    HH(d,a,b,c,W[12],11,0xe6db99e5UL) +    HH(c,d,a,b,W[15],16,0x1fa27cf8UL) +    HH(b,c,d,a,W[2],23,0xc4ac5665UL) +    II(a,b,c,d,W[0],6,0xf4292244UL) +    II(d,a,b,c,W[7],10,0x432aff97UL) +    II(c,d,a,b,W[14],15,0xab9423a7UL) +    II(b,c,d,a,W[5],21,0xfc93a039UL) +    II(a,b,c,d,W[12],6,0x655b59c3UL) +    II(d,a,b,c,W[3],10,0x8f0ccc92UL) +    II(c,d,a,b,W[10],15,0xffeff47dUL) +    II(b,c,d,a,W[1],21,0x85845dd1UL) +    II(a,b,c,d,W[8],6,0x6fa87e4fUL) +    II(d,a,b,c,W[15],10,0xfe2ce6e0UL) +    II(c,d,a,b,W[6],15,0xa3014314UL) +    II(b,c,d,a,W[13],21,0x4e0811a1UL) +    II(a,b,c,d,W[4],6,0xf7537e82UL) +    II(d,a,b,c,W[11],10,0xbd3af235UL) +    II(c,d,a,b,W[2],15,0x2ad7d2bbUL) +    II(b,c,d,a,W[9],21,0xeb86d391UL) +#endif + +    md->md5.state[0] = md->md5.state[0] + a; +    md->md5.state[1] = md->md5.state[1] + b; +    md->md5.state[2] = md->md5.state[2] + c; +    md->md5.state[3] = md->md5.state[3] + d; + +    return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int md5_compress(hash_state *md, unsigned char *buf) +{ +   int err; +   err = _md5_compress(md, buf); +   burn_stack(sizeof(ulong32) * 21); +   return err; +} +#endif + +/** +   Initialize the hash state +   @param md   The hash state you wish to initialize +   @return CRYPT_OK if successful +*/ +int md5_init(hash_state * md) +{ +   LTC_ARGCHK(md != NULL); +   md->md5.state[0] = 0x67452301UL; +   md->md5.state[1] = 0xefcdab89UL; +   md->md5.state[2] = 0x98badcfeUL; +   md->md5.state[3] = 0x10325476UL; +   md->md5.curlen = 0; +   md->md5.length = 0; +   return CRYPT_OK; +} + +/** +   Process a block of memory though the hash +   @param md     The hash state +   @param in     The data to hash +   @param inlen  The length of the data (octets) +   @return CRYPT_OK if successful +*/ +HASH_PROCESS(md5_process, md5_compress, md5, 64) + +/** +   Terminate the hash to get the digest +   @param md  The hash state +   @param out [out] The destination of the hash (16 bytes) +   @return CRYPT_OK if successful +*/ +int md5_done(hash_state * md, unsigned char *out) +{ +    int i; + +    LTC_ARGCHK(md  != NULL); +    LTC_ARGCHK(out != NULL); + +    if (md->md5.curlen >= sizeof(md->md5.buf)) { +       return CRYPT_INVALID_ARG; +    } + + +    /* increase the length of the message */ +    md->md5.length += md->md5.curlen * 8; + +    /* append the '1' bit */ +    md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; + +    /* if the length is currently above 56 bytes we append zeros +     * then compress.  Then we can fall back to padding zeros and length +     * encoding like normal. +     */ +    if (md->md5.curlen > 56) { +        while (md->md5.curlen < 64) { +            md->md5.buf[md->md5.curlen++] = (unsigned char)0; +        } +        md5_compress(md, md->md5.buf); +        md->md5.curlen = 0; +    } + +    /* pad upto 56 bytes of zeroes */ +    while (md->md5.curlen < 56) { +        md->md5.buf[md->md5.curlen++] = (unsigned char)0; +    } + +    /* store length */ +    STORE64L(md->md5.length, md->md5.buf+56); +    md5_compress(md, md->md5.buf); + +    /* copy output */ +    for (i = 0; i < 4; i++) { +        STORE32L(md->md5.state[i], out+(4*i)); +    } +#ifdef LTC_CLEAN_STACK +    zeromem(md, sizeof(hash_state)); +#endif +    return CRYPT_OK; +} + +/** +  Self-test the hash +  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/   +int  md5_test(void) +{ + #ifndef LTC_TEST +    return CRYPT_NOP; + #else     +  static const struct { +      char *msg; +      unsigned char hash[16]; +  } tests[] = { +    { "", +      { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,  +        0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, +    { "a", +      {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,  +       0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, +    { "abc", +      { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,  +        0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, +    { "message digest",  +      { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,  +        0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },  +    { "abcdefghijklmnopqrstuvwxyz", +      { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,  +        0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, +    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", +      { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,  +        0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, +    { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", +      { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,  +        0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },  +    { NULL, { 0 } } +  }; + +  int i; +  unsigned char tmp[16]; +  hash_state md; + +  for (i = 0; tests[i].msg != NULL; i++) { +      md5_init(&md); +      md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); +      md5_done(&md, tmp); +      if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { +         return CRYPT_FAIL_TESTVECTOR; +      } +  } +  return CRYPT_OK; + #endif +} + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/md5.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ diff --git a/src/libtomcrypt/src/hashes/sha1.c b/src/libtomcrypt/src/hashes/sha1.c new file mode 100644 index 0000000..409d095 --- /dev/null +++ b/src/libtomcrypt/src/hashes/sha1.c @@ -0,0 +1,288 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file sha1.c +  LTC_SHA1 code by Tom St Denis  +*/ + + +#ifdef LTC_SHA1 + +const struct ltc_hash_descriptor sha1_desc = +{ +    "sha1", +    2, +    20, +    64, + +    /* OID */ +   { 1, 3, 14, 3, 2, 26,  }, +   6, + +    &sha1_init, +    &sha1_process, +    &sha1_done, +    &sha1_test, +    NULL +}; + +#define F0(x,y,z)  (z ^ (x & (y ^ z))) +#define F1(x,y,z)  (x ^ y ^ z) +#define F2(x,y,z)  ((x & y) | (z & (x | y))) +#define F3(x,y,z)  (x ^ y ^ z) + +#ifdef LTC_CLEAN_STACK +static int _sha1_compress(hash_state *md, unsigned char *buf) +#else +static int  sha1_compress(hash_state *md, unsigned char *buf) +#endif +{ +    ulong32 a,b,c,d,e,W[80],i; +#ifdef LTC_SMALL_CODE +    ulong32 t; +#endif + +    /* copy the state into 512-bits into W[0..15] */ +    for (i = 0; i < 16; i++) { +        LOAD32H(W[i], buf + (4*i)); +    } + +    /* copy state */ +    a = md->sha1.state[0]; +    b = md->sha1.state[1]; +    c = md->sha1.state[2]; +    d = md->sha1.state[3]; +    e = md->sha1.state[4]; + +    /* expand it */ +    for (i = 16; i < 80; i++) { +        W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);  +    } + +    /* compress */ +    /* round one */ +    #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); +    #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); +    #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); +    #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); +  +#ifdef LTC_SMALL_CODE +  +    for (i = 0; i < 20; ) { +       FF0(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 40; ) { +       FF1(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 60; ) { +       FF2(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; +    } + +    for (; i < 80; ) { +       FF3(a,b,c,d,e,i++); t = e; e = d; d = c; c = b; b = a; a = t; +    } + +#else + +    for (i = 0; i < 20; ) { +       FF0(a,b,c,d,e,i++); +       FF0(e,a,b,c,d,i++); +       FF0(d,e,a,b,c,i++); +       FF0(c,d,e,a,b,i++); +       FF0(b,c,d,e,a,i++); +    } + +    /* round two */ +    for (; i < 40; )  {  +       FF1(a,b,c,d,e,i++); +       FF1(e,a,b,c,d,i++); +       FF1(d,e,a,b,c,i++); +       FF1(c,d,e,a,b,i++); +       FF1(b,c,d,e,a,i++); +    } + +    /* round three */ +    for (; i < 60; )  {  +       FF2(a,b,c,d,e,i++); +       FF2(e,a,b,c,d,i++); +       FF2(d,e,a,b,c,i++); +       FF2(c,d,e,a,b,i++); +       FF2(b,c,d,e,a,i++); +    } + +    /* round four */ +    for (; i < 80; )  {  +       FF3(a,b,c,d,e,i++); +       FF3(e,a,b,c,d,i++); +       FF3(d,e,a,b,c,i++); +       FF3(c,d,e,a,b,i++); +       FF3(b,c,d,e,a,i++); +    } +#endif + +    #undef FF0 +    #undef FF1 +    #undef FF2 +    #undef FF3 + +    /* store */ +    md->sha1.state[0] = md->sha1.state[0] + a; +    md->sha1.state[1] = md->sha1.state[1] + b; +    md->sha1.state[2] = md->sha1.state[2] + c; +    md->sha1.state[3] = md->sha1.state[3] + d; +    md->sha1.state[4] = md->sha1.state[4] + e; + +    return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int sha1_compress(hash_state *md, unsigned char *buf) +{ +   int err; +   err = _sha1_compress(md, buf); +   burn_stack(sizeof(ulong32) * 87); +   return err; +} +#endif + +/** +   Initialize the hash state +   @param md   The hash state you wish to initialize +   @return CRYPT_OK if successful +*/ +int sha1_init(hash_state * md) +{ +   LTC_ARGCHK(md != NULL); +   md->sha1.state[0] = 0x67452301UL; +   md->sha1.state[1] = 0xefcdab89UL; +   md->sha1.state[2] = 0x98badcfeUL; +   md->sha1.state[3] = 0x10325476UL; +   md->sha1.state[4] = 0xc3d2e1f0UL; +   md->sha1.curlen = 0; +   md->sha1.length = 0; +   return CRYPT_OK; +} + +/** +   Process a block of memory though the hash +   @param md     The hash state +   @param in     The data to hash +   @param inlen  The length of the data (octets) +   @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha1_process, sha1_compress, sha1, 64) + +/** +   Terminate the hash to get the digest +   @param md  The hash state +   @param out [out] The destination of the hash (20 bytes) +   @return CRYPT_OK if successful +*/ +int sha1_done(hash_state * md, unsigned char *out) +{ +    int i; + +    LTC_ARGCHK(md  != NULL); +    LTC_ARGCHK(out != NULL); + +    if (md->sha1.curlen >= sizeof(md->sha1.buf)) { +       return CRYPT_INVALID_ARG; +    } + +    /* increase the length of the message */ +    md->sha1.length += md->sha1.curlen * 8; + +    /* append the '1' bit */ +    md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + +    /* if the length is currently above 56 bytes we append zeros +     * then compress.  Then we can fall back to padding zeros and length +     * encoding like normal. +     */ +    if (md->sha1.curlen > 56) { +        while (md->sha1.curlen < 64) { +            md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; +        } +        sha1_compress(md, md->sha1.buf); +        md->sha1.curlen = 0; +    } + +    /* pad upto 56 bytes of zeroes */ +    while (md->sha1.curlen < 56) { +        md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; +    } + +    /* store length */ +    STORE64H(md->sha1.length, md->sha1.buf+56); +    sha1_compress(md, md->sha1.buf); + +    /* copy output */ +    for (i = 0; i < 5; i++) { +        STORE32H(md->sha1.state[i], out+(4*i)); +    } +#ifdef LTC_CLEAN_STACK +    zeromem(md, sizeof(hash_state)); +#endif +    return CRYPT_OK; +} + +/** +  Self-test the hash +  @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/   +int  sha1_test(void) +{ + #ifndef LTC_TEST +    return CRYPT_NOP; + #else     +  static const struct { +      char *msg; +      unsigned char hash[20]; +  } tests[] = { +    { "abc", +      { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, +        0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, +        0x9c, 0xd0, 0xd8, 0x9d } +    }, +    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", +      { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, +        0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, +        0xE5, 0x46, 0x70, 0xF1 } +    } +  }; + +  int i; +  unsigned char tmp[20]; +  hash_state md; + +  for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0]));  i++) { +      sha1_init(&md); +      sha1_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); +      sha1_done(&md, tmp); +      if (XMEMCMP(tmp, tests[i].hash, 20) != 0) { +         return CRYPT_FAIL_TESTVECTOR; +      } +  } +  return CRYPT_OK; +  #endif +} + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt.h b/src/libtomcrypt/src/headers/tomcrypt.h new file mode 100644 index 0000000..74cdff4 --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt.h @@ -0,0 +1,87 @@ +#ifndef TOMCRYPT_H_ +#define TOMCRYPT_H_ +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#include <limits.h> + +/* use configuration data */ +#include "tomcrypt_custom.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* version */ +#define CRYPT   0x0117 +#define SCRYPT  "1.17" + +/* max size of either a cipher/hash block or symmetric key [largest of the two] */ +#define MAXBLOCKSIZE  128 + +/* descriptor table size */ +#define TAB_SIZE      32 + +/* error codes [will be expanded in future releases] */ +enum { +   CRYPT_OK=0,             /* Result OK */ +   CRYPT_ERROR,            /* Generic Error */ +   CRYPT_NOP,              /* Not a failure but no operation was performed */ + +   CRYPT_INVALID_KEYSIZE,  /* Invalid key size given */ +   CRYPT_INVALID_ROUNDS,   /* Invalid number of rounds */ +   CRYPT_FAIL_TESTVECTOR,  /* Algorithm failed test vectors */ + +   CRYPT_BUFFER_OVERFLOW,  /* Not enough space for output */ +   CRYPT_INVALID_PACKET,   /* Invalid input packet given */ + +   CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ +   CRYPT_ERROR_READPRNG,   /* Could not read enough from PRNG */ + +   CRYPT_INVALID_CIPHER,   /* Invalid cipher specified */ +   CRYPT_INVALID_HASH,     /* Invalid hash specified */ +   CRYPT_INVALID_PRNG,     /* Invalid PRNG specified */ + +   CRYPT_MEM,              /* Out of memory */ + +   CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ +   CRYPT_PK_NOT_PRIVATE,   /* Requires a private PK key */ + +   CRYPT_INVALID_ARG,      /* Generic invalid argument */ +   CRYPT_FILE_NOTFOUND,    /* File Not Found */ + +   CRYPT_PK_INVALID_TYPE,  /* Invalid type of PK key */ +   CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */ +   CRYPT_PK_DUP,           /* Duplicate key already in key ring */ +   CRYPT_PK_NOT_FOUND,     /* Key not found in keyring */ +   CRYPT_PK_INVALID_SIZE,  /* Invalid size input for PK parameters */ + +   CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */ +   CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ +}; + +#include "tomcrypt_cfg.h" +#include "tomcrypt_macros.h" +#include "tomcrypt_cipher.h" +#include "tomcrypt_hash.h" +#include "tomcrypt_mac.h" +#include "tomcrypt_prng.h" +#include "tomcrypt_pk.h" +#include "tomcrypt_math.h" +#include "tomcrypt_misc.h" +#include "tomcrypt_argchk.h" +#include "tomcrypt_pkcs.h" + +#ifdef __cplusplus +   } +#endif + +#endif /* TOMCRYPT_H_ */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ +/* $Revision: 1.21 $ */ +/* $Date: 2006/12/16 19:34:05 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_argchk.h b/src/libtomcrypt/src/headers/tomcrypt_argchk.h new file mode 100644 index 0000000..cfc93ad --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_argchk.h @@ -0,0 +1,38 @@ +/* Defines the LTC_ARGCHK macro used within the library */ +/* ARGTYPE is defined in mycrypt_cfg.h */ +#if ARGTYPE == 0 + +#include <signal.h> + +/* this is the default LibTomCrypt macro  */ +void crypt_argchk(char *v, char *s, int d); +#define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 1 + +/* fatal type of error */ +#define LTC_ARGCHK(x) assert((x)) +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 2 + +#define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); } +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 3 + +#define LTC_ARGCHK(x)  +#define LTC_ARGCHKVD(x) LTC_ARGCHK(x) + +#elif ARGTYPE == 4 + +#define LTC_ARGCHK(x)   if (!(x)) return CRYPT_INVALID_ARG; +#define LTC_ARGCHKVD(x) if (!(x)) return; + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/08/27 20:50:21 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_cfg.h b/src/libtomcrypt/src/headers/tomcrypt_cfg.h new file mode 100644 index 0000000..7feae6e --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_cfg.h @@ -0,0 +1,136 @@ +/* This is the build config file. + * + * With this you can setup what to inlcude/exclude automatically during any build.  Just comment + * out the line that #define's the word for the thing you want to remove.  phew! + */ + +#ifndef TOMCRYPT_CFG_H +#define TOMCRYPT_CFG_H + +#if defined(_WIN32) || defined(_MSC_VER) +#define LTC_CALL __cdecl +#else +#ifndef LTC_CALL +   #define LTC_CALL +#endif +#endif + +#ifndef LTC_EXPORT +#define LTC_EXPORT +#endif + +/* certain platforms use macros for these, making the prototypes broken */ +#ifndef LTC_NO_PROTOTYPES + +/* you can change how memory allocation works ... */ +LTC_EXPORT void * LTC_CALL XMALLOC(size_t n); +LTC_EXPORT void * LTC_CALL XREALLOC(void *p, size_t n); +LTC_EXPORT void * LTC_CALL XCALLOC(size_t n, size_t s); +LTC_EXPORT void LTC_CALL XFREE(void *p); + +LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); + + +/* change the clock function too */ +LTC_EXPORT clock_t LTC_CALL XCLOCK(void); + +/* various other functions */ +LTC_EXPORT void * LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); +LTC_EXPORT int   LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); +LTC_EXPORT void * LTC_CALL XMEMSET(void *s, int c, size_t n); + +LTC_EXPORT int   LTC_CALL XSTRCMP(const char *s1, const char *s2); + +#endif + +/* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ +#ifndef ARGTYPE +   #define ARGTYPE  0 +#endif + +/* Controls endianess and size of registers.  Leave uncommented to get platform neutral [slower] code  + *  + * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. + * The x86 platforms allow this but some others [ARM for instance] do not.  On those platforms you **MUST** + * use the portable [slower] macros. + */ + +/* detect x86-32 machines somewhat */ +#if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))) +   #define ENDIAN_LITTLE +   #define ENDIAN_32BITWORD +   #define LTC_FAST +   #define LTC_FAST_TYPE    unsigned long +#endif + +/* detects MIPS R5900 processors (PS2) */ +#if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips)) +   #define ENDIAN_LITTLE +   #define ENDIAN_64BITWORD +#endif + +/* detect amd64 */ +#if !defined(__STRICT_ANSI__) && defined(__x86_64__) +   #define ENDIAN_LITTLE +   #define ENDIAN_64BITWORD +   #define LTC_FAST +   #define LTC_FAST_TYPE    unsigned long +#endif + +/* detect PPC32 */ +#if !defined(__STRICT_ANSI__) && defined(LTC_PPC32) +   #define ENDIAN_BIG +   #define ENDIAN_32BITWORD +   #define LTC_FAST +   #define LTC_FAST_TYPE    unsigned long +#endif    + +/* detect sparc and sparc64 */ +#if defined(__sparc__) +  #define ENDIAN_BIG +  #if defined(__arch64__) +    #define ENDIAN_64BITWORD +  #else +    #define ENDIAN_32BITWORD +  #endif +#endif + + +#ifdef LTC_NO_FAST +   #ifdef LTC_FAST +      #undef LTC_FAST +   #endif +#endif + +/* No asm is a quick way to disable anything "not portable" */ +#ifdef LTC_NO_ASM +   #undef ENDIAN_LITTLE +   #undef ENDIAN_BIG +   #undef ENDIAN_32BITWORD +   #undef ENDIAN_64BITWORD +   #undef LTC_FAST +   #undef LTC_FAST_TYPE +   #define LTC_NO_ROLC +	#define LTC_NO_BSWAP +#endif + +/* #define ENDIAN_LITTLE */ +/* #define ENDIAN_BIG */ + +/* #define ENDIAN_32BITWORD */ +/* #define ENDIAN_64BITWORD */ + +#if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD)) +    #error You must specify a word size as well as endianess in tomcrypt_cfg.h +#endif + +#if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) +   #define ENDIAN_NEUTRAL +#endif + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */ +/* $Revision: 1.19 $ */ +/* $Date: 2006/12/04 02:19:48 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_cipher.h b/src/libtomcrypt/src/headers/tomcrypt_cipher.h new file mode 100644 index 0000000..bd740bf --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_cipher.h @@ -0,0 +1,891 @@ +/* ---- SYMMETRIC KEY STUFF ----- + * + * We put each of the ciphers scheduled keys in their own structs then we put all of  + * the key formats in one union.  This makes the function prototypes easier to use. + */ +#ifdef LTC_BLOWFISH +struct blowfish_key { +   ulong32 S[4][256]; +   ulong32 K[18]; +}; +#endif + +#ifdef LTC_RC5 +struct rc5_key { +   int rounds; +   ulong32 K[50]; +}; +#endif + +#ifdef LTC_RC6 +struct rc6_key { +   ulong32 K[44]; +}; +#endif + +#ifdef LTC_SAFERP +struct saferp_key { +   unsigned char K[33][16]; +   long rounds; +}; +#endif + +#ifdef LTC_RIJNDAEL +struct rijndael_key { +   ulong32 eK[60], dK[60]; +   int Nr; +}; +#endif + +#ifdef LTC_KSEED +struct kseed_key { +    ulong32 K[32], dK[32]; +}; +#endif + +#ifdef LTC_KASUMI +struct kasumi_key { +    ulong32 KLi1[8], KLi2[8], +            KOi1[8], KOi2[8], KOi3[8], +            KIi1[8], KIi2[8], KIi3[8]; +}; +#endif + +#ifdef LTC_XTEA +struct xtea_key { +   unsigned long A[32], B[32]; +}; +#endif + +#ifdef LTC_TWOFISH +#ifndef LTC_TWOFISH_SMALL +   struct twofish_key { +      ulong32 S[4][256], K[40]; +   }; +#else +   struct twofish_key { +      ulong32 K[40]; +      unsigned char S[32], start; +   }; +#endif +#endif + +#ifdef LTC_SAFER +#define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS     6 +#define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS   10 +#define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS    8 +#define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS  10 +#define LTC_SAFER_MAX_NOF_ROUNDS            13 +#define LTC_SAFER_BLOCK_LEN                  8 +#define LTC_SAFER_KEY_LEN     (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS)) +typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN]; +typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN]; +struct safer_key { safer_key_t key; }; +#endif + +#ifdef LTC_RC2 +struct rc2_key { unsigned xkey[64]; }; +#endif + +#ifdef LTC_DES +struct des_key { +    ulong32 ek[32], dk[32]; +}; + +struct des3_key { +    ulong32 ek[3][32], dk[3][32]; +}; +#endif + +#ifdef LTC_CAST5 +struct cast5_key { +    ulong32 K[32], keylen; +}; +#endif + +#ifdef LTC_NOEKEON +struct noekeon_key { +    ulong32 K[4], dK[4]; +}; +#endif + +#ifdef LTC_SKIPJACK  +struct skipjack_key { +    unsigned char key[10]; +}; +#endif + +#ifdef LTC_KHAZAD +struct khazad_key { +   ulong64 roundKeyEnc[8 + 1];  +   ulong64 roundKeyDec[8 + 1];  +}; +#endif + +#ifdef LTC_ANUBIS +struct anubis_key {  +   int keyBits;  +   int R;  +   ulong32 roundKeyEnc[18 + 1][4];  +   ulong32 roundKeyDec[18 + 1][4];  +};  +#endif + +#ifdef LTC_MULTI2 +struct multi2_key { +    int N; +    ulong32 uk[8]; +}; +#endif + +typedef union Symmetric_key { +#ifdef LTC_DES +   struct des_key des; +   struct des3_key des3; +#endif +#ifdef LTC_RC2 +   struct rc2_key rc2; +#endif +#ifdef LTC_SAFER +   struct safer_key safer; +#endif +#ifdef LTC_TWOFISH +   struct twofish_key  twofish; +#endif +#ifdef LTC_BLOWFISH +   struct blowfish_key blowfish; +#endif +#ifdef LTC_RC5 +   struct rc5_key      rc5; +#endif +#ifdef LTC_RC6 +   struct rc6_key      rc6; +#endif +#ifdef LTC_SAFERP +   struct saferp_key   saferp; +#endif +#ifdef LTC_RIJNDAEL +   struct rijndael_key rijndael; +#endif +#ifdef LTC_XTEA +   struct xtea_key     xtea; +#endif +#ifdef LTC_CAST5 +   struct cast5_key    cast5; +#endif +#ifdef LTC_NOEKEON +   struct noekeon_key  noekeon; +#endif    +#ifdef LTC_SKIPJACK +   struct skipjack_key skipjack; +#endif +#ifdef LTC_KHAZAD +   struct khazad_key   khazad; +#endif +#ifdef LTC_ANUBIS +   struct anubis_key   anubis; +#endif +#ifdef LTC_KSEED +   struct kseed_key    kseed; +#endif +#ifdef LTC_KASUMI +   struct kasumi_key   kasumi; +#endif   +#ifdef LTC_MULTI2 +   struct multi2_key   multi2; +#endif +   void   *data; +} symmetric_key; + +#ifdef LTC_ECB_MODE +/** A block cipher ECB structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher,  +   /** The block size of the given cipher */ +                       blocklen; +   /** The scheduled key */                        +   symmetric_key       key; +} symmetric_ECB; +#endif + +#ifdef LTC_CFB_MODE +/** A block cipher CFB structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher,  +   /** The block size of the given cipher */                         +                       blocklen,  +   /** The padding offset */ +                       padlen; +   /** The current IV */ +   unsigned char       IV[MAXBLOCKSIZE],  +   /** The pad used to encrypt/decrypt */  +                       pad[MAXBLOCKSIZE]; +   /** The scheduled key */ +   symmetric_key       key; +} symmetric_CFB; +#endif + +#ifdef LTC_OFB_MODE +/** A block cipher OFB structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher,  +   /** The block size of the given cipher */                         +                       blocklen,  +   /** The padding offset */ +                       padlen; +   /** The current IV */ +   unsigned char       IV[MAXBLOCKSIZE]; +   /** The scheduled key */ +   symmetric_key       key; +} symmetric_OFB; +#endif + +#ifdef LTC_CBC_MODE +/** A block cipher CBC structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher,  +   /** The block size of the given cipher */                         +                       blocklen; +   /** The current IV */ +   unsigned char       IV[MAXBLOCKSIZE]; +   /** The scheduled key */ +   symmetric_key       key; +} symmetric_CBC; +#endif + + +#ifdef LTC_CTR_MODE +/** A block cipher CTR structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher, +   /** The block size of the given cipher */                         +                       blocklen,  +   /** The padding offset */ +                       padlen,  +   /** The mode (endianess) of the CTR, 0==little, 1==big */ +                       mode, +   /** counter width */ +                       ctrlen; + +   /** The counter */                        +   unsigned char       ctr[MAXBLOCKSIZE],  +   /** The pad used to encrypt/decrypt */                        +                       pad[MAXBLOCKSIZE]; +   /** The scheduled key */ +   symmetric_key       key; +} symmetric_CTR; +#endif + + +#ifdef LTC_LRW_MODE +/** A LRW structure */ +typedef struct { +    /** The index of the cipher chosen (must be a 128-bit block cipher) */ +    int               cipher; + +    /** The current IV */ +    unsigned char     IV[16], +  +    /** the tweak key */ +                      tweak[16], + +    /** The current pad, it's the product of the first 15 bytes against the tweak key */ +                      pad[16]; + +    /** The scheduled symmetric key */ +    symmetric_key     key; + +#ifdef LRW_TABLES +    /** The pre-computed multiplication table */ +    unsigned char     PC[16][256][16]; +#endif +} symmetric_LRW; +#endif + +#ifdef LTC_F8_MODE +/** A block cipher F8 structure */ +typedef struct { +   /** The index of the cipher chosen */ +   int                 cipher,  +   /** The block size of the given cipher */                         +                       blocklen,  +   /** The padding offset */ +                       padlen; +   /** The current IV */ +   unsigned char       IV[MAXBLOCKSIZE], +                       MIV[MAXBLOCKSIZE]; +   /** Current block count */ +   ulong32             blockcnt; +   /** The scheduled key */ +   symmetric_key       key; +} symmetric_F8; +#endif + + +/** cipher descriptor table, last entry has "name == NULL" to mark the end of table */ +extern struct ltc_cipher_descriptor { +   /** name of cipher */ +   char *name; +   /** internal ID */ +   unsigned char ID; +   /** min keysize (octets) */ +   int  min_key_length,  +   /** max keysize (octets) */ +        max_key_length,  +   /** block size (octets) */ +        block_length,  +   /** default number of rounds */ +        default_rounds; +   /** Setup the cipher  +      @param key         The input symmetric key +      @param keylen      The length of the input key (octets) +      @param num_rounds  The requested number of rounds (0==default) +      @param skey        [out] The destination of the scheduled key +      @return CRYPT_OK if successful +   */ +   int  (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +   /** Encrypt a block +      @param pt      The plaintext +      @param ct      [out] The ciphertext +      @param skey    The scheduled key +      @return CRYPT_OK if successful +   */ +   int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +   /** Decrypt a block +      @param ct      The ciphertext +      @param pt      [out] The plaintext +      @param skey    The scheduled key +      @return CRYPT_OK if successful +   */ +   int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +   /** Test the block cipher +       @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled +   */ +   int (*test)(void); + +   /** Terminate the context  +      @param skey    The scheduled key +   */ +   void (*done)(symmetric_key *skey);       + +   /** Determine a key size +       @param keysize    [in/out] The size of the key desired and the suggested size +       @return CRYPT_OK if successful +   */ +   int  (*keysize)(int *keysize); + +/** Accelerators **/ +   /** Accelerated ECB encryption  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey); + +   /** Accelerated ECB decryption  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey); + +   /** Accelerated CBC encryption  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param IV      The initial value (input/output) +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + +   /** Accelerated CBC decryption  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param IV      The initial value (input/output) +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey); + +   /** Accelerated CTR encryption  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param IV      The initial value (input/output) +       @param mode    little or big endian counter (mode=0 or mode=1) +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey); + +   /** Accelerated LRW  +       @param pt      Plaintext +       @param ct      Ciphertext +       @param blocks  The number of complete blocks to process +       @param IV      The initial value (input/output) +       @param tweak   The LRW tweak +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + +   /** Accelerated LRW  +       @param ct      Ciphertext +       @param pt      Plaintext +       @param blocks  The number of complete blocks to process +       @param IV      The initial value (input/output) +       @param tweak   The LRW tweak +       @param skey    The scheduled key context +       @return CRYPT_OK if successful +   */ +   int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); + +   /** Accelerated CCM packet (one-shot) +       @param key        The secret key to use +       @param keylen     The length of the secret key (octets) +       @param uskey      A previously scheduled key [optional can be NULL] +       @param nonce      The session nonce [use once] +       @param noncelen   The length of the nonce +       @param header     The header for the session +       @param headerlen  The length of the header (octets) +       @param pt         [out] The plaintext +       @param ptlen      The length of the plaintext (octets) +       @param ct         [out] The ciphertext +       @param tag        [out] The destination tag +       @param taglen     [in/out] The max size and resulting size of the authentication tag +       @param direction  Encrypt or Decrypt direction (0 or 1) +       @return CRYPT_OK if successful +   */ +   int (*accel_ccm_memory)( +       const unsigned char *key,    unsigned long keylen, +       symmetric_key       *uskey, +       const unsigned char *nonce,  unsigned long noncelen, +       const unsigned char *header, unsigned long headerlen, +             unsigned char *pt,     unsigned long ptlen, +             unsigned char *ct, +             unsigned char *tag,    unsigned long *taglen, +                       int  direction); + +   /** Accelerated GCM packet (one shot) +       @param key        The secret key +       @param keylen     The length of the secret key +       @param IV         The initial vector  +       @param IVlen      The length of the initial vector +       @param adata      The additional authentication data (header) +       @param adatalen   The length of the adata +       @param pt         The plaintext +       @param ptlen      The length of the plaintext (ciphertext length is the same) +       @param ct         The ciphertext +       @param tag        [out] The MAC tag +       @param taglen     [in/out] The MAC tag length +       @param direction  Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) +       @return CRYPT_OK on success +   */ +   int (*accel_gcm_memory)( +       const unsigned char *key,    unsigned long keylen, +       const unsigned char *IV,     unsigned long IVlen, +       const unsigned char *adata,  unsigned long adatalen, +             unsigned char *pt,     unsigned long ptlen, +             unsigned char *ct,  +             unsigned char *tag,    unsigned long *taglen, +                       int direction); + +   /** Accelerated one shot LTC_OMAC  +       @param key            The secret key +       @param keylen         The key length (octets)  +       @param in             The message  +       @param inlen          Length of message (octets) +       @param out            [out] Destination for tag +       @param outlen         [in/out] Initial and final size of out +       @return CRYPT_OK on success +   */ +   int (*omac_memory)( +       const unsigned char *key, unsigned long keylen, +       const unsigned char *in,  unsigned long inlen, +             unsigned char *out, unsigned long *outlen); + +   /** Accelerated one shot XCBC  +       @param key            The secret key +       @param keylen         The key length (octets)  +       @param in             The message  +       @param inlen          Length of message (octets) +       @param out            [out] Destination for tag +       @param outlen         [in/out] Initial and final size of out +       @return CRYPT_OK on success +   */ +   int (*xcbc_memory)( +       const unsigned char *key, unsigned long keylen, +       const unsigned char *in,  unsigned long inlen, +             unsigned char *out, unsigned long *outlen); + +   /** Accelerated one shot F9  +       @param key            The secret key +       @param keylen         The key length (octets)  +       @param in             The message  +       @param inlen          Length of message (octets) +       @param out            [out] Destination for tag +       @param outlen         [in/out] Initial and final size of out +       @return CRYPT_OK on success +       @remark Requires manual padding +   */ +   int (*f9_memory)( +       const unsigned char *key, unsigned long keylen, +       const unsigned char *in,  unsigned long inlen, +             unsigned char *out, unsigned long *outlen); +} cipher_descriptor[]; + +#ifdef LTC_BLOWFISH +int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int blowfish_test(void); +void blowfish_done(symmetric_key *skey); +int blowfish_keysize(int *keysize); +extern const struct ltc_cipher_descriptor blowfish_desc; +#endif + +#ifdef LTC_RC5 +int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc5_test(void); +void rc5_done(symmetric_key *skey); +int rc5_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc5_desc; +#endif + +#ifdef LTC_RC6 +int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc6_test(void); +void rc6_done(symmetric_key *skey); +int rc6_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc6_desc; +#endif + +#ifdef LTC_RC2 +int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rc2_test(void); +void rc2_done(symmetric_key *skey); +int rc2_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rc2_desc; +#endif + +#ifdef LTC_SAFERP +int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int saferp_test(void); +void saferp_done(symmetric_key *skey); +int saferp_keysize(int *keysize); +extern const struct ltc_cipher_descriptor saferp_desc; +#endif + +#ifdef LTC_SAFER +int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key); +int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key); +int safer_k64_test(void); +int safer_sk64_test(void); +int safer_sk128_test(void); +void safer_done(symmetric_key *skey); +int safer_64_keysize(int *keysize); +int safer_128_keysize(int *keysize); +extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc; +#endif + +#ifdef LTC_RIJNDAEL + +/* make aes an alias */ +#define aes_setup           rijndael_setup +#define aes_ecb_encrypt     rijndael_ecb_encrypt +#define aes_ecb_decrypt     rijndael_ecb_decrypt +#define aes_test            rijndael_test +#define aes_done            rijndael_done +#define aes_keysize         rijndael_keysize + +#define aes_enc_setup           rijndael_enc_setup +#define aes_enc_ecb_encrypt     rijndael_enc_ecb_encrypt +#define aes_enc_keysize         rijndael_enc_keysize + +int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int rijndael_test(void); +void rijndael_done(symmetric_key *skey); +int rijndael_keysize(int *keysize); +int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +void rijndael_enc_done(symmetric_key *skey); +int rijndael_enc_keysize(int *keysize); +extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc; +extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc; +#endif + +#ifdef LTC_XTEA +int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int xtea_test(void); +void xtea_done(symmetric_key *skey); +int xtea_keysize(int *keysize); +extern const struct ltc_cipher_descriptor xtea_desc; +#endif + +#ifdef LTC_TWOFISH +int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int twofish_test(void); +void twofish_done(symmetric_key *skey); +int twofish_keysize(int *keysize); +extern const struct ltc_cipher_descriptor twofish_desc; +#endif + +#ifdef LTC_DES +int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des_test(void); +void des_done(symmetric_key *skey); +int des_keysize(int *keysize); +int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int des3_test(void); +void des3_done(symmetric_key *skey); +int des3_keysize(int *keysize); +extern const struct ltc_cipher_descriptor des_desc, des3_desc; +#endif + +#ifdef LTC_CAST5 +int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int cast5_test(void); +void cast5_done(symmetric_key *skey); +int cast5_keysize(int *keysize); +extern const struct ltc_cipher_descriptor cast5_desc; +#endif + +#ifdef LTC_NOEKEON +int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int noekeon_test(void); +void noekeon_done(symmetric_key *skey); +int noekeon_keysize(int *keysize); +extern const struct ltc_cipher_descriptor noekeon_desc; +#endif + +#ifdef LTC_SKIPJACK +int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int skipjack_test(void); +void skipjack_done(symmetric_key *skey); +int skipjack_keysize(int *keysize); +extern const struct ltc_cipher_descriptor skipjack_desc; +#endif + +#ifdef LTC_KHAZAD +int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int khazad_test(void); +void khazad_done(symmetric_key *skey); +int khazad_keysize(int *keysize); +extern const struct ltc_cipher_descriptor khazad_desc; +#endif + +#ifdef LTC_ANUBIS +int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int anubis_test(void); +void anubis_done(symmetric_key *skey); +int anubis_keysize(int *keysize); +extern const struct ltc_cipher_descriptor anubis_desc; +#endif + +#ifdef LTC_KSEED +int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kseed_test(void); +void kseed_done(symmetric_key *skey); +int kseed_keysize(int *keysize); +extern const struct ltc_cipher_descriptor kseed_desc; +#endif + +#ifdef LTC_KASUMI +int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int kasumi_test(void); +void kasumi_done(symmetric_key *skey); +int kasumi_keysize(int *keysize); +extern const struct ltc_cipher_descriptor kasumi_desc; +#endif + + +#ifdef LTC_MULTI2 +int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); +int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); +int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); +int multi2_test(void); +void multi2_done(symmetric_key *skey); +int multi2_keysize(int *keysize); +extern const struct ltc_cipher_descriptor multi2_desc; +#endif + +#ifdef LTC_ECB_MODE +int ecb_start(int cipher, const unsigned char *key,  +              int keylen, int num_rounds, symmetric_ECB *ecb); +int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb); +int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb); +int ecb_done(symmetric_ECB *ecb); +#endif + +#ifdef LTC_CFB_MODE +int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key,  +              int keylen, int num_rounds, symmetric_CFB *cfb); +int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb); +int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb); +int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb); +int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb); +int cfb_done(symmetric_CFB *cfb); +#endif + +#ifdef LTC_OFB_MODE +int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key,  +              int keylen, int num_rounds, symmetric_OFB *ofb); +int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb); +int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb); +int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb); +int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb); +int ofb_done(symmetric_OFB *ofb); +#endif + +#ifdef LTC_CBC_MODE +int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, +               int keylen, int num_rounds, symmetric_CBC *cbc); +int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc); +int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc); +int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc); +int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc); +int cbc_done(symmetric_CBC *cbc); +#endif + +#ifdef LTC_CTR_MODE + +#define CTR_COUNTER_LITTLE_ENDIAN    0x0000 +#define CTR_COUNTER_BIG_ENDIAN       0x1000 +#define LTC_CTR_RFC3686              0x2000 + +int ctr_start(               int   cipher, +              const unsigned char *IV, +              const unsigned char *key,       int keylen, +                             int  num_rounds, int ctr_mode, +                   symmetric_CTR *ctr); +int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr); +int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr); +int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr); +int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr); +int ctr_done(symmetric_CTR *ctr); +int ctr_test(void); +#endif + +#ifdef LTC_LRW_MODE + +#define LRW_ENCRYPT 0 +#define LRW_DECRYPT 1 + +int lrw_start(               int   cipher, +              const unsigned char *IV, +              const unsigned char *key,       int keylen, +              const unsigned char *tweak, +                             int  num_rounds,  +                   symmetric_LRW *lrw); +int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw); +int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw); +int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw); +int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw); +int lrw_done(symmetric_LRW *lrw); +int lrw_test(void); + +/* don't call */ +int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw); +#endif     + +#ifdef LTC_F8_MODE +int f8_start(                int  cipher, const unsigned char *IV,  +             const unsigned char *key,                    int  keylen,  +             const unsigned char *salt_key,               int  skeylen, +                             int  num_rounds,   symmetric_F8  *f8); +int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8); +int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8); +int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8); +int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8); +int f8_done(symmetric_F8 *f8); +int f8_test_mode(void); +#endif + +#ifdef LTC_XTS_MODE +typedef struct { +   symmetric_key  key1, key2; +   int            cipher; +} symmetric_xts; + +int xts_start(                int  cipher, +              const unsigned char *key1,  +              const unsigned char *key2,  +                    unsigned long  keylen, +                              int  num_rounds,  +                    symmetric_xts *xts); + +int xts_encrypt( +   const unsigned char *pt, unsigned long ptlen, +         unsigned char *ct, +   const unsigned char *tweak, +         symmetric_xts *xts); +int xts_decrypt( +   const unsigned char *ct, unsigned long ptlen, +         unsigned char *pt, +   const unsigned char *tweak, +         symmetric_xts *xts); + +void xts_done(symmetric_xts *xts); +int  xts_test(void); +void xts_mult_x(unsigned char *I); +#endif + +int find_cipher(const char *name); +int find_cipher_any(const char *name, int blocklen, int keylen); +int find_cipher_id(unsigned char ID); +int register_cipher(const struct ltc_cipher_descriptor *cipher); +int unregister_cipher(const struct ltc_cipher_descriptor *cipher); +int cipher_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_cipher_mutex) + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */ +/* $Revision: 1.54 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_custom.h b/src/libtomcrypt/src/headers/tomcrypt_custom.h new file mode 100644 index 0000000..88ec8f9 --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_custom.h @@ -0,0 +1,424 @@ +#ifndef TOMCRYPT_CUSTOM_H_ +#define TOMCRYPT_CUSTOM_H_ + +#define LTC_NO_CIPHERS +#define LTC_NO_HASHES +#define LTC_NO_MACS +#define LTC_NO_PRNGS +#define LTC_NO_CURVES +#define LTC_NO_MODES +#define LTC_NO_PKCS +#define LTC_NO_ROLC + +#define LTC_SOURCE +#define LTC_SHA1 +#define LTC_MD5 +#define LTC_DER +#define LTC_RC4 + +#define USE_LTM +#define LTM_DESC + +/* macros for various libc functions you can change for embedded targets */ +#ifndef XMALLOC +   #ifdef malloc  +   #define LTC_NO_PROTOTYPES +   #endif +#define XMALLOC  LibTomMalloc +#endif +#ifndef XREALLOC +   #ifdef realloc  +   #define LTC_NO_PROTOTYPES +   #endif +#define XREALLOC LibTomRealloc +#endif +#ifndef XCALLOC +   #ifdef calloc  +   #define LTC_NO_PROTOTYPES +   #endif +#define XCALLOC  LibTomCalloc +#endif +#ifndef XFREE +   #ifdef free +   #define LTC_NO_PROTOTYPES +   #endif +#define XFREE    LibTomFree +#endif + +#ifndef XMEMSET +   #ifdef memset +   #define LTC_NO_PROTOTYPES +   #endif +#define XMEMSET  memset +#endif +#ifndef XMEMCPY +   #ifdef memcpy +   #define LTC_NO_PROTOTYPES +   #endif +#define XMEMCPY  memcpy +#endif +#ifndef XMEMCMP +   #ifdef memcmp  +   #define LTC_NO_PROTOTYPES +   #endif +#define XMEMCMP  memcmp +#endif +#ifndef XSTRCMP +   #ifdef strcmp +   #define LTC_NO_PROTOTYPES +   #endif +#define XSTRCMP strcmp +#endif + +#ifndef XCLOCK +#define XCLOCK   LibTomClock +#endif +#ifndef XCLOCKS_PER_SEC +#define XCLOCKS_PER_SEC CLOCKS_PER_SEC +#endif + +#ifndef XQSORT +   #ifdef qsort +   #define LTC_NO_PROTOTYPES +   #endif +#define XQSORT LibTomQsort +#endif + +/* Easy button? */ +#ifdef LTC_EASY +   #define LTC_NO_CIPHERS +   #define LTC_RIJNDAEL +   #define LTC_BLOWFISH +   #define LTC_DES +   #define LTC_CAST5 +    +   #define LTC_NO_MODES +   #define LTC_ECB_MODE +   #define LTC_CBC_MODE +   #define LTC_CTR_MODE +    +   #define LTC_NO_HASHES +   #define LTC_SHA1 +   #define LTC_SHA512 +   #define LTC_SHA384 +   #define LTC_SHA256 +   #define LTC_SHA224 +    +   #define LTC_NO_MACS +   #define LTC_HMAC +   #define LTC_OMAC +   #define LTC_CCM_MODE + +   #define LTC_NO_PRNGS +   #define LTC_SPRNG +   #define LTC_YARROW +   #define LTC_DEVRANDOM +   #define TRY_URANDOM_FIRST +       +   #define LTC_NO_PK +   #define LTC_MRSA +   #define LTC_MECC +#endif    + +/* Use small code where possible */ +/* #define LTC_SMALL_CODE */ + +/* Enable self-test test vector checking */ +#ifndef LTC_NO_TEST +   #define LTC_TEST +#endif + +/* clean the stack of functions which put private information on stack */ +/* #define LTC_CLEAN_STACK */ + +/* disable all file related functions */ +/* #define LTC_NO_FILE */ + +/* disable all forms of ASM */ +/* #define LTC_NO_ASM */ + +/* disable FAST mode */ +/* #define LTC_NO_FAST */ + +/* disable BSWAP on x86 */ +/* #define LTC_NO_BSWAP */ + +/* ---> Symmetric Block Ciphers <--- */ +#ifndef LTC_NO_CIPHERS + +#define LTC_BLOWFISH +#define LTC_RC2 +#define LTC_RC5 +#define LTC_RC6 +#define LTC_SAFERP +#define LTC_RIJNDAEL +#define LTC_XTEA +/* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format + * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */ +#define LTC_TWOFISH +#ifndef LTC_NO_TABLES +   #define LTC_TWOFISH_TABLES +   /* #define LTC_TWOFISH_ALL_TABLES */ +#else +   #define LTC_TWOFISH_SMALL +#endif +/* #define LTC_TWOFISH_SMALL */ +/* LTC_DES includes EDE triple-LTC_DES */ +#define LTC_DES +#define LTC_CAST5 +#define LTC_NOEKEON +#define LTC_SKIPJACK +#define LTC_SAFER +#define LTC_KHAZAD +#define LTC_ANUBIS +#define LTC_ANUBIS_TWEAK +#define LTC_KSEED +#define LTC_KASUMI + +#endif /* LTC_NO_CIPHERS */ + + +/* ---> Block Cipher Modes of Operation <--- */ +#ifndef LTC_NO_MODES + +#define LTC_CFB_MODE +#define LTC_OFB_MODE +#define LTC_ECB_MODE +#define LTC_CBC_MODE +#define LTC_CTR_MODE + +/* F8 chaining mode */ +#define LTC_F8_MODE + +/* LRW mode */ +#define LTC_LRW_MODE +#ifndef LTC_NO_TABLES +   /* like GCM mode this will enable 16 8x128 tables [64KB] that make +    * seeking very fast.   +    */ +   #define LRW_TABLES +#endif + +/* XTS mode */ +#define LTC_XTS_MODE + +#endif /* LTC_NO_MODES */ + +/* ---> One-Way Hash Functions <--- */ +#ifndef LTC_NO_HASHES  + +#define LTC_CHC_HASH +#define LTC_WHIRLPOOL +#define LTC_SHA512 +#define LTC_SHA384 +#define LTC_SHA256 +#define LTC_SHA224 +#define LTC_TIGER +#define LTC_SHA1 +#define LTC_MD5 +#define LTC_MD4 +#define LTC_MD2 +#define LTC_RIPEMD128 +#define LTC_RIPEMD160 +#define LTC_RIPEMD256 +#define LTC_RIPEMD320 + +#endif /* LTC_NO_HASHES */ + +/* ---> MAC functions <--- */ +#ifndef LTC_NO_MACS + +#define LTC_HMAC +#define LTC_OMAC +#define LTC_PMAC +#define LTC_XCBC +#define LTC_F9_MODE +#define LTC_PELICAN + +#if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL) +   #error Pelican-MAC requires LTC_RIJNDAEL +#endif + +/* ---> Encrypt + Authenticate Modes <--- */ + +#define LTC_EAX_MODE +#if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC)) +   #error LTC_EAX_MODE requires CTR and LTC_OMAC mode +#endif + +#define LTC_OCB_MODE +#define LTC_CCM_MODE +#define LTC_GCM_MODE + +/* Use 64KiB tables */ +#ifndef LTC_NO_TABLES +   #define LTC_GCM_TABLES  +#endif + +/* USE SSE2? requires GCC works on x86_32 and x86_64*/ +#ifdef LTC_GCM_TABLES +/* #define LTC_GCM_TABLES_SSE2 */ +#endif + +#endif /* LTC_NO_MACS */ + +/* Various tidbits of modern neatoness */ +#define LTC_BASE64 + +/* --> Pseudo Random Number Generators <--- */ +#ifndef LTC_NO_PRNGS + +/* Yarrow */ +#define LTC_YARROW +/* which descriptor of AES to use?  */ +/* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */ +#define LTC_YARROW_AES 0 + +#if defined(LTC_YARROW) && !defined(LTC_CTR_MODE) +   #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined! +#endif + +/* a PRNG that simply reads from an available system source */ +#define LTC_SPRNG + +/* The LTC_RC4 stream cipher */ +#define LTC_RC4 + +/* Fortuna PRNG */ +#define LTC_FORTUNA +/* reseed every N calls to the read function */ +#define LTC_FORTUNA_WD    10 +/* number of pools (4..32) can save a bit of ram by lowering the count */ +#define LTC_FORTUNA_POOLS 32 + +/* Greg's LTC_SOBER128 PRNG ;-0 */ +#define LTC_SOBER128 + +/* the *nix style /dev/random device */ +#define LTC_DEVRANDOM +/* try /dev/urandom before trying /dev/random */ +#define TRY_URANDOM_FIRST + +#endif /* LTC_NO_PRNGS */ + +/* ---> math provider? <--- */ +#ifndef LTC_NO_MATH + +/* LibTomMath */ +#define LTM_LTC_DESC + +/* TomsFastMath */ +//#define TFM_LTC_DESC + +#endif /* LTC_NO_MATH */ + +/* ---> Public Key Crypto <--- */ +#ifndef LTC_NO_PK + +/* Include RSA support */ +#define LTC_MRSA + +/* Include Katja (a Rabin variant like RSA) */ +/* #define MKAT */  + +/* Digital Signature Algorithm */ +#define LTC_MDSA + +/* ECC */ +#define LTC_MECC + +/* use Shamir's trick for point mul (speeds up signature verification) */ +#define LTC_ECC_SHAMIR + +#if defined(TFM_LTC_DESC) && defined(LTC_MECC) +   #define LTC_MECC_ACCEL +#endif    + +/* do we want fixed point ECC */ +/* #define LTC_MECC_FP */ + +/* Timing Resistant? */ +/* #define LTC_ECC_TIMING_RESISTANT */ + +#endif /* LTC_NO_PK */ + +/* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */ +#ifndef LTC_NO_PKCS + +#define LTC_PKCS_1 +#define LTC_PKCS_5 + +/* Include ASN.1 DER (required by DSA/RSA) */ +#define LTC_DER + +#endif /* LTC_NO_PKCS */ + +/* cleanup */ + +#ifdef LTC_MECC +/* Supported ECC Key Sizes */ +#ifndef LTC_NO_CURVES +   #define ECC112 +   #define ECC128 +   #define ECC160 +   #define ECC192 +   #define ECC224 +   #define ECC256 +   #define ECC384 +   #define ECC521 +#endif +#endif + +#if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA) +   /* Include the MPI functionality?  (required by the PK algorithms) */ +   #define MPI +#endif + +#ifdef LTC_MRSA +   #define LTC_PKCS_1 +#endif    + +#if defined(LTC_DER) && !defined(MPI)  +   #error ASN.1 DER requires MPI functionality +#endif + +#if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER) +   #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled +#endif + +/* THREAD management */ +#ifdef LTC_PTHREAD + +#include <pthread.h> + +#define LTC_MUTEX_GLOBAL(x)   pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; +#define LTC_MUTEX_PROTO(x)    extern pthread_mutex_t x; +#define LTC_MUTEX_TYPE(x)     pthread_mutex_t x; +#define LTC_MUTEX_INIT(x)     pthread_mutex_init(x, NULL); +#define LTC_MUTEX_LOCK(x)     pthread_mutex_lock(x); +#define LTC_MUTEX_UNLOCK(x)   pthread_mutex_unlock(x); + +#else + +/* default no functions */ +#define LTC_MUTEX_GLOBAL(x) +#define LTC_MUTEX_PROTO(x) +#define LTC_MUTEX_TYPE(x) +#define LTC_MUTEX_INIT(x) +#define LTC_MUTEX_LOCK(x) +#define LTC_MUTEX_UNLOCK(x) + +#endif + +/* Debuggers */ + +/* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */ +/* #define LTC_VALGRIND */ + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */ +/* $Revision: 1.73 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_hash.h b/src/libtomcrypt/src/headers/tomcrypt_hash.h new file mode 100644 index 0000000..18553eb --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_hash.h @@ -0,0 +1,378 @@ +/* ---- HASH FUNCTIONS ---- */ +#ifdef LTC_SHA512 +struct sha512_state { +    ulong64  length, state[8]; +    unsigned long curlen; +    unsigned char buf[128]; +}; +#endif + +#ifdef LTC_SHA256 +struct sha256_state { +    ulong64 length; +    ulong32 state[8], curlen; +    unsigned char buf[64]; +}; +#endif + +#ifdef LTC_SHA1 +struct sha1_state { +    ulong64 length; +    ulong32 state[5], curlen; +    unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD5 +struct md5_state { +    ulong64 length; +    ulong32 state[4], curlen; +    unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD4 +struct md4_state { +    ulong64 length; +    ulong32 state[4], curlen; +    unsigned char buf[64]; +}; +#endif + +#ifdef LTC_TIGER +struct tiger_state { +    ulong64 state[3], length; +    unsigned long curlen; +    unsigned char buf[64]; +}; +#endif + +#ifdef LTC_MD2 +struct md2_state { +    unsigned char chksum[16], X[48], buf[16]; +    unsigned long curlen; +}; +#endif + +#ifdef LTC_RIPEMD128 +struct rmd128_state { +    ulong64 length; +    unsigned char buf[64]; +    ulong32 curlen, state[4]; +}; +#endif + +#ifdef LTC_RIPEMD160 +struct rmd160_state { +    ulong64 length; +    unsigned char buf[64]; +    ulong32 curlen, state[5]; +}; +#endif + +#ifdef LTC_RIPEMD256 +struct rmd256_state { +    ulong64 length; +    unsigned char buf[64]; +    ulong32 curlen, state[8]; +}; +#endif + +#ifdef LTC_RIPEMD320 +struct rmd320_state { +    ulong64 length; +    unsigned char buf[64]; +    ulong32 curlen, state[10]; +}; +#endif + +#ifdef LTC_WHIRLPOOL +struct whirlpool_state { +    ulong64 length, state[8]; +    unsigned char buf[64]; +    ulong32 curlen; +}; +#endif + +#ifdef LTC_CHC_HASH +struct chc_state { +    ulong64 length; +    unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE]; +    ulong32 curlen; +}; +#endif + +typedef union Hash_state { +#ifdef LTC_CHC_HASH +    struct chc_state chc; +#endif +#ifdef LTC_WHIRLPOOL +    struct whirlpool_state whirlpool; +#endif +#ifdef LTC_SHA512 +    struct sha512_state sha512; +#endif +#ifdef LTC_SHA256 +    struct sha256_state sha256; +#endif +#ifdef LTC_SHA1 +    struct sha1_state   sha1; +#endif +#ifdef LTC_MD5 +    struct md5_state    md5; +#endif +#ifdef LTC_MD4 +    struct md4_state    md4; +#endif +#ifdef LTC_MD2 +    struct md2_state    md2; +#endif +#ifdef LTC_TIGER +    struct tiger_state  tiger; +#endif +#ifdef LTC_RIPEMD128 +    struct rmd128_state rmd128; +#endif +#ifdef LTC_RIPEMD160 +    struct rmd160_state rmd160; +#endif +#ifdef LTC_RIPEMD256 +    struct rmd256_state rmd256; +#endif +#ifdef LTC_RIPEMD320 +    struct rmd320_state rmd320; +#endif +    void *data; +} hash_state; + +/** hash descriptor */ +extern  struct ltc_hash_descriptor { +    /** name of hash */ +    char *name; +    /** internal ID */ +    unsigned char ID; +    /** Size of digest in octets */ +    unsigned long hashsize; +    /** Input block size in octets */ +    unsigned long blocksize; +    /** ASN.1 OID */ +    unsigned long OID[16]; +    /** Length of DER encoding */ +    unsigned long OIDlen; + +    /** Init a hash state +      @param hash   The hash to initialize +      @return CRYPT_OK if successful +    */ +    int (*init)(hash_state *hash); +    /** Process a block of data  +      @param hash   The hash state +      @param in     The data to hash +      @param inlen  The length of the data (octets) +      @return CRYPT_OK if successful +    */ +    int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen); +    /** Produce the digest and store it +      @param hash   The hash state +      @param out    [out] The destination of the digest +      @return CRYPT_OK if successful +    */ +    int (*done)(hash_state *hash, unsigned char *out); +    /** Self-test +      @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +    */ +    int (*test)(void); + +    /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */ +    int  (*hmac_block)(const unsigned char *key, unsigned long  keylen, +                       const unsigned char *in,  unsigned long  inlen,  +                             unsigned char *out, unsigned long *outlen); + +} hash_descriptor[]; + +#ifdef LTC_CHC_HASH +int chc_register(int cipher); +int chc_init(hash_state * md); +int chc_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int chc_done(hash_state * md, unsigned char *hash); +int chc_test(void); +extern const struct ltc_hash_descriptor chc_desc; +#endif + +#ifdef LTC_WHIRLPOOL +int whirlpool_init(hash_state * md); +int whirlpool_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int whirlpool_done(hash_state * md, unsigned char *hash); +int whirlpool_test(void); +extern const struct ltc_hash_descriptor whirlpool_desc; +#endif + +#ifdef LTC_SHA512 +int sha512_init(hash_state * md); +int sha512_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha512_done(hash_state * md, unsigned char *hash); +int sha512_test(void); +extern const struct ltc_hash_descriptor sha512_desc; +#endif + +#ifdef LTC_SHA384 +#ifndef LTC_SHA512 +   #error LTC_SHA512 is required for LTC_SHA384 +#endif +int sha384_init(hash_state * md); +#define sha384_process sha512_process +int sha384_done(hash_state * md, unsigned char *hash); +int sha384_test(void); +extern const struct ltc_hash_descriptor sha384_desc; +#endif + +#ifdef LTC_SHA256 +int sha256_init(hash_state * md); +int sha256_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha256_done(hash_state * md, unsigned char *hash); +int sha256_test(void); +extern const struct ltc_hash_descriptor sha256_desc; + +#ifdef LTC_SHA224 +#ifndef LTC_SHA256 +   #error LTC_SHA256 is required for LTC_SHA224 +#endif +int sha224_init(hash_state * md); +#define sha224_process sha256_process +int sha224_done(hash_state * md, unsigned char *hash); +int sha224_test(void); +extern const struct ltc_hash_descriptor sha224_desc; +#endif +#endif + +#ifdef LTC_SHA1 +int sha1_init(hash_state * md); +int sha1_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int sha1_done(hash_state * md, unsigned char *hash); +int sha1_test(void); +extern const struct ltc_hash_descriptor sha1_desc; +#endif + +#ifdef LTC_MD5 +int md5_init(hash_state * md); +int md5_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md5_done(hash_state * md, unsigned char *hash); +int md5_test(void); +extern const struct ltc_hash_descriptor md5_desc; +#endif + +#ifdef LTC_MD4 +int md4_init(hash_state * md); +int md4_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md4_done(hash_state * md, unsigned char *hash); +int md4_test(void); +extern const struct ltc_hash_descriptor md4_desc; +#endif + +#ifdef LTC_MD2 +int md2_init(hash_state * md); +int md2_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int md2_done(hash_state * md, unsigned char *hash); +int md2_test(void); +extern const struct ltc_hash_descriptor md2_desc; +#endif + +#ifdef LTC_TIGER +int tiger_init(hash_state * md); +int tiger_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int tiger_done(hash_state * md, unsigned char *hash); +int tiger_test(void); +extern const struct ltc_hash_descriptor tiger_desc; +#endif + +#ifdef LTC_RIPEMD128 +int rmd128_init(hash_state * md); +int rmd128_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd128_done(hash_state * md, unsigned char *hash); +int rmd128_test(void); +extern const struct ltc_hash_descriptor rmd128_desc; +#endif + +#ifdef LTC_RIPEMD160 +int rmd160_init(hash_state * md); +int rmd160_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd160_done(hash_state * md, unsigned char *hash); +int rmd160_test(void); +extern const struct ltc_hash_descriptor rmd160_desc; +#endif + +#ifdef LTC_RIPEMD256 +int rmd256_init(hash_state * md); +int rmd256_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd256_done(hash_state * md, unsigned char *hash); +int rmd256_test(void); +extern const struct ltc_hash_descriptor rmd256_desc; +#endif + +#ifdef LTC_RIPEMD320 +int rmd320_init(hash_state * md); +int rmd320_process(hash_state * md, const unsigned char *in, unsigned long inlen); +int rmd320_done(hash_state * md, unsigned char *hash); +int rmd320_test(void); +extern const struct ltc_hash_descriptor rmd320_desc; +#endif + + +int find_hash(const char *name); +int find_hash_id(unsigned char ID); +int find_hash_oid(const unsigned long *ID, unsigned long IDlen); +int find_hash_any(const char *name, int digestlen); +int register_hash(const struct ltc_hash_descriptor *hash); +int unregister_hash(const struct ltc_hash_descriptor *hash); +int hash_is_valid(int idx); + +LTC_MUTEX_PROTO(ltc_hash_mutex) + +int hash_memory(int hash,  +                const unsigned char *in,  unsigned long inlen,  +                      unsigned char *out, unsigned long *outlen); +int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, +                      const unsigned char *in, unsigned long inlen, ...); +int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen); +int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen); + +/* a simple macro for making hash "process" functions */ +#define HASH_PROCESS(func_name, compress_name, state_var, block_size)                       \ +int func_name (hash_state * md, const unsigned char *in, unsigned long inlen)               \ +{                                                                                           \ +    unsigned long n;                                                                        \ +    int           err;                                                                      \ +    LTC_ARGCHK(md != NULL);                                                                 \ +    LTC_ARGCHK(in != NULL);                                                                 \ +    if (md-> state_var .curlen > sizeof(md-> state_var .buf)) {                             \ +       return CRYPT_INVALID_ARG;                                                            \ +    }                                                                                       \ +    while (inlen > 0) {                                                                     \ +        if (md-> state_var .curlen == 0 && inlen >= block_size) {                           \ +           if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) {               \ +              return err;                                                                   \ +           }                                                                                \ +           md-> state_var .length += block_size * 8;                                        \ +           in             += block_size;                                                    \ +           inlen          -= block_size;                                                    \ +        } else {                                                                            \ +           n = MIN(inlen, (block_size - md-> state_var .curlen));                           \ +           memcpy(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n);              \ +           md-> state_var .curlen += n;                                                     \ +           in             += n;                                                             \ +           inlen          -= n;                                                             \ +           if (md-> state_var .curlen == block_size) {                                      \ +              if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) {            \ +                 return err;                                                                \ +              }                                                                             \ +              md-> state_var .length += 8*block_size;                                       \ +              md-> state_var .curlen = 0;                                                   \ +           }                                                                                \ +       }                                                                                    \ +    }                                                                                       \ +    return CRYPT_OK;                                                                        \ +} + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */ +/* $Revision: 1.22 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_mac.h b/src/libtomcrypt/src/headers/tomcrypt_mac.h new file mode 100644 index 0000000..7ad9516 --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_mac.h @@ -0,0 +1,384 @@ +#ifdef LTC_HMAC +typedef struct Hmac_state { +     hash_state     md; +     int            hash; +     hash_state     hashstate; +     unsigned char  *key; +} hmac_state; + +int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); +int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); +int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); +int hmac_test(void); +int hmac_memory(int hash,  +                const unsigned char *key, unsigned long keylen, +                const unsigned char *in,  unsigned long inlen,  +                      unsigned char *out, unsigned long *outlen); +int hmac_memory_multi(int hash,  +                const unsigned char *key,  unsigned long keylen, +                      unsigned char *out,  unsigned long *outlen, +                const unsigned char *in,   unsigned long inlen, ...); +int hmac_file(int hash, const char *fname, const unsigned char *key, +              unsigned long keylen,  +              unsigned char *dst, unsigned long *dstlen); +#endif + +#ifdef LTC_OMAC + +typedef struct { +   int             cipher_idx,  +                   buflen, +                   blklen; +   unsigned char   block[MAXBLOCKSIZE], +                   prev[MAXBLOCKSIZE], +                   Lu[2][MAXBLOCKSIZE]; +   symmetric_key   key; +} omac_state; + +int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen); +int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen); +int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen); +int omac_memory(int cipher,  +               const unsigned char *key, unsigned long keylen, +               const unsigned char *in,  unsigned long inlen, +                     unsigned char *out, unsigned long *outlen); +int omac_memory_multi(int cipher,  +                const unsigned char *key, unsigned long keylen, +                      unsigned char *out, unsigned long *outlen, +                const unsigned char *in,  unsigned long inlen, ...); +int omac_file(int cipher,  +              const unsigned char *key, unsigned long keylen, +              const          char *filename,  +                    unsigned char *out, unsigned long *outlen); +int omac_test(void); +#endif /* LTC_OMAC */ + +#ifdef LTC_PMAC + +typedef struct { +   unsigned char     Ls[32][MAXBLOCKSIZE],    /* L shifted by i bits to the left */ +                     Li[MAXBLOCKSIZE],        /* value of Li [current value, we calc from previous recall] */ +                     Lr[MAXBLOCKSIZE],        /* L * x^-1 */ +                     block[MAXBLOCKSIZE],     /* currently accumulated block */ +                     checksum[MAXBLOCKSIZE];  /* current checksum */ + +   symmetric_key     key;                     /* scheduled key for cipher */ +   unsigned long     block_index;             /* index # for current block */ +   int               cipher_idx,              /* cipher idx */ +                     block_len,               /* length of block */ +                     buflen;                  /* number of bytes in the buffer */ +} pmac_state; + +int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen); +int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen); +int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen); + +int pmac_memory(int cipher,  +               const unsigned char *key, unsigned long keylen, +               const unsigned char *msg, unsigned long msglen, +                     unsigned char *out, unsigned long *outlen); + +int pmac_memory_multi(int cipher,  +                const unsigned char *key, unsigned long keylen, +                      unsigned char *out, unsigned long *outlen, +                const unsigned char *in, unsigned long inlen, ...); + +int pmac_file(int cipher,  +             const unsigned char *key, unsigned long keylen, +             const          char *filename,  +                   unsigned char *out, unsigned long *outlen); + +int pmac_test(void); + +/* internal functions */ +int pmac_ntz(unsigned long x); +void pmac_shift_xor(pmac_state *pmac); + +#endif /* PMAC */ + +#ifdef LTC_EAX_MODE + +#if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE)) +   #error LTC_EAX_MODE requires LTC_OMAC and CTR +#endif + +typedef struct { +   unsigned char N[MAXBLOCKSIZE]; +   symmetric_CTR ctr; +   omac_state    headeromac, ctomac; +} eax_state; + +int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen, +             const unsigned char *nonce, unsigned long noncelen, +             const unsigned char *header, unsigned long headerlen); + +int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length); +int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length); +int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length); +int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen); + +int eax_encrypt_authenticate_memory(int cipher, +    const unsigned char *key,    unsigned long keylen, +    const unsigned char *nonce,  unsigned long noncelen, +    const unsigned char *header, unsigned long headerlen, +    const unsigned char *pt,     unsigned long ptlen, +          unsigned char *ct, +          unsigned char *tag,    unsigned long *taglen); + +int eax_decrypt_verify_memory(int cipher, +    const unsigned char *key,    unsigned long keylen, +    const unsigned char *nonce,  unsigned long noncelen, +    const unsigned char *header, unsigned long headerlen, +    const unsigned char *ct,     unsigned long ctlen, +          unsigned char *pt, +          unsigned char *tag,    unsigned long taglen, +          int           *stat); + + int eax_test(void); +#endif /* EAX MODE */ + +#ifdef LTC_OCB_MODE +typedef struct { +   unsigned char     L[MAXBLOCKSIZE],         /* L value */ +                     Ls[32][MAXBLOCKSIZE],    /* L shifted by i bits to the left */ +                     Li[MAXBLOCKSIZE],        /* value of Li [current value, we calc from previous recall] */ +                     Lr[MAXBLOCKSIZE],        /* L * x^-1 */ +                     R[MAXBLOCKSIZE],         /* R value */ +                     checksum[MAXBLOCKSIZE];  /* current checksum */ + +   symmetric_key     key;                     /* scheduled key for cipher */ +   unsigned long     block_index;             /* index # for current block */ +   int               cipher,                  /* cipher idx */ +                     block_len;               /* length of block */ +} ocb_state; + +int ocb_init(ocb_state *ocb, int cipher,  +             const unsigned char *key, unsigned long keylen, const unsigned char *nonce); + +int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct); +int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt); + +int ocb_done_encrypt(ocb_state *ocb,  +                     const unsigned char *pt,  unsigned long ptlen, +                           unsigned char *ct,  +                           unsigned char *tag, unsigned long *taglen); + +int ocb_done_decrypt(ocb_state *ocb,  +                     const unsigned char *ct,  unsigned long ctlen, +                           unsigned char *pt,  +                     const unsigned char *tag, unsigned long taglen, int *stat); + +int ocb_encrypt_authenticate_memory(int cipher, +    const unsigned char *key,    unsigned long keylen, +    const unsigned char *nonce,   +    const unsigned char *pt,     unsigned long ptlen, +          unsigned char *ct, +          unsigned char *tag,    unsigned long *taglen); + +int ocb_decrypt_verify_memory(int cipher, +    const unsigned char *key,    unsigned long keylen, +    const unsigned char *nonce,   +    const unsigned char *ct,     unsigned long ctlen, +          unsigned char *pt, +    const unsigned char *tag,    unsigned long taglen, +          int           *stat); + +int ocb_test(void); + +/* internal functions */ +void ocb_shift_xor(ocb_state *ocb, unsigned char *Z); +int ocb_ntz(unsigned long x); +int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, +               unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode); + +#endif /* LTC_OCB_MODE */ + +#ifdef LTC_CCM_MODE + +#define CCM_ENCRYPT 0 +#define CCM_DECRYPT 1 + +int ccm_memory(int cipher, +    const unsigned char *key,    unsigned long keylen, +    symmetric_key       *uskey, +    const unsigned char *nonce,  unsigned long noncelen, +    const unsigned char *header, unsigned long headerlen, +          unsigned char *pt,     unsigned long ptlen, +          unsigned char *ct, +          unsigned char *tag,    unsigned long *taglen, +                    int  direction); + +int ccm_test(void); + +#endif /* LTC_CCM_MODE */ + +#if defined(LRW_MODE) || defined(LTC_GCM_MODE) +void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); +#endif + + +/* table shared between GCM and LRW */ +#if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) +extern const unsigned char gcm_shift_table[]; +#endif + +#ifdef LTC_GCM_MODE + +#define GCM_ENCRYPT 0 +#define GCM_DECRYPT 1 + +#define LTC_GCM_MODE_IV    0 +#define LTC_GCM_MODE_AAD   1 +#define LTC_GCM_MODE_TEXT  2 + +typedef struct {  +   symmetric_key       K; +   unsigned char       H[16],        /* multiplier */ +                       X[16],        /* accumulator */ +                       Y[16],        /* counter */ +                       Y_0[16],      /* initial counter */ +                       buf[16];      /* buffer for stuff */ + +   int                 cipher,       /* which cipher */ +                       ivmode,       /* Which mode is the IV in? */ +                       mode,         /* mode the GCM code is in */ +                       buflen;       /* length of data in buf */ + +   ulong64             totlen,       /* 64-bit counter used for IV and AAD */ +                       pttotlen;     /* 64-bit counter for the PT */ + +#ifdef LTC_GCM_TABLES +   unsigned char       PC[16][256][16]  /* 16 tables of 8x128 */ +#ifdef LTC_GCM_TABLES_SSE2 +__attribute__ ((aligned (16))) +#endif +; +#endif   +} gcm_state; + +void gcm_mult_h(gcm_state *gcm, unsigned char *I); + +int gcm_init(gcm_state *gcm, int cipher, +             const unsigned char *key, int keylen); + +int gcm_reset(gcm_state *gcm); + +int gcm_add_iv(gcm_state *gcm,  +               const unsigned char *IV,     unsigned long IVlen); + +int gcm_add_aad(gcm_state *gcm, +               const unsigned char *adata,  unsigned long adatalen); + +int gcm_process(gcm_state *gcm, +                     unsigned char *pt,     unsigned long ptlen, +                     unsigned char *ct, +                     int direction); + +int gcm_done(gcm_state *gcm,  +                     unsigned char *tag,    unsigned long *taglen); + +int gcm_memory(      int           cipher, +               const unsigned char *key,    unsigned long keylen, +               const unsigned char *IV,     unsigned long IVlen, +               const unsigned char *adata,  unsigned long adatalen, +                     unsigned char *pt,     unsigned long ptlen, +                     unsigned char *ct,  +                     unsigned char *tag,    unsigned long *taglen, +                               int direction); +int gcm_test(void); + +#endif /* LTC_GCM_MODE */ + +#ifdef LTC_PELICAN + +typedef struct pelican_state +{ +    symmetric_key K; +    unsigned char state[16]; +    int           buflen; +} pelican_state; + +int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen); +int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen); +int pelican_done(pelican_state *pelmac, unsigned char *out); +int pelican_test(void); + +int pelican_memory(const unsigned char *key, unsigned long keylen, +                   const unsigned char *in, unsigned long inlen, +                         unsigned char *out); + +#endif + +#ifdef LTC_XCBC + +/* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */ +#define LTC_XCBC_PURE  0x8000UL + +typedef struct { +   unsigned char K[3][MAXBLOCKSIZE], +                 IV[MAXBLOCKSIZE]; + +   symmetric_key key; + +             int cipher, +                 buflen, +                 blocksize; +} xcbc_state; + +int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen); +int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen); +int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen); +int xcbc_memory(int cipher,  +               const unsigned char *key, unsigned long keylen, +               const unsigned char *in,  unsigned long inlen, +                     unsigned char *out, unsigned long *outlen); +int xcbc_memory_multi(int cipher,  +                const unsigned char *key, unsigned long keylen, +                      unsigned char *out, unsigned long *outlen, +                const unsigned char *in,  unsigned long inlen, ...); +int xcbc_file(int cipher,  +              const unsigned char *key, unsigned long keylen, +              const          char *filename,  +                    unsigned char *out, unsigned long *outlen); +int xcbc_test(void); + +#endif + +#ifdef LTC_F9_MODE + +typedef struct { +   unsigned char akey[MAXBLOCKSIZE], +                 ACC[MAXBLOCKSIZE], +                 IV[MAXBLOCKSIZE]; + +   symmetric_key key; + +             int cipher, +                 buflen, +                 keylen, +                 blocksize; +} f9_state; + +int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen); +int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen); +int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen); +int f9_memory(int cipher,  +               const unsigned char *key, unsigned long keylen, +               const unsigned char *in,  unsigned long inlen, +                     unsigned char *out, unsigned long *outlen); +int f9_memory_multi(int cipher,  +                const unsigned char *key, unsigned long keylen, +                      unsigned char *out, unsigned long *outlen, +                const unsigned char *in,  unsigned long inlen, ...); +int f9_file(int cipher,  +              const unsigned char *key, unsigned long keylen, +              const          char *filename,  +                    unsigned char *out, unsigned long *outlen); +int f9_test(void); + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:37:41 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_macros.h b/src/libtomcrypt/src/headers/tomcrypt_macros.h new file mode 100644 index 0000000..53bda9b --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_macros.h @@ -0,0 +1,424 @@ +/* fix for MSVC ...evil! */ +#ifdef _MSC_VER +   #define CONST64(n) n ## ui64 +   typedef unsigned __int64 ulong64; +#else +   #define CONST64(n) n ## ULL +   typedef unsigned long long ulong64; +#endif + +/* this is the "32-bit at least" data type  + * Re-define it to suit your platform but it must be at least 32-bits  + */ +#if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) +   typedef unsigned ulong32; +#else +   typedef unsigned long ulong32; +#endif + +/* ---- HELPER MACROS ---- */ +#ifdef ENDIAN_NEUTRAL + +#define STORE32L(x, y)                                                                     \ +     { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \ +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y)                            \ +     { x = ((unsigned long)((y)[3] & 255)<<24) | \ +           ((unsigned long)((y)[2] & 255)<<16) | \ +           ((unsigned long)((y)[1] & 255)<<8)  | \ +           ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y)                                                                     \ +     { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);   \ +       (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);   \ +       (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \ +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y)                                                       \ +     { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ +           (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ +           (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ +           (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#define STORE32H(x, y)                                                                     \ +     { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \ +       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y)                            \ +     { x = ((unsigned long)((y)[0] & 255)<<24) | \ +           ((unsigned long)((y)[1] & 255)<<16) | \ +           ((unsigned long)((y)[2] & 255)<<8)  | \ +           ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y)                                                                     \ +   { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \ +     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \ +     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \ +     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y)                                                      \ +   { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ +         (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ +         (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ +         (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif /* ENDIAN_NEUTRAL */ + +#ifdef ENDIAN_LITTLE + +#if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) + +#define STORE32H(x, y)           \ +asm __volatile__ (               \ +   "bswapl %0     \n\t"          \ +   "movl   %0,(%1)\n\t"          \ +   "bswapl %0     \n\t"          \ +      ::"r"(x), "r"(y)); + +#define LOAD32H(x, y)          \ +asm __volatile__ (             \ +   "movl (%1),%0\n\t"          \ +   "bswapl %0\n\t"             \ +   :"=r"(x): "r"(y)); + +#else + +#define STORE32H(x, y)                                                                     \ +     { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \ +       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define LOAD32H(x, y)                            \ +     { x = ((unsigned long)((y)[0] & 255)<<24) | \ +           ((unsigned long)((y)[1] & 255)<<16) | \ +           ((unsigned long)((y)[2] & 255)<<8)  | \ +           ((unsigned long)((y)[3] & 255)); } + +#endif + + +/* x86_64 processor */ +#if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) + +#define STORE64H(x, y)           \ +asm __volatile__ (               \ +   "bswapq %0     \n\t"          \ +   "movq   %0,(%1)\n\t"          \ +   "bswapq %0     \n\t"          \ +      ::"r"(x), "r"(y)); + +#define LOAD64H(x, y)          \ +asm __volatile__ (             \ +   "movq (%1),%0\n\t"          \ +   "bswapq %0\n\t"             \ +   :"=r"(x): "r"(y)); + +#else + +#define STORE64H(x, y)                                                                     \ +   { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \ +     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \ +     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \ +     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y)                                                      \ +   { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48) | \ +         (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32) | \ +         (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16) | \ +         (((ulong64)((y)[6] & 255))<<8)|(((ulong64)((y)[7] & 255))); } + +#endif + +#ifdef ENDIAN_32BITWORD  + +#define STORE32L(x, y)        \ +     { ulong32  __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y)         \ +     XMEMCPY(&(x), y, 4); + +#define STORE64L(x, y)                                                                     \ +     { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);   \ +       (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);   \ +       (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \ +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y)                                                       \ +     { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48)| \ +           (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32)| \ +           (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16)| \ +           (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#else /* 64-bit words then  */ + +#define STORE32L(x, y)        \ +     { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32L(x, y)         \ +     { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64L(x, y)        \ +     { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64L(x, y)         \ +    { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ + +#endif /* ENDIAN_LITTLE */ + +#ifdef ENDIAN_BIG +#define STORE32L(x, y)                                                                     \ +     { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \ +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD32L(x, y)                            \ +     { x = ((unsigned long)((y)[3] & 255)<<24) | \ +           ((unsigned long)((y)[2] & 255)<<16) | \ +           ((unsigned long)((y)[1] & 255)<<8)  | \ +           ((unsigned long)((y)[0] & 255)); } + +#define STORE64L(x, y)                                                                     \ +   { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);     \ +     (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);     \ +     (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);     \ +     (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } + +#define LOAD64L(x, y)                                                      \ +   { x = (((ulong64)((y)[7] & 255))<<56)|(((ulong64)((y)[6] & 255))<<48) | \ +         (((ulong64)((y)[5] & 255))<<40)|(((ulong64)((y)[4] & 255))<<32) | \ +         (((ulong64)((y)[3] & 255))<<24)|(((ulong64)((y)[2] & 255))<<16) | \ +         (((ulong64)((y)[1] & 255))<<8)|(((ulong64)((y)[0] & 255))); } + +#ifdef ENDIAN_32BITWORD  + +#define STORE32H(x, y)        \ +     { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y)         \ +     XMEMCPY(&(x), y, 4); + +#define STORE64H(x, y)                                                                     \ +     { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);   \ +       (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);   \ +       (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);   \ +       (y)[6] = (unsigned char)(((x)>>8)&255);  (y)[7] = (unsigned char)((x)&255); } + +#define LOAD64H(x, y)                                                       \ +     { x = (((ulong64)((y)[0] & 255))<<56)|(((ulong64)((y)[1] & 255))<<48)| \ +           (((ulong64)((y)[2] & 255))<<40)|(((ulong64)((y)[3] & 255))<<32)| \ +           (((ulong64)((y)[4] & 255))<<24)|(((ulong64)((y)[5] & 255))<<16)| \ +           (((ulong64)((y)[6] & 255))<<8)| (((ulong64)((y)[7] & 255))); } + +#else /* 64-bit words then  */ + +#define STORE32H(x, y)        \ +     { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } + +#define LOAD32H(x, y)         \ +     { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } + +#define STORE64H(x, y)        \ +     { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } + +#define LOAD64H(x, y)         \ +    { XMEMCPY(&(x), y, 8); } + +#endif /* ENDIAN_64BITWORD */ +#endif /* ENDIAN_BIG */ + +#define BSWAP(x)  ( ((x>>24)&0x000000FFUL) | ((x<<24)&0xFF000000UL)  | \ +                    ((x>>8)&0x0000FF00UL)  | ((x<<8)&0x00FF0000UL) ) + + +/* 32-bit Rotates */ +#if defined(_MSC_VER) + +/* instrinsic rotate */ +#include <stdlib.h> +#pragma intrinsic(_lrotr,_lrotl) +#define ROR(x,n) _lrotr(x,n) +#define ROL(x,n) _lrotl(x,n) +#define RORc(x,n) _lrotr(x,n) +#define ROLc(x,n) _lrotl(x,n) + +#elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) + +static inline unsigned ROL(unsigned word, int i) +{ +   asm ("roll %%cl,%0" +      :"=r" (word) +      :"0" (word),"c" (i)); +   return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ +   asm ("rorl %%cl,%0" +      :"=r" (word) +      :"0" (word),"c" (i)); +   return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ +   asm ("roll %2,%0" +      :"=r" (word) +      :"0" (word),"I" (i)); +   return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ +   asm ("rorl %2,%0" +      :"=r" (word) +      :"0" (word),"I" (i)); +   return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + +#elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) + +static inline unsigned ROL(unsigned word, int i) +{ +   asm ("rotlw %0,%0,%2" +      :"=r" (word) +      :"0" (word),"r" (i)); +   return word; +} + +static inline unsigned ROR(unsigned word, int i) +{ +   asm ("rotlw %0,%0,%2" +      :"=r" (word) +      :"0" (word),"r" (32-i)); +   return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned ROLc(unsigned word, const int i) +{ +   asm ("rotlwi %0,%0,%2" +      :"=r" (word) +      :"0" (word),"I" (i)); +   return word; +} + +static inline unsigned RORc(unsigned word, const int i) +{ +   asm ("rotrwi %0,%0,%2" +      :"=r" (word) +      :"0" (word),"I" (i)); +   return word; +} + +#else + +#define ROLc ROL +#define RORc ROR + +#endif + + +#else + +/* rotates the hard way */ +#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) + +#endif + + +/* 64-bit Rotates */ +#if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) + +static inline unsigned long ROL64(unsigned long word, int i) +{ +   asm("rolq %%cl,%0" +      :"=r" (word) +      :"0" (word),"c" (i)); +   return word; +} + +static inline unsigned long ROR64(unsigned long word, int i) +{ +   asm("rorq %%cl,%0" +      :"=r" (word) +      :"0" (word),"c" (i)); +   return word; +} + +#ifndef LTC_NO_ROLC + +static inline unsigned long ROL64c(unsigned long word, const int i) +{ +   asm("rolq %2,%0" +      :"=r" (word) +      :"0" (word),"J" (i)); +   return word; +} + +static inline unsigned long ROR64c(unsigned long word, const int i) +{ +   asm("rorq %2,%0" +      :"=r" (word) +      :"0" (word),"J" (i)); +   return word; +} + +#else /* LTC_NO_ROLC */ + +#define ROL64c ROL64 +#define ROR64c ROR64 + +#endif + +#else /* Not x86_64  */ + +#define ROL64(x, y) \ +    ( (((x)<<((ulong64)(y)&63)) | \ +      (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64(x, y) \ +    ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ +      ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROL64c(x, y) \ +    ( (((x)<<((ulong64)(y)&63)) | \ +      (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)64-((y)&63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#define ROR64c(x, y) \ +    ( ((((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)(y)&CONST64(63))) | \ +      ((x)<<((ulong64)(64-((y)&CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) + +#endif + +#ifndef MAX +   #define MAX(x, y) ( ((x)>(y))?(x):(y) ) +#endif + +#ifndef MIN +   #define MIN(x, y) ( ((x)<(y))?(x):(y) ) +#endif + +/* extract a byte portably */ +#ifdef _MSC_VER +   #define byte(x, n) ((unsigned char)((x) >> (8 * (n)))) +#else +   #define byte(x, n) (((x) >> (8 * (n))) & 255) +#endif    + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ +/* $Revision: 1.15 $ */ +/* $Date: 2006/11/29 23:43:57 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_math.h b/src/libtomcrypt/src/headers/tomcrypt_math.h new file mode 100644 index 0000000..a05d7ff --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_math.h @@ -0,0 +1,500 @@ +/** math functions **/ + +#define LTC_MP_LT   -1 +#define LTC_MP_EQ    0 +#define LTC_MP_GT    1 + +#define LTC_MP_NO    0 +#define LTC_MP_YES   1 + +#ifndef LTC_MECC +   typedef void ecc_point; +#endif + +#ifndef LTC_MRSA +   typedef void rsa_key; +#endif + +/** math descriptor */ +typedef struct { +   /** Name of the math provider */ +   char *name; + +   /** Bits per digit, amount of bits must fit in an unsigned long */ +   int  bits_per_digit; + +/* ---- init/deinit functions ---- */ + +   /** initialize a bignum +     @param   a     The number to initialize +     @return  CRYPT_OK on success +   */ +   int (*init)(void **a); +    +   /** init copy  +     @param  dst    The number to initialize and write to +     @param  src    The number to copy from +     @return CRYPT_OK on success +   */ +   int (*init_copy)(void **dst, void *src); + +   /** deinit  +      @param   a    The number to free +      @return CRYPT_OK on success +   */ +   void (*deinit)(void *a); + +/* ---- data movement ---- */ + +   /** negate +      @param   src   The number to negate +      @param   dst   The destination +      @return CRYPT_OK on success +   */ +   int (*neg)(void *src, void *dst); +    +   /** copy  +      @param   src   The number to copy from +      @param   dst   The number to write to  +      @return CRYPT_OK on success +   */ +   int (*copy)(void *src, void *dst); + +/* ---- trivial low level functions ---- */ + +   /** set small constant  +      @param a    Number to write to +      @param n    Source upto bits_per_digit (actually meant for very small constants)  +      @return CRYPT_OK on succcess +   */ +   int (*set_int)(void *a, unsigned long n); + +   /** get small constant  +      @param a    Number to read, only fetches upto bits_per_digit from the number +      @return  The lower bits_per_digit of the integer (unsigned) +   */ +   unsigned long (*get_int)(void *a); + +   /** get digit n  +     @param a  The number to read from +     @param n  The number of the digit to fetch +     @return  The bits_per_digit  sized n'th digit of a +   */ +   unsigned long (*get_digit)(void *a, int n); + +   /** Get the number of digits that represent the number +     @param a   The number to count +     @return The number of digits used to represent the number +   */ +   int (*get_digit_count)(void *a); + +   /** compare two integers +     @param a   The left side integer +     @param b   The right side integer +     @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise.  (signed comparison) +   */ +   int (*compare)(void *a, void *b); + +   /** compare against int  +     @param a   The left side integer +     @param b   The right side integer (upto bits_per_digit) +     @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise.  (signed comparison) +   */ +   int (*compare_d)(void *a, unsigned long n); + +   /** Count the number of bits used to represent the integer +     @param a   The integer to count +     @return The number of bits required to represent the integer +   */ +   int (*count_bits)(void * a); + +   /** Count the number of LSB bits which are zero  +     @param a   The integer to count +     @return The number of contiguous zero LSB bits +   */ +   int (*count_lsb_bits)(void *a); + +   /** Compute a power of two +     @param a  The integer to store the power in +     @param n  The power of two you want to store (a = 2^n) +     @return CRYPT_OK on success +   */ +   int (*twoexpt)(void *a , int n); + +/* ---- radix conversions ---- */ +    +   /** read ascii string  +     @param a     The integer to store into +     @param str   The string to read +     @param radix The radix the integer has been represented in (2-64) +     @return CRYPT_OK on success +   */ +   int (*read_radix)(void *a, const char *str, int radix); + +   /** write number to string +     @param a     The integer to store +     @param str   The destination for the string +     @param radix The radix the integer is to be represented in (2-64) +     @return CRYPT_OK on success +   */ +   int (*write_radix)(void *a, char *str, int radix); + +   /** get size as unsigned char string  +     @param a     The integer to get the size (when stored in array of octets) +     @return The length of the integer +   */ +   unsigned long (*unsigned_size)(void *a); + +   /** store an integer as an array of octets  +     @param src   The integer to store +     @param dst   The buffer to store the integer in +     @return CRYPT_OK on success +   */ +   int (*unsigned_write)(void *src, unsigned char *dst); + +   /** read an array of octets and store as integer +     @param dst   The integer to load +     @param src   The array of octets  +     @param len   The number of octets  +     @return CRYPT_OK on success +   */ +   int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len); + +/* ---- basic math ---- */ + +   /** add two integers  +     @param a   The first source integer +     @param b   The second source integer +     @param c   The destination of "a + b" +     @return CRYPT_OK on success +   */ +   int (*add)(void *a, void *b, void *c); + + +   /** add two integers  +     @param a   The first source integer +     @param b   The second source integer (single digit of upto bits_per_digit in length) +     @param c   The destination of "a + b" +     @return CRYPT_OK on success +   */ +   int (*addi)(void *a, unsigned long b, void *c); + +   /** subtract two integers  +     @param a   The first source integer +     @param b   The second source integer +     @param c   The destination of "a - b" +     @return CRYPT_OK on success +   */ +   int (*sub)(void *a, void *b, void *c); + +   /** subtract two integers  +     @param a   The first source integer +     @param b   The second source integer (single digit of upto bits_per_digit in length) +     @param c   The destination of "a - b" +     @return CRYPT_OK on success +   */ +   int (*subi)(void *a, unsigned long b, void *c); + +   /** multiply two integers  +     @param a   The first source integer +     @param b   The second source integer (single digit of upto bits_per_digit in length) +     @param c   The destination of "a * b" +     @return CRYPT_OK on success +   */ +   int (*mul)(void *a, void *b, void *c); + +   /** multiply two integers  +     @param a   The first source integer +     @param b   The second source integer (single digit of upto bits_per_digit in length) +     @param c   The destination of "a * b" +     @return CRYPT_OK on success +   */ +   int (*muli)(void *a, unsigned long b, void *c); + +   /** Square an integer +     @param a    The integer to square +     @param b    The destination +     @return CRYPT_OK on success +   */ +   int (*sqr)(void *a, void *b); + +   /** Divide an integer +     @param a    The dividend +     @param b    The divisor +     @param c    The quotient (can be NULL to signify don't care) +     @param d    The remainder (can be NULL to signify don't care) +     @return CRYPT_OK on success +   */ +   int (*mpdiv)(void *a, void *b, void *c, void *d); + +   /** divide by two  +      @param  a   The integer to divide (shift right) +      @param  b   The destination  +      @return CRYPT_OK on success +   */ +   int (*div_2)(void *a, void *b); + +   /** Get remainder (small value) +      @param  a    The integer to reduce +      @param  b    The modulus (upto bits_per_digit in length) +      @param  c    The destination for the residue +      @return CRYPT_OK on success +   */ +   int (*modi)(void *a, unsigned long b, unsigned long *c); + +   /** gcd  +      @param  a     The first integer +      @param  b     The second integer +      @param  c     The destination for (a, b) +      @return CRYPT_OK on success +   */ +   int (*gcd)(void *a, void *b, void *c); + +   /** lcm  +      @param  a     The first integer +      @param  b     The second integer +      @param  c     The destination for [a, b] +      @return CRYPT_OK on success +   */ +   int (*lcm)(void *a, void *b, void *c); + +   /** Modular multiplication +      @param  a     The first source +      @param  b     The second source  +      @param  c     The modulus +      @param  d     The destination (a*b mod c) +      @return CRYPT_OK on success +   */ +   int (*mulmod)(void *a, void *b, void *c, void *d); + +   /** Modular squaring +      @param  a     The first source +      @param  b     The modulus +      @param  c     The destination (a*a mod b) +      @return CRYPT_OK on success +   */ +   int (*sqrmod)(void *a, void *b, void *c); + +   /** Modular inversion +      @param  a     The value to invert +      @param  b     The modulus  +      @param  c     The destination (1/a mod b) +      @return CRYPT_OK on success +   */ +   int (*invmod)(void *, void *, void *); + +/* ---- reduction ---- */ + +   /** setup montgomery +       @param a  The modulus  +       @param b  The destination for the reduction digit  +       @return CRYPT_OK on success +   */ +   int (*montgomery_setup)(void *a, void **b); + +   /** get normalization value  +       @param a   The destination for the normalization value +       @param b   The modulus +       @return  CRYPT_OK on success +   */ +   int (*montgomery_normalization)(void *a, void *b); + +   /** reduce a number +       @param a   The number [and dest] to reduce +       @param b   The modulus +       @param c   The value "b" from montgomery_setup() +       @return CRYPT_OK on success +   */ +   int (*montgomery_reduce)(void *a, void *b, void *c); + +   /** clean up  (frees memory) +       @param a   The value "b" from montgomery_setup() +       @return CRYPT_OK on success +   */       +   void (*montgomery_deinit)(void *a); + +/* ---- exponentiation ---- */ + +   /** Modular exponentiation +       @param a    The base integer +       @param b    The power (can be negative) integer +       @param c    The modulus integer +       @param d    The destination +       @return CRYPT_OK on success +   */ +   int (*exptmod)(void *a, void *b, void *c, void *d); + +   /** Primality testing +       @param a     The integer to test +       @param b     The destination of the result (FP_YES if prime) +       @return CRYPT_OK on success +   */ +   int (*isprime)(void *a, int *b); + +/* ----  (optional) ecc point math ---- */ + +   /** ECC GF(p) point multiplication (from the NIST curves) +       @param k   The integer to multiply the point by +       @param G   The point to multiply +       @param R   The destination for kG   +       @param modulus  The modulus for the field +       @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only) +       @return CRYPT_OK on success +   */ +   int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +   /** ECC GF(p) point addition  +       @param P    The first point +       @param Q    The second point +       @param R    The destination of P + Q +       @param modulus  The modulus +       @param mp   The "b" value from montgomery_setup() +       @return CRYPT_OK on success +   */ +   int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); + +   /** ECC GF(p) point double  +       @param P    The first point +       @param R    The destination of 2P +       @param modulus  The modulus +       @param mp   The "b" value from montgomery_setup() +       @return CRYPT_OK on success +   */ +   int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp); + +   /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1) +       @param P     The point to map +       @param modulus The modulus +       @param mp    The "b" value from montgomery_setup() +       @return CRYPT_OK on success +       @remark  The mapping can be different but keep in mind a ecc_point only has three  +                integers (x,y,z) so if you use a different mapping you have to make it fit. +   */ +   int (*ecc_map)(ecc_point *P, void *modulus, void *mp); + +   /** Computes kA*A + kB*B = C using Shamir's Trick +       @param A        First point to multiply +       @param kA       What to multiple A by +       @param B        Second point to multiply +       @param kB       What to multiple B by +       @param C        [out] Destination point (can overlap with A or B +       @param modulus  Modulus for curve  +       @return CRYPT_OK on success +   */  +   int (*ecc_mul2add)(ecc_point *A, void *kA, +                      ecc_point *B, void *kB, +                      ecc_point *C, +                           void *modulus); + +/* ---- (optional) rsa optimized math (for internal CRT) ---- */ + +   /** RSA Key Generation  +       @param prng     An active PRNG state +       @param wprng    The index of the PRNG desired +       @param size     The size of the modulus (key size) desired (octets) +       @param e        The "e" value (public key).  e==65537 is a good choice +       @param key      [out] Destination of a newly created private key pair +       @return CRYPT_OK if successful, upon error all allocated ram is freed +    */ +    int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key); +    + +   /** RSA exponentiation +      @param in       The octet array representing the base +      @param inlen    The length of the input +      @param out      The destination (to be stored in an octet array format) +      @param outlen   The length of the output buffer and the resulting size (zero padded to the size of the modulus) +      @param which    PK_PUBLIC for public RSA and PK_PRIVATE for private RSA +      @param key      The RSA key to use  +      @return CRYPT_OK on success +   */ +   int (*rsa_me)(const unsigned char *in,   unsigned long inlen, +                       unsigned char *out,  unsigned long *outlen, int which, +                       rsa_key *key); +} ltc_math_descriptor; + +extern ltc_math_descriptor ltc_mp; + +int ltc_init_multi(void **a, ...); +void ltc_deinit_multi(void *a, ...); + +#ifdef LTM_DESC +extern const ltc_math_descriptor ltm_desc; +#endif + +#ifdef TFM_DESC +extern const ltc_math_descriptor tfm_desc; +#endif + +#ifdef GMP_DESC +extern const ltc_math_descriptor gmp_desc; +#endif + +#if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE) + +#define MP_DIGIT_BIT                 ltc_mp.bits_per_digit + +/* some handy macros */ +#define mp_init(a)                   ltc_mp.init(a) +#define mp_init_multi                ltc_init_multi +#define mp_clear(a)                  ltc_mp.deinit(a) +#define mp_clear_multi               ltc_deinit_multi +#define mp_init_copy(a, b)           ltc_mp.init_copy(a, b) + +#define mp_neg(a, b)                 ltc_mp.neg(a, b) +#define mp_copy(a, b)                ltc_mp.copy(a, b) + +#define mp_set(a, b)                 ltc_mp.set_int(a, b) +#define mp_set_int(a, b)             ltc_mp.set_int(a, b) +#define mp_get_int(a)                ltc_mp.get_int(a) +#define mp_get_digit(a, n)           ltc_mp.get_digit(a, n) +#define mp_get_digit_count(a)        ltc_mp.get_digit_count(a) +#define mp_cmp(a, b)                 ltc_mp.compare(a, b) +#define mp_cmp_d(a, b)               ltc_mp.compare_d(a, b) +#define mp_count_bits(a)             ltc_mp.count_bits(a) +#define mp_cnt_lsb(a)                ltc_mp.count_lsb_bits(a) +#define mp_2expt(a, b)               ltc_mp.twoexpt(a, b) + +#define mp_read_radix(a, b, c)       ltc_mp.read_radix(a, b, c) +#define mp_toradix(a, b, c)          ltc_mp.write_radix(a, b, c) +#define mp_unsigned_bin_size(a)      ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b)     ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) + +#define mp_add(a, b, c)              ltc_mp.add(a, b, c) +#define mp_add_d(a, b, c)            ltc_mp.addi(a, b, c) +#define mp_sub(a, b, c)              ltc_mp.sub(a, b, c) +#define mp_sub_d(a, b, c)            ltc_mp.subi(a, b, c) +#define mp_mul(a, b, c)              ltc_mp.mul(a, b, c) +#define mp_mul_d(a, b, c)            ltc_mp.muli(a, b, c) +#define mp_sqr(a, b)                 ltc_mp.sqr(a, b) +#define mp_div(a, b, c, d)           ltc_mp.mpdiv(a, b, c, d) +#define mp_div_2(a, b)               ltc_mp.div_2(a, b) +#define mp_mod(a, b, c)              ltc_mp.mpdiv(a, b, NULL, c) +#define mp_mod_d(a, b, c)            ltc_mp.modi(a, b, c) +#define mp_gcd(a, b, c)              ltc_mp.gcd(a, b, c) +#define mp_lcm(a, b, c)              ltc_mp.lcm(a, b, c) + +#define mp_mulmod(a, b, c, d)        ltc_mp.mulmod(a, b, c, d) +#define mp_sqrmod(a, b, c)           ltc_mp.sqrmod(a, b, c) +#define mp_invmod(a, b, c)           ltc_mp.invmod(a, b, c) + +#define mp_montgomery_setup(a, b)    ltc_mp.montgomery_setup(a, b) +#define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) +#define mp_montgomery_reduce(a, b, c)   ltc_mp.montgomery_reduce(a, b, c) +#define mp_montgomery_free(a)        ltc_mp.montgomery_deinit(a) + +#define mp_exptmod(a,b,c,d)          ltc_mp.exptmod(a,b,c,d) +#define mp_prime_is_prime(a, b, c)   ltc_mp.isprime(a, c) + +#define mp_iszero(a)                 (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) +#define mp_isodd(a)                  (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) +#define mp_exch(a, b)                do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while(0); + +#define mp_tohex(a, b)               mp_toradix(a, b, 16) + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */ +/* $Revision: 1.44 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_misc.h b/src/libtomcrypt/src/headers/tomcrypt_misc.h new file mode 100644 index 0000000..f5384ca --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_misc.h @@ -0,0 +1,23 @@ +/* ---- LTC_BASE64 Routines ---- */ +#ifdef LTC_BASE64 +int base64_encode(const unsigned char *in,  unsigned long len,  +                        unsigned char *out, unsigned long *outlen); + +int base64_decode(const unsigned char *in,  unsigned long len,  +                        unsigned char *out, unsigned long *outlen); +#endif + +/* ---- MEM routines ---- */ +void zeromem(void *dst, size_t len); +void burn_stack(unsigned long len); + +const char *error_to_string(int err); + +extern const char *crypt_build_settings; + +/* ---- HMM ---- */ +int crypt_fsa(void *mp, ...); + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_pk.h b/src/libtomcrypt/src/headers/tomcrypt_pk.h new file mode 100644 index 0000000..b5f277a --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_pk.h @@ -0,0 +1,558 @@ +/* ---- NUMBER THEORY ---- */ + +enum { +   PK_PUBLIC=0, +   PK_PRIVATE=1 +}; + +int rand_prime(void *N, long len, prng_state *prng, int wprng); + +/* ---- RSA ---- */ +#ifdef LTC_MRSA + +/* Min and Max RSA key sizes (in bits) */ +#define MIN_RSA_SIZE 1024 +#define MAX_RSA_SIZE 4096 + +/** RSA LTC_PKCS style key */ +typedef struct Rsa_key { +    /** Type of key, PK_PRIVATE or PK_PUBLIC */ +    int type; +    /** The public exponent */ +    void *e;  +    /** The private exponent */ +    void *d;  +    /** The modulus */ +    void *N;  +    /** The p factor of N */ +    void *p;  +    /** The q factor of N */ +    void *q;  +    /** The 1/q mod p CRT param */ +    void *qP;  +    /** The d mod (p - 1) CRT param */ +    void *dP;  +    /** The d mod (q - 1) CRT param */ +    void *dQ; +} rsa_key; + +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key); + +int rsa_exptmod(const unsigned char *in,   unsigned long inlen, +                      unsigned char *out,  unsigned long *outlen, int which, +                      rsa_key *key); + +void rsa_free(rsa_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +#define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \ +  rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key) + +#define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \ +  rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key) + +#define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \ +  rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key) + +#define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \ +  rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key) + +/* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */ +int rsa_encrypt_key_ex(const unsigned char *in,     unsigned long inlen, +                             unsigned char *out,    unsigned long *outlen, +                       const unsigned char *lparam, unsigned long lparamlen, +                       prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key); + +int rsa_decrypt_key_ex(const unsigned char *in,       unsigned long  inlen, +                             unsigned char *out,      unsigned long *outlen, +                       const unsigned char *lparam,   unsigned long  lparamlen, +                             int            hash_idx, int            padding, +                             int           *stat,     rsa_key       *key); + +int rsa_sign_hash_ex(const unsigned char *in,       unsigned long  inlen, +                           unsigned char *out,      unsigned long *outlen, +                           int            padding, +                           prng_state    *prng,     int            prng_idx, +                           int            hash_idx, unsigned long  saltlen, +                           rsa_key *key); + +int rsa_verify_hash_ex(const unsigned char *sig,      unsigned long siglen, +                       const unsigned char *hash,     unsigned long hashlen, +                             int            padding, +                             int            hash_idx, unsigned long saltlen, +                             int           *stat,     rsa_key      *key); + +/* LTC_PKCS #1 import/export */ +int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key); +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key); + +/* Ladik: Added for verifying Blizzard strong signature verification */ +int rsa_verify_simple(const unsigned char *sig,  unsigned long siglen, +                      const unsigned char *hash, unsigned long hashlen, +                            int           *stat, +                            rsa_key       *key); + +#endif + +/* ---- Katja ---- */ +#ifdef MKAT + +/* Min and Max KAT key sizes (in bits) */ +#define MIN_KAT_SIZE 1024 +#define MAX_KAT_SIZE 4096 + +/** Katja LTC_PKCS style key */ +typedef struct KAT_key { +    /** Type of key, PK_PRIVATE or PK_PUBLIC */ +    int type; +    /** The private exponent */ +    void *d;  +    /** The modulus */ +    void *N;  +    /** The p factor of N */ +    void *p;  +    /** The q factor of N */ +    void *q;  +    /** The 1/q mod p CRT param */ +    void *qP;  +    /** The d mod (p - 1) CRT param */ +    void *dP;  +    /** The d mod (q - 1) CRT param */ +    void *dQ; +    /** The pq param */ +    void *pq; +} katja_key; + +int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key); + +int katja_exptmod(const unsigned char *in,   unsigned long inlen, +                        unsigned char *out,  unsigned long *outlen, int which, +                        katja_key *key); + +void katja_free(katja_key *key); + +/* These use LTC_PKCS #1 v2.0 padding */ +int katja_encrypt_key(const unsigned char *in,     unsigned long inlen, +                            unsigned char *out,    unsigned long *outlen, +                      const unsigned char *lparam, unsigned long lparamlen, +                      prng_state *prng, int prng_idx, int hash_idx, katja_key *key); +                                         +int katja_decrypt_key(const unsigned char *in,       unsigned long inlen, +                            unsigned char *out,      unsigned long *outlen,  +                      const unsigned char *lparam,   unsigned long lparamlen, +                            int            hash_idx, int *stat, +                            katja_key       *key); + +/* LTC_PKCS #1 import/export */ +int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key); +int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key); +                         +#endif + +/* ---- ECC Routines ---- */ +#ifdef LTC_MECC + +/* size of our temp buffers for exported keys */ +#define ECC_BUF_SIZE 256 + +/* max private key size */ +#define ECC_MAXSIZE  66 + +/** Structure defines a NIST GF(p) curve */ +typedef struct { +   /** The size of the curve in octets */ +   int size; + +   /** name of curve */ +   char *name;  + +   /** The prime that defines the field the curve is in (encoded in hex) */ +   char *prime; + +   /** The fields B param (hex) */ +   char *B; + +   /** The order of the curve (hex) */ +   char *order; +   +   /** The x co-ordinate of the base point on the curve (hex) */ +   char *Gx; +  +   /** The y co-ordinate of the base point on the curve (hex) */ +   char *Gy; +} ltc_ecc_set_type; + +/** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */ +typedef struct { +    /** The x co-ordinate */ +    void *x; + +    /** The y co-ordinate */ +    void *y; + +    /** The z co-ordinate */ +    void *z; +} ecc_point; + +/** An ECC key */ +typedef struct { +    /** Type of key, PK_PRIVATE or PK_PUBLIC */ +    int type; + +    /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */ +    int idx; + +	/** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */ +	const ltc_ecc_set_type *dp; + +    /** The public key */ +    ecc_point pubkey; + +    /** The private key */ +    void *k; +} ecc_key; + +/** the ECC params provided */ +extern const ltc_ecc_set_type ltc_ecc_sets[]; + +int  ecc_test(void); +void ecc_sizes(int *low, int *high); +int  ecc_get_size(ecc_key *key); + +int  ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); +int  ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp); +void ecc_free(ecc_key *key); + +int  ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key); +int  ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int  ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp); + +int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen); +int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); +int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp); + +int  ecc_shared_secret(ecc_key *private_key, ecc_key *public_key,  +                       unsigned char *out, unsigned long *outlen); + +int  ecc_encrypt_key(const unsigned char *in,   unsigned long inlen, +                           unsigned char *out,  unsigned long *outlen,  +                           prng_state *prng, int wprng, int hash,  +                           ecc_key *key); + +int  ecc_decrypt_key(const unsigned char *in,  unsigned long  inlen, +                           unsigned char *out, unsigned long *outlen,  +                           ecc_key *key); + +int  ecc_sign_hash(const unsigned char *in,  unsigned long inlen,  +                         unsigned char *out, unsigned long *outlen,  +                         prng_state *prng, int wprng, ecc_key *key); + +int  ecc_verify_hash(const unsigned char *sig,  unsigned long siglen, +                     const unsigned char *hash, unsigned long hashlen,  +                     int *stat, ecc_key *key); + +/* low level functions */ +ecc_point *ltc_ecc_new_point(void); +void       ltc_ecc_del_point(ecc_point *p); +int        ltc_ecc_is_valid_idx(int n); + +/* point ops (mp == montgomery digit) */ +#if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC) +/* R = 2P */ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp); + +/* R = P + Q */ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); +#endif + +#if defined(LTC_MECC_FP) +/* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */ +int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +/* functions for saving/loading/freeing/adding to fixed point cache */ +int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen); +int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen); +void ltc_ecc_fp_free(void); +int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock); + +/* lock/unlock all points currently in fixed point cache */ +void ltc_ecc_fp_tablelock(int lock); +#endif + +/* R = kG */ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); + +#ifdef LTC_ECC_SHAMIR +/* kA*A + kB*B = C */ +int ltc_ecc_mul2add(ecc_point *A, void *kA, +                    ecc_point *B, void *kB, +                    ecc_point *C, +                         void *modulus); + +#ifdef LTC_MECC_FP +/* Shamir's trick with optimized point multiplication using fixed point cache */ +int ltc_ecc_fp_mul2add(ecc_point *A, void *kA, +                       ecc_point *B, void *kB, +                       ecc_point *C, void *modulus); +#endif + +#endif + + +/* map P to affine from projective */ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp); + +#endif + +#ifdef LTC_MDSA + +/* Max diff between group and modulus size in bytes */ +#define LTC_MDSA_DELTA     512 + +/* Max DSA group size in bytes (default allows 4k-bit groups) */ +#define LTC_MDSA_MAX_GROUP 512 + +/** DSA key structure */ +typedef struct { +   /** The key type, PK_PRIVATE or PK_PUBLIC */ +   int type;  + +   /** The order of the sub-group used in octets */ +   int qord; + +   /** The generator  */ +   void *g; + +   /** The prime used to generate the sub-group */ +   void *q; + +   /** The large prime that generats the field the contains the sub-group */ +   void *p; + +   /** The private key */ +   void *x; + +   /** The public key */ +   void *y; +} dsa_key; + +int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key); +void dsa_free(dsa_key *key); + +int dsa_sign_hash_raw(const unsigned char *in,  unsigned long inlen, +                                   void *r,   void *s, +                               prng_state *prng, int wprng, dsa_key *key); + +int dsa_sign_hash(const unsigned char *in,  unsigned long inlen, +                        unsigned char *out, unsigned long *outlen, +                        prng_state *prng, int wprng, dsa_key *key); + +int dsa_verify_hash_raw(         void *r,          void *s, +                    const unsigned char *hash, unsigned long hashlen,  +                                    int *stat,      dsa_key *key); + +int dsa_verify_hash(const unsigned char *sig,  unsigned long siglen, +                    const unsigned char *hash, unsigned long hashlen,  +                          int           *stat, dsa_key       *key); + +int dsa_encrypt_key(const unsigned char *in,   unsigned long inlen, +                          unsigned char *out,  unsigned long *outlen,  +                          prng_state *prng, int wprng, int hash,  +                          dsa_key *key); +                       +int dsa_decrypt_key(const unsigned char *in,  unsigned long  inlen, +                          unsigned char *out, unsigned long *outlen,  +                          dsa_key *key); +                           +int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key); +int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key); +int dsa_verify_key(dsa_key *key, int *stat); + +int dsa_shared_secret(void          *private_key, void *base, +                      dsa_key       *public_key, +                      unsigned char *out,         unsigned long *outlen); +#endif + +#ifdef LTC_DER +/* DER handling */ + +enum { + LTC_ASN1_EOL, + LTC_ASN1_BOOLEAN, + LTC_ASN1_INTEGER, + LTC_ASN1_SHORT_INTEGER, + LTC_ASN1_BIT_STRING, + LTC_ASN1_OCTET_STRING, + LTC_ASN1_NULL, + LTC_ASN1_OBJECT_IDENTIFIER, + LTC_ASN1_IA5_STRING, + LTC_ASN1_PRINTABLE_STRING, + LTC_ASN1_UTF8_STRING, + LTC_ASN1_UTCTIME, + LTC_ASN1_CHOICE, + LTC_ASN1_SEQUENCE, + LTC_ASN1_SET, + LTC_ASN1_SETOF +}; + +/** A LTC ASN.1 list type */ +typedef struct ltc_asn1_list_ { +   /** The LTC ASN.1 enumerated type identifier */ +   int           type; +   /** The data to encode or place for decoding */ +   void         *data; +   /** The size of the input or resulting output */ +   unsigned long size; +   /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */ +   int           used; +   /** prev/next entry in the list */ +   struct ltc_asn1_list_ *prev, *next, *child, *parent; +} ltc_asn1_list; + +#define LTC_SET_ASN1(list, index, Type, Data, Size)  \ +   do {                                              \ +      int LTC_MACRO_temp            = (index);       \ +      ltc_asn1_list *LTC_MACRO_list = (list);        \ +      LTC_MACRO_list[LTC_MACRO_temp].type = (Type);  \ +      LTC_MACRO_list[LTC_MACRO_temp].data = (void*)(Data);  \ +      LTC_MACRO_list[LTC_MACRO_temp].size = (Size);  \ +      LTC_MACRO_list[LTC_MACRO_temp].used = 0;       \ +   } while (0); + +/* SEQUENCE */ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, +                           unsigned char *out,  unsigned long *outlen, int type_of); +                           +#define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE)                         + +int der_decode_sequence_ex(const unsigned char *in, unsigned long  inlen, +                           ltc_asn1_list *list,     unsigned long  outlen, int ordered); +                               +#define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1) + +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, +                        unsigned long *outlen); + +/* SET */ +#define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0) +#define der_length_set der_length_sequence +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, +                   unsigned char *out,  unsigned long *outlen); + +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, +                     unsigned char *out,  unsigned long *outlen); +                         +/* VA list handy helpers with triplets of <type, size, data> */ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...); +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...); + +/* FLEXI DECODER handle unknown list decoder */ +int  der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out); +void der_free_sequence_flexi(ltc_asn1_list *list); +void der_sequence_free(ltc_asn1_list *in); + +/* BOOLEAN */ +int der_length_boolean(unsigned long *outlen); +int der_encode_boolean(int in,  +                       unsigned char *out, unsigned long *outlen); +int der_decode_boolean(const unsigned char *in, unsigned long inlen, +                                       int *out);		        +/* INTEGER */ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen); +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num); +int der_length_integer(void *num, unsigned long *len); + +/* INTEGER -- handy for 0..2^32-1 values */ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num); +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen); +int der_length_short_integer(unsigned long num, unsigned long *outlen); + +/* BIT STRING */ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_decode_bit_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_length_bit_string(unsigned long nbits, unsigned long *outlen); + +/* OCTET STRING */ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, +                                  unsigned char *out, unsigned long *outlen); +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, +                                  unsigned char *out, unsigned long *outlen); +int der_length_octet_string(unsigned long noctets, unsigned long *outlen); + +/* OBJECT IDENTIFIER */ +int der_encode_object_identifier(unsigned long *words, unsigned long  nwords, +                                 unsigned char *out,   unsigned long *outlen); +int der_decode_object_identifier(const unsigned char *in,    unsigned long  inlen, +                                       unsigned long *words, unsigned long *outlen); +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen); +unsigned long der_object_identifier_bits(unsigned long x); + +/* IA5 STRING */ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_ia5_char_encode(int c); +int der_ia5_value_decode(int v); + +/* Printable STRING */ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen); +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); + +int der_printable_char_encode(int c); +int der_printable_value_decode(int v); + +/* UTF-8 */ +#if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR)  +#include <wchar.h> +#else +typedef ulong32 wchar_t; +#endif + +int der_encode_utf8_string(const wchar_t *in,  unsigned long inlen, +                           unsigned char *out, unsigned long *outlen); + +int der_decode_utf8_string(const unsigned char *in,  unsigned long inlen, +                                       wchar_t *out, unsigned long *outlen); +unsigned long der_utf8_charsize(const wchar_t c); +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen); + + +/* CHOICE */ +int der_decode_choice(const unsigned char *in,   unsigned long *inlen, +                            ltc_asn1_list *list, unsigned long  outlen); + +/* UTCTime */ +typedef struct { +   unsigned YY, /* year */ +            MM, /* month */ +            DD, /* day */ +            hh, /* hour */ +            mm, /* minute */ +            ss, /* second */ +            off_dir, /* timezone offset direction 0 == +, 1 == - */ +            off_hh, /* timezone offset hours */ +            off_mm; /* timezone offset minutes */ +} ltc_utctime; + +int der_encode_utctime(ltc_utctime *utctime,  +                       unsigned char *out,   unsigned long *outlen); + +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, +                             ltc_utctime   *out); + +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */ +/* $Revision: 1.81 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_pkcs.h b/src/libtomcrypt/src/headers/tomcrypt_pkcs.h new file mode 100644 index 0000000..84fb82a --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_pkcs.h @@ -0,0 +1,89 @@ +/* LTC_PKCS Header Info */ + +/* ===> LTC_PKCS #1 -- RSA Cryptography <=== */ +#ifdef LTC_PKCS_1 + +enum ltc_pkcs_1_v1_5_blocks +{ +  LTC_LTC_PKCS_1_EMSA   = 1,        /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */ +  LTC_LTC_PKCS_1_EME    = 2         /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */ +}; + +enum ltc_pkcs_1_paddings +{ +  LTC_LTC_PKCS_1_V1_5   = 1,        /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */ +  LTC_LTC_PKCS_1_OAEP   = 2,        /* LTC_PKCS #1 v2.0 encryption padding */ +  LTC_LTC_PKCS_1_PSS    = 3         /* LTC_PKCS #1 v2.1 signature padding */ +}; + +int pkcs_1_mgf1(      int            hash_idx, +                const unsigned char *seed, unsigned long seedlen, +                      unsigned char *mask, unsigned long masklen); + +int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out); +int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen); + +/* *** v1.5 padding */ +int pkcs_1_v1_5_encode(const unsigned char *msg,  +                             unsigned long  msglen, +                             int            block_type, +                             unsigned long  modulus_bitlen, +                                prng_state *prng,  +                                       int  prng_idx, +                             unsigned char *out,  +                             unsigned long *outlen); + +int pkcs_1_v1_5_decode(const unsigned char *msg,  +                             unsigned long  msglen, +                                       int  block_type, +                             unsigned long  modulus_bitlen, +                             unsigned char *out,  +                             unsigned long *outlen, +                                       int *is_valid); + +/* *** v2.1 padding */ +int pkcs_1_oaep_encode(const unsigned char *msg,    unsigned long msglen, +                       const unsigned char *lparam, unsigned long lparamlen, +                             unsigned long modulus_bitlen, prng_state *prng, +                             int           prng_idx,         int  hash_idx, +                             unsigned char *out,    unsigned long *outlen); + +int pkcs_1_oaep_decode(const unsigned char *msg,    unsigned long msglen, +                       const unsigned char *lparam, unsigned long lparamlen, +                             unsigned long modulus_bitlen, int hash_idx, +                             unsigned char *out,    unsigned long *outlen, +                             int           *res); + +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, +                            unsigned long saltlen,  prng_state   *prng,      +                            int           prng_idx, int           hash_idx, +                            unsigned long modulus_bitlen, +                            unsigned char *out,     unsigned long *outlen); + +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, +                      const unsigned char *sig,     unsigned long siglen, +                            unsigned long saltlen,  int           hash_idx, +                            unsigned long modulus_bitlen, int    *res); + +#endif /* LTC_PKCS_1 */ + +/* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */ +#ifdef LTC_PKCS_5 + +/* Algorithm #1 (old) */ +int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,  +                const unsigned char *salt,  +                int iteration_count,  int hash_idx, +                unsigned char *out,   unsigned long *outlen); + +/* Algorithm #2 (new) */ +int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,  +                const unsigned char *salt,     unsigned long salt_len, +                int iteration_count,           int hash_idx, +                unsigned char *out,            unsigned long *outlen); + +#endif  /* LTC_PKCS_5 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/headers/tomcrypt_prng.h b/src/libtomcrypt/src/headers/tomcrypt_prng.h new file mode 100644 index 0000000..f3e3e55 --- /dev/null +++ b/src/libtomcrypt/src/headers/tomcrypt_prng.h @@ -0,0 +1,199 @@ +/* ---- PRNG Stuff ---- */ +#ifdef LTC_YARROW +struct yarrow_prng { +    int                   cipher, hash; +    unsigned char         pool[MAXBLOCKSIZE]; +    symmetric_CTR         ctr; +    LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_RC4 +struct rc4_prng { +    int x, y; +    unsigned char buf[256]; +}; +#endif + +#ifdef LTC_FORTUNA +struct fortuna_prng { +    hash_state pool[LTC_FORTUNA_POOLS];     /* the  pools */ + +    symmetric_key skey; + +    unsigned char K[32],      /* the current key */ +                  IV[16];     /* IV for CTR mode */ +     +    unsigned long pool_idx,   /* current pool we will add to */ +                  pool0_len,  /* length of 0'th pool */ +                  wd;             + +    ulong64       reset_cnt;  /* number of times we have reset */ +    LTC_MUTEX_TYPE(prng_lock) +}; +#endif + +#ifdef LTC_SOBER128 +struct sober128_prng { +    ulong32      R[17],          /* Working storage for the shift register */ +                 initR[17],      /* saved register contents */  +                 konst,          /* key dependent constant */ +                 sbuf;           /* partial word encryption buffer */ + +    int          nbuf,           /* number of part-word stream bits buffered */ +                 flag,           /* first add_entropy call or not? */ +                 set;            /* did we call add_entropy to set key? */ +     +}; +#endif + +typedef union Prng_state { +    char dummy[1]; +#ifdef LTC_YARROW +    struct yarrow_prng    yarrow; +#endif +#ifdef LTC_RC4 +    struct rc4_prng       rc4; +#endif +#ifdef LTC_FORTUNA +    struct fortuna_prng   fortuna; +#endif +#ifdef LTC_SOBER128 +    struct sober128_prng  sober128; +#endif +} prng_state; + +/** PRNG descriptor */ +extern struct ltc_prng_descriptor { +    /** Name of the PRNG */ +    char *name; +    /** size in bytes of exported state */ +    int  export_size; +    /** Start a PRNG state +        @param prng   [out] The state to initialize +        @return CRYPT_OK if successful +    */ +    int (*start)(prng_state *prng); +    /** Add entropy to the PRNG +        @param in         The entropy +        @param inlen      Length of the entropy (octets)\ +        @param prng       The PRNG state +        @return CRYPT_OK if successful +    */ +    int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng); +    /** Ready a PRNG state to read from +        @param prng       The PRNG state to ready +        @return CRYPT_OK if successful +    */ +    int (*ready)(prng_state *prng); +    /** Read from the PRNG +        @param out     [out] Where to store the data +        @param outlen  Length of data desired (octets) +        @param prng    The PRNG state to read from +        @return Number of octets read +    */ +    unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng); +    /** Terminate a PRNG state +        @param prng   The PRNG state to terminate +        @return CRYPT_OK if successful +    */ +    int (*done)(prng_state *prng); +    /** Export a PRNG state   +        @param out     [out] The destination for the state +        @param outlen  [in/out] The max size and resulting size of the PRNG state +        @param prng    The PRNG to export +        @return CRYPT_OK if successful +    */ +    int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng); +    /** Import a PRNG state +        @param in      The data to import +        @param inlen   The length of the data to import (octets) +        @param prng    The PRNG to initialize/import +        @return CRYPT_OK if successful +    */ +    int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng); +    /** Self-test the PRNG +        @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled +    */ +    int (*test)(void); +} prng_descriptor[]; + +#ifdef LTC_YARROW +int yarrow_start(prng_state *prng); +int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int yarrow_ready(prng_state *prng); +unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int yarrow_done(prng_state *prng); +int  yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int  yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int  yarrow_test(void); +extern const struct ltc_prng_descriptor yarrow_desc; +#endif + +#ifdef LTC_FORTUNA +int fortuna_start(prng_state *prng); +int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int fortuna_ready(prng_state *prng); +unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int fortuna_done(prng_state *prng); +int  fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int  fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int  fortuna_test(void); +extern const struct ltc_prng_descriptor fortuna_desc; +#endif + +#ifdef LTC_RC4 +int rc4_start(prng_state *prng); +int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int rc4_ready(prng_state *prng); +unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int  rc4_done(prng_state *prng); +int  rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int  rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int  rc4_test(void); +extern const struct ltc_prng_descriptor rc4_desc; +#endif + +#ifdef LTC_SPRNG +int sprng_start(prng_state *prng); +int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sprng_ready(prng_state *prng); +unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sprng_done(prng_state *prng); +int  sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int  sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int  sprng_test(void); +extern const struct ltc_prng_descriptor sprng_desc; +#endif + +#ifdef LTC_SOBER128 +int sober128_start(prng_state *prng); +int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); +int sober128_ready(prng_state *prng); +unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng); +int sober128_done(prng_state *prng); +int  sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); +int  sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); +int  sober128_test(void); +extern const struct ltc_prng_descriptor sober128_desc; +#endif + +int find_prng(const char *name); +int register_prng(const struct ltc_prng_descriptor *prng); +int unregister_prng(const struct ltc_prng_descriptor *prng); +int prng_is_valid(int idx); +LTC_MUTEX_PROTO(ltc_prng_mutex) + +/* Slow RNG you **might** be able to use to seed a PRNG with.  Be careful as this + * might not work on all platforms as planned + */ +unsigned long rng_get_bytes(unsigned char *out,  +                            unsigned long outlen,  +                            void (*callback)(void)); + +int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); + + +/* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/math/ltm_desc.c b/src/libtomcrypt/src/math/ltm_desc.c new file mode 100644 index 0000000..25dc0b3 --- /dev/null +++ b/src/libtomcrypt/src/math/ltm_desc.c @@ -0,0 +1,483 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#define DESC_DEF_ONLY +#include "../headers/tomcrypt.h" + +#ifdef LTM_DESC + +#include "../../../libtommath/tommath.h" + +static const struct { +    int mpi_code, ltc_code; +} mpi_to_ltc_codes[] = { +   { MP_OKAY ,  CRYPT_OK}, +   { MP_MEM  ,  CRYPT_MEM}, +   { MP_VAL  ,  CRYPT_INVALID_ARG}, +}; + +/** +   Convert a MPI error to a LTC error (Possibly the most powerful function ever!  Oh wait... no)  +   @param err    The error to convert +   @return The equivalent LTC error code or CRYPT_ERROR if none found +*/ +static int mpi_to_ltc_error(int err) +{ +   int x; + +   for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes)/sizeof(mpi_to_ltc_codes[0])); x++) { +       if (err == mpi_to_ltc_codes[x].mpi_code) {  +          return mpi_to_ltc_codes[x].ltc_code; +       } +   } +   return CRYPT_ERROR; +} + +static int init(void **a) +{ +   int err; + +   LTC_ARGCHK(a != NULL); + +   *a = XCALLOC(1, sizeof(mp_int)); +   if (*a == NULL) { +      return CRYPT_MEM; +   } +    +   if ((err = mpi_to_ltc_error(mp_init(*a))) != CRYPT_OK) { +      XFREE(*a); +   } +   return err; +} + +static void deinit(void *a) +{ +   LTC_ARGCHKVD(a != NULL); +   mp_clear(a); +   XFREE(a); +} + +static int neg(void *a, void *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_neg(a, b)); +} + +static int copy(void *a, void *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_copy(a, b)); +} + +static int init_copy(void **a, void *b) +{ +   if (init(a) != CRYPT_OK) { +      return CRYPT_MEM; +   } +   return copy(b, *a); +} + +/* ---- trivial ---- */ +static int set_int(void *a, unsigned long b) +{ +   LTC_ARGCHK(a != NULL); +   return mpi_to_ltc_error(mp_set_int(a, b)); +} + +static unsigned long get_int(void *a) +{ +   LTC_ARGCHK(a != NULL); +   return mp_get_int(a); +} + +static unsigned long get_digit(void *a, int n) +{ +   mp_int *A; +   LTC_ARGCHK(a != NULL); +   A = a; +   return (n >= A->used || n < 0) ? 0 : A->dp[n]; +} + +static int get_digit_count(void *a) +{ +   mp_int *A; +   LTC_ARGCHK(a != NULL); +   A = a; +   return A->used; +} +    +static int compare(void *a, void *b) +{ +   int ret; +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   ret = mp_cmp(a, b); +   switch (ret) { +      case MP_LT: return LTC_MP_LT; +      case MP_EQ: return LTC_MP_EQ; +      case MP_GT: return LTC_MP_GT; +   } +   return 0; +} + +static int compare_d(void *a, unsigned long b) +{ +   int ret; +   LTC_ARGCHK(a != NULL); +   ret = mp_cmp_d(a, b); +   switch (ret) { +      case MP_LT: return LTC_MP_LT; +      case MP_EQ: return LTC_MP_EQ; +      case MP_GT: return LTC_MP_GT; +   } +   return 0; +} + +static int count_bits(void *a) +{ +   LTC_ARGCHK(a != NULL); +   return mp_count_bits(a); +} + +static int count_lsb_bits(void *a) +{ +   LTC_ARGCHK(a != NULL); +   return mp_cnt_lsb(a); +} + + +static int twoexpt(void *a, int n) +{ +   LTC_ARGCHK(a != NULL); +   return mpi_to_ltc_error(mp_2expt(a, n)); +} + +/* ---- conversions ---- */ + +/* read ascii string */ +static int read_radix(void *a, const char *b, int radix) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_read_radix(a, b, radix)); +} + +/* write one */ +static int write_radix(void *a, char *b, int radix) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_toradix(a, b, radix)); +} + +/* get size as unsigned char string */ +static unsigned long unsigned_size(void *a) +{ +   LTC_ARGCHK(a != NULL); +   return mp_unsigned_bin_size(a); +} + +/* store */ +static int unsigned_write(void *a, unsigned char *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_to_unsigned_bin(a, b)); +} + +/* read */ +static int unsigned_read(void *a, unsigned char *b, unsigned long len) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_read_unsigned_bin(a, b, len)); +} + +/* add */ +static int add(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_add(a, b, c)); +} +   +static int addi(void *a, unsigned long b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_add_d(a, b, c)); +} + +/* sub */ +static int sub(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_sub(a, b, c)); +} + +static int subi(void *a, unsigned long b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_sub_d(a, b, c)); +} + +/* mul */ +static int mul(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_mul(a, b, c)); +} + +static int muli(void *a, unsigned long b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_mul_d(a, b, c)); +} + +/* sqr */ +static int sqr(void *a, void *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_sqr(a, b)); +} + +/* div */ +static int divide(void *a, void *b, void *c, void *d) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_div(a, b, c, d)); +} + +static int div_2(void *a, void *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_div_2(a, b)); +} + +/* modi */ +static int modi(void *a, unsigned long b, unsigned long *c) +{ +   mp_digit tmp; +   int      err; + +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(c != NULL); + +   if ((err = mpi_to_ltc_error(mp_mod_d(a, b, &tmp))) != CRYPT_OK) { +      return err; +   } +   *c = tmp; +   return CRYPT_OK; +}   + +/* gcd */ +static int gcd(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_gcd(a, b, c)); +} + +/* lcm */ +static int lcm(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_lcm(a, b, c)); +} + +static int mulmod(void *a, void *b, void *c, void *d) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   LTC_ARGCHK(d != NULL); +   return mpi_to_ltc_error(mp_mulmod(a,b,c,d)); +} + +static int sqrmod(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_sqrmod(a,b,c)); +} + +/* invmod */ +static int invmod(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_invmod(a, b, c)); +} + +/* setup */ +static int montgomery_setup(void *a, void **b) +{ +   int err; +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   *b = XCALLOC(1, sizeof(mp_digit)); +   if (*b == NULL) { +      return CRYPT_MEM; +   } +   if ((err = mpi_to_ltc_error(mp_montgomery_setup(a, (mp_digit *)*b))) != CRYPT_OK) { +      XFREE(*b); +   } +   return err; +} + +/* get normalization value */ +static int montgomery_normalization(void *a, void *b) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   return mpi_to_ltc_error(mp_montgomery_calc_normalization(a, b)); +} + +/* reduce */ +static int montgomery_reduce(void *a, void *b, void *c) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   return mpi_to_ltc_error(mp_montgomery_reduce(a, b, *((mp_digit *)c))); +} + +/* clean up */ +static void montgomery_deinit(void *a) +{ +   XFREE(a); +} + +static int exptmod(void *a, void *b, void *c, void *d) +{ +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   LTC_ARGCHK(c != NULL); +   LTC_ARGCHK(d != NULL); +   return mpi_to_ltc_error(mp_exptmod(a,b,c,d)); +}    + +static int isprime(void *a, int *b) +{ +   int err; +   LTC_ARGCHK(a != NULL); +   LTC_ARGCHK(b != NULL); +   err = mpi_to_ltc_error(mp_prime_is_prime(a, 8, b)); +   *b = (*b == MP_YES) ? LTC_MP_YES : LTC_MP_NO; +   return err; +}    + +const ltc_math_descriptor ltm_desc = { + +   "LibTomMath", +   (int)DIGIT_BIT, + +   &init, +   &init_copy, +   &deinit, + +   &neg, +   ©, + +   &set_int, +   &get_int, +   &get_digit, +   &get_digit_count, +   &compare, +   &compare_d, +   &count_bits, +   &count_lsb_bits, +   &twoexpt, + +   &read_radix, +   &write_radix, +   &unsigned_size, +   &unsigned_write, +   &unsigned_read, + +   &add, +   &addi, +   &sub, +   &subi, +   &mul, +   &muli, +   &sqr, +   ÷, +   &div_2, +   &modi, +   &gcd, +   &lcm, + +   &mulmod, +   &sqrmod, +   &invmod, +    +   &montgomery_setup, +   &montgomery_normalization, +   &montgomery_reduce, +   &montgomery_deinit, + +   &exptmod, +   &isprime, + +#ifdef LTC_MECC +#ifdef LTC_MECC_FP +   <c_ecc_fp_mulmod, +#else    +   <c_ecc_mulmod, +#endif +   <c_ecc_projective_add_point, +   <c_ecc_projective_dbl_point, +   <c_ecc_map, +#ifdef LTC_ECC_SHAMIR +#ifdef LTC_MECC_FP +   <c_ecc_fp_mul2add, +#else +   <c_ecc_mul2add, +#endif /* LTC_MECC_FP */ +#else +   NULL, +#endif /* LTC_ECC_SHAMIR */ +#else +   NULL, NULL, NULL, NULL, NULL, +#endif /* LTC_MECC */ + +#ifdef LTC_MRSA +   &rsa_make_key, +   &rsa_exptmod, +#else +   NULL, NULL +#endif +}; + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/ltm_desc.c,v $ */ +/* $Revision: 1.31 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/math/multi.c b/src/libtomcrypt/src/math/multi.c new file mode 100644 index 0000000..7d40040 --- /dev/null +++ b/src/libtomcrypt/src/math/multi.c @@ -0,0 +1,61 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +#ifdef MPI +#include <stdarg.h> + +int ltc_init_multi(void **a, ...) +{ +   void    **cur = a; +   int       np  = 0; +   va_list   args; + +   va_start(args, a); +   while (cur != NULL) { +       if (mp_init(cur) != CRYPT_OK) { +          /* failed */ +          va_list clean_list; + +          va_start(clean_list, a); +          cur = a; +          while (np--) { +              mp_clear(*cur); +              cur = va_arg(clean_list, void**); +          } +          va_end(clean_list); +          return CRYPT_MEM; +       } +       ++np; +       cur = va_arg(args, void**); +   } +   va_end(args); +   return CRYPT_OK;    +} + +void ltc_deinit_multi(void *a, ...) +{ +   void     *cur = a; +   va_list   args; + +   va_start(args, a); +   while (cur != NULL) { +       mp_clear(cur); +       cur = va_arg(args, void *); +   } +   va_end(args); +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/math/multi.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ diff --git a/src/libtomcrypt/src/math/rand_prime.c b/src/libtomcrypt/src/math/rand_prime.c new file mode 100644 index 0000000..913fa95 --- /dev/null +++ b/src/libtomcrypt/src/math/rand_prime.c @@ -0,0 +1,87 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file rand_prime.c +  Generate a random prime, Tom St Denis +*/   + +#define USE_BBS 1 + +int rand_prime(void *N, long len, prng_state *prng, int wprng) +{ +   int            err, res, type; +   unsigned char *buf; + +   LTC_ARGCHK(N != NULL); + +   /* get type */ +   if (len < 0) { +      type = USE_BBS; +      len = -len; +   } else { +      type = 0; +   } + +   /* allow sizes between 2 and 512 bytes for a prime size */ +   if (len < 2 || len > 512) {  +      return CRYPT_INVALID_PRIME_SIZE; +   } +    +   /* valid PRNG? Better be! */ +   if ((err = prng_is_valid(wprng)) != CRYPT_OK) { +      return err;  +   } + +   /* allocate buffer to work with */ +   buf = XCALLOC(1, len); +   if (buf == NULL) { +       return CRYPT_MEM; +   } + +   do { +      /* generate value */ +      if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) { +         XFREE(buf); +         return CRYPT_ERROR_READPRNG; +      } + +      /* munge bits */ +      buf[0]     |= 0x80 | 0x40; +      buf[len-1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00); +  +      /* load value */ +      if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) { +         XFREE(buf); +         return err; +      } + +      /* test */ +      if ((err = mp_prime_is_prime(N, 8, &res)) != CRYPT_OK) { +         XFREE(buf); +         return err; +      } +   } while (res == LTC_MP_NO); + +#ifdef LTC_CLEAN_STACK +   zeromem(buf, len); +#endif + +   XFREE(buf); +   return CRYPT_OK; +} +       + + +/* $Source: /cvs/libtom/libtomcrypt/src/math/rand_prime.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:23 $ */ diff --git a/src/libtomcrypt/src/misc/base64_decode.c b/src/libtomcrypt/src/misc/base64_decode.c new file mode 100644 index 0000000..3d13393 --- /dev/null +++ b/src/libtomcrypt/src/misc/base64_decode.c @@ -0,0 +1,104 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file base64_decode.c +  Compliant base64 code donated by Wayne Scott (wscott@bitmover.com) +*/ + + +#ifdef LTC_BASE64 + +static const unsigned char map[256] = { +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63, + 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, +255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6, +  7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18, + 19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255, +255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36, + 37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48, + 49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +255, 255, 255, 255 }; + +/** +   base64 decode a block of memory +   @param in       The base64 data to decode +   @param inlen    The length of the base64 data +   @param out      [out] The destination of the binary decoded data +   @param outlen   [in/out] The max size and resulting size of the decoded data +   @return CRYPT_OK if successful +*/ +int base64_decode(const unsigned char *in,  unsigned long inlen,  +                        unsigned char *out, unsigned long *outlen) +{ +   unsigned long t, x, y, z; +   unsigned char c; +   int           g; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   g = 3; +   for (x = y = z = t = 0; x < inlen; x++) { +       c = map[in[x]&0xFF]; +       if (c == 255) continue; +       /* the final = symbols are read and used to trim the remaining bytes */ +       if (c == 254) {  +          c = 0;  +          /* prevent g < 0 which would potentially allow an overflow later */ +          if (--g < 0) { +             return CRYPT_INVALID_PACKET; +          } +       } else if (g != 3) { +          /* we only allow = to be at the end */ +          return CRYPT_INVALID_PACKET; +       } + +       t = (t<<6)|c; + +       if (++y == 4) { +          if (z + g > *outlen) {  +             return CRYPT_BUFFER_OVERFLOW;  +          } +          out[z++] = (unsigned char)((t>>16)&255); +          if (g > 1) out[z++] = (unsigned char)((t>>8)&255); +          if (g > 2) out[z++] = (unsigned char)(t&255); +          y = t = 0; +       } +   } +   if (y != 0) { +       return CRYPT_INVALID_PACKET; +   } +   *outlen = z; +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/base64/base64_decode.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_argchk.c b/src/libtomcrypt/src/misc/crypt_argchk.c new file mode 100644 index 0000000..537516d --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_argchk.c @@ -0,0 +1,30 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" +#include <signal.h> + +/** +  @file crypt_argchk.c +  Perform argument checking, Tom St Denis +*/   + +#if (ARGTYPE == 0) +void crypt_argchk(char *v, char *s, int d) +{ + fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", +         v, d, s); + (void)raise(SIGABRT); +} +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_find_hash.c b/src/libtomcrypt/src/misc/crypt_find_hash.c new file mode 100644 index 0000000..fef2d8c --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_find_hash.c @@ -0,0 +1,40 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_find_hash.c +  Find a hash, Tom St Denis +*/ + +/** +   Find a registered hash by name +   @param name   The name of the hash to look for +   @return >= 0 if found, -1 if not present +*/ +int find_hash(const char *name) +{ +   int x; +   LTC_ARGCHK(name != NULL); +   LTC_MUTEX_LOCK(<c_hash_mutex); +   for (x = 0; x < TAB_SIZE; x++) { +       if (hash_descriptor[x].name != NULL && XSTRCMP(hash_descriptor[x].name, name) == 0) { +          LTC_MUTEX_UNLOCK(<c_hash_mutex); +          return x; +       } +   } +   LTC_MUTEX_UNLOCK(<c_hash_mutex); +   return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_find_prng.c b/src/libtomcrypt/src/misc/crypt_find_prng.c new file mode 100644 index 0000000..fafbb0e --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_find_prng.c @@ -0,0 +1,41 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_find_prng.c +  Find a PRNG, Tom St Denis +*/ + +/** +   Find a registered PRNG by name +   @param name   The name of the PRNG to look for +   @return >= 0 if found, -1 if not present +*/ +int find_prng(const char *name) +{ +   int x; +   LTC_ARGCHK(name != NULL); +   LTC_MUTEX_LOCK(<c_prng_mutex); +   for (x = 0; x < TAB_SIZE; x++) { +       if ((prng_descriptor[x].name != NULL) && XSTRCMP(prng_descriptor[x].name, name) == 0) { +          LTC_MUTEX_UNLOCK(<c_prng_mutex); +          return x; +       } +   } +   LTC_MUTEX_UNLOCK(<c_prng_mutex); +   return -1; +} + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_prng.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_hash_descriptor.c b/src/libtomcrypt/src/misc/crypt_hash_descriptor.c new file mode 100644 index 0000000..5925fd2 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_hash_descriptor.c @@ -0,0 +1,27 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_hash_descriptor.c +  Stores the hash descriptor table, Tom St Denis   +*/ + +struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = { +{ NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_hash_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_hash_is_valid.c b/src/libtomcrypt/src/misc/crypt_hash_is_valid.c new file mode 100644 index 0000000..8ed5105 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_hash_is_valid.c @@ -0,0 +1,36 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_hash_is_valid.c +  Determine if hash is valid, Tom St Denis +*/   + +/* +   Test if a hash index is valid +   @param idx   The index of the hash to search for +   @return CRYPT_OK if valid +*/ +int hash_is_valid(int idx) +{ +   LTC_MUTEX_LOCK(<c_hash_mutex); +   if (idx < 0 || idx >= TAB_SIZE || hash_descriptor[idx].name == NULL) { +      LTC_MUTEX_UNLOCK(<c_hash_mutex); +      return CRYPT_INVALID_HASH; +   } +   LTC_MUTEX_UNLOCK(<c_hash_mutex); +   return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_libc.c b/src/libtomcrypt/src/misc/crypt_libc.c new file mode 100644 index 0000000..bcc89f4 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_libc.c @@ -0,0 +1,43 @@ +/*****************************************************************************/ +/* crypt_libc.c                           Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* Description:                                                              */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 05.05.10  1.00  Lad  The first version of crypt_libc.c                    */ +/*****************************************************************************/ + +// LibTomCrypt header +#include <stdlib.h> +#include "../headers/tomcrypt.h" + +void * LibTomMalloc(size_t n) +{ +    return malloc(n); +} + +void * LibTomCalloc(size_t n, size_t s) +{ +    return calloc(n, s); +} + +void * LibTomRealloc(void *p, size_t n) +{ +    return realloc(p, n); +} + +void LibTomFree(void * p) +{ +    free(p); +} + +clock_t LibTomClock(void) +{ +    return clock(); +} + +void LibTomQsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)) +{ +    qsort(base, nmemb, size, compar); +} diff --git a/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c b/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c new file mode 100644 index 0000000..91ba9d1 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c @@ -0,0 +1,13 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +ltc_math_descriptor ltc_mp; diff --git a/src/libtomcrypt/src/misc/crypt_prng_descriptor.c b/src/libtomcrypt/src/misc/crypt_prng_descriptor.c new file mode 100644 index 0000000..c5b39e0 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_prng_descriptor.c @@ -0,0 +1,26 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_prng_descriptor.c +  Stores the PRNG descriptors, Tom St Denis +*/   +struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = { +{ NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +}; + +LTC_MUTEX_GLOBAL(ltc_prng_mutex) + + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_descriptor.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_prng_is_valid.c b/src/libtomcrypt/src/misc/crypt_prng_is_valid.c new file mode 100644 index 0000000..d38fd3a --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_prng_is_valid.c @@ -0,0 +1,36 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_prng_is_valid.c +  Determine if PRNG is valid, Tom St Denis +*/ + +/* +   Test if a PRNG index is valid +   @param idx   The index of the PRNG to search for +   @return CRYPT_OK if valid +*/ +int prng_is_valid(int idx) +{ +   LTC_MUTEX_LOCK(<c_prng_mutex); +   if (idx < 0 || idx >= TAB_SIZE || prng_descriptor[idx].name == NULL) { +      LTC_MUTEX_UNLOCK(<c_prng_mutex); +      return CRYPT_INVALID_PRNG; +   } +   LTC_MUTEX_UNLOCK(<c_prng_mutex); +   return CRYPT_OK; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_is_valid.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_register_hash.c b/src/libtomcrypt/src/misc/crypt_register_hash.c new file mode 100644 index 0000000..1730091 --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_register_hash.c @@ -0,0 +1,54 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_register_hash.c +  Register a HASH, Tom St Denis +*/ + +/** +   Register a hash with the descriptor table +   @param hash   The hash you wish to register +   @return value >= 0 if successfully added (or already present), -1 if unsuccessful +*/ +int register_hash(const struct ltc_hash_descriptor *hash) +{ +   int x; + +   LTC_ARGCHK(hash != NULL); + +   /* is it already registered? */ +   LTC_MUTEX_LOCK(<c_hash_mutex); +   for (x = 0; x < TAB_SIZE; x++) { +       if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { +          LTC_MUTEX_UNLOCK(<c_hash_mutex); +          return x; +       } +   } + +   /* find a blank spot */ +   for (x = 0; x < TAB_SIZE; x++) { +       if (hash_descriptor[x].name == NULL) { +          XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)); +          LTC_MUTEX_UNLOCK(<c_hash_mutex); +          return x; +       } +   } + +   /* no spot */ +   LTC_MUTEX_UNLOCK(<c_hash_mutex); +   return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_hash.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/crypt_register_prng.c b/src/libtomcrypt/src/misc/crypt_register_prng.c new file mode 100644 index 0000000..29fc9bd --- /dev/null +++ b/src/libtomcrypt/src/misc/crypt_register_prng.c @@ -0,0 +1,54 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +  @file crypt_register_prng.c +  Register a PRNG, Tom St Denis +*/ +   +/** +   Register a PRNG with the descriptor table +   @param prng   The PRNG you wish to register +   @return value >= 0 if successfully added (or already present), -1 if unsuccessful +*/ +int register_prng(const struct ltc_prng_descriptor *prng) +{ +   int x; + +   LTC_ARGCHK(prng != NULL); + +   /* is it already registered? */ +   LTC_MUTEX_LOCK(<c_prng_mutex); +   for (x = 0; x < TAB_SIZE; x++) { +       if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) { +          LTC_MUTEX_UNLOCK(<c_prng_mutex); +          return x; +       } +   } + +   /* find a blank spot */ +   for (x = 0; x < TAB_SIZE; x++) { +       if (prng_descriptor[x].name == NULL) { +          XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)); +          LTC_MUTEX_UNLOCK(<c_prng_mutex); +          return x; +       } +   } + +   /* no spot */ +   LTC_MUTEX_UNLOCK(<c_prng_mutex); +   return -1; +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_prng.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/misc/zeromem.c b/src/libtomcrypt/src/misc/zeromem.c new file mode 100644 index 0000000..faa0efa --- /dev/null +++ b/src/libtomcrypt/src/misc/zeromem.c @@ -0,0 +1,34 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** +   @file zeromem.c +   Zero a block of memory, Tom St Denis +*/ + +/** +   Zero a block of memory +   @param out    The destination of the area to zero +   @param outlen The length of the area to zero (octets) +*/ +void zeromem(void *out, size_t outlen) +{ +   unsigned char *mem = out; +   LTC_ARGCHKVD(out != NULL); +   while (outlen-- > 0) { +      *mem++ = 0; +   } +} + +/* $Source: /cvs/libtom/libtomcrypt/src/misc/zeromem.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_bit_string.c b/src/libtomcrypt/src/pk/asn1/der_decode_bit_string.c new file mode 100644 index 0000000..e536867 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_bit_string.c @@ -0,0 +1,102 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_bit_string.c +  ASN.1 DER, encode a BIT STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Store a BIT STRING +  @param in      The DER encoded BIT STRING +  @param inlen   The size of the DER BIT STRING +  @param out     [out] The array of bits stored (one per char) +  @param outlen  [in/out] The number of bits stored +  @return CRYPT_OK if successful +*/ +int der_decode_bit_string(const unsigned char *in,  unsigned long inlen, +                                unsigned char *out, unsigned long *outlen) +{ +   unsigned long dlen, blen, x, y; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* packet must be at least 4 bytes */ +   if (inlen < 4) { +       return CRYPT_INVALID_ARG; +   } + +   /* check for 0x03 */ +   if ((in[0]&0x1F) != 0x03) { +      return CRYPT_INVALID_PACKET; +   } + +    /* offset in the data */ +    x = 1; + +   /* get the length of the data */ +   if (in[x] & 0x80) { +      /* long format get number of length bytes */ +      y = in[x++] & 0x7F; + +      /* invalid if 0 or > 2 */ +      if (y == 0 || y > 2) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read the data len */ +      dlen = 0; +      while (y--) { +         dlen = (dlen << 8) | (unsigned long)in[x++]; +      } +   } else { +      /* short format */ +      dlen = in[x++] & 0x7F; +   } +   +   /* is the data len too long or too short? */ +   if ((dlen == 0) || (dlen + x > inlen)) { +       return CRYPT_INVALID_PACKET; +   } + +   /* get padding count */ +   blen = ((dlen - 1) << 3) - (in[x++] & 7); + +   /* too many bits? */ +   if (blen > *outlen) { +      *outlen = blen; +      return CRYPT_BUFFER_OVERFLOW; +   } + +   /* decode/store the bits */ +   for (y = 0; y < blen; y++) { +       out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0; +       if ((y & 7) == 7) { +          ++x; +       } +   } + +   /* we done */ +   *outlen = blen; +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_boolean.c b/src/libtomcrypt/src/pk/asn1/der_decode_boolean.c new file mode 100644 index 0000000..617d4e8 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_boolean.c @@ -0,0 +1,47 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_boolean.c +  ASN.1 DER, decode a BOOLEAN, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Read a BOOLEAN +  @param in      The destination for the DER encoded BOOLEAN +  @param inlen   The size of the DER BOOLEAN +  @param out     [out]  The boolean to decode +  @return CRYPT_OK if successful +*/ +int der_decode_boolean(const unsigned char *in, unsigned long inlen, +                                       int *out) +{ +   LTC_ARGCHK(in  != NULL); +   LTC_ARGCHK(out != NULL); +    +   if (inlen != 3 || in[0] != 0x01 || in[1] != 0x01 || (in[2] != 0x00 && in[2] != 0xFF)) { +      return CRYPT_INVALID_ARG; +   } +    +   *out = (in[2]==0xFF) ? 1 : 0; +    +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c,v $ */ +/* $Revision: 1.2 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_choice.c b/src/libtomcrypt/src/pk/asn1/der_decode_choice.c new file mode 100644 index 0000000..44a0891 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_choice.c @@ -0,0 +1,182 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_choice.c +  ASN.1 DER, decode a CHOICE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** +   Decode a CHOICE +   @param in       The DER encoded input +   @param inlen    [in/out] The size of the input and resulting size of read type +   @param list     The list of items to decode +   @param outlen   The number of items in the list +   @return CRYPT_OK on success +*/ +int der_decode_choice(const unsigned char *in,   unsigned long *inlen, +                            ltc_asn1_list *list, unsigned long  outlen) +{ +   unsigned long size, x, z; +   void          *data; + +   LTC_ARGCHK(in    != NULL); +   LTC_ARGCHK(inlen != NULL); +   LTC_ARGCHK(list  != NULL); + +   /* get blk size */ +   if (*inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* set all of the "used" flags to zero */ +   for (x = 0; x < outlen; x++) { +       list[x].used = 0; +   } + +   /* now scan until we have a winner */ +   for (x = 0; x < outlen; x++) { +       size = list[x].size; +       data = list[x].data; + +       switch (list[x].type) { +           case LTC_ASN1_INTEGER: +               if (der_decode_integer(in, *inlen, data) == CRYPT_OK) { +                  if (der_length_integer(data, &z) == CRYPT_OK) { +                      list[x].used = 1; +                      *inlen       = z; +                      return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_SHORT_INTEGER: +               if (der_decode_short_integer(in, *inlen, data) == CRYPT_OK) { +                  if (der_length_short_integer(size, &z) == CRYPT_OK) { +                      list[x].used = 1; +                      *inlen       = z; +                      return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_BIT_STRING: +               if (der_decode_bit_string(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_bit_string(size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_OCTET_STRING: +               if (der_decode_octet_string(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_octet_string(size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_NULL: +               if (*inlen == 2 && in[x] == 0x05 && in[x+1] == 0x00) { +                  *inlen = 2; +                  list[x].used   = 1; +                  return CRYPT_OK; +               } +               break; +                   +           case LTC_ASN1_OBJECT_IDENTIFIER: +               if (der_decode_object_identifier(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_object_identifier(data, size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_IA5_STRING: +               if (der_decode_ia5_string(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_ia5_string(data, size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + + +           case LTC_ASN1_PRINTABLE_STRING: +               if (der_decode_printable_string(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_printable_string(data, size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_UTF8_STRING: +               if (der_decode_utf8_string(in, *inlen, data, &size) == CRYPT_OK) { +                  if (der_length_utf8_string(data, size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     list[x].size = size; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           case LTC_ASN1_UTCTIME: +               z = *inlen; +               if (der_decode_utctime(in, &z, data) == CRYPT_OK) { +                  list[x].used = 1; +                  *inlen       = z; +                  return CRYPT_OK; +               } +               break; + +           case LTC_ASN1_SET: +           case LTC_ASN1_SETOF: +           case LTC_ASN1_SEQUENCE: +               if (der_decode_sequence(in, *inlen, data, size) == CRYPT_OK) { +                  if (der_length_sequence(data, size, &z) == CRYPT_OK) { +                     list[x].used = 1; +                     *inlen       = z; +                     return CRYPT_OK; +                  } +               } +               break; + +           default: +               return CRYPT_INVALID_ARG; +       } +   } + +   return CRYPT_INVALID_PACKET; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/choice/der_decode_choice.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_ia5_string.c b/src/libtomcrypt/src/pk/asn1/der_decode_ia5_string.c new file mode 100644 index 0000000..f2e073b --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_ia5_string.c @@ -0,0 +1,96 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_ia5_string.c +  ASN.1 DER, encode a IA5 STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Store a IA5 STRING +  @param in      The DER encoded IA5 STRING +  @param inlen   The size of the DER IA5 STRING +  @param out     [out] The array of octets stored (one per char) +  @param outlen  [in/out] The number of octets stored +  @return CRYPT_OK if successful +*/ +int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen) +{ +   unsigned long x, y, len; +   int           t; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* must have header at least */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* check for 0x16 */ +   if ((in[0] & 0x1F) != 0x16) { +      return CRYPT_INVALID_PACKET; +   } +   x = 1; + +   /* decode the length */ +   if (in[x] & 0x80) { +      /* valid # of bytes in length are 1,2,3 */ +      y = in[x] & 0x7F; +      if ((y == 0) || (y > 3) || ((x + y) > inlen)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read the length in */ +      len = 0; +      ++x; +      while (y--) { +         len = (len << 8) | in[x++]; +      } +   } else { +      len = in[x++] & 0x7F; +   } + +   /* is it too long? */ +   if (len > *outlen) { +      *outlen = len; +      return CRYPT_BUFFER_OVERFLOW; +   } + +   if (len + x > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* read the data */ +   for (y = 0; y < len; y++) { +       t = der_ia5_value_decode(in[x++]); +       if (t == -1) { +           return CRYPT_INVALID_ARG; +       } +       out[y] = t; +   } + +   *outlen = y; + +   return CRYPT_OK; +} +  +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_integer.c b/src/libtomcrypt/src/pk/asn1/der_decode_integer.c new file mode 100644 index 0000000..cca2745 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_integer.c @@ -0,0 +1,110 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_integer.c +  ASN.1 DER, decode an integer, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Read a mp_int integer +  @param in       The DER encoded data +  @param inlen    Size of DER encoded data +  @param num      The first mp_int to decode +  @return CRYPT_OK if successful +*/ +int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num) +{ +   unsigned long x, y, z; +   int           err; + +   LTC_ARGCHK(num    != NULL); +   LTC_ARGCHK(in     != NULL); + +   /* min DER INTEGER is 0x02 01 00 == 0 */ +   if (inlen < (1 + 1 + 1)) { +      return CRYPT_INVALID_PACKET; +   } + +   /* ok expect 0x02 when we AND with 0001 1111 [1F] */ +   x = 0; +   if ((in[x++] & 0x1F) != 0x02) { +      return CRYPT_INVALID_PACKET; +   } + +   /* now decode the len stuff */ +   z = in[x++]; + +   if ((z & 0x80) == 0x00) { +      /* short form */ + +      /* will it overflow? */ +      if (x + z > inlen) { +         return CRYPT_INVALID_PACKET; +      } +      +      /* no so read it */ +      if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) { +         return err; +      } +   } else { +      /* long form */ +      z &= 0x7F; +       +      /* will number of length bytes overflow? (or > 4) */ +      if (((x + z) > inlen) || (z > 4) || (z == 0)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* now read it in */ +      y = 0; +      while (z--) { +         y = ((unsigned long)(in[x++])) | (y << 8); +      } + +      /* now will reading y bytes overrun? */ +      if ((x + y) > inlen) { +         return CRYPT_INVALID_PACKET; +      } + +      /* no so read it */ +      if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) { +         return err; +      } +   } + +   /* see if it's negative */ +   if (in[x] & 0x80) { +      void *tmp; +      if (mp_init(&tmp) != CRYPT_OK) { +         return CRYPT_MEM; +      } + +      if (mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK || mp_sub(num, tmp, num) != CRYPT_OK) { +         mp_clear(tmp); +         return CRYPT_MEM; +      } +      mp_clear(tmp); +   }  + +   return CRYPT_OK; + +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_decode_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_object_identifier.c b/src/libtomcrypt/src/pk/asn1/der_decode_object_identifier.c new file mode 100644 index 0000000..e7baae8 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_object_identifier.c @@ -0,0 +1,99 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_object_identifier.c +  ASN.1 DER, Decode Object Identifier, Tom St Denis +*/ + +#ifdef LTC_DER +/** +  Decode OID data and store the array of integers in words +  @param in      The OID DER encoded data +  @param inlen   The length of the OID data +  @param words   [out] The destination of the OID words +  @param outlen  [in/out] The number of OID words +  @return CRYPT_OK if successful +*/ +int der_decode_object_identifier(const unsigned char *in,    unsigned long  inlen, +                                       unsigned long *words, unsigned long *outlen) +{ +   unsigned long x, y, t, len; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(words  != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* header is at least 3 bytes */ +   if (inlen < 3) { +      return CRYPT_INVALID_PACKET; +   } + +   /* must be room for at least two words */ +   if (*outlen < 2) { +      return CRYPT_BUFFER_OVERFLOW; +   } + +   /* decode the packet header */ +   x = 0; +   if ((in[x++] & 0x1F) != 0x06) { +      return CRYPT_INVALID_PACKET; +   } +    +   /* get the length */ +   if (in[x] < 128) { +      len = in[x++];  +   } else { +       if (in[x] < 0x81 || in[x] > 0x82) { +          return CRYPT_INVALID_PACKET; +       } +       y   = in[x++] & 0x7F; +       len = 0; +       while (y--) { +          len = (len << 8) | (unsigned long)in[x++]; +       } +   } + +   if (len < 1 || (len + x) > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* decode words */ +   y = 0; +   t = 0; +   while (len--) { +       t = (t << 7) | (in[x] & 0x7F); +       if (!(in[x++] & 0x80)) { +           /* store t */ +           if (y >= *outlen) { +              return CRYPT_BUFFER_OVERFLOW; +           } +      if (y == 0) { +         words[0] = t / 40; +         words[1] = t % 40; +         y = 2; +      } else { +              words[y++] = t; +      } +           t          = 0; +       } +   } +        +   *outlen = y; +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_octet_string.c b/src/libtomcrypt/src/pk/asn1/der_decode_octet_string.c new file mode 100644 index 0000000..523d0ba --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_octet_string.c @@ -0,0 +1,91 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_octet_string.c +  ASN.1 DER, encode a OCTET STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Store a OCTET STRING +  @param in      The DER encoded OCTET STRING +  @param inlen   The size of the DER OCTET STRING +  @param out     [out] The array of octets stored (one per char) +  @param outlen  [in/out] The number of octets stored +  @return CRYPT_OK if successful +*/ +int der_decode_octet_string(const unsigned char *in, unsigned long inlen, +                                  unsigned char *out, unsigned long *outlen) +{ +   unsigned long x, y, len; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* must have header at least */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* check for 0x04 */ +   if ((in[0] & 0x1F) != 0x04) { +      return CRYPT_INVALID_PACKET; +   } +   x = 1; + +   /* decode the length */ +   if (in[x] & 0x80) { +      /* valid # of bytes in length are 1,2,3 */ +      y = in[x] & 0x7F; +      if ((y == 0) || (y > 3) || ((x + y) > inlen)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read the length in */ +      len = 0; +      ++x; +      while (y--) { +         len = (len << 8) | in[x++]; +      } +   } else { +      len = in[x++] & 0x7F; +   } + +   /* is it too long? */ +   if (len > *outlen) { +      *outlen = len; +      return CRYPT_BUFFER_OVERFLOW; +   } + +   if (len + x > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* read the data */ +   for (y = 0; y < len; y++) { +       out[y] = in[x++]; +   } + +   *outlen = y; + +   return CRYPT_OK; +} +  +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_printable_string.c b/src/libtomcrypt/src/pk/asn1/der_decode_printable_string.c new file mode 100644 index 0000000..f832593 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_printable_string.c @@ -0,0 +1,96 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_printable_string.c +  ASN.1 DER, encode a printable STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Store a printable STRING +  @param in      The DER encoded printable STRING +  @param inlen   The size of the DER printable STRING +  @param out     [out] The array of octets stored (one per char) +  @param outlen  [in/out] The number of octets stored +  @return CRYPT_OK if successful +*/ +int der_decode_printable_string(const unsigned char *in, unsigned long inlen, +                                unsigned char *out, unsigned long *outlen) +{ +   unsigned long x, y, len; +   int           t; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* must have header at least */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* check for 0x13 */ +   if ((in[0] & 0x1F) != 0x13) { +      return CRYPT_INVALID_PACKET; +   } +   x = 1; + +   /* decode the length */ +   if (in[x] & 0x80) { +      /* valid # of bytes in length are 1,2,3 */ +      y = in[x] & 0x7F; +      if ((y == 0) || (y > 3) || ((x + y) > inlen)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read the length in */ +      len = 0; +      ++x; +      while (y--) { +         len = (len << 8) | in[x++]; +      } +   } else { +      len = in[x++] & 0x7F; +   } + +   /* is it too long? */ +   if (len > *outlen) { +      *outlen = len; +      return CRYPT_BUFFER_OVERFLOW; +   } + +   if (len + x > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* read the data */ +   for (y = 0; y < len; y++) { +       t = der_printable_value_decode(in[x++]); +       if (t == -1) { +           return CRYPT_INVALID_ARG; +       } +       out[y] = t; +   } + +   *outlen = y; + +   return CRYPT_OK; +} +  +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c new file mode 100644 index 0000000..9b00f61 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c @@ -0,0 +1,287 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" +#include <stdarg.h> + + +/** +  @file der_decode_sequence_ex.c +  ASN.1 DER, decode a SEQUENCE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** +   Decode a SEQUENCE +   @param in       The DER encoded input +   @param inlen    The size of the input +   @param list     The list of items to decode +   @param outlen   The number of items in the list +   @param ordered  Search an unordeded or ordered list +   @return CRYPT_OK on success +*/ +int der_decode_sequence_ex(const unsigned char *in, unsigned long  inlen, +                           ltc_asn1_list *list,     unsigned long  outlen, int ordered) +{ +   int           err, type; +   unsigned long size, x, y, z, i, blksize; +   void          *data; + +   LTC_ARGCHK(in   != NULL); +   LTC_ARGCHK(list != NULL); +    +   /* get blk size */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */ +   x = 0; +   if (in[x] != 0x30 && in[x] != 0x31) { +      return CRYPT_INVALID_PACKET; +   } +   ++x; + +   if (in[x] < 128) { +      blksize = in[x++]; +   } else if (in[x] & 0x80) { +      if (in[x] < 0x81 || in[x] > 0x83) { +         return CRYPT_INVALID_PACKET; +      } +      y = in[x++] & 0x7F; + +      /* would reading the len bytes overrun? */ +      if (x + y > inlen) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read len */ +      blksize = 0; +      while (y--) { +          blksize = (blksize << 8) | (unsigned long)in[x++]; +      } +  } + +  /* would this blksize overflow? */ +  if (x + blksize > inlen) { +     return CRYPT_INVALID_PACKET; +  } + +   /* mark all as unused */ +   for (i = 0; i < outlen; i++) { +       list[i].used = 0; +   }      + +  /* ok read data */ +   inlen = blksize; +   for (i = 0; i < outlen; i++) { +       z    = 0; +       type = list[i].type; +       size = list[i].size; +       data = list[i].data; +       if (!ordered && list[i].used == 1) { continue; } + +       if (type == LTC_ASN1_EOL) {  +          break; +       } + +       switch (type) { +           case LTC_ASN1_BOOLEAN: +               z = inlen; +               if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) { +                   goto LBL_ERR; +               } +               if ((err = der_length_boolean(&z)) != CRYPT_OK) { +                   goto LBL_ERR; +                } +                break; +           +           case LTC_ASN1_INTEGER: +               z = inlen; +               if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) { +                  if (!ordered) {  continue; } +                  goto LBL_ERR; +               } +               if ((err = der_length_integer(data, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_SHORT_INTEGER: +               z = inlen; +               if ((err = der_decode_short_integer(in + x, z, data)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               if ((err = der_length_short_integer(((unsigned long*)data)[0], &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +                +               break; + +           case LTC_ASN1_BIT_STRING: +               z = inlen; +               if ((err = der_decode_bit_string(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_OCTET_STRING: +               z = inlen; +               if ((err = der_decode_octet_string(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_NULL: +               if (inlen < 2 || in[x] != 0x05 || in[x+1] != 0x00) { +                  if (!ordered) { continue; } +                  err = CRYPT_INVALID_PACKET; +                  goto LBL_ERR; +               } +               z = 2; +               break; +                   +           case LTC_ASN1_OBJECT_IDENTIFIER: +               z = inlen; +               if ((err = der_decode_object_identifier(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_object_identifier(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_IA5_STRING: +               z = inlen; +               if ((err = der_decode_ia5_string(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_ia5_string(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + + +           case LTC_ASN1_PRINTABLE_STRING: +               z = inlen; +               if ((err = der_decode_printable_string(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_printable_string(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_UTF8_STRING: +               z = inlen; +               if ((err = der_decode_utf8_string(in + x, z, data, &size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               list[i].size = size; +               if ((err = der_length_utf8_string(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_UTCTIME: +               z = inlen; +               if ((err = der_decode_utctime(in + x, &z, data)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               break; + +           case LTC_ASN1_SET: +               z = inlen; +               if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; +            +           case LTC_ASN1_SETOF: +           case LTC_ASN1_SEQUENCE: +               /* detect if we have the right type */ +               if ((type == LTC_ASN1_SETOF && (in[x] & 0x3F) != 0x31) || (type == LTC_ASN1_SEQUENCE && (in[x] & 0x3F) != 0x30)) { +                  err = CRYPT_INVALID_PACKET; +                  goto LBL_ERR; +               } + +               z = inlen; +               if ((err = der_decode_sequence(in + x, z, data, size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               if ((err = der_length_sequence(data, size, &z)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               break; + + +           case LTC_ASN1_CHOICE: +               z = inlen; +               if ((err = der_decode_choice(in + x, &z, data, size)) != CRYPT_OK) { +                  if (!ordered) { continue; } +                  goto LBL_ERR; +               } +               break; + +           default: +               err = CRYPT_INVALID_ARG; +               goto LBL_ERR; +       } +       x           += z; +       inlen       -= z; +       list[i].used = 1; +       if (!ordered) {  +          /* restart the decoder */ +          i = -1; +       }           +   } +      +   for (i = 0; i < outlen; i++) { +      if (list[i].used == 0) { +          err = CRYPT_INVALID_PACKET; +          goto LBL_ERR; +      } +   }                 +   err = CRYPT_OK;    + +LBL_ERR: +   return err; +}   +  +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c new file mode 100644 index 0000000..9c648bc --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c @@ -0,0 +1,386 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_sequence_flexi.c +  ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis +*/ + +#ifdef LTC_DER + +static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) +{ +   unsigned long x, y, z; + +   y = 0; + +   /* skip type and read len */ +   if (inlen < 2) { +      return 0xFFFFFFFF; +   } +   ++in; ++y; +    +   /* read len */ +   x = *in++; ++y; +    +   /* <128 means literal */ +   if (x < 128) { +      return x+y; +   } +   x     &= 0x7F; /* the lower 7 bits are the length of the length */ +   inlen -= 2; +    +   /* len means len of len! */ +   if (x == 0 || x > 4 || x > inlen) { +      return 0xFFFFFFFF; +   } +    +   y += x; +   z = 0; +   while (x--) {    +      z = (z<<8) | ((unsigned long)*in); +      ++in; +   } +   return z+y; +} + +/**  +   ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. +   @param in      The input buffer +   @param inlen   [in/out] The length of the input buffer and on output the amount of decoded data  +   @param out     [out] A pointer to the linked list +   @return CRYPT_OK on success. +*/    +int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) +{ +   ltc_asn1_list *l; +   unsigned long err, type, len, totlen, x, y; +   void          *realloc_tmp; +    +   LTC_ARGCHK(in    != NULL); +   LTC_ARGCHK(inlen != NULL); +   LTC_ARGCHK(out   != NULL); + +   l = NULL; +   totlen = 0; +    +   /* scan the input and and get lengths and what not */ +   while (*inlen) {      +      /* read the type byte */ +      type = *in; + +      /* fetch length */ +      len = fetch_length(in, *inlen); +      if (len > *inlen) { +         err = CRYPT_INVALID_PACKET; +         goto error; +      } + +      /* alloc new link */ +      if (l == NULL) { +         l = XCALLOC(1, sizeof(*l)); +         if (l == NULL) { +            err = CRYPT_MEM; +            goto error; +         } +      } else { +         l->next = XCALLOC(1, sizeof(*l)); +         if (l->next == NULL) { +            err = CRYPT_MEM; +            goto error; +         } +         l->next->prev = l; +         l = l->next; +      } + +      /* now switch on type */ +      switch (type) { +         case 0x01: /* BOOLEAN */ +            l->type = LTC_ASN1_BOOLEAN; +            l->size = 1; +            l->data = XCALLOC(1, sizeof(int)); +        +            if ((err = der_decode_boolean(in, *inlen, l->data)) != CRYPT_OK) { +               goto error; +            } +         +            if ((err = der_length_boolean(&len)) != CRYPT_OK) { +               goto error; +            } +            break; + +         case 0x02: /* INTEGER */ +             /* init field */ +             l->type = LTC_ASN1_INTEGER; +             l->size = 1; +             if ((err = mp_init(&l->data)) != CRYPT_OK) { +                 goto error; +             } +              +             /* decode field */ +             if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) { +                 goto error; +             } +              +             /* calc length of object */ +             if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) { +                 goto error; +             } +             break; + +         case 0x03: /* BIT */ +            /* init field */ +            l->type = LTC_ASN1_BIT_STRING; +            l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char.  */ + +            if ((l->data = XCALLOC(1, l->size)) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_bit_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) { +               goto error; +            } +            break; + +         case 0x04: /* OCTET */ + +            /* init field */ +            l->type = LTC_ASN1_OCTET_STRING; +            l->size = len; + +            if ((l->data = XCALLOC(1, l->size)) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_octet_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) { +               goto error; +            } +            break; + +         case 0x05: /* NULL */ +          +            /* valid NULL is 0x05 0x00 */ +            if (in[0] != 0x05 || in[1] != 0x00) { +               err = CRYPT_INVALID_PACKET; +               goto error; +            } +             +            /* simple to store ;-) */ +            l->type = LTC_ASN1_NULL; +            l->data = NULL; +            l->size = 0; +            len     = 2; +             +            break; +          +         case 0x06: /* OID */ +          +            /* init field */ +            l->type = LTC_ASN1_OBJECT_IDENTIFIER; +            l->size = len; + +            if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_object_identifier(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_object_identifier(l->data, l->size, &len)) != CRYPT_OK) { +               goto error; +            } +             +            /* resize it to save a bunch of mem */ +            if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) { +               /* out of heap but this is not an error */ +               break; +            } +            l->data = realloc_tmp; +            break; +   +         case 0x0C: /* UTF8 */ +          +            /* init field */ +            l->type = LTC_ASN1_UTF8_STRING; +            l->size = len; + +            if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_utf8_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_utf8_string(l->data, l->size, &len)) != CRYPT_OK) { +               goto error; +            } +            break; + +         case 0x13: /* PRINTABLE */ +          +            /* init field */ +            l->type = LTC_ASN1_PRINTABLE_STRING; +            l->size = len; + +            if ((l->data = XCALLOC(1, l->size)) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_printable_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_printable_string(l->data, l->size, &len)) != CRYPT_OK) { +               goto error; +            } +            break; +          +         case 0x16: /* IA5 */ +          +            /* init field */ +            l->type = LTC_ASN1_IA5_STRING; +            l->size = len; + +            if ((l->data = XCALLOC(1, l->size)) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            if ((err = der_decode_ia5_string(in, *inlen, l->data, &l->size)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_ia5_string(l->data, l->size, &len)) != CRYPT_OK) { +               goto error; +            } +            break; +          +         case 0x17: /* UTC TIME */ +          +            /* init field */ +            l->type = LTC_ASN1_UTCTIME; +            l->size = 1; + +            if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) { +               err = CRYPT_MEM; +               goto error; +            } +             +            len = *inlen; +            if ((err = der_decode_utctime(in, &len, l->data)) != CRYPT_OK) { +               goto error; +            } +             +            if ((err = der_length_utctime(l->data, &len)) != CRYPT_OK) { +               goto error; +            } +            break; +          +         case 0x30: /* SEQUENCE */ +         case 0x31: /* SET */ +          +             /* init field */ +             l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET; +              +             /* we have to decode the SEQUENCE header and get it's length */ +              +                /* move past type */ +                ++in; --(*inlen); +                 +                /* read length byte */ +                x = *in++; --(*inlen); +                 +                /* smallest SEQUENCE/SET header */ +                y = 2; +                 +                /* now if it's > 127 the next bytes are the length of the length */ +                if (x > 128) { +                   x      &= 0x7F; +                   in     += x; +                   *inlen -= x; +                    +                   /* update sequence header len */ +                   y      += x; +                } +              +             /* Sequence elements go as child */ +             len = len - y; +             if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) { +                goto error; +             } +              +             /* len update */ +             totlen += y; +              +             /* link them up y0 */ +             l->child->parent = l; +              +             break; +         default: +           /* invalid byte ... this is a soft error */ +           /* remove link */ +           l       = l->prev; +           XFREE(l->next); +           l->next = NULL; +           goto outside; +      } +       +      /* advance pointers */ +      totlen  += len; +      in      += len; +      *inlen  -= len; +   } +    +outside:    + +   /* rewind l please */ +   while (l->prev != NULL || l->parent != NULL) { +      if (l->parent != NULL) { +         l = l->parent; +      } else { +         l = l->prev; +      } +   } +    +   /* return */ +   *out   = l; +   *inlen = totlen; +   return CRYPT_OK; + +error: +   /* free list */ +   der_sequence_free(l); + +   return err; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c new file mode 100644 index 0000000..ff633df --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c @@ -0,0 +1,139 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" +#include <stdarg.h> + + +/** +  @file der_decode_sequence_multi.c +  ASN.1 DER, decode a SEQUENCE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** +  Decode a SEQUENCE type using a VA list +  @param in    Input buffer +  @param inlen Length of input in octets +  @remark <...> is of the form <type, size, data> (int, unsigned long, void*) +  @return CRYPT_OK on success +*/   +int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) +{ +   int           err, type; +   unsigned long size, x; +   void          *data; +   va_list       args; +   ltc_asn1_list *list; + +   LTC_ARGCHK(in    != NULL); + +   /* get size of output that will be required */ +   va_start(args, inlen); +   x = 0; +   for (;;) { +       type = va_arg(args, int); +       size = va_arg(args, unsigned long); +       data = va_arg(args, void*); + +       if (type == LTC_ASN1_EOL) {  +          break; +       } + +       switch (type) { +           case LTC_ASN1_BOOLEAN: +           case LTC_ASN1_INTEGER: +           case LTC_ASN1_SHORT_INTEGER: +           case LTC_ASN1_BIT_STRING: +           case LTC_ASN1_OCTET_STRING: +           case LTC_ASN1_NULL: +           case LTC_ASN1_OBJECT_IDENTIFIER: +           case LTC_ASN1_IA5_STRING: +           case LTC_ASN1_PRINTABLE_STRING: +           case LTC_ASN1_UTF8_STRING: +           case LTC_ASN1_UTCTIME: +           case LTC_ASN1_SET: +           case LTC_ASN1_SETOF: +           case LTC_ASN1_SEQUENCE: +           case LTC_ASN1_CHOICE: +                ++x;  +                break; +           +           default: +               va_end(args); +               return CRYPT_INVALID_ARG; +       } +   } +   va_end(args); + +   /* allocate structure for x elements */ +   if (x == 0) { +      return CRYPT_NOP; +   } + +   list = XCALLOC(sizeof(*list), x); +   if (list == NULL) { +      return CRYPT_MEM; +   } + +   /* fill in the structure */ +   va_start(args, inlen); +   x = 0; +   for (;;) { +       type = va_arg(args, int); +       size = va_arg(args, unsigned long); +       data = va_arg(args, void*); + +       if (type == LTC_ASN1_EOL) {  +          break; +       } + +       switch (type) { +           case LTC_ASN1_BOOLEAN: +           case LTC_ASN1_INTEGER: +           case LTC_ASN1_SHORT_INTEGER: +           case LTC_ASN1_BIT_STRING: +           case LTC_ASN1_OCTET_STRING: +           case LTC_ASN1_NULL: +           case LTC_ASN1_OBJECT_IDENTIFIER: +           case LTC_ASN1_IA5_STRING: +           case LTC_ASN1_PRINTABLE_STRING: +           case LTC_ASN1_UTF8_STRING: +           case LTC_ASN1_UTCTIME: +           case LTC_ASN1_SEQUENCE: +           case LTC_ASN1_SET: +           case LTC_ASN1_SETOF:           +           case LTC_ASN1_CHOICE: +                list[x].type   = type; +                list[x].size   = size; +                list[x++].data = data; +                break; +          +           default: +               va_end(args); +               err = CRYPT_INVALID_ARG; +               goto LBL_ERR; +       } +   } +   va_end(args); + +   err = der_decode_sequence(in, inlen, list, x); +LBL_ERR: +   XFREE(list); +   return err; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c b/src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c new file mode 100644 index 0000000..907e4e1 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c @@ -0,0 +1,68 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_short_integer.c +  ASN.1 DER, decode an integer, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Read a short integer +  @param in       The DER encoded data +  @param inlen    Size of data +  @param num      [out] The integer to decode +  @return CRYPT_OK if successful +*/ +int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num) +{ +   unsigned long len, x, y; + +   LTC_ARGCHK(num    != NULL); +   LTC_ARGCHK(in     != NULL); + +   /* check length */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* check header */ +   x = 0; +   if ((in[x++] & 0x1F) != 0x02) { +      return CRYPT_INVALID_PACKET; +   } + +   /* get the packet len */ +   len = in[x++]; + +   if (x + len > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* read number */ +   y = 0; +   while (len--) { +      y = (y<<8) | (unsigned long)in[x++]; +   } +   *num = y; + +   return CRYPT_OK; + +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_utctime.c b/src/libtomcrypt/src/pk/asn1/der_decode_utctime.c new file mode 100644 index 0000000..7f3f0d7 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_utctime.c @@ -0,0 +1,127 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_utctime.c +  ASN.1 DER, decode a  UTCTIME, Tom St Denis +*/ + +#ifdef LTC_DER + +static int char_to_int(unsigned char x) +{ +   switch (x)  { +      case '0': return 0; +      case '1': return 1; +      case '2': return 2; +      case '3': return 3; +      case '4': return 4; +      case '5': return 5; +      case '6': return 6; +      case '7': return 7; +      case '8': return 8; +      case '9': return 9; +   } +   return 100; +} + +#define DECODE_V(y, max) \ +   y  = char_to_int(buf[x])*10 + char_to_int(buf[x+1]); \ +   if (y >= max) return CRYPT_INVALID_PACKET;           \ +   x += 2; + +/** +  Decodes a UTC time structure in DER format (reads all 6 valid encoding formats) +  @param in     Input buffer +  @param inlen  Length of input buffer in octets +  @param out    [out] Destination of UTC time structure +  @return CRYPT_OK   if successful +*/ +int der_decode_utctime(const unsigned char *in, unsigned long *inlen, +                             ltc_utctime   *out) +{ +   unsigned char buf[32]; +   unsigned long x; +   int           y; + +   LTC_ARGCHK(in    != NULL); +   LTC_ARGCHK(inlen != NULL); +   LTC_ARGCHK(out   != NULL); + +   /* check header */ +   if (*inlen < 2UL || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) { +      return CRYPT_INVALID_PACKET; +   } + +   /* decode the string */ +   for (x = 0; x < in[1]; x++) { +       y = der_ia5_value_decode(in[x+2]); +       if (y == -1) { +          return CRYPT_INVALID_PACKET; +       } +       buf[x] = y; +   } +   *inlen = 2 + x; + + +   /* possible encodings are  +YYMMDDhhmmZ +YYMMDDhhmm+hh'mm' +YYMMDDhhmm-hh'mm' +YYMMDDhhmmssZ +YYMMDDhhmmss+hh'mm' +YYMMDDhhmmss-hh'mm' + +    So let's do a trivial decode upto [including] mm  +   */ + +    x = 0; +    DECODE_V(out->YY, 100); +    DECODE_V(out->MM, 13); +    DECODE_V(out->DD, 32); +    DECODE_V(out->hh, 24); +    DECODE_V(out->mm, 60); + +    /* clear timezone and seconds info */ +    out->off_dir = out->off_hh = out->off_mm = out->ss = 0; + +    /* now is it Z, +, - or 0-9 */ +    if (buf[x] == 'Z') { +       return CRYPT_OK; +    } else if (buf[x] == '+' || buf[x] == '-') { +       out->off_dir = (buf[x++] == '+') ? 0 : 1; +       DECODE_V(out->off_hh, 24); +       DECODE_V(out->off_mm, 60); +       return CRYPT_OK; +    } + +    /* decode seconds */ +    DECODE_V(out->ss, 60); + +    /* now is it Z, +, - */ +    if (buf[x] == 'Z') { +       return CRYPT_OK; +    } else if (buf[x] == '+' || buf[x] == '-') { +       out->off_dir = (buf[x++] == '+') ? 0 : 1; +       DECODE_V(out->off_hh, 24); +       DECODE_V(out->off_mm, 60); +       return CRYPT_OK; +    } else { +       return CRYPT_INVALID_PACKET; +    } +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c b/src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c new file mode 100644 index 0000000..898d6cd --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c @@ -0,0 +1,111 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_decode_utf8_string.c +  ASN.1 DER, encode a UTF8 STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** +  Store a UTF8 STRING +  @param in      The DER encoded UTF8 STRING +  @param inlen   The size of the DER UTF8 STRING +  @param out     [out] The array of utf8s stored (one per char) +  @param outlen  [in/out] The number of utf8s stored +  @return CRYPT_OK if successful +*/ +int der_decode_utf8_string(const unsigned char *in,  unsigned long inlen, +                                       wchar_t *out, unsigned long *outlen) +{ +   wchar_t       tmp; +   unsigned long x, y, z, len; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); + +   /* must have header at least */ +   if (inlen < 2) { +      return CRYPT_INVALID_PACKET; +   } + +   /* check for 0x0C */ +   if ((in[0] & 0x1F) != 0x0C) { +      return CRYPT_INVALID_PACKET; +   } +   x = 1; + +   /* decode the length */ +   if (in[x] & 0x80) { +      /* valid # of bytes in length are 1,2,3 */ +      y = in[x] & 0x7F; +      if ((y == 0) || (y > 3) || ((x + y) > inlen)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* read the length in */ +      len = 0; +      ++x; +      while (y--) { +         len = (len << 8) | in[x++]; +      } +   } else { +      len = in[x++] & 0x7F; +   } + +   if (len + x > inlen) { +      return CRYPT_INVALID_PACKET; +   } + +   /* proceed to decode */ +   for (y = 0; x < inlen; ) { +      /* get first byte */ +      tmp = in[x++]; +  +      /* count number of bytes */ +      for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF); +       +      if (z > 4 || (x + (z - 1) > inlen)) { +         return CRYPT_INVALID_PACKET; +      } + +      /* decode, grab upper bits */ +      tmp >>= z; + +      /* grab remaining bytes */ +      if (z > 1) { --z; } +      while (z-- != 0) { +         if ((in[x] & 0xC0) != 0x80) { +            return CRYPT_INVALID_PACKET; +         } +         tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F); +      } + +      if (y > *outlen) { +         *outlen = y; +         return CRYPT_BUFFER_OVERFLOW; +      } +      out[y++] = tmp; +   } +   *outlen = y; + +   return CRYPT_OK; +} +  +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_bit_string.c b/src/libtomcrypt/src/pk/asn1/der_length_bit_string.c new file mode 100644 index 0000000..2bffa3b --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_bit_string.c @@ -0,0 +1,54 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_bit_string.c +  ASN.1 DER, get length of BIT STRING, Tom St Denis +*/ + +#ifdef LTC_DER +/** +  Gets length of DER encoding of BIT STRING  +  @param nbits  The number of bits in the string to encode +  @param outlen [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_bit_string(unsigned long nbits, unsigned long *outlen) +{ +   unsigned long nbytes; +   LTC_ARGCHK(outlen != NULL); + +   /* get the number of the bytes */ +   nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1; +  +   if (nbytes < 128) { +      /* 03 LL PP DD DD DD ... */ +      *outlen = 2 + nbytes; +   } else if (nbytes < 256) { +      /* 03 81 LL PP DD DD DD ... */ +      *outlen = 3 + nbytes; +   } else if (nbytes < 65536) { +      /* 03 82 LL LL PP DD DD DD ... */ +      *outlen = 4 + nbytes; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_boolean.c b/src/libtomcrypt/src/pk/asn1/der_length_boolean.c new file mode 100644 index 0000000..e34ce5c --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_boolean.c @@ -0,0 +1,35 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_boolean.c +  ASN.1 DER, get length of a BOOLEAN, Tom St Denis +*/ + +#ifdef LTC_DER +/** +  Gets length of DER encoding of a BOOLEAN  +  @param outlen [out] The length of the DER encoding +  @return CRYPT_OK if successful +*/ +int der_length_boolean(unsigned long *outlen) +{ +   LTC_ARGCHK(outlen != NULL); +   *outlen = 3; +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c b/src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c new file mode 100644 index 0000000..473bc79 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c @@ -0,0 +1,194 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_ia5_string.c +  ASN.1 DER, get length of IA5 STRING, Tom St Denis +*/ + +#ifdef LTC_DER + +static const struct { +   int code, value; +} ia5_table[] = { +{ '\0', 0 }, +{ '\a', 7 },  +{ '\b', 8 },  +{ '\t', 9 },  +{ '\n', 10 },  +{ '\f', 12 },  +{ '\r', 13 },  +{ ' ', 32 },  +{ '!', 33 },  +{ '"', 34 },  +{ '#', 35 },  +{ '$', 36 },  +{ '%', 37 },  +{ '&', 38 },  +{ '\'', 39 },  +{ '(', 40 },  +{ ')', 41 },  +{ '*', 42 },  +{ '+', 43 },  +{ ',', 44 },  +{ '-', 45 },  +{ '.', 46 },  +{ '/', 47 },  +{ '0', 48 },  +{ '1', 49 },  +{ '2', 50 },  +{ '3', 51 },  +{ '4', 52 },  +{ '5', 53 },  +{ '6', 54 },  +{ '7', 55 },  +{ '8', 56 },  +{ '9', 57 },  +{ ':', 58 },  +{ ';', 59 },  +{ '<', 60 },  +{ '=', 61 },  +{ '>', 62 },  +{ '?', 63 },  +{ '@', 64 },  +{ 'A', 65 },  +{ 'B', 66 },  +{ 'C', 67 },  +{ 'D', 68 },  +{ 'E', 69 },  +{ 'F', 70 },  +{ 'G', 71 },  +{ 'H', 72 },  +{ 'I', 73 },  +{ 'J', 74 },  +{ 'K', 75 },  +{ 'L', 76 },  +{ 'M', 77 },  +{ 'N', 78 },  +{ 'O', 79 },  +{ 'P', 80 },  +{ 'Q', 81 },  +{ 'R', 82 },  +{ 'S', 83 },  +{ 'T', 84 },  +{ 'U', 85 },  +{ 'V', 86 },  +{ 'W', 87 },  +{ 'X', 88 },  +{ 'Y', 89 },  +{ 'Z', 90 },  +{ '[', 91 },  +{ '\\', 92 },  +{ ']', 93 },  +{ '^', 94 },  +{ '_', 95 },  +{ '`', 96 },  +{ 'a', 97 },  +{ 'b', 98 },  +{ 'c', 99 },  +{ 'd', 100 },  +{ 'e', 101 },  +{ 'f', 102 },  +{ 'g', 103 },  +{ 'h', 104 },  +{ 'i', 105 },  +{ 'j', 106 },  +{ 'k', 107 },  +{ 'l', 108 },  +{ 'm', 109 },  +{ 'n', 110 },  +{ 'o', 111 },  +{ 'p', 112 },  +{ 'q', 113 },  +{ 'r', 114 },  +{ 's', 115 },  +{ 't', 116 },  +{ 'u', 117 },  +{ 'v', 118 },  +{ 'w', 119 },  +{ 'x', 120 },  +{ 'y', 121 },  +{ 'z', 122 },  +{ '{', 123 },  +{ '|', 124 },  +{ '}', 125 },  +{ '~', 126 } +}; + +int der_ia5_char_encode(int c) +{ +   int x; +   for (x = 0; x < (int)(sizeof(ia5_table)/sizeof(ia5_table[0])); x++) { +       if (ia5_table[x].code == c) { +          return ia5_table[x].value; +       } +   } +   return -1; +} + +int der_ia5_value_decode(int v) +{ +   int x; +   for (x = 0; x < (int)(sizeof(ia5_table)/sizeof(ia5_table[0])); x++) { +       if (ia5_table[x].value == v) { +          return ia5_table[x].code; +       } +   } +   return -1; +} +    +/** +  Gets length of DER encoding of IA5 STRING  +  @param octets   The values you want to encode  +  @param noctets  The number of octets in the string to encode +  @param outlen   [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) +{ +   unsigned long x; + +   LTC_ARGCHK(outlen != NULL); +   LTC_ARGCHK(octets != NULL); + +   /* scan string for validity */ +   for (x = 0; x < noctets; x++) { +       if (der_ia5_char_encode(octets[x]) == -1) { +          return CRYPT_INVALID_ARG; +       } +   } + +   if (noctets < 128) { +      /* 16 LL DD DD DD ... */ +      *outlen = 2 + noctets; +   } else if (noctets < 256) { +      /* 16 81 LL DD DD DD ... */ +      *outlen = 3 + noctets; +   } else if (noctets < 65536UL) { +      /* 16 82 LL LL DD DD DD ... */ +      *outlen = 4 + noctets; +   } else if (noctets < 16777216UL) { +      /* 16 83 LL LL LL DD DD DD ... */ +      *outlen = 5 + noctets; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_integer.c b/src/libtomcrypt/src/pk/asn1/der_length_integer.c new file mode 100644 index 0000000..540d205 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_integer.c @@ -0,0 +1,82 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_integer.c +  ASN.1 DER, get length of encoding, Tom St Denis +*/ + + +#ifdef LTC_DER +/** +  Gets length of DER encoding of num  +  @param num    The int to get the size of  +  @param outlen [out] The length of the DER encoding for the given integer +  @return CRYPT_OK if successful +*/ +int der_length_integer(void *num, unsigned long *outlen) +{ +   unsigned long z, len; +   int           leading_zero; + +   LTC_ARGCHK(num     != NULL); +   LTC_ARGCHK(outlen  != NULL); + +   if (mp_cmp_d(num, 0) != LTC_MP_LT) { +      /* positive */ + +      /* we only need a leading zero if the msb of the first byte is one */ +      if ((mp_count_bits(num) & 7) == 0 || mp_iszero(num) == LTC_MP_YES) { +         leading_zero = 1; +      } else { +         leading_zero = 0; +      } + +      /* size for bignum */ +      z = len = leading_zero + mp_unsigned_bin_size(num); +   } else { +      /* it's negative */ +      /* find power of 2 that is a multiple of eight and greater than count bits */ +      leading_zero = 0; +      z = mp_count_bits(num); +      z = z + (8 - (z & 7)); +      if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) --z; +      len = z = z >> 3; +   } + +   /* now we need a length */ +   if (z < 128) { +      /* short form */ +      ++len; +   } else { +      /* long form (relies on z != 0), assumes length bytes < 128 */ +      ++len; + +      while (z) { +         ++len; +         z >>= 8; +      } +   } + +   /* we need a 0x02 to indicate it's INTEGER */ +   ++len; + +   /* return length */ +   *outlen = len;  +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_length_integer.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_object_identifier.c b/src/libtomcrypt/src/pk/asn1/der_length_object_identifier.c new file mode 100644 index 0000000..94c326f --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_object_identifier.c @@ -0,0 +1,89 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_object_identifier.c +  ASN.1 DER, get length of Object Identifier, Tom St Denis +*/ + +#ifdef LTC_DER + +unsigned long der_object_identifier_bits(unsigned long x) +{ +   unsigned long c; +   x &= 0xFFFFFFFF; +   c  = 0; +   while (x) { +     ++c; +     x >>= 1; +   } +   return c; +} + + +/** +  Gets length of DER encoding of Object Identifier +  @param nwords   The number of OID words  +  @param words    The actual OID words to get the size of +  @param outlen   [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen) +{ +   unsigned long y, z, t, wordbuf;    + +   LTC_ARGCHK(words  != NULL); +   LTC_ARGCHK(outlen != NULL); + + +   /* must be >= 2 words */ +   if (nwords < 2) { +      return CRYPT_INVALID_ARG; +   } + +   /* word1 = 0,1,2,3 and word2 0..39 */ +   if (words[0] > 3 || (words[0] < 2 && words[1] > 39)) { +      return CRYPT_INVALID_ARG; +   } + +   /* leading word is the first two */ +   z = 0; +   wordbuf = words[0] * 40 + words[1]; +   for (y = 1; y < nwords; y++) { +       t = der_object_identifier_bits(wordbuf); +       z += t/7 + ((t%7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); +       if (y < nwords - 1) { +          /* grab next word */ +          wordbuf = words[y+1]; +       } +   } + +   /* now depending on the length our length encoding changes */ +   if (z < 128) { +      z += 2; +   } else if (z < 256) { +      z += 3; +   } else if (z < 65536UL) { +      z += 4; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   *outlen = z; +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_octet_string.c b/src/libtomcrypt/src/pk/asn1/der_length_octet_string.c new file mode 100644 index 0000000..acd4053 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_octet_string.c @@ -0,0 +1,53 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_octet_string.c +  ASN.1 DER, get length of OCTET STRING, Tom St Denis +*/ + +#ifdef LTC_DER +/** +  Gets length of DER encoding of OCTET STRING  +  @param noctets  The number of octets in the string to encode +  @param outlen   [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_octet_string(unsigned long noctets, unsigned long *outlen) +{ +   LTC_ARGCHK(outlen != NULL); + +   if (noctets < 128) { +      /* 04 LL DD DD DD ... */ +      *outlen = 2 + noctets; +   } else if (noctets < 256) { +      /* 04 81 LL DD DD DD ... */ +      *outlen = 3 + noctets; +   } else if (noctets < 65536UL) { +      /* 04 82 LL LL DD DD DD ... */ +      *outlen = 4 + noctets; +   } else if (noctets < 16777216UL) { +      /* 04 83 LL LL LL DD DD DD ... */ +      *outlen = 5 + noctets; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_printable_string.c b/src/libtomcrypt/src/pk/asn1/der_length_printable_string.c new file mode 100644 index 0000000..ef1ed0e --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_printable_string.c @@ -0,0 +1,166 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_printable_string.c +  ASN.1 DER, get length of Printable STRING, Tom St Denis +*/ + +#ifdef LTC_DER + +static const struct { +   int code, value; +} printable_table[] = { +{ ' ', 32 },  +{ '\'', 39 },  +{ '(', 40 },  +{ ')', 41 },  +{ '+', 43 },  +{ ',', 44 },  +{ '-', 45 },  +{ '.', 46 },  +{ '/', 47 },  +{ '0', 48 },  +{ '1', 49 },  +{ '2', 50 },  +{ '3', 51 },  +{ '4', 52 },  +{ '5', 53 },  +{ '6', 54 },  +{ '7', 55 },  +{ '8', 56 },  +{ '9', 57 },  +{ ':', 58 },  +{ '=', 61 },  +{ '?', 63 },  +{ 'A', 65 },  +{ 'B', 66 },  +{ 'C', 67 },  +{ 'D', 68 },  +{ 'E', 69 },  +{ 'F', 70 },  +{ 'G', 71 },  +{ 'H', 72 },  +{ 'I', 73 },  +{ 'J', 74 },  +{ 'K', 75 },  +{ 'L', 76 },  +{ 'M', 77 },  +{ 'N', 78 },  +{ 'O', 79 },  +{ 'P', 80 },  +{ 'Q', 81 },  +{ 'R', 82 },  +{ 'S', 83 },  +{ 'T', 84 },  +{ 'U', 85 },  +{ 'V', 86 },  +{ 'W', 87 },  +{ 'X', 88 },  +{ 'Y', 89 },  +{ 'Z', 90 },  +{ 'a', 97 },  +{ 'b', 98 },  +{ 'c', 99 },  +{ 'd', 100 },  +{ 'e', 101 },  +{ 'f', 102 },  +{ 'g', 103 },  +{ 'h', 104 },  +{ 'i', 105 },  +{ 'j', 106 },  +{ 'k', 107 },  +{ 'l', 108 },  +{ 'm', 109 },  +{ 'n', 110 },  +{ 'o', 111 },  +{ 'p', 112 },  +{ 'q', 113 },  +{ 'r', 114 },  +{ 's', 115 },  +{ 't', 116 },  +{ 'u', 117 },  +{ 'v', 118 },  +{ 'w', 119 },  +{ 'x', 120 },  +{ 'y', 121 },  +{ 'z', 122 },  +}; + +int der_printable_char_encode(int c) +{ +   int x; +   for (x = 0; x < (int)(sizeof(printable_table)/sizeof(printable_table[0])); x++) { +       if (printable_table[x].code == c) { +          return printable_table[x].value; +       } +   } +   return -1; +} + +int der_printable_value_decode(int v) +{ +   int x; +   for (x = 0; x < (int)(sizeof(printable_table)/sizeof(printable_table[0])); x++) { +       if (printable_table[x].value == v) { +          return printable_table[x].code; +       } +   } +   return -1; +} +    +/** +  Gets length of DER encoding of Printable STRING  +  @param octets   The values you want to encode  +  @param noctets  The number of octets in the string to encode +  @param outlen   [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) +{ +   unsigned long x; + +   LTC_ARGCHK(outlen != NULL); +   LTC_ARGCHK(octets != NULL); + +   /* scan string for validity */ +   for (x = 0; x < noctets; x++) { +       if (der_printable_char_encode(octets[x]) == -1) { +          return CRYPT_INVALID_ARG; +       } +   } + +   if (noctets < 128) { +      /* 16 LL DD DD DD ... */ +      *outlen = 2 + noctets; +   } else if (noctets < 256) { +      /* 16 81 LL DD DD DD ... */ +      *outlen = 3 + noctets; +   } else if (noctets < 65536UL) { +      /* 16 82 LL LL DD DD DD ... */ +      *outlen = 4 + noctets; +   } else if (noctets < 16777216UL) { +      /* 16 83 LL LL LL DD DD DD ... */ +      *outlen = 5 + noctets; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_sequence.c b/src/libtomcrypt/src/pk/asn1/der_length_sequence.c new file mode 100644 index 0000000..e75ed7e --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_sequence.c @@ -0,0 +1,169 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_sequence.c +  ASN.1 DER, length a SEQUENCE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** +   Get the length of a DER sequence  +   @param list   The sequences of items in the SEQUENCE +   @param inlen  The number of items +   @param outlen [out] The length required in octets to store it  +   @return CRYPT_OK on success +*/ +int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, +                        unsigned long *outlen)  +{ +   int           err, type; +   unsigned long size, x, y, z, i; +   void          *data; + +   LTC_ARGCHK(list    != NULL); +   LTC_ARGCHK(outlen  != NULL); + +   /* get size of output that will be required */ +   y = 0; +   for (i = 0; i < inlen; i++) { +       type = list[i].type; +       size = list[i].size; +       data = list[i].data; + +       if (type == LTC_ASN1_EOL) {  +          break; +       } + +       switch (type) { +           case LTC_ASN1_BOOLEAN: +              if ((err = der_length_boolean(&x)) != CRYPT_OK) { +                 goto LBL_ERR; +              } +              y += x; +              break; +           +           case LTC_ASN1_INTEGER: +               if ((err = der_length_integer(data, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_SHORT_INTEGER: +               if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_BIT_STRING: +               if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_OCTET_STRING: +               if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_NULL: +               y += 2; +               break; + +           case LTC_ASN1_OBJECT_IDENTIFIER: +               if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_IA5_STRING: +               if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_PRINTABLE_STRING: +               if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_UTCTIME: +               if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_UTF8_STRING: +               if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           case LTC_ASN1_SET: +           case LTC_ASN1_SETOF: +           case LTC_ASN1_SEQUENCE: +               if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { +                  goto LBL_ERR; +               } +               y += x; +               break; + +           +           default: +               err = CRYPT_INVALID_ARG; +               goto LBL_ERR; +       } +   } + +   /* calc header size */ +   z = y; +   if (y < 128) { +      y += 2; +   } else if (y < 256) { +      /* 0x30 0x81 LL */ +      y += 3; +   } else if (y < 65536UL) { +      /* 0x30 0x82 LL LL */ +      y += 4; +   } else if (y < 16777216UL) { +      /* 0x30 0x83 LL LL LL */ +      y += 5; +   } else { +      err = CRYPT_INVALID_ARG; +      goto LBL_ERR; +   } + +   /* store size */ +   *outlen = y; +   err     = CRYPT_OK; + +LBL_ERR: +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c,v $ */ +/* $Revision: 1.14 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_short_integer.c b/src/libtomcrypt/src/pk/asn1/der_length_short_integer.c new file mode 100644 index 0000000..afa6dd0 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_short_integer.c @@ -0,0 +1,70 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_short_integer.c +  ASN.1 DER, get length of encoding, Tom St Denis +*/ + + +#ifdef LTC_DER +/** +  Gets length of DER encoding of num  +  @param num    The integer to get the size of  +  @param outlen [out] The length of the DER encoding for the given integer +  @return CRYPT_OK if successful +*/ +int der_length_short_integer(unsigned long num, unsigned long *outlen) +{ +   unsigned long z, y, len; + +   LTC_ARGCHK(outlen  != NULL); + +   /* force to 32 bits */ +   num &= 0xFFFFFFFFUL; + +   /* get the number of bytes */ +   z = 0; +   y = num; +   while (y) { +     ++z; +     y >>= 8; +   } +    +   /* handle zero */ +   if (z == 0) { +      z = 1; +   } + +   /* we need a 0x02 to indicate it's INTEGER */ +   len = 1; + +   /* length byte */ +   ++len; + +   /* bytes in value */ +   len += z; + +   /* see if msb is set */ +   len += (num&(1UL<<((z<<3) - 1))) ? 1 : 0; + +   /* return length */ +   *outlen = len;  +    +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_utctime.c b/src/libtomcrypt/src/pk/asn1/der_length_utctime.c new file mode 100644 index 0000000..1296bab --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_utctime.c @@ -0,0 +1,46 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_utctime.c +  ASN.1 DER, get length of UTCTIME, Tom St Denis +*/ + +#ifdef LTC_DER + +/** +  Gets length of DER encoding of UTCTIME +  @param utctime      The UTC time structure to get the size of +  @param outlen [out] The length of the DER encoding +  @return CRYPT_OK if successful +*/ +int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen) +{ +   LTC_ARGCHK(outlen  != NULL); +   LTC_ARGCHK(utctime != NULL); + +   if (utctime->off_hh == 0 && utctime->off_mm == 0) { +      /* we encode as YYMMDDhhmmssZ */ +      *outlen = 2 + 13; +   } else { +      /* we encode as YYMMDDhhmmss{+|-}hh'mm' */ +      *outlen = 2 + 17; +   } + +   return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_length_utf8_string.c b/src/libtomcrypt/src/pk/asn1/der_length_utf8_string.c new file mode 100644 index 0000000..514db84 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_length_utf8_string.c @@ -0,0 +1,83 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_length_utf8_string.c +  ASN.1 DER, get length of UTF8 STRING, Tom St Denis +*/ + +#ifdef LTC_DER + +/** Return the size in bytes of a UTF-8 character +  @param c   The UTF-8 character to measure +  @return    The size in bytes +*/ +unsigned long der_utf8_charsize(const wchar_t c) +{ +   if (c <= 0x7F) { +      return 1; +   } else if (c <= 0x7FF) { +      return 2; +   } else if (c <= 0xFFFF) { +      return 3; +   } else { +      return 4; +   } +} + +/** +  Gets length of DER encoding of UTF8 STRING  +  @param in       The characters to measure the length of +  @param noctets  The number of octets in the string to encode +  @param outlen   [out] The length of the DER encoding for the given string +  @return CRYPT_OK if successful +*/ +int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen) +{ +   unsigned long x, len; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(outlen != NULL); + +   len = 0; +   for (x = 0; x < noctets; x++) { +      if (in[x] < 0 || in[x] > 0x10FFFF) { +         return CRYPT_INVALID_ARG; +      } +      len += der_utf8_charsize(in[x]); +   } + +   if (len < 128) { +      /* 0C LL DD DD DD ... */ +      *outlen = 2 + len; +   } else if (len < 256) { +      /* 0C 81 LL DD DD DD ... */ +      *outlen = 3 + len; +   } else if (len < 65536UL) { +      /* 0C 82 LL LL DD DD DD ... */ +      *outlen = 4 + len; +   } else if (len < 16777216UL) { +      /* 0C 83 LL LL LL DD DD DD ... */ +      *outlen = 5 + len; +   } else { +      return CRYPT_INVALID_ARG; +   } + +   return CRYPT_OK; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/asn1/der_sequence_free.c b/src/libtomcrypt/src/pk/asn1/der_sequence_free.c new file mode 100644 index 0000000..4887215 --- /dev/null +++ b/src/libtomcrypt/src/pk/asn1/der_sequence_free.c @@ -0,0 +1,65 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file der_sequence_free.c +  ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis +*/ + +#ifdef LTC_DER + +/** +  Free memory allocated by der_decode_sequence_flexi() +  @param in     The list to free +*/   +void der_sequence_free(ltc_asn1_list *in) +{ +   ltc_asn1_list *l; +    +   /* walk to the start of the chain */ +   while (in->prev != NULL || in->parent != NULL) { +      if (in->parent != NULL) { +          in = in->parent; +      } else { +          in = in->prev; +      } +   } +    +   /* now walk the list and free stuff */ +   while (in != NULL) { +      /* is there a child? */ +      if (in->child) { +         /* disconnect */ +         in->child->parent = NULL; +         der_sequence_free(in->child); +      } +       +      switch (in->type) {  +         case LTC_ASN1_SET: +         case LTC_ASN1_SETOF: +         case LTC_ASN1_SEQUENCE: break; +         case LTC_ASN1_INTEGER : if (in->data != NULL) { mp_clear(in->data); } break; +         default               : if (in->data != NULL) { XFREE(in->data);    } +      } +       +      /* move to next and free current */ +      l = in->next; +      free(in); +      in = l; +   }      +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_map.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_map.c new file mode 100644 index 0000000..5a1324c --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_map.c @@ -0,0 +1,76 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_map.c +  ECC Crypto, Tom St Denis +*/   + +#ifdef LTC_MECC + +/** +  Map a projective jacbobian point back to affine space +  @param P        [in/out] The point to map +  @param modulus  The modulus of the field the ECC curve is in +  @param mp       The "b" value from montgomery_setup() +  @return CRYPT_OK on success +*/ +int ltc_ecc_map(ecc_point *P, void *modulus, void *mp) +{ +   void *t1, *t2; +   int   err; + +   LTC_ARGCHK(P       != NULL); +   LTC_ARGCHK(modulus != NULL); +   LTC_ARGCHK(mp      != NULL); + +   if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { +      return CRYPT_MEM; +   } + +   /* first map z back to normal */ +   if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK)           { goto done; } + +   /* get 1/z */ +   if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK)                      { goto done; } +  +   /* get 1/z^2 and 1/z^3 */ +   if ((err = mp_sqr(t1, t2)) != CRYPT_OK)                                    { goto done; } +   if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK)                           { goto done; } +   if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK)                                { goto done; } +   if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK)                           { goto done; } + +   /* multiply against x/y */ +   if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK)                            { goto done; } +   if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK)           { goto done; } +   if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK)                            { goto done; } +   if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK)           { goto done; } +   if ((err = mp_set(P->z, 1)) != CRYPT_OK)                                   { goto done; } + +   err = CRYPT_OK; +done: +   mp_clear_multi(t1, t2, NULL); +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_map.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c new file mode 100644 index 0000000..2c468ea --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c @@ -0,0 +1,207 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_mul2add.c +  ECC Crypto, Shamir's Trick, Tom St Denis +*/   + +#ifdef LTC_MECC + +#ifdef LTC_ECC_SHAMIR + +/** Computes kA*A + kB*B = C using Shamir's Trick +  @param A        First point to multiply +  @param kA       What to multiple A by +  @param B        Second point to multiply +  @param kB       What to multiple B by +  @param C        [out] Destination point (can overlap with A or B +  @param modulus  Modulus for curve  +  @return CRYPT_OK on success +*/  +int ltc_ecc_mul2add(ecc_point *A, void *kA, +                    ecc_point *B, void *kB, +                    ecc_point *C, +                         void *modulus) +{ +  ecc_point     *precomp[16]; +  unsigned       bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble; +  unsigned char *tA, *tB; +  int            err, first; +  void          *mp, *mu; +  +  /* argchks */ +  LTC_ARGCHK(A       != NULL); +  LTC_ARGCHK(B       != NULL); +  LTC_ARGCHK(C       != NULL); +  LTC_ARGCHK(kA      != NULL); +  LTC_ARGCHK(kB      != NULL); +  LTC_ARGCHK(modulus != NULL); + +  /* allocate memory */ +  tA = XCALLOC(1, ECC_BUF_SIZE); +  if (tA == NULL) { +     return CRYPT_MEM; +  } +  tB = XCALLOC(1, ECC_BUF_SIZE); +  if (tB == NULL) { +     XFREE(tA); +     return CRYPT_MEM; +  } + +  /* get sizes */ +  lenA = mp_unsigned_bin_size(kA); +  lenB = mp_unsigned_bin_size(kB); +  len  = MAX(lenA, lenB); + +  /* sanity check */ +  if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) { +     err = CRYPT_INVALID_ARG; +     goto ERR_T; +  } + +  /* extract and justify kA */ +  mp_to_unsigned_bin(kA, (len - lenA) + tA); + +  /* extract and justify kB */ +  mp_to_unsigned_bin(kB, (len - lenB) + tB); + +  /* allocate the table */ +  for (x = 0; x < 16; x++) { +     precomp[x] = ltc_ecc_new_point(); +     if (precomp[x] == NULL) { +         for (y = 0; y < x; ++y) { +            ltc_ecc_del_point(precomp[y]); +         } +         err = CRYPT_MEM; +         goto ERR_T; +     } +  } + +   /* init montgomery reduction */ +   if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { +      goto ERR_P; +   } +   if ((err = mp_init(&mu)) != CRYPT_OK) { +      goto ERR_MP; +   } +   if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { +      goto ERR_MU; +   } + +  /* copy ones ... */ +  if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK)                                         { goto ERR_MU; } +  if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK)                                         { goto ERR_MU; } +  if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK)                                         { goto ERR_MU; } + +  if ((err = mp_mulmod(B->x, mu, modulus, precomp[1<<2]->x)) != CRYPT_OK)                                      { goto ERR_MU; } +  if ((err = mp_mulmod(B->y, mu, modulus, precomp[1<<2]->y)) != CRYPT_OK)                                      { goto ERR_MU; } +  if ((err = mp_mulmod(B->z, mu, modulus, precomp[1<<2]->z)) != CRYPT_OK)                                      { goto ERR_MU; } + +  /* precomp [i,0](A + B) table */ +  if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK)                               { goto ERR_MU; } +  if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK)                   { goto ERR_MU; } + +  /* precomp [0,i](A + B) table */ +  if ((err = ltc_mp.ecc_ptdbl(precomp[1<<2], precomp[2<<2], modulus, mp)) != CRYPT_OK)                         { goto ERR_MU; } +  if ((err = ltc_mp.ecc_ptadd(precomp[1<<2], precomp[2<<2], precomp[3<<2], modulus, mp)) != CRYPT_OK)          { goto ERR_MU; } + +  /* precomp [i,j](A + B) table (i != 0, j != 0) */ +  for (x = 1; x < 4; x++) { +     for (y = 1; y < 4; y++) { +        if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y<<2)], precomp[x+(y<<2)], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } +     } +  }    + +  nibble  = 3; +  first   = 1; +  bitbufA = tA[0]; +  bitbufB = tB[0]; + +  /* for every byte of the multiplicands */ +  for (x = -1;; ) { +     /* grab a nibble */ +     if (++nibble == 4) { +        ++x; if (x == len) break; +        bitbufA = tA[x]; +        bitbufB = tB[x]; +        nibble  = 0; +     } + +     /* extract two bits from both, shift/update */ +     nA = (bitbufA >> 6) & 0x03; +     nB = (bitbufB >> 6) & 0x03; +     bitbufA = (bitbufA << 2) & 0xFF;    +     bitbufB = (bitbufB << 2) & 0xFF;    + +     /* if both zero, if first, continue */ +     if ((nA == 0) && (nB == 0) && (first == 1)) { +        continue; +     } + +     /* double twice, only if this isn't the first */ +     if (first == 0) { +        /* double twice */ +        if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK)                  { goto ERR_MU; } +        if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK)                  { goto ERR_MU; } +     } + +     /* if not both zero */ +     if ((nA != 0) || (nB != 0)) { +        if (first == 1) { +           /* if first, copy from table */ +           first = 0; +           if ((err = mp_copy(precomp[nA + (nB<<2)]->x, C->x)) != CRYPT_OK)           { goto ERR_MU; } +           if ((err = mp_copy(precomp[nA + (nB<<2)]->y, C->y)) != CRYPT_OK)           { goto ERR_MU; } +           if ((err = mp_copy(precomp[nA + (nB<<2)]->z, C->z)) != CRYPT_OK)           { goto ERR_MU; } +        } else { +           /* if not first, add from table */ +           if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB<<2)], C, modulus, mp)) != CRYPT_OK) { goto ERR_MU; } +        } +     } +  } + +  /* reduce to affine */ +  err = ltc_ecc_map(C, modulus, mp); + +  /* clean up */ +ERR_MU: +   mp_clear(mu); +ERR_MP: +   mp_montgomery_free(mp); +ERR_P: +   for (x = 0; x < 16; x++) { +       ltc_ecc_del_point(precomp[x]); +   } +ERR_T: +#ifdef LTC_CLEAN_STACK +   zeromem(tA, ECC_BUF_SIZE); +   zeromem(tB, ECC_BUF_SIZE); +#endif +   XFREE(tA); +   XFREE(tB); + +   return err; +} + +#endif +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c new file mode 100644 index 0000000..f9d0cad --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c @@ -0,0 +1,222 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_mulmod.c +  ECC Crypto, Tom St Denis +*/   + +#ifdef LTC_MECC +#ifndef LTC_ECC_TIMING_RESISTANT + +/* size of sliding window, don't change this! */ +#define WINSIZE 4 + +/** +   Perform a point multiplication  +   @param k    The scalar to multiply by +   @param G    The base point +   @param R    [out] Destination for kG +   @param modulus  The modulus of the field the ECC curve is in +   @param map      Boolean whether to map back to affine or not (1==map, 0 == leave in projective) +   @return CRYPT_OK on success +*/ +int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) +{ +   ecc_point *tG, *M[8]; +   int        i, j, err; +   void       *mu, *mp; +   unsigned long buf; +   int        first, bitbuf, bitcpy, bitcnt, mode, digidx; + +   LTC_ARGCHK(k       != NULL); +   LTC_ARGCHK(G       != NULL); +   LTC_ARGCHK(R       != NULL); +   LTC_ARGCHK(modulus != NULL); + +   /* init montgomery reduction */ +   if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { +      return err; +   } +   if ((err = mp_init(&mu)) != CRYPT_OK) { +      mp_montgomery_free(mp); +      return err; +   } +   if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { +      mp_montgomery_free(mp); +      mp_clear(mu); +      return err; +   } +   +  /* alloc ram for window temps */ +  for (i = 0; i < 8; i++) { +      M[i] = ltc_ecc_new_point(); +      if (M[i] == NULL) { +         for (j = 0; j < i; j++) { +             ltc_ecc_del_point(M[j]); +         } +         mp_montgomery_free(mp); +         mp_clear(mu); +         return CRYPT_MEM; +      } +  } + +   /* make a copy of G incase R==G */ +   tG = ltc_ecc_new_point(); +   if (tG == NULL)                                                                   { err = CRYPT_MEM; goto done; } + +   /* tG = G  and convert to montgomery */ +   if (mp_cmp_d(mu, 1) == LTC_MP_EQ) { +      if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK)                                  { goto done; } +      if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK)                                  { goto done; } +      if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK)                                  { goto done; } +   } else {       +      if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK)                   { goto done; } +      if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK)                   { goto done; } +      if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK)                   { goto done; } +   } +   mp_clear(mu); +   mu = NULL; +    +   /* calc the M tab, which holds kG for k==8..15 */ +   /* M[0] == 8G */ +   if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK)                 { goto done; } +   if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK)               { goto done; } +   if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK)               { goto done; } + +   /* now find (8+k)G for k=1..7 */ +   for (j = 9; j < 16; j++) { +       if ((err = ltc_mp.ecc_ptadd(M[j-9], tG, M[j-8], modulus, mp)) != CRYPT_OK)   { goto done; } +   } + +   /* setup sliding window */ +   mode   = 0; +   bitcnt = 1; +   buf    = 0; +   digidx = mp_get_digit_count(k) - 1; +   bitcpy = bitbuf = 0; +   first  = 1; + +   /* perform ops */ +   for (;;) { +     /* grab next digit as required */ +     if (--bitcnt == 0) { +       if (digidx == -1) { +          break; +       } +       buf    = mp_get_digit(k, digidx); +       bitcnt = (int) ltc_mp.bits_per_digit; +       --digidx; +     } + +     /* grab the next msb from the ltiplicand */ +     i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1; +     buf <<= 1; + +     /* skip leading zero bits */ +     if (mode == 0 && i == 0) { +        continue; +     } + +     /* if the bit is zero and mode == 1 then we double */ +     if (mode == 1 && i == 0) { +        if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)                 { goto done; } +        continue; +     } + +     /* else we add it to the window */ +     bitbuf |= (i << (WINSIZE - ++bitcpy)); +     mode = 2; + +     if (bitcpy == WINSIZE) { +       /* if this is the first window we do a simple copy */ +       if (first == 1) { +          /* R = kG [k = first window] */ +          if ((err = mp_copy(M[bitbuf-8]->x, R->x)) != CRYPT_OK)                     { goto done; } +          if ((err = mp_copy(M[bitbuf-8]->y, R->y)) != CRYPT_OK)                     { goto done; } +          if ((err = mp_copy(M[bitbuf-8]->z, R->z)) != CRYPT_OK)                     { goto done; } +          first = 0; +       } else { +         /* normal window */ +         /* ok window is filled so double as required and add  */ +         /* double first */ +         for (j = 0; j < WINSIZE; j++) { +           if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)              { goto done; } +         } + +         /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */ +         if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf-8], R, modulus, mp)) != CRYPT_OK)   { goto done; } +       } +       /* empty window and reset */ +       bitcpy = bitbuf = 0; +       mode = 1; +    } +  } + +   /* if bits remain then double/add */ +   if (mode == 2 && bitcpy > 0) { +     /* double then add */ +     for (j = 0; j < bitcpy; j++) { +       /* only double if we have had at least one add first */ +       if (first == 0) { +          if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK)              { goto done; } +       } + +       bitbuf <<= 1; +       if ((bitbuf & (1 << WINSIZE)) != 0) { +         if (first == 1){ +            /* first add, so copy */ +            if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK)                           { goto done; } +            if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK)                           { goto done; } +            if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK)                           { goto done; } +            first = 0; +         } else { +            /* then add */ +            if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK)        { goto done; } +         } +       } +     } +   } + +   /* map R back from projective space */ +   if (map) { +      err = ltc_ecc_map(R, modulus, mp); +   } else { +      err = CRYPT_OK; +   } +done: +   if (mu != NULL) { +      mp_clear(mu); +   } +   mp_montgomery_free(mp); +   ltc_ecc_del_point(tG); +   for (i = 0; i < 8; i++) { +       ltc_ecc_del_point(M[i]); +   } +   return err; +} + +#endif + +#undef WINSIZE + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c,v $ */ +/* $Revision: 1.26 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c new file mode 100644 index 0000000..f5a4acb --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c @@ -0,0 +1,60 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_points.c +  ECC Crypto, Tom St Denis +*/   + +#ifdef LTC_MECC + +/** +   Allocate a new ECC point +   @return A newly allocated point or NULL on error  +*/ +ecc_point *ltc_ecc_new_point(void) +{ +   ecc_point *p; +   p = XCALLOC(1, sizeof(*p)); +   if (p == NULL) { +      return NULL; +   } +   if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) { +      XFREE(p); +      return NULL; +   } +   return p; +} + +/** Free an ECC point from memory +  @param p   The point to free +*/ +void ltc_ecc_del_point(ecc_point *p) +{ +   /* prevents free'ing null arguments */ +   if (p != NULL) { +      mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */ +      XFREE(p); +   } +} + +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_points.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c new file mode 100644 index 0000000..b4416fc --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c @@ -0,0 +1,196 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_projective_add_point.c +  ECC Crypto, Tom St Denis +*/   + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** +   Add two ECC points +   @param P        The point to add +   @param Q        The point to add +   @param R        [out] The destination of the double +   @param modulus  The modulus of the field the ECC curve is in +   @param mp       The "b" value from montgomery_setup() +   @return CRYPT_OK on success +*/ +int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp) +{ +   void  *t1, *t2, *x, *y, *z; +   int    err; + +   LTC_ARGCHK(P       != NULL); +   LTC_ARGCHK(Q       != NULL); +   LTC_ARGCHK(R       != NULL); +   LTC_ARGCHK(modulus != NULL); +   LTC_ARGCHK(mp      != NULL); + +   if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) { +      return err; +   } +    +   /* should we dbl instead? */ +   if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK)                          { goto done; } + +   if ( (mp_cmp(P->x, Q->x) == LTC_MP_EQ) &&  +        (Q->z != NULL && mp_cmp(P->z, Q->z) == LTC_MP_EQ) && +        (mp_cmp(P->y, Q->y) == LTC_MP_EQ || mp_cmp(P->y, t1) == LTC_MP_EQ)) { +        mp_clear_multi(t1, t2, x, y, z, NULL); +        return ltc_ecc_projective_dbl_point(P, R, modulus, mp); +   } + +   if ((err = mp_copy(P->x, x)) != CRYPT_OK)                                   { goto done; } +   if ((err = mp_copy(P->y, y)) != CRYPT_OK)                                   { goto done; } +   if ((err = mp_copy(P->z, z)) != CRYPT_OK)                                   { goto done; } + +   /* if Z is one then these are no-operations */ +   if (Q->z != NULL) { +      /* T1 = Z' * Z' */ +      if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK)                                { goto done; } +      if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)           { goto done; } +      /* X = X * T1 */ +      if ((err = mp_mul(t1, x, x)) != CRYPT_OK)                                { goto done; } +      if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)            { goto done; } +      /* T1 = Z' * T1 */ +      if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK)                            { goto done; } +      if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)           { goto done; } +      /* Y = Y * T1 */ +      if ((err = mp_mul(t1, y, y)) != CRYPT_OK)                                { goto done; } +      if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK)            { goto done; } +   } + +   /* T1 = Z*Z */ +   if ((err = mp_sqr(z, t1)) != CRYPT_OK)                                      { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* T2 = X' * T1 */ +   if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK)                               { goto done; } +   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* T1 = Z * T1 */ +   if ((err = mp_mul(z, t1, t1)) != CRYPT_OK)                                  { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* T1 = Y' * T1 */ +   if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK)                               { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; } + +   /* Y = Y - T1 */ +   if ((err = mp_sub(y, t1, y)) != CRYPT_OK)                                   { goto done; } +   if (mp_cmp_d(y, 0) == LTC_MP_LT) { +      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; } +   } +   /* T1 = 2T1 */ +   if ((err = mp_add(t1, t1, t1)) != CRYPT_OK)                                 { goto done; } +   if (mp_cmp(t1, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                         { goto done; } +   } +   /* T1 = Y + T1 */ +   if ((err = mp_add(t1, y, t1)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp(t1, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                         { goto done; } +   } +   /* X = X - T2 */ +   if ((err = mp_sub(x, t2, x)) != CRYPT_OK)                                   { goto done; } +   if (mp_cmp_d(x, 0) == LTC_MP_LT) { +      if ((err = mp_add(x, modulus, x)) != CRYPT_OK)                           { goto done; } +   } +   /* T2 = 2T2 */ +   if ((err = mp_add(t2, t2, t2)) != CRYPT_OK)                                 { goto done; } +   if (mp_cmp(t2, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK)                         { goto done; } +   } +   /* T2 = X + T2 */ +   if ((err = mp_add(t2, x, t2)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp(t2, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK)                         { goto done; } +   } + +   /* if Z' != 1 */ +   if (Q->z != NULL) { +      /* Z = Z * Z' */ +      if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK)                              { goto done; } +      if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK)            { goto done; } +   } + +   /* Z = Z * X */ +   if ((err = mp_mul(z, x, z)) != CRYPT_OK)                                    { goto done; } +   if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK)               { goto done; } + +   /* T1 = T1 * X  */ +   if ((err = mp_mul(t1, x, t1)) != CRYPT_OK)                                  { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* X = X * X */ +   if ((err = mp_sqr(x, x)) != CRYPT_OK)                                       { goto done; } +   if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* T2 = T2 * x */ +   if ((err = mp_mul(t2, x, t2)) != CRYPT_OK)                                  { goto done; } +   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* T1 = T1 * X  */ +   if ((err = mp_mul(t1, x, t1)) != CRYPT_OK)                                  { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)              { goto done; } +  +   /* X = Y*Y */ +   if ((err = mp_sqr(y, x)) != CRYPT_OK)                                       { goto done; } +   if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* X = X - T2 */ +   if ((err = mp_sub(x, t2, x)) != CRYPT_OK)                                   { goto done; } +   if (mp_cmp_d(x, 0) == LTC_MP_LT) { +      if ((err = mp_add(x, modulus, x)) != CRYPT_OK)                           { goto done; } +   } + +   /* T2 = T2 - X */ +   if ((err = mp_sub(t2, x, t2)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp_d(t2, 0) == LTC_MP_LT) { +      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                         { goto done; } +   }  +   /* T2 = T2 - X */ +   if ((err = mp_sub(t2, x, t2)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp_d(t2, 0) == LTC_MP_LT) { +      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                         { goto done; } +   } +   /* T2 = T2 * Y */ +   if ((err = mp_mul(t2, y, t2)) != CRYPT_OK)                                  { goto done; } +   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)              { goto done; } +   /* Y = T2 - T1 */ +   if ((err = mp_sub(t2, t1, y)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp_d(y, 0) == LTC_MP_LT) { +      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; } +   } +   /* Y = Y/2 */ +   if (mp_isodd(y)) { +      if ((err = mp_add(y, modulus, y)) != CRYPT_OK)                           { goto done; } +   } +   if ((err = mp_div_2(y, y)) != CRYPT_OK)                                     { goto done; } + +   if ((err = mp_copy(x, R->x)) != CRYPT_OK)                                   { goto done; } +   if ((err = mp_copy(y, R->y)) != CRYPT_OK)                                   { goto done; } +   if ((err = mp_copy(z, R->z)) != CRYPT_OK)                                   { goto done; } + +   err = CRYPT_OK; +done: +   mp_clear_multi(t1, t2, x, y, z, NULL); +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + diff --git a/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c b/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c new file mode 100644 index 0000000..b990e0a --- /dev/null +++ b/src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c @@ -0,0 +1,147 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b + * + * All curves taken from NIST recommendation paper of July 1999 + * Available at http://csrc.nist.gov/cryptval/dss.htm + */ +#include "../../headers/tomcrypt.h" + +/** +  @file ltc_ecc_projective_dbl_point.c +  ECC Crypto, Tom St Denis +*/   + +#if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) + +/** +   Double an ECC point +   @param P   The point to double +   @param R   [out] The destination of the double +   @param modulus  The modulus of the field the ECC curve is in +   @param mp       The "b" value from montgomery_setup() +   @return CRYPT_OK on success +*/ +int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp) +{ +   void *t1, *t2; +   int   err; + +   LTC_ARGCHK(P       != NULL); +   LTC_ARGCHK(R       != NULL); +   LTC_ARGCHK(modulus != NULL); +   LTC_ARGCHK(mp      != NULL); + +   if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { +      return err; +   } + +   if (P != R) { +      if ((err = mp_copy(P->x, R->x)) != CRYPT_OK)                                { goto done; } +      if ((err = mp_copy(P->y, R->y)) != CRYPT_OK)                                { goto done; } +      if ((err = mp_copy(P->z, R->z)) != CRYPT_OK)                                { goto done; } +   } + +   /* t1 = Z * Z */ +   if ((err = mp_sqr(R->z, t1)) != CRYPT_OK)                                      { goto done; } +   if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK)                 { goto done; } +   /* Z = Y * Z */ +   if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK)                              { goto done; } +   if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* Z = 2Z */ +   if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK)                              { goto done; } +   if (mp_cmp(R->z, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK)                        { goto done; } +   } +    +   /* T2 = X - T1 */ +   if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp_d(t2, 0) == LTC_MP_LT) { +      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                            { goto done; } +   } +   /* T1 = X + T1 */ +   if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK)                                  { goto done; } +   if (mp_cmp(t1, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; } +   } +   /* T2 = T1 * T2 */ +   if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK)                                    { goto done; } +   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)                 { goto done; } +   /* T1 = 2T2 */ +   if ((err = mp_add(t2, t2, t1)) != CRYPT_OK)                                    { goto done; } +   if (mp_cmp(t1, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; } +   } +   /* T1 = T1 + T2 */ +   if ((err = mp_add(t1, t2, t1)) != CRYPT_OK)                                    { goto done; } +   if (mp_cmp(t1, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK)                            { goto done; } +   } + +   /* Y = 2Y */ +   if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK)                              { goto done; } +   if (mp_cmp(R->y, modulus) != LTC_MP_LT) { +      if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; } +   } +   /* Y = Y * Y */ +   if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK)                                    { goto done; } +   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* T2 = Y * Y */ +   if ((err = mp_sqr(R->y, t2)) != CRYPT_OK)                                      { goto done; } +   if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK)                 { goto done; } +   /* T2 = T2/2 */ +   if (mp_isodd(t2)) { +      if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK)                            { goto done; } +   } +   if ((err = mp_div_2(t2, t2)) != CRYPT_OK)                                      { goto done; } +   /* Y = Y * X */ +   if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK)                              { goto done; } +   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; } + +   /* X  = T1 * T1 */ +   if ((err = mp_sqr(t1, R->x)) != CRYPT_OK)                                      { goto done; } +   if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* X = X - Y */ +   if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK)                              { goto done; } +   if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { +      if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK)                        { goto done; } +   } +   /* X = X - Y */ +   if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK)                              { goto done; } +   if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { +      if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK)                        { goto done; } +   } + +   /* Y = Y - X */      +   if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK)                              { goto done; } +   if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { +      if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; } +   } +   /* Y = Y * T1 */ +   if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK)                                { goto done; } +   if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK)               { goto done; } +   /* Y = Y - T2 */ +   if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK)                                { goto done; } +   if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { +      if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK)                        { goto done; } +   } +  +   err = CRYPT_OK; +done: +   mp_clear_multi(t1, t2, NULL); +   return err; +} +#endif +/* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ + diff --git a/src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c new file mode 100644 index 0000000..e8f6418 --- /dev/null +++ b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c @@ -0,0 +1,108 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/**  +  @file pkcs_1_mgf1.c +  The Mask Generation Function (MGF1) for LTC_PKCS #1, Tom St Denis  +*/ + +#ifdef LTC_PKCS_1 + +/** +   Perform LTC_PKCS #1 MGF1 (internal) +   @param seed        The seed for MGF1 +   @param seedlen     The length of the seed +   @param hash_idx    The index of the hash desired +   @param mask        [out] The destination +   @param masklen     The length of the mask desired +   @return CRYPT_OK if successful +*/ +int pkcs_1_mgf1(int                  hash_idx, +                const unsigned char *seed, unsigned long seedlen, +                      unsigned char *mask, unsigned long masklen) +{ +   unsigned long hLen, x; +   ulong32       counter; +   int           err; +   hash_state    *md; +   unsigned char *buf; +  +   LTC_ARGCHK(seed != NULL); +   LTC_ARGCHK(mask != NULL); + +   /* ensure valid hash */ +   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {  +      return err; +   } + +   /* get hash output size */ +   hLen = hash_descriptor[hash_idx].hashsize; + +   /* allocate memory */ +   md  = XMALLOC(sizeof(hash_state)); +   buf = XMALLOC(hLen); +   if (md == NULL || buf == NULL) { +      if (md != NULL) { +         XFREE(md); +      } +      if (buf != NULL) { +         XFREE(buf); +      } +      return CRYPT_MEM; +   } + +   /* start counter */ +   counter = 0; + +   while (masklen > 0) { +       /* handle counter */ +       STORE32H(counter, buf); +       ++counter; + +       /* get hash of seed || counter */ +       if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) { +          goto LBL_ERR; +       } +       if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) { +          goto LBL_ERR; +       } +       if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) { +          goto LBL_ERR; +       } +       if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) { +          goto LBL_ERR; +       } + +       /* store it */ +       for (x = 0; x < hLen && masklen > 0; x++, masklen--) { +          *mask++ = buf[x]; +       } +   } + +   err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK +   zeromem(buf, hLen); +   zeromem(md,  sizeof(hash_state)); +#endif + +   XFREE(buf); +   XFREE(md); + +   return err; +} + +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c new file mode 100644 index 0000000..709ab8a --- /dev/null +++ b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c @@ -0,0 +1,189 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/**  +  @file pkcs_1_oaep_decode.c +  OAEP Padding for LTC_PKCS #1, Tom St Denis  +*/ + +#ifdef LTC_PKCS_1 + +/** +   LTC_PKCS #1 v2.00 OAEP decode +   @param msg              The encoded data to decode +   @param msglen           The length of the encoded data (octets) +   @param lparam           The session or system data (can be NULL) +   @param lparamlen        The length of the lparam +   @param modulus_bitlen   The bit length of the RSA modulus +   @param hash_idx         The index of the hash desired +   @param out              [out] Destination of decoding +   @param outlen           [in/out] The max size and resulting size of the decoding +   @param res              [out] Result of decoding, 1==valid, 0==invalid +   @return CRYPT_OK if successful (even if invalid) +*/ +int pkcs_1_oaep_decode(const unsigned char *msg,    unsigned long msglen, +                       const unsigned char *lparam, unsigned long lparamlen, +                             unsigned long modulus_bitlen, int hash_idx, +                             unsigned char *out,    unsigned long *outlen, +                             int           *res) +{ +   unsigned char *DB, *seed, *mask; +   unsigned long hLen, x, y, modulus_len; +   int           err; + +   LTC_ARGCHK(msg    != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); +   LTC_ARGCHK(res    != NULL); + +   /* default to invalid packet */ +   *res = 0; +    +   /* test valid hash */ +   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {  +      return err; +   } +   hLen        = hash_descriptor[hash_idx].hashsize; +   modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + +   /* test hash/message size */ +   if ((2*hLen >= (modulus_len - 2)) || (msglen != modulus_len)) { +      return CRYPT_PK_INVALID_SIZE; +   } + +   /* allocate ram for DB/mask/salt of size modulus_len */ +   DB   = XMALLOC(modulus_len); +   mask = XMALLOC(modulus_len); +   seed = XMALLOC(hLen); +   if (DB == NULL || mask == NULL || seed == NULL) { +      if (DB != NULL) { +         XFREE(DB); +      } +      if (mask != NULL) { +         XFREE(mask); +      } +      if (seed != NULL) { +         XFREE(seed); +      } +      return CRYPT_MEM; +   } + +   /* ok so it's now in the form +   +      0x00  || maskedseed || maskedDB  +   +       1    ||   hLen     ||  modulus_len - hLen - 1 +    +    */ + +   /* must have leading 0x00 byte */ +   if (msg[0] != 0x00) { +      err = CRYPT_OK; +      goto LBL_ERR; +   } + +   /* now read the masked seed */ +   x = 1; +   XMEMCPY(seed, msg + x, hLen); +   x += hLen; + +   /* now read the masked DB */ +   XMEMCPY(DB, msg + x, modulus_len - hLen - 1); +   x += modulus_len - hLen - 1; + +   /* compute MGF1 of maskedDB (hLen) */  +   if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { +      goto LBL_ERR; +   } + +   /* XOR against seed */ +   for (y = 0; y < hLen; y++) { +      seed[y] ^= mask[y]; +   } + +   /* compute MGF1 of seed (k - hlen - 1) */ +   if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { +      goto LBL_ERR; +   } + +   /* xor against DB */ +   for (y = 0; y < (modulus_len - hLen - 1); y++) { +       DB[y] ^= mask[y];  +   } + +   /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ + +   /* compute lhash and store it in seed [reuse temps!] */ +   x = modulus_len; +   if (lparam != NULL) { +      if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) { +         goto LBL_ERR; +      } +   } else { +      /* can't pass hash_memory a NULL so use DB with zero length */ +      if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) { +         goto LBL_ERR; +      } +   } + +   /* compare the lhash'es */ +   if (XMEMCMP(seed, DB, hLen) != 0) { +      err = CRYPT_OK; +      goto LBL_ERR; +   } + +   /* now zeroes before a 0x01 */ +   for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) { +      /* step... */ +   } + +   /* error out if wasn't 0x01 */ +   if (x == (modulus_len - hLen - 1) || DB[x] != 0x01) { +      err = CRYPT_INVALID_PACKET; +      goto LBL_ERR; +   } + +   /* rest is the message (and skip 0x01) */ +   if ((modulus_len - hLen - 1 - ++x) > *outlen) { +      *outlen = modulus_len - hLen - 1 - x; +      err = CRYPT_BUFFER_OVERFLOW; +      goto LBL_ERR; +   } + +   /* copy message */ +   *outlen = modulus_len - hLen - 1 - x; +   XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x); +   x += modulus_len - hLen - 1; + +   /* valid packet */ +   *res = 1; + +   err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK +   zeromem(DB,   modulus_len); +   zeromem(seed, hLen); +   zeromem(mask, modulus_len); +#endif + +   XFREE(seed); +   XFREE(mask); +   XFREE(DB); + +   return err; +} + +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c new file mode 100644 index 0000000..c3a7211 --- /dev/null +++ b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c @@ -0,0 +1,177 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/**  +  @file pkcs_1_pss_decode.c +  LTC_PKCS #1 PSS Signature Padding, Tom St Denis  +*/ + +#ifdef LTC_PKCS_1 + +/** +   LTC_PKCS #1 v2.00 PSS decode +   @param  msghash         The hash to verify +   @param  msghashlen      The length of the hash (octets) +   @param  sig             The signature data (encoded data) +   @param  siglen          The length of the signature data (octets) +   @param  saltlen         The length of the salt used (octets) +   @param  hash_idx        The index of the hash desired +   @param  modulus_bitlen  The bit length of the RSA modulus +   @param  res             [out] The result of the comparison, 1==valid, 0==invalid +   @return CRYPT_OK if successful (even if the comparison failed) +*/ +int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, +                      const unsigned char *sig,     unsigned long siglen, +                            unsigned long saltlen,  int           hash_idx, +                            unsigned long modulus_bitlen, int    *res) +{ +   unsigned char *DB, *mask, *salt, *hash; +   unsigned long x, y, hLen, modulus_len; +   int           err; +   hash_state    md; + +   LTC_ARGCHK(msghash != NULL); +   LTC_ARGCHK(res     != NULL); + +   /* default to invalid */ +   *res = 0; + +   /* ensure hash is valid */ +   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { +      return err; +   } + +   hLen        = hash_descriptor[hash_idx].hashsize; +   modulus_len = (modulus_bitlen>>3) + (modulus_bitlen & 7 ? 1 : 0); + +   /* check sizes */ +   if ((saltlen > modulus_len) ||  +       (modulus_len < hLen + saltlen + 2) || (siglen != modulus_len)) { +      return CRYPT_PK_INVALID_SIZE; +   } + +   /* allocate ram for DB/mask/salt/hash of size modulus_len */ +   DB   = XMALLOC(modulus_len); +   mask = XMALLOC(modulus_len); +   salt = XMALLOC(modulus_len); +   hash = XMALLOC(modulus_len); +   if (DB == NULL || mask == NULL || salt == NULL || hash == NULL) { +      if (DB != NULL) { +         XFREE(DB); +      } +      if (mask != NULL) { +         XFREE(mask); +      } +      if (salt != NULL) { +         XFREE(salt); +      } +      if (hash != NULL) { +         XFREE(hash); +      } +      return CRYPT_MEM; +   } + +   /* ensure the 0xBC byte */ +   if (sig[siglen-1] != 0xBC) { +      err = CRYPT_INVALID_PACKET; +      goto LBL_ERR; +   } + +   /* copy out the DB */ +   x = 0; +   XMEMCPY(DB, sig + x, modulus_len - hLen - 1); +   x += modulus_len - hLen - 1; + +   /* copy out the hash */ +   XMEMCPY(hash, sig + x, hLen); +   x += hLen; + +   /* check the MSB */ +   if ((sig[0] & ~(0xFF >> ((modulus_len<<3) - (modulus_bitlen-1)))) != 0) { +      err = CRYPT_INVALID_PACKET; +      goto LBL_ERR; +   } + +   /* generate mask of length modulus_len - hLen - 1 from hash */ +   if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { +      goto LBL_ERR; +   } + +   /* xor against DB */ +   for (y = 0; y < (modulus_len - hLen - 1); y++) { +      DB[y] ^= mask[y]; +   } +    +   /* now clear the first byte [make sure smaller than modulus] */ +   DB[0] &= 0xFF >> ((modulus_len<<3) - (modulus_bitlen-1)); + +   /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + +   /* check for zeroes and 0x01 */ +   for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) { +       if (DB[x] != 0x00) { +          err = CRYPT_INVALID_PACKET; +          goto LBL_ERR; +       } +   } + +   /* check for the 0x01 */ +   if (DB[x++] != 0x01) { +      err = CRYPT_INVALID_PACKET; +      goto LBL_ERR; +   } + +   /* M = (eight) 0x00 || msghash || salt, mask = H(M) */ +   if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { +      goto LBL_ERR; +   } +   zeromem(mask, 8); +   if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) { +      goto LBL_ERR; +   } +   if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { +      goto LBL_ERR; +   } +   if ((err = hash_descriptor[hash_idx].process(&md, DB+x, saltlen)) != CRYPT_OK) { +      goto LBL_ERR; +   } +   if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) { +      goto LBL_ERR; +   } + +   /* mask == hash means valid signature */ +   if (XMEMCMP(mask, hash, hLen) == 0) { +      *res = 1; +   } + +   err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK +   zeromem(DB,   modulus_len);    +   zeromem(mask, modulus_len);    +   zeromem(salt, modulus_len);    +   zeromem(hash, modulus_len);    +#endif + +   XFREE(hash); +   XFREE(salt); +   XFREE(mask); +   XFREE(DB); + +   return err; +} + +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c new file mode 100644 index 0000000..7c3711c --- /dev/null +++ b/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c @@ -0,0 +1,110 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** @file pkcs_1_v1_5_decode.c + * + *  LTC_PKCS #1 v1.5 Padding. (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/** @brief LTC_PKCS #1 v1.5 decode. + * + *  @param msg              The encoded data to decode + *  @param msglen           The length of the encoded data (octets) + *  @param block_type       Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + *  @param modulus_bitlen   The bit length of the RSA modulus + *  @param out              [out] Destination of decoding + *  @param outlen           [in/out] The max size and resulting size of the decoding + *  @param is_valid         [out] Boolean whether the padding was valid + * + *  @return CRYPT_OK if successful (even if invalid) + */ +int pkcs_1_v1_5_decode(const unsigned char *msg,  +                             unsigned long  msglen, +                                       int  block_type, +                             unsigned long  modulus_bitlen, +                             unsigned char *out,  +                             unsigned long *outlen, +                                       int *is_valid) +{ +  unsigned long modulus_len, ps_len, i; +  int result; + +  /* default to invalid packet */ +  *is_valid = 0; + +  modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + +  /* test message size */ + +  if ((msglen > modulus_len) || (modulus_len < 11)) { +    return CRYPT_PK_INVALID_SIZE; +  } + +  /* separate encoded message */ + +  if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) { +    result = CRYPT_INVALID_PACKET; +    goto bail; +  } + +  if (block_type == LTC_LTC_PKCS_1_EME) { +    for (i = 2; i < modulus_len; i++) { +      /* separator */ +      if (msg[i] == 0x00) { break; } +    } +    ps_len = i++ - 2; + +    if ((i >= modulus_len) || (ps_len < 8)) { +      /* There was no octet with hexadecimal value 0x00 to separate ps from m, +       * or the length of ps is less than 8 octets. +       */ +      result = CRYPT_INVALID_PACKET; +      goto bail; +    } +  } else { +    for (i = 2; i < modulus_len - 1; i++) { +       if (msg[i] != 0xFF) { break; } +    } + +    /* separator check */ +    if (msg[i] != 0) { +      /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ +      result = CRYPT_INVALID_PACKET; +      goto bail; +    } + +    ps_len = i - 2; +  } + +  if (*outlen < (msglen - (2 + ps_len + 1))) { +    *outlen = msglen - (2 + ps_len + 1); +    result = CRYPT_BUFFER_OVERFLOW; +    goto bail; +  } + +  *outlen = (msglen - (2 + ps_len + 1)); +  XMEMCPY(out, &msg[2 + ps_len + 1], *outlen); + +  /* valid packet */ +  *is_valid = 1; +  result    = CRYPT_OK; +bail: +  return result; +} /* pkcs_1_v1_5_decode */ + +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_exptmod.c b/src/libtomcrypt/src/pk/rsa/rsa_exptmod.c new file mode 100644 index 0000000..ba44106 --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_exptmod.c @@ -0,0 +1,113 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_exptmod.c +  RSA LTC_PKCS exptmod, Tom St Denis +*/   + +#ifdef LTC_MRSA + +/**  +   Compute an RSA modular exponentiation  +   @param in         The input data to send into RSA +   @param inlen      The length of the input (octets) +   @param out        [out] The destination  +   @param outlen     [in/out] The max size and resulting size of the output +   @param which      Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC +   @param key        The RSA key to use  +   @return CRYPT_OK if successful +*/    +int rsa_exptmod(const unsigned char *in,   unsigned long inlen, +                      unsigned char *out,  unsigned long *outlen, int which, +                      rsa_key *key) +{ +   void         *tmp, *tmpa, *tmpb; +   unsigned long x; +   int           err; + +   LTC_ARGCHK(in     != NULL); +   LTC_ARGCHK(out    != NULL); +   LTC_ARGCHK(outlen != NULL); +   LTC_ARGCHK(key    != NULL); +   +   /* is the key of the right type for the operation? */ +   if (which == PK_PRIVATE && (key->type != PK_PRIVATE)) { +      return CRYPT_PK_NOT_PRIVATE; +   } + +   /* must be a private or public operation */ +   if (which != PK_PRIVATE && which != PK_PUBLIC) { +      return CRYPT_PK_INVALID_TYPE; +   } + +   /* init and copy into tmp */ +   if ((err = mp_init_multi(&tmp, &tmpa, &tmpb, NULL)) != CRYPT_OK)                                    { return err; } +   if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK)                 { goto error; } + +   /* sanity check on the input */ +   if (mp_cmp(key->N, tmp) == LTC_MP_LT) { +      err = CRYPT_PK_INVALID_SIZE; +      goto error; +   } + +   /* are we using the private exponent and is the key optimized? */ +   if (which == PK_PRIVATE) { +      /* tmpa = tmp^dP mod p */ +      if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK)                               { goto error; } + +      /* tmpb = tmp^dQ mod q */ +      if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK)                               { goto error; } + +      /* tmp = (tmpa - tmpb) * qInv (mod p) */ +      if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK)                                              { goto error; } +      if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK)                                { goto error; } + +      /* tmp = tmpb + q * tmp */ +      if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK)                                             { goto error; } +      if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK)                                               { goto error; } +   } else { +      /* exptmod it */ +      if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK)                                { goto error; } +   } + +   /* read it back */ +   x = (unsigned long)mp_unsigned_bin_size(key->N); +   if (x > *outlen) { +      *outlen = x; +      err = CRYPT_BUFFER_OVERFLOW; +      goto error; +   } + +   /* this should never happen ... */ +   if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) { +      err = CRYPT_ERROR; +      goto error; +   } +   *outlen = x; + +   /* convert it */ +   zeromem(out, x); +   if ((err = mp_to_unsigned_bin(tmp, out+(x-mp_unsigned_bin_size(tmp)))) != CRYPT_OK)               { goto error; } + +   /* clean up and return */ +   err = CRYPT_OK; +error: +   mp_clear_multi(tmp, tmpa, tmpb, NULL); +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_exptmod.c,v $ */ +/* $Revision: 1.18 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_free.c b/src/libtomcrypt/src/pk/rsa/rsa_free.c new file mode 100644 index 0000000..a10ed59 --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_free.c @@ -0,0 +1,34 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_free.c +  Free an RSA key, Tom St Denis +*/   + +#ifdef LTC_MRSA + +/** +  Free an RSA key from memory +  @param key   The RSA key to free +*/ +void rsa_free(rsa_key *key) +{ +   LTC_ARGCHKVD(key != NULL); +   mp_clear_multi(key->e, key->d, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_free.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_import.c b/src/libtomcrypt/src/pk/rsa/rsa_import.c new file mode 100644 index 0000000..6254fd7 --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_import.c @@ -0,0 +1,143 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_import.c +  Import a LTC_PKCS RSA key, Tom St Denis +*/   + +#ifdef LTC_MRSA + +/** +  Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in LTC_PKCS #1 v2.1] +  @param in      The packet to import from +  @param inlen   It's length (octets) +  @param key     [out] Destination for newly imported key +  @return CRYPT_OK if successful, upon error allocated memory is freed +*/ +int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) +{ +   int           err; +   void         *zero; +   unsigned char *tmpbuf; +   unsigned long  t, x, y, z, tmpoid[16]; +   ltc_asn1_list ssl_pubkey_hashoid[2]; +   ltc_asn1_list ssl_pubkey[2]; + +   LTC_ARGCHK(in          != NULL); +   LTC_ARGCHK(key         != NULL); +   LTC_ARGCHK(ltc_mp.name != NULL); + +   /* init key */ +   if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ,  +                            &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { +      return err; +   } + +   /* see if the OpenSSL DER format RSA public key will work */ +   tmpbuf = XCALLOC(1, MAX_RSA_SIZE*8); +   if (tmpbuf == NULL) { +       err = CRYPT_MEM; +       goto LBL_ERR; +   } + +   /* this includes the internal hash ID and optional params (NULL in this case) */ +   LTC_SET_ASN1(ssl_pubkey_hashoid, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid,                sizeof(tmpoid)/sizeof(tmpoid[0]));    +   LTC_SET_ASN1(ssl_pubkey_hashoid, 1, LTC_ASN1_NULL,              NULL,                  0); + +   /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey in a **BIT** string ... so we have to extract it +      then proceed to convert bit to octet  +    */ +   LTC_SET_ASN1(ssl_pubkey, 0,         LTC_ASN1_SEQUENCE,          &ssl_pubkey_hashoid,   2); +   LTC_SET_ASN1(ssl_pubkey, 1,         LTC_ASN1_BIT_STRING,        tmpbuf,                MAX_RSA_SIZE*8); + +   if (der_decode_sequence(in, inlen, +                           ssl_pubkey, 2UL) == CRYPT_OK) { + +      /* ok now we have to reassemble the BIT STRING to an OCTET STRING.  Thanks OpenSSL... */ +      for (t = y = z = x = 0; x < ssl_pubkey[1].size; x++) { +          y = (y << 1) | tmpbuf[x]; +          if (++z == 8) { +             tmpbuf[t++] = (unsigned char)y; +             y           = 0; +             z           = 0; +          } +      } + +      /* now it should be SEQUENCE { INTEGER, INTEGER } */ +      if ((err = der_decode_sequence_multi(tmpbuf, t, +                                           LTC_ASN1_INTEGER, 1UL, key->N,  +                                           LTC_ASN1_INTEGER, 1UL, key->e,  +                                           LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) { +         XFREE(tmpbuf); +         goto LBL_ERR; +      } +      XFREE(tmpbuf); +      key->type = PK_PUBLIC; +      return CRYPT_OK; +   } +   XFREE(tmpbuf); + +   /* not SSL public key, try to match against LTC_PKCS #1 standards */ +   if ((err = der_decode_sequence_multi(in, inlen,  +                                  LTC_ASN1_INTEGER, 1UL, key->N,  +                                  LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) { +      goto LBL_ERR; +   } + +   if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) { +      if ((err = mp_init(&zero)) != CRYPT_OK) {  +         goto LBL_ERR; +      } +      /* it's a private key */ +      if ((err = der_decode_sequence_multi(in, inlen,  +                          LTC_ASN1_INTEGER, 1UL, zero,  +                          LTC_ASN1_INTEGER, 1UL, key->N,  +                          LTC_ASN1_INTEGER, 1UL, key->e, +                          LTC_ASN1_INTEGER, 1UL, key->d,  +                          LTC_ASN1_INTEGER, 1UL, key->p,  +                          LTC_ASN1_INTEGER, 1UL, key->q,  +                          LTC_ASN1_INTEGER, 1UL, key->dP, +                          LTC_ASN1_INTEGER, 1UL, key->dQ,  +                          LTC_ASN1_INTEGER, 1UL, key->qP,  +                          LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) { +         mp_clear(zero); +         goto LBL_ERR; +      } +      mp_clear(zero); +      key->type = PK_PRIVATE; +   } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) { +      /* we don't support multi-prime RSA */ +      err = CRYPT_PK_INVALID_TYPE; +      goto LBL_ERR; +   } else { +      /* it's a public key and we lack e */ +      if ((err = der_decode_sequence_multi(in, inlen,  +                                     LTC_ASN1_INTEGER, 1UL, key->N,  +                                     LTC_ASN1_INTEGER, 1UL, key->e,  +                                     LTC_ASN1_EOL,     0UL, NULL)) != CRYPT_OK) { +         goto LBL_ERR; +      } +      key->type = PK_PUBLIC; +   } +   return CRYPT_OK; +LBL_ERR: +   mp_clear_multi(key->d,  key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +   return err; +} + +#endif /* LTC_MRSA */ + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_import.c,v $ */ +/* $Revision: 1.23 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_make_key.c b/src/libtomcrypt/src/pk/rsa/rsa_make_key.c new file mode 100644 index 0000000..bd37b4a --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_make_key.c @@ -0,0 +1,112 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_make_key.c +  RSA key generation, Tom St Denis +*/   + +#ifdef LTC_MRSA + +/**  +   Create an RSA key +   @param prng     An active PRNG state +   @param wprng    The index of the PRNG desired +   @param size     The size of the modulus (key size) desired (octets) +   @param e        The "e" value (public key).  e==65537 is a good choice +   @param key      [out] Destination of a newly created private key pair +   @return CRYPT_OK if successful, upon error all allocated ram is freed +*/ +int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) +{ +   void *p, *q, *tmp1, *tmp2, *tmp3; +   int    err; + +   LTC_ARGCHK(ltc_mp.name != NULL); +   LTC_ARGCHK(key         != NULL); + +   if ((size < (MIN_RSA_SIZE/8)) || (size > (MAX_RSA_SIZE/8))) { +      return CRYPT_INVALID_KEYSIZE; +   } + +   if ((e < 3) || ((e & 1) == 0)) { +      return CRYPT_INVALID_ARG; +   } + +   if ((err = prng_is_valid(wprng)) != CRYPT_OK) { +      return err; +   } + +   if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) { +      return err; +   } + +   /* make primes p and q (optimization provided by Wayne Scott) */ +   if ((err = mp_set_int(tmp3, e)) != CRYPT_OK)                      { goto errkey; }  /* tmp3 = e */ + +   /* make prime "p" */ +   do { +       if ((err = rand_prime( p, size/2, prng, wprng)) != CRYPT_OK)  { goto errkey; } +       if ((err = mp_sub_d( p, 1,  tmp1)) != CRYPT_OK)               { goto errkey; }  /* tmp1 = p-1 */ +       if ((err = mp_gcd( tmp1,  tmp3,  tmp2)) != CRYPT_OK)          { goto errkey; }  /* tmp2 = gcd(p-1, e) */ +   } while (mp_cmp_d( tmp2, 1) != 0);                                                  /* while e divides p-1 */ + +   /* make prime "q" */ +   do { +       if ((err = rand_prime( q, size/2, prng, wprng)) != CRYPT_OK)  { goto errkey; } +       if ((err = mp_sub_d( q, 1,  tmp1)) != CRYPT_OK)               { goto errkey; } /* tmp1 = q-1 */ +       if ((err = mp_gcd( tmp1,  tmp3,  tmp2)) != CRYPT_OK)          { goto errkey; } /* tmp2 = gcd(q-1, e) */ +   } while (mp_cmp_d( tmp2, 1) != 0);                                                 /* while e divides q-1 */ + +   /* tmp1 = lcm(p-1, q-1) */ +   if ((err = mp_sub_d( p, 1,  tmp2)) != CRYPT_OK)                   { goto errkey; } /* tmp2 = p-1 */ +                                                                                      /* tmp1 = q-1 (previous do/while loop) */ +   if ((err = mp_lcm( tmp1,  tmp2,  tmp1)) != CRYPT_OK)              { goto errkey; } /* tmp1 = lcm(p-1, q-1) */ + +   /* make key */ +   if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { +      goto errkey; +   } + +   if ((err = mp_set_int( key->e, e)) != CRYPT_OK)                     { goto errkey; } /* key->e =  e */ +   if ((err = mp_invmod( key->e,  tmp1,  key->d)) != CRYPT_OK)         { goto errkey; } /* key->d = 1/e mod lcm(p-1,q-1) */ +   if ((err = mp_mul( p,  q,  key->N)) != CRYPT_OK)                    { goto errkey; } /* key->N = pq */ + +   /* optimize for CRT now */ +   /* find d mod q-1 and d mod p-1 */ +   if ((err = mp_sub_d( p, 1,  tmp1)) != CRYPT_OK)                     { goto errkey; } /* tmp1 = q-1 */ +   if ((err = mp_sub_d( q, 1,  tmp2)) != CRYPT_OK)                     { goto errkey; } /* tmp2 = p-1 */ +   if ((err = mp_mod( key->d,  tmp1,  key->dP)) != CRYPT_OK)           { goto errkey; } /* dP = d mod p-1 */ +   if ((err = mp_mod( key->d,  tmp2,  key->dQ)) != CRYPT_OK)           { goto errkey; } /* dQ = d mod q-1 */ +   if ((err = mp_invmod( q,  p,  key->qP)) != CRYPT_OK)                { goto errkey; } /* qP = 1/q mod p */ + +   if ((err = mp_copy( p,  key->p)) != CRYPT_OK)                       { goto errkey; } +   if ((err = mp_copy( q,  key->q)) != CRYPT_OK)                       { goto errkey; } + +   /* set key type (in this case it's CRT optimized) */ +   key->type = PK_PRIVATE; + +   /* return ok and free temps */ +   err       = CRYPT_OK; +   goto cleanup; +errkey: +   mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); +cleanup: +   mp_clear_multi(tmp3, tmp2, tmp1, p, q, NULL); +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_make_key.c,v $ */ +/* $Revision: 1.16 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c b/src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c new file mode 100644 index 0000000..103ae2f --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c @@ -0,0 +1,167 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_verify_hash.c +  RSA LTC_PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange +*/ + +#ifdef LTC_MRSA + +/** +  LTC_PKCS #1 de-sign then v1.5 or PSS depad +  @param sig              The signature data +  @param siglen           The length of the signature data (octets) +  @param hash             The hash of the message that was signed +  @param hashlen          The length of the hash of the message that was signed (octets) +  @param padding          Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) +  @param hash_idx         The index of the desired hash +  @param saltlen          The length of the salt used during signature +  @param stat             [out] The result of the signature comparison, 1==valid, 0==invalid +  @param key              The public RSA key corresponding to the key that performed the signature +  @return CRYPT_OK on success (even if the signature is invalid) +*/ +int rsa_verify_hash_ex(const unsigned char *sig,      unsigned long siglen, +                       const unsigned char *hash,     unsigned long hashlen, +                             int            padding, +                             int            hash_idx, unsigned long saltlen, +                             int           *stat,     rsa_key      *key) +{ +  unsigned long modulus_bitlen, modulus_bytelen, x; +  int           err; +  unsigned char *tmpbuf; + +  LTC_ARGCHK(hash  != NULL); +  LTC_ARGCHK(sig   != NULL); +  LTC_ARGCHK(stat  != NULL); +  LTC_ARGCHK(key   != NULL); + +  /* default to invalid */ +  *stat = 0; + +  /* valid padding? */ + +  if ((padding != LTC_LTC_PKCS_1_V1_5) && +      (padding != LTC_LTC_PKCS_1_PSS)) { +    return CRYPT_PK_INVALID_PADDING; +  } + +  if (padding == LTC_LTC_PKCS_1_PSS) { +    /* valid hash ? */ +    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { +       return err; +    } +  } + +  /* get modulus len in bits */ +  modulus_bitlen = mp_count_bits( (key->N)); + +  /* outlen must be at least the size of the modulus */ +  modulus_bytelen = mp_unsigned_bin_size( (key->N)); +  if (modulus_bytelen != siglen) { +     return CRYPT_INVALID_PACKET; +  } + +  /* allocate temp buffer for decoded sig */ +  tmpbuf = XMALLOC(siglen); +  if (tmpbuf == NULL) { +     return CRYPT_MEM; +  } + +  /* RSA decode it  */ +  x = siglen; +  if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { +     XFREE(tmpbuf); +     return err; +  } + +  /* make sure the output is the right size */ +  if (x != siglen) { +     XFREE(tmpbuf); +     return CRYPT_INVALID_PACKET; +  } + +  if (padding == LTC_LTC_PKCS_1_PSS) { +    /* PSS decode and verify it */ +    err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat); +  } else { +    /* LTC_PKCS #1 v1.5 decode it */ +    unsigned char *out; +    unsigned long outlen, loid[16]; +    int           decoded; +    ltc_asn1_list digestinfo[2], siginfo[2]; + +    /* not all hashes have OIDs... so sad */ +    if (hash_descriptor[hash_idx].OIDlen == 0) { +       err = CRYPT_INVALID_ARG; +       goto bail_2; +    } + +    /* allocate temp buffer for decoded hash */ +    outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3; +    out    = XMALLOC(outlen); +    if (out == NULL) { +      err = CRYPT_MEM; +      goto bail_2; +    } + +    if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) { +      XFREE(out);        +      goto bail_2; +    } + +    /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */ +    /* construct the SEQUENCE  +      SEQUENCE { +         SEQUENCE {hashoid OID +                   blah    NULL +         } +         hash    OCTET STRING  +      } +   */ +    LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid)/sizeof(loid[0])); +    LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL,              NULL,                          0); +    LTC_SET_ASN1(siginfo,    0, LTC_ASN1_SEQUENCE,          digestinfo,                    2); +    LTC_SET_ASN1(siginfo,    1, LTC_ASN1_OCTET_STRING,      tmpbuf,                        siglen); +    +    if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) { +       XFREE(out); +       goto bail_2; +    } + +    /* test OID */ +    if ((digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) && +        (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) && +        (siginfo[1].size == hashlen) && +        (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) { +       *stat = 1; +    } + +#ifdef LTC_CLEAN_STACK +    zeromem(out, outlen); +#endif +    XFREE(out); +  } + +bail_2: +#ifdef LTC_CLEAN_STACK +  zeromem(tmpbuf, siglen); +#endif +  XFREE(tmpbuf); +  return err; +} + +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_verify_hash.c,v $ */ +/* $Revision: 1.13 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c b/src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c new file mode 100644 index 0000000..6d8888c --- /dev/null +++ b/src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c @@ -0,0 +1,87 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** +  @file rsa_verify_simple.c +  Created by Ladislav Zezula (zezula@volny.cz) as modification +  for Blizzard strong signature verification +*/ + +#ifdef LTC_MRSA + +/** +  Simple RSA decryption +  @param sig              The signature data +  @param siglen           The length of the signature data (octets) +  @param hash             The hash of the message that was signed +  @param hashlen          The length of the hash of the message that was signed (octets) +  @param stat             [out] The result of the signature comparison, 1==valid, 0==invalid +  @param key              The public RSA key corresponding +  @return Error code +*/ +int rsa_verify_simple(const unsigned char *sig,  unsigned long siglen, +                      const unsigned char *hash, unsigned long hashlen, +                            int           *stat, +                            rsa_key       *key) +{ +  unsigned long modulus_bitlen, modulus_bytelen, x; +  unsigned char *tmpbuf; +  int           err; + +  LTC_ARGCHK(sig  != NULL); +  LTC_ARGCHK(hash != NULL); +  LTC_ARGCHK(stat != NULL); +  LTC_ARGCHK(key  != NULL); + +  /* default to invalid */ +  *stat = 0; + +  /* get modulus len in bits */ +  modulus_bitlen = mp_count_bits( (key->N)); + +  /* outlen must be at least the size of the modulus */ +  modulus_bytelen = mp_unsigned_bin_size( (key->N)); +  if (modulus_bytelen != siglen) { +     return CRYPT_INVALID_PACKET; +  } + +  /* allocate temp buffer for decoded sig */ +  tmpbuf = XMALLOC(siglen); +  if (tmpbuf == NULL) { +     return CRYPT_MEM; +  } + +  /* RSA decode it  */ +  x = siglen; +  if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { +     XFREE(tmpbuf); +     return err; +  } + +  /* make sure the output is the right size */ +  if (x != siglen) { +     XFREE(tmpbuf); +     return CRYPT_INVALID_PACKET; +  } + +  /* compare the decrypted signature with the given hash */ +  if(x == hashlen && XMEMCMP(tmpbuf, hash, hashlen) == 0) +      *stat = 1; +        +#ifdef LTC_CLEAN_STACK +  zeromem(tmpbuf, siglen); +#endif +  XFREE(tmpbuf); +  return CRYPT_OK; +} + +#endif /* LTC_MRSA */ diff --git a/src/libtommath/bn_fast_mp_invmod.c b/src/libtommath/bn_fast_mp_invmod.c new file mode 100644 index 0000000..597d7a9 --- /dev/null +++ b/src/libtommath/bn_fast_mp_invmod.c @@ -0,0 +1,148 @@ +#include "tommath.h" +#ifdef BN_FAST_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes the modular inverse via binary extended euclidean algorithm,  + * that is c = 1/a mod b  + * + * Based on slow invmod except this is optimized for the case where b is  + * odd as per HAC Note 14.64 on pp. 610 + */ +int fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int  x, y, u, v, B, D; +  int     res, neg; + +  /* 2. [modified] b must be odd   */ +  if (mp_iseven (b) == 1) { +    return MP_VAL; +  } + +  /* init all our temps */ +  if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { +     return res; +  } + +  /* x == modulus, y == value to invert */ +  if ((res = mp_copy (b, &x)) != MP_OKAY) { +    goto LBL_ERR; +  } + +  /* we need y = |a| */ +  if ((res = mp_mod (a, b, &y)) != MP_OKAY) { +    goto LBL_ERR; +  } + +  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ +  if ((res = mp_copy (&x, &u)) != MP_OKAY) { +    goto LBL_ERR; +  } +  if ((res = mp_copy (&y, &v)) != MP_OKAY) { +    goto LBL_ERR; +  } +  mp_set (&D, 1); + +top: +  /* 4.  while u is even do */ +  while (mp_iseven (&u) == 1) { +    /* 4.1 u = u/2 */ +    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { +      goto LBL_ERR; +    } +    /* 4.2 if B is odd then */ +    if (mp_isodd (&B) == 1) { +      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { +        goto LBL_ERR; +      } +    } +    /* B = B/2 */ +    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* 5.  while v is even do */ +  while (mp_iseven (&v) == 1) { +    /* 5.1 v = v/2 */ +    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { +      goto LBL_ERR; +    } +    /* 5.2 if D is odd then */ +    if (mp_isodd (&D) == 1) { +      /* D = (D-x)/2 */ +      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { +        goto LBL_ERR; +      } +    } +    /* D = D/2 */ +    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* 6.  if u >= v then */ +  if (mp_cmp (&u, &v) != MP_LT) { +    /* u = u - v, B = B - D */ +    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } else { +    /* v - v - u, D = D - B */ +    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* if not zero goto step 4 */ +  if (mp_iszero (&u) == 0) { +    goto top; +  } + +  /* now a = C, b = D, gcd == g*v */ + +  /* if v != 1 then there is no inverse */ +  if (mp_cmp_d (&v, 1) != MP_EQ) { +    res = MP_VAL; +    goto LBL_ERR; +  } + +  /* b is now the inverse */ +  neg = a->sign; +  while (D.sign == MP_NEG) { +    if ((res = mp_add (&D, b, &D)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } +  mp_exch (&D, c); +  c->sign = neg; +  res = MP_OKAY; + +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_invmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_fast_mp_montgomery_reduce.c b/src/libtommath/bn_fast_mp_montgomery_reduce.c new file mode 100644 index 0000000..65eed7d --- /dev/null +++ b/src/libtommath/bn_fast_mp_montgomery_reduce.c @@ -0,0 +1,172 @@ +#include "tommath.h" +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ +  int     ix, res, olduse; +  mp_word W[MP_WARRAY]; + +  /* get old used count */ +  olduse = x->used; + +  /* grow a as required */ +  if (x->alloc < n->used + 1) { +    if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { +      return res; +    } +  } + +  /* first we have to get the digits of the input into +   * an array of double precision words W[...] +   */ +  { +    register mp_word *_W; +    register mp_digit *tmpx; + +    /* alias for the W[] array */ +    _W   = W; + +    /* alias for the digits of  x*/ +    tmpx = x->dp; + +    /* copy the digits of a into W[0..a->used-1] */ +    for (ix = 0; ix < x->used; ix++) { +      *_W++ = *tmpx++; +    } + +    /* zero the high words of W[a->used..m->used*2] */ +    for (; ix < n->used * 2 + 1; ix++) { +      *_W++ = 0; +    } +  } + +  /* now we proceed to zero successive digits +   * from the least significant upwards +   */ +  for (ix = 0; ix < n->used; ix++) { +    /* mu = ai * m' mod b +     * +     * We avoid a double precision multiplication (which isn't required) +     * by casting the value down to a mp_digit.  Note this requires +     * that W[ix-1] have  the carry cleared (see after the inner loop) +     */ +    register mp_digit mu; +    mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + +    /* a = a + mu * m * b**i +     * +     * This is computed in place and on the fly.  The multiplication +     * by b**i is handled by offseting which columns the results +     * are added to. +     * +     * Note the comba method normally doesn't handle carries in the +     * inner loop In this case we fix the carry from the previous +     * column since the Montgomery reduction requires digits of the +     * result (so far) [see above] to work.  This is +     * handled by fixing up one carry after the inner loop.  The +     * carry fixups are done in order so after these loops the +     * first m->used words of W[] have the carries fixed +     */ +    { +      register int iy; +      register mp_digit *tmpn; +      register mp_word *_W; + +      /* alias for the digits of the modulus */ +      tmpn = n->dp; + +      /* Alias for the columns set by an offset of ix */ +      _W = W + ix; + +      /* inner loop */ +      for (iy = 0; iy < n->used; iy++) { +          *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); +      } +    } + +    /* now fix carry for next digit, W[ix+1] */ +    W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); +  } + +  /* now we have to propagate the carries and +   * shift the words downward [all those least +   * significant digits we zeroed]. +   */ +  { +    register mp_digit *tmpx; +    register mp_word *_W, *_W1; + +    /* nox fix rest of carries */ + +    /* alias for current word */ +    _W1 = W + ix; + +    /* alias for next word, where the carry goes */ +    _W = W + ++ix; + +    for (; ix <= n->used * 2 + 1; ix++) { +      *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); +    } + +    /* copy out, A = A/b**n +     * +     * The result is A/b**n but instead of converting from an +     * array of mp_word to mp_digit than calling mp_rshd +     * we just copy them in the right order +     */ + +    /* alias for destination word */ +    tmpx = x->dp; + +    /* alias for shifted double precision result */ +    _W = W + n->used; + +    for (ix = 0; ix < n->used + 1; ix++) { +      *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); +    } + +    /* zero oldused digits, if the input a was larger than +     * m->used+1 we'll have to clear the digits +     */ +    for (; ix < olduse; ix++) { +      *tmpx++ = 0; +    } +  } + +  /* set the max used and clamp */ +  x->used = n->used + 1; +  mp_clamp (x); + +  /* if A >= m then A = A - m */ +  if (mp_cmp_mag (x, n) != MP_LT) { +    return s_mp_sub (x, n, x); +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_fast_s_mp_mul_digs.c b/src/libtommath/bn_fast_s_mp_mul_digs.c new file mode 100644 index 0000000..df83f89 --- /dev/null +++ b/src/libtommath/bn_fast_s_mp_mul_digs.c @@ -0,0 +1,107 @@ +#include "tommath.h" +#ifdef BN_FAST_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier.  It is  + * designed to compute the columns of the product first  + * then handle the carries afterwards.  This has the effect  + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of  + * digits of output so if say only a half-product is required  + * you don't have to compute the upper half (a feature  + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ +  int     olduse, res, pa, ix, iz; +  mp_digit W[MP_WARRAY]; +  register mp_word  _W; + +  /* grow the destination as required */ +  if (c->alloc < digs) { +    if ((res = mp_grow (c, digs)) != MP_OKAY) { +      return res; +    } +  } + +  /* number of output digits to produce */ +  pa = MIN(digs, a->used + b->used); + +  /* clear the carry */ +  _W = 0; +  for (ix = 0; ix < pa; ix++) {  +      int      tx, ty; +      int      iy; +      mp_digit *tmpx, *tmpy; + +      /* get offsets into the two bignums */ +      ty = MIN(b->used-1, ix); +      tx = ix - ty; + +      /* setup temp aliases */ +      tmpx = a->dp + tx; +      tmpy = b->dp + ty; + +      /* this is the number of times the loop will iterrate, essentially  +         while (tx++ < a->used && ty-- >= 0) { ... } +       */ +      iy = MIN(a->used-tx, ty+1); + +      /* execute loop */ +      for (iz = 0; iz < iy; ++iz) { +         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + +      } + +      /* store term */ +      W[ix] = ((mp_digit)_W) & MP_MASK; + +      /* make next carry */ +      _W = _W >> ((mp_word)DIGIT_BIT); + } + +  /* setup dest */ +  olduse  = c->used; +  c->used = pa; + +  { +    register mp_digit *tmpc; +    tmpc = c->dp; +    for (ix = 0; ix < pa+1; ix++) { +      /* now extract the previous digit [below the carry] */ +      *tmpc++ = W[ix]; +    } + +    /* clear unused digits [that existed in the old copy of c] */ +    for (; ix < olduse; ix++) { +      *tmpc++ = 0; +    } +  } +  mp_clamp (c); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_fast_s_mp_mul_high_digs.c b/src/libtommath/bn_fast_s_mp_mul_high_digs.c new file mode 100644 index 0000000..6866aab --- /dev/null +++ b/src/libtommath/bn_fast_s_mp_mul_high_digs.c @@ -0,0 +1,98 @@ +#include "tommath.h" +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* this is a modified version of fast_s_mul_digs that only produces + * output digits *above* digs.  See the comments for fast_s_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed.  This essentially halves the work. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +int fast_s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ +  int     olduse, res, pa, ix, iz; +  mp_digit W[MP_WARRAY]; +  mp_word  _W; + +  /* grow the destination as required */ +  pa = a->used + b->used; +  if (c->alloc < pa) { +    if ((res = mp_grow (c, pa)) != MP_OKAY) { +      return res; +    } +  } + +  /* number of output digits to produce */ +  pa = a->used + b->used; +  _W = 0; +  for (ix = digs; ix < pa; ix++) {  +      int      tx, ty, iy; +      mp_digit *tmpx, *tmpy; + +      /* get offsets into the two bignums */ +      ty = MIN(b->used-1, ix); +      tx = ix - ty; + +      /* setup temp aliases */ +      tmpx = a->dp + tx; +      tmpy = b->dp + ty; + +      /* this is the number of times the loop will iterrate, essentially its  +         while (tx++ < a->used && ty-- >= 0) { ... } +       */ +      iy = MIN(a->used-tx, ty+1); + +      /* execute loop */ +      for (iz = 0; iz < iy; iz++) { +         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); +      } + +      /* store term */ +      W[ix] = ((mp_digit)_W) & MP_MASK; + +      /* make next carry */ +      _W = _W >> ((mp_word)DIGIT_BIT); +  } +   +  /* setup dest */ +  olduse  = c->used; +  c->used = pa; + +  { +    register mp_digit *tmpc; + +    tmpc = c->dp + digs; +    for (ix = digs; ix < pa; ix++) { +      /* now extract the previous digit [below the carry] */ +      *tmpc++ = W[ix]; +    } + +    /* clear unused digits [that existed in the old copy of c] */ +    for (; ix < olduse; ix++) { +      *tmpc++ = 0; +    } +  } +  mp_clamp (c); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_fast_s_mp_sqr.c b/src/libtommath/bn_fast_s_mp_sqr.c new file mode 100644 index 0000000..5f9d58c --- /dev/null +++ b/src/libtommath/bn_fast_s_mp_sqr.c @@ -0,0 +1,114 @@ +#include "tommath.h" +#ifdef BN_FAST_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that  + * starts closer to zero] can't equal the offset of tmpy.   + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens.  You double all those  + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ +  int       olduse, res, pa, ix, iz; +  mp_digit   W[MP_WARRAY], *tmpx; +  mp_word   W1; + +  /* grow the destination as required */ +  pa = a->used + a->used; +  if (b->alloc < pa) { +    if ((res = mp_grow (b, pa)) != MP_OKAY) { +      return res; +    } +  } + +  /* number of output digits to produce */ +  W1 = 0; +  for (ix = 0; ix < pa; ix++) {  +      int      tx, ty, iy; +      mp_word  _W; +      mp_digit *tmpy; + +      /* clear counter */ +      _W = 0; + +      /* get offsets into the two bignums */ +      ty = MIN(a->used-1, ix); +      tx = ix - ty; + +      /* setup temp aliases */ +      tmpx = a->dp + tx; +      tmpy = a->dp + ty; + +      /* this is the number of times the loop will iterrate, essentially +         while (tx++ < a->used && ty-- >= 0) { ... } +       */ +      iy = MIN(a->used-tx, ty+1); + +      /* now for squaring tx can never equal ty  +       * we halve the distance since they approach at a rate of 2x +       * and we have to round because odd cases need to be executed +       */ +      iy = MIN(iy, (ty-tx+1)>>1); + +      /* execute loop */ +      for (iz = 0; iz < iy; iz++) { +         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); +      } + +      /* double the inner product and add carry */ +      _W = _W + _W + W1; + +      /* even columns have the square term in them */ +      if ((ix&1) == 0) { +         _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); +      } + +      /* store it */ +      W[ix] = (mp_digit)(_W & MP_MASK); + +      /* make next carry */ +      W1 = _W >> ((mp_word)DIGIT_BIT); +  } + +  /* setup dest */ +  olduse  = b->used; +  b->used = a->used+a->used; + +  { +    mp_digit *tmpb; +    tmpb = b->dp; +    for (ix = 0; ix < pa; ix++) { +      *tmpb++ = W[ix] & MP_MASK; +    } + +    /* clear unused digits [that existed in the old copy of c] */ +    for (; ix < olduse; ix++) { +      *tmpb++ = 0; +    } +  } +  mp_clamp (b); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_fast_s_mp_sqr.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_2expt.c b/src/libtommath/bn_mp_2expt.c new file mode 100644 index 0000000..f899eae --- /dev/null +++ b/src/libtommath/bn_mp_2expt.c @@ -0,0 +1,48 @@ +#include "tommath.h" +#ifdef BN_MP_2EXPT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes a = 2**b  + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt (mp_int * a, int b) +{ +  int     res; + +  /* zero a as per default */ +  mp_zero (a); + +  /* grow a to accomodate the single bit */ +  if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { +    return res; +  } + +  /* set the used count of where the bit will go */ +  a->used = b / DIGIT_BIT + 1; + +  /* put the single bit in its place */ +  a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_2expt.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_abs.c b/src/libtommath/bn_mp_abs.c new file mode 100644 index 0000000..14f3a7e --- /dev/null +++ b/src/libtommath/bn_mp_abs.c @@ -0,0 +1,43 @@ +#include "tommath.h" +#ifdef BN_MP_ABS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* b = |a|  + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs (mp_int * a, mp_int * b) +{ +  int     res; + +  /* copy a to b */ +  if (a != b) { +     if ((res = mp_copy (a, b)) != MP_OKAY) { +       return res; +     } +  } + +  /* force the sign of b to positive */ +  b->sign = MP_ZPOS; + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_abs.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_add.c b/src/libtommath/bn_mp_add.c new file mode 100644 index 0000000..b368b21 --- /dev/null +++ b/src/libtommath/bn_mp_add.c @@ -0,0 +1,53 @@ +#include "tommath.h" +#ifdef BN_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* high level addition (handles signs) */ +int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ +  int     sa, sb, res; + +  /* get sign of both inputs */ +  sa = a->sign; +  sb = b->sign; + +  /* handle two cases, not four */ +  if (sa == sb) { +    /* both positive or both negative */ +    /* add their magnitudes, copy the sign */ +    c->sign = sa; +    res = s_mp_add (a, b, c); +  } else { +    /* one positive, the other negative */ +    /* subtract the one with the greater magnitude from */ +    /* the one of the lesser magnitude.  The result gets */ +    /* the sign of the one with the greater magnitude. */ +    if (mp_cmp_mag (a, b) == MP_LT) { +      c->sign = sb; +      res = s_mp_sub (b, a, c); +    } else { +      c->sign = sa; +      res = s_mp_sub (a, b, c); +    } +  } +  return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_add_d.c b/src/libtommath/bn_mp_add_d.c new file mode 100644 index 0000000..c147554 --- /dev/null +++ b/src/libtommath/bn_mp_add_d.c @@ -0,0 +1,112 @@ +#include "tommath.h" +#ifdef BN_MP_ADD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ +  int     res, ix, oldused; +  mp_digit *tmpa, *tmpc, mu; + +  /* grow c as required */ +  if (c->alloc < a->used + 1) { +     if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { +        return res; +     } +  } + +  /* if a is negative and |a| >= b, call c = |a| - b */ +  if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) { +     /* temporarily fix sign of a */ +     a->sign = MP_ZPOS; + +     /* c = |a| - b */ +     res = mp_sub_d(a, b, c); + +     /* fix sign  */ +     a->sign = c->sign = MP_NEG; + +     /* clamp */ +     mp_clamp(c); + +     return res; +  } + +  /* old number of used digits in c */ +  oldused = c->used; + +  /* sign always positive */ +  c->sign = MP_ZPOS; + +  /* source alias */ +  tmpa    = a->dp; + +  /* destination alias */ +  tmpc    = c->dp; + +  /* if a is positive */ +  if (a->sign == MP_ZPOS) { +     /* add digit, after this we're propagating +      * the carry. +      */ +     *tmpc   = *tmpa++ + b; +     mu      = *tmpc >> DIGIT_BIT; +     *tmpc++ &= MP_MASK; + +     /* now handle rest of the digits */ +     for (ix = 1; ix < a->used; ix++) { +        *tmpc   = *tmpa++ + mu; +        mu      = *tmpc >> DIGIT_BIT; +        *tmpc++ &= MP_MASK; +     } +     /* set final carry */ +     ix++; +     *tmpc++  = mu; + +     /* setup size */ +     c->used = a->used + 1; +  } else { +     /* a was negative and |a| < b */ +     c->used  = 1; + +     /* the result is a single digit */ +     if (a->used == 1) { +        *tmpc++  =  b - a->dp[0]; +     } else { +        *tmpc++  =  b; +     } + +     /* setup count so the clearing of oldused +      * can fall through correctly +      */ +     ix       = 1; +  } + +  /* now zero to oldused */ +  while (ix++ < oldused) { +     *tmpc++ = 0; +  } +  mp_clamp(c); + +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_add_d.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_addmod.c b/src/libtommath/bn_mp_addmod.c new file mode 100644 index 0000000..0a21f62 --- /dev/null +++ b/src/libtommath/bn_mp_addmod.c @@ -0,0 +1,41 @@ +#include "tommath.h" +#ifdef BN_MP_ADDMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* d = a + b (mod c) */ +int +mp_addmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ +  int     res; +  mp_int  t; + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_add (a, b, &t)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } +  res = mp_mod (&t, c, d); +  mp_clear (&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_addmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_and.c b/src/libtommath/bn_mp_and.c new file mode 100644 index 0000000..6b7afc1 --- /dev/null +++ b/src/libtommath/bn_mp_and.c @@ -0,0 +1,57 @@ +#include "tommath.h" +#ifdef BN_MP_AND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* AND two ints together */ +int +mp_and (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res, ix, px; +  mp_int  t, *x; + +  if (a->used > b->used) { +    if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +      return res; +    } +    px = b->used; +    x = b; +  } else { +    if ((res = mp_init_copy (&t, b)) != MP_OKAY) { +      return res; +    } +    px = a->used; +    x = a; +  } + +  for (ix = 0; ix < px; ix++) { +    t.dp[ix] &= x->dp[ix]; +  } + +  /* zero digits above the last from the smallest mp_int */ +  for (; ix < t.used; ix++) { +    t.dp[ix] = 0; +  } + +  mp_clamp (&t); +  mp_exch (c, &t); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_and.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_clamp.c b/src/libtommath/bn_mp_clamp.c new file mode 100644 index 0000000..d3cc21c --- /dev/null +++ b/src/libtommath/bn_mp_clamp.c @@ -0,0 +1,44 @@ +#include "tommath.h" +#ifdef BN_MP_CLAMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* trim unused digits  + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast.  Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp (mp_int * a) +{ +  /* decrease used while the most significant digit is +   * zero. +   */ +  while (a->used > 0 && a->dp[a->used - 1] == 0) { +    --(a->used); +  } + +  /* reset the sign flag if used == 0 */ +  if (a->used == 0) { +    a->sign = MP_ZPOS; +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clamp.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_clear.c b/src/libtommath/bn_mp_clear.c new file mode 100644 index 0000000..7644c38 --- /dev/null +++ b/src/libtommath/bn_mp_clear.c @@ -0,0 +1,44 @@ +#include "tommath.h" +#ifdef BN_MP_CLEAR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* clear one (frees)  */ +void +mp_clear (mp_int * a) +{ +  int i; + +  /* only do anything if a hasn't been freed previously */ +  if (a->dp != NULL) { +    /* first zero the digits */ +    for (i = 0; i < a->used; i++) { +        a->dp[i] = 0; +    } + +    /* free ram */ +    XFREE(a->dp); + +    /* reset members to make debugging easier */ +    a->dp    = NULL; +    a->alloc = a->used = 0; +    a->sign  = MP_ZPOS; +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_clear_multi.c b/src/libtommath/bn_mp_clear_multi.c new file mode 100644 index 0000000..a107624 --- /dev/null +++ b/src/libtommath/bn_mp_clear_multi.c @@ -0,0 +1,34 @@ +#include "tommath.h" +#ifdef BN_MP_CLEAR_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include <stdarg.h> + +void mp_clear_multi(mp_int *mp, ...)  +{ +    mp_int* next_mp = mp; +    va_list args; +    va_start(args, mp); +    while (next_mp != NULL) { +        mp_clear(next_mp); +        next_mp = va_arg(args, mp_int*); +    } +    va_end(args); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_clear_multi.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_cmp.c b/src/libtommath/bn_mp_cmp.c new file mode 100644 index 0000000..761d2b0 --- /dev/null +++ b/src/libtommath/bn_mp_cmp.c @@ -0,0 +1,43 @@ +#include "tommath.h" +#ifdef BN_MP_CMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ +  /* compare based on sign */ +  if (a->sign != b->sign) { +     if (a->sign == MP_NEG) { +        return MP_LT; +     } else { +        return MP_GT; +     } +  } +   +  /* compare digits */ +  if (a->sign == MP_NEG) { +     /* if negative compare opposite direction */ +     return mp_cmp_mag(b, a); +  } else { +     return mp_cmp_mag(a, b); +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_cmp_d.c b/src/libtommath/bn_mp_cmp_d.c new file mode 100644 index 0000000..420dfd3 --- /dev/null +++ b/src/libtommath/bn_mp_cmp_d.c @@ -0,0 +1,44 @@ +#include "tommath.h" +#ifdef BN_MP_CMP_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* compare a digit */ +int mp_cmp_d(mp_int * a, mp_digit b) +{ +  /* compare based on sign */ +  if (a->sign == MP_NEG) { +    return MP_LT; +  } + +  /* compare based on magnitude */ +  if (a->used > 1) { +    return MP_GT; +  } + +  /* compare the only digit of a to b */ +  if (a->dp[0] > b) { +    return MP_GT; +  } else if (a->dp[0] < b) { +    return MP_LT; +  } else { +    return MP_EQ; +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_cmp_mag.c b/src/libtommath/bn_mp_cmp_mag.c new file mode 100644 index 0000000..92565a3 --- /dev/null +++ b/src/libtommath/bn_mp_cmp_mag.c @@ -0,0 +1,55 @@ +#include "tommath.h" +#ifdef BN_MP_CMP_MAG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag (mp_int * a, mp_int * b) +{ +  int     n; +  mp_digit *tmpa, *tmpb; + +  /* compare based on # of non-zero digits */ +  if (a->used > b->used) { +    return MP_GT; +  } +   +  if (a->used < b->used) { +    return MP_LT; +  } + +  /* alias for a */ +  tmpa = a->dp + (a->used - 1); + +  /* alias for b */ +  tmpb = b->dp + (a->used - 1); + +  /* compare based on digits  */ +  for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { +    if (*tmpa > *tmpb) { +      return MP_GT; +    } + +    if (*tmpa < *tmpb) { +      return MP_LT; +    } +  } +  return MP_EQ; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cmp_mag.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_cnt_lsb.c b/src/libtommath/bn_mp_cnt_lsb.c new file mode 100644 index 0000000..6040661 --- /dev/null +++ b/src/libtommath/bn_mp_cnt_lsb.c @@ -0,0 +1,53 @@ +#include "tommath.h" +#ifdef BN_MP_CNT_LSB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +static const int lnz[16] = {  +   4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) +{ +   int x; +   mp_digit q, qq; + +   /* easy out */ +   if (mp_iszero(a) == 1) { +      return 0; +   } + +   /* scan lower digits until non-zero */ +   for (x = 0; x < a->used && a->dp[x] == 0; x++); +   q = a->dp[x]; +   x *= DIGIT_BIT; + +   /* now scan this digit until a 1 is found */ +   if ((q & 1) == 0) { +      do { +         qq  = q & 15; +         x  += lnz[qq]; +         q >>= 4; +      } while (qq == 0); +   } +   return x; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_cnt_lsb.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_copy.c b/src/libtommath/bn_mp_copy.c new file mode 100644 index 0000000..7828592 --- /dev/null +++ b/src/libtommath/bn_mp_copy.c @@ -0,0 +1,68 @@ +#include "tommath.h" +#ifdef BN_MP_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ +  int     res, n; + +  /* if dst == src do nothing */ +  if (a == b) { +    return MP_OKAY; +  } + +  /* grow dest */ +  if (b->alloc < a->used) { +     if ((res = mp_grow (b, a->used)) != MP_OKAY) { +        return res; +     } +  } + +  /* zero b and copy the parameters over */ +  { +    register mp_digit *tmpa, *tmpb; + +    /* pointer aliases */ + +    /* source */ +    tmpa = a->dp; + +    /* destination */ +    tmpb = b->dp; + +    /* copy all the digits */ +    for (n = 0; n < a->used; n++) { +      *tmpb++ = *tmpa++; +    } + +    /* clear high digits */ +    for (; n < b->used; n++) { +      *tmpb++ = 0; +    } +  } + +  /* copy used count and sign */ +  b->used = a->used; +  b->sign = a->sign; +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_copy.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_count_bits.c b/src/libtommath/bn_mp_count_bits.c new file mode 100644 index 0000000..9d8640f --- /dev/null +++ b/src/libtommath/bn_mp_count_bits.c @@ -0,0 +1,45 @@ +#include "tommath.h" +#ifdef BN_MP_COUNT_BITS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ +  int     r; +  mp_digit q; + +  /* shortcut */ +  if (a->used == 0) { +    return 0; +  } + +  /* get number of digits and add that */ +  r = (a->used - 1) * DIGIT_BIT; +   +  /* take the last digit and count the bits in it */ +  q = a->dp[a->used - 1]; +  while (q > ((mp_digit) 0)) { +    ++r; +    q >>= ((mp_digit) 1); +  } +  return r; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_count_bits.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_div.c b/src/libtommath/bn_mp_div.c new file mode 100644 index 0000000..3004a3e --- /dev/null +++ b/src/libtommath/bn_mp_div.c @@ -0,0 +1,292 @@ +#include "tommath.h" +#ifdef BN_MP_DIV_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ +   mp_int ta, tb, tq, q; +   int    res, n, n2; + +  /* is divisor zero ? */ +  if (mp_iszero (b) == 1) { +    return MP_VAL; +  } + +  /* if a < b then q=0, r = a */ +  if (mp_cmp_mag (a, b) == MP_LT) { +    if (d != NULL) { +      res = mp_copy (a, d); +    } else { +      res = MP_OKAY; +    } +    if (c != NULL) { +      mp_zero (c); +    } +    return res; +  } +	 +  /* init our temps */ +  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { +     return res; +  } + + +  mp_set(&tq, 1); +  n = mp_count_bits(a) - mp_count_bits(b); +  if (((res = mp_abs(a, &ta)) != MP_OKAY) || +      ((res = mp_abs(b, &tb)) != MP_OKAY) ||  +      ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || +      ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { +      goto LBL_ERR; +  } + +  while (n-- >= 0) { +     if (mp_cmp(&tb, &ta) != MP_GT) { +        if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || +            ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { +           goto LBL_ERR; +        } +     } +     if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || +         ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { +           goto LBL_ERR; +     } +  } + +  /* now q == quotient and ta == remainder */ +  n  = a->sign; +  n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); +  if (c != NULL) { +     mp_exch(c, &q); +     c->sign  = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; +  } +  if (d != NULL) { +     mp_exch(d, &ta); +     d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; +  } +LBL_ERR: +   mp_clear_multi(&ta, &tb, &tq, &q, NULL); +   return res; +} + +#else + +/* integer signed division.  + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly  + * incomplete.  For example, it doesn't consider  + * the case where digits are removed from 'x' in  + * the inner loop.  It also doesn't consider the  + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as  + * 14.20 from HAC but fixed to treat these cases. +*/ +int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ +  mp_int  q, x, y, t1, t2; +  int     res, n, t, i, norm, neg; + +  /* is divisor zero ? */ +  if (mp_iszero (b) == 1) { +    return MP_VAL; +  } + +  /* if a < b then q=0, r = a */ +  if (mp_cmp_mag (a, b) == MP_LT) { +    if (d != NULL) { +      res = mp_copy (a, d); +    } else { +      res = MP_OKAY; +    } +    if (c != NULL) { +      mp_zero (c); +    } +    return res; +  } + +  if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { +    return res; +  } +  q.used = a->used + 2; + +  if ((res = mp_init (&t1)) != MP_OKAY) { +    goto LBL_Q; +  } + +  if ((res = mp_init (&t2)) != MP_OKAY) { +    goto LBL_T1; +  } + +  if ((res = mp_init_copy (&x, a)) != MP_OKAY) { +    goto LBL_T2; +  } + +  if ((res = mp_init_copy (&y, b)) != MP_OKAY) { +    goto LBL_X; +  } + +  /* fix the sign */ +  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; +  x.sign = y.sign = MP_ZPOS; + +  /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ +  norm = mp_count_bits(&y) % DIGIT_BIT; +  if (norm < (int)(DIGIT_BIT-1)) { +     norm = (DIGIT_BIT-1) - norm; +     if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { +       goto LBL_Y; +     } +     if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { +       goto LBL_Y; +     } +  } else { +     norm = 0; +  } + +  /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ +  n = x.used - 1; +  t = y.used - 1; + +  /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ +  if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ +    goto LBL_Y; +  } + +  while (mp_cmp (&x, &y) != MP_LT) { +    ++(q.dp[n - t]); +    if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { +      goto LBL_Y; +    } +  } + +  /* reset y by shifting it back down */ +  mp_rshd (&y, n - t); + +  /* step 3. for i from n down to (t + 1) */ +  for (i = n; i >= (t + 1); i--) { +    if (i > x.used) { +      continue; +    } + +    /* step 3.1 if xi == yt then set q{i-t-1} to b-1,  +     * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ +    if (x.dp[i] == y.dp[t]) { +      q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); +    } else { +      mp_word tmp; +      tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); +      tmp |= ((mp_word) x.dp[i - 1]); +      tmp /= ((mp_word) y.dp[t]); +      if (tmp > (mp_word) MP_MASK) +        tmp = MP_MASK; +      q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); +    } + +    /* while (q{i-t-1} * (yt * b + y{t-1})) >  +             xi * b**2 + xi-1 * b + xi-2  +      +       do q{i-t-1} -= 1;  +    */ +    q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; +    do { +      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + +      /* find left hand */ +      mp_zero (&t1); +      t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; +      t1.dp[1] = y.dp[t]; +      t1.used = 2; +      if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { +        goto LBL_Y; +      } + +      /* find right hand */ +      t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; +      t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; +      t2.dp[2] = x.dp[i]; +      t2.used = 3; +    } while (mp_cmp_mag(&t1, &t2) == MP_GT); + +    /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ +    if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { +      goto LBL_Y; +    } + +    if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { +      goto LBL_Y; +    } + +    if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { +      goto LBL_Y; +    } + +    /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ +    if (x.sign == MP_NEG) { +      if ((res = mp_copy (&y, &t1)) != MP_OKAY) { +        goto LBL_Y; +      } +      if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { +        goto LBL_Y; +      } +      if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { +        goto LBL_Y; +      } + +      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; +    } +  } + +  /* now q is the quotient and x is the remainder  +   * [which we have to normalize]  +   */ +   +  /* get sign before writing to c */ +  x.sign = x.used == 0 ? MP_ZPOS : a->sign; + +  if (c != NULL) { +    mp_clamp (&q); +    mp_exch (&q, c); +    c->sign = neg; +  } + +  if (d != NULL) { +    mp_div_2d (&x, norm, &x, NULL); +    mp_exch (&x, d); +  } + +  res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); +  return res; +} + +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_div_2.c b/src/libtommath/bn_mp_div_2.c new file mode 100644 index 0000000..f3b9d16 --- /dev/null +++ b/src/libtommath/bn_mp_div_2.c @@ -0,0 +1,68 @@ +#include "tommath.h" +#ifdef BN_MP_DIV_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* b = a/2 */ +int mp_div_2(mp_int * a, mp_int * b) +{ +  int     x, res, oldused; + +  /* copy */ +  if (b->alloc < a->used) { +    if ((res = mp_grow (b, a->used)) != MP_OKAY) { +      return res; +    } +  } + +  oldused = b->used; +  b->used = a->used; +  { +    register mp_digit r, rr, *tmpa, *tmpb; + +    /* source alias */ +    tmpa = a->dp + b->used - 1; + +    /* dest alias */ +    tmpb = b->dp + b->used - 1; + +    /* carry */ +    r = 0; +    for (x = b->used - 1; x >= 0; x--) { +      /* get the carry for the next iteration */ +      rr = *tmpa & 1; + +      /* shift the current digit, add in carry and store */ +      *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + +      /* forward carry to next iteration */ +      r = rr; +    } + +    /* zero excess digits */ +    tmpb = b->dp + b->used; +    for (x = b->used; x < oldused; x++) { +      *tmpb++ = 0; +    } +  } +  b->sign = a->sign; +  mp_clamp (b); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_div_2d.c b/src/libtommath/bn_mp_div_2d.c new file mode 100644 index 0000000..861ea23 --- /dev/null +++ b/src/libtommath/bn_mp_div_2d.c @@ -0,0 +1,97 @@ +#include "tommath.h" +#ifdef BN_MP_DIV_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ +  mp_digit D, r, rr; +  int     x, res; +  mp_int  t; + + +  /* if the shift count is <= 0 then we do no work */ +  if (b <= 0) { +    res = mp_copy (a, c); +    if (d != NULL) { +      mp_zero (d); +    } +    return res; +  } + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  /* get the remainder */ +  if (d != NULL) { +    if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { +      mp_clear (&t); +      return res; +    } +  } + +  /* copy */ +  if ((res = mp_copy (a, c)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } + +  /* shift by as many digits in the bit count */ +  if (b >= (int)DIGIT_BIT) { +    mp_rshd (c, b / DIGIT_BIT); +  } + +  /* shift any bit count < DIGIT_BIT */ +  D = (mp_digit) (b % DIGIT_BIT); +  if (D != 0) { +    register mp_digit *tmpc, mask, shift; + +    /* mask */ +    mask = (((mp_digit)1) << D) - 1; + +    /* shift for lsb */ +    shift = DIGIT_BIT - D; + +    /* alias */ +    tmpc = c->dp + (c->used - 1); + +    /* carry */ +    r = 0; +    for (x = c->used - 1; x >= 0; x--) { +      /* get the lower  bits of this word in a temp */ +      rr = *tmpc & mask; + +      /* shift the current word and mix in the carry bits from the previous word */ +      *tmpc = (*tmpc >> D) | (r << shift); +      --tmpc; + +      /* set the carry to the carry bits of the current word found above */ +      r = rr; +    } +  } +  mp_clamp (c); +  if (d != NULL) { +    mp_exch (&t, d); +  } +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_2d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_div_3.c b/src/libtommath/bn_mp_div_3.c new file mode 100644 index 0000000..4fc08fc --- /dev/null +++ b/src/libtommath/bn_mp_div_3.c @@ -0,0 +1,79 @@ +#include "tommath.h" +#ifdef BN_MP_DIV_3_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) +{ +  mp_int   q; +  mp_word  w, t; +  mp_digit b; +  int      res, ix; +   +  /* b = 2**DIGIT_BIT / 3 */ +  b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + +  if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { +     return res; +  } +   +  q.used = a->used; +  q.sign = a->sign; +  w = 0; +  for (ix = a->used - 1; ix >= 0; ix--) { +     w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + +     if (w >= 3) { +        /* multiply w by [1/3] */ +        t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + +        /* now subtract 3 * [w/3] from w, to get the remainder */ +        w -= t+t+t; + +        /* fixup the remainder as required since +         * the optimization is not exact. +         */ +        while (w >= 3) { +           t += 1; +           w -= 3; +        } +      } else { +        t = 0; +      } +      q.dp[ix] = (mp_digit)t; +  } + +  /* [optional] store the remainder */ +  if (d != NULL) { +     *d = (mp_digit)w; +  } + +  /* [optional] store the quotient */ +  if (c != NULL) { +     mp_clamp(&q); +     mp_exch(&q, c); +  } +  mp_clear(&q); +   +  return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_3.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_div_d.c b/src/libtommath/bn_mp_div_d.c new file mode 100644 index 0000000..c0318a4 --- /dev/null +++ b/src/libtommath/bn_mp_div_d.c @@ -0,0 +1,115 @@ +#include "tommath.h" +#ifdef BN_MP_DIV_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +static int s_is_power_of_two(mp_digit b, int *p) +{ +   int x; + +   /* fast return if no power of two */ +   if ((b==0) || (b & (b-1))) { +      return 0; +   } + +   for (x = 0; x < DIGIT_BIT; x++) { +      if (b == (((mp_digit)1)<<x)) { +         *p = x; +         return 1; +      } +   } +   return 0; +} + +/* single digit division (based on routine from MPI) */ +int mp_div_d (mp_int * a, mp_digit b, mp_int * c, mp_digit * d) +{ +  mp_int  q; +  mp_word w; +  mp_digit t; +  int     res, ix; + +  /* cannot divide by zero */ +  if (b == 0) { +     return MP_VAL; +  } + +  /* quick outs */ +  if (b == 1 || mp_iszero(a) == 1) { +     if (d != NULL) { +        *d = 0; +     } +     if (c != NULL) { +        return mp_copy(a, c); +     } +     return MP_OKAY; +  } + +  /* power of two ? */ +  if (s_is_power_of_two(b, &ix) == 1) { +     if (d != NULL) { +        *d = a->dp[0] & ((((mp_digit)1)<<ix) - 1); +     } +     if (c != NULL) { +        return mp_div_2d(a, ix, c, NULL); +     } +     return MP_OKAY; +  } + +#ifdef BN_MP_DIV_3_C +  /* three? */ +  if (b == 3) { +     return mp_div_3(a, c, d); +  } +#endif + +  /* no easy answer [c'est la vie].  Just division */ +  if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { +     return res; +  } +   +  q.used = a->used; +  q.sign = a->sign; +  w = 0; +  for (ix = a->used - 1; ix >= 0; ix--) { +     w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); +      +     if (w >= b) { +        t = (mp_digit)(w / b); +        w -= ((mp_word)t) * ((mp_word)b); +      } else { +        t = 0; +      } +      q.dp[ix] = (mp_digit)t; +  } +   +  if (d != NULL) { +     *d = (mp_digit)w; +  } +   +  if (c != NULL) { +     mp_clamp(&q); +     mp_exch(&q, c); +  } +  mp_clear(&q); +   +  return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_div_d.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2007/01/09 04:44:32 $ */ diff --git a/src/libtommath/bn_mp_dr_is_modulus.c b/src/libtommath/bn_mp_dr_is_modulus.c new file mode 100644 index 0000000..22ba5df --- /dev/null +++ b/src/libtommath/bn_mp_dr_is_modulus.c @@ -0,0 +1,43 @@ +#include "tommath.h" +#ifdef BN_MP_DR_IS_MODULUS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) +{ +   int ix; + +   /* must be at least two digits */ +   if (a->used < 2) { +      return 0; +   } + +   /* must be of the form b**k - a [a <= b] so all +    * but the first digit must be equal to -1 (mod b). +    */ +   for (ix = 1; ix < a->used; ix++) { +       if (a->dp[ix] != MP_MASK) { +          return 0; +       } +   } +   return 1; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_is_modulus.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_dr_reduce.c b/src/libtommath/bn_mp_dr_reduce.c new file mode 100644 index 0000000..0afac94 --- /dev/null +++ b/src/libtommath/bn_mp_dr_reduce.c @@ -0,0 +1,94 @@ +#include "tommath.h" +#ifdef BN_MP_DR_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + *                 Chae Hoon Lim, Pil Joong Lee, + *          POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) +{ +  int      err, i, m; +  mp_word  r; +  mp_digit mu, *tmpx1, *tmpx2; + +  /* m = digits in modulus */ +  m = n->used; + +  /* ensure that "x" has at least 2m digits */ +  if (x->alloc < m + m) { +    if ((err = mp_grow (x, m + m)) != MP_OKAY) { +      return err; +    } +  } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: +  /* aliases for digits */ +  /* alias for lower half of x */ +  tmpx1 = x->dp; + +  /* alias for upper half of x, or x/B**m */ +  tmpx2 = x->dp + m; + +  /* set carry to zero */ +  mu = 0; + +  /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ +  for (i = 0; i < m; i++) { +      r         = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu; +      *tmpx1++  = (mp_digit)(r & MP_MASK); +      mu        = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); +  } + +  /* set final carry */ +  *tmpx1++ = mu; + +  /* zero words above m */ +  for (i = m + 1; i < x->used; i++) { +      *tmpx1++ = 0; +  } + +  /* clamp, sub and return */ +  mp_clamp (x); + +  /* if x >= n then subtract and reduce again +   * Each successive "recursion" makes the input smaller and smaller. +   */ +  if (mp_cmp_mag (x, n) != MP_LT) { +    s_mp_sub(x, n, x); +    goto top; +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_reduce.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_dr_setup.c b/src/libtommath/bn_mp_dr_setup.c new file mode 100644 index 0000000..a5152f7 --- /dev/null +++ b/src/libtommath/bn_mp_dr_setup.c @@ -0,0 +1,32 @@ +#include "tommath.h" +#ifdef BN_MP_DR_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) +{ +   /* the casts are required if DIGIT_BIT is one less than +    * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] +    */ +   *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) -  +        ((mp_word)a->dp[0])); +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_dr_setup.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_exch.c b/src/libtommath/bn_mp_exch.c new file mode 100644 index 0000000..e5ec7f5 --- /dev/null +++ b/src/libtommath/bn_mp_exch.c @@ -0,0 +1,34 @@ +#include "tommath.h" +#ifdef BN_MP_EXCH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* swap the elements of two integers, for cases where you can't simply swap the  + * mp_int pointers around + */ +void +mp_exch (mp_int * a, mp_int * b) +{ +  mp_int  t; + +  t  = *a; +  *a = *b; +  *b = t; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exch.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_expt_d.c b/src/libtommath/bn_mp_expt_d.c new file mode 100644 index 0000000..7bf371c --- /dev/null +++ b/src/libtommath/bn_mp_expt_d.c @@ -0,0 +1,57 @@ +#include "tommath.h" +#ifdef BN_MP_EXPT_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* calculate c = a**b  using a square-multiply algorithm */ +int mp_expt_d (mp_int * a, mp_digit b, mp_int * c) +{ +  int     res, x; +  mp_int  g; + +  if ((res = mp_init_copy (&g, a)) != MP_OKAY) { +    return res; +  } + +  /* set initial result */ +  mp_set (c, 1); + +  for (x = 0; x < (int) DIGIT_BIT; x++) { +    /* square */ +    if ((res = mp_sqr (c, c)) != MP_OKAY) { +      mp_clear (&g); +      return res; +    } + +    /* if the bit is set multiply */ +    if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { +      if ((res = mp_mul (c, &g, c)) != MP_OKAY) { +         mp_clear (&g); +         return res; +      } +    } + +    /* shift to next bit */ +    b <<= 1; +  } + +  mp_clear (&g); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_expt_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_exptmod.c b/src/libtommath/bn_mp_exptmod.c new file mode 100644 index 0000000..27c46ea --- /dev/null +++ b/src/libtommath/bn_mp_exptmod.c @@ -0,0 +1,112 @@ +#include "tommath.h" +#ifdef BN_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions.  Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ +  int dr; + +  /* modulus P must be positive */ +  if (P->sign == MP_NEG) { +     return MP_VAL; +  } + +  /* if exponent X is negative we have to recurse */ +  if (X->sign == MP_NEG) { +#ifdef BN_MP_INVMOD_C +     mp_int tmpG, tmpX; +     int err; + +     /* first compute 1/G mod P */ +     if ((err = mp_init(&tmpG)) != MP_OKAY) { +        return err; +     } +     if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { +        mp_clear(&tmpG); +        return err; +     } + +     /* now get |X| */ +     if ((err = mp_init(&tmpX)) != MP_OKAY) { +        mp_clear(&tmpG); +        return err; +     } +     if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { +        mp_clear_multi(&tmpG, &tmpX, NULL); +        return err; +     } + +     /* and now compute (1/G)**|X| instead of G**X [X < 0] */ +     err = mp_exptmod(&tmpG, &tmpX, P, Y); +     mp_clear_multi(&tmpG, &tmpX, NULL); +     return err; +#else  +     /* no invmod */ +     return MP_VAL; +#endif +  } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) +  if (mp_reduce_is_2k_l(P) == MP_YES) { +     return s_mp_exptmod(G, X, P, Y, 1); +  } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C +  /* is it a DR modulus? */ +  dr = mp_dr_is_modulus(P); +#else +  /* default to no */ +  dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C +  /* if not, is it a unrestricted DR modulus? */ +  if (dr == 0) { +     dr = mp_reduce_is_2k(P) << 1; +  } +#endif +     +  /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C +  if (mp_isodd (P) == 1 || dr !=  0) { +    return mp_exptmod_fast (G, X, P, Y, dr); +  } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C +    /* otherwise use the generic Barrett reduction technique */ +    return s_mp_exptmod (G, X, P, Y, 0); +#else +    /* no exptmod for evens */ +    return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C +  } +#endif +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_exptmod_fast.c b/src/libtommath/bn_mp_exptmod_fast.c new file mode 100644 index 0000000..31205d4 --- /dev/null +++ b/src/libtommath/bn_mp_exptmod_fast.c @@ -0,0 +1,321 @@ +#include "tommath.h" +#ifdef BN_MP_EXPTMOD_FAST_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +#ifdef MP_LOW_MEM +   #define TAB_SIZE 32 +#else +   #define TAB_SIZE 256 +#endif + +int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ +  mp_int  M[TAB_SIZE], res; +  mp_digit buf, mp; +  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + +  /* use a pointer to the reduction algorithm.  This allows us to use +   * one of many reduction algorithms without modding the guts of +   * the code with if statements everywhere. +   */ +  int     (*redux)(mp_int*,mp_int*,mp_digit); + +  /* find window size */ +  x = mp_count_bits (X); +  if (x <= 7) { +    winsize = 2; +  } else if (x <= 36) { +    winsize = 3; +  } else if (x <= 140) { +    winsize = 4; +  } else if (x <= 450) { +    winsize = 5; +  } else if (x <= 1303) { +    winsize = 6; +  } else if (x <= 3529) { +    winsize = 7; +  } else { +    winsize = 8; +  } + +#ifdef MP_LOW_MEM +  if (winsize > 5) { +     winsize = 5; +  } +#endif + +  /* init M array */ +  /* init first cell */ +  if ((err = mp_init(&M[1])) != MP_OKAY) { +     return err; +  } + +  /* now init the second half of the array */ +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) { +    if ((err = mp_init(&M[x])) != MP_OKAY) { +      for (y = 1<<(winsize-1); y < x; y++) { +        mp_clear (&M[y]); +      } +      mp_clear(&M[1]); +      return err; +    } +  } + +  /* determine and setup reduction code */ +  if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C      +     /* now setup montgomery  */ +     if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { +        goto LBL_M; +     } +#else +     err = MP_VAL; +     goto LBL_M; +#endif + +     /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +     if (((P->used * 2 + 1) < MP_WARRAY) && +          P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { +        redux = fast_mp_montgomery_reduce; +     } else  +#endif +     { +#ifdef BN_MP_MONTGOMERY_REDUCE_C +        /* use slower baseline Montgomery method */ +        redux = mp_montgomery_reduce; +#else +        err = MP_VAL; +        goto LBL_M; +#endif +     } +  } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) +     /* setup DR reduction for moduli of the form B**k - b */ +     mp_dr_setup(P, &mp); +     redux = mp_dr_reduce; +#else +     err = MP_VAL; +     goto LBL_M; +#endif +  } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) +     /* setup DR reduction for moduli of the form 2**k - b */ +     if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { +        goto LBL_M; +     } +     redux = mp_reduce_2k; +#else +     err = MP_VAL; +     goto LBL_M; +#endif +  } + +  /* setup result */ +  if ((err = mp_init (&res)) != MP_OKAY) { +    goto LBL_M; +  } + +  /* create M table +   * + +   * +   * The first half of the table is not computed though accept for M[0] and M[1] +   */ + +  if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +     /* now we need R mod m */ +     if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { +       goto LBL_RES; +     } +#else  +     err = MP_VAL; +     goto LBL_RES; +#endif + +     /* now set M[1] to G * R mod m */ +     if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { +       goto LBL_RES; +     } +  } else { +     mp_set(&res, 1); +     if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { +        goto LBL_RES; +     } +  } + +  /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ +  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { +    goto LBL_RES; +  } + +  for (x = 0; x < (winsize - 1); x++) { +    if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { +      goto LBL_RES; +    } +    if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { +      goto LBL_RES; +    } +  } + +  /* create upper table */ +  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { +    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { +      goto LBL_RES; +    } +    if ((err = redux (&M[x], P, mp)) != MP_OKAY) { +      goto LBL_RES; +    } +  } + +  /* set initial mode and bit cnt */ +  mode   = 0; +  bitcnt = 1; +  buf    = 0; +  digidx = X->used - 1; +  bitcpy = 0; +  bitbuf = 0; + +  for (;;) { +    /* grab next digit as required */ +    if (--bitcnt == 0) { +      /* if digidx == -1 we are out of digits so break */ +      if (digidx == -1) { +        break; +      } +      /* read next digit and reset bitcnt */ +      buf    = X->dp[digidx--]; +      bitcnt = (int)DIGIT_BIT; +    } + +    /* grab the next msb from the exponent */ +    y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; +    buf <<= (mp_digit)1; + +    /* if the bit is zero and mode == 0 then we ignore it +     * These represent the leading zero bits before the first 1 bit +     * in the exponent.  Technically this opt is not required but it +     * does lower the # of trivial squaring/reductions used +     */ +    if (mode == 0 && y == 0) { +      continue; +    } + +    /* if the bit is zero and mode == 1 then we square */ +    if (mode == 1 && y == 0) { +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, mp)) != MP_OKAY) { +        goto LBL_RES; +      } +      continue; +    } + +    /* else we add it to the window */ +    bitbuf |= (y << (winsize - ++bitcpy)); +    mode    = 2; + +    if (bitcpy == winsize) { +      /* ok window is filled so square as required and multiply  */ +      /* square first */ +      for (x = 0; x < winsize; x++) { +        if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +          goto LBL_RES; +        } +        if ((err = redux (&res, P, mp)) != MP_OKAY) { +          goto LBL_RES; +        } +      } + +      /* then multiply */ +      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, mp)) != MP_OKAY) { +        goto LBL_RES; +      } + +      /* empty window and reset */ +      bitcpy = 0; +      bitbuf = 0; +      mode   = 1; +    } +  } + +  /* if bits remain then square/multiply */ +  if (mode == 2 && bitcpy > 0) { +    /* square then multiply if the bit is set */ +    for (x = 0; x < bitcpy; x++) { +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, mp)) != MP_OKAY) { +        goto LBL_RES; +      } + +      /* get next bit of the window */ +      bitbuf <<= 1; +      if ((bitbuf & (1 << winsize)) != 0) { +        /* then multiply */ +        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { +          goto LBL_RES; +        } +        if ((err = redux (&res, P, mp)) != MP_OKAY) { +          goto LBL_RES; +        } +      } +    } +  } + +  if (redmode == 0) { +     /* fixup result if Montgomery reduction is used +      * recall that any value in a Montgomery system is +      * actually multiplied by R mod n.  So we have +      * to reduce one more time to cancel out the factor +      * of R. +      */ +     if ((err = redux(&res, P, mp)) != MP_OKAY) { +       goto LBL_RES; +     } +  } + +  /* swap res with Y */ +  mp_exch (&res, Y); +  err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: +  mp_clear(&M[1]); +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) { +    mp_clear (&M[x]); +  } +  return err; +} +#endif + + +/* $Source: /cvs/libtom/libtommath/bn_mp_exptmod_fast.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_exteuclid.c b/src/libtommath/bn_mp_exteuclid.c new file mode 100644 index 0000000..9881d6e --- /dev/null +++ b/src/libtommath/bn_mp_exteuclid.c @@ -0,0 +1,82 @@ +#include "tommath.h" +#ifdef BN_MP_EXTEUCLID_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Extended euclidean algorithm of (a, b) produces  +   a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) +{ +   mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; +   int err; + +   if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { +      return err; +   } + +   /* initialize, (u1,u2,u3) = (1,0,a) */ +   mp_set(&u1, 1); +   if ((err = mp_copy(a, &u3)) != MP_OKAY)                                        { goto _ERR; } + +   /* initialize, (v1,v2,v3) = (0,1,b) */ +   mp_set(&v2, 1); +   if ((err = mp_copy(b, &v3)) != MP_OKAY)                                        { goto _ERR; } + +   /* loop while v3 != 0 */ +   while (mp_iszero(&v3) == MP_NO) { +       /* q = u3/v3 */ +       if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY)                         { goto _ERR; } + +       /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ +       if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY)                              { goto _ERR; } +       if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY)                             { goto _ERR; } +       if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY)                              { goto _ERR; } +       if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY)                             { goto _ERR; } +       if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY)                              { goto _ERR; } +       if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY)                             { goto _ERR; } + +       /* (u1,u2,u3) = (v1,v2,v3) */ +       if ((err = mp_copy(&v1, &u1)) != MP_OKAY)                                  { goto _ERR; } +       if ((err = mp_copy(&v2, &u2)) != MP_OKAY)                                  { goto _ERR; } +       if ((err = mp_copy(&v3, &u3)) != MP_OKAY)                                  { goto _ERR; } + +       /* (v1,v2,v3) = (t1,t2,t3) */ +       if ((err = mp_copy(&t1, &v1)) != MP_OKAY)                                  { goto _ERR; } +       if ((err = mp_copy(&t2, &v2)) != MP_OKAY)                                  { goto _ERR; } +       if ((err = mp_copy(&t3, &v3)) != MP_OKAY)                                  { goto _ERR; } +   } + +   /* make sure U3 >= 0 */ +   if (u3.sign == MP_NEG) { +      mp_neg(&u1, &u1); +      mp_neg(&u2, &u2); +      mp_neg(&u3, &u3); +   } + +   /* copy result out */ +   if (U1 != NULL) { mp_exch(U1, &u1); } +   if (U2 != NULL) { mp_exch(U2, &u2); } +   if (U3 != NULL) { mp_exch(U3, &u3); } + +   err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); +   return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_exteuclid.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_fread.c b/src/libtommath/bn_mp_fread.c new file mode 100644 index 0000000..2976b30 --- /dev/null +++ b/src/libtommath/bn_mp_fread.c @@ -0,0 +1,67 @@ +#include "tommath.h" +#ifdef BN_MP_FREAD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) +{ +   int err, ch, neg, y; +    +   /* clear a */ +   mp_zero(a); +    +   /* if first digit is - then set negative */ +   ch = fgetc(stream); +   if (ch == '-') { +      neg = MP_NEG; +      ch = fgetc(stream); +   } else { +      neg = MP_ZPOS; +   } +    +   for (;;) { +      /* find y in the radix map */ +      for (y = 0; y < radix; y++) { +          if (mp_s_rmap[y] == ch) { +             break; +          } +      } +      if (y == radix) { +         break; +      } +       +      /* shift up and add */ +      if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { +         return err; +      } +      if ((err = mp_add_d(a, y, a)) != MP_OKAY) { +         return err; +      } +       +      ch = fgetc(stream); +   } +   if (mp_cmp_d(a, 0) != MP_EQ) { +      a->sign = neg; +   } +    +   return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fread.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_fwrite.c b/src/libtommath/bn_mp_fwrite.c new file mode 100644 index 0000000..6782b2e --- /dev/null +++ b/src/libtommath/bn_mp_fwrite.c @@ -0,0 +1,52 @@ +#include "tommath.h" +#ifdef BN_MP_FWRITE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +int mp_fwrite(mp_int *a, int radix, FILE *stream) +{ +   char *buf; +   int err, len, x; +    +   if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { +      return err; +   } + +   buf = OPT_CAST(char) XMALLOC (len); +   if (buf == NULL) { +      return MP_MEM; +   } +    +   if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { +      XFREE (buf); +      return err; +   } +    +   for (x = 0; x < len; x++) { +       if (fputc(buf[x], stream) == EOF) { +          XFREE (buf); +          return MP_VAL; +       } +   } +    +   XFREE (buf); +   return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_fwrite.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_gcd.c b/src/libtommath/bn_mp_gcd.c new file mode 100644 index 0000000..ce980eb --- /dev/null +++ b/src/libtommath/bn_mp_gcd.c @@ -0,0 +1,105 @@ +#include "tommath.h" +#ifdef BN_MP_GCD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Greatest Common Divisor using the binary method */ +int mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int  u, v; +  int     k, u_lsb, v_lsb, res; + +  /* either zero than gcd is the largest */ +  if (mp_iszero (a) == MP_YES) { +    return mp_abs (b, c); +  } +  if (mp_iszero (b) == MP_YES) { +    return mp_abs (a, c); +  } + +  /* get copies of a and b we can modify */ +  if ((res = mp_init_copy (&u, a)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_init_copy (&v, b)) != MP_OKAY) { +    goto LBL_U; +  } + +  /* must be positive for the remainder of the algorithm */ +  u.sign = v.sign = MP_ZPOS; + +  /* B1.  Find the common power of two for u and v */ +  u_lsb = mp_cnt_lsb(&u); +  v_lsb = mp_cnt_lsb(&v); +  k     = MIN(u_lsb, v_lsb); + +  if (k > 0) { +     /* divide the power of two out */ +     if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { +        goto LBL_V; +     } + +     if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { +        goto LBL_V; +     } +  } + +  /* divide any remaining factors of two out */ +  if (u_lsb != k) { +     if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { +        goto LBL_V; +     } +  } + +  if (v_lsb != k) { +     if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { +        goto LBL_V; +     } +  } + +  while (mp_iszero(&v) == 0) { +     /* make sure v is the largest */ +     if (mp_cmp_mag(&u, &v) == MP_GT) { +        /* swap u and v to make sure v is >= u */ +        mp_exch(&u, &v); +     } +      +     /* subtract smallest from largest */ +     if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { +        goto LBL_V; +     } +      +     /* Divide out all factors of two */ +     if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { +        goto LBL_V; +     }  +  }  + +  /* multiply by 2**k which we divided out at the beginning */ +  if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { +     goto LBL_V; +  } +  c->sign = MP_ZPOS; +  res = MP_OKAY; +LBL_V:mp_clear (&u); +LBL_U:mp_clear (&v); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_gcd.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_get_int.c b/src/libtommath/bn_mp_get_int.c new file mode 100644 index 0000000..d9c76d0 --- /dev/null +++ b/src/libtommath/bn_mp_get_int.c @@ -0,0 +1,45 @@ +#include "tommath.h" +#ifdef BN_MP_GET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int * a)  +{ +  int i; +  unsigned long res; + +  if (a->used == 0) { +     return 0; +  } + +  /* get number of digits of the lsb we have to read */ +  i = MIN(a->used,(int)((sizeof(unsigned long)*CHAR_BIT+DIGIT_BIT-1)/DIGIT_BIT))-1; + +  /* get most significant digit of result */ +  res = DIGIT(a,i); +    +  while (--i >= 0) { +    res = (res << DIGIT_BIT) | DIGIT(a,i); +  } + +  /* force result to 32-bits always so it is consistent on non 32-bit platforms */ +  return res & 0xFFFFFFFFUL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_get_int.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_grow.c b/src/libtommath/bn_mp_grow.c new file mode 100644 index 0000000..a05dad7 --- /dev/null +++ b/src/libtommath/bn_mp_grow.c @@ -0,0 +1,57 @@ +#include "tommath.h" +#ifdef BN_MP_GROW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* grow as required */ +int mp_grow (mp_int * a, int size) +{ +  int     i; +  mp_digit *tmp; + +  /* if the alloc size is smaller alloc more ram */ +  if (a->alloc < size) { +    /* ensure there are always at least MP_PREC digits extra on top */ +    size += (MP_PREC * 2) - (size % MP_PREC); + +    /* reallocate the array a->dp +     * +     * We store the return in a temporary variable +     * in case the operation failed we don't want +     * to overwrite the dp member of a. +     */ +    tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); +    if (tmp == NULL) { +      /* reallocation failed but "a" is still valid [can be freed] */ +      return MP_MEM; +    } + +    /* reallocation succeeded so set a->dp */ +    a->dp = tmp; + +    /* zero excess digits */ +    i        = a->alloc; +    a->alloc = size; +    for (; i < a->alloc; i++) { +      a->dp[i] = 0; +    } +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_grow.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init.c b/src/libtommath/bn_mp_init.c new file mode 100644 index 0000000..107d98b --- /dev/null +++ b/src/libtommath/bn_mp_init.c @@ -0,0 +1,46 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* init a new mp_int */ +int mp_init (mp_int * a) +{ +  int i; + +  /* allocate memory required and clear it */ +  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); +  if (a->dp == NULL) { +    return MP_MEM; +  } + +  /* set the digits to zero */ +  for (i = 0; i < MP_PREC; i++) { +      a->dp[i] = 0; +  } + +  /* set the used to zero, allocated digits to the default precision +   * and sign to positive */ +  a->used  = 0; +  a->alloc = MP_PREC; +  a->sign  = MP_ZPOS; + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init_copy.c b/src/libtommath/bn_mp_init_copy.c new file mode 100644 index 0000000..3ca1186 --- /dev/null +++ b/src/libtommath/bn_mp_init_copy.c @@ -0,0 +1,32 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* creates "a" then copies b into it */ +int mp_init_copy (mp_int * a, mp_int * b) +{ +  int     res; + +  if ((res = mp_init (a)) != MP_OKAY) { +    return res; +  } +  return mp_copy (b, a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_copy.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init_multi.c b/src/libtommath/bn_mp_init_multi.c new file mode 100644 index 0000000..4f6f367 --- /dev/null +++ b/src/libtommath/bn_mp_init_multi.c @@ -0,0 +1,59 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include <stdarg.h> + +int mp_init_multi(mp_int *mp, ...)  +{ +    mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */ +    int n = 0;                 /* Number of ok inits */ +    mp_int* cur_arg = mp; +    va_list args; + +    va_start(args, mp);        /* init args to next argument from caller */ +    while (cur_arg != NULL) { +        if (mp_init(cur_arg) != MP_OKAY) { +            /* Oops - error! Back-track and mp_clear what we already +               succeeded in init-ing, then return error. +            */ +            va_list clean_args; +             +            /* end the current list */ +            va_end(args); +             +            /* now start cleaning up */             +            cur_arg = mp; +            va_start(clean_args, mp); +            while (n--) { +                mp_clear(cur_arg); +                cur_arg = va_arg(clean_args, mp_int*); +            } +            va_end(clean_args); +            res = MP_MEM; +            break; +        } +        n++; +        cur_arg = va_arg(args, mp_int*); +    } +    va_end(args); +    return res;                /* Assumed ok, if error flagged above. */ +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_multi.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init_set.c b/src/libtommath/bn_mp_init_set.c new file mode 100644 index 0000000..853323f --- /dev/null +++ b/src/libtommath/bn_mp_init_set.c @@ -0,0 +1,32 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b) +{ +  int err; +  if ((err = mp_init(a)) != MP_OKAY) { +     return err; +  } +  mp_set(a, b); +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init_set_int.c b/src/libtommath/bn_mp_init_set_int.c new file mode 100644 index 0000000..b2f8727 --- /dev/null +++ b/src/libtommath/bn_mp_init_set_int.c @@ -0,0 +1,31 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* initialize and set a digit */ +int mp_init_set_int (mp_int * a, unsigned long b) +{ +  int err; +  if ((err = mp_init(a)) != MP_OKAY) { +     return err; +  } +  return mp_set_int(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_set_int.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_init_size.c b/src/libtommath/bn_mp_init_size.c new file mode 100644 index 0000000..17b8d9f --- /dev/null +++ b/src/libtommath/bn_mp_init_size.c @@ -0,0 +1,48 @@ +#include "tommath.h" +#ifdef BN_MP_INIT_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* init an mp_init for a given size */ +int mp_init_size (mp_int * a, int size) +{ +  int x; + +  /* pad size so there are always extra digits */ +  size += (MP_PREC * 2) - (size % MP_PREC);	 +   +  /* alloc mem */ +  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); +  if (a->dp == NULL) { +    return MP_MEM; +  } + +  /* set the members */ +  a->used  = 0; +  a->alloc = size; +  a->sign  = MP_ZPOS; + +  /* zero the digits */ +  for (x = 0; x < size; x++) { +      a->dp[x] = 0; +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_init_size.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_invmod.c b/src/libtommath/bn_mp_invmod.c new file mode 100644 index 0000000..038e584 --- /dev/null +++ b/src/libtommath/bn_mp_invmod.c @@ -0,0 +1,43 @@ +#include "tommath.h" +#ifdef BN_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ +  /* b cannot be negative */ +  if (b->sign == MP_NEG || mp_iszero(b) == 1) { +    return MP_VAL; +  } + +#ifdef BN_FAST_MP_INVMOD_C +  /* if the modulus is odd we can use a faster routine instead */ +  if (mp_isodd (b) == 1) { +    return fast_mp_invmod (a, b, c); +  } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C +  return mp_invmod_slow(a, b, c); +#endif + +  return MP_VAL; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_invmod_slow.c b/src/libtommath/bn_mp_invmod_slow.c new file mode 100644 index 0000000..3792a4c --- /dev/null +++ b/src/libtommath/bn_mp_invmod_slow.c @@ -0,0 +1,175 @@ +#include "tommath.h" +#ifdef BN_MP_INVMOD_SLOW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* hac 14.61, pp608 */ +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int  x, y, u, v, A, B, C, D; +  int     res; + +  /* b cannot be negative */ +  if (b->sign == MP_NEG || mp_iszero(b) == 1) { +    return MP_VAL; +  } + +  /* init temps */ +  if ((res = mp_init_multi(&x, &y, &u, &v,  +                           &A, &B, &C, &D, NULL)) != MP_OKAY) { +     return res; +  } + +  /* x = a, y = b */ +  if ((res = mp_mod(a, b, &x)) != MP_OKAY) { +      goto LBL_ERR; +  } +  if ((res = mp_copy (b, &y)) != MP_OKAY) { +    goto LBL_ERR; +  } + +  /* 2. [modified] if x,y are both even then return an error! */ +  if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { +    res = MP_VAL; +    goto LBL_ERR; +  } + +  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ +  if ((res = mp_copy (&x, &u)) != MP_OKAY) { +    goto LBL_ERR; +  } +  if ((res = mp_copy (&y, &v)) != MP_OKAY) { +    goto LBL_ERR; +  } +  mp_set (&A, 1); +  mp_set (&D, 1); + +top: +  /* 4.  while u is even do */ +  while (mp_iseven (&u) == 1) { +    /* 4.1 u = u/2 */ +    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { +      goto LBL_ERR; +    } +    /* 4.2 if A or B is odd then */ +    if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { +      /* A = (A+y)/2, B = (B-x)/2 */ +      if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { +         goto LBL_ERR; +      } +      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { +         goto LBL_ERR; +      } +    } +    /* A = A/2, B = B/2 */ +    if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { +      goto LBL_ERR; +    } +    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* 5.  while v is even do */ +  while (mp_iseven (&v) == 1) { +    /* 5.1 v = v/2 */ +    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { +      goto LBL_ERR; +    } +    /* 5.2 if C or D is odd then */ +    if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { +      /* C = (C+y)/2, D = (D-x)/2 */ +      if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { +         goto LBL_ERR; +      } +      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { +         goto LBL_ERR; +      } +    } +    /* C = C/2, D = D/2 */ +    if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { +      goto LBL_ERR; +    } +    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* 6.  if u >= v then */ +  if (mp_cmp (&u, &v) != MP_LT) { +    /* u = u - v, A = A - C, B = B - D */ +    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } else { +    /* v - v - u, C = C - A, D = D - B */ +    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { +      goto LBL_ERR; +    } + +    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { +      goto LBL_ERR; +    } +  } + +  /* if not zero goto step 4 */ +  if (mp_iszero (&u) == 0) +    goto top; + +  /* now a = C, b = D, gcd == g*v */ + +  /* if v != 1 then there is no inverse */ +  if (mp_cmp_d (&v, 1) != MP_EQ) { +    res = MP_VAL; +    goto LBL_ERR; +  } + +  /* if its too low */ +  while (mp_cmp_d(&C, 0) == MP_LT) { +      if ((res = mp_add(&C, b, &C)) != MP_OKAY) { +         goto LBL_ERR; +      } +  } +   +  /* too big */ +  while (mp_cmp_mag(&C, b) != MP_LT) { +      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { +         goto LBL_ERR; +      } +  } +   +  /* C is now the inverse */ +  mp_exch (&C, c); +  res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_invmod_slow.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_is_square.c b/src/libtommath/bn_mp_is_square.c new file mode 100644 index 0000000..5d2fa07 --- /dev/null +++ b/src/libtommath/bn_mp_is_square.c @@ -0,0 +1,109 @@ +#include "tommath.h" +#ifdef BN_MP_IS_SQUARE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg,int *ret)  +{ +  int           res; +  mp_digit      c; +  mp_int        t; +  unsigned long r; + +  /* Default to Non-square :) */ +  *ret = MP_NO;  + +  if (arg->sign == MP_NEG) { +    return MP_VAL; +  } + +  /* digits used?  (TSD) */ +  if (arg->used == 0) { +     return MP_OKAY; +  } + +  /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ +  if (rem_128[127 & DIGIT(arg,0)] == 1) { +     return MP_OKAY; +  } + +  /* Next check mod 105 (3*5*7) */ +  if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { +     return res; +  } +  if (rem_105[c] == 1) { +     return MP_OKAY; +  } + + +  if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { +     return res; +  } +  if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { +     goto ERR; +  } +  r = mp_get_int(&t); +  /* Check for other prime modules, note it's not an ERROR but we must +   * free "t" so the easiest way is to goto ERR.  We know that res +   * is already equal to MP_OKAY from the mp_mod call  +   */  +  if ( (1L<<(r%11)) & 0x5C4L )             goto ERR; +  if ( (1L<<(r%13)) & 0x9E4L )             goto ERR; +  if ( (1L<<(r%17)) & 0x5CE8L )            goto ERR; +  if ( (1L<<(r%19)) & 0x4F50CL )           goto ERR; +  if ( (1L<<(r%23)) & 0x7ACCA0L )          goto ERR; +  if ( (1L<<(r%29)) & 0xC2EDD0CL )         goto ERR; +  if ( (1L<<(r%31)) & 0x6DE2B848L )        goto ERR; + +  /* Final check - is sqr(sqrt(arg)) == arg ? */ +  if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { +     goto ERR; +  } +  if ((res = mp_sqr(&t,&t)) != MP_OKAY) { +     goto ERR; +  } + +  *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; +ERR:mp_clear(&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_is_square.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_jacobi.c b/src/libtommath/bn_mp_jacobi.c new file mode 100644 index 0000000..c70b946 --- /dev/null +++ b/src/libtommath/bn_mp_jacobi.c @@ -0,0 +1,105 @@ +#include "tommath.h" +#ifdef BN_MP_JACOBI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int mp_jacobi (mp_int * a, mp_int * p, int *c) +{ +  mp_int  a1, p1; +  int     k, s, r, res; +  mp_digit residue; + +  /* if p <= 0 return MP_VAL */ +  if (mp_cmp_d(p, 0) != MP_GT) { +     return MP_VAL; +  } + +  /* step 1.  if a == 0, return 0 */ +  if (mp_iszero (a) == 1) { +    *c = 0; +    return MP_OKAY; +  } + +  /* step 2.  if a == 1, return 1 */ +  if (mp_cmp_d (a, 1) == MP_EQ) { +    *c = 1; +    return MP_OKAY; +  } + +  /* default */ +  s = 0; + +  /* step 3.  write a = a1 * 2**k  */ +  if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_init (&p1)) != MP_OKAY) { +    goto LBL_A1; +  } + +  /* divide out larger power of two */ +  k = mp_cnt_lsb(&a1); +  if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { +     goto LBL_P1; +  } + +  /* step 4.  if e is even set s=1 */ +  if ((k & 1) == 0) { +    s = 1; +  } else { +    /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ +    residue = p->dp[0] & 7; + +    if (residue == 1 || residue == 7) { +      s = 1; +    } else if (residue == 3 || residue == 5) { +      s = -1; +    } +  } + +  /* step 5.  if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ +  if ( ((p->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { +    s = -s; +  } + +  /* if a1 == 1 we're done */ +  if (mp_cmp_d (&a1, 1) == MP_EQ) { +    *c = s; +  } else { +    /* n1 = n mod a1 */ +    if ((res = mp_mod (p, &a1, &p1)) != MP_OKAY) { +      goto LBL_P1; +    } +    if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { +      goto LBL_P1; +    } +    *c = s * r; +  } + +  /* done */ +  res = MP_OKAY; +LBL_P1:mp_clear (&p1); +LBL_A1:mp_clear (&a1); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_jacobi.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_karatsuba_mul.c b/src/libtommath/bn_mp_karatsuba_mul.c new file mode 100644 index 0000000..b15ec24 --- /dev/null +++ b/src/libtommath/bn_mp_karatsuba_mul.c @@ -0,0 +1,167 @@ +#include "tommath.h" +#ifdef BN_MP_KARATSUBA_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* c = |a| * |b| using Karatsuba Multiplication using  + * three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and  + * let n represent half of the number of digits in  + * the min(a,b) + * + * a = a1 * B**n + a0 + * b = b1 * B**n + b0 + * + * Then, a * b =>  +   a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be  + * computed once.  So in total three half size (half # of  + * digit) multiplications are performed, a0b0, a1b1 and  + * (a1+b1)(a0+b0) + * + * Note that a multiplication of half the digits requires + * 1/4th the number of single precision multiplications so in  + * total after one call 25% of the single precision multiplications  + * are saved.  Note also that the call to mp_mul can end up back  + * in this function if the a0, a1, b0, or b1 are above the threshold.   + * This is known as divide-and-conquer and leads to the famous  + * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than  + * the standard O(N**2) that the baseline/comba methods use.   + * Generally though the overhead of this method doesn't pay off  + * until a certain size (N ~ 80) is reached. + */ +int mp_karatsuba_mul (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int  x0, x1, y0, y1, t1, x0y0, x1y1; +  int     B, err; + +  /* default the return code to an error */ +  err = MP_MEM; + +  /* min # of digits */ +  B = MIN (a->used, b->used); + +  /* now divide in two */ +  B = B >> 1; + +  /* init copy all the temps */ +  if (mp_init_size (&x0, B) != MP_OKAY) +    goto ERR; +  if (mp_init_size (&x1, a->used - B) != MP_OKAY) +    goto X0; +  if (mp_init_size (&y0, B) != MP_OKAY) +    goto X1; +  if (mp_init_size (&y1, b->used - B) != MP_OKAY) +    goto Y0; + +  /* init temps */ +  if (mp_init_size (&t1, B * 2) != MP_OKAY) +    goto Y1; +  if (mp_init_size (&x0y0, B * 2) != MP_OKAY) +    goto T1; +  if (mp_init_size (&x1y1, B * 2) != MP_OKAY) +    goto X0Y0; + +  /* now shift the digits */ +  x0.used = y0.used = B; +  x1.used = a->used - B; +  y1.used = b->used - B; + +  { +    register int x; +    register mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + +    /* we copy the digits directly instead of using higher level functions +     * since we also need to shift the digits +     */ +    tmpa = a->dp; +    tmpb = b->dp; + +    tmpx = x0.dp; +    tmpy = y0.dp; +    for (x = 0; x < B; x++) { +      *tmpx++ = *tmpa++; +      *tmpy++ = *tmpb++; +    } + +    tmpx = x1.dp; +    for (x = B; x < a->used; x++) { +      *tmpx++ = *tmpa++; +    } + +    tmpy = y1.dp; +    for (x = B; x < b->used; x++) { +      *tmpy++ = *tmpb++; +    } +  } + +  /* only need to clamp the lower words since by definition the  +   * upper words x1/y1 must have a known number of digits +   */ +  mp_clamp (&x0); +  mp_clamp (&y0); + +  /* now calc the products x0y0 and x1y1 */ +  /* after this x0 is no longer required, free temp [x0==t2]! */ +  if (mp_mul (&x0, &y0, &x0y0) != MP_OKAY)   +    goto X1Y1;          /* x0y0 = x0*y0 */ +  if (mp_mul (&x1, &y1, &x1y1) != MP_OKAY) +    goto X1Y1;          /* x1y1 = x1*y1 */ + +  /* now calc x1+x0 and y1+y0 */ +  if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) +    goto X1Y1;          /* t1 = x1 - x0 */ +  if (s_mp_add (&y1, &y0, &x0) != MP_OKAY) +    goto X1Y1;          /* t2 = y1 - y0 */ +  if (mp_mul (&t1, &x0, &t1) != MP_OKAY) +    goto X1Y1;          /* t1 = (x1 + x0) * (y1 + y0) */ + +  /* add x0y0 */ +  if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) +    goto X1Y1;          /* t2 = x0y0 + x1y1 */ +  if (s_mp_sub (&t1, &x0, &t1) != MP_OKAY) +    goto X1Y1;          /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + +  /* shift by B */ +  if (mp_lshd (&t1, B) != MP_OKAY) +    goto X1Y1;          /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */ +  if (mp_lshd (&x1y1, B * 2) != MP_OKAY) +    goto X1Y1;          /* x1y1 = x1y1 << 2*B */ + +  if (mp_add (&x0y0, &t1, &t1) != MP_OKAY) +    goto X1Y1;          /* t1 = x0y0 + t1 */ +  if (mp_add (&t1, &x1y1, c) != MP_OKAY) +    goto X1Y1;          /* t1 = x0y0 + t1 + x1y1 */ + +  /* Algorithm succeeded set the return code to MP_OKAY */ +  err = MP_OKAY; + +X1Y1:mp_clear (&x1y1); +X0Y0:mp_clear (&x0y0); +T1:mp_clear (&t1); +Y1:mp_clear (&y1); +Y0:mp_clear (&y0); +X1:mp_clear (&x1); +X0:mp_clear (&x0); +ERR: +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_karatsuba_mul.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_karatsuba_sqr.c b/src/libtommath/bn_mp_karatsuba_sqr.c new file mode 100644 index 0000000..b3a45ab --- /dev/null +++ b/src/libtommath/bn_mp_karatsuba_sqr.c @@ -0,0 +1,121 @@ +#include "tommath.h" +#ifdef BN_MP_KARATSUBA_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Karatsuba squaring, computes b = a*a using three  + * half size squarings + * + * See comments of karatsuba_mul for details.  It  + * is essentially the same algorithm but merely  + * tuned to perform recursive squarings. + */ +int mp_karatsuba_sqr (mp_int * a, mp_int * b) +{ +  mp_int  x0, x1, t1, t2, x0x0, x1x1; +  int     B, err; + +  err = MP_MEM; + +  /* min # of digits */ +  B = a->used; + +  /* now divide in two */ +  B = B >> 1; + +  /* init copy all the temps */ +  if (mp_init_size (&x0, B) != MP_OKAY) +    goto ERR; +  if (mp_init_size (&x1, a->used - B) != MP_OKAY) +    goto X0; + +  /* init temps */ +  if (mp_init_size (&t1, a->used * 2) != MP_OKAY) +    goto X1; +  if (mp_init_size (&t2, a->used * 2) != MP_OKAY) +    goto T1; +  if (mp_init_size (&x0x0, B * 2) != MP_OKAY) +    goto T2; +  if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) +    goto X0X0; + +  { +    register int x; +    register mp_digit *dst, *src; + +    src = a->dp; + +    /* now shift the digits */ +    dst = x0.dp; +    for (x = 0; x < B; x++) { +      *dst++ = *src++; +    } + +    dst = x1.dp; +    for (x = B; x < a->used; x++) { +      *dst++ = *src++; +    } +  } + +  x0.used = B; +  x1.used = a->used - B; + +  mp_clamp (&x0); + +  /* now calc the products x0*x0 and x1*x1 */ +  if (mp_sqr (&x0, &x0x0) != MP_OKAY) +    goto X1X1;           /* x0x0 = x0*x0 */ +  if (mp_sqr (&x1, &x1x1) != MP_OKAY) +    goto X1X1;           /* x1x1 = x1*x1 */ + +  /* now calc (x1+x0)**2 */ +  if (s_mp_add (&x1, &x0, &t1) != MP_OKAY) +    goto X1X1;           /* t1 = x1 - x0 */ +  if (mp_sqr (&t1, &t1) != MP_OKAY) +    goto X1X1;           /* t1 = (x1 - x0) * (x1 - x0) */ + +  /* add x0y0 */ +  if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) +    goto X1X1;           /* t2 = x0x0 + x1x1 */ +  if (s_mp_sub (&t1, &t2, &t1) != MP_OKAY) +    goto X1X1;           /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + +  /* shift by B */ +  if (mp_lshd (&t1, B) != MP_OKAY) +    goto X1X1;           /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<<B */ +  if (mp_lshd (&x1x1, B * 2) != MP_OKAY) +    goto X1X1;           /* x1x1 = x1x1 << 2*B */ + +  if (mp_add (&x0x0, &t1, &t1) != MP_OKAY) +    goto X1X1;           /* t1 = x0x0 + t1 */ +  if (mp_add (&t1, &x1x1, b) != MP_OKAY) +    goto X1X1;           /* t1 = x0x0 + t1 + x1x1 */ + +  err = MP_OKAY; + +X1X1:mp_clear (&x1x1); +X0X0:mp_clear (&x0x0); +T2:mp_clear (&t2); +T1:mp_clear (&t1); +X1:mp_clear (&x1); +X0:mp_clear (&x0); +ERR: +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_karatsuba_sqr.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_lcm.c b/src/libtommath/bn_mp_lcm.c new file mode 100644 index 0000000..af7ae23 --- /dev/null +++ b/src/libtommath/bn_mp_lcm.c @@ -0,0 +1,60 @@ +#include "tommath.h" +#ifdef BN_MP_LCM_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes least common multiple as |a*b|/(a, b) */ +int mp_lcm (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res; +  mp_int  t1, t2; + + +  if ((res = mp_init_multi (&t1, &t2, NULL)) != MP_OKAY) { +    return res; +  } + +  /* t1 = get the GCD of the two inputs */ +  if ((res = mp_gcd (a, b, &t1)) != MP_OKAY) { +    goto LBL_T; +  } + +  /* divide the smallest by the GCD */ +  if (mp_cmp_mag(a, b) == MP_LT) { +     /* store quotient in t2 such that t2 * b is the LCM */ +     if ((res = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) { +        goto LBL_T; +     } +     res = mp_mul(b, &t2, c); +  } else { +     /* store quotient in t2 such that t2 * a is the LCM */ +     if ((res = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) { +        goto LBL_T; +     } +     res = mp_mul(a, &t2, c); +  } + +  /* fix the sign to positive */ +  c->sign = MP_ZPOS; + +LBL_T: +  mp_clear_multi (&t1, &t2, NULL); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lcm.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_lshd.c b/src/libtommath/bn_mp_lshd.c new file mode 100644 index 0000000..ffb0def --- /dev/null +++ b/src/libtommath/bn_mp_lshd.c @@ -0,0 +1,67 @@ +#include "tommath.h" +#ifdef BN_MP_LSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* shift left a certain amount of digits */ +int mp_lshd (mp_int * a, int b) +{ +  int     x, res; + +  /* if its less than zero return */ +  if (b <= 0) { +    return MP_OKAY; +  } + +  /* grow to fit the new digits */ +  if (a->alloc < a->used + b) { +     if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { +       return res; +     } +  } + +  { +    register mp_digit *top, *bottom; + +    /* increment the used by the shift amount then copy upwards */ +    a->used += b; + +    /* top */ +    top = a->dp + a->used - 1; + +    /* base */ +    bottom = a->dp + a->used - 1 - b; + +    /* much like mp_rshd this is implemented using a sliding window +     * except the window goes the otherway around.  Copying from +     * the bottom to the top.  see bn_mp_rshd.c for more info. +     */ +    for (x = a->used - 1; x >= b; x--) { +      *top-- = *bottom--; +    } + +    /* zero the lower digits */ +    top = a->dp; +    for (x = 0; x < b; x++) { +      *top++ = 0; +    } +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_lshd.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mod.c b/src/libtommath/bn_mp_mod.c new file mode 100644 index 0000000..b24c71f --- /dev/null +++ b/src/libtommath/bn_mp_mod.c @@ -0,0 +1,48 @@ +#include "tommath.h" +#ifdef BN_MP_MOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* c = a mod b, 0 <= c < b */ +int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int  t; +  int     res; + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } + +  if (t.sign != b->sign) { +    res = mp_add (b, &t, c); +  } else { +    res = MP_OKAY; +    mp_exch (&t, c); +  } + +  mp_clear (&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mod_2d.c b/src/libtommath/bn_mp_mod_2d.c new file mode 100644 index 0000000..a54a024 --- /dev/null +++ b/src/libtommath/bn_mp_mod_2d.c @@ -0,0 +1,55 @@ +#include "tommath.h" +#ifdef BN_MP_MOD_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* calc a value mod 2**b */ +int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ +  int     x, res; + +  /* if b is <= 0 then zero the int */ +  if (b <= 0) { +    mp_zero (c); +    return MP_OKAY; +  } + +  /* if the modulus is larger than the value than return */ +  if (b >= (int) (a->used * DIGIT_BIT)) { +    res = mp_copy (a, c); +    return res; +  } + +  /* copy */ +  if ((res = mp_copy (a, c)) != MP_OKAY) { +    return res; +  } + +  /* zero digits above the last digit of the modulus */ +  for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { +    c->dp[x] = 0; +  } +  /* clear the digit that is not completely outside/inside the modulus */ +  c->dp[b / DIGIT_BIT] &= +    (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); +  mp_clamp (c); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_2d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mod_d.c b/src/libtommath/bn_mp_mod_d.c new file mode 100644 index 0000000..59886e7 --- /dev/null +++ b/src/libtommath/bn_mp_mod_d.c @@ -0,0 +1,27 @@ +#include "tommath.h" +#ifdef BN_MP_MOD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ +  return mp_div_d(a, b, NULL, c); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mod_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_montgomery_calc_normalization.c b/src/libtommath/bn_mp_montgomery_calc_normalization.c new file mode 100644 index 0000000..fdefcbd --- /dev/null +++ b/src/libtommath/bn_mp_montgomery_calc_normalization.c @@ -0,0 +1,59 @@ +#include "tommath.h" +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b.  This saves alot of multiple precision shifting. + */ +int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ +  int     x, bits, res; + +  /* how many bits of last digit does b use */ +  bits = mp_count_bits (b) % DIGIT_BIT; + +  if (b->used > 1) { +     if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { +        return res; +     } +  } else { +     mp_set(a, 1); +     bits = 1; +  } + + +  /* now compute C = A * B mod b */ +  for (x = bits - 1; x < (int)DIGIT_BIT; x++) { +    if ((res = mp_mul_2 (a, a)) != MP_OKAY) { +      return res; +    } +    if (mp_cmp_mag (a, b) != MP_LT) { +      if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { +        return res; +      } +    } +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_calc_normalization.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_montgomery_reduce.c b/src/libtommath/bn_mp_montgomery_reduce.c new file mode 100644 index 0000000..173848e --- /dev/null +++ b/src/libtommath/bn_mp_montgomery_reduce.c @@ -0,0 +1,118 @@ +#include "tommath.h" +#ifdef BN_MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ +  int     ix, res, digs; +  mp_digit mu; + +  /* can the fast reduction [comba] method be used? +   * +   * Note that unlike in mul you're safely allowed *less* +   * than the available columns [255 per default] since carries +   * are fixed up in the inner loop. +   */ +  digs = n->used * 2 + 1; +  if ((digs < MP_WARRAY) && +      n->used < +      (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { +    return fast_mp_montgomery_reduce (x, n, rho); +  } + +  /* grow the input as required */ +  if (x->alloc < digs) { +    if ((res = mp_grow (x, digs)) != MP_OKAY) { +      return res; +    } +  } +  x->used = digs; + +  for (ix = 0; ix < n->used; ix++) { +    /* mu = ai * rho mod b +     * +     * The value of rho must be precalculated via +     * montgomery_setup() such that +     * it equals -1/n0 mod b this allows the +     * following inner loop to reduce the +     * input one digit at a time +     */ +    mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK); + +    /* a = a + mu * m * b**i */ +    { +      register int iy; +      register mp_digit *tmpn, *tmpx, u; +      register mp_word r; + +      /* alias for digits of the modulus */ +      tmpn = n->dp; + +      /* alias for the digits of x [the input] */ +      tmpx = x->dp + ix; + +      /* set the carry to zero */ +      u = 0; + +      /* Multiply and add in place */ +      for (iy = 0; iy < n->used; iy++) { +        /* compute product and sum */ +        r       = ((mp_word)mu) * ((mp_word)*tmpn++) + +                  ((mp_word) u) + ((mp_word) * tmpx); + +        /* get carry */ +        u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + +        /* fix digit */ +        *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); +      } +      /* At this point the ix'th digit of x should be zero */ + + +      /* propagate carries upwards as required*/ +      while (u) { +        *tmpx   += u; +        u        = *tmpx >> DIGIT_BIT; +        *tmpx++ &= MP_MASK; +      } +    } +  } + +  /* at this point the n.used'th least +   * significant digits of x are all zero +   * which means we can shift x to the +   * right by n.used digits and the +   * residue is unchanged. +   */ + +  /* x = x/b**n.used */ +  mp_clamp(x); +  mp_rshd (x, n->used); + +  /* if x >= n then x = x - n */ +  if (mp_cmp_mag (x, n) != MP_LT) { +    return s_mp_sub (x, n, x); +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_reduce.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_montgomery_setup.c b/src/libtommath/bn_mp_montgomery_setup.c new file mode 100644 index 0000000..6f27732 --- /dev/null +++ b/src/libtommath/bn_mp_montgomery_setup.c @@ -0,0 +1,59 @@ +#include "tommath.h" +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ +  mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n) + *                    =>  2*X*A - X*X*A*A = 1 + *                    =>  2*(1) - (1)     = 1 + */ +  b = n->dp[0]; + +  if ((b & 1) == 0) { +    return MP_VAL; +  } + +  x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ +  x *= 2 - b * x;               /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) +  x *= 2 - b * x;               /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) +  x *= 2 - b * x;               /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT +  x *= 2 - b * x;               /* here x*a==1 mod 2**64 */ +#endif + +  /* rho = -1/m mod b */ +  *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_montgomery_setup.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mul.c b/src/libtommath/bn_mp_mul.c new file mode 100644 index 0000000..a1315da --- /dev/null +++ b/src/libtommath/bn_mp_mul.c @@ -0,0 +1,66 @@ +#include "tommath.h" +#ifdef BN_MP_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* high level multiplication (handles sign) */ +int mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res, neg; +  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + +  /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C +  if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { +    res = mp_toom_mul(a, b, c); +  } else  +#endif +#ifdef BN_MP_KARATSUBA_MUL_C +  /* use Karatsuba? */ +  if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { +    res = mp_karatsuba_mul (a, b, c); +  } else  +#endif +  { +    /* can we use the fast multiplier? +     * +     * The fast multiplier can be used if the output will  +     * have less than MP_WARRAY digits and the number of  +     * digits won't affect carry propagation +     */ +    int     digs = a->used + b->used + 1; + +#ifdef BN_FAST_S_MP_MUL_DIGS_C +    if ((digs < MP_WARRAY) && +        MIN(a->used, b->used) <=  +        (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { +      res = fast_s_mp_mul_digs (a, b, c, digs); +    } else  +#endif +#ifdef BN_S_MP_MUL_DIGS_C +      res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +      res = MP_VAL; +#endif + +  } +  c->sign = (c->used > 0) ? neg : MP_ZPOS; +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mul_2.c b/src/libtommath/bn_mp_mul_2.c new file mode 100644 index 0000000..3315744 --- /dev/null +++ b/src/libtommath/bn_mp_mul_2.c @@ -0,0 +1,82 @@ +#include "tommath.h" +#ifdef BN_MP_MUL_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b) +{ +  int     x, res, oldused; + +  /* grow to accomodate result */ +  if (b->alloc < a->used + 1) { +    if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { +      return res; +    } +  } + +  oldused = b->used; +  b->used = a->used; + +  { +    register mp_digit r, rr, *tmpa, *tmpb; + +    /* alias for source */ +    tmpa = a->dp; +     +    /* alias for dest */ +    tmpb = b->dp; + +    /* carry */ +    r = 0; +    for (x = 0; x < a->used; x++) { +     +      /* get what will be the *next* carry bit from the  +       * MSB of the current digit  +       */ +      rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); +       +      /* now shift up this digit, add in the carry [from the previous] */ +      *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; +       +      /* copy the carry that would be from the source  +       * digit into the next iteration  +       */ +      r = rr; +    } + +    /* new leading digit? */ +    if (r != 0) { +      /* add a MSB which is always 1 at this point */ +      *tmpb = 1; +      ++(b->used); +    } + +    /* now zero any excess digits on the destination  +     * that we didn't write to  +     */ +    tmpb = b->dp + b->used; +    for (x = b->used; x < oldused; x++) { +      *tmpb++ = 0; +    } +  } +  b->sign = a->sign; +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mul_2d.c b/src/libtommath/bn_mp_mul_2d.c new file mode 100644 index 0000000..c636c17 --- /dev/null +++ b/src/libtommath/bn_mp_mul_2d.c @@ -0,0 +1,85 @@ +#include "tommath.h" +#ifdef BN_MP_MUL_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* shift left by a certain bit count */ +int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ +  mp_digit d; +  int      res; + +  /* copy */ +  if (a != c) { +     if ((res = mp_copy (a, c)) != MP_OKAY) { +       return res; +     } +  } + +  if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { +     if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { +       return res; +     } +  } + +  /* shift by as many digits in the bit count */ +  if (b >= (int)DIGIT_BIT) { +    if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { +      return res; +    } +  } + +  /* shift any bit count < DIGIT_BIT */ +  d = (mp_digit) (b % DIGIT_BIT); +  if (d != 0) { +    register mp_digit *tmpc, shift, mask, r, rr; +    register int x; + +    /* bitmask for carries */ +    mask = (((mp_digit)1) << d) - 1; + +    /* shift for msbs */ +    shift = DIGIT_BIT - d; + +    /* alias */ +    tmpc = c->dp; + +    /* carry */ +    r    = 0; +    for (x = 0; x < c->used; x++) { +      /* get the higher bits of the current word */ +      rr = (*tmpc >> shift) & mask; + +      /* shift the current word and OR in the carry */ +      *tmpc = ((*tmpc << d) | r) & MP_MASK; +      ++tmpc; + +      /* set the carry to the carry bits of the current word */ +      r = rr; +    } +     +    /* set final carry */ +    if (r != 0) { +       c->dp[(c->used)++] = r; +    } +  } +  mp_clamp (c); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_2d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mul_d.c b/src/libtommath/bn_mp_mul_d.c new file mode 100644 index 0000000..a36a76b --- /dev/null +++ b/src/libtommath/bn_mp_mul_d.c @@ -0,0 +1,79 @@ +#include "tommath.h" +#ifdef BN_MP_MUL_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ +  mp_digit u, *tmpa, *tmpc; +  mp_word  r; +  int      ix, res, olduse; + +  /* make sure c is big enough to hold a*b */ +  if (c->alloc < a->used + 1) { +    if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { +      return res; +    } +  } + +  /* get the original destinations used count */ +  olduse = c->used; + +  /* set the sign */ +  c->sign = a->sign; + +  /* alias for a->dp [source] */ +  tmpa = a->dp; + +  /* alias for c->dp [dest] */ +  tmpc = c->dp; + +  /* zero carry */ +  u = 0; + +  /* compute columns */ +  for (ix = 0; ix < a->used; ix++) { +    /* compute product and carry sum for this term */ +    r       = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + +    /* mask off higher bits to get a single digit */ +    *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + +    /* send carry into next iteration */ +    u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); +  } + +  /* store final carry [if any] and increment ix offset  */ +  *tmpc++ = u; +  ++ix; + +  /* now zero digits above the top */ +  while (ix++ < olduse) { +     *tmpc++ = 0; +  } + +  /* set used count */ +  c->used = a->used + 1; +  mp_clamp(c); + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mul_d.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_mulmod.c b/src/libtommath/bn_mp_mulmod.c new file mode 100644 index 0000000..8ec98bb --- /dev/null +++ b/src/libtommath/bn_mp_mulmod.c @@ -0,0 +1,40 @@ +#include "tommath.h" +#ifdef BN_MP_MULMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* d = a * b (mod c) */ +int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ +  int     res; +  mp_int  t; + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_mul (a, b, &t)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } +  res = mp_mod (&t, c, d); +  mp_clear (&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_mulmod.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_n_root.c b/src/libtommath/bn_mp_n_root.c new file mode 100644 index 0000000..f188f52 --- /dev/null +++ b/src/libtommath/bn_mp_n_root.c @@ -0,0 +1,132 @@ +#include "tommath.h" +#ifdef BN_MP_N_ROOT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* find the n'th root of an integer  + * + * Result found such that (c)**b <= a and (c+1)**b > a  + * + * This algorithm uses Newton's approximation  + * x[i+1] = x[i] - f(x[i])/f'(x[i])  + * which will find the root in log(N) time where  + * each step involves a fair bit.  This is not meant to  + * find huge roots [square and cube, etc]. + */ +int mp_n_root (mp_int * a, mp_digit b, mp_int * c) +{ +  mp_int  t1, t2, t3; +  int     res, neg; + +  /* input must be positive if b is even */ +  if ((b & 1) == 0 && a->sign == MP_NEG) { +    return MP_VAL; +  } + +  if ((res = mp_init (&t1)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_init (&t2)) != MP_OKAY) { +    goto LBL_T1; +  } + +  if ((res = mp_init (&t3)) != MP_OKAY) { +    goto LBL_T2; +  } + +  /* if a is negative fudge the sign but keep track */ +  neg     = a->sign; +  a->sign = MP_ZPOS; + +  /* t2 = 2 */ +  mp_set (&t2, 2); + +  do { +    /* t1 = t2 */ +    if ((res = mp_copy (&t2, &t1)) != MP_OKAY) { +      goto LBL_T3; +    } + +    /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ +     +    /* t3 = t1**(b-1) */ +    if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) {    +      goto LBL_T3; +    } + +    /* numerator */ +    /* t2 = t1**b */ +    if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) {     +      goto LBL_T3; +    } + +    /* t2 = t1**b - a */ +    if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) {   +      goto LBL_T3; +    } + +    /* denominator */ +    /* t3 = t1**(b-1) * b  */ +    if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) {     +      goto LBL_T3; +    } + +    /* t3 = (t1**b - a)/(b * t1**(b-1)) */ +    if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) {   +      goto LBL_T3; +    } + +    if ((res = mp_sub (&t1, &t3, &t2)) != MP_OKAY) { +      goto LBL_T3; +    } +  }  while (mp_cmp (&t1, &t2) != MP_EQ); + +  /* result can be off by a few so check */ +  for (;;) { +    if ((res = mp_expt_d (&t1, b, &t2)) != MP_OKAY) { +      goto LBL_T3; +    } + +    if (mp_cmp (&t2, a) == MP_GT) { +      if ((res = mp_sub_d (&t1, 1, &t1)) != MP_OKAY) { +         goto LBL_T3; +      } +    } else { +      break; +    } +  } + +  /* reset the sign of a first */ +  a->sign = neg; + +  /* set the result */ +  mp_exch (&t1, c); + +  /* set the sign of the result */ +  c->sign = neg; + +  res = MP_OKAY; + +LBL_T3:mp_clear (&t3); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_n_root.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_neg.c b/src/libtommath/bn_mp_neg.c new file mode 100644 index 0000000..87a8b50 --- /dev/null +++ b/src/libtommath/bn_mp_neg.c @@ -0,0 +1,40 @@ +#include "tommath.h" +#ifdef BN_MP_NEG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* b = -a */ +int mp_neg (mp_int * a, mp_int * b) +{ +  int     res; +  if (a != b) { +     if ((res = mp_copy (a, b)) != MP_OKAY) { +        return res; +     } +  } + +  if (mp_iszero(b) != MP_YES) { +     b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; +  } else { +     b->sign = MP_ZPOS; +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_neg.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_or.c b/src/libtommath/bn_mp_or.c new file mode 100644 index 0000000..12601ea --- /dev/null +++ b/src/libtommath/bn_mp_or.c @@ -0,0 +1,50 @@ +#include "tommath.h" +#ifdef BN_MP_OR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* OR two ints together */ +int mp_or (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res, ix, px; +  mp_int  t, *x; + +  if (a->used > b->used) { +    if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +      return res; +    } +    px = b->used; +    x = b; +  } else { +    if ((res = mp_init_copy (&t, b)) != MP_OKAY) { +      return res; +    } +    px = a->used; +    x = a; +  } + +  for (ix = 0; ix < px; ix++) { +    t.dp[ix] |= x->dp[ix]; +  } +  mp_clamp (&t); +  mp_exch (c, &t); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_or.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_fermat.c b/src/libtommath/bn_mp_prime_fermat.c new file mode 100644 index 0000000..297e13c --- /dev/null +++ b/src/libtommath/bn_mp_prime_fermat.c @@ -0,0 +1,62 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_FERMAT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* performs one Fermat test. + *  + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1.  That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat (mp_int * a, mp_int * b, int *result) +{ +  mp_int  t; +  int     err; + +  /* default to composite  */ +  *result = MP_NO; + +  /* ensure b > 1 */ +  if (mp_cmp_d(b, 1) != MP_GT) { +     return MP_VAL; +  } + +  /* init t */ +  if ((err = mp_init (&t)) != MP_OKAY) { +    return err; +  } + +  /* compute t = b**a mod a */ +  if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { +    goto LBL_T; +  } + +  /* is it equal to b? */ +  if (mp_cmp (&t, b) == MP_EQ) { +    *result = MP_YES; +  } + +  err = MP_OKAY; +LBL_T:mp_clear (&t); +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_fermat.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_is_divisible.c b/src/libtommath/bn_mp_prime_is_divisible.c new file mode 100644 index 0000000..0ae6498 --- /dev/null +++ b/src/libtommath/bn_mp_prime_is_divisible.c @@ -0,0 +1,50 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_IS_DIVISIBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines if an integers is divisible by one  + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible (mp_int * a, int *result) +{ +  int     err, ix; +  mp_digit res; + +  /* default to not */ +  *result = MP_NO; + +  for (ix = 0; ix < PRIME_SIZE; ix++) { +    /* what is a mod LBL_prime_tab[ix] */ +    if ((err = mp_mod_d (a, ltm_prime_tab[ix], &res)) != MP_OKAY) { +      return err; +    } + +    /* is the residue zero? */ +    if (res == 0) { +      *result = MP_YES; +      return MP_OKAY; +    } +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_divisible.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_is_prime.c b/src/libtommath/bn_mp_prime_is_prime.c new file mode 100644 index 0000000..0e1e94b --- /dev/null +++ b/src/libtommath/bn_mp_prime_is_prime.c @@ -0,0 +1,83 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_IS_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime (mp_int * a, int t, int *result) +{ +  mp_int  b; +  int     ix, err, res; + +  /* default to no */ +  *result = MP_NO; + +  /* valid value of t? */ +  if (t <= 0 || t > PRIME_SIZE) { +    return MP_VAL; +  } + +  /* is the input equal to one of the primes in the table? */ +  for (ix = 0; ix < PRIME_SIZE; ix++) { +      if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { +         *result = 1; +         return MP_OKAY; +      } +  } + +  /* first perform trial division */ +  if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { +    return err; +  } + +  /* return if it was trivially divisible */ +  if (res == MP_YES) { +    return MP_OKAY; +  } + +  /* now perform the miller-rabin rounds */ +  if ((err = mp_init (&b)) != MP_OKAY) { +    return err; +  } + +  for (ix = 0; ix < t; ix++) { +    /* set the prime */ +    mp_set (&b, ltm_prime_tab[ix]); + +    if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { +      goto LBL_B; +    } + +    if (res == MP_NO) { +      goto LBL_B; +    } +  } + +  /* passed the test */ +  *result = MP_YES; +LBL_B:mp_clear (&b); +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_is_prime.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_miller_rabin.c b/src/libtommath/bn_mp_prime_miller_rabin.c new file mode 100644 index 0000000..47385bc --- /dev/null +++ b/src/libtommath/bn_mp_prime_miller_rabin.c @@ -0,0 +1,103 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_MILLER_RABIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Miller-Rabin test of "a" to the base of "b" as described in  + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often  + * very much lower. + */ +int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) +{ +  mp_int  n1, y, r; +  int     s, j, err; + +  /* default */ +  *result = MP_NO; + +  /* ensure b > 1 */ +  if (mp_cmp_d(b, 1) != MP_GT) { +     return MP_VAL; +  }      + +  /* get n1 = a - 1 */ +  if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { +    return err; +  } +  if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { +    goto LBL_N1; +  } + +  /* set 2**s * r = n1 */ +  if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { +    goto LBL_N1; +  } + +  /* count the number of least significant bits +   * which are zero +   */ +  s = mp_cnt_lsb(&r); + +  /* now divide n - 1 by 2**s */ +  if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { +    goto LBL_R; +  } + +  /* compute y = b**r mod a */ +  if ((err = mp_init (&y)) != MP_OKAY) { +    goto LBL_R; +  } +  if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { +    goto LBL_Y; +  } + +  /* if y != 1 and y != n1 do */ +  if (mp_cmp_d (&y, 1) != MP_EQ && mp_cmp (&y, &n1) != MP_EQ) { +    j = 1; +    /* while j <= s-1 and y != n1 */ +    while ((j <= (s - 1)) && mp_cmp (&y, &n1) != MP_EQ) { +      if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { +         goto LBL_Y; +      } + +      /* if y == 1 then composite */ +      if (mp_cmp_d (&y, 1) == MP_EQ) { +         goto LBL_Y; +      } + +      ++j; +    } + +    /* if y != n1 then composite */ +    if (mp_cmp (&y, &n1) != MP_EQ) { +      goto LBL_Y; +    } +  } + +  /* probably prime now */ +  *result = MP_YES; +LBL_Y:mp_clear (&y); +LBL_R:mp_clear (&r); +LBL_N1:mp_clear (&n1); +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_miller_rabin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_next_prime.c b/src/libtommath/bn_mp_prime_next_prime.c new file mode 100644 index 0000000..833992b --- /dev/null +++ b/src/libtommath/bn_mp_prime_next_prime.c @@ -0,0 +1,170 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_NEXT_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) +{ +   int      err, res, x, y; +   mp_digit res_tab[PRIME_SIZE], step, kstep; +   mp_int   b; + +   /* ensure t is valid */ +   if (t <= 0 || t > PRIME_SIZE) { +      return MP_VAL; +   } + +   /* force positive */ +   a->sign = MP_ZPOS; + +   /* simple algo if a is less than the largest prime in the table */ +   if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE-1]) == MP_LT) { +      /* find which prime it is bigger than */ +      for (x = PRIME_SIZE - 2; x >= 0; x--) { +          if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { +             if (bbs_style == 1) { +                /* ok we found a prime smaller or +                 * equal [so the next is larger] +                 * +                 * however, the prime must be +                 * congruent to 3 mod 4 +                 */ +                if ((ltm_prime_tab[x + 1] & 3) != 3) { +                   /* scan upwards for a prime congruent to 3 mod 4 */ +                   for (y = x + 1; y < PRIME_SIZE; y++) { +                       if ((ltm_prime_tab[y] & 3) == 3) { +                          mp_set(a, ltm_prime_tab[y]); +                          return MP_OKAY; +                       } +                   } +                } +             } else { +                mp_set(a, ltm_prime_tab[x + 1]); +                return MP_OKAY; +             } +          } +      } +      /* at this point a maybe 1 */ +      if (mp_cmp_d(a, 1) == MP_EQ) { +         mp_set(a, 2); +         return MP_OKAY; +      } +      /* fall through to the sieve */ +   } + +   /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ +   if (bbs_style == 1) { +      kstep   = 4; +   } else { +      kstep   = 2; +   } + +   /* at this point we will use a combination of a sieve and Miller-Rabin */ + +   if (bbs_style == 1) { +      /* if a mod 4 != 3 subtract the correct value to make it so */ +      if ((a->dp[0] & 3) != 3) { +         if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; +      } +   } else { +      if (mp_iseven(a) == 1) { +         /* force odd */ +         if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { +            return err; +         } +      } +   } + +   /* generate the restable */ +   for (x = 1; x < PRIME_SIZE; x++) { +      if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { +         return err; +      } +   } + +   /* init temp used for Miller-Rabin Testing */ +   if ((err = mp_init(&b)) != MP_OKAY) { +      return err; +   } + +   for (;;) { +      /* skip to the next non-trivially divisible candidate */ +      step = 0; +      do { +         /* y == 1 if any residue was zero [e.g. cannot be prime] */ +         y     =  0; + +         /* increase step to next candidate */ +         step += kstep; + +         /* compute the new residue without using division */ +         for (x = 1; x < PRIME_SIZE; x++) { +             /* add the step to each residue */ +             res_tab[x] += kstep; + +             /* subtract the modulus [instead of using division] */ +             if (res_tab[x] >= ltm_prime_tab[x]) { +                res_tab[x]  -= ltm_prime_tab[x]; +             } + +             /* set flag if zero */ +             if (res_tab[x] == 0) { +                y = 1; +             } +         } +      } while (y == 1 && step < ((((mp_digit)1)<<DIGIT_BIT) - kstep)); + +      /* add the step */ +      if ((err = mp_add_d(a, step, a)) != MP_OKAY) { +         goto LBL_ERR; +      } + +      /* if didn't pass sieve and step == MAX then skip test */ +      if (y == 1 && step >= ((((mp_digit)1)<<DIGIT_BIT) - kstep)) { +         continue; +      } + +      /* is this prime? */ +      for (x = 0; x < t; x++) { +          mp_set(&b, ltm_prime_tab[t]); +          if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { +             goto LBL_ERR; +          } +          if (res == MP_NO) { +             break; +          } +      } + +      if (res == MP_YES) { +         break; +      } +   } + +   err = MP_OKAY; +LBL_ERR: +   mp_clear(&b); +   return err; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_next_prime.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_rabin_miller_trials.c b/src/libtommath/bn_mp_prime_rabin_miller_trials.c new file mode 100644 index 0000000..3f7608a --- /dev/null +++ b/src/libtommath/bn_mp_prime_rabin_miller_trials.c @@ -0,0 +1,52 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + + +static const struct { +   int k, t; +} sizes[] = { +{   128,    28 }, +{   256,    16 }, +{   384,    10 }, +{   512,     7 }, +{   640,     6 }, +{   768,     5 }, +{   896,     4 }, +{  1024,     4 } +}; + +/* returns # of RM trials required for a given bit size */ +int mp_prime_rabin_miller_trials(int size) +{ +   int x; + +   for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) { +       if (sizes[x].k == size) { +          return sizes[x].t; +       } else if (sizes[x].k > size) { +          return (x == 0) ? sizes[0].t : sizes[x - 1].t; +       } +   } +   return sizes[x-1].t + 1; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_rabin_miller_trials.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_prime_random_ex.c b/src/libtommath/bn_mp_prime_random_ex.c new file mode 100644 index 0000000..4eec3f6 --- /dev/null +++ b/src/libtommath/bn_mp_prime_random_ex.c @@ -0,0 +1,125 @@ +#include "tommath.h" +#ifdef BN_MP_PRIME_RANDOM_EX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + *  + *   LTM_PRIME_BBS      - make prime congruent to 3 mod 4 + *   LTM_PRIME_SAFE     - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + *   LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + *   LTM_PRIME_2MSB_ON  - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can + * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) +{ +   unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; +   int res, err, bsize, maskOR_msb_offset; + +   /* sanity check the input */ +   if (size <= 1 || t <= 0) { +      return MP_VAL; +   } + +   /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ +   if (flags & LTM_PRIME_SAFE) { +      flags |= LTM_PRIME_BBS; +   } + +   /* calc the byte size */ +   bsize = (size>>3) + ((size&7)?1:0); + +   /* we need a buffer of bsize bytes */ +   tmp = OPT_CAST(unsigned char) XMALLOC(bsize); +   if (tmp == NULL) { +      return MP_MEM; +   } + +   /* calc the maskAND value for the MSbyte*/ +   maskAND = ((size&7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); + +   /* calc the maskOR_msb */ +   maskOR_msb        = 0; +   maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; +   if (flags & LTM_PRIME_2MSB_ON) { +      maskOR_msb       |= 0x80 >> ((9 - size) & 7); +   }   + +   /* get the maskOR_lsb */ +   maskOR_lsb         = 1; +   if (flags & LTM_PRIME_BBS) { +      maskOR_lsb     |= 3; +   } + +   do { +      /* read the bytes */ +      if (cb(tmp, bsize, dat) != bsize) { +         err = MP_VAL; +         goto error; +      } +  +      /* work over the MSbyte */ +      tmp[0]    &= maskAND; +      tmp[0]    |= 1 << ((size - 1) & 7); + +      /* mix in the maskORs */ +      tmp[maskOR_msb_offset]   |= maskOR_msb; +      tmp[bsize-1]             |= maskOR_lsb; + +      /* read it in */ +      if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY)     { goto error; } + +      /* is it prime? */ +      if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY)           { goto error; } +      if (res == MP_NO) {   +         continue; +      } + +      if (flags & LTM_PRIME_SAFE) { +         /* see if (a-1)/2 is prime */ +         if ((err = mp_sub_d(a, 1, a)) != MP_OKAY)                    { goto error; } +         if ((err = mp_div_2(a, a)) != MP_OKAY)                       { goto error; } +  +         /* is it prime? */ +         if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY)        { goto error; } +      } +   } while (res == MP_NO); + +   if (flags & LTM_PRIME_SAFE) { +      /* restore a to the original value */ +      if ((err = mp_mul_2(a, a)) != MP_OKAY)                          { goto error; } +      if ((err = mp_add_d(a, 1, a)) != MP_OKAY)                       { goto error; } +   } + +   err = MP_OKAY; +error: +   XFREE(tmp); +   return err; +} + + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_prime_random_ex.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_radix_size.c b/src/libtommath/bn_mp_radix_size.c new file mode 100644 index 0000000..2378f1f --- /dev/null +++ b/src/libtommath/bn_mp_radix_size.c @@ -0,0 +1,78 @@ +#include "tommath.h" +#ifdef BN_MP_RADIX_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* returns size of ASCII reprensentation */ +int mp_radix_size (mp_int * a, int radix, int *size) +{ +  int     res, digs; +  mp_int  t; +  mp_digit d; + +  *size = 0; + +  /* special case for binary */ +  if (radix == 2) { +    *size = mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; +    return MP_OKAY; +  } + +  /* make sure the radix is in range */ +  if (radix < 2 || radix > 64) { +    return MP_VAL; +  } + +  if (mp_iszero(a) == MP_YES) { +    *size = 2; +    return MP_OKAY; +  } + +  /* digs is the digit count */ +  digs = 0; + +  /* if it's negative add one for the sign */ +  if (a->sign == MP_NEG) { +    ++digs; +  } + +  /* init a copy of the input */ +  if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +    return res; +  } + +  /* force temp to positive */ +  t.sign = MP_ZPOS;  + +  /* fetch out all of the digits */ +  while (mp_iszero (&t) == MP_NO) { +    if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { +      mp_clear (&t); +      return res; +    } +    ++digs; +  } +  mp_clear (&t); + +  /* return digs + 1, the 1 is for the NULL byte that would be required. */ +  *size = digs + 1; +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_size.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_radix_smap.c b/src/libtommath/bn_mp_radix_smap.c new file mode 100644 index 0000000..5cbe952 --- /dev/null +++ b/src/libtommath/bn_mp_radix_smap.c @@ -0,0 +1,24 @@ +#include "tommath.h" +#ifdef BN_MP_RADIX_SMAP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_radix_smap.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_rand.c b/src/libtommath/bn_mp_rand.c new file mode 100644 index 0000000..e124178 --- /dev/null +++ b/src/libtommath/bn_mp_rand.c @@ -0,0 +1,55 @@ +#include "tommath.h" +#ifdef BN_MP_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* makes a pseudo-random int of a given size */ +int +mp_rand (mp_int * a, int digits) +{ +  int     res; +  mp_digit d; + +  mp_zero (a); +  if (digits <= 0) { +    return MP_OKAY; +  } + +  /* first place a random non-zero digit */ +  do { +    d = ((mp_digit) abs (rand ())) & MP_MASK; +  } while (d == 0); + +  if ((res = mp_add_d (a, d, a)) != MP_OKAY) { +    return res; +  } + +  while (--digits > 0) { +    if ((res = mp_lshd (a, 1)) != MP_OKAY) { +      return res; +    } + +    if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { +      return res; +    } +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rand.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_read_radix.c b/src/libtommath/bn_mp_read_radix.c new file mode 100644 index 0000000..6869668 --- /dev/null +++ b/src/libtommath/bn_mp_read_radix.c @@ -0,0 +1,85 @@ +#include "tommath.h" +#ifdef BN_MP_READ_RADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* read a string [ASCII] in a given radix */ +int mp_read_radix (mp_int * a, const char *str, int radix) +{ +  int     y, res, neg; +  char    ch; + +  /* zero the digit bignum */ +  mp_zero(a); + +  /* make sure the radix is ok */ +  if (radix < 2 || radix > 64) { +    return MP_VAL; +  } + +  /* if the leading digit is a  +   * minus set the sign to negative.  +   */ +  if (*str == '-') { +    ++str; +    neg = MP_NEG; +  } else { +    neg = MP_ZPOS; +  } + +  /* set the integer to the default of zero */ +  mp_zero (a); +   +  /* process each digit of the string */ +  while (*str) { +    /* if the radix < 36 the conversion is case insensitive +     * this allows numbers like 1AB and 1ab to represent the same  value +     * [e.g. in hex] +     */ +    ch = (char) ((radix < 36) ? toupper (*str) : *str); +    for (y = 0; y < 64; y++) { +      if (ch == mp_s_rmap[y]) { +         break; +      } +    } + +    /* if the char was found in the map  +     * and is less than the given radix add it +     * to the number, otherwise exit the loop.  +     */ +    if (y < radix) { +      if ((res = mp_mul_d (a, (mp_digit) radix, a)) != MP_OKAY) { +         return res; +      } +      if ((res = mp_add_d (a, (mp_digit) y, a)) != MP_OKAY) { +         return res; +      } +    } else { +      break; +    } +    ++str; +  } +   +  /* set the sign only if a != 0 */ +  if (mp_iszero(a) != 1) { +     a->sign = neg; +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_radix.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_read_signed_bin.c b/src/libtommath/bn_mp_read_signed_bin.c new file mode 100644 index 0000000..e9a780c --- /dev/null +++ b/src/libtommath/bn_mp_read_signed_bin.c @@ -0,0 +1,41 @@ +#include "tommath.h" +#ifdef BN_MP_READ_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int mp_read_signed_bin (mp_int * a, const unsigned char *b, int c) +{ +  int     res; + +  /* read magnitude */ +  if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { +    return res; +  } + +  /* first byte is 0 for positive, non-zero for negative */ +  if (b[0] == 0) { +     a->sign = MP_ZPOS; +  } else { +     a->sign = MP_NEG; +  } + +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_signed_bin.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_read_unsigned_bin.c b/src/libtommath/bn_mp_read_unsigned_bin.c new file mode 100644 index 0000000..7d35370 --- /dev/null +++ b/src/libtommath/bn_mp_read_unsigned_bin.c @@ -0,0 +1,55 @@ +#include "tommath.h" +#ifdef BN_MP_READ_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ +  int     res; + +  /* make sure there are at least two digits */ +  if (a->alloc < 2) { +     if ((res = mp_grow(a, 2)) != MP_OKAY) { +        return res; +     } +  } + +  /* zero the int */ +  mp_zero (a); + +  /* read the bytes in */ +  while (c-- > 0) { +    if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { +      return res; +    } + +#ifndef MP_8BIT +      a->dp[0] |= *b++; +      a->used += 1; +#else +      a->dp[0] = (*b & MP_MASK); +      a->dp[1] |= ((*b++ >> 7U) & 1); +      a->used += 2; +#endif +  } +  mp_clamp (a); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_read_unsigned_bin.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce.c b/src/libtommath/bn_mp_reduce.c new file mode 100644 index 0000000..3a6bb5a --- /dev/null +++ b/src/libtommath/bn_mp_reduce.c @@ -0,0 +1,100 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is  + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ +  mp_int  q; +  int     res, um = m->used; + +  /* q = x */ +  if ((res = mp_init_copy (&q, x)) != MP_OKAY) { +    return res; +  } + +  /* q1 = x / b**(k-1)  */ +  mp_rshd (&q, um - 1);          + +  /* according to HAC this optimization is ok */ +  if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { +    if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { +      goto CLEANUP; +    } +  } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C +    if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { +      goto CLEANUP; +    } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) +    if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { +      goto CLEANUP; +    } +#else  +    {  +      res = MP_VAL; +      goto CLEANUP; +    } +#endif +  } + +  /* q3 = q2 / b**(k+1) */ +  mp_rshd (&q, um + 1);          + +  /* x = x mod b**(k+1), quick (no division) */ +  if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { +    goto CLEANUP; +  } + +  /* q = q * m mod b**(k+1), quick (no division) */ +  if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { +    goto CLEANUP; +  } + +  /* x = x - q */ +  if ((res = mp_sub (x, &q, x)) != MP_OKAY) { +    goto CLEANUP; +  } + +  /* If x < 0, add b**(k+1) to it */ +  if (mp_cmp_d (x, 0) == MP_LT) { +    mp_set (&q, 1); +    if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) +      goto CLEANUP; +    if ((res = mp_add (x, &q, x)) != MP_OKAY) +      goto CLEANUP; +  } + +  /* Back off if it's too big */ +  while (mp_cmp (x, m) != MP_LT) { +    if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { +      goto CLEANUP; +    } +  } +   +CLEANUP: +  mp_clear (&q); + +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_2k.c b/src/libtommath/bn_mp_reduce_2k.c new file mode 100644 index 0000000..3191d82 --- /dev/null +++ b/src/libtommath/bn_mp_reduce_2k.c @@ -0,0 +1,61 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) +{ +   mp_int q; +   int    p, res; +    +   if ((res = mp_init(&q)) != MP_OKAY) { +      return res; +   } +    +   p = mp_count_bits(n);     +top: +   /* q = a/2**p, a = a mod 2**p */ +   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { +      goto ERR; +   } +    +   if (d != 1) { +      /* q = q * d */ +      if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) {  +         goto ERR; +      } +   } +    +   /* a = a + q */ +   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { +      goto ERR; +   } +    +   if (mp_cmp_mag(a, n) != MP_LT) { +      s_mp_sub(a, n, a); +      goto top; +   } +    +ERR: +   mp_clear(&q); +   return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_2k_l.c b/src/libtommath/bn_mp_reduce_2k_l.c new file mode 100644 index 0000000..49b7e34 --- /dev/null +++ b/src/libtommath/bn_mp_reduce_2k_l.c @@ -0,0 +1,62 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reduces a modulo n where n is of the form 2**p - d  +   This differs from reduce_2k since "d" can be larger +   than a single digit. +*/ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ +   mp_int q; +   int    p, res; +    +   if ((res = mp_init(&q)) != MP_OKAY) { +      return res; +   } +    +   p = mp_count_bits(n);     +top: +   /* q = a/2**p, a = a mod 2**p */ +   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { +      goto ERR; +   } +    +   /* q = q * d */ +   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {  +      goto ERR; +   } +    +   /* a = a + q */ +   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { +      goto ERR; +   } +    +   if (mp_cmp_mag(a, n) != MP_LT) { +      s_mp_sub(a, n, a); +      goto top; +   } +    +ERR: +   mp_clear(&q); +   return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_l.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_2k_setup.c b/src/libtommath/bn_mp_reduce_2k_setup.c new file mode 100644 index 0000000..aa3b3ba --- /dev/null +++ b/src/libtommath/bn_mp_reduce_2k_setup.c @@ -0,0 +1,47 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d) +{ +   int res, p; +   mp_int tmp; +    +   if ((res = mp_init(&tmp)) != MP_OKAY) { +      return res; +   } +    +   p = mp_count_bits(a); +   if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { +      mp_clear(&tmp); +      return res; +   } +    +   if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { +      mp_clear(&tmp); +      return res; +   } +    +   *d = tmp.dp[0]; +   mp_clear(&tmp); +   return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_2k_setup_l.c b/src/libtommath/bn_mp_reduce_2k_setup_l.c new file mode 100644 index 0000000..4eca870 --- /dev/null +++ b/src/libtommath/bn_mp_reduce_2k_setup_l.c @@ -0,0 +1,44 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_2K_SETUP_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines the setup value */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ +   int    res; +   mp_int tmp; +    +   if ((res = mp_init(&tmp)) != MP_OKAY) { +      return res; +   } +    +   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { +      goto ERR; +   } +    +   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { +      goto ERR; +   } +    +ERR: +   mp_clear(&tmp); +   return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_2k_setup_l.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_is_2k.c b/src/libtommath/bn_mp_reduce_is_2k.c new file mode 100644 index 0000000..b9ede97 --- /dev/null +++ b/src/libtommath/bn_mp_reduce_is_2k.c @@ -0,0 +1,52 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) +{ +   int ix, iy, iw; +   mp_digit iz; +    +   if (a->used == 0) { +      return MP_NO; +   } else if (a->used == 1) { +      return MP_YES; +   } else if (a->used > 1) { +      iy = mp_count_bits(a); +      iz = 1; +      iw = 1; +     +      /* Test every bit from the second digit up, must be 1 */ +      for (ix = DIGIT_BIT; ix < iy; ix++) { +          if ((a->dp[iw] & iz) == 0) { +             return MP_NO; +          } +          iz <<= 1; +          if (iz > (mp_digit)MP_MASK) { +             ++iw; +             iz = 1; +          } +      } +   } +   return MP_YES; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_is_2k_l.c b/src/libtommath/bn_mp_reduce_is_2k_l.c new file mode 100644 index 0000000..787875f --- /dev/null +++ b/src/libtommath/bn_mp_reduce_is_2k_l.c @@ -0,0 +1,44 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_IS_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* determines if reduce_2k_l can be used */ +int mp_reduce_is_2k_l(mp_int *a) +{ +   int ix, iy; +    +   if (a->used == 0) { +      return MP_NO; +   } else if (a->used == 1) { +      return MP_YES; +   } else if (a->used > 1) { +      /* if more than half of the digits are -1 we're sold */ +      for (iy = ix = 0; ix < a->used; ix++) { +          if (a->dp[ix] == MP_MASK) { +              ++iy; +          } +      } +      return (iy >= (a->used/2)) ? MP_YES : MP_NO; +       +   } +   return MP_NO; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_is_2k_l.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_reduce_setup.c b/src/libtommath/bn_mp_reduce_setup.c new file mode 100644 index 0000000..00e0a62 --- /dev/null +++ b/src/libtommath/bn_mp_reduce_setup.c @@ -0,0 +1,34 @@ +#include "tommath.h" +#ifdef BN_MP_REDUCE_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int mp_reduce_setup (mp_int * a, mp_int * b) +{ +  int     res; +   +  if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { +    return res; +  } +  return mp_div (a, b, a, NULL); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_reduce_setup.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_rshd.c b/src/libtommath/bn_mp_rshd.c new file mode 100644 index 0000000..eac6721 --- /dev/null +++ b/src/libtommath/bn_mp_rshd.c @@ -0,0 +1,72 @@ +#include "tommath.h" +#ifdef BN_MP_RSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* shift right a certain amount of digits */ +void mp_rshd (mp_int * a, int b) +{ +  int     x; + +  /* if b <= 0 then ignore it */ +  if (b <= 0) { +    return; +  } + +  /* if b > used then simply zero it and return */ +  if (a->used <= b) { +    mp_zero (a); +    return; +  } + +  { +    register mp_digit *bottom, *top; + +    /* shift the digits down */ + +    /* bottom */ +    bottom = a->dp; + +    /* top [offset into digits] */ +    top = a->dp + b; + +    /* this is implemented as a sliding window where  +     * the window is b-digits long and digits from  +     * the top of the window are copied to the bottom +     * +     * e.g. + +     b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ----> +                 /\                   |      ----> +                  \-------------------/      ----> +     */ +    for (x = 0; x < (a->used - b); x++) { +      *bottom++ = *top++; +    } + +    /* zero the top digits */ +    for (; x < a->used; x++) { +      *bottom++ = 0; +    } +  } +   +  /* remove excess digits */ +  a->used -= b; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_rshd.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_set.c b/src/libtommath/bn_mp_set.c new file mode 100644 index 0000000..d76d5bb --- /dev/null +++ b/src/libtommath/bn_mp_set.c @@ -0,0 +1,29 @@ +#include "tommath.h" +#ifdef BN_MP_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* set to a digit */ +void mp_set (mp_int * a, mp_digit b) +{ +  mp_zero (a); +  a->dp[0] = b & MP_MASK; +  a->used  = (a->dp[0] != 0) ? 1 : 0; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_set_int.c b/src/libtommath/bn_mp_set_int.c new file mode 100644 index 0000000..68cf0e3 --- /dev/null +++ b/src/libtommath/bn_mp_set_int.c @@ -0,0 +1,48 @@ +#include "tommath.h" +#ifdef BN_MP_SET_INT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* set a 32-bit const */ +int mp_set_int (mp_int * a, unsigned long b) +{ +  int     x, res; + +  mp_zero (a); +   +  /* set four bits at a time */ +  for (x = 0; x < 8; x++) { +    /* shift the number up four bits */ +    if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { +      return res; +    } + +    /* OR in the top four bits of the source */ +    a->dp[0] |= (b >> 28) & 15; + +    /* shift the source up to the next four bits */ +    b <<= 4; + +    /* ensure that digits are not clamped off */ +    a->used += 1; +  } +  mp_clamp (a); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_set_int.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_shrink.c b/src/libtommath/bn_mp_shrink.c new file mode 100644 index 0000000..54920d1 --- /dev/null +++ b/src/libtommath/bn_mp_shrink.c @@ -0,0 +1,35 @@ +#include "tommath.h" +#ifdef BN_MP_SHRINK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* shrink a bignum */ +int mp_shrink (mp_int * a) +{ +  mp_digit *tmp; +  if (a->alloc != a->used && a->used > 0) { +    if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) { +      return MP_MEM; +    } +    a->dp    = tmp; +    a->alloc = a->used; +  } +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_shrink.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_signed_bin_size.c b/src/libtommath/bn_mp_signed_bin_size.c new file mode 100644 index 0000000..b9492a5 --- /dev/null +++ b/src/libtommath/bn_mp_signed_bin_size.c @@ -0,0 +1,27 @@ +#include "tommath.h" +#ifdef BN_MP_SIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* get the size for an signed equivalent */ +int mp_signed_bin_size (mp_int * a) +{ +  return 1 + mp_unsigned_bin_size (a); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_signed_bin_size.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_sqr.c b/src/libtommath/bn_mp_sqr.c new file mode 100644 index 0000000..c10fa6f --- /dev/null +++ b/src/libtommath/bn_mp_sqr.c @@ -0,0 +1,58 @@ +#include "tommath.h" +#ifdef BN_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ +  int     res; + +#ifdef BN_MP_TOOM_SQR_C +  /* use Toom-Cook? */ +  if (a->used >= TOOM_SQR_CUTOFF) { +    res = mp_toom_sqr(a, b); +  /* Karatsuba? */ +  } else  +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { +    res = mp_karatsuba_sqr (a, b); +  } else  +#endif +  { +#ifdef BN_FAST_S_MP_SQR_C +    /* can we use the fast comba multiplier? */ +    if ((a->used * 2 + 1) < MP_WARRAY &&  +         a->used <  +         (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { +      res = fast_s_mp_sqr (a, b); +    } else +#endif +#ifdef BN_S_MP_SQR_C +      res = s_mp_sqr (a, b); +#else +      res = MP_VAL; +#endif +  } +  b->sign = MP_ZPOS; +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqr.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_sqrmod.c b/src/libtommath/bn_mp_sqrmod.c new file mode 100644 index 0000000..5f4b2f3 --- /dev/null +++ b/src/libtommath/bn_mp_sqrmod.c @@ -0,0 +1,41 @@ +#include "tommath.h" +#ifdef BN_MP_SQRMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* c = a * a (mod b) */ +int +mp_sqrmod (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res; +  mp_int  t; + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_sqr (a, &t)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } +  res = mp_mod (&t, b, c); +  mp_clear (&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrmod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_sqrt.c b/src/libtommath/bn_mp_sqrt.c new file mode 100644 index 0000000..e15ba98 --- /dev/null +++ b/src/libtommath/bn_mp_sqrt.c @@ -0,0 +1,81 @@ +#include "tommath.h" +#ifdef BN_MP_SQRT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret)  +{ +  int res; +  mp_int t1,t2; + +  /* must be positive */ +  if (arg->sign == MP_NEG) { +    return MP_VAL; +  } + +  /* easy out */ +  if (mp_iszero(arg) == MP_YES) { +    mp_zero(ret); +    return MP_OKAY; +  } + +  if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_init(&t2)) != MP_OKAY) { +    goto E2; +  } + +  /* First approx. (not very bad for large arg) */ +  mp_rshd (&t1,t1.used/2); + +  /* t1 > 0  */  +  if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { +    goto E1; +  } +  if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { +    goto E1; +  } +  if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { +    goto E1; +  } +  /* And now t1 > sqrt(arg) */ +  do {  +    if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { +      goto E1; +    } +    if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { +      goto E1; +    } +    if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { +      goto E1; +    } +    /* t1 >= sqrt(arg) >= t2 at this point */ +  } while (mp_cmp_mag(&t1,&t2) == MP_GT); + +  mp_exch(&t1,ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); +  return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sqrt.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_sub.c b/src/libtommath/bn_mp_sub.c new file mode 100644 index 0000000..6e72138 --- /dev/null +++ b/src/libtommath/bn_mp_sub.c @@ -0,0 +1,59 @@ +#include "tommath.h" +#ifdef BN_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* high level subtraction (handles signs) */ +int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ +  int     sa, sb, res; + +  sa = a->sign; +  sb = b->sign; + +  if (sa != sb) { +    /* subtract a negative from a positive, OR */ +    /* subtract a positive from a negative. */ +    /* In either case, ADD their magnitudes, */ +    /* and use the sign of the first number. */ +    c->sign = sa; +    res = s_mp_add (a, b, c); +  } else { +    /* subtract a positive from a positive, OR */ +    /* subtract a negative from a negative. */ +    /* First, take the difference between their */ +    /* magnitudes, then... */ +    if (mp_cmp_mag (a, b) != MP_LT) { +      /* Copy the sign from the first */ +      c->sign = sa; +      /* The first has a larger or equal magnitude */ +      res = s_mp_sub (a, b, c); +    } else { +      /* The result has the *opposite* sign from */ +      /* the first number. */ +      c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; +      /* The second has a larger magnitude */ +      res = s_mp_sub (b, a, c); +    } +  } +  return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_sub_d.c b/src/libtommath/bn_mp_sub_d.c new file mode 100644 index 0000000..aa08e31 --- /dev/null +++ b/src/libtommath/bn_mp_sub_d.c @@ -0,0 +1,93 @@ +#include "tommath.h" +#ifdef BN_MP_SUB_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ +  mp_digit *tmpa, *tmpc, mu; +  int       res, ix, oldused; + +  /* grow c as required */ +  if (c->alloc < a->used + 1) { +     if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { +        return res; +     } +  } + +  /* if a is negative just do an unsigned +   * addition [with fudged signs] +   */ +  if (a->sign == MP_NEG) { +     a->sign = MP_ZPOS; +     res     = mp_add_d(a, b, c); +     a->sign = c->sign = MP_NEG; + +     /* clamp */ +     mp_clamp(c); + +     return res; +  } + +  /* setup regs */ +  oldused = c->used; +  tmpa    = a->dp; +  tmpc    = c->dp; + +  /* if a <= b simply fix the single digit */ +  if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) { +     if (a->used == 1) { +        *tmpc++ = b - *tmpa; +     } else { +        *tmpc++ = b; +     } +     ix      = 1; + +     /* negative/1digit */ +     c->sign = MP_NEG; +     c->used = 1; +  } else { +     /* positive/size */ +     c->sign = MP_ZPOS; +     c->used = a->used; + +     /* subtract first digit */ +     *tmpc    = *tmpa++ - b; +     mu       = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); +     *tmpc++ &= MP_MASK; + +     /* handle rest of the digits */ +     for (ix = 1; ix < a->used; ix++) { +        *tmpc    = *tmpa++ - mu; +        mu       = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); +        *tmpc++ &= MP_MASK; +     } +  } + +  /* zero excess digits */ +  while (ix++ < oldused) { +     *tmpc++ = 0; +  } +  mp_clamp(c); +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_sub_d.c,v $ */ +/* $Revision: 1.6 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_submod.c b/src/libtommath/bn_mp_submod.c new file mode 100644 index 0000000..6617ff4 --- /dev/null +++ b/src/libtommath/bn_mp_submod.c @@ -0,0 +1,42 @@ +#include "tommath.h" +#ifdef BN_MP_SUBMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* d = a - b (mod c) */ +int +mp_submod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ +  int     res; +  mp_int  t; + + +  if ((res = mp_init (&t)) != MP_OKAY) { +    return res; +  } + +  if ((res = mp_sub (a, b, &t)) != MP_OKAY) { +    mp_clear (&t); +    return res; +  } +  res = mp_mod (&t, c, d); +  mp_clear (&t); +  return res; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_submod.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_to_signed_bin.c b/src/libtommath/bn_mp_to_signed_bin.c new file mode 100644 index 0000000..154f64b --- /dev/null +++ b/src/libtommath/bn_mp_to_signed_bin.c @@ -0,0 +1,33 @@ +#include "tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin (mp_int * a, unsigned char *b) +{ +  int     res; + +  if ((res = mp_to_unsigned_bin (a, b + 1)) != MP_OKAY) { +    return res; +  } +  b[0] = (unsigned char) ((a->sign == MP_ZPOS) ? 0 : 1); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_to_signed_bin_n.c b/src/libtommath/bn_mp_to_signed_bin_n.c new file mode 100644 index 0000000..e119c38 --- /dev/null +++ b/src/libtommath/bn_mp_to_signed_bin_n.c @@ -0,0 +1,31 @@ +#include "tommath.h" +#ifdef BN_MP_TO_SIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* store in signed [big endian] format */ +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ +   if (*outlen < (unsigned long)mp_signed_bin_size(a)) { +      return MP_VAL; +   } +   *outlen = mp_signed_bin_size(a); +   return mp_to_signed_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_signed_bin_n.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_to_unsigned_bin.c b/src/libtommath/bn_mp_to_unsigned_bin.c new file mode 100644 index 0000000..ce69e5b --- /dev/null +++ b/src/libtommath/bn_mp_to_unsigned_bin.c @@ -0,0 +1,48 @@ +#include "tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ +  int     x, res; +  mp_int  t; + +  if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +    return res; +  } + +  x = 0; +  while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT +      b[x++] = (unsigned char) (t.dp[0] & 255); +#else +      b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif +    if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { +      mp_clear (&t); +      return res; +    } +  } +  bn_reverse (b, x); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_to_unsigned_bin_n.c b/src/libtommath/bn_mp_to_unsigned_bin_n.c new file mode 100644 index 0000000..dfa27c4 --- /dev/null +++ b/src/libtommath/bn_mp_to_unsigned_bin_n.c @@ -0,0 +1,31 @@ +#include "tommath.h" +#ifdef BN_MP_TO_UNSIGNED_BIN_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* store in unsigned [big endian] format */ +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen) +{ +   if (*outlen < (unsigned long)mp_unsigned_bin_size(a)) { +      return MP_VAL; +   } +   *outlen = mp_unsigned_bin_size(a); +   return mp_to_unsigned_bin(a, b); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_to_unsigned_bin_n.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_toom_mul.c b/src/libtommath/bn_mp_toom_mul.c new file mode 100644 index 0000000..e48c6b3 --- /dev/null +++ b/src/libtommath/bn_mp_toom_mul.c @@ -0,0 +1,284 @@ +#include "tommath.h" +#ifdef BN_MP_TOOM_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* multiplication using the Toom-Cook 3-way algorithm  + * + * Much more complicated than Karatsuba but has a lower  + * asymptotic running time of O(N**1.464).  This algorithm is  + * only particularly useful on VERY large inputs  + * (we're talking 1000s of digits here...). +*/ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) +{ +    mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; +    int res, B; +         +    /* init temps */ +    if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4,  +                             &a0, &a1, &a2, &b0, &b1,  +                             &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { +       return res; +    } +     +    /* B */ +    B = MIN(a->used, b->used) / 3; +     +    /* a = a2 * B**2 + a1 * B + a0 */ +    if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { +       goto ERR; +    } + +    if ((res = mp_copy(a, &a1)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&a1, B); +    mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + +    if ((res = mp_copy(a, &a2)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&a2, B*2); +     +    /* b = b2 * B**2 + b1 * B + b0 */ +    if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { +       goto ERR; +    } + +    if ((res = mp_copy(b, &b1)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&b1, B); +    mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + +    if ((res = mp_copy(b, &b2)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&b2, B*2); +     +    /* w0 = a0*b0 */ +    if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { +       goto ERR; +    } +     +    /* w4 = a2 * b2 */ +    if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { +       goto ERR; +    } +     +    /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ +    if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +     +    if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +     +    if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { +       goto ERR; +    } +     +    /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ +    if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +     +    if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +     +    if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { +       goto ERR; +    } +     + +    /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ +    if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { +       goto ERR; +    } +     +    /* now solve the matrix  +     +       0  0  0  0  1 +       1  2  4  8  16 +       1  1  1  1  1 +       16 8  4  2  1 +       1  0  0  0  0 +        +       using 12 subtractions, 4 shifts,  +              2 small divisions and 1 small multiplication  +     */ +      +     /* r1 - r4 */ +     if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r0 */ +     if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1/2 */ +     if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3/2 */ +     if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r2 - r0 - r4 */ +     if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - r2 */ +     if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r2 */ +     if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - 8r0 */ +     if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - 8r4 */ +     if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* 3r2 - r1 - r3 */ +     if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - r2 */ +     if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r2 */ +     if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1/3 */ +     if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { +        goto ERR; +     } +     /* r3/3 */ +     if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { +        goto ERR; +     } +      +     /* at this point shift W[n] by B*n */ +     if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { +        goto ERR; +     }      +      +     if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { +        goto ERR; +     }      +      +ERR: +     mp_clear_multi(&w0, &w1, &w2, &w3, &w4,  +                    &a0, &a1, &a2, &b0, &b1,  +                    &b2, &tmp1, &tmp2, NULL); +     return res; +}      +      +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_mul.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_toom_sqr.c b/src/libtommath/bn_mp_toom_sqr.c new file mode 100644 index 0000000..fd8bc67 --- /dev/null +++ b/src/libtommath/bn_mp_toom_sqr.c @@ -0,0 +1,226 @@ +#include "tommath.h" +#ifdef BN_MP_TOOM_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) +{ +    mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; +    int res, B; + +    /* init temps */ +    if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { +       return res; +    } + +    /* B */ +    B = a->used / 3; + +    /* a = a2 * B**2 + a1 * B + a0 */ +    if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { +       goto ERR; +    } + +    if ((res = mp_copy(a, &a1)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&a1, B); +    mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + +    if ((res = mp_copy(a, &a2)) != MP_OKAY) { +       goto ERR; +    } +    mp_rshd(&a2, B*2); + +    /* w0 = a0*a0 */ +    if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { +       goto ERR; +    } + +    /* w4 = a2 * a2 */ +    if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { +       goto ERR; +    } + +    /* w1 = (a2 + 2(a1 + 2a0))**2 */ +    if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { +       goto ERR; +    } + +    if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { +       goto ERR; +    } + +    /* w3 = (a0 + 2(a1 + 2a2))**2 */ +    if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } + +    if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { +       goto ERR; +    } + + +    /* w2 = (a2 + a1 + a0)**2 */ +    if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { +       goto ERR; +    } +    if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { +       goto ERR; +    } + +    /* now solve the matrix + +       0  0  0  0  1 +       1  2  4  8  16 +       1  1  1  1  1 +       16 8  4  2  1 +       1  0  0  0  0 + +       using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. +     */ + +     /* r1 - r4 */ +     if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r0 */ +     if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1/2 */ +     if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3/2 */ +     if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r2 - r0 - r4 */ +     if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - r2 */ +     if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r2 */ +     if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - 8r0 */ +     if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - 8r4 */ +     if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* 3r2 - r1 - r3 */ +     if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { +        goto ERR; +     } +     /* r1 - r2 */ +     if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { +        goto ERR; +     } +     /* r3 - r2 */ +     if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { +        goto ERR; +     } +     /* r1/3 */ +     if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { +        goto ERR; +     } +     /* r3/3 */ +     if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { +        goto ERR; +     } + +     /* at this point shift W[n] by B*n */ +     if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { +        goto ERR; +     } + +     if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { +        goto ERR; +     } +     if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { +        goto ERR; +     } + +ERR: +     mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); +     return res; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toom_sqr.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_toradix.c b/src/libtommath/bn_mp_toradix.c new file mode 100644 index 0000000..539abe9 --- /dev/null +++ b/src/libtommath/bn_mp_toradix.c @@ -0,0 +1,75 @@ +#include "tommath.h" +#ifdef BN_MP_TORADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int mp_toradix (mp_int * a, char *str, int radix) +{ +  int     res, digs; +  mp_int  t; +  mp_digit d; +  char   *_s = str; + +  /* check range of the radix */ +  if (radix < 2 || radix > 64) { +    return MP_VAL; +  } + +  /* quick out if its zero */ +  if (mp_iszero(a) == 1) { +     *str++ = '0'; +     *str = '\0'; +     return MP_OKAY; +  } + +  if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +    return res; +  } + +  /* if it is negative output a - */ +  if (t.sign == MP_NEG) { +    ++_s; +    *str++ = '-'; +    t.sign = MP_ZPOS; +  } + +  digs = 0; +  while (mp_iszero (&t) == 0) { +    if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { +      mp_clear (&t); +      return res; +    } +    *str++ = mp_s_rmap[d]; +    ++digs; +  } + +  /* reverse the digits of the string.  In this case _s points +   * to the first digit [exluding the sign] of the number] +   */ +  bn_reverse ((unsigned char *)_s, digs); + +  /* append a NULL so the string is properly terminated */ +  *str = '\0'; + +  mp_clear (&t); +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_toradix_n.c b/src/libtommath/bn_mp_toradix_n.c new file mode 100644 index 0000000..0322f8d --- /dev/null +++ b/src/libtommath/bn_mp_toradix_n.c @@ -0,0 +1,88 @@ +#include "tommath.h" +#ifdef BN_MP_TORADIX_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* stores a bignum as a ASCII string in a given radix (2..64)  + * + * Stores upto maxlen-1 chars and always a NULL byte  + */ +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) +{ +  int     res, digs; +  mp_int  t; +  mp_digit d; +  char   *_s = str; + +  /* check range of the maxlen, radix */ +  if (maxlen < 2 || radix < 2 || radix > 64) { +    return MP_VAL; +  } + +  /* quick out if its zero */ +  if (mp_iszero(a) == MP_YES) { +     *str++ = '0'; +     *str = '\0'; +     return MP_OKAY; +  } + +  if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +    return res; +  } + +  /* if it is negative output a - */ +  if (t.sign == MP_NEG) { +    /* we have to reverse our digits later... but not the - sign!! */ +    ++_s; + +    /* store the flag and mark the number as positive */ +    *str++ = '-'; +    t.sign = MP_ZPOS; +  +    /* subtract a char */ +    --maxlen; +  } + +  digs = 0; +  while (mp_iszero (&t) == 0) { +    if (--maxlen < 1) { +       /* no more room */ +       break; +    } +    if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { +      mp_clear (&t); +      return res; +    } +    *str++ = mp_s_rmap[d]; +    ++digs; +  } + +  /* reverse the digits of the string.  In this case _s points +   * to the first digit [exluding the sign] of the number +   */ +  bn_reverse ((unsigned char *)_s, digs); + +  /* append a NULL so the string is properly terminated */ +  *str = '\0'; + +  mp_clear (&t); +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_toradix_n.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_unsigned_bin_size.c b/src/libtommath/bn_mp_unsigned_bin_size.c new file mode 100644 index 0000000..88f3e92 --- /dev/null +++ b/src/libtommath/bn_mp_unsigned_bin_size.c @@ -0,0 +1,28 @@ +#include "tommath.h" +#ifdef BN_MP_UNSIGNED_BIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* get the size for an unsigned equivalent */ +int mp_unsigned_bin_size (mp_int * a) +{ +  int     size = mp_count_bits (a); +  return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_unsigned_bin_size.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_xor.c b/src/libtommath/bn_mp_xor.c new file mode 100644 index 0000000..bf0446e --- /dev/null +++ b/src/libtommath/bn_mp_xor.c @@ -0,0 +1,51 @@ +#include "tommath.h" +#ifdef BN_MP_XOR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* XOR two ints together */ +int +mp_xor (mp_int * a, mp_int * b, mp_int * c) +{ +  int     res, ix, px; +  mp_int  t, *x; + +  if (a->used > b->used) { +    if ((res = mp_init_copy (&t, a)) != MP_OKAY) { +      return res; +    } +    px = b->used; +    x = b; +  } else { +    if ((res = mp_init_copy (&t, b)) != MP_OKAY) { +      return res; +    } +    px = a->used; +    x = a; +  } + +  for (ix = 0; ix < px; ix++) { +     t.dp[ix] ^= x->dp[ix]; +  } +  mp_clamp (&t); +  mp_exch (c, &t); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_xor.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_mp_zero.c b/src/libtommath/bn_mp_zero.c new file mode 100644 index 0000000..f21db5e --- /dev/null +++ b/src/libtommath/bn_mp_zero.c @@ -0,0 +1,36 @@ +#include "tommath.h" +#ifdef BN_MP_ZERO_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* set to zero */ +void mp_zero (mp_int * a) +{ +  int       n; +  mp_digit *tmp; + +  a->sign = MP_ZPOS; +  a->used = 0; + +  tmp = a->dp; +  for (n = 0; n < a->alloc; n++) { +     *tmp++ = 0; +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_mp_zero.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_prime_tab.c b/src/libtommath/bn_prime_tab.c new file mode 100644 index 0000000..7d306dd --- /dev/null +++ b/src/libtommath/bn_prime_tab.c @@ -0,0 +1,61 @@ +#include "tommath.h" +#ifdef BN_PRIME_TAB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +const mp_digit ltm_prime_tab[] = { +  0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, +  0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, +  0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, +  0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, +#ifndef MP_8BIT +  0x0083, +  0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, +  0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, +  0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, +  0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + +  0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, +  0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, +  0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, +  0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, +  0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, +  0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, +  0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, +  0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + +  0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, +  0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, +  0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, +  0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, +  0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, +  0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, +  0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, +  0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + +  0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, +  0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, +  0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, +  0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, +  0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, +  0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, +  0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, +  0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 +#endif +}; +#endif + +/* $Source: /cvs/libtom/libtommath/bn_prime_tab.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_reverse.c b/src/libtommath/bn_reverse.c new file mode 100644 index 0000000..d4a919a --- /dev/null +++ b/src/libtommath/bn_reverse.c @@ -0,0 +1,39 @@ +#include "tommath.h" +#ifdef BN_REVERSE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* reverse an array, used for radix code */ +void +bn_reverse (unsigned char *s, int len) +{ +  int     ix, iy; +  unsigned char t; + +  ix = 0; +  iy = len - 1; +  while (ix < iy) { +    t     = s[ix]; +    s[ix] = s[iy]; +    s[iy] = t; +    ++ix; +    --iy; +  } +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_reverse.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_add.c b/src/libtommath/bn_s_mp_add.c new file mode 100644 index 0000000..5ea9c6d --- /dev/null +++ b/src/libtommath/bn_s_mp_add.c @@ -0,0 +1,109 @@ +#include "tommath.h" +#ifdef BN_S_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ +  mp_int *x; +  int     olduse, res, min, max; + +  /* find sizes, we let |a| <= |b| which means we have to sort +   * them.  "x" will point to the input with the most digits +   */ +  if (a->used > b->used) { +    min = b->used; +    max = a->used; +    x = a; +  } else { +    min = a->used; +    max = b->used; +    x = b; +  } + +  /* init result */ +  if (c->alloc < max + 1) { +    if ((res = mp_grow (c, max + 1)) != MP_OKAY) { +      return res; +    } +  } + +  /* get old used digit count and set new one */ +  olduse = c->used; +  c->used = max + 1; + +  { +    register mp_digit u, *tmpa, *tmpb, *tmpc; +    register int i; + +    /* alias for digit pointers */ + +    /* first input */ +    tmpa = a->dp; + +    /* second input */ +    tmpb = b->dp; + +    /* destination */ +    tmpc = c->dp; + +    /* zero the carry */ +    u = 0; +    for (i = 0; i < min; i++) { +      /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ +      *tmpc = *tmpa++ + *tmpb++ + u; + +      /* U = carry bit of T[i] */ +      u = *tmpc >> ((mp_digit)DIGIT_BIT); + +      /* take away carry bit from T[i] */ +      *tmpc++ &= MP_MASK; +    } + +    /* now copy higher words if any, that is in A+B  +     * if A or B has more digits add those in  +     */ +    if (min != max) { +      for (; i < max; i++) { +        /* T[i] = X[i] + U */ +        *tmpc = x->dp[i] + u; + +        /* U = carry bit of T[i] */ +        u = *tmpc >> ((mp_digit)DIGIT_BIT); + +        /* take away carry bit from T[i] */ +        *tmpc++ &= MP_MASK; +      } +    } + +    /* add carry */ +    *tmpc++ = u; + +    /* clear digits above oldused */ +    for (i = c->used; i < olduse; i++) { +      *tmpc++ = 0; +    } +  } + +  mp_clamp (c); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_add.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_exptmod.c b/src/libtommath/bn_s_mp_exptmod.c new file mode 100644 index 0000000..9fb2da8 --- /dev/null +++ b/src/libtommath/bn_s_mp_exptmod.c @@ -0,0 +1,252 @@ +#include "tommath.h" +#ifdef BN_S_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#ifdef MP_LOW_MEM +   #define TAB_SIZE 32 +#else +   #define TAB_SIZE 256 +#endif + +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ +  mp_int  M[TAB_SIZE], res, mu; +  mp_digit buf; +  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; +  int (*redux)(mp_int*,mp_int*,mp_int*); + +  /* find window size */ +  x = mp_count_bits (X); +  if (x <= 7) { +    winsize = 2; +  } else if (x <= 36) { +    winsize = 3; +  } else if (x <= 140) { +    winsize = 4; +  } else if (x <= 450) { +    winsize = 5; +  } else if (x <= 1303) { +    winsize = 6; +  } else if (x <= 3529) { +    winsize = 7; +  } else { +    winsize = 8; +  } + +#ifdef MP_LOW_MEM +    if (winsize > 5) { +       winsize = 5; +    } +#endif + +  /* init M array */ +  /* init first cell */ +  if ((err = mp_init(&M[1])) != MP_OKAY) { +     return err;  +  } + +  /* now init the second half of the array */ +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) { +    if ((err = mp_init(&M[x])) != MP_OKAY) { +      for (y = 1<<(winsize-1); y < x; y++) { +        mp_clear (&M[y]); +      } +      mp_clear(&M[1]); +      return err; +    } +  } + +  /* create mu, used for Barrett reduction */ +  if ((err = mp_init (&mu)) != MP_OKAY) { +    goto LBL_M; +  } +   +  if (redmode == 0) { +     if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { +        goto LBL_MU; +     } +     redux = mp_reduce; +  } else { +     if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { +        goto LBL_MU; +     } +     redux = mp_reduce_2k_l; +  }     + +  /* create M table +   * +   * The M table contains powers of the base,  +   * e.g. M[x] = G**x mod P +   * +   * The first half of the table is not  +   * computed though accept for M[0] and M[1] +   */ +  if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { +    goto LBL_MU; +  } + +  /* compute the value at M[1<<(winsize-1)] by squaring  +   * M[1] (winsize-1) times  +   */ +  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { +    goto LBL_MU; +  } + +  for (x = 0; x < (winsize - 1); x++) { +    /* square it */ +    if ((err = mp_sqr (&M[1 << (winsize - 1)],  +                       &M[1 << (winsize - 1)])) != MP_OKAY) { +      goto LBL_MU; +    } + +    /* reduce modulo P */ +    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { +      goto LBL_MU; +    } +  } + +  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) +   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) +   */ +  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { +    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { +      goto LBL_MU; +    } +    if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { +      goto LBL_MU; +    } +  } + +  /* setup result */ +  if ((err = mp_init (&res)) != MP_OKAY) { +    goto LBL_MU; +  } +  mp_set (&res, 1); + +  /* set initial mode and bit cnt */ +  mode   = 0; +  bitcnt = 1; +  buf    = 0; +  digidx = X->used - 1; +  bitcpy = 0; +  bitbuf = 0; + +  for (;;) { +    /* grab next digit as required */ +    if (--bitcnt == 0) { +      /* if digidx == -1 we are out of digits */ +      if (digidx == -1) { +        break; +      } +      /* read next digit and reset the bitcnt */ +      buf    = X->dp[digidx--]; +      bitcnt = (int) DIGIT_BIT; +    } + +    /* grab the next msb from the exponent */ +    y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; +    buf <<= (mp_digit)1; + +    /* if the bit is zero and mode == 0 then we ignore it +     * These represent the leading zero bits before the first 1 bit +     * in the exponent.  Technically this opt is not required but it +     * does lower the # of trivial squaring/reductions used +     */ +    if (mode == 0 && y == 0) { +      continue; +    } + +    /* if the bit is zero and mode == 1 then we square */ +    if (mode == 1 && y == 0) { +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, &mu)) != MP_OKAY) { +        goto LBL_RES; +      } +      continue; +    } + +    /* else we add it to the window */ +    bitbuf |= (y << (winsize - ++bitcpy)); +    mode    = 2; + +    if (bitcpy == winsize) { +      /* ok window is filled so square as required and multiply  */ +      /* square first */ +      for (x = 0; x < winsize; x++) { +        if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +          goto LBL_RES; +        } +        if ((err = redux (&res, P, &mu)) != MP_OKAY) { +          goto LBL_RES; +        } +      } + +      /* then multiply */ +      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, &mu)) != MP_OKAY) { +        goto LBL_RES; +      } + +      /* empty window and reset */ +      bitcpy = 0; +      bitbuf = 0; +      mode   = 1; +    } +  } + +  /* if bits remain then square/multiply */ +  if (mode == 2 && bitcpy > 0) { +    /* square then multiply if the bit is set */ +    for (x = 0; x < bitcpy; x++) { +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) { +        goto LBL_RES; +      } +      if ((err = redux (&res, P, &mu)) != MP_OKAY) { +        goto LBL_RES; +      } + +      bitbuf <<= 1; +      if ((bitbuf & (1 << winsize)) != 0) { +        /* then multiply */ +        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { +          goto LBL_RES; +        } +        if ((err = redux (&res, P, &mu)) != MP_OKAY) { +          goto LBL_RES; +        } +      } +    } +  } + +  mp_exch (&res, Y); +  err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: +  mp_clear(&M[1]); +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) { +    mp_clear (&M[x]); +  } +  return err; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_exptmod.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_mul_digs.c b/src/libtommath/bn_s_mp_mul_digs.c new file mode 100644 index 0000000..f04dacf --- /dev/null +++ b/src/libtommath/bn_s_mp_mul_digs.c @@ -0,0 +1,90 @@ +#include "tommath.h" +#ifdef BN_S_MP_MUL_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12  Modified so you can control how  + * many digits of output are created. + */ +int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ +  mp_int  t; +  int     res, pa, pb, ix, iy; +  mp_digit u; +  mp_word r; +  mp_digit tmpx, *tmpt, *tmpy; + +  /* can we use the fast multiplier? */ +  if (((digs) < MP_WARRAY) && +      MIN (a->used, b->used) <  +          (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { +    return fast_s_mp_mul_digs (a, b, c, digs); +  } + +  if ((res = mp_init_size (&t, digs)) != MP_OKAY) { +    return res; +  } +  t.used = digs; + +  /* compute the digits of the product directly */ +  pa = a->used; +  for (ix = 0; ix < pa; ix++) { +    /* set the carry to zero */ +    u = 0; + +    /* limit ourselves to making digs digits of output */ +    pb = MIN (b->used, digs - ix); + +    /* setup some aliases */ +    /* copy of the digit from a used within the nested loop */ +    tmpx = a->dp[ix]; +     +    /* an alias for the destination shifted ix places */ +    tmpt = t.dp + ix; +     +    /* an alias for the digits of b */ +    tmpy = b->dp; + +    /* compute the columns of the output and propagate the carry */ +    for (iy = 0; iy < pb; iy++) { +      /* compute the column as a mp_word */ +      r       = ((mp_word)*tmpt) + +                ((mp_word)tmpx) * ((mp_word)*tmpy++) + +                ((mp_word) u); + +      /* the new column is the lower part of the result */ +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + +      /* get the carry word from the result */ +      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); +    } +    /* set carry if it is placed below digs */ +    if (ix + iy < digs) { +      *tmpt = u; +    } +  } + +  mp_clamp (&t); +  mp_exch (&t, c); + +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_digs.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_mul_high_digs.c b/src/libtommath/bn_s_mp_mul_high_digs.c new file mode 100644 index 0000000..b1d0199 --- /dev/null +++ b/src/libtommath/bn_s_mp_mul_high_digs.c @@ -0,0 +1,81 @@ +#include "tommath.h" +#ifdef BN_S_MP_MUL_HIGH_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ +  mp_int  t; +  int     res, pa, pb, ix, iy; +  mp_digit u; +  mp_word r; +  mp_digit tmpx, *tmpt, *tmpy; + +  /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C +  if (((a->used + b->used + 1) < MP_WARRAY) +      && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { +    return fast_s_mp_mul_high_digs (a, b, c, digs); +  } +#endif + +  if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { +    return res; +  } +  t.used = a->used + b->used + 1; + +  pa = a->used; +  pb = b->used; +  for (ix = 0; ix < pa; ix++) { +    /* clear the carry */ +    u = 0; + +    /* left hand side of A[ix] * B[iy] */ +    tmpx = a->dp[ix]; + +    /* alias to the address of where the digits will be stored */ +    tmpt = &(t.dp[digs]); + +    /* alias for where to read the right hand side from */ +    tmpy = b->dp + (digs - ix); + +    for (iy = digs - ix; iy < pb; iy++) { +      /* calculate the double precision result */ +      r       = ((mp_word)*tmpt) + +                ((mp_word)tmpx) * ((mp_word)*tmpy++) + +                ((mp_word) u); + +      /* get the lower part */ +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + +      /* carry the carry */ +      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); +    } +    *tmpt = u; +  } +  mp_clamp (&t); +  mp_exch (&t, c); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_mul_high_digs.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_sqr.c b/src/libtommath/bn_s_mp_sqr.c new file mode 100644 index 0000000..c1e994e --- /dev/null +++ b/src/libtommath/bn_s_mp_sqr.c @@ -0,0 +1,84 @@ +#include "tommath.h" +#ifdef BN_S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int s_mp_sqr (mp_int * a, mp_int * b) +{ +  mp_int  t; +  int     res, ix, iy, pa; +  mp_word r; +  mp_digit u, tmpx, *tmpt; + +  pa = a->used; +  if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { +    return res; +  } + +  /* default used is maximum possible size */ +  t.used = 2*pa + 1; + +  for (ix = 0; ix < pa; ix++) { +    /* first calculate the digit at 2*ix */ +    /* calculate double precision result */ +    r = ((mp_word) t.dp[2*ix]) + +        ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + +    /* store lower part in result */ +    t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + +    /* get the carry */ +    u           = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + +    /* left hand side of A[ix] * A[iy] */ +    tmpx        = a->dp[ix]; + +    /* alias for where to store the results */ +    tmpt        = t.dp + (2*ix + 1); +     +    for (iy = ix + 1; iy < pa; iy++) { +      /* first calculate the product */ +      r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + +      /* now calculate the double precision result, note we use +       * addition instead of *2 since it's easier to optimize +       */ +      r       = ((mp_word) *tmpt) + r + r + ((mp_word) u); + +      /* store lower part */ +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + +      /* get carry */ +      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); +    } +    /* propagate upwards */ +    while (u != ((mp_digit) 0)) { +      r       = ((mp_word) *tmpt) + ((mp_word) u); +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); +      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); +    } +  } + +  mp_clamp (&t); +  mp_exch (&t, b); +  mp_clear (&t); +  return MP_OKAY; +} +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sqr.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bn_s_mp_sub.c b/src/libtommath/bn_s_mp_sub.c new file mode 100644 index 0000000..0ae91cc --- /dev/null +++ b/src/libtommath/bn_s_mp_sub.c @@ -0,0 +1,89 @@ +#include "tommath.h" +#ifdef BN_S_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ +  int     olduse, res, min, max; + +  /* find sizes */ +  min = b->used; +  max = a->used; + +  /* init result */ +  if (c->alloc < max) { +    if ((res = mp_grow (c, max)) != MP_OKAY) { +      return res; +    } +  } +  olduse = c->used; +  c->used = max; + +  { +    register mp_digit u, *tmpa, *tmpb, *tmpc; +    register int i; + +    /* alias for digit pointers */ +    tmpa = a->dp; +    tmpb = b->dp; +    tmpc = c->dp; + +    /* set carry to zero */ +    u = 0; +    for (i = 0; i < min; i++) { +      /* T[i] = A[i] - B[i] - U */ +      *tmpc = *tmpa++ - *tmpb++ - u; + +      /* U = carry bit of T[i] +       * Note this saves performing an AND operation since +       * if a carry does occur it will propagate all the way to the +       * MSB.  As a result a single shift is enough to get the carry +       */ +      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + +      /* Clear carry from T[i] */ +      *tmpc++ &= MP_MASK; +    } + +    /* now copy higher words if any, e.g. if A has more digits than B  */ +    for (; i < max; i++) { +      /* T[i] = A[i] - U */ +      *tmpc = *tmpa++ - u; + +      /* U = carry bit of T[i] */ +      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + +      /* Clear carry from T[i] */ +      *tmpc++ &= MP_MASK; +    } + +    /* clear digits above used (since we may not have grown result above) */ +    for (i = c->used; i < olduse; i++) { +      *tmpc++ = 0; +    } +  } + +  mp_clamp (c); +  return MP_OKAY; +} + +#endif + +/* $Source: /cvs/libtom/libtommath/bn_s_mp_sub.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/bncore.c b/src/libtommath/bncore.c new file mode 100644 index 0000000..ad7347f --- /dev/null +++ b/src/libtommath/bncore.c @@ -0,0 +1,36 @@ +#include "tommath.h" +#ifdef BNCORE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ + +/* Known optimal configurations + + CPU                    /Compiler     /MUL CUTOFF/SQR CUTOFF +------------------------------------------------------------- + Intel P4 Northwood     /GCC v3.4.1   /        88/       128/LTM 0.32 ;-) + AMD Athlon64           /GCC v3.4.4   /        80/       120/LTM 0.35 +  +*/ + +int     KARATSUBA_MUL_CUTOFF = 80,      /* Min. number of digits before Karatsuba multiplication is used. */ +        KARATSUBA_SQR_CUTOFF = 120,     /* Min. number of digits before Karatsuba squaring is used. */ +         +        TOOM_MUL_CUTOFF      = 350,      /* no optimal values of these are known yet so set em high */ +        TOOM_SQR_CUTOFF      = 400;  +#endif + +/* $Source: /cvs/libtom/libtommath/bncore.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:25:13 $ */ diff --git a/src/libtommath/tommath.h b/src/libtommath/tommath.h new file mode 100644 index 0000000..1ead3d0 --- /dev/null +++ b/src/libtommath/tommath.h @@ -0,0 +1,584 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://math.libtomcrypt.com + */ +#ifndef BN_H_ +#define BN_H_ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> + +#include "tommath_class.h" + +#ifndef MIN +   #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX +   #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#ifdef __cplusplus +extern "C" { + +/* C++ compilers don't like assigning void * to mp_digit * */ +#define  OPT_CAST(x)  (x *) + +#else + +/* C on the other hand doesn't care */ +#define  OPT_CAST(x) + +#endif + + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__)  +   #if !(defined(MP_64BIT) && defined(MP_16BIT) && defined(MP_8BIT)) +      #define MP_64BIT +   #endif +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT +   typedef unsigned char      mp_digit; +   typedef unsigned short     mp_word; +#elif defined(MP_16BIT) +   typedef unsigned short     mp_digit; +   typedef unsigned long      mp_word; +#elif defined(MP_64BIT) +   /* for GCC only on supported platforms */ +#ifndef CRYPT +   typedef unsigned long long ulong64; +   typedef signed long long   long64; +#endif + +   typedef unsigned long      mp_digit; +   typedef unsigned long      mp_word __attribute__ ((mode(TI))); + +   #define DIGIT_BIT          60 +#else +   /* this is the default case, 28-bit digits */ +    +   /* this is to make porting into LibTomCrypt easier :-) */ +#ifndef CRYPT +   #if defined(_MSC_VER) || defined(__BORLANDC__)  +      typedef unsigned __int64   ulong64; +      typedef signed __int64     long64; +   #else +      typedef unsigned long long ulong64; +      typedef signed long long   long64; +   #endif +#endif + +   typedef unsigned long      mp_digit; +   typedef ulong64            mp_word; + +#ifdef MP_31BIT    +   /* this is an extension that uses 31-bit digits */ +   #define DIGIT_BIT          31 +#else +   /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ +   #define DIGIT_BIT          28 +   #define MP_28BIT +#endif    +#endif + +/* define heap macros */ +#ifndef CRYPT +   /* default to libc stuff */ +   #ifndef XMALLOC  +       #define XMALLOC  malloc +       #define XFREE    free +       #define XREALLOC realloc +       #define XCALLOC  calloc +   #else +      /* prototypes for our heap functions */ +      extern void *XMALLOC(size_t n); +      extern void *XREALLOC(void *p, size_t n); +      extern void *XCALLOC(size_t n, size_t s); +      extern void XFREE(void *p); +   #endif +#endif + + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT +   #define DIGIT_BIT     ((int)((CHAR_BIT * sizeof(mp_digit) - 1)))  /* bits per digit */ +#endif + +#define MP_DIGIT_BIT     DIGIT_BIT +#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) +#define MP_DIGIT_MAX     MP_MASK + +/* equalities */ +#define MP_LT        -1   /* less than */ +#define MP_EQ         0   /* equal to */ +#define MP_GT         1   /* greater than */ + +#define MP_ZPOS       0   /* positive integer */ +#define MP_NEG        1   /* negative */ + +#define MP_OKAY       0   /* ok result */ +#define MP_MEM        -2  /* out of mem */ +#define MP_VAL        -3  /* invalid input */ +#define MP_RANGE      MP_VAL + +#define MP_YES        1   /* yes response */ +#define MP_NO         0   /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS      0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE     0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_ON  0x0008 /* force 2nd MSB to 1 */ + +typedef int           mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, +           KARATSUBA_SQR_CUTOFF, +           TOOM_MUL_CUTOFF, +           TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC +   #ifndef MP_LOW_MEM +      #define MP_PREC                 32     /* default digits of precision */ +   #else +      #define MP_PREC                 8      /* default digits of precision */ +   #endif    +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY               (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct  { +    int used, alloc, sign; +    mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat); + + +#define USED(m)    ((m)->used) +#define DIGIT(m,k) ((m)->dp[(k)]) +#define SIGN(m)    ((m)->sign) + +/* error code to char* string */ +char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a)  (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int * a); + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int (mp_int * a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**d */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b  */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a  */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b  */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a mod b, 0 <= c < b  */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime)  */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* returns true if a can be reduced with mp_reduce_2k_l */ +int mp_reduce_is_2k_l(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT +   #define PRIME_SIZE      31 +#else +   #define PRIME_SIZE      256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit ltm_prime_tab[]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96  + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases.  Also performs an initial sieve of trial + * division.  Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4  + * + * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can + * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + *  + *   LTM_PRIME_BBS      - make prime congruent to 3 mod 4 + *   LTM_PRIME_SAFE     - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + *   LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + *   LTM_PRIME_2MSB_ON  - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes.  "dat" is a parameter you can + * have passed to the callback (e.g. a state or something).  This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); +int mp_to_unsigned_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a,  unsigned char *b); +int mp_to_signed_bin_n (mp_int * a, unsigned char *b, unsigned long *outlen); + +int mp_read_radix(mp_int *a, const char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp)           mp_signed_bin_size(mp) +#define mp_toraw(mp, str)         mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp)           mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str)         mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S)  mp_toradix((M), (S), 2) +#define mp_tooctal(M, S)   mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S)     mp_toradix((M), (S), 16) + +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c); +int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int mode); +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int mode); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +#ifdef __cplusplus +   } +#endif + +#endif + + +/* $Source: /cvs/libtom/libtommath/tommath.h,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/03/31 14:18:44 $ */ diff --git a/src/libtommath/tommath_class.h b/src/libtommath/tommath_class.h new file mode 100644 index 0000000..18d1553 --- /dev/null +++ b/src/libtommath/tommath_class.h @@ -0,0 +1,999 @@ +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) +#if defined(LTM2) +#define LTM3 +#endif +#if defined(LTM1) +#define LTM2 +#endif +#define LTM1 + +#if defined(LTM_ALL) +#define BN_ERROR_C +#define BN_FAST_MP_INVMOD_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_FAST_S_MP_MUL_DIGS_C +#define BN_FAST_S_MP_MUL_HIGH_DIGS_C +#define BN_FAST_S_MP_SQR_C +#define BN_MP_2EXPT_C +#define BN_MP_ABS_C +#define BN_MP_ADD_C +#define BN_MP_ADD_D_C +#define BN_MP_ADDMOD_C +#define BN_MP_AND_C +#define BN_MP_CLAMP_C +#define BN_MP_CLEAR_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_CMP_C +#define BN_MP_CMP_D_C +#define BN_MP_CMP_MAG_C +#define BN_MP_CNT_LSB_C +#define BN_MP_COPY_C +#define BN_MP_COUNT_BITS_C +#define BN_MP_DIV_C +#define BN_MP_DIV_2_C +#define BN_MP_DIV_2D_C +#define BN_MP_DIV_3_C +#define BN_MP_DIV_D_C +#define BN_MP_DR_IS_MODULUS_C +#define BN_MP_DR_REDUCE_C +#define BN_MP_DR_SETUP_C +#define BN_MP_EXCH_C +#define BN_MP_EXPT_D_C +#define BN_MP_EXPTMOD_C +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_EXTEUCLID_C +#define BN_MP_FREAD_C +#define BN_MP_FWRITE_C +#define BN_MP_GCD_C +#define BN_MP_GET_INT_C +#define BN_MP_GROW_C +#define BN_MP_INIT_C +#define BN_MP_INIT_COPY_C +#define BN_MP_INIT_MULTI_C +#define BN_MP_INIT_SET_C +#define BN_MP_INIT_SET_INT_C +#define BN_MP_INIT_SIZE_C +#define BN_MP_INVMOD_C +#define BN_MP_INVMOD_SLOW_C +#define BN_MP_IS_SQUARE_C +#define BN_MP_JACOBI_C +#define BN_MP_KARATSUBA_MUL_C +#define BN_MP_KARATSUBA_SQR_C +#define BN_MP_LCM_C +#define BN_MP_LSHD_C +#define BN_MP_MOD_C +#define BN_MP_MOD_2D_C +#define BN_MP_MOD_D_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_MP_MUL_C +#define BN_MP_MUL_2_C +#define BN_MP_MUL_2D_C +#define BN_MP_MUL_D_C +#define BN_MP_MULMOD_C +#define BN_MP_N_ROOT_C +#define BN_MP_NEG_C +#define BN_MP_OR_C +#define BN_MP_PRIME_FERMAT_C +#define BN_MP_PRIME_IS_DIVISIBLE_C +#define BN_MP_PRIME_IS_PRIME_C +#define BN_MP_PRIME_MILLER_RABIN_C +#define BN_MP_PRIME_NEXT_PRIME_C +#define BN_MP_PRIME_RABIN_MILLER_TRIALS_C +#define BN_MP_PRIME_RANDOM_EX_C +#define BN_MP_RADIX_SIZE_C +#define BN_MP_RADIX_SMAP_C +#define BN_MP_RAND_C +#define BN_MP_READ_RADIX_C +#define BN_MP_READ_SIGNED_BIN_C +#define BN_MP_READ_UNSIGNED_BIN_C +#define BN_MP_REDUCE_C +#define BN_MP_REDUCE_2K_C +#define BN_MP_REDUCE_2K_L_C +#define BN_MP_REDUCE_2K_SETUP_C +#define BN_MP_REDUCE_2K_SETUP_L_C +#define BN_MP_REDUCE_IS_2K_C +#define BN_MP_REDUCE_IS_2K_L_C +#define BN_MP_REDUCE_SETUP_C +#define BN_MP_RSHD_C +#define BN_MP_SET_C +#define BN_MP_SET_INT_C +#define BN_MP_SHRINK_C +#define BN_MP_SIGNED_BIN_SIZE_C +#define BN_MP_SQR_C +#define BN_MP_SQRMOD_C +#define BN_MP_SQRT_C +#define BN_MP_SUB_C +#define BN_MP_SUB_D_C +#define BN_MP_SUBMOD_C +#define BN_MP_TO_SIGNED_BIN_C +#define BN_MP_TO_SIGNED_BIN_N_C +#define BN_MP_TO_UNSIGNED_BIN_C +#define BN_MP_TO_UNSIGNED_BIN_N_C +#define BN_MP_TOOM_MUL_C +#define BN_MP_TOOM_SQR_C +#define BN_MP_TORADIX_C +#define BN_MP_TORADIX_N_C +#define BN_MP_UNSIGNED_BIN_SIZE_C +#define BN_MP_XOR_C +#define BN_MP_ZERO_C +#define BN_PRIME_TAB_C +#define BN_REVERSE_C +#define BN_S_MP_ADD_C +#define BN_S_MP_EXPTMOD_C +#define BN_S_MP_MUL_DIGS_C +#define BN_S_MP_MUL_HIGH_DIGS_C +#define BN_S_MP_SQR_C +#define BN_S_MP_SUB_C +#define BNCORE_C +#endif + +#if defined(BN_ERROR_C) +   #define BN_MP_ERROR_TO_STRING_C +#endif + +#if defined(BN_FAST_MP_INVMOD_C) +   #define BN_MP_ISEVEN_C +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_COPY_C +   #define BN_MP_MOD_C +   #define BN_MP_SET_C +   #define BN_MP_DIV_2_C +   #define BN_MP_ISODD_C +   #define BN_MP_SUB_C +   #define BN_MP_CMP_C +   #define BN_MP_ISZERO_C +   #define BN_MP_CMP_D_C +   #define BN_MP_ADD_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) +   #define BN_MP_GROW_C +   #define BN_MP_RSHD_C +   #define BN_MP_CLAMP_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_FAST_S_MP_MUL_DIGS_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_FAST_S_MP_SQR_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_2EXPT_C) +   #define BN_MP_ZERO_C +   #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_ABS_C) +   #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_ADD_C) +   #define BN_S_MP_ADD_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_ADD_D_C) +   #define BN_MP_GROW_C +   #define BN_MP_SUB_D_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_ADDMOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_ADD_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_AND_C) +   #define BN_MP_INIT_COPY_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CLAMP_C) +#endif + +#if defined(BN_MP_CLEAR_C) +#endif + +#if defined(BN_MP_CLEAR_MULTI_C) +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_CMP_C) +   #define BN_MP_CMP_MAG_C +#endif + +#if defined(BN_MP_CMP_D_C) +#endif + +#if defined(BN_MP_CMP_MAG_C) +#endif + +#if defined(BN_MP_CNT_LSB_C) +   #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_COPY_C) +   #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_COUNT_BITS_C) +#endif + +#if defined(BN_MP_DIV_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_COPY_C +   #define BN_MP_ZERO_C +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_SET_C +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_ABS_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_CMP_C +   #define BN_MP_SUB_C +   #define BN_MP_ADD_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_MULTI_C +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_INIT_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_LSHD_C +   #define BN_MP_RSHD_C +   #define BN_MP_MUL_D_C +   #define BN_MP_CLAMP_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_2_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_DIV_2D_C) +   #define BN_MP_COPY_C +   #define BN_MP_ZERO_C +   #define BN_MP_INIT_C +   #define BN_MP_MOD_2D_C +   #define BN_MP_CLEAR_C +   #define BN_MP_RSHD_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_DIV_3_C) +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DIV_D_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_COPY_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_DIV_3_C +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_DR_IS_MODULUS_C) +#endif + +#if defined(BN_MP_DR_REDUCE_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_DR_SETUP_C) +#endif + +#if defined(BN_MP_EXCH_C) +#endif + +#if defined(BN_MP_EXPT_D_C) +   #define BN_MP_INIT_COPY_C +   #define BN_MP_SET_C +   #define BN_MP_SQR_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MUL_C +#endif + +#if defined(BN_MP_EXPTMOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_INVMOD_C +   #define BN_MP_CLEAR_C +   #define BN_MP_ABS_C +   #define BN_MP_CLEAR_MULTI_C +   #define BN_MP_REDUCE_IS_2K_L_C +   #define BN_S_MP_EXPTMOD_C +   #define BN_MP_DR_IS_MODULUS_C +   #define BN_MP_REDUCE_IS_2K_C +   #define BN_MP_ISODD_C +   #define BN_MP_EXPTMOD_FAST_C +#endif + +#if defined(BN_MP_EXPTMOD_FAST_C) +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_INIT_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MONTGOMERY_SETUP_C +   #define BN_FAST_MP_MONTGOMERY_REDUCE_C +   #define BN_MP_MONTGOMERY_REDUCE_C +   #define BN_MP_DR_SETUP_C +   #define BN_MP_DR_REDUCE_C +   #define BN_MP_REDUCE_2K_SETUP_C +   #define BN_MP_REDUCE_2K_C +   #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +   #define BN_MP_MULMOD_C +   #define BN_MP_SET_C +   #define BN_MP_MOD_C +   #define BN_MP_COPY_C +   #define BN_MP_SQR_C +   #define BN_MP_MUL_C +   #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_EXTEUCLID_C) +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_SET_C +   #define BN_MP_COPY_C +   #define BN_MP_ISZERO_C +   #define BN_MP_DIV_C +   #define BN_MP_MUL_C +   #define BN_MP_SUB_C +   #define BN_MP_NEG_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_FREAD_C) +   #define BN_MP_ZERO_C +   #define BN_MP_S_RMAP_C +   #define BN_MP_MUL_D_C +   #define BN_MP_ADD_D_C +   #define BN_MP_CMP_D_C +#endif + +#if defined(BN_MP_FWRITE_C) +   #define BN_MP_RADIX_SIZE_C +   #define BN_MP_TORADIX_C +#endif + +#if defined(BN_MP_GCD_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_ABS_C +   #define BN_MP_ZERO_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_CNT_LSB_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_EXCH_C +   #define BN_S_MP_SUB_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_GET_INT_C) +#endif + +#if defined(BN_MP_GROW_C) +#endif + +#if defined(BN_MP_INIT_C) +#endif + +#if defined(BN_MP_INIT_COPY_C) +   #define BN_MP_COPY_C +#endif + +#if defined(BN_MP_INIT_MULTI_C) +   #define BN_MP_ERR_C +   #define BN_MP_INIT_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_INIT_SET_C) +   #define BN_MP_INIT_C +   #define BN_MP_SET_C +#endif + +#if defined(BN_MP_INIT_SET_INT_C) +   #define BN_MP_INIT_C +   #define BN_MP_SET_INT_C +#endif + +#if defined(BN_MP_INIT_SIZE_C) +   #define BN_MP_INIT_C +#endif + +#if defined(BN_MP_INVMOD_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_ISODD_C +   #define BN_FAST_MP_INVMOD_C +   #define BN_MP_INVMOD_SLOW_C +#endif + +#if defined(BN_MP_INVMOD_SLOW_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_MOD_C +   #define BN_MP_COPY_C +   #define BN_MP_ISEVEN_C +   #define BN_MP_SET_C +   #define BN_MP_DIV_2_C +   #define BN_MP_ISODD_C +   #define BN_MP_ADD_C +   #define BN_MP_SUB_C +   #define BN_MP_CMP_C +   #define BN_MP_CMP_D_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_IS_SQUARE_C) +   #define BN_MP_MOD_D_C +   #define BN_MP_INIT_SET_INT_C +   #define BN_MP_MOD_C +   #define BN_MP_GET_INT_C +   #define BN_MP_SQRT_C +   #define BN_MP_SQR_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_JACOBI_C) +   #define BN_MP_CMP_D_C +   #define BN_MP_ISZERO_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_CNT_LSB_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_MOD_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_MUL_C) +   #define BN_MP_MUL_C +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_SUB_C +   #define BN_MP_ADD_C +   #define BN_MP_LSHD_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_KARATSUBA_SQR_C) +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_SQR_C +   #define BN_MP_SUB_C +   #define BN_S_MP_ADD_C +   #define BN_MP_LSHD_C +   #define BN_MP_ADD_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_LCM_C) +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_GCD_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_DIV_C +   #define BN_MP_MUL_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_LSHD_C) +   #define BN_MP_GROW_C +   #define BN_MP_RSHD_C +#endif + +#if defined(BN_MP_MOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_DIV_C +   #define BN_MP_CLEAR_C +   #define BN_MP_ADD_C +   #define BN_MP_EXCH_C +#endif + +#if defined(BN_MP_MOD_2D_C) +   #define BN_MP_ZERO_C +   #define BN_MP_COPY_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MOD_D_C) +   #define BN_MP_DIV_D_C +#endif + +#if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_2EXPT_C +   #define BN_MP_SET_C +   #define BN_MP_MUL_2_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_REDUCE_C) +   #define BN_FAST_MP_MONTGOMERY_REDUCE_C +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +   #define BN_MP_RSHD_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_MONTGOMERY_SETUP_C) +#endif + +#if defined(BN_MP_MUL_C) +   #define BN_MP_TOOM_MUL_C +   #define BN_MP_KARATSUBA_MUL_C +   #define BN_FAST_S_MP_MUL_DIGS_C +   #define BN_S_MP_MUL_C +   #define BN_S_MP_MUL_DIGS_C +#endif + +#if defined(BN_MP_MUL_2_C) +   #define BN_MP_GROW_C +#endif + +#if defined(BN_MP_MUL_2D_C) +   #define BN_MP_COPY_C +   #define BN_MP_GROW_C +   #define BN_MP_LSHD_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MUL_D_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_MULMOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_MUL_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_N_ROOT_C) +   #define BN_MP_INIT_C +   #define BN_MP_SET_C +   #define BN_MP_COPY_C +   #define BN_MP_EXPT_D_C +   #define BN_MP_MUL_C +   #define BN_MP_SUB_C +   #define BN_MP_MUL_D_C +   #define BN_MP_DIV_C +   #define BN_MP_CMP_C +   #define BN_MP_SUB_D_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_NEG_C) +   #define BN_MP_COPY_C +   #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_OR_C) +   #define BN_MP_INIT_COPY_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_FERMAT_C) +   #define BN_MP_CMP_D_C +   #define BN_MP_INIT_C +   #define BN_MP_EXPTMOD_C +   #define BN_MP_CMP_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_IS_DIVISIBLE_C) +   #define BN_MP_MOD_D_C +#endif + +#if defined(BN_MP_PRIME_IS_PRIME_C) +   #define BN_MP_CMP_D_C +   #define BN_MP_PRIME_IS_DIVISIBLE_C +   #define BN_MP_INIT_C +   #define BN_MP_SET_C +   #define BN_MP_PRIME_MILLER_RABIN_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_MILLER_RABIN_C) +   #define BN_MP_CMP_D_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_SUB_D_C +   #define BN_MP_CNT_LSB_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_EXPTMOD_C +   #define BN_MP_CMP_C +   #define BN_MP_SQRMOD_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_NEXT_PRIME_C) +   #define BN_MP_CMP_D_C +   #define BN_MP_SET_C +   #define BN_MP_SUB_D_C +   #define BN_MP_ISEVEN_C +   #define BN_MP_MOD_D_C +   #define BN_MP_INIT_C +   #define BN_MP_ADD_D_C +   #define BN_MP_PRIME_MILLER_RABIN_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) +#endif + +#if defined(BN_MP_PRIME_RANDOM_EX_C) +   #define BN_MP_READ_UNSIGNED_BIN_C +   #define BN_MP_PRIME_IS_PRIME_C +   #define BN_MP_SUB_D_C +   #define BN_MP_DIV_2_C +   #define BN_MP_MUL_2_C +   #define BN_MP_ADD_D_C +#endif + +#if defined(BN_MP_RADIX_SIZE_C) +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_ISZERO_C +   #define BN_MP_DIV_D_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_RADIX_SMAP_C) +   #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_RAND_C) +   #define BN_MP_ZERO_C +   #define BN_MP_ADD_D_C +   #define BN_MP_LSHD_C +#endif + +#if defined(BN_MP_READ_RADIX_C) +   #define BN_MP_ZERO_C +   #define BN_MP_S_RMAP_C +   #define BN_MP_RADIX_SMAP_C +   #define BN_MP_MUL_D_C +   #define BN_MP_ADD_D_C +   #define BN_MP_ISZERO_C +#endif + +#if defined(BN_MP_READ_SIGNED_BIN_C) +   #define BN_MP_READ_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_READ_UNSIGNED_BIN_C) +   #define BN_MP_GROW_C +   #define BN_MP_ZERO_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_REDUCE_C) +   #define BN_MP_REDUCE_SETUP_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_RSHD_C +   #define BN_MP_MUL_C +   #define BN_S_MP_MUL_HIGH_DIGS_C +   #define BN_FAST_S_MP_MUL_HIGH_DIGS_C +   #define BN_MP_MOD_2D_C +   #define BN_S_MP_MUL_DIGS_C +   #define BN_MP_SUB_C +   #define BN_MP_CMP_D_C +   #define BN_MP_SET_C +   #define BN_MP_LSHD_C +   #define BN_MP_ADD_C +   #define BN_MP_CMP_C +   #define BN_S_MP_SUB_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_C) +   #define BN_MP_INIT_C +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_MUL_D_C +   #define BN_S_MP_ADD_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_L_C) +   #define BN_MP_INIT_C +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_MUL_C +   #define BN_S_MP_ADD_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_C) +   #define BN_MP_INIT_C +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_2EXPT_C +   #define BN_MP_CLEAR_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_REDUCE_2K_SETUP_L_C) +   #define BN_MP_INIT_C +   #define BN_MP_2EXPT_C +   #define BN_MP_COUNT_BITS_C +   #define BN_S_MP_SUB_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_C) +   #define BN_MP_REDUCE_2K_C +   #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_REDUCE_IS_2K_L_C) +#endif + +#if defined(BN_MP_REDUCE_SETUP_C) +   #define BN_MP_2EXPT_C +   #define BN_MP_DIV_C +#endif + +#if defined(BN_MP_RSHD_C) +   #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_C) +   #define BN_MP_ZERO_C +#endif + +#if defined(BN_MP_SET_INT_C) +   #define BN_MP_ZERO_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SHRINK_C) +#endif + +#if defined(BN_MP_SIGNED_BIN_SIZE_C) +   #define BN_MP_UNSIGNED_BIN_SIZE_C +#endif + +#if defined(BN_MP_SQR_C) +   #define BN_MP_TOOM_SQR_C +   #define BN_MP_KARATSUBA_SQR_C +   #define BN_FAST_S_MP_SQR_C +   #define BN_S_MP_SQR_C +#endif + +#if defined(BN_MP_SQRMOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_SQR_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_SQRT_C) +   #define BN_MP_N_ROOT_C +   #define BN_MP_ISZERO_C +   #define BN_MP_ZERO_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_RSHD_C +   #define BN_MP_DIV_C +   #define BN_MP_ADD_C +   #define BN_MP_DIV_2_C +   #define BN_MP_CMP_MAG_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_SUB_C) +   #define BN_S_MP_ADD_C +   #define BN_MP_CMP_MAG_C +   #define BN_S_MP_SUB_C +#endif + +#if defined(BN_MP_SUB_D_C) +   #define BN_MP_GROW_C +   #define BN_MP_ADD_D_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_MP_SUBMOD_C) +   #define BN_MP_INIT_C +   #define BN_MP_SUB_C +   #define BN_MP_CLEAR_C +   #define BN_MP_MOD_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_C) +   #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_SIGNED_BIN_N_C) +   #define BN_MP_SIGNED_BIN_SIZE_C +   #define BN_MP_TO_SIGNED_BIN_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_C) +   #define BN_MP_INIT_COPY_C +   #define BN_MP_ISZERO_C +   #define BN_MP_DIV_2D_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_TO_UNSIGNED_BIN_N_C) +   #define BN_MP_UNSIGNED_BIN_SIZE_C +   #define BN_MP_TO_UNSIGNED_BIN_C +#endif + +#if defined(BN_MP_TOOM_MUL_C) +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_MOD_2D_C +   #define BN_MP_COPY_C +   #define BN_MP_RSHD_C +   #define BN_MP_MUL_C +   #define BN_MP_MUL_2_C +   #define BN_MP_ADD_C +   #define BN_MP_SUB_C +   #define BN_MP_DIV_2_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_MUL_D_C +   #define BN_MP_DIV_3_C +   #define BN_MP_LSHD_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TOOM_SQR_C) +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_MOD_2D_C +   #define BN_MP_COPY_C +   #define BN_MP_RSHD_C +   #define BN_MP_SQR_C +   #define BN_MP_MUL_2_C +   #define BN_MP_ADD_C +   #define BN_MP_SUB_C +   #define BN_MP_DIV_2_C +   #define BN_MP_MUL_2D_C +   #define BN_MP_MUL_D_C +   #define BN_MP_DIV_3_C +   #define BN_MP_LSHD_C +   #define BN_MP_CLEAR_MULTI_C +#endif + +#if defined(BN_MP_TORADIX_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_DIV_D_C +   #define BN_MP_CLEAR_C +   #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_TORADIX_N_C) +   #define BN_MP_ISZERO_C +   #define BN_MP_INIT_COPY_C +   #define BN_MP_DIV_D_C +   #define BN_MP_CLEAR_C +   #define BN_MP_S_RMAP_C +#endif + +#if defined(BN_MP_UNSIGNED_BIN_SIZE_C) +   #define BN_MP_COUNT_BITS_C +#endif + +#if defined(BN_MP_XOR_C) +   #define BN_MP_INIT_COPY_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_MP_ZERO_C) +#endif + +#if defined(BN_PRIME_TAB_C) +#endif + +#if defined(BN_REVERSE_C) +#endif + +#if defined(BN_S_MP_ADD_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BN_S_MP_EXPTMOD_C) +   #define BN_MP_COUNT_BITS_C +   #define BN_MP_INIT_C +   #define BN_MP_CLEAR_C +   #define BN_MP_REDUCE_SETUP_C +   #define BN_MP_REDUCE_C +   #define BN_MP_REDUCE_2K_SETUP_L_C +   #define BN_MP_REDUCE_2K_L_C +   #define BN_MP_MOD_C +   #define BN_MP_COPY_C +   #define BN_MP_SQR_C +   #define BN_MP_MUL_C +   #define BN_MP_SET_C +   #define BN_MP_EXCH_C +#endif + +#if defined(BN_S_MP_MUL_DIGS_C) +   #define BN_FAST_S_MP_MUL_DIGS_C +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_MUL_HIGH_DIGS_C) +   #define BN_FAST_S_MP_MUL_HIGH_DIGS_C +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SQR_C) +   #define BN_MP_INIT_SIZE_C +   #define BN_MP_CLAMP_C +   #define BN_MP_EXCH_C +   #define BN_MP_CLEAR_C +#endif + +#if defined(BN_S_MP_SUB_C) +   #define BN_MP_GROW_C +   #define BN_MP_CLAMP_C +#endif + +#if defined(BNCORE_C) +#endif + +#ifdef LTM3 +#define LTM_LAST +#endif +#include "tommath_superclass.h" +#include "tommath_class.h" +#else +#define LTM_LAST +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_class.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/07/28 11:59:32 $ */ diff --git a/src/libtommath/tommath_superclass.h b/src/libtommath/tommath_superclass.h new file mode 100644 index 0000000..2fdebe6 --- /dev/null +++ b/src/libtommath/tommath_superclass.h @@ -0,0 +1,76 @@ +/* super class file for PK algos */ + +/* default ... include all MPI */ +#define LTM_ALL + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ + +/* For reference.... On an Athlon64 optimizing for speed... + +   LTM's mpi.o with all functions [striped] is 142KiB in size. + +*/ + +/* Works for RSA only, mpi.o is 68KiB */ +#ifdef SC_RSA_1 +   #define BN_MP_SHRINK_C +   #define BN_MP_LCM_C +   #define BN_MP_PRIME_RANDOM_EX_C +   #define BN_MP_INVMOD_C +   #define BN_MP_GCD_C +   #define BN_MP_MOD_C +   #define BN_MP_MULMOD_C +   #define BN_MP_ADDMOD_C +   #define BN_MP_EXPTMOD_C +   #define BN_MP_SET_INT_C +   #define BN_MP_INIT_MULTI_C +   #define BN_MP_CLEAR_MULTI_C +   #define BN_MP_UNSIGNED_BIN_SIZE_C +   #define BN_MP_TO_UNSIGNED_BIN_C +   #define BN_MP_MOD_D_C +   #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C +   #define BN_REVERSE_C +   #define BN_PRIME_TAB_C + +   /* other modifiers */ +   #define BN_MP_DIV_SMALL                    /* Slower division, not critical */ + +   /* here we are on the last pass so we turn things off.  The functions classes are still there +    * but we remove them specifically from the build.  This also invokes tweaks in functions +    * like removing support for even moduli, etc... +    */ +#ifdef LTM_LAST +   #undef  BN_MP_TOOM_MUL_C +   #undef  BN_MP_TOOM_SQR_C +   #undef  BN_MP_KARATSUBA_MUL_C +   #undef  BN_MP_KARATSUBA_SQR_C +   #undef  BN_MP_REDUCE_C +   #undef  BN_MP_REDUCE_SETUP_C +   #undef  BN_MP_DR_IS_MODULUS_C +   #undef  BN_MP_DR_SETUP_C +   #undef  BN_MP_DR_REDUCE_C +   #undef  BN_MP_REDUCE_IS_2K_C +   #undef  BN_MP_REDUCE_2K_SETUP_C +   #undef  BN_MP_REDUCE_2K_C +   #undef  BN_S_MP_EXPTMOD_C +   #undef  BN_MP_DIV_3_C +   #undef  BN_S_MP_MUL_HIGH_DIGS_C +   #undef  BN_FAST_S_MP_MUL_HIGH_DIGS_C +   #undef  BN_FAST_MP_INVMOD_C + +   /* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold +    * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines]  +    * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without +    * trouble.   +    */ +   #undef  BN_S_MP_MUL_DIGS_C +   #undef  BN_S_MP_SQR_C +   #undef  BN_MP_MONTGOMERY_REDUCE_C +#endif + +#endif + +/* $Source: /cvs/libtom/libtommath/tommath_superclass.h,v $ */ +/* $Revision: 1.3 $ */ +/* $Date: 2005/05/14 13:29:17 $ */ diff --git a/src/lzma/C/LzFind.c b/src/lzma/C/LzFind.c new file mode 100644 index 0000000..e3ecb05 --- /dev/null +++ b/src/lzma/C/LzFind.c @@ -0,0 +1,761 @@ +/* LzFind.c -- Match finder for LZ algorithms +2009-04-22 : Igor Pavlov : Public domain */ + +#include <string.h> + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((UInt32)3 << 30) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +{ +  if (!p->directInput) +  { +    alloc->Free(alloc, p->bufferBase); +    p->bufferBase = 0; +  } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +{ +  UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; +  if (p->directInput) +  { +    p->blockSize = blockSize; +    return 1; +  } +  if (p->bufferBase == 0 || p->blockSize != blockSize) +  { +    LzInWindow_Free(p, alloc); +    p->blockSize = blockSize; +    p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); +  } +  return (p->bufferBase != 0); +} + +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } +Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } + +UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } + +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) +{ +  p->posLimit -= subValue; +  p->pos -= subValue; +  p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ +  if (p->streamEndWasReached || p->result != SZ_OK) +    return; +  if (p->directInput) +  { +    UInt32 curSize = 0xFFFFFFFF - p->streamPos; +    if (curSize > p->directInputRem) +      curSize = (UInt32)p->directInputRem; +    p->directInputRem -= curSize; +    p->streamPos += curSize; +    if (p->directInputRem == 0) +      p->streamEndWasReached = 1; +    return; +  } +  for (;;) +  { +    Byte *dest = p->buffer + (p->streamPos - p->pos); +    size_t size = (p->bufferBase + p->blockSize - dest); +    if (size == 0) +      return; +    p->result = p->stream->Read(p->stream, dest, &size); +    if (p->result != SZ_OK) +      return; +    if (size == 0) +    { +      p->streamEndWasReached = 1; +      return; +    } +    p->streamPos += (UInt32)size; +    if (p->streamPos - p->pos > p->keepSizeAfter) +      return; +  } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ +  memmove(p->bufferBase, +    p->buffer - p->keepSizeBefore, +    (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); +  p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ +  if (p->directInput) +    return 0; +  /* if (p->streamEndWasReached) return 0; */ +  return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ +  if (p->streamEndWasReached) +    return; +  if (p->keepSizeAfter >= p->streamPos - p->pos) +    MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ +  if (MatchFinder_NeedMove(p)) +    MatchFinder_MoveBlock(p); +  MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ +  p->cutValue = 32; +  p->btMode = 1; +  p->numHashBytes = 4; +  p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ +  UInt32 i; +  p->bufferBase = 0; +  p->directInput = 0; +  p->hash = 0; +  MatchFinder_SetDefaultSettings(p); + +  for (i = 0; i < 256; i++) +  { +    UInt32 r = i; +    int j; +    for (j = 0; j < 8; j++) +      r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); +    p->crc[i] = r; +  } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->hash); +  p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +{ +  MatchFinder_FreeThisClassMemory(p, alloc); +  LzInWindow_Free(p, alloc); +} + +static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) +{ +  size_t sizeInBytes = (size_t)num * sizeof(CLzRef); +  if (sizeInBytes / sizeof(CLzRef) != num) +    return 0; +  return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, +    UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, +    ISzAlloc *alloc) +{ +  UInt32 sizeReserv; +  if (historySize > kMaxHistorySize) +  { +    MatchFinder_Free(p, alloc); +    return 0; +  } +  sizeReserv = historySize >> 1; +  if (historySize > ((UInt32)2 << 30)) +    sizeReserv = historySize >> 2; +  sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + +  p->keepSizeBefore = historySize + keepAddBufferBefore + 1; +  p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; +  /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ +  if (LzInWindow_Create(p, sizeReserv, alloc)) +  { +    UInt32 newCyclicBufferSize = historySize + 1; +    UInt32 hs; +    p->matchMaxLen = matchMaxLen; +    { +      p->fixedHashSize = 0; +      if (p->numHashBytes == 2) +        hs = (1 << 16) - 1; +      else +      { +        hs = historySize - 1; +        hs |= (hs >> 1); +        hs |= (hs >> 2); +        hs |= (hs >> 4); +        hs |= (hs >> 8); +        hs >>= 1; +        hs |= 0xFFFF; /* don't change it! It's required for Deflate */ +        if (hs > (1 << 24)) +        { +          if (p->numHashBytes == 3) +            hs = (1 << 24) - 1; +          else +            hs >>= 1; +        } +      } +      p->hashMask = hs; +      hs++; +      if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; +      if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; +      if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; +      hs += p->fixedHashSize; +    } + +    { +      UInt32 prevSize = p->hashSizeSum + p->numSons; +      UInt32 newSize; +      p->historySize = historySize; +      p->hashSizeSum = hs; +      p->cyclicBufferSize = newCyclicBufferSize; +      p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); +      newSize = p->hashSizeSum + p->numSons; +      if (p->hash != 0 && prevSize == newSize) +        return 1; +      MatchFinder_FreeThisClassMemory(p, alloc); +      p->hash = AllocRefs(newSize, alloc); +      if (p->hash != 0) +      { +        p->son = p->hash + p->hashSizeSum; +        return 1; +      } +    } +  } +  MatchFinder_Free(p, alloc); +  return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ +  UInt32 limit = kMaxValForNormalize - p->pos; +  UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; +  if (limit2 < limit) +    limit = limit2; +  limit2 = p->streamPos - p->pos; +  if (limit2 <= p->keepSizeAfter) +  { +    if (limit2 > 0) +      limit2 = 1; +  } +  else +    limit2 -= p->keepSizeAfter; +  if (limit2 < limit) +    limit = limit2; +  { +    UInt32 lenLimit = p->streamPos - p->pos; +    if (lenLimit > p->matchMaxLen) +      lenLimit = p->matchMaxLen; +    p->lenLimit = lenLimit; +  } +  p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ +  UInt32 i; +  for (i = 0; i < p->hashSizeSum; i++) +    p->hash[i] = kEmptyHashValue; +  p->cyclicBufferPos = 0; +  p->buffer = p->bufferBase; +  p->pos = p->streamPos = p->cyclicBufferSize; +  p->result = SZ_OK; +  p->streamEndWasReached = 0; +  MatchFinder_ReadBlock(p); +  MatchFinder_SetLimits(p); +} + +static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) +{ +  return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) +{ +  UInt32 i; +  for (i = 0; i < numItems; i++) +  { +    UInt32 value = items[i]; +    if (value <= subValue) +      value = kEmptyHashValue; +    else +      value -= subValue; +    items[i] = value; +  } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ +  UInt32 subValue = MatchFinder_GetSubValue(p); +  MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); +  MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ +  if (p->pos == kMaxValForNormalize) +    MatchFinder_Normalize(p); +  if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) +    MatchFinder_CheckAndMoveAndRead(p); +  if (p->cyclicBufferPos == p->cyclicBufferSize) +    p->cyclicBufferPos = 0; +  MatchFinder_SetLimits(p); +} + +static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, +    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, +    UInt32 *distances, UInt32 maxLen) +{ +  son[_cyclicBufferPos] = curMatch; +  for (;;) +  { +    UInt32 delta = pos - curMatch; +    if (cutValue-- == 0 || delta >= _cyclicBufferSize) +      return distances; +    { +      const Byte *pb = cur - delta; +      curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; +      if (pb[maxLen] == cur[maxLen] && *pb == *cur) +      { +        UInt32 len = 0; +        while (++len != lenLimit) +          if (pb[len] != cur[len]) +            break; +        if (maxLen < len) +        { +          *distances++ = maxLen = len; +          *distances++ = delta - 1; +          if (len == lenLimit) +            return distances; +        } +      } +    } +  } +} + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, +    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, +    UInt32 *distances, UInt32 maxLen) +{ +  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; +  CLzRef *ptr1 = son + (_cyclicBufferPos << 1); +  UInt32 len0 = 0, len1 = 0; +  for (;;) +  { +    UInt32 delta = pos - curMatch; +    if (cutValue-- == 0 || delta >= _cyclicBufferSize) +    { +      *ptr0 = *ptr1 = kEmptyHashValue; +      return distances; +    } +    { +      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); +      const Byte *pb = cur - delta; +      UInt32 len = (len0 < len1 ? len0 : len1); +      if (pb[len] == cur[len]) +      { +        if (++len != lenLimit && pb[len] == cur[len]) +          while (++len != lenLimit) +            if (pb[len] != cur[len]) +              break; +        if (maxLen < len) +        { +          *distances++ = maxLen = len; +          *distances++ = delta - 1; +          if (len == lenLimit) +          { +            *ptr1 = pair[0]; +            *ptr0 = pair[1]; +            return distances; +          } +        } +      } +      if (pb[len] < cur[len]) +      { +        *ptr1 = curMatch; +        ptr1 = pair + 1; +        curMatch = *ptr1; +        len1 = len; +      } +      else +      { +        *ptr0 = curMatch; +        ptr0 = pair; +        curMatch = *ptr0; +        len0 = len; +      } +    } +  } +} + +static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, +    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) +{ +  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; +  CLzRef *ptr1 = son + (_cyclicBufferPos << 1); +  UInt32 len0 = 0, len1 = 0; +  for (;;) +  { +    UInt32 delta = pos - curMatch; +    if (cutValue-- == 0 || delta >= _cyclicBufferSize) +    { +      *ptr0 = *ptr1 = kEmptyHashValue; +      return; +    } +    { +      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); +      const Byte *pb = cur - delta; +      UInt32 len = (len0 < len1 ? len0 : len1); +      if (pb[len] == cur[len]) +      { +        while (++len != lenLimit) +          if (pb[len] != cur[len]) +            break; +        { +          if (len == lenLimit) +          { +            *ptr1 = pair[0]; +            *ptr0 = pair[1]; +            return; +          } +        } +      } +      if (pb[len] < cur[len]) +      { +        *ptr1 = curMatch; +        ptr1 = pair + 1; +        curMatch = *ptr1; +        len1 = len; +      } +      else +      { +        *ptr0 = curMatch; +        ptr0 = pair; +        curMatch = *ptr0; +        len0 = len; +      } +    } +  } +} + +#define MOVE_POS \ +  ++p->cyclicBufferPos; \ +  p->buffer++; \ +  if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ +  UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ +  lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ +  cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen)        GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ +  offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ +  distances + offset, maxLen) - distances); MOVE_POS_RET; + +#define SKIP_FOOTER \ +  SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; + +static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 offset; +  GET_MATCHES_HEADER(2) +  HASH2_CALC; +  curMatch = p->hash[hashValue]; +  p->hash[hashValue] = p->pos; +  offset = 0; +  GET_MATCHES_FOOTER(offset, 1) +} + +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 offset; +  GET_MATCHES_HEADER(3) +  HASH_ZIP_CALC; +  curMatch = p->hash[hashValue]; +  p->hash[hashValue] = p->pos; +  offset = 0; +  GET_MATCHES_FOOTER(offset, 2) +} + +static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 hash2Value, delta2, maxLen, offset; +  GET_MATCHES_HEADER(3) + +  HASH3_CALC; + +  delta2 = p->pos - p->hash[hash2Value]; +  curMatch = p->hash[kFix3HashSize + hashValue]; +   +  p->hash[hash2Value] = +  p->hash[kFix3HashSize + hashValue] = p->pos; + + +  maxLen = 2; +  offset = 0; +  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) +  { +    for (; maxLen != lenLimit; maxLen++) +      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) +        break; +    distances[0] = maxLen; +    distances[1] = delta2 - 1; +    offset = 2; +    if (maxLen == lenLimit) +    { +      SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); +      MOVE_POS_RET; +    } +  } +  GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; +  GET_MATCHES_HEADER(4) + +  HASH4_CALC; + +  delta2 = p->pos - p->hash[                hash2Value]; +  delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; +  curMatch = p->hash[kFix4HashSize + hashValue]; +   +  p->hash[                hash2Value] = +  p->hash[kFix3HashSize + hash3Value] = +  p->hash[kFix4HashSize + hashValue] = p->pos; + +  maxLen = 1; +  offset = 0; +  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) +  { +    distances[0] = maxLen = 2; +    distances[1] = delta2 - 1; +    offset = 2; +  } +  if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) +  { +    maxLen = 3; +    distances[offset + 1] = delta3 - 1; +    offset += 2; +    delta2 = delta3; +  } +  if (offset != 0) +  { +    for (; maxLen != lenLimit; maxLen++) +      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) +        break; +    distances[offset - 2] = maxLen; +    if (maxLen == lenLimit) +    { +      SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); +      MOVE_POS_RET; +    } +  } +  if (maxLen < 3) +    maxLen = 3; +  GET_MATCHES_FOOTER(offset, maxLen) +} + +static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; +  GET_MATCHES_HEADER(4) + +  HASH4_CALC; + +  delta2 = p->pos - p->hash[                hash2Value]; +  delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; +  curMatch = p->hash[kFix4HashSize + hashValue]; + +  p->hash[                hash2Value] = +  p->hash[kFix3HashSize + hash3Value] = +  p->hash[kFix4HashSize + hashValue] = p->pos; + +  maxLen = 1; +  offset = 0; +  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) +  { +    distances[0] = maxLen = 2; +    distances[1] = delta2 - 1; +    offset = 2; +  } +  if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) +  { +    maxLen = 3; +    distances[offset + 1] = delta3 - 1; +    offset += 2; +    delta2 = delta3; +  } +  if (offset != 0) +  { +    for (; maxLen != lenLimit; maxLen++) +      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) +        break; +    distances[offset - 2] = maxLen; +    if (maxLen == lenLimit) +    { +      p->son[p->cyclicBufferPos] = curMatch; +      MOVE_POS_RET; +    } +  } +  if (maxLen < 3) +    maxLen = 3; +  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), +    distances + offset, maxLen) - (distances)); +  MOVE_POS_RET +} + +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) +{ +  UInt32 offset; +  GET_MATCHES_HEADER(3) +  HASH_ZIP_CALC; +  curMatch = p->hash[hashValue]; +  p->hash[hashValue] = p->pos; +  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), +    distances, 2) - (distances)); +  MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    SKIP_HEADER(2) +    HASH2_CALC; +    curMatch = p->hash[hashValue]; +    p->hash[hashValue] = p->pos; +    SKIP_FOOTER +  } +  while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    SKIP_HEADER(3) +    HASH_ZIP_CALC; +    curMatch = p->hash[hashValue]; +    p->hash[hashValue] = p->pos; +    SKIP_FOOTER +  } +  while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    UInt32 hash2Value; +    SKIP_HEADER(3) +    HASH3_CALC; +    curMatch = p->hash[kFix3HashSize + hashValue]; +    p->hash[hash2Value] = +    p->hash[kFix3HashSize + hashValue] = p->pos; +    SKIP_FOOTER +  } +  while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    UInt32 hash2Value, hash3Value; +    SKIP_HEADER(4) +    HASH4_CALC; +    curMatch = p->hash[kFix4HashSize + hashValue]; +    p->hash[                hash2Value] = +    p->hash[kFix3HashSize + hash3Value] = p->pos; +    p->hash[kFix4HashSize + hashValue] = p->pos; +    SKIP_FOOTER +  } +  while (--num != 0); +} + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    UInt32 hash2Value, hash3Value; +    SKIP_HEADER(4) +    HASH4_CALC; +    curMatch = p->hash[kFix4HashSize + hashValue]; +    p->hash[                hash2Value] = +    p->hash[kFix3HashSize + hash3Value] = +    p->hash[kFix4HashSize + hashValue] = p->pos; +    p->son[p->cyclicBufferPos] = curMatch; +    MOVE_POS +  } +  while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) +{ +  do +  { +    SKIP_HEADER(3) +    HASH_ZIP_CALC; +    curMatch = p->hash[hashValue]; +    p->hash[hashValue] = p->pos; +    p->son[p->cyclicBufferPos] = curMatch; +    MOVE_POS +  } +  while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ +  vTable->Init = (Mf_Init_Func)MatchFinder_Init; +  vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; +  vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; +  vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; +  if (!p->btMode) +  { +    vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; +    vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; +  } +  else if (p->numHashBytes == 2) +  { +    vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; +    vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; +  } +  else if (p->numHashBytes == 3) +  { +    vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; +    vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; +  } +  else +  { +    vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; +    vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; +  } +} diff --git a/src/lzma/C/LzFind.h b/src/lzma/C/LzFind.h new file mode 100644 index 0000000..010c4b9 --- /dev/null +++ b/src/lzma/C/LzFind.h @@ -0,0 +1,115 @@ +/* LzFind.h -- Match finder for LZ algorithms +2009-04-22 : Igor Pavlov : Public domain */ + +#ifndef __LZ_FIND_H +#define __LZ_FIND_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef UInt32 CLzRef; + +typedef struct _CMatchFinder +{ +  Byte *buffer; +  UInt32 pos; +  UInt32 posLimit; +  UInt32 streamPos; +  UInt32 lenLimit; + +  UInt32 cyclicBufferPos; +  UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ + +  UInt32 matchMaxLen; +  CLzRef *hash; +  CLzRef *son; +  UInt32 hashMask; +  UInt32 cutValue; + +  Byte *bufferBase; +  ISeqInStream *stream; +  int streamEndWasReached; + +  UInt32 blockSize; +  UInt32 keepSizeBefore; +  UInt32 keepSizeAfter; + +  UInt32 numHashBytes; +  int directInput; +  size_t directInputRem; +  int btMode; +  int bigHash; +  UInt32 historySize; +  UInt32 fixedHashSize; +  UInt32 hashSizeSum; +  UInt32 numSons; +  SRes result; +  UInt32 crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: +     historySize <= 3 GB +     keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, +    UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, +    ISzAlloc *alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); +void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); + +UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, +    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, +    UInt32 *distances, UInt32 maxLen); + +/* +Conditions: +  Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. +  Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); +typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); +typedef void (*Mf_Skip_Func)(void *object, UInt32); + +typedef struct _IMatchFinder +{ +  Mf_Init_Func Init; +  Mf_GetIndexByte_Func GetIndexByte; +  Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; +  Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; +  Mf_GetMatches_Func GetMatches; +  Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lzma/C/LzFindMt.c b/src/lzma/C/LzFindMt.c new file mode 100644 index 0000000..aa41ed9 --- /dev/null +++ b/src/lzma/C/LzFindMt.c @@ -0,0 +1,793 @@ +/* LzFindMt.c -- multithreaded Match finder for LZ algorithms +2009-09-20 : Igor Pavlov : Public domain */ + +#include "LzHash.h" + +#include "LzFindMt.h" + +void MtSync_Construct(CMtSync *p) +{ +  p->wasCreated = False; +  p->csWasInitialized = False; +  p->csWasEntered = False; +  Thread_Construct(&p->thread); +  Event_Construct(&p->canStart); +  Event_Construct(&p->wasStarted); +  Event_Construct(&p->wasStopped); +  Semaphore_Construct(&p->freeSemaphore); +  Semaphore_Construct(&p->filledSemaphore); +} + +void MtSync_GetNextBlock(CMtSync *p) +{ +  if (p->needStart) +  { +    p->numProcessedBlocks = 1; +    p->needStart = False; +    p->stopWriting = False; +    p->exit = False; +    Event_Reset(&p->wasStarted); +    Event_Reset(&p->wasStopped); + +    Event_Set(&p->canStart); +    Event_Wait(&p->wasStarted); +  } +  else +  { +    CriticalSection_Leave(&p->cs); +    p->csWasEntered = False; +    p->numProcessedBlocks++; +    Semaphore_Release1(&p->freeSemaphore); +  } +  Semaphore_Wait(&p->filledSemaphore); +  CriticalSection_Enter(&p->cs); +  p->csWasEntered = True; +} + +/* MtSync_StopWriting must be called if Writing was started */ + +void MtSync_StopWriting(CMtSync *p) +{ +  UInt32 myNumBlocks = p->numProcessedBlocks; +  if (!Thread_WasCreated(&p->thread) || p->needStart) +    return; +  p->stopWriting = True; +  if (p->csWasEntered) +  { +    CriticalSection_Leave(&p->cs); +    p->csWasEntered = False; +  } +  Semaphore_Release1(&p->freeSemaphore); +  +  Event_Wait(&p->wasStopped); + +  while (myNumBlocks++ != p->numProcessedBlocks) +  { +    Semaphore_Wait(&p->filledSemaphore); +    Semaphore_Release1(&p->freeSemaphore); +  } +  p->needStart = True; +} + +void MtSync_Destruct(CMtSync *p) +{ +  if (Thread_WasCreated(&p->thread)) +  { +    MtSync_StopWriting(p); +    p->exit = True; +    if (p->needStart) +      Event_Set(&p->canStart); +    Thread_Wait(&p->thread); +    Thread_Close(&p->thread); +  } +  if (p->csWasInitialized) +  { +    CriticalSection_Delete(&p->cs); +    p->csWasInitialized = False; +  } + +  Event_Close(&p->canStart); +  Event_Close(&p->wasStarted); +  Event_Close(&p->wasStopped); +  Semaphore_Close(&p->freeSemaphore); +  Semaphore_Close(&p->filledSemaphore); + +  p->wasCreated = False; +} + +#define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; } + +static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks) +{ +  if (p->wasCreated) +    return SZ_OK; + +  RINOK_THREAD(CriticalSection_Init(&p->cs)); +  p->csWasInitialized = True; + +  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart)); +  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted)); +  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped)); +   +  RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks)); +  RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks)); + +  p->needStart = True; +   +  RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj)); +  p->wasCreated = True; +  return SZ_OK; +} + +static SRes MtSync_Create(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks) +{ +  SRes res = MtSync_Create2(p, startAddress, obj, numBlocks); +  if (res != SZ_OK) +    MtSync_Destruct(p); +  return res; +} + +void MtSync_Init(CMtSync *p) { p->needStart = True; } + +#define kMtMaxValForNormalize 0xFFFFFFFF + +#define DEF_GetHeads2(name, v, action) \ +static void GetHeads ## name(const Byte *p, UInt32 pos, \ +UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \ +{ action; for (; numHeads != 0; numHeads--) { \ +const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++;  } } + +#define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;) + +DEF_GetHeads2(2,  (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; ) +DEF_GetHeads(3,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask) +DEF_GetHeads(4,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask) +DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask) +/* DEF_GetHeads(5,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask) */ + +void HashThreadFunc(CMatchFinderMt *mt) +{ +  CMtSync *p = &mt->hashSync; +  for (;;) +  { +    UInt32 numProcessedBlocks = 0; +    Event_Wait(&p->canStart); +    Event_Set(&p->wasStarted); +    for (;;) +    { +      if (p->exit) +        return; +      if (p->stopWriting) +      { +        p->numProcessedBlocks = numProcessedBlocks; +        Event_Set(&p->wasStopped); +        break; +      } + +      { +        CMatchFinder *mf = mt->MatchFinder; +        if (MatchFinder_NeedMove(mf)) +        { +          CriticalSection_Enter(&mt->btSync.cs); +          CriticalSection_Enter(&mt->hashSync.cs); +          { +            const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf); +            const Byte *afterPtr; +            MatchFinder_MoveBlock(mf); +            afterPtr = MatchFinder_GetPointerToCurrentPos(mf); +            mt->pointerToCurPos -= beforePtr - afterPtr; +            mt->buffer -= beforePtr - afterPtr; +          } +          CriticalSection_Leave(&mt->btSync.cs); +          CriticalSection_Leave(&mt->hashSync.cs); +          continue; +        } + +        Semaphore_Wait(&p->freeSemaphore); + +        MatchFinder_ReadIfRequired(mf); +        if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize)) +        { +          UInt32 subValue = (mf->pos - mf->historySize - 1); +          MatchFinder_ReduceOffsets(mf, subValue); +          MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1); +        } +        { +          UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize; +          UInt32 num = mf->streamPos - mf->pos; +          heads[0] = 2; +          heads[1] = num; +          if (num >= mf->numHashBytes) +          { +            num = num - mf->numHashBytes + 1; +            if (num > kMtHashBlockSize - 2) +              num = kMtHashBlockSize - 2; +            mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc); +            heads[0] += num; +          } +          mf->pos += num; +          mf->buffer += num; +        } +      } + +      Semaphore_Release1(&p->filledSemaphore); +    } +  } +} + +void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p) +{ +  MtSync_GetNextBlock(&p->hashSync); +  p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize; +  p->hashBufPosLimit += p->hashBuf[p->hashBufPos++]; +  p->hashNumAvail = p->hashBuf[p->hashBufPos++]; +} + +#define kEmptyHashValue 0 + +/* #define MFMT_GM_INLINE */ + +#ifdef MFMT_GM_INLINE + +#define NO_INLINE MY_FAST_CALL + +Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son, +    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, +    UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes) +{ +  do +  { +  UInt32 *distances = _distances + 1; +  UInt32 curMatch = pos - *hash++; + +  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; +  CLzRef *ptr1 = son + (_cyclicBufferPos << 1); +  UInt32 len0 = 0, len1 = 0; +  UInt32 cutValue = _cutValue; +  UInt32 maxLen = _maxLen; +  for (;;) +  { +    UInt32 delta = pos - curMatch; +    if (cutValue-- == 0 || delta >= _cyclicBufferSize) +    { +      *ptr0 = *ptr1 = kEmptyHashValue; +      break; +    } +    { +      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); +      const Byte *pb = cur - delta; +      UInt32 len = (len0 < len1 ? len0 : len1); +      if (pb[len] == cur[len]) +      { +        if (++len != lenLimit && pb[len] == cur[len]) +          while (++len != lenLimit) +            if (pb[len] != cur[len]) +              break; +        if (maxLen < len) +        { +          *distances++ = maxLen = len; +          *distances++ = delta - 1; +          if (len == lenLimit) +          { +            *ptr1 = pair[0]; +            *ptr0 = pair[1]; +            break; +          } +        } +      } +      if (pb[len] < cur[len]) +      { +        *ptr1 = curMatch; +        ptr1 = pair + 1; +        curMatch = *ptr1; +        len1 = len; +      } +      else +      { +        *ptr0 = curMatch; +        ptr0 = pair; +        curMatch = *ptr0; +        len0 = len; +      } +    } +  } +  pos++; +  _cyclicBufferPos++; +  cur++; +  { +    UInt32 num = (UInt32)(distances - _distances); +    *_distances = num - 1; +    _distances += num; +    limit -= num; +  } +  } +  while (limit > 0 && --size != 0); +  *posRes = pos; +  return limit; +} + +#endif + +void BtGetMatches(CMatchFinderMt *p, UInt32 *distances) +{ +  UInt32 numProcessed = 0; +  UInt32 curPos = 2; +  UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2); +  distances[1] = p->hashNumAvail; +  while (curPos < limit) +  { +    if (p->hashBufPos == p->hashBufPosLimit) +    { +      MatchFinderMt_GetNextBlock_Hash(p); +      distances[1] = numProcessed + p->hashNumAvail; +      if (p->hashNumAvail >= p->numHashBytes) +        continue; +      for (; p->hashNumAvail != 0; p->hashNumAvail--) +        distances[curPos++] = 0; +      break; +    } +    { +      UInt32 size = p->hashBufPosLimit - p->hashBufPos; +      UInt32 lenLimit = p->matchMaxLen; +      UInt32 pos = p->pos; +      UInt32 cyclicBufferPos = p->cyclicBufferPos; +      if (lenLimit >= p->hashNumAvail) +        lenLimit = p->hashNumAvail; +      { +        UInt32 size2 = p->hashNumAvail - lenLimit + 1; +        if (size2 < size) +          size = size2; +        size2 = p->cyclicBufferSize - cyclicBufferPos; +        if (size2 < size) +          size = size2; +      } +      #ifndef MFMT_GM_INLINE +      while (curPos < limit && size-- != 0) +      { +        UInt32 *startDistances = distances + curPos; +        UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++], +          pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, +          startDistances + 1, p->numHashBytes - 1) - startDistances); +        *startDistances = num - 1; +        curPos += num; +        cyclicBufferPos++; +        pos++; +        p->buffer++; +      } +      #else +      { +        UInt32 posRes; +        curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue, +          distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes); +        p->hashBufPos += posRes - pos; +        cyclicBufferPos += posRes - pos; +        p->buffer += posRes - pos; +        pos = posRes; +      } +      #endif + +      numProcessed += pos - p->pos; +      p->hashNumAvail -= pos - p->pos; +      p->pos = pos; +      if (cyclicBufferPos == p->cyclicBufferSize) +        cyclicBufferPos = 0; +      p->cyclicBufferPos = cyclicBufferPos; +    } +  } +  distances[0] = curPos; +} + +void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex) +{ +  CMtSync *sync = &p->hashSync; +  if (!sync->needStart) +  { +    CriticalSection_Enter(&sync->cs); +    sync->csWasEntered = True; +  } +   +  BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize); + +  if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) +  { +    UInt32 subValue = p->pos - p->cyclicBufferSize; +    MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2); +    p->pos -= subValue; +  } + +  if (!sync->needStart) +  { +    CriticalSection_Leave(&sync->cs); +    sync->csWasEntered = False; +  } +} + +void BtThreadFunc(CMatchFinderMt *mt) +{ +  CMtSync *p = &mt->btSync; +  for (;;) +  { +    UInt32 blockIndex = 0; +    Event_Wait(&p->canStart); +    Event_Set(&p->wasStarted); +    for (;;) +    { +      if (p->exit) +        return; +      if (p->stopWriting) +      { +        p->numProcessedBlocks = blockIndex; +        MtSync_StopWriting(&mt->hashSync); +        Event_Set(&p->wasStopped); +        break; +      } +      Semaphore_Wait(&p->freeSemaphore); +      BtFillBlock(mt, blockIndex++); +      Semaphore_Release1(&p->filledSemaphore); +    } +  } +} + +void MatchFinderMt_Construct(CMatchFinderMt *p) +{ +  p->hashBuf = 0; +  MtSync_Construct(&p->hashSync); +  MtSync_Construct(&p->btSync); +} + +void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->hashBuf); +  p->hashBuf = 0; +} + +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) +{ +  MtSync_Destruct(&p->hashSync); +  MtSync_Destruct(&p->btSync); +  MatchFinderMt_FreeMem(p, alloc); +} + +#define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks) +#define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks) + +static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p);  return 0; } +static unsigned MY_STD_CALL BtThreadFunc2(void *p) +{ +  Byte allocaDummy[0x180]; +  int i = 0; +  for (i = 0; i < 16; i++) +    allocaDummy[i] = (Byte)i; +  BtThreadFunc((CMatchFinderMt *)p); +  return 0; +} + +SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, +    UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) +{ +  CMatchFinder *mf = p->MatchFinder; +  p->historySize = historySize; +  if (kMtBtBlockSize <= matchMaxLen * 4) +    return SZ_ERROR_PARAM; +  if (p->hashBuf == 0) +  { +    p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); +    if (p->hashBuf == 0) +      return SZ_ERROR_MEM; +    p->btBuf = p->hashBuf + kHashBufferSize; +  } +  keepAddBufferBefore += (kHashBufferSize + kBtBufferSize); +  keepAddBufferAfter += kMtHashBlockSize; +  if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc)) +    return SZ_ERROR_MEM; + +  RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks)); +  RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks)); +  return SZ_OK; +} + +/* Call it after ReleaseStream / SetStream */ +void MatchFinderMt_Init(CMatchFinderMt *p) +{ +  CMatchFinder *mf = p->MatchFinder; +  p->btBufPos = p->btBufPosLimit = 0; +  p->hashBufPos = p->hashBufPosLimit = 0; +  MatchFinder_Init(mf); +  p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf); +  p->btNumAvailBytes = 0; +  p->lzPos = p->historySize + 1; + +  p->hash = mf->hash; +  p->fixedHashSize = mf->fixedHashSize; +  p->crc = mf->crc; + +  p->son = mf->son; +  p->matchMaxLen = mf->matchMaxLen; +  p->numHashBytes = mf->numHashBytes; +  p->pos = mf->pos; +  p->buffer = mf->buffer; +  p->cyclicBufferPos = mf->cyclicBufferPos; +  p->cyclicBufferSize = mf->cyclicBufferSize; +  p->cutValue = mf->cutValue; +} + +/* ReleaseStream is required to finish multithreading */ +void MatchFinderMt_ReleaseStream(CMatchFinderMt *p) +{ +  MtSync_StopWriting(&p->btSync); +  /* p->MatchFinder->ReleaseStream(); */ +} + +void MatchFinderMt_Normalize(CMatchFinderMt *p) +{ +  MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize); +  p->lzPos = p->historySize + 1; +} + +void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p) +{ +  UInt32 blockIndex; +  MtSync_GetNextBlock(&p->btSync); +  blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask); +  p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize; +  p->btBufPosLimit += p->btBuf[p->btBufPos++]; +  p->btNumAvailBytes = p->btBuf[p->btBufPos++]; +  if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize) +    MatchFinderMt_Normalize(p); +} + +const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p) +{ +  return p->pointerToCurPos; +} + +#define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p); + +UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p) +{ +  GET_NEXT_BLOCK_IF_REQUIRED; +  return p->btNumAvailBytes; +} + +Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index) +{ +  return p->pointerToCurPos[index]; +} + +UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ +  UInt32 hash2Value, curMatch2; +  UInt32 *hash = p->hash; +  const Byte *cur = p->pointerToCurPos; +  UInt32 lzPos = p->lzPos; +  MT_HASH2_CALC +       +  curMatch2 = hash[hash2Value]; +  hash[hash2Value] = lzPos; + +  if (curMatch2 >= matchMinPos) +    if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) +    { +      *distances++ = 2; +      *distances++ = lzPos - curMatch2 - 1; +    } +  return distances; +} + +UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ +  UInt32 hash2Value, hash3Value, curMatch2, curMatch3; +  UInt32 *hash = p->hash; +  const Byte *cur = p->pointerToCurPos; +  UInt32 lzPos = p->lzPos; +  MT_HASH3_CALC + +  curMatch2 = hash[                hash2Value]; +  curMatch3 = hash[kFix3HashSize + hash3Value]; +   +  hash[                hash2Value] = +  hash[kFix3HashSize + hash3Value] = +    lzPos; + +  if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) +  { +    distances[1] = lzPos - curMatch2 - 1; +    if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) +    { +      distances[0] = 3; +      return distances + 2; +    } +    distances[0] = 2; +    distances += 2; +  } +  if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) +  { +    *distances++ = 3; +    *distances++ = lzPos - curMatch3 - 1; +  } +  return distances; +} + +/* +UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances) +{ +  UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4; +  UInt32 *hash = p->hash; +  const Byte *cur = p->pointerToCurPos; +  UInt32 lzPos = p->lzPos; +  MT_HASH4_CALC +       +  curMatch2 = hash[                hash2Value]; +  curMatch3 = hash[kFix3HashSize + hash3Value]; +  curMatch4 = hash[kFix4HashSize + hash4Value]; +   +  hash[                hash2Value] = +  hash[kFix3HashSize + hash3Value] = +  hash[kFix4HashSize + hash4Value] = +    lzPos; + +  if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) +  { +    distances[1] = lzPos - curMatch2 - 1; +    if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2]) +    { +      distances[0] =  (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3; +      return distances + 2; +    } +    distances[0] = 2; +    distances += 2; +  } +  if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0]) +  { +    distances[1] = lzPos - curMatch3 - 1; +    if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3]) +    { +      distances[0] = 4; +      return distances + 2; +    } +    distances[0] = 3; +    distances += 2; +  } + +  if (curMatch4 >= matchMinPos) +    if ( +      cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] && +      cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3] +      ) +    { +      *distances++ = 4; +      *distances++ = lzPos - curMatch4 - 1; +    } +  return distances; +} +*/ + +#define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++; + +UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances) +{ +  const UInt32 *btBuf = p->btBuf + p->btBufPos; +  UInt32 len = *btBuf++; +  p->btBufPos += 1 + len; +  p->btNumAvailBytes--; +  { +    UInt32 i; +    for (i = 0; i < len; i += 2) +    { +      *distances++ = *btBuf++; +      *distances++ = *btBuf++; +    } +  } +  INCREASE_LZ_POS +  return len; +} + +UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances) +{ +  const UInt32 *btBuf = p->btBuf + p->btBufPos; +  UInt32 len = *btBuf++; +  p->btBufPos += 1 + len; + +  if (len == 0) +  { +    if (p->btNumAvailBytes-- >= 4) +      len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances)); +  } +  else +  { +    /* Condition: there are matches in btBuf with length < p->numHashBytes */ +    UInt32 *distances2; +    p->btNumAvailBytes--; +    distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances); +    do +    { +      *distances2++ = *btBuf++; +      *distances2++ = *btBuf++; +    } +    while ((len -= 2) != 0); +    len  = (UInt32)(distances2 - (distances)); +  } +  INCREASE_LZ_POS +  return len; +} + +#define SKIP_HEADER2_MT  do { GET_NEXT_BLOCK_IF_REQUIRED +#define SKIP_HEADER_MT(n) SKIP_HEADER2_MT if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash; +#define SKIP_FOOTER_MT } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0); + +void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num) +{ +  SKIP_HEADER2_MT { p->btNumAvailBytes--; +  SKIP_FOOTER_MT +} + +void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num) +{ +  SKIP_HEADER_MT(2) +      UInt32 hash2Value; +      MT_HASH2_CALC +      hash[hash2Value] = p->lzPos; +  SKIP_FOOTER_MT +} + +void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num) +{ +  SKIP_HEADER_MT(3) +      UInt32 hash2Value, hash3Value; +      MT_HASH3_CALC +      hash[kFix3HashSize + hash3Value] = +      hash[                hash2Value] = +        p->lzPos; +  SKIP_FOOTER_MT +} + +/* +void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num) +{ +  SKIP_HEADER_MT(4) +      UInt32 hash2Value, hash3Value, hash4Value; +      MT_HASH4_CALC +      hash[kFix4HashSize + hash4Value] = +      hash[kFix3HashSize + hash3Value] = +      hash[                hash2Value] = +        p->lzPos; +  SKIP_FOOTER_MT +} +*/ + +void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable) +{ +  vTable->Init = (Mf_Init_Func)MatchFinderMt_Init; +  vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte; +  vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes; +  vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos; +  vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches; +  switch(p->MatchFinder->numHashBytes) +  { +    case 2: +      p->GetHeadsFunc = GetHeads2; +      p->MixMatchesFunc = (Mf_Mix_Matches)0; +      vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip; +      vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches; +      break; +    case 3: +      p->GetHeadsFunc = GetHeads3; +      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2; +      vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip; +      break; +    default: +    /* case 4: */ +      p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4; +      /* p->GetHeadsFunc = GetHeads4; */ +      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3; +      vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip; +      break; +    /* +    default: +      p->GetHeadsFunc = GetHeads5; +      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4; +      vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip; +      break; +    */ +  } +} diff --git a/src/lzma/C/LzFindMt.h b/src/lzma/C/LzFindMt.h new file mode 100644 index 0000000..b985af5 --- /dev/null +++ b/src/lzma/C/LzFindMt.h @@ -0,0 +1,105 @@ +/* LzFindMt.h -- multithreaded Match finder for LZ algorithms +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZ_FIND_MT_H +#define __LZ_FIND_MT_H + +#include "LzFind.h" +#include "Threads.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define kMtHashBlockSize (1 << 13) +#define kMtHashNumBlocks (1 << 3) +#define kMtHashNumBlocksMask (kMtHashNumBlocks - 1) + +#define kMtBtBlockSize (1 << 14) +#define kMtBtNumBlocks (1 << 6) +#define kMtBtNumBlocksMask (kMtBtNumBlocks - 1) + +typedef struct _CMtSync +{ +  Bool wasCreated; +  Bool needStart; +  Bool exit; +  Bool stopWriting; + +  CThread thread; +  CAutoResetEvent canStart; +  CAutoResetEvent wasStarted; +  CAutoResetEvent wasStopped; +  CSemaphore freeSemaphore; +  CSemaphore filledSemaphore; +  Bool csWasInitialized; +  Bool csWasEntered; +  CCriticalSection cs; +  UInt32 numProcessedBlocks; +} CMtSync; + +typedef UInt32 * (*Mf_Mix_Matches)(void *p, UInt32 matchMinPos, UInt32 *distances); + +/* kMtCacheLineDummy must be >= size_of_CPU_cache_line */ +#define kMtCacheLineDummy 128 + +typedef void (*Mf_GetHeads)(const Byte *buffer, UInt32 pos, +  UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc); + +typedef struct _CMatchFinderMt +{ +  /* LZ */ +  const Byte *pointerToCurPos; +  UInt32 *btBuf; +  UInt32 btBufPos; +  UInt32 btBufPosLimit; +  UInt32 lzPos; +  UInt32 btNumAvailBytes; + +  UInt32 *hash; +  UInt32 fixedHashSize; +  UInt32 historySize; +  const UInt32 *crc; + +  Mf_Mix_Matches MixMatchesFunc; +   +  /* LZ + BT */ +  CMtSync btSync; +  Byte btDummy[kMtCacheLineDummy]; + +  /* BT */ +  UInt32 *hashBuf; +  UInt32 hashBufPos; +  UInt32 hashBufPosLimit; +  UInt32 hashNumAvail; + +  CLzRef *son; +  UInt32 matchMaxLen; +  UInt32 numHashBytes; +  UInt32 pos; +  Byte *buffer; +  UInt32 cyclicBufferPos; +  UInt32 cyclicBufferSize; /* it must be historySize + 1 */ +  UInt32 cutValue; + +  /* BT + Hash */ +  CMtSync hashSync; +  /* Byte hashDummy[kMtCacheLineDummy]; */ +   +  /* Hash */ +  Mf_GetHeads GetHeadsFunc; +  CMatchFinder *MatchFinder; +} CMatchFinderMt; + +void MatchFinderMt_Construct(CMatchFinderMt *p); +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc); +SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, +    UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); +void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); +void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lzma/C/LzHash.h b/src/lzma/C/LzHash.h new file mode 100644 index 0000000..f3e8996 --- /dev/null +++ b/src/lzma/C/LzHash.h @@ -0,0 +1,54 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZ_HASH_H +#define __LZ_HASH_H + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); + +#define HASH3_CALC { \ +  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ +  hash2Value = temp & (kHash2Size - 1); \ +  hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } + +#define HASH4_CALC { \ +  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ +  hash2Value = temp & (kHash2Size - 1); \ +  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ +  hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } + +#define HASH5_CALC { \ +  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ +  hash2Value = temp & (kHash2Size - 1); \ +  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ +  hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ +  hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ +  hash4Value &= (kHash4Size - 1); } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ +#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + + +#define MT_HASH2_CALC \ +  hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC { \ +  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ +  hash2Value = temp & (kHash2Size - 1); \ +  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } + +#define MT_HASH4_CALC { \ +  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ +  hash2Value = temp & (kHash2Size - 1); \ +  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ +  hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } + +#endif diff --git a/src/lzma/C/LzmaDec.c b/src/lzma/C/LzmaDec.c new file mode 100644 index 0000000..2036761 --- /dev/null +++ b/src/lzma/C/LzmaDec.c @@ -0,0 +1,999 @@ +/* LzmaDec.c -- LZMA Decoder +2009-09-20 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include <string.h> + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ +  { UPDATE_0(p); i = (i + i); A0; } else \ +  { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ +  { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ +  { i = 1; \ +  TREE_GET_BIT(probs, i); \ +  TREE_GET_BIT(probs, i); \ +  TREE_GET_BIT(probs, i); \ +  TREE_GET_BIT(probs, i); \ +  TREE_GET_BIT(probs, i); \ +  TREE_GET_BIT(probs, i); \ +  i -= 0x40; } +#endif + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ +  { UPDATE_0_CHECK; i = (i + i); A0; } else \ +  { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ +  { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: +  Result: +    SZ_OK - OK +    SZ_ERROR_DATA - Error +  p->remainLen: +    < kMatchSpecLenStart : normal remain +    = kMatchSpecLenStart : finished +    = kMatchSpecLenStart + 1 : Flush marker +    = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ +  CLzmaProb *probs = p->probs; + +  unsigned state = p->state; +  UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; +  unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; +  unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; +  unsigned lc = p->prop.lc; + +  Byte *dic = p->dic; +  SizeT dicBufSize = p->dicBufSize; +  SizeT dicPos = p->dicPos; +   +  UInt32 processedPos = p->processedPos; +  UInt32 checkDicSize = p->checkDicSize; +  unsigned len = 0; + +  const Byte *buf = p->buf; +  UInt32 range = p->range; +  UInt32 code = p->code; + +  do +  { +    CLzmaProb *prob; +    UInt32 bound; +    unsigned ttt; +    unsigned posState = processedPos & pbMask; + +    prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; +    IF_BIT_0(prob) +    { +      unsigned symbol; +      UPDATE_0(prob); +      prob = probs + Literal; +      if (checkDicSize != 0 || processedPos != 0) +        prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + +        (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + +      if (state < kNumLitStates) +      { +        state -= (state < 4) ? state : 3; +        symbol = 1; +        do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); +      } +      else +      { +        unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; +        unsigned offs = 0x100; +        state -= (state < 10) ? 3 : 6; +        symbol = 1; +        do +        { +          unsigned bit; +          CLzmaProb *probLit; +          matchByte <<= 1; +          bit = (matchByte & offs); +          probLit = prob + offs + bit + symbol; +          GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) +        } +        while (symbol < 0x100); +      } +      dic[dicPos++] = (Byte)symbol; +      processedPos++; +      continue; +    } +    else +    { +      UPDATE_1(prob); +      prob = probs + IsRep + state; +      IF_BIT_0(prob) +      { +        UPDATE_0(prob); +        state += kNumStates; +        prob = probs + LenCoder; +      } +      else +      { +        UPDATE_1(prob); +        if (checkDicSize == 0 && processedPos == 0) +          return SZ_ERROR_DATA; +        prob = probs + IsRepG0 + state; +        IF_BIT_0(prob) +        { +          UPDATE_0(prob); +          prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; +          IF_BIT_0(prob) +          { +            UPDATE_0(prob); +            dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; +            dicPos++; +            processedPos++; +            state = state < kNumLitStates ? 9 : 11; +            continue; +          } +          UPDATE_1(prob); +        } +        else +        { +          UInt32 distance; +          UPDATE_1(prob); +          prob = probs + IsRepG1 + state; +          IF_BIT_0(prob) +          { +            UPDATE_0(prob); +            distance = rep1; +          } +          else +          { +            UPDATE_1(prob); +            prob = probs + IsRepG2 + state; +            IF_BIT_0(prob) +            { +              UPDATE_0(prob); +              distance = rep2; +            } +            else +            { +              UPDATE_1(prob); +              distance = rep3; +              rep3 = rep2; +            } +            rep2 = rep1; +          } +          rep1 = rep0; +          rep0 = distance; +        } +        state = state < kNumLitStates ? 8 : 11; +        prob = probs + RepLenCoder; +      } +      { +        unsigned limit, offset; +        CLzmaProb *probLen = prob + LenChoice; +        IF_BIT_0(probLen) +        { +          UPDATE_0(probLen); +          probLen = prob + LenLow + (posState << kLenNumLowBits); +          offset = 0; +          limit = (1 << kLenNumLowBits); +        } +        else +        { +          UPDATE_1(probLen); +          probLen = prob + LenChoice2; +          IF_BIT_0(probLen) +          { +            UPDATE_0(probLen); +            probLen = prob + LenMid + (posState << kLenNumMidBits); +            offset = kLenNumLowSymbols; +            limit = (1 << kLenNumMidBits); +          } +          else +          { +            UPDATE_1(probLen); +            probLen = prob + LenHigh; +            offset = kLenNumLowSymbols + kLenNumMidSymbols; +            limit = (1 << kLenNumHighBits); +          } +        } +        TREE_DECODE(probLen, limit, len); +        len += offset; +      } + +      if (state >= kNumStates) +      { +        UInt32 distance; +        prob = probs + PosSlot + +            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); +        TREE_6_DECODE(prob, distance); +        if (distance >= kStartPosModelIndex) +        { +          unsigned posSlot = (unsigned)distance; +          int numDirectBits = (int)(((distance >> 1) - 1)); +          distance = (2 | (distance & 1)); +          if (posSlot < kEndPosModelIndex) +          { +            distance <<= numDirectBits; +            prob = probs + SpecPos + distance - posSlot - 1; +            { +              UInt32 mask = 1; +              unsigned i = 1; +              do +              { +                GET_BIT2(prob + i, i, ; , distance |= mask); +                mask <<= 1; +              } +              while (--numDirectBits != 0); +            } +          } +          else +          { +            numDirectBits -= kNumAlignBits; +            do +            { +              NORMALIZE +              range >>= 1; +               +              { +                UInt32 t; +                code -= range; +                t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ +                distance = (distance << 1) + (t + 1); +                code += range & t; +              } +              /* +              distance <<= 1; +              if (code >= range) +              { +                code -= range; +                distance |= 1; +              } +              */ +            } +            while (--numDirectBits != 0); +            prob = probs + Align; +            distance <<= kNumAlignBits; +            { +              unsigned i = 1; +              GET_BIT2(prob + i, i, ; , distance |= 1); +              GET_BIT2(prob + i, i, ; , distance |= 2); +              GET_BIT2(prob + i, i, ; , distance |= 4); +              GET_BIT2(prob + i, i, ; , distance |= 8); +            } +            if (distance == (UInt32)0xFFFFFFFF) +            { +              len += kMatchSpecLenStart; +              state -= kNumStates; +              break; +            } +          } +        } +        rep3 = rep2; +        rep2 = rep1; +        rep1 = rep0; +        rep0 = distance + 1; +        if (checkDicSize == 0) +        { +          if (distance >= processedPos) +            return SZ_ERROR_DATA; +        } +        else if (distance >= checkDicSize) +          return SZ_ERROR_DATA; +        state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; +      } + +      len += kMatchMinLen; + +      if (limit == dicPos) +        return SZ_ERROR_DATA; +      { +        SizeT rem = limit - dicPos; +        unsigned curLen = ((rem < len) ? (unsigned)rem : len); +        SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + +        processedPos += curLen; + +        len -= curLen; +        if (pos + curLen <= dicBufSize) +        { +          Byte *dest = dic + dicPos; +          ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; +          const Byte *lim = dest + curLen; +          dicPos += curLen; +          do +            *(dest) = (Byte)*(dest + src); +          while (++dest != lim); +        } +        else +        { +          do +          { +            dic[dicPos++] = dic[pos]; +            if (++pos == dicBufSize) +              pos = 0; +          } +          while (--curLen != 0); +        } +      } +    } +  } +  while (dicPos < limit && buf < bufLimit); +  NORMALIZE; +  p->buf = buf; +  p->range = range; +  p->code = code; +  p->remainLen = len; +  p->dicPos = dicPos; +  p->processedPos = processedPos; +  p->reps[0] = rep0; +  p->reps[1] = rep1; +  p->reps[2] = rep2; +  p->reps[3] = rep3; +  p->state = state; + +  return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ +  if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) +  { +    Byte *dic = p->dic; +    SizeT dicPos = p->dicPos; +    SizeT dicBufSize = p->dicBufSize; +    unsigned len = p->remainLen; +    UInt32 rep0 = p->reps[0]; +    if (limit - dicPos < len) +      len = (unsigned)(limit - dicPos); + +    if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) +      p->checkDicSize = p->prop.dicSize; + +    p->processedPos += len; +    p->remainLen -= len; +    while (len-- != 0) +    { +      dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; +      dicPos++; +    } +    p->dicPos = dicPos; +  } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ +  do +  { +    SizeT limit2 = limit; +    if (p->checkDicSize == 0) +    { +      UInt32 rem = p->prop.dicSize - p->processedPos; +      if (limit - p->dicPos > rem) +        limit2 = p->dicPos + rem; +    } +    RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); +    if (p->processedPos >= p->prop.dicSize) +      p->checkDicSize = p->prop.dicSize; +    LzmaDec_WriteRem(p, limit); +  } +  while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + +  if (p->remainLen > kMatchSpecLenStart) +  { +    p->remainLen = kMatchSpecLenStart; +  } +  return 0; +} + +typedef enum +{ +  DUMMY_ERROR, /* unexpected end of input stream */ +  DUMMY_LIT, +  DUMMY_MATCH, +  DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ +  UInt32 range = p->range; +  UInt32 code = p->code; +  const Byte *bufLimit = buf + inSize; +  CLzmaProb *probs = p->probs; +  unsigned state = p->state; +  ELzmaDummy res; + +  { +    CLzmaProb *prob; +    UInt32 bound; +    unsigned ttt; +    unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + +    prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; +    IF_BIT_0_CHECK(prob) +    { +      UPDATE_0_CHECK + +      /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + +      prob = probs + Literal; +      if (p->checkDicSize != 0 || p->processedPos != 0) +        prob += (LZMA_LIT_SIZE * +          ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + +          (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + +      if (state < kNumLitStates) +      { +        unsigned symbol = 1; +        do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); +      } +      else +      { +        unsigned matchByte = p->dic[p->dicPos - p->reps[0] + +            ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; +        unsigned offs = 0x100; +        unsigned symbol = 1; +        do +        { +          unsigned bit; +          CLzmaProb *probLit; +          matchByte <<= 1; +          bit = (matchByte & offs); +          probLit = prob + offs + bit + symbol; +          GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) +        } +        while (symbol < 0x100); +      } +      res = DUMMY_LIT; +    } +    else +    { +      unsigned len; +      UPDATE_1_CHECK; + +      prob = probs + IsRep + state; +      IF_BIT_0_CHECK(prob) +      { +        UPDATE_0_CHECK; +        state = 0; +        prob = probs + LenCoder; +        res = DUMMY_MATCH; +      } +      else +      { +        UPDATE_1_CHECK; +        res = DUMMY_REP; +        prob = probs + IsRepG0 + state; +        IF_BIT_0_CHECK(prob) +        { +          UPDATE_0_CHECK; +          prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; +          IF_BIT_0_CHECK(prob) +          { +            UPDATE_0_CHECK; +            NORMALIZE_CHECK; +            return DUMMY_REP; +          } +          else +          { +            UPDATE_1_CHECK; +          } +        } +        else +        { +          UPDATE_1_CHECK; +          prob = probs + IsRepG1 + state; +          IF_BIT_0_CHECK(prob) +          { +            UPDATE_0_CHECK; +          } +          else +          { +            UPDATE_1_CHECK; +            prob = probs + IsRepG2 + state; +            IF_BIT_0_CHECK(prob) +            { +              UPDATE_0_CHECK; +            } +            else +            { +              UPDATE_1_CHECK; +            } +          } +        } +        state = kNumStates; +        prob = probs + RepLenCoder; +      } +      { +        unsigned limit, offset; +        CLzmaProb *probLen = prob + LenChoice; +        IF_BIT_0_CHECK(probLen) +        { +          UPDATE_0_CHECK; +          probLen = prob + LenLow + (posState << kLenNumLowBits); +          offset = 0; +          limit = 1 << kLenNumLowBits; +        } +        else +        { +          UPDATE_1_CHECK; +          probLen = prob + LenChoice2; +          IF_BIT_0_CHECK(probLen) +          { +            UPDATE_0_CHECK; +            probLen = prob + LenMid + (posState << kLenNumMidBits); +            offset = kLenNumLowSymbols; +            limit = 1 << kLenNumMidBits; +          } +          else +          { +            UPDATE_1_CHECK; +            probLen = prob + LenHigh; +            offset = kLenNumLowSymbols + kLenNumMidSymbols; +            limit = 1 << kLenNumHighBits; +          } +        } +        TREE_DECODE_CHECK(probLen, limit, len); +        len += offset; +      } + +      if (state < 4) +      { +        unsigned posSlot; +        prob = probs + PosSlot + +            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << +            kNumPosSlotBits); +        TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); +        if (posSlot >= kStartPosModelIndex) +        { +          int numDirectBits = ((posSlot >> 1) - 1); + +          /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + +          if (posSlot < kEndPosModelIndex) +          { +            prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; +          } +          else +          { +            numDirectBits -= kNumAlignBits; +            do +            { +              NORMALIZE_CHECK +              range >>= 1; +              code -= range & (((code - range) >> 31) - 1); +              /* if (code >= range) code -= range; */ +            } +            while (--numDirectBits != 0); +            prob = probs + Align; +            numDirectBits = kNumAlignBits; +          } +          { +            unsigned i = 1; +            do +            { +              GET_BIT_CHECK(prob + i, i); +            } +            while (--numDirectBits != 0); +          } +        } +      } +    } +  } +  NORMALIZE_CHECK; +  return res; +} + + +static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) +{ +  p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); +  p->range = 0xFFFFFFFF; +  p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ +  p->needFlush = 1; +  p->remainLen = 0; +  p->tempBufSize = 0; + +  if (initDic) +  { +    p->processedPos = 0; +    p->checkDicSize = 0; +    p->needInitState = 1; +  } +  if (initState) +    p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ +  p->dicPos = 0; +  LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ +  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); +  UInt32 i; +  CLzmaProb *probs = p->probs; +  for (i = 0; i < numProbs; i++) +    probs[i] = kBitModelTotal >> 1; +  p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; +  p->state = 0; +  p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, +    ELzmaFinishMode finishMode, ELzmaStatus *status) +{ +  SizeT inSize = *srcLen; +  (*srcLen) = 0; +  LzmaDec_WriteRem(p, dicLimit); +   +  *status = LZMA_STATUS_NOT_SPECIFIED; + +  while (p->remainLen != kMatchSpecLenStart) +  { +      int checkEndMarkNow; + +      if (p->needFlush != 0) +      { +        for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) +          p->tempBuf[p->tempBufSize++] = *src++; +        if (p->tempBufSize < RC_INIT_SIZE) +        { +          *status = LZMA_STATUS_NEEDS_MORE_INPUT; +          return SZ_OK; +        } +        if (p->tempBuf[0] != 0) +          return SZ_ERROR_DATA; + +        LzmaDec_InitRc(p, p->tempBuf); +        p->tempBufSize = 0; +      } + +      checkEndMarkNow = 0; +      if (p->dicPos >= dicLimit) +      { +        if (p->remainLen == 0 && p->code == 0) +        { +          *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; +          return SZ_OK; +        } +        if (finishMode == LZMA_FINISH_ANY) +        { +          *status = LZMA_STATUS_NOT_FINISHED; +          return SZ_OK; +        } +        if (p->remainLen != 0) +        { +          *status = LZMA_STATUS_NOT_FINISHED; +          return SZ_ERROR_DATA; +        } +        checkEndMarkNow = 1; +      } + +      if (p->needInitState) +        LzmaDec_InitStateReal(p); +   +      if (p->tempBufSize == 0) +      { +        SizeT processed; +        const Byte *bufLimit; +        if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) +        { +          int dummyRes = LzmaDec_TryDummy(p, src, inSize); +          if (dummyRes == DUMMY_ERROR) +          { +            memcpy(p->tempBuf, src, inSize); +            p->tempBufSize = (unsigned)inSize; +            (*srcLen) += inSize; +            *status = LZMA_STATUS_NEEDS_MORE_INPUT; +            return SZ_OK; +          } +          if (checkEndMarkNow && dummyRes != DUMMY_MATCH) +          { +            *status = LZMA_STATUS_NOT_FINISHED; +            return SZ_ERROR_DATA; +          } +          bufLimit = src; +        } +        else +          bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; +        p->buf = src; +        if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) +          return SZ_ERROR_DATA; +        processed = (SizeT)(p->buf - src); +        (*srcLen) += processed; +        src += processed; +        inSize -= processed; +      } +      else +      { +        unsigned rem = p->tempBufSize, lookAhead = 0; +        while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) +          p->tempBuf[rem++] = src[lookAhead++]; +        p->tempBufSize = rem; +        if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) +        { +          int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); +          if (dummyRes == DUMMY_ERROR) +          { +            (*srcLen) += lookAhead; +            *status = LZMA_STATUS_NEEDS_MORE_INPUT; +            return SZ_OK; +          } +          if (checkEndMarkNow && dummyRes != DUMMY_MATCH) +          { +            *status = LZMA_STATUS_NOT_FINISHED; +            return SZ_ERROR_DATA; +          } +        } +        p->buf = p->tempBuf; +        if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) +          return SZ_ERROR_DATA; +        lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); +        (*srcLen) += lookAhead; +        src += lookAhead; +        inSize -= lookAhead; +        p->tempBufSize = 0; +      } +  } +  if (p->code == 0) +    *status = LZMA_STATUS_FINISHED_WITH_MARK; +  return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ +  SizeT outSize = *destLen; +  SizeT inSize = *srcLen; +  *srcLen = *destLen = 0; +  for (;;) +  { +    SizeT inSizeCur = inSize, outSizeCur, dicPos; +    ELzmaFinishMode curFinishMode; +    SRes res; +    if (p->dicPos == p->dicBufSize) +      p->dicPos = 0; +    dicPos = p->dicPos; +    if (outSize > p->dicBufSize - dicPos) +    { +      outSizeCur = p->dicBufSize; +      curFinishMode = LZMA_FINISH_ANY; +    } +    else +    { +      outSizeCur = dicPos + outSize; +      curFinishMode = finishMode; +    } + +    res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); +    src += inSizeCur; +    inSize -= inSizeCur; +    *srcLen += inSizeCur; +    outSizeCur = p->dicPos - dicPos; +    memcpy(dest, p->dic + dicPos, outSizeCur); +    dest += outSizeCur; +    outSize -= outSizeCur; +    *destLen += outSizeCur; +    if (res != 0) +      return res; +    if (outSizeCur == 0 || outSize == 0) +      return SZ_OK; +  } +} + +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->probs); +  p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->dic); +  p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +{ +  LzmaDec_FreeProbs(p, alloc); +  LzmaDec_FreeDict(p, alloc); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ +  UInt32 dicSize; +  Byte d; +   +  if (size < LZMA_PROPS_SIZE) +    return SZ_ERROR_UNSUPPORTED; +  else +    dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); +  +  if (dicSize < LZMA_DIC_MIN) +    dicSize = LZMA_DIC_MIN; +  p->dicSize = dicSize; + +  d = data[0]; +  if (d >= (9 * 5 * 5)) +    return SZ_ERROR_UNSUPPORTED; + +  p->lc = d % 9; +  d /= 9; +  p->pb = d / 5; +  p->lp = d % 5; + +  return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ +  UInt32 numProbs = LzmaProps_GetNumProbs(propNew); +  if (p->probs == 0 || numProbs != p->numProbs) +  { +    LzmaDec_FreeProbs(p, alloc); +    p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); +    p->numProbs = numProbs; +    if (p->probs == 0) +      return SZ_ERROR_MEM; +  } +  return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ +  CLzmaProps propNew; +  RINOK(LzmaProps_Decode(&propNew, props, propsSize)); +  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); +  p->prop = propNew; +  return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ +  CLzmaProps propNew; +  SizeT dicBufSize; +  RINOK(LzmaProps_Decode(&propNew, props, propsSize)); +  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); +  dicBufSize = propNew.dicSize; +  if (p->dic == 0 || dicBufSize != p->dicBufSize) +  { +    LzmaDec_FreeDict(p, alloc); +    p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); +    if (p->dic == 0) +    { +      LzmaDec_FreeProbs(p, alloc); +      return SZ_ERROR_MEM; +    } +  } +  p->dicBufSize = dicBufSize; +  p->prop = propNew; +  return SZ_OK; +} + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, +    const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, +    ELzmaStatus *status, ISzAlloc *alloc) +{ +  CLzmaDec p; +  SRes res; +  SizeT inSize = *srcLen; +  SizeT outSize = *destLen; +  *srcLen = *destLen = 0; +  if (inSize < RC_INIT_SIZE) +    return SZ_ERROR_INPUT_EOF; + +  LzmaDec_Construct(&p); +  res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); +  if (res != 0) +    return res; +  p.dic = dest; +  p.dicBufSize = outSize; + +  LzmaDec_Init(&p); +   +  *srcLen = inSize; +  res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + +  if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) +    res = SZ_ERROR_INPUT_EOF; + +  (*destLen) = p.dicPos; +  LzmaDec_FreeProbs(&p, alloc); +  return res; +} diff --git a/src/lzma/C/LzmaDec.h b/src/lzma/C/LzmaDec.h new file mode 100644 index 0000000..bf7f084 --- /dev/null +++ b/src/lzma/C/LzmaDec.h @@ -0,0 +1,231 @@ +/* LzmaDec.h -- LZMA Decoder +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_DEC_H +#define __LZMA_DEC_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, +   but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ +  unsigned lc, lp, pb; +  UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: +  SZ_OK +  SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. +   Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ +  CLzmaProps prop; +  CLzmaProb *probs; +  Byte *dic; +  const Byte *buf; +  UInt32 range, code; +  SizeT dicPos; +  SizeT dicBufSize; +  UInt32 processedPos; +  UInt32 checkDicSize; +  unsigned state; +  UInt32 reps[4]; +  unsigned remainLen; +  int needFlush; +  int needInitState; +  UInt32 numProbs; +  unsigned tempBufSize; +  Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: +     0) Stream with end mark. That end mark adds about 6 bytes to compressed size. +     1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ +  LZMA_FINISH_ANY,   /* finish at any point */ +  LZMA_FINISH_END    /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + +   You must use LZMA_FINISH_END, when you know that current output buffer +   covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + +   If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, +   and output value of destLen will be less than output buffer size limit. +   You can check status result also. + +   You can use multiple checks to test data integrity after full decompression: +     1) Check Result and "status" variable. +     2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. +     3) Check that output(srcLen) = compressedSize, if you know real compressedSize. +        You must use correct finish mode in that case. */ + +typedef enum +{ +  LZMA_STATUS_NOT_SPECIFIED,               /* use main error code instead */ +  LZMA_STATUS_FINISHED_WITH_MARK,          /* stream was finished with end mark. */ +  LZMA_STATUS_NOT_FINISHED,                /* stream was not finished */ +  LZMA_STATUS_NEEDS_MORE_INPUT,            /* you must provide more input bytes */ +  LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK  /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: +     1) Dictionary Interface +     2) Buffer Interface +     3) One Call Interface +   You can select any of these interfaces, but don't mix functions from different +   groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: +     1) LzmaDec_Allocate / LzmaDec_Free +     2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs +   You can use variant 2, if you set dictionary buffer manually. +   For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: +  SZ_OK +  SZ_ERROR_MEM         - Memory allocation error +  SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ +    +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); +void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from +   dictionary to some other external buffer. +   You must work with CLzmaDec variables directly in this interface. + +   STEPS: +     LzmaDec_Constr() +     LzmaDec_Allocate() +     for (each new stream) +     { +       LzmaDec_Init() +       while (it needs more decompression) +       { +         LzmaDec_DecodeToDic() +         use data from CLzmaDec::dic and update CLzmaDec::dicPos +       } +     } +     LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic +    +   The decoding to internal dictionary buffer (CLzmaDec::dic). +   You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: +  It has meaning only if the decoding reaches output limit (dicLimit). +  LZMA_FINISH_ANY - Decode just dicLimit bytes. +  LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: +  SZ_OK +    status: +      LZMA_STATUS_FINISHED_WITH_MARK +      LZMA_STATUS_NOT_FINISHED +      LZMA_STATUS_NEEDS_MORE_INPUT +      LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK +  SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, +    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. +   See LzmaDec_DecodeToDic description for information about STEPS and return results, +   but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need +   to work with CLzmaDec variables manually. + +finishMode: +  It has meaning only if the decoding reaches output limit (*destLen). +  LZMA_FINISH_ANY - Decode just destLen bytes. +  LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, +    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: +  It has meaning only if the decoding reaches output limit (*destLen). +  LZMA_FINISH_ANY - Decode just destLen bytes. +  LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: +  SZ_OK +    status: +      LZMA_STATUS_FINISHED_WITH_MARK +      LZMA_STATUS_NOT_FINISHED +      LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK +  SZ_ERROR_DATA - Data error +  SZ_ERROR_MEM  - Memory allocation error +  SZ_ERROR_UNSUPPORTED - Unsupported properties +  SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, +    const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, +    ELzmaStatus *status, ISzAlloc *alloc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lzma/C/LzmaEnc.c b/src/lzma/C/LzmaEnc.c new file mode 100644 index 0000000..169d4f4 --- /dev/null +++ b/src/lzma/C/LzmaEnc.c @@ -0,0 +1,2268 @@ +/* LzmaEnc.c -- LZMA Encoder +2009-11-24 : Igor Pavlov : Public domain */ + +#include <string.h> + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include <stdio.h> +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifndef _7ZIP_ST +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ +  p->level = 5; +  p->dictSize = p->mc = 0; +  p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; +  p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ +  int level = p->level; +  if (level < 0) level = 5; +  p->level = level; +  if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); +  if (p->lc < 0) p->lc = 3; +  if (p->lp < 0) p->lp = 0; +  if (p->pb < 0) p->pb = 2; +  if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); +  if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); +  if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); +  if (p->numHashBytes < 0) p->numHashBytes = 4; +  if (p->mc == 0)  p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); +  if (p->numThreads < 0) +    p->numThreads = +      #ifndef _7ZIP_ST +      ((p->btMode && p->algo) ? 2 : 1); +      #else +      1; +      #endif +} + +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ +  CLzmaEncProps props = *props2; +  LzmaEncProps_Normalize(&props); +  return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } + +UInt32 GetPosSlot1(UInt32 pos) +{ +  UInt32 res; +  BSR2_RET(pos, res); +  return res; +} +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(Byte *g_FastPos) +{ +  int c = 2, slotFast; +  g_FastPos[0] = 0; +  g_FastPos[1] = 1; +   +  for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) +  { +    UInt32 k = (1 << ((slotFast >> 1) - 1)); +    UInt32 j; +    for (j = 0; j < k; j++, c++) +      g_FastPos[c] = (Byte)slotFast; +  } +} + +#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ +  (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ +  res = p->g_FastPos[pos >> i] + (i * 2); } +/* +#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ +  p->g_FastPos[pos >> 6] + 12 : \ +  p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } +*/ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } + +#endif + + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct +{ +  UInt32 price; + +  CState state; +  int prev1IsChar; +  int prev2; + +  UInt32 posPrev2; +  UInt32 backPrev2; + +  UInt32 posPrev; +  UInt32 backPrev; +  UInt32 backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ +  CLzmaProb choice; +  CLzmaProb choice2; +  CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; +  CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; +  CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ +  CLenEnc p; +  UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; +  UInt32 tableSize; +  UInt32 counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct +{ +  UInt32 range; +  Byte cache; +  UInt64 low; +  UInt64 cacheSize; +  Byte *buf; +  Byte *bufLim; +  Byte *bufBase; +  ISeqOutStream *outStream; +  UInt64 processed; +  SRes res; +} CRangeEnc; + +typedef struct +{ +  CLzmaProb *litProbs; + +  CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; +  CLzmaProb isRep[kNumStates]; +  CLzmaProb isRepG0[kNumStates]; +  CLzmaProb isRepG1[kNumStates]; +  CLzmaProb isRepG2[kNumStates]; +  CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + +  CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; +  CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; +  CLzmaProb posAlignEncoder[1 << kNumAlignBits]; +   +  CLenPriceEnc lenEnc; +  CLenPriceEnc repLenEnc; + +  UInt32 reps[LZMA_NUM_REPS]; +  UInt32 state; +} CSaveState; + +typedef struct +{ +  IMatchFinder matchFinder; +  void *matchFinderObj; + +  #ifndef _7ZIP_ST +  Bool mtMode; +  CMatchFinderMt matchFinderMt; +  #endif + +  CMatchFinder matchFinderBase; + +  #ifndef _7ZIP_ST +  Byte pad[128]; +  #endif +   +  UInt32 optimumEndIndex; +  UInt32 optimumCurrentIndex; + +  UInt32 longestMatchLength; +  UInt32 numPairs; +  UInt32 numAvail; +  COptimal opt[kNumOpts]; +   +  #ifndef LZMA_LOG_BSR +  Byte g_FastPos[1 << kNumLogBits]; +  #endif + +  UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; +  UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; +  UInt32 numFastBytes; +  UInt32 additionalOffset; +  UInt32 reps[LZMA_NUM_REPS]; +  UInt32 state; + +  UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; +  UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; +  UInt32 alignPrices[kAlignTableSize]; +  UInt32 alignPriceCount; + +  UInt32 distTableSize; + +  unsigned lc, lp, pb; +  unsigned lpMask, pbMask; + +  CLzmaProb *litProbs; + +  CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; +  CLzmaProb isRep[kNumStates]; +  CLzmaProb isRepG0[kNumStates]; +  CLzmaProb isRepG1[kNumStates]; +  CLzmaProb isRepG2[kNumStates]; +  CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + +  CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; +  CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; +  CLzmaProb posAlignEncoder[1 << kNumAlignBits]; +   +  CLenPriceEnc lenEnc; +  CLenPriceEnc repLenEnc; + +  unsigned lclp; + +  Bool fastMode; +   +  CRangeEnc rc; + +  Bool writeEndMark; +  UInt64 nowPos64; +  UInt32 matchPriceCount; +  Bool finished; +  Bool multiThread; + +  SRes result; +  UInt32 dictSize; +  UInt32 matchFinderCycles; + +  int needInit; + +  CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  CSaveState *dest = &p->saveState; +  int i; +  dest->lenEnc = p->lenEnc; +  dest->repLenEnc = p->repLenEnc; +  dest->state = p->state; + +  for (i = 0; i < kNumStates; i++) +  { +    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); +    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); +  } +  for (i = 0; i < kNumLenToPosStates; i++) +    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); +  memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); +  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); +  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); +  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); +  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); +  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); +  memcpy(dest->reps, p->reps, sizeof(p->reps)); +  memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ +  CLzmaEnc *dest = (CLzmaEnc *)pp; +  const CSaveState *p = &dest->saveState; +  int i; +  dest->lenEnc = p->lenEnc; +  dest->repLenEnc = p->repLenEnc; +  dest->state = p->state; + +  for (i = 0; i < kNumStates; i++) +  { +    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); +    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); +  } +  for (i = 0; i < kNumLenToPosStates; i++) +    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); +  memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); +  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); +  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); +  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); +  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); +  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); +  memcpy(dest->reps, p->reps, sizeof(p->reps)); +  memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  CLzmaEncProps props = *props2; +  LzmaEncProps_Normalize(&props); + +  if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || +      props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) +    return SZ_ERROR_PARAM; +  p->dictSize = props.dictSize; +  p->matchFinderCycles = props.mc; +  { +    unsigned fb = props.fb; +    if (fb < 5) +      fb = 5; +    if (fb > LZMA_MATCH_LEN_MAX) +      fb = LZMA_MATCH_LEN_MAX; +    p->numFastBytes = fb; +  } +  p->lc = props.lc; +  p->lp = props.lp; +  p->pb = props.pb; +  p->fastMode = (props.algo == 0); +  p->matchFinderBase.btMode = props.btMode; +  { +    UInt32 numHashBytes = 4; +    if (props.btMode) +    { +      if (props.numHashBytes < 2) +        numHashBytes = 2; +      else if (props.numHashBytes < 4) +        numHashBytes = props.numHashBytes; +    } +    p->matchFinderBase.numHashBytes = numHashBytes; +  } + +  p->matchFinderBase.cutValue = props.mc; + +  p->writeEndMark = props.writeEndMark; + +  #ifndef _7ZIP_ST +  /* +  if (newMultiThread != _multiThread) +  { +    ReleaseMatchFinder(); +    _multiThread = newMultiThread; +  } +  */ +  p->multiThread = (props.numThreads > 1); +  #endif + +  return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4,  5,  6,   4, 5}; +static const int kMatchNextStates[kNumStates]   = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates]     = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ +  p->outStream = 0; +  p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) +{ +  if (p->bufBase == 0) +  { +    p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); +    if (p->bufBase == 0) +      return 0; +    p->bufLim = p->bufBase + RC_BUF_SIZE; +  } +  return 1; +} + +static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->bufBase); +  p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ +  /* Stream.Init(); */ +  p->low = 0; +  p->range = 0xFFFFFFFF; +  p->cacheSize = 1; +  p->cache = 0; + +  p->buf = p->bufBase; + +  p->processed = 0; +  p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ +  size_t num; +  if (p->res != SZ_OK) +    return; +  num = p->buf - p->bufBase; +  if (num != p->outStream->Write(p->outStream, p->bufBase, num)) +    p->res = SZ_ERROR_WRITE; +  p->processed += num; +  p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ +  if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) +  { +    Byte temp = p->cache; +    do +    { +      Byte *buf = p->buf; +      *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); +      p->buf = buf; +      if (buf == p->bufLim) +        RangeEnc_FlushStream(p); +      temp = 0xFF; +    } +    while (--p->cacheSize != 0); +    p->cache = (Byte)((UInt32)p->low >> 24); +  } +  p->cacheSize++; +  p->low = (UInt32)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ +  int i; +  for (i = 0; i < 5; i++) +    RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) +{ +  do +  { +    p->range >>= 1; +    p->low += p->range & (0 - ((value >> --numBits) & 1)); +    if (p->range < kTopValue) +    { +      p->range <<= 8; +      RangeEnc_ShiftLow(p); +    } +  } +  while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) +{ +  UInt32 ttt = *prob; +  UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; +  if (symbol == 0) +  { +    p->range = newBound; +    ttt += (kBitModelTotal - ttt) >> kNumMoveBits; +  } +  else +  { +    p->low += newBound; +    p->range -= newBound; +    ttt -= ttt >> kNumMoveBits; +  } +  *prob = (CLzmaProb)ttt; +  if (p->range < kTopValue) +  { +    p->range <<= 8; +    RangeEnc_ShiftLow(p); +  } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) +{ +  symbol |= 0x100; +  do +  { +    RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); +    symbol <<= 1; +  } +  while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) +{ +  UInt32 offs = 0x100; +  symbol |= 0x100; +  do +  { +    matchByte <<= 1; +    RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); +    symbol <<= 1; +    offs &= ~(matchByte ^ symbol); +  } +  while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +{ +  UInt32 i; +  for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) +  { +    const int kCyclesBits = kNumBitPriceShiftBits; +    UInt32 w = i; +    UInt32 bitCount = 0; +    int j; +    for (j = 0; j < kCyclesBits; j++) +    { +      w = w * w; +      bitCount <<= 1; +      while (w >= ((UInt32)1 << 16)) +      { +        w >>= 1; +        bitCount++; +      } +    } +    ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); +  } +} + + +#define GET_PRICE(prob, symbol) \ +  p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ +  ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) +{ +  UInt32 price = 0; +  symbol |= 0x100; +  do +  { +    price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); +    symbol <<= 1; +  } +  while (symbol < 0x10000); +  return price; +} + +static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) +{ +  UInt32 price = 0; +  UInt32 offs = 0x100; +  symbol |= 0x100; +  do +  { +    matchByte <<= 1; +    price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); +    symbol <<= 1; +    offs &= ~(matchByte ^ symbol); +  } +  while (symbol < 0x10000); +  return price; +} + + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ +  UInt32 m = 1; +  int i; +  for (i = numBitLevels; i != 0;) +  { +    UInt32 bit; +    i--; +    bit = (symbol >> i) & 1; +    RangeEnc_EncodeBit(rc, probs + m, bit); +    m = (m << 1) | bit; +  } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) +{ +  UInt32 m = 1; +  int i; +  for (i = 0; i < numBitLevels; i++) +  { +    UInt32 bit = symbol & 1; +    RangeEnc_EncodeBit(rc, probs + m, bit); +    m = (m << 1) | bit; +    symbol >>= 1; +  } +} + +static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ +  UInt32 price = 0; +  symbol |= (1 << numBitLevels); +  while (symbol != 1) +  { +    price += GET_PRICEa(probs[symbol >> 1], symbol & 1); +    symbol >>= 1; +  } +  return price; +} + +static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) +{ +  UInt32 price = 0; +  UInt32 m = 1; +  int i; +  for (i = numBitLevels; i != 0; i--) +  { +    UInt32 bit = symbol & 1; +    symbol >>= 1; +    price += GET_PRICEa(probs[m], bit); +    m = (m << 1) | bit; +  } +  return price; +} + + +static void LenEnc_Init(CLenEnc *p) +{ +  unsigned i; +  p->choice = p->choice2 = kProbInitValue; +  for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) +    p->low[i] = kProbInitValue; +  for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) +    p->mid[i] = kProbInitValue; +  for (i = 0; i < kLenNumHighSymbols; i++) +    p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) +{ +  if (symbol < kLenNumLowSymbols) +  { +    RangeEnc_EncodeBit(rc, &p->choice, 0); +    RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); +  } +  else +  { +    RangeEnc_EncodeBit(rc, &p->choice, 1); +    if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) +    { +      RangeEnc_EncodeBit(rc, &p->choice2, 0); +      RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); +    } +    else +    { +      RangeEnc_EncodeBit(rc, &p->choice2, 1); +      RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); +    } +  } +} + +static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) +{ +  UInt32 a0 = GET_PRICE_0a(p->choice); +  UInt32 a1 = GET_PRICE_1a(p->choice); +  UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); +  UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); +  UInt32 i = 0; +  for (i = 0; i < kLenNumLowSymbols; i++) +  { +    if (i >= numSymbols) +      return; +    prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); +  } +  for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) +  { +    if (i >= numSymbols) +      return; +    prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); +  } +  for (; i < numSymbols; i++) +    prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) +{ +  LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); +  p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) +{ +  UInt32 posState; +  for (posState = 0; posState < numPosStates; posState++) +    LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) +{ +  LenEnc_Encode(&p->p, rc, symbol, posState); +  if (updatePrice) +    if (--p->counters[posState] == 0) +      LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + + + + +static void MovePos(CLzmaEnc *p, UInt32 num) +{ +  #ifdef SHOW_STAT +  ttt += num; +  printf("\n MovePos %d", num); +  #endif +  if (num != 0) +  { +    p->additionalOffset += num; +    p->matchFinder.Skip(p->matchFinderObj, num); +  } +} + +static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) +{ +  UInt32 lenRes = 0, numPairs; +  p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +  numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); +  #ifdef SHOW_STAT +  printf("\n i = %d numPairs = %d    ", ttt, numPairs / 2); +  ttt++; +  { +    UInt32 i; +    for (i = 0; i < numPairs; i += 2) +      printf("%2d %6d   | ", p->matches[i], p->matches[i + 1]); +  } +  #endif +  if (numPairs > 0) +  { +    lenRes = p->matches[numPairs - 2]; +    if (lenRes == p->numFastBytes) +    { +      const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; +      UInt32 distance = p->matches[numPairs - 1] + 1; +      UInt32 numAvail = p->numAvail; +      if (numAvail > LZMA_MATCH_LEN_MAX) +        numAvail = LZMA_MATCH_LEN_MAX; +      { +        const Byte *pby2 = pby - distance; +        for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); +      } +    } +  } +  p->additionalOffset++; +  *numDistancePairsRes = numPairs; +  return lenRes; +} + + +#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; +#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) +{ +  return +    GET_PRICE_0(p->isRepG0[state]) + +    GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) +{ +  UInt32 price; +  if (repIndex == 0) +  { +    price = GET_PRICE_0(p->isRepG0[state]); +    price += GET_PRICE_1(p->isRep0Long[state][posState]); +  } +  else +  { +    price = GET_PRICE_1(p->isRepG0[state]); +    if (repIndex == 1) +      price += GET_PRICE_0(p->isRepG1[state]); +    else +    { +      price += GET_PRICE_1(p->isRepG1[state]); +      price += GET_PRICE(p->isRepG2[state], repIndex - 2); +    } +  } +  return price; +} + +static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) +{ +  return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + +    GetPureRepPrice(p, repIndex, state, posState); +} + +static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) +{ +  UInt32 posMem = p->opt[cur].posPrev; +  UInt32 backMem = p->opt[cur].backPrev; +  p->optimumEndIndex = cur; +  do +  { +    if (p->opt[cur].prev1IsChar) +    { +      MakeAsChar(&p->opt[posMem]) +      p->opt[posMem].posPrev = posMem - 1; +      if (p->opt[cur].prev2) +      { +        p->opt[posMem - 1].prev1IsChar = False; +        p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; +        p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; +      } +    } +    { +      UInt32 posPrev = posMem; +      UInt32 backCur = backMem; +       +      backMem = p->opt[posPrev].backPrev; +      posMem = p->opt[posPrev].posPrev; +       +      p->opt[posPrev].backPrev = backCur; +      p->opt[posPrev].posPrev = cur; +      cur = posPrev; +    } +  } +  while (cur != 0); +  *backRes = p->opt[0].backPrev; +  p->optimumCurrentIndex  = p->opt[0].posPrev; +  return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) +{ +  UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; +  UInt32 matchPrice, repMatchPrice, normalMatchPrice; +  UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; +  UInt32 *matches; +  const Byte *data; +  Byte curByte, matchByte; +  if (p->optimumEndIndex != p->optimumCurrentIndex) +  { +    const COptimal *opt = &p->opt[p->optimumCurrentIndex]; +    UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; +    *backRes = opt->backPrev; +    p->optimumCurrentIndex = opt->posPrev; +    return lenRes; +  } +  p->optimumCurrentIndex = p->optimumEndIndex = 0; +   +  if (p->additionalOffset == 0) +    mainLen = ReadMatchDistances(p, &numPairs); +  else +  { +    mainLen = p->longestMatchLength; +    numPairs = p->numPairs; +  } + +  numAvail = p->numAvail; +  if (numAvail < 2) +  { +    *backRes = (UInt32)(-1); +    return 1; +  } +  if (numAvail > LZMA_MATCH_LEN_MAX) +    numAvail = LZMA_MATCH_LEN_MAX; + +  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; +  repMaxIndex = 0; +  for (i = 0; i < LZMA_NUM_REPS; i++) +  { +    UInt32 lenTest; +    const Byte *data2; +    reps[i] = p->reps[i]; +    data2 = data - (reps[i] + 1); +    if (data[0] != data2[0] || data[1] != data2[1]) +    { +      repLens[i] = 0; +      continue; +    } +    for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); +    repLens[i] = lenTest; +    if (lenTest > repLens[repMaxIndex]) +      repMaxIndex = i; +  } +  if (repLens[repMaxIndex] >= p->numFastBytes) +  { +    UInt32 lenRes; +    *backRes = repMaxIndex; +    lenRes = repLens[repMaxIndex]; +    MovePos(p, lenRes - 1); +    return lenRes; +  } + +  matches = p->matches; +  if (mainLen >= p->numFastBytes) +  { +    *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; +    MovePos(p, mainLen - 1); +    return mainLen; +  } +  curByte = *data; +  matchByte = *(data - (reps[0] + 1)); + +  if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) +  { +    *backRes = (UInt32)-1; +    return 1; +  } + +  p->opt[0].state = (CState)p->state; + +  posState = (position & p->pbMask); + +  { +    const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); +    p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + +        (!IsCharState(p->state) ? +          LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : +          LitEnc_GetPrice(probs, curByte, p->ProbPrices)); +  } + +  MakeAsChar(&p->opt[1]); + +  matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); +  repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + +  if (matchByte == curByte) +  { +    UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); +    if (shortRepPrice < p->opt[1].price) +    { +      p->opt[1].price = shortRepPrice; +      MakeAsShortRep(&p->opt[1]); +    } +  } +  lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + +  if (lenEnd < 2) +  { +    *backRes = p->opt[1].backPrev; +    return 1; +  } + +  p->opt[1].posPrev = 0; +  for (i = 0; i < LZMA_NUM_REPS; i++) +    p->opt[0].backs[i] = reps[i]; + +  len = lenEnd; +  do +    p->opt[len--].price = kInfinityPrice; +  while (len >= 2); + +  for (i = 0; i < LZMA_NUM_REPS; i++) +  { +    UInt32 repLen = repLens[i]; +    UInt32 price; +    if (repLen < 2) +      continue; +    price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); +    do +    { +      UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; +      COptimal *opt = &p->opt[repLen]; +      if (curAndLenPrice < opt->price) +      { +        opt->price = curAndLenPrice; +        opt->posPrev = 0; +        opt->backPrev = i; +        opt->prev1IsChar = False; +      } +    } +    while (--repLen >= 2); +  } + +  normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + +  len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); +  if (len <= mainLen) +  { +    UInt32 offs = 0; +    while (len > matches[offs]) +      offs += 2; +    for (; ; len++) +    { +      COptimal *opt; +      UInt32 distance = matches[offs + 1]; + +      UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; +      UInt32 lenToPosState = GetLenToPosState(len); +      if (distance < kNumFullDistances) +        curAndLenPrice += p->distancesPrices[lenToPosState][distance]; +      else +      { +        UInt32 slot; +        GetPosSlot2(distance, slot); +        curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; +      } +      opt = &p->opt[len]; +      if (curAndLenPrice < opt->price) +      { +        opt->price = curAndLenPrice; +        opt->posPrev = 0; +        opt->backPrev = distance + LZMA_NUM_REPS; +        opt->prev1IsChar = False; +      } +      if (len == matches[offs]) +      { +        offs += 2; +        if (offs == numPairs) +          break; +      } +    } +  } + +  cur = 0; + +    #ifdef SHOW_STAT2 +    if (position >= 0) +    { +      unsigned i; +      printf("\n pos = %4X", position); +      for (i = cur; i <= lenEnd; i++) +      printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); +    } +    #endif + +  for (;;) +  { +    UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; +    UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; +    Bool nextIsChar; +    Byte curByte, matchByte; +    const Byte *data; +    COptimal *curOpt; +    COptimal *nextOpt; + +    cur++; +    if (cur == lenEnd) +      return Backward(p, backRes, cur); + +    newLen = ReadMatchDistances(p, &numPairs); +    if (newLen >= p->numFastBytes) +    { +      p->numPairs = numPairs; +      p->longestMatchLength = newLen; +      return Backward(p, backRes, cur); +    } +    position++; +    curOpt = &p->opt[cur]; +    posPrev = curOpt->posPrev; +    if (curOpt->prev1IsChar) +    { +      posPrev--; +      if (curOpt->prev2) +      { +        state = p->opt[curOpt->posPrev2].state; +        if (curOpt->backPrev2 < LZMA_NUM_REPS) +          state = kRepNextStates[state]; +        else +          state = kMatchNextStates[state]; +      } +      else +        state = p->opt[posPrev].state; +      state = kLiteralNextStates[state]; +    } +    else +      state = p->opt[posPrev].state; +    if (posPrev == cur - 1) +    { +      if (IsShortRep(curOpt)) +        state = kShortRepNextStates[state]; +      else +        state = kLiteralNextStates[state]; +    } +    else +    { +      UInt32 pos; +      const COptimal *prevOpt; +      if (curOpt->prev1IsChar && curOpt->prev2) +      { +        posPrev = curOpt->posPrev2; +        pos = curOpt->backPrev2; +        state = kRepNextStates[state]; +      } +      else +      { +        pos = curOpt->backPrev; +        if (pos < LZMA_NUM_REPS) +          state = kRepNextStates[state]; +        else +          state = kMatchNextStates[state]; +      } +      prevOpt = &p->opt[posPrev]; +      if (pos < LZMA_NUM_REPS) +      { +        UInt32 i; +        reps[0] = prevOpt->backs[pos]; +        for (i = 1; i <= pos; i++) +          reps[i] = prevOpt->backs[i - 1]; +        for (; i < LZMA_NUM_REPS; i++) +          reps[i] = prevOpt->backs[i]; +      } +      else +      { +        UInt32 i; +        reps[0] = (pos - LZMA_NUM_REPS); +        for (i = 1; i < LZMA_NUM_REPS; i++) +          reps[i] = prevOpt->backs[i - 1]; +      } +    } +    curOpt->state = (CState)state; + +    curOpt->backs[0] = reps[0]; +    curOpt->backs[1] = reps[1]; +    curOpt->backs[2] = reps[2]; +    curOpt->backs[3] = reps[3]; + +    curPrice = curOpt->price; +    nextIsChar = False; +    data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; +    curByte = *data; +    matchByte = *(data - (reps[0] + 1)); + +    posState = (position & p->pbMask); + +    curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); +    { +      const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); +      curAnd1Price += +        (!IsCharState(state) ? +          LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : +          LitEnc_GetPrice(probs, curByte, p->ProbPrices)); +    } + +    nextOpt = &p->opt[cur + 1]; + +    if (curAnd1Price < nextOpt->price) +    { +      nextOpt->price = curAnd1Price; +      nextOpt->posPrev = cur; +      MakeAsChar(nextOpt); +      nextIsChar = True; +    } + +    matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); +    repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); +     +    if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) +    { +      UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); +      if (shortRepPrice <= nextOpt->price) +      { +        nextOpt->price = shortRepPrice; +        nextOpt->posPrev = cur; +        MakeAsShortRep(nextOpt); +        nextIsChar = True; +      } +    } +    numAvailFull = p->numAvail; +    { +      UInt32 temp = kNumOpts - 1 - cur; +      if (temp < numAvailFull) +        numAvailFull = temp; +    } + +    if (numAvailFull < 2) +      continue; +    numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + +    if (!nextIsChar && matchByte != curByte) /* speed optimization */ +    { +      /* try Literal + rep0 */ +      UInt32 temp; +      UInt32 lenTest2; +      const Byte *data2 = data - (reps[0] + 1); +      UInt32 limit = p->numFastBytes + 1; +      if (limit > numAvailFull) +        limit = numAvailFull; + +      for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); +      lenTest2 = temp - 1; +      if (lenTest2 >= 2) +      { +        UInt32 state2 = kLiteralNextStates[state]; +        UInt32 posStateNext = (position + 1) & p->pbMask; +        UInt32 nextRepMatchPrice = curAnd1Price + +            GET_PRICE_1(p->isMatch[state2][posStateNext]) + +            GET_PRICE_1(p->isRep[state2]); +        /* for (; lenTest2 >= 2; lenTest2--) */ +        { +          UInt32 curAndLenPrice; +          COptimal *opt; +          UInt32 offset = cur + 1 + lenTest2; +          while (lenEnd < offset) +            p->opt[++lenEnd].price = kInfinityPrice; +          curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); +          opt = &p->opt[offset]; +          if (curAndLenPrice < opt->price) +          { +            opt->price = curAndLenPrice; +            opt->posPrev = cur + 1; +            opt->backPrev = 0; +            opt->prev1IsChar = True; +            opt->prev2 = False; +          } +        } +      } +    } +     +    startLen = 2; /* speed optimization */ +    { +    UInt32 repIndex; +    for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) +    { +      UInt32 lenTest; +      UInt32 lenTestTemp; +      UInt32 price; +      const Byte *data2 = data - (reps[repIndex] + 1); +      if (data[0] != data2[0] || data[1] != data2[1]) +        continue; +      for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); +      while (lenEnd < cur + lenTest) +        p->opt[++lenEnd].price = kInfinityPrice; +      lenTestTemp = lenTest; +      price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); +      do +      { +        UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; +        COptimal *opt = &p->opt[cur + lenTest]; +        if (curAndLenPrice < opt->price) +        { +          opt->price = curAndLenPrice; +          opt->posPrev = cur; +          opt->backPrev = repIndex; +          opt->prev1IsChar = False; +        } +      } +      while (--lenTest >= 2); +      lenTest = lenTestTemp; +       +      if (repIndex == 0) +        startLen = lenTest + 1; +         +      /* if (_maxMode) */ +        { +          UInt32 lenTest2 = lenTest + 1; +          UInt32 limit = lenTest2 + p->numFastBytes; +          UInt32 nextRepMatchPrice; +          if (limit > numAvailFull) +            limit = numAvailFull; +          for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); +          lenTest2 -= lenTest + 1; +          if (lenTest2 >= 2) +          { +            UInt32 state2 = kRepNextStates[state]; +            UInt32 posStateNext = (position + lenTest) & p->pbMask; +            UInt32 curAndLenCharPrice = +                price + p->repLenEnc.prices[posState][lenTest - 2] + +                GET_PRICE_0(p->isMatch[state2][posStateNext]) + +                LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), +                    data[lenTest], data2[lenTest], p->ProbPrices); +            state2 = kLiteralNextStates[state2]; +            posStateNext = (position + lenTest + 1) & p->pbMask; +            nextRepMatchPrice = curAndLenCharPrice + +                GET_PRICE_1(p->isMatch[state2][posStateNext]) + +                GET_PRICE_1(p->isRep[state2]); +             +            /* for (; lenTest2 >= 2; lenTest2--) */ +            { +              UInt32 curAndLenPrice; +              COptimal *opt; +              UInt32 offset = cur + lenTest + 1 + lenTest2; +              while (lenEnd < offset) +                p->opt[++lenEnd].price = kInfinityPrice; +              curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); +              opt = &p->opt[offset]; +              if (curAndLenPrice < opt->price) +              { +                opt->price = curAndLenPrice; +                opt->posPrev = cur + lenTest + 1; +                opt->backPrev = 0; +                opt->prev1IsChar = True; +                opt->prev2 = True; +                opt->posPrev2 = cur; +                opt->backPrev2 = repIndex; +              } +            } +          } +        } +    } +    } +    /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ +    if (newLen > numAvail) +    { +      newLen = numAvail; +      for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); +      matches[numPairs] = newLen; +      numPairs += 2; +    } +    if (newLen >= startLen) +    { +      UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); +      UInt32 offs, curBack, posSlot; +      UInt32 lenTest; +      while (lenEnd < cur + newLen) +        p->opt[++lenEnd].price = kInfinityPrice; + +      offs = 0; +      while (startLen > matches[offs]) +        offs += 2; +      curBack = matches[offs + 1]; +      GetPosSlot2(curBack, posSlot); +      for (lenTest = /*2*/ startLen; ; lenTest++) +      { +        UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; +        UInt32 lenToPosState = GetLenToPosState(lenTest); +        COptimal *opt; +        if (curBack < kNumFullDistances) +          curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; +        else +          curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; +         +        opt = &p->opt[cur + lenTest]; +        if (curAndLenPrice < opt->price) +        { +          opt->price = curAndLenPrice; +          opt->posPrev = cur; +          opt->backPrev = curBack + LZMA_NUM_REPS; +          opt->prev1IsChar = False; +        } + +        if (/*_maxMode && */lenTest == matches[offs]) +        { +          /* Try Match + Literal + Rep0 */ +          const Byte *data2 = data - (curBack + 1); +          UInt32 lenTest2 = lenTest + 1; +          UInt32 limit = lenTest2 + p->numFastBytes; +          UInt32 nextRepMatchPrice; +          if (limit > numAvailFull) +            limit = numAvailFull; +          for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); +          lenTest2 -= lenTest + 1; +          if (lenTest2 >= 2) +          { +            UInt32 state2 = kMatchNextStates[state]; +            UInt32 posStateNext = (position + lenTest) & p->pbMask; +            UInt32 curAndLenCharPrice = curAndLenPrice + +                GET_PRICE_0(p->isMatch[state2][posStateNext]) + +                LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), +                    data[lenTest], data2[lenTest], p->ProbPrices); +            state2 = kLiteralNextStates[state2]; +            posStateNext = (posStateNext + 1) & p->pbMask; +            nextRepMatchPrice = curAndLenCharPrice + +                GET_PRICE_1(p->isMatch[state2][posStateNext]) + +                GET_PRICE_1(p->isRep[state2]); +             +            /* for (; lenTest2 >= 2; lenTest2--) */ +            { +              UInt32 offset = cur + lenTest + 1 + lenTest2; +              UInt32 curAndLenPrice; +              COptimal *opt; +              while (lenEnd < offset) +                p->opt[++lenEnd].price = kInfinityPrice; +              curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); +              opt = &p->opt[offset]; +              if (curAndLenPrice < opt->price) +              { +                opt->price = curAndLenPrice; +                opt->posPrev = cur + lenTest + 1; +                opt->backPrev = 0; +                opt->prev1IsChar = True; +                opt->prev2 = True; +                opt->posPrev2 = cur; +                opt->backPrev2 = curBack + LZMA_NUM_REPS; +              } +            } +          } +          offs += 2; +          if (offs == numPairs) +            break; +          curBack = matches[offs + 1]; +          if (curBack >= kNumFullDistances) +            GetPosSlot2(curBack, posSlot); +        } +      } +    } +  } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) +{ +  UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; +  const Byte *data; +  const UInt32 *matches; + +  if (p->additionalOffset == 0) +    mainLen = ReadMatchDistances(p, &numPairs); +  else +  { +    mainLen = p->longestMatchLength; +    numPairs = p->numPairs; +  } + +  numAvail = p->numAvail; +  *backRes = (UInt32)-1; +  if (numAvail < 2) +    return 1; +  if (numAvail > LZMA_MATCH_LEN_MAX) +    numAvail = LZMA_MATCH_LEN_MAX; +  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + +  repLen = repIndex = 0; +  for (i = 0; i < LZMA_NUM_REPS; i++) +  { +    UInt32 len; +    const Byte *data2 = data - (p->reps[i] + 1); +    if (data[0] != data2[0] || data[1] != data2[1]) +      continue; +    for (len = 2; len < numAvail && data[len] == data2[len]; len++); +    if (len >= p->numFastBytes) +    { +      *backRes = i; +      MovePos(p, len - 1); +      return len; +    } +    if (len > repLen) +    { +      repIndex = i; +      repLen = len; +    } +  } + +  matches = p->matches; +  if (mainLen >= p->numFastBytes) +  { +    *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; +    MovePos(p, mainLen - 1); +    return mainLen; +  } + +  mainDist = 0; /* for GCC */ +  if (mainLen >= 2) +  { +    mainDist = matches[numPairs - 1]; +    while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) +    { +      if (!ChangePair(matches[numPairs - 3], mainDist)) +        break; +      numPairs -= 2; +      mainLen = matches[numPairs - 2]; +      mainDist = matches[numPairs - 1]; +    } +    if (mainLen == 2 && mainDist >= 0x80) +      mainLen = 1; +  } + +  if (repLen >= 2 && ( +        (repLen + 1 >= mainLen) || +        (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || +        (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) +  { +    *backRes = repIndex; +    MovePos(p, repLen - 1); +    return repLen; +  } +   +  if (mainLen < 2 || numAvail <= 2) +    return 1; + +  p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); +  if (p->longestMatchLength >= 2) +  { +    UInt32 newDistance = matches[p->numPairs - 1]; +    if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || +        (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || +        (p->longestMatchLength > mainLen + 1) || +        (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) +      return 1; +  } +   +  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; +  for (i = 0; i < LZMA_NUM_REPS; i++) +  { +    UInt32 len, limit; +    const Byte *data2 = data - (p->reps[i] + 1); +    if (data[0] != data2[0] || data[1] != data2[1]) +      continue; +    limit = mainLen - 1; +    for (len = 2; len < limit && data[len] == data2[len]; len++); +    if (len >= limit) +      return 1; +  } +  *backRes = mainDist + LZMA_NUM_REPS; +  MovePos(p, mainLen - 2); +  return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) +{ +  UInt32 len; +  RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); +  RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); +  p->state = kMatchNextStates[p->state]; +  len = LZMA_MATCH_LEN_MIN; +  LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); +  RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); +  RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); +  RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ +  if (p->result != SZ_OK) +    return p->result; +  if (p->rc.res != SZ_OK) +    p->result = SZ_ERROR_WRITE; +  if (p->matchFinderBase.result != SZ_OK) +    p->result = SZ_ERROR_READ; +  if (p->result != SZ_OK) +    p->finished = True; +  return p->result; +} + +static SRes Flush(CLzmaEnc *p, UInt32 nowPos) +{ +  /* ReleaseMFStream(); */ +  p->finished = True; +  if (p->writeEndMark) +    WriteEndMarker(p, nowPos & p->pbMask); +  RangeEnc_FlushData(&p->rc); +  RangeEnc_FlushStream(&p->rc); +  return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ +  UInt32 i; +  for (i = 0; i < kAlignTableSize; i++) +    p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); +  p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ +  UInt32 tempPrices[kNumFullDistances]; +  UInt32 i, lenToPosState; +  for (i = kStartPosModelIndex; i < kNumFullDistances; i++) +  { +    UInt32 posSlot = GetPosSlot1(i); +    UInt32 footerBits = ((posSlot >> 1) - 1); +    UInt32 base = ((2 | (posSlot & 1)) << footerBits); +    tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); +  } + +  for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) +  { +    UInt32 posSlot; +    const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; +    UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; +    for (posSlot = 0; posSlot < p->distTableSize; posSlot++) +      posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); +    for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) +      posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + +    { +      UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; +      UInt32 i; +      for (i = 0; i < kStartPosModelIndex; i++) +        distancesPrices[i] = posSlotPrices[i]; +      for (; i < kNumFullDistances; i++) +        distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; +    } +  } +  p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ +  RangeEnc_Construct(&p->rc); +  MatchFinder_Construct(&p->matchFinderBase); +  #ifndef _7ZIP_ST +  MatchFinderMt_Construct(&p->matchFinderMt); +  p->matchFinderMt.MatchFinder = &p->matchFinderBase; +  #endif + +  { +    CLzmaEncProps props; +    LzmaEncProps_Init(&props); +    LzmaEnc_SetProps(p, &props); +  } + +  #ifndef LZMA_LOG_BSR +  LzmaEnc_FastPosInit(p->g_FastPos); +  #endif + +  LzmaEnc_InitPriceTables(p->ProbPrices); +  p->litProbs = 0; +  p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +{ +  void *p; +  p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); +  if (p != 0) +    LzmaEnc_Construct((CLzmaEnc *)p); +  return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +{ +  alloc->Free(alloc, p->litProbs); +  alloc->Free(alloc, p->saveState.litProbs); +  p->litProbs = 0; +  p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  #ifndef _7ZIP_ST +  MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); +  #endif +  MatchFinder_Free(&p->matchFinderBase, allocBig); +  LzmaEnc_FreeLits(p, alloc); +  RangeEnc_Free(&p->rc, alloc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); +  alloc->Free(alloc, p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) +{ +  UInt32 nowPos32, startPos32; +  if (p->needInit) +  { +    p->matchFinder.Init(p->matchFinderObj); +    p->needInit = 0; +  } + +  if (p->finished) +    return p->result; +  RINOK(CheckErrors(p)); + +  nowPos32 = (UInt32)p->nowPos64; +  startPos32 = nowPos32; + +  if (p->nowPos64 == 0) +  { +    UInt32 numPairs; +    Byte curByte; +    if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) +      return Flush(p, nowPos32); +    ReadMatchDistances(p, &numPairs); +    RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); +    p->state = kLiteralNextStates[p->state]; +    curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); +    LitEnc_Encode(&p->rc, p->litProbs, curByte); +    p->additionalOffset--; +    nowPos32++; +  } + +  if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) +  for (;;) +  { +    UInt32 pos, len, posState; + +    if (p->fastMode) +      len = GetOptimumFast(p, &pos); +    else +      len = GetOptimum(p, nowPos32, &pos); + +    #ifdef SHOW_STAT2 +    printf("\n pos = %4X,   len = %d   pos = %d", nowPos32, len, pos); +    #endif + +    posState = nowPos32 & p->pbMask; +    if (len == 1 && pos == (UInt32)-1) +    { +      Byte curByte; +      CLzmaProb *probs; +      const Byte *data; + +      RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); +      data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +      curByte = *data; +      probs = LIT_PROBS(nowPos32, *(data - 1)); +      if (IsCharState(p->state)) +        LitEnc_Encode(&p->rc, probs, curByte); +      else +        LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); +      p->state = kLiteralNextStates[p->state]; +    } +    else +    { +      RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); +      if (pos < LZMA_NUM_REPS) +      { +        RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); +        if (pos == 0) +        { +          RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); +          RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); +        } +        else +        { +          UInt32 distance = p->reps[pos]; +          RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); +          if (pos == 1) +            RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); +          else +          { +            RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); +            RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); +            if (pos == 3) +              p->reps[3] = p->reps[2]; +            p->reps[2] = p->reps[1]; +          } +          p->reps[1] = p->reps[0]; +          p->reps[0] = distance; +        } +        if (len == 1) +          p->state = kShortRepNextStates[p->state]; +        else +        { +          LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); +          p->state = kRepNextStates[p->state]; +        } +      } +      else +      { +        UInt32 posSlot; +        RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); +        p->state = kMatchNextStates[p->state]; +        LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); +        pos -= LZMA_NUM_REPS; +        GetPosSlot(pos, posSlot); +        RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); +         +        if (posSlot >= kStartPosModelIndex) +        { +          UInt32 footerBits = ((posSlot >> 1) - 1); +          UInt32 base = ((2 | (posSlot & 1)) << footerBits); +          UInt32 posReduced = pos - base; + +          if (posSlot < kEndPosModelIndex) +            RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); +          else +          { +            RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); +            RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); +            p->alignPriceCount++; +          } +        } +        p->reps[3] = p->reps[2]; +        p->reps[2] = p->reps[1]; +        p->reps[1] = p->reps[0]; +        p->reps[0] = pos; +        p->matchPriceCount++; +      } +    } +    p->additionalOffset -= len; +    nowPos32 += len; +    if (p->additionalOffset == 0) +    { +      UInt32 processed; +      if (!p->fastMode) +      { +        if (p->matchPriceCount >= (1 << 7)) +          FillDistancesPrices(p); +        if (p->alignPriceCount >= kAlignTableSize) +          FillAlignPrices(p); +      } +      if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) +        break; +      processed = nowPos32 - startPos32; +      if (useLimits) +      { +        if (processed + kNumOpts + 300 >= maxUnpackSize || +            RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) +          break; +      } +      else if (processed >= (1 << 15)) +      { +        p->nowPos64 += nowPos32 - startPos32; +        return CheckErrors(p); +      } +    } +  } +  p->nowPos64 += nowPos32 - startPos32; +  return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((UInt32)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  UInt32 beforeSize = kNumOpts; +  Bool btMode; +  if (!RangeEnc_Alloc(&p->rc, alloc)) +    return SZ_ERROR_MEM; +  btMode = (p->matchFinderBase.btMode != 0); +  #ifndef _7ZIP_ST +  p->mtMode = (p->multiThread && !p->fastMode && btMode); +  #endif + +  { +    unsigned lclp = p->lc + p->lp; +    if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) +    { +      LzmaEnc_FreeLits(p, alloc); +      p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); +      p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); +      if (p->litProbs == 0 || p->saveState.litProbs == 0) +      { +        LzmaEnc_FreeLits(p, alloc); +        return SZ_ERROR_MEM; +      } +      p->lclp = lclp; +    } +  } + +  p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + +  if (beforeSize + p->dictSize < keepWindowSize) +    beforeSize = keepWindowSize - p->dictSize; + +  #ifndef _7ZIP_ST +  if (p->mtMode) +  { +    RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); +    p->matchFinderObj = &p->matchFinderMt; +    MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); +  } +  else +  #endif +  { +    if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) +      return SZ_ERROR_MEM; +    p->matchFinderObj = &p->matchFinderBase; +    MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); +  } +  return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ +  UInt32 i; +  p->state = 0; +  for (i = 0 ; i < LZMA_NUM_REPS; i++) +    p->reps[i] = 0; + +  RangeEnc_Init(&p->rc); + + +  for (i = 0; i < kNumStates; i++) +  { +    UInt32 j; +    for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) +    { +      p->isMatch[i][j] = kProbInitValue; +      p->isRep0Long[i][j] = kProbInitValue; +    } +    p->isRep[i] = kProbInitValue; +    p->isRepG0[i] = kProbInitValue; +    p->isRepG1[i] = kProbInitValue; +    p->isRepG2[i] = kProbInitValue; +  } + +  { +    UInt32 num = 0x300 << (p->lp + p->lc); +    for (i = 0; i < num; i++) +      p->litProbs[i] = kProbInitValue; +  } + +  { +    for (i = 0; i < kNumLenToPosStates; i++) +    { +      CLzmaProb *probs = p->posSlotEncoder[i]; +      UInt32 j; +      for (j = 0; j < (1 << kNumPosSlotBits); j++) +        probs[j] = kProbInitValue; +    } +  } +  { +    for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) +      p->posEncoders[i] = kProbInitValue; +  } + +  LenEnc_Init(&p->lenEnc.p); +  LenEnc_Init(&p->repLenEnc.p); + +  for (i = 0; i < (1 << kNumAlignBits); i++) +    p->posAlignEncoder[i] = kProbInitValue; + +  p->optimumEndIndex = 0; +  p->optimumCurrentIndex = 0; +  p->additionalOffset = 0; + +  p->pbMask = (1 << p->pb) - 1; +  p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ +  if (!p->fastMode) +  { +    FillDistancesPrices(p); +    FillAlignPrices(p); +  } + +  p->lenEnc.tableSize = +  p->repLenEnc.tableSize = +      p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; +  LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); +  LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  UInt32 i; +  for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) +    if (p->dictSize <= ((UInt32)1 << i)) +      break; +  p->distTableSize = i * 2; + +  p->finished = False; +  p->result = SZ_OK; +  RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); +  LzmaEnc_Init(p); +  LzmaEnc_InitPrices(p); +  p->nowPos64 = 0; +  return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, +    ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  p->matchFinderBase.stream = inStream; +  p->needInit = 1; +  p->rc.outStream = outStream; +  return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, +    ISeqInStream *inStream, UInt32 keepWindowSize, +    ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  p->matchFinderBase.stream = inStream; +  p->needInit = 1; +  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) +{ +  p->matchFinderBase.directInput = 1; +  p->matchFinderBase.bufferBase = (Byte *)src; +  p->matchFinderBase.directInputRem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, +    UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  LzmaEnc_SetInputBuf(p, src, srcLen); +  p->needInit = 1; + +  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ +  #ifndef _7ZIP_ST +  CLzmaEnc *p = (CLzmaEnc *)pp; +  if (p->mtMode) +    MatchFinderMt_ReleaseStream(&p->matchFinderMt); +  #else +  pp = pp; +  #endif +} + +typedef struct +{ +  ISeqOutStream funcTable; +  Byte *data; +  SizeT rem; +  Bool overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ +  CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; +  if (p->rem < size) +  { +    size = p->rem; +    p->overflow = True; +  } +  memcpy(p->data, data, size); +  p->rem -= size; +  p->data += size; +  return size; +} + + +UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ +  const CLzmaEnc *p = (CLzmaEnc *)pp; +  return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ +  const CLzmaEnc *p = (CLzmaEnc *)pp; +  return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, +    Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  UInt64 nowPos64; +  SRes res; +  CSeqOutStreamBuf outStream; + +  outStream.funcTable.Write = MyWrite; +  outStream.data = dest; +  outStream.rem = *destLen; +  outStream.overflow = False; + +  p->writeEndMark = False; +  p->finished = False; +  p->result = SZ_OK; + +  if (reInit) +    LzmaEnc_Init(p); +  LzmaEnc_InitPrices(p); +  nowPos64 = p->nowPos64; +  RangeEnc_Init(&p->rc); +  p->rc.outStream = &outStream.funcTable; + +  res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); +   +  *unpackSize = (UInt32)(p->nowPos64 - nowPos64); +  *destLen -= outStream.rem; +  if (outStream.overflow) +    return SZ_ERROR_OUTPUT_EOF; + +  return res; +} + +static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) +{ +  SRes res = SZ_OK; + +  #ifndef _7ZIP_ST +  Byte allocaDummy[0x300]; +  int i = 0; +  for (i = 0; i < 16; i++) +    allocaDummy[i] = (Byte)i; +  #endif + +  for (;;) +  { +    res = LzmaEnc_CodeOneBlock(p, False, 0, 0); +    if (res != SZ_OK || p->finished != 0) +      break; +    if (progress != 0) +    { +      res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); +      if (res != SZ_OK) +      { +        res = SZ_ERROR_PROGRESS; +        break; +      } +    } +  } +  LzmaEnc_Finish(p); +  return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, +    ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); +  return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) +{ +  CLzmaEnc *p = (CLzmaEnc *)pp; +  int i; +  UInt32 dictSize = p->dictSize; +  if (*size < LZMA_PROPS_SIZE) +    return SZ_ERROR_PARAM; +  *size = LZMA_PROPS_SIZE; +  props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); + +  for (i = 11; i <= 30; i++) +  { +    if (dictSize <= ((UInt32)2 << i)) +    { +      dictSize = (2 << i); +      break; +    } +    if (dictSize <= ((UInt32)3 << i)) +    { +      dictSize = (3 << i); +      break; +    } +  } + +  for (i = 0; i < 4; i++) +    props[1 + i] = (Byte)(dictSize >> (8 * i)); +  return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +    int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  SRes res; +  CLzmaEnc *p = (CLzmaEnc *)pp; + +  CSeqOutStreamBuf outStream; + +  LzmaEnc_SetInputBuf(p, src, srcLen); + +  outStream.funcTable.Write = MyWrite; +  outStream.data = dest; +  outStream.rem = *destLen; +  outStream.overflow = False; + +  p->writeEndMark = writeEndMark; + +  p->rc.outStream = &outStream.funcTable; +  res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); +  if (res == SZ_OK) +    res = LzmaEnc_Encode2(p, progress); + +  *destLen -= outStream.rem; +  if (outStream.overflow) +    return SZ_ERROR_OUTPUT_EOF; +  return res; +} + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, +    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) +{ +  CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); +  SRes res; +  if (p == 0) +    return SZ_ERROR_MEM; + +  res = LzmaEnc_SetProps(p, props); +  if (res == SZ_OK) +  { +    res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); +    if (res == SZ_OK) +      res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, +          writeEndMark, progress, alloc, allocBig); +  } + +  LzmaEnc_Destroy(p, alloc, allocBig); +  return res; +} diff --git a/src/lzma/C/LzmaEnc.h b/src/lzma/C/LzmaEnc.h new file mode 100644 index 0000000..200d60e --- /dev/null +++ b/src/lzma/C/LzmaEnc.h @@ -0,0 +1,80 @@ +/*  LzmaEnc.h -- LZMA Encoder +2009-02-07 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_ENC_H +#define __LZMA_ENC_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ +  int level;       /*  0 <= level <= 9 */ +  UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version +                      (1 << 12) <= dictSize <= (1 << 30) for 64-bit version +                       default = (1 << 24) */ +  int lc;          /* 0 <= lc <= 8, default = 3 */ +  int lp;          /* 0 <= lp <= 4, default = 0 */ +  int pb;          /* 0 <= pb <= 4, default = 2 */ +  int algo;        /* 0 - fast, 1 - normal, default = 1 */ +  int fb;          /* 5 <= fb <= 273, default = 32 */ +  int btMode;      /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ +  int numHashBytes; /* 2, 3 or 4, default = 4 */ +  UInt32 mc;        /* 1 <= mc <= (1 << 30), default = 32 */ +  unsigned writeEndMark;  /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ +  int numThreads;  /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: +Returns: +  SZ_OK           - OK +  SZ_ERROR_MEM    - Memory allocation error +  SZ_ERROR_PARAM  - Incorrect paramater in props +  SZ_ERROR_WRITE  - Write callback error. +  SZ_ERROR_PROGRESS - some break from progress callback +  SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +typedef void * CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, +    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +    int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode +Return code: +  SZ_OK               - OK +  SZ_ERROR_MEM        - Memory allocation error +  SZ_ERROR_PARAM      - Incorrect paramater +  SZ_ERROR_OUTPUT_EOF - output buffer overflow +  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version) +*/ + +SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, +    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, +    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lzma/C/Threads.c b/src/lzma/C/Threads.c new file mode 100644 index 0000000..7af1da2 --- /dev/null +++ b/src/lzma/C/Threads.c @@ -0,0 +1,84 @@ +/* Threads.c -- multithreading library +2009-09-20 : Igor Pavlov : Public domain */ + +#ifndef _WIN32_WCE +#include <process.h> +#endif + +#include "Threads.h" + +static WRes GetError() +{ +  DWORD res = GetLastError(); +  return (res) ? (WRes)(res) : 1; +} + +WRes HandleToWRes(HANDLE h) { return (h != 0) ? 0 : GetError(); } +WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); } + +WRes HandlePtr_Close(HANDLE *p) +{ +  if (*p != NULL) +    if (!CloseHandle(*p)) +      return GetError(); +  *p = NULL; +  return 0; +} + +WRes Handle_WaitObject(HANDLE h) { return (WRes)WaitForSingleObject(h, INFINITE); } + +WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param) +{ +  unsigned threadId; /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */ +  *p = +    #ifdef UNDER_CE +    CreateThread(0, 0, func, param, 0, &threadId); +    #else +    (HANDLE)_beginthreadex(NULL, 0, func, param, 0, &threadId); +    #endif +    /* maybe we must use errno here, but probably GetLastError() is also OK. */ +  return HandleToWRes(*p); +} + +WRes Event_Create(CEvent *p, BOOL manualReset, int signaled) +{ +  *p = CreateEvent(NULL, manualReset, (signaled ? TRUE : FALSE), NULL); +  return HandleToWRes(*p); +} + +WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(*p)); } +WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(*p)); } + +WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled) { return Event_Create(p, TRUE, signaled); } +WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled) { return Event_Create(p, FALSE, signaled); } +WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p) { return ManualResetEvent_Create(p, 0); } +WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p) { return AutoResetEvent_Create(p, 0); } + + +WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount) +{ +  *p = CreateSemaphore(NULL, (LONG)initCount, (LONG)maxCount, NULL); +  return HandleToWRes(*p); +} + +static WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount) +  { return BOOLToWRes(ReleaseSemaphore(*p, releaseCount, previousCount)); } +WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num) +  { return Semaphore_Release(p, (LONG)num, NULL); } +WRes Semaphore_Release1(CSemaphore *p) { return Semaphore_ReleaseN(p, 1); } + +WRes CriticalSection_Init(CCriticalSection *p) +{ +  /* InitializeCriticalSection can raise only STATUS_NO_MEMORY exception */ +  #ifdef _MSC_VER +  __try +  #endif +  { +    InitializeCriticalSection(p); +    /* InitializeCriticalSectionAndSpinCount(p, 0); */ +  } +  #ifdef _MSC_VER +  __except (EXCEPTION_EXECUTE_HANDLER) { return 1; } +  #endif +  return 0; +} diff --git a/src/lzma/C/Threads.h b/src/lzma/C/Threads.h new file mode 100644 index 0000000..d0ddd80 --- /dev/null +++ b/src/lzma/C/Threads.h @@ -0,0 +1,59 @@ +/* Threads.h -- multithreading library +2009-03-27 : Igor Pavlov : Public domain */ + +#ifndef __7Z_THREADS_H +#define __7Z_THREADS_H + +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +WRes HandlePtr_Close(HANDLE *h); +WRes Handle_WaitObject(HANDLE h); + +typedef HANDLE CThread; +#define Thread_Construct(p) *(p) = NULL +#define Thread_WasCreated(p) (*(p) != NULL) +#define Thread_Close(p) HandlePtr_Close(p) +#define Thread_Wait(p) Handle_WaitObject(*(p)) +typedef unsigned THREAD_FUNC_RET_TYPE; +#define THREAD_FUNC_CALL_TYPE MY_STD_CALL +#define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE +typedef THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE * THREAD_FUNC_TYPE)(void *); +WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param); + +typedef HANDLE CEvent; +typedef CEvent CAutoResetEvent; +typedef CEvent CManualResetEvent; +#define Event_Construct(p) *(p) = NULL +#define Event_IsCreated(p) (*(p) != NULL) +#define Event_Close(p) HandlePtr_Close(p) +#define Event_Wait(p) Handle_WaitObject(*(p)) +WRes Event_Set(CEvent *p); +WRes Event_Reset(CEvent *p); +WRes ManualResetEvent_Create(CManualResetEvent *p, int signaled); +WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p); +WRes AutoResetEvent_Create(CAutoResetEvent *p, int signaled); +WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p); + +typedef HANDLE CSemaphore; +#define Semaphore_Construct(p) (*p) = NULL +#define Semaphore_Close(p) HandlePtr_Close(p) +#define Semaphore_Wait(p) Handle_WaitObject(*(p)) +WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount); +WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num); +WRes Semaphore_Release1(CSemaphore *p); + +typedef CRITICAL_SECTION CCriticalSection; +WRes CriticalSection_Init(CCriticalSection *p); +#define CriticalSection_Delete(p) DeleteCriticalSection(p) +#define CriticalSection_Enter(p) EnterCriticalSection(p) +#define CriticalSection_Leave(p) LeaveCriticalSection(p) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lzma/C/Types.h b/src/lzma/C/Types.h new file mode 100644 index 0000000..0526cb4 --- /dev/null +++ b/src/lzma/C/Types.h @@ -0,0 +1,236 @@ +/* Types.h -- Basic types +2010-03-11 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#include <stddef.h> + +#ifdef _WIN32 +#include <windows.h> +#endif + +#ifndef EXTERN_C_BEGIN +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +#endif + +EXTERN_C_BEGIN + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +typedef DWORD WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. +   NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _WIN32 +#define MY_STD_CALL __stdcall +#else +#define MY_STD_CALL +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_FAST_CALL __fastcall + +#else + +#define MY_CDECL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ +  Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ +} IByteIn; + +typedef struct +{ +  void (*Write)(void *p, Byte b); +} IByteOut; + +typedef struct +{ +  SRes (*Read)(void *p, void *buf, size_t *size); +    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. +       (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +/* it can return SZ_ERROR_INPUT_EOF */ +SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); + +typedef struct +{ +  size_t (*Write)(void *p, const void *buf, size_t size); +    /* Returns: result - the number of actually written bytes. +       (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ +  SZ_SEEK_SET = 0, +  SZ_SEEK_CUR = 1, +  SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ +  SRes (*Read)(void *p, void *buf, size_t *size);  /* same as ISeqInStream::Read */ +  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ +  SRes (*Look)(void *p, const void **buf, size_t *size); +    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. +       (output(*size) > input(*size)) is not allowed +       (output(*size) < input(*size)) is allowed */ +  SRes (*Skip)(void *p, size_t offset); +    /* offset must be <= output(*size) of Look */ + +  SRes (*Read)(void *p, void *buf, size_t *size); +    /* reads directly (without buffer). It's same as ISeqInStream::Read */ +  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ +  ILookInStream s; +  ISeekInStream *realStream; +  size_t pos; +  size_t size; +  Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +void LookToRead_Init(CLookToRead *p); + +typedef struct +{ +  ISeqInStream s; +  ILookInStream *realStream; +} CSecToLook; + +void SecToLook_CreateVTable(CSecToLook *p); + +typedef struct +{ +  ISeqInStream s; +  ILookInStream *realStream; +} CSecToRead; + +void SecToRead_CreateVTable(CSecToRead *p); + +typedef struct +{ +  SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); +    /* Returns: result. (result != SZ_OK) means break. +       Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ +  void *(*Alloc)(void *p, size_t size); +  void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +EXTERN_C_END + +#endif diff --git a/src/lzma/info.txt b/src/lzma/info.txt new file mode 100644 index 0000000..4cee86e --- /dev/null +++ b/src/lzma/info.txt @@ -0,0 +1 @@ +Taken from LZMA SDK v 9.11
\ No newline at end of file diff --git a/src/pklib/crc32.c b/src/pklib/crc32.c new file mode 100644 index 0000000..cd47b1d --- /dev/null +++ b/src/pklib/crc32.c @@ -0,0 +1,66 @@ +/*****************************************************************************/ +/* crc32.c                                Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Pkware Data Compression Library Version 1.11                              */ +/* Dissassembled method crc32 - cdecl version                                */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 09.04.03  1.00  Lad  The first version of crc32.c                         */ +/* 02.05.03  1.00  Lad  Stress test done                                     */ +/*****************************************************************************/ + +#include "pklib.h" + +static unsigned long crc_table[] = +{ +    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, +    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, +    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, +    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, +    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, +    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, +    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, +    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, +    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, +    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, +    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, +    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, +    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, +    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, +    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, +    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, +    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, +    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, +    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, +    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, +    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, +    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, +    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, +    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, +    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, +    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, +    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, +    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, +    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, +    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, +    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, +    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + + +unsigned long PKEXPORT crc32_pklib(char * buffer, unsigned int * psize, unsigned long * old_crc) +{ +    unsigned int  size = *psize; +    unsigned long ch; +    unsigned long crc_value = *old_crc; + +    while(size-- != 0) +    { +        ch = *buffer++ ^ (char)crc_value; +        crc_value >>= 8; + +        crc_value = crc_table[ch & 0x0FF] ^ crc_value; +    } +    return crc_value; +} diff --git a/src/pklib/explode.c b/src/pklib/explode.c new file mode 100644 index 0000000..73c5004 --- /dev/null +++ b/src/pklib/explode.c @@ -0,0 +1,522 @@ +/*****************************************************************************/ +/* explode.c                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Implode function of PKWARE Data Compression library                       */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 11.03.03  1.00  Lad  Splitted from Pkware.cpp                             */ +/* 08.04.03  1.01  Lad  Renamed to explode.c to be compatible with pklib     */ +/* 02.05.03  1.01  Lad  Stress test done                                     */ +/* 22.04.10  1.01  Lad  Documented                                           */ +/*****************************************************************************/ + +#include <assert.h> +#include <string.h> + +#include "pklib.h" + +#define PKDCL_OK                    0        +#define PKDCL_STREAM_END            1   // All data from the input stream is read +#define PKDCL_NEED_DICT             2   // Need more data (dictionary) +#define PKDCL_CONTINUE             10   // Internal flag, not returned to user +#define PKDCL_GET_INPUT            11   // Internal flag, not returned to user + +char CopyrightPkware[] = "PKWARE Data Compression Library for Win32\r\n" +                         "Copyright 1989-1995 PKWARE Inc.  All Rights Reserved\r\n" +                         "Patent No. 5,051,745\r\n" +                         "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" +                         "Version 1.11\r\n"; + +//----------------------------------------------------------------------------- +// Tables + +static unsigned char DistBits[] =  +{ +    0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, +    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +static unsigned char DistCode[] =  +{ +    0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, +    0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, +    0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, +    0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 +}; + +static unsigned char ExLenBits[] = +{ +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 +}; + +static unsigned short LenBase[] = +{ +    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, +    0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106 +}; + +static unsigned char LenBits[] = +{ +    0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 +}; + +static unsigned char LenCode[] = +{ +    0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 +}; + +static unsigned char ChBitsAsc[] = +{ +    0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, +    0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, +    0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, +    0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, +    0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, +    0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, +    0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D +}; + +static unsigned short ChCodeAsc[] =  +{ +    0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, +    0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, +    0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, +    0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, +    0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, +    0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, +    0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, +    0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, +    0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, +    0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, +    0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, +    0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, +    0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, +    0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, +    0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, +    0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, +    0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, +    0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, +    0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, +    0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, +    0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, +    0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, +    0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, +    0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, +    0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, +    0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, +    0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, +    0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, +    0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, +    0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, +    0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, +    0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000   +}; + +//----------------------------------------------------------------------------- +// Local functions + +static void GenDecodeTabs( +    unsigned char * positions,          // [out] Table of positions +    unsigned char * start_indexes,      // [in] Table of start indexes +    unsigned char * length_bits,        // [in] Table of lengths. Each length is stored as number of bits +    size_t elements)                    // [in] Number of elements in start_indexes and length_bits +{ +    unsigned int index; +    unsigned int length; +    size_t i; + +    for(i = 0; i < elements; i++) +    { +        length = 1 << length_bits[i];   // Get the length in bytes + +        for(index = start_indexes[i]; index < 0x100; index += length) +        { +            positions[index] = (unsigned char)i; +        } +    } +} + +static void GenAscTabs(TDcmpStruct * pWork) +{ +    unsigned short * pChCodeAsc = &ChCodeAsc[0xFF]; +    unsigned int  acc, add; +    unsigned short count; + +    for(count = 0x00FF; pChCodeAsc >= ChCodeAsc; pChCodeAsc--, count--) +    { +        unsigned char * pChBitsAsc = pWork->ChBitsAsc + count; +        unsigned char bits_asc = *pChBitsAsc; + +        if(bits_asc <= 8) +        { +            add = (1 << bits_asc); +            acc = *pChCodeAsc; + +            do +            { +                pWork->offs2C34[acc] = (unsigned char)count; +                acc += add; +            } +            while(acc < 0x100); +        } +        else if((acc = (*pChCodeAsc & 0xFF)) != 0) +        { +            pWork->offs2C34[acc] = 0xFF; + +            if(*pChCodeAsc & 0x3F) +            { +                bits_asc -= 4; +                *pChBitsAsc = bits_asc; + +                add = (1 << bits_asc); +                acc = *pChCodeAsc >> 4; +                do +                { +                    pWork->offs2D34[acc] = (unsigned char)count; +                    acc += add; +                } +                while(acc < 0x100); +            } +            else +            { +                bits_asc -= 6; +                *pChBitsAsc = bits_asc; + +                add = (1 << bits_asc); +                acc = *pChCodeAsc >> 6; +                do +                { +                    pWork->offs2E34[acc] = (unsigned char)count; +                    acc += add; +                } +                while(acc < 0x80); +            } +        } +        else +        { +            bits_asc -= 8; +            *pChBitsAsc = bits_asc; + +            add = (1 << bits_asc); +            acc = *pChCodeAsc >> 8; +            do +            { +                pWork->offs2EB4[acc] = (unsigned char)count; +                acc += add; +            } +            while(acc < 0x100); +        } +    } +} + +//----------------------------------------------------------------------------- +// Removes given number of bits in the bit buffer. New bits are reloaded from +// the input buffer, if needed. +// Returns: PKDCL_OK:         Operation was successful +//          PKDCL_STREAM_END: There are no more bits in the input buffer + +static int WasteBits(TDcmpStruct * pWork, unsigned int nBits) +{ +    // If number of bits required is less than number of (bits in the buffer) ? +    if(nBits <= pWork->extra_bits) +    { +        pWork->extra_bits -= nBits; +        pWork->bit_buff  >>= nBits; +        return PKDCL_OK; +    } + +    // Load input buffer if necessary +    pWork->bit_buff >>= pWork->extra_bits; +    if(pWork->in_pos == pWork->in_bytes) +    { +        pWork->in_pos = sizeof(pWork->in_buff); +        if((pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param)) == 0) +            return PKDCL_STREAM_END; +        pWork->in_pos = 0; +    } + +    // Update bit buffer +    pWork->bit_buff  |= (pWork->in_buff[pWork->in_pos++] << 8); +    pWork->bit_buff >>= (nBits - pWork->extra_bits); +    pWork->extra_bits = (pWork->extra_bits - nBits) + 8; +    return PKDCL_OK; +} + +//----------------------------------------------------------------------------- +// Decodes next literal from the input (compressed) data. +// Returns : 0x000: One byte 0x00 +//           0x001: One byte 0x01 +//           ... +//           0x0FF: One byte 0xFF +//           0x100: Repetition, length of 0x02 bytes +//           0x101: Repetition, length of 0x03 bytes +//           ... +//           0x304: Repetition, length of 0x206 bytes +//           0x305: End of stream +//           0x306: Error + +static unsigned int DecodeLit(TDcmpStruct * pWork) +{ +    unsigned int extra_length_bits;    // Number of bits of extra literal length +    unsigned int length_code;          // Length code +    unsigned int value; + +    // Test the current bit in byte buffer. If is not set, simply return the next 8 bits. +    if(pWork->bit_buff & 1) +    { +        // Remove one bit from the input data +        if(WasteBits(pWork, 1)) +            return 0x306;    +                       +        // The next 8 bits hold the index to the length code table +        length_code = pWork->LengthCodes[pWork->bit_buff & 0xFF]; +         +        // Remove the apropriate number of bits +        if(WasteBits(pWork, pWork->LenBits[length_code])) +            return 0x306; + +        // Are there some extra bits for the obtained length code ? +        if((extra_length_bits = pWork->ExLenBits[length_code]) != 0) +        { +            unsigned int extra_length = pWork->bit_buff & ((1 << extra_length_bits) - 1); + +            if(WasteBits(pWork, extra_length_bits)) +            { +                if((length_code + extra_length) != 0x10E) +                    return 0x306; +            } +            length_code = pWork->LenBase[length_code] + extra_length; +        } + +        // In order to distinguish uncompressed byte from repetition length, +        // we have to add 0x100 to the length. +        return length_code + 0x100; +    } + +    // Remove one bit from the input data +    if(WasteBits(pWork, 1)) +        return 0x306; + +    // If the binary compression type, read 8 bits and return them as one byte. +    if(pWork->ctype == CMP_BINARY) +    { +        unsigned int uncompressed_byte = pWork->bit_buff & 0xFF; + +        if(WasteBits(pWork, 8)) +            return 0x306; +        return uncompressed_byte; +    } + +    // When ASCII compression ... +    if(pWork->bit_buff & 0xFF) +    { +        value = pWork->offs2C34[pWork->bit_buff & 0xFF]; + +        if(value == 0xFF) +        { +            if(pWork->bit_buff & 0x3F) +            { +                if(WasteBits(pWork, 4)) +                    return 0x306; + +                value = pWork->offs2D34[pWork->bit_buff & 0xFF]; +            } +            else +            { +                if(WasteBits(pWork, 6)) +                    return 0x306; + +                value = pWork->offs2E34[pWork->bit_buff & 0x7F]; +            } +        } +    } +    else +    { +        if(WasteBits(pWork, 8)) +            return 0x306; + +        value = pWork->offs2EB4[pWork->bit_buff & 0xFF]; +    } + +    return WasteBits(pWork, pWork->ChBitsAsc[value]) ? 0x306 : value; +} + +//----------------------------------------------------------------------------- +// Decodes the distance of the repetition, backwards relative to the +// current output buffer position + +static unsigned int DecodeDist(TDcmpStruct * pWork, unsigned int rep_length) +{ +    unsigned int dist_pos_code;            // Distance position code +    unsigned int dist_pos_bits;            // Number of bits of distance position +    unsigned int distance;                 // Distance position + +    // Next 2-8 bits in the input buffer is the distance position code +    dist_pos_code = pWork->DistPosCodes[pWork->bit_buff & 0xFF]; +    dist_pos_bits = pWork->DistBits[dist_pos_code]; +    if(WasteBits(pWork, dist_pos_bits)) +        return 0; + +    if(rep_length == 2) +    { +        // If the repetition is only 2 bytes length, +        // then take 2 bits from the stream in order to get the distance +        distance = (dist_pos_code << 2) | (pWork->bit_buff & 0x03); +        if(WasteBits(pWork, 2)) +            return 0; +    } +    else +    { +        // If the repetition is more than 2 bytes length, +        // then take "dsize_bits" bits in order to get the distance +        distance = (dist_pos_code << pWork->dsize_bits) | (pWork->bit_buff & pWork->dsize_mask); +        if(WasteBits(pWork, pWork->dsize_bits)) +            return 0; +    } +    return distance + 1; +} + +static unsigned int Expand(TDcmpStruct * pWork) +{ +    unsigned int next_literal;         // Literal decoded from the compressed data +    unsigned int result;               // Value to be returned +    unsigned int copyBytes;             // Number of bytes to copy to the output buffer + +    pWork->outputPos = 0x1000;          // Initialize output buffer position + +    // Decode the next literal from the input data. +    // The returned literal can either be an uncompressed byte (next_literal < 0x100) +    // or an encoded length of the repeating byte sequence that +    // is to be copied to the current buffer position +    while((result = next_literal = DecodeLit(pWork)) < 0x305) +    { +        // If the literal is greater than 0x100, it holds length +        // of repeating byte sequence +        // literal of 0x100 means repeating sequence of 0x2 bytes +        // literal of 0x101 means repeating sequence of 0x3 bytes +        // ... +        // literal of 0x305 means repeating sequence of 0x207 bytes +        if(next_literal >= 0x100) +        { +            unsigned char * source; +            unsigned char * target; +            unsigned int rep_length;       // Length of the repetition, in bytes +            unsigned int minus_dist;       // Backward distance to the repetition, relative to the current buffer position + +            // Get the length of the repeating sequence. +            // Note that the repeating block may overlap the current output position, +            // for example if there was a sequence of equal bytes +            rep_length = next_literal - 0xFE; + +            // Get backward distance to the repetition +            if((minus_dist = DecodeDist(pWork, rep_length)) == 0) +            { +                result = 0x306; +                break; +            } + +            // Target and source pointer +            target = &pWork->out_buff[pWork->outputPos]; +            source = target - minus_dist; + +            // Update buffer output position +            pWork->outputPos += rep_length; + +            // Copy the repeating sequence +            while(rep_length-- > 0) +                *target++ = *source++; +        } +        else +        { +            pWork->out_buff[pWork->outputPos++] = (unsigned char)next_literal; +        } +     +        // Flush the output buffer, if number of extracted bytes has reached the end +        if(pWork->outputPos >= 0x2000) +        { +            // Copy decompressed data into user buffer +            copyBytes = 0x1000; +            pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); + +            // Now copy the decompressed data to the first half of the buffer. +            // This is needed because the decompression might reuse them as repetitions. +            // Note that if the output buffer overflowed previously, the extra decompressed bytes +            // are stored in "out_buff_overflow", and they will now be +            // within decompressed part of the output buffer. +            memcpy(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000); +            pWork->outputPos -= 0x1000; +        } +    } + +    // Flush any remaining decompressed bytes +    copyBytes = pWork->outputPos - 0x1000; +    pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); +    return result; +} + + +//----------------------------------------------------------------------------- +// Main exploding function. + +unsigned int explode( +        unsigned int (*read_buf)(char *buf, unsigned  int *size, void *param), +        void         (*write_buf)(char *buf, unsigned  int *size, void *param), +        char         *work_buf, +        void         *param) +{ +    TDcmpStruct * pWork = (TDcmpStruct *)work_buf; + +    // Initialize work struct and load compressed data +    // Note: The caller must zero the "work_buff" before passing it to explode +    pWork->read_buf   = read_buf; +    pWork->write_buf  = write_buf; +    pWork->param      = param; +    pWork->in_pos     = sizeof(pWork->in_buff); +    pWork->in_bytes   = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param); +    if(pWork->in_bytes <= 4) +        return CMP_BAD_DATA; + +    pWork->ctype      = pWork->in_buff[0]; // Get the compression type (CMP_BINARY or CMP_ASCII) +    pWork->dsize_bits = pWork->in_buff[1]; // Get the dictionary size +    pWork->bit_buff   = pWork->in_buff[2]; // Initialize 16-bit bit buffer +    pWork->extra_bits = 0;                 // Extra (over 8) bits +    pWork->in_pos     = 3;                 // Position in input buffer + +    // Test for the valid dictionary size +    if(4 > pWork->dsize_bits || pWork->dsize_bits > 6)  +        return CMP_INVALID_DICTSIZE; + +    pWork->dsize_mask = 0xFFFF >> (0x10 - pWork->dsize_bits); // Shifted by 'sar' instruction + +    if(pWork->ctype != CMP_BINARY) +    { +        if(pWork->ctype != CMP_ASCII) +            return CMP_INVALID_MODE; + +        memcpy(pWork->ChBitsAsc, ChBitsAsc, sizeof(pWork->ChBitsAsc)); +        GenAscTabs(pWork); +    } + +    memcpy(pWork->LenBits, LenBits, sizeof(pWork->LenBits)); +    GenDecodeTabs(pWork->LengthCodes, LenCode, pWork->LenBits, sizeof(pWork->LenBits)); +    memcpy(pWork->ExLenBits, ExLenBits, sizeof(pWork->ExLenBits)); +    memcpy(pWork->LenBase, LenBase, sizeof(pWork->LenBase)); +    memcpy(pWork->DistBits, DistBits, sizeof(pWork->DistBits)); +    GenDecodeTabs(pWork->DistPosCodes, DistCode, pWork->DistBits, sizeof(pWork->DistBits)); +    if(Expand(pWork) != 0x306) +        return CMP_NO_ERROR; +         +    return CMP_ABORT; +} diff --git a/src/pklib/implode.c b/src/pklib/implode.c new file mode 100644 index 0000000..1771b18 --- /dev/null +++ b/src/pklib/implode.c @@ -0,0 +1,769 @@ +/*****************************************************************************/ +/* implode.c                              Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Implode function of PKWARE Data Compression library                       */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 11.04.03  1.00  Lad  First version of implode.c                           */ +/* 02.05.03  1.00  Lad  Stress test done                                     */ +/* 22.04.10  1.01  Lad  Documented                                           */ +/*****************************************************************************/ + +#include <assert.h> +#include <string.h> + +#include "pklib.h" + +#if ((1200 < _MSC_VER) && (_MSC_VER < 1400)) +#pragma optimize("", off)               // Fucking Microsoft VS.NET 2003 compiler !!! (_MSC_VER=1310) +#endif + +//----------------------------------------------------------------------------- +// Defines + +#define MAX_REP_LENGTH 0x204            // The longest allowed repetition + +//----------------------------------------------------------------------------- +// Tables + +static unsigned char DistBits[] =  +{ +    0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, +    0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +    0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +static unsigned char DistCode[] =  +{ +    0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, +    0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, +    0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, +    0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 +}; + +static unsigned char ExLenBits[] = +{ +    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 +}; + +static unsigned char LenBits[] = +{ +    0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 +}; + +static unsigned char LenCode[] = +{ +    0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 +}; + +static unsigned char ChBitsAsc[] = +{ +    0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, +    0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, +    0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, +    0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, +    0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, +    0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, +    0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, +    0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D +}; + +static unsigned short ChCodeAsc[] =  +{ +    0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, +    0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, +    0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, +    0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, +    0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, +    0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, +    0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, +    0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, +    0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, +    0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, +    0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, +    0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, +    0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, +    0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, +    0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, +    0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, +    0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, +    0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, +    0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, +    0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, +    0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, +    0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, +    0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, +    0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, +    0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, +    0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, +    0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, +    0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, +    0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, +    0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, +    0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, +    0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000   +}; + +//----------------------------------------------------------------------------- +// Macros + +// Macro for calculating hash of the current byte pair. +// Note that most exact byte pair hash would be buffer[0] + buffer[1] << 0x08, +// but even this way gives nice indication of equal byte pairs, with significantly +// smaller size of the array that holds numbers of those hashes +#define BYTE_PAIR_HASH(buffer)   ((buffer[0] * 4) + (buffer[1] * 5)) + +//----------------------------------------------------------------------------- +// Local functions + +// Builds the "hash_to_index" table and "pair_hash_offsets" table. +// Every element of "hash_to_index" will contain lowest index to the +// "pair_hash_offsets" table, effectively giving offset of the first +// occurence of the given PAIR_HASH in the input data. +static void SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigned char * buffer_end) +{ +    unsigned short * phash_to_index; +    unsigned char  * buffer_ptr; +    unsigned short total_sum = 0; +    unsigned long byte_pair_hash;           // Hash value of the byte pair +    unsigned short byte_pair_offs;          // Offset of the byte pair, relative to "work_buff" + +    // Zero the entire "phash_to_index" table +    memset(pWork->phash_to_index, 0, sizeof(pWork->phash_to_index)); +     +    // Step 1: Count amount of each PAIR_HASH in the input buffer +    // The table will look like this: +    //  offs 0x000: Number of occurences of PAIR_HASH 0 +    //  offs 0x001: Number of occurences of PAIR_HASH 1 +    //  ... +    //  offs 0x8F7: Number of occurences of PAIR_HASH 0x8F7 (the highest hash value) +    for(buffer_ptr = buffer_begin; buffer_ptr < buffer_end; buffer_ptr++) +        pWork->phash_to_index[BYTE_PAIR_HASH(buffer_ptr)]++; + +    // Step 2: Convert the table to the array of PAIR_HASH amounts.  +    // Each element contains count of PAIR_HASHes that is less or equal +    // to element index +    // The table will look like this: +    //  offs 0x000: Number of occurences of PAIR_HASH 0 or lower +    //  offs 0x001: Number of occurences of PAIR_HASH 1 or lower +    //  ... +    //  offs 0x8F7: Number of occurences of PAIR_HASH 0x8F7 or lower +    for(phash_to_index = pWork->phash_to_index; phash_to_index < &pWork->phash_to_index_end; phash_to_index++) +    { +        total_sum = total_sum + phash_to_index[0]; +        phash_to_index[0] = total_sum; +    } + +    // Step 3: Convert the table to the array of indexes. +    // Now, each element contains index to the first occurence of given PAIR_HASH +    for(buffer_end--; buffer_end >= buffer_begin; buffer_end--) +    { +        byte_pair_hash = BYTE_PAIR_HASH(buffer_end); +        byte_pair_offs = (unsigned short)(buffer_end - pWork->work_buff); + +        pWork->phash_to_index[byte_pair_hash]--; +        pWork->phash_offs[pWork->phash_to_index[byte_pair_hash]] = byte_pair_offs; +    } +} + +static void FlushBuf(TCmpStruct * pWork) +{ +    unsigned char save_ch1; +    unsigned char save_ch2; +    unsigned int size = 0x800; + +    pWork->write_buf(pWork->out_buff, &size, pWork->param); + +    save_ch1 = pWork->out_buff[0x800]; +    save_ch2 = pWork->out_buff[pWork->out_bytes]; +    pWork->out_bytes -= 0x800; + +    memset(pWork->out_buff, 0, sizeof(pWork->out_buff)); + +    if(pWork->out_bytes != 0) +        pWork->out_buff[0] = save_ch1; +    if(pWork->out_bits != 0) +        pWork->out_buff[pWork->out_bytes] = save_ch2; +} + +static void OutputBits(TCmpStruct * pWork, unsigned int nbits, unsigned long bit_buff) +{ +    unsigned int out_bits; + +    // If more than 8 bits to output, do recursion +    if(nbits > 8) +    { +        OutputBits(pWork, 8, bit_buff); +        bit_buff >>= 8; +        nbits -= 8; +    } + +    // Add bits to the last out byte in out_buff; +    out_bits = pWork->out_bits; +    pWork->out_buff[pWork->out_bytes] |= (unsigned char)(bit_buff << out_bits); +    pWork->out_bits += nbits; + +    // If 8 or more bits, increment number of bytes +    if(pWork->out_bits > 8) +    { +        pWork->out_bytes++; +        bit_buff >>= (8 - out_bits); +         +        pWork->out_buff[pWork->out_bytes] = (unsigned char)bit_buff; +        pWork->out_bits &= 7; +    } +    else +    { +        pWork->out_bits &= 7; +        if(pWork->out_bits == 0) +            pWork->out_bytes++; +    } + +    // If there is enough compressed bytes, flush them +    if(pWork->out_bytes >= 0x800) +        FlushBuf(pWork); +} + +// This function searches for a repetition +// (a previous occurence of the current byte sequence) +// Returns length of the repetition, and stores the backward distance  +// to pWork structure. +static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) +{ +    unsigned short * phash_to_index;            // Pointer into pWork->phash_to_index table +    unsigned short * phash_offs;                // Pointer to the table containing offsets of each PAIR_HASH +    unsigned char * repetition_limit;           // An eventual repetition must be at position below this pointer +    unsigned char * prev_repetition;            // Pointer to the previous occurence of the current PAIR_HASH +    unsigned char * prev_rep_end;               // End of the previous repetition +    unsigned char * input_data_ptr; +    unsigned short phash_offs_index;            // Index to the table with PAIR_HASH positions +    unsigned short min_phash_offs;              // The lowest allowed hash offset +    unsigned short offs_in_rep;                 // Offset within found repetition +    unsigned int equal_byte_count;              // Number of bytes that are equal to the previous occurence +    unsigned int rep_length = 1;                // Length of the found repetition +    unsigned int rep_length2;                   // Secondary repetition +    unsigned char pre_last_byte;                // Last but one byte from a repetion +    unsigned short di_val; + +    // Calculate the previous position of the PAIR_HASH +    phash_to_index   = pWork->phash_to_index + BYTE_PAIR_HASH(input_data); +    min_phash_offs   = (unsigned short)((input_data - pWork->work_buff) - pWork->dsize_bytes + 1); +    phash_offs_index = phash_to_index[0]; + +    // If the PAIR_HASH offset is below the limit, find a next one +    phash_offs = pWork->phash_offs + phash_offs_index; +    if(*phash_offs < min_phash_offs) +    { +        while(*phash_offs < min_phash_offs) +        { +            phash_offs_index++; +            phash_offs++; +        } +        *phash_to_index = phash_offs_index; +    } + +    // Get the first location of the PAIR_HASH, +    // and thus the first eventual location of byte repetition +    phash_offs = pWork->phash_offs + phash_offs_index; +    prev_repetition = pWork->work_buff + phash_offs[0]; +    repetition_limit = input_data - 1; +     +    // If the current PAIR_HASH was not encountered before, +    // we haven't found a repetition. +    if(prev_repetition >= repetition_limit) +        return 0; + +    // We have found a match of a PAIR_HASH. Now we have to make sure +    // that it is also a byte match, because PAIR_HASH is not unique. +    // We compare the bytes and count the length of the repetition +    input_data_ptr = input_data; +    for(;;) +    { +        // If the first byte of the repetition and the so-far-last byte +        // of the repetition are equal, we will compare the blocks. +        if(*input_data_ptr == *prev_repetition && input_data_ptr[rep_length-1] == prev_repetition[rep_length-1]) +        { +            // Skip the current byte +            prev_repetition++; +            input_data_ptr++; +            equal_byte_count = 2; + +            // Now count how many more bytes are equal +            while(equal_byte_count < MAX_REP_LENGTH) +            { +                prev_repetition++; +                input_data_ptr++; +                 +                // Are the bytes different ? +                if(*prev_repetition != *input_data_ptr) +                    break; + +                equal_byte_count++; +            } + +            // If we found a repetition of at least the same length, take it. +            // If there are multiple repetitions in the input buffer, this will +            // make sure that we find the most recent one, which in turn allows +            // us to store backward length in less amount of bits +            input_data_ptr = input_data; +            if(equal_byte_count >= rep_length) +            { +                // Calculate the backward distance of the repetition. +                // Note that the distance is stored as decremented by 1 +                pWork->distance = (unsigned int)(input_data - prev_repetition + equal_byte_count - 1); + +                // Repetitions longer than 10 bytes will be stored in more bits, +                // so they need a bit different handling +                if((rep_length = equal_byte_count) > 10) +                    break; +            } +        } + +        // Move forward in the table of PAIR_HASH repetitions. +        // There might be a more recent occurence of the same repetition. +        phash_offs_index++; +        phash_offs++; +        prev_repetition = pWork->work_buff + phash_offs[0]; + +        // If the next repetition is beyond the minimum allowed repetition, we are done. +        if(prev_repetition >= repetition_limit) +        { +            // A repetition must have at least 2 bytes, otherwise it's not worth it +            return (rep_length >= 2) ? rep_length : 0; +        } +    } + +    // If the repetition has max length of 0x204 bytes, we can't go any fuhrter +    if(equal_byte_count == MAX_REP_LENGTH) +    { +        pWork->distance--; +        return equal_byte_count; +    } + +    // Check for possibility of a repetition that occurs at more recent position +    phash_offs = pWork->phash_offs + phash_offs_index; +    if(pWork->work_buff + phash_offs[1] >= repetition_limit) +        return rep_length; + +    // +    // The following part checks if there isn't a longer repetition at +    // a latter offset, that would lead to better compression. +    // +    // Example of data that can trigger this optimization: +    // +    //   "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEQQQQQQQQQQQQ" +    //   "XYZ" +    //   "EEEEEEEEEEEEEEEEQQQQQQQQQQQQ"; +    // +    // Description of data in this buffer +    //   [0x00] Single byte "E" +    //   [0x01] Single byte "E" +    //   [0x02] Repeat 0x1E bytes from [0x00] +    //   [0x20] Single byte "X" +    //   [0x21] Single byte "Y" +    //   [0x22] Single byte "Z" +    //   [0x23] 17 possible previous repetitions of length at least 0x10 bytes: +    //          - Repetition of 0x10 bytes from [0x00] "EEEEEEEEEEEEEEEE" +    //          - Repetition of 0x10 bytes from [0x01] "EEEEEEEEEEEEEEEE" +    //          - Repetition of 0x10 bytes from [0x02] "EEEEEEEEEEEEEEEE" +    //          ... +    //          - Repetition of 0x10 bytes from [0x0F] "EEEEEEEEEEEEEEEE" +    //          - Repetition of 0x1C bytes from [0x10] "EEEEEEEEEEEEEEEEQQQQQQQQQQQQ" +    //          The last repetition is the best one. +    // + +    pWork->offs09BC[0] = 0xFFFF; +    pWork->offs09BC[1] = 0x0000; +    di_val = 0; + +    // Note: I failed to figure out what does the table "offs09BC" mean. +    // If anyone has an idea, let me know to zezula_at_volny_dot_cz +    for(offs_in_rep = 1; offs_in_rep < rep_length; ) +    { +        if(input_data[offs_in_rep] != input_data[di_val]) +        { +            di_val = pWork->offs09BC[di_val]; +            if(di_val != 0xFFFF) +                continue; +        } +        pWork->offs09BC[++offs_in_rep] = ++di_val; +    } + +    //  +    // Now go through all the repetitions from the first found one +    // to the current input data, and check if any of them migh be +    // a start of a greater sequence match. +    // + +    prev_repetition = pWork->work_buff + phash_offs[0]; +    prev_rep_end = prev_repetition + rep_length; +    rep_length2 = rep_length; +     +    for(;;) +    { +        rep_length2 = pWork->offs09BC[rep_length2]; +        if(rep_length2 == 0xFFFF) +            rep_length2 = 0; + +        // Get the pointer to the previous repetition +        phash_offs = pWork->phash_offs + phash_offs_index; + +        // Skip those repetitions that don't reach the end +        // of the first found repetition +        do +        { +            phash_offs++; +            phash_offs_index++; +            prev_repetition = pWork->work_buff + *phash_offs; +            if(prev_repetition >= repetition_limit) +                return rep_length; +        } +        while(prev_repetition + rep_length2 < prev_rep_end); + +        // Verify if the last but one byte from the repetition matches +        // the last but one byte from the input data. +        // If not, find a next repetition +        pre_last_byte = input_data[rep_length - 2]; +        if(pre_last_byte == prev_repetition[rep_length - 2]) +        { +            // If the new repetition reaches beyond the end +            // of previously found repetition, reset the repetition length to zero. +            if(prev_repetition + rep_length2 != prev_rep_end) +            { +                prev_rep_end = prev_repetition; +                rep_length2 = 0; +            } +        } +        else +        { +            phash_offs = pWork->phash_offs + phash_offs_index; +            do +            { +                phash_offs++; +                phash_offs_index++; +                prev_repetition = pWork->work_buff + *phash_offs; +                if(prev_repetition >= repetition_limit) +                    return rep_length; +            } +            while(prev_repetition[rep_length - 2] != pre_last_byte || prev_repetition[0] != input_data[0]); + +            // Reset the length of the repetition to 2 bytes only +            prev_rep_end = prev_repetition + 2; +            rep_length2 = 2; +        } + +        // Find out how many more characters are equal to the first repetition. +        while(*prev_rep_end == input_data[rep_length2]) +        { +            if(++rep_length2 >= 0x204) +                break; +            prev_rep_end++; +        } + +        // Is the newly found repetion at least as long as the previous one ? +        if(rep_length2 >= rep_length) +        { +            // Calculate the distance of the new repetition +            pWork->distance = (unsigned int)(input_data - prev_repetition - 1); +            if((rep_length = rep_length2) == 0x204) +                return rep_length; + +            // Update the additional elements in the "offs09BC" table +            // to reflect new rep length +            while(offs_in_rep < rep_length2) +            { +                if(input_data[offs_in_rep] != input_data[di_val]) +                { +                    di_val = pWork->offs09BC[di_val]; +                    if(di_val != 0xFFFF) +                        continue; +                } +                pWork->offs09BC[++offs_in_rep] = ++di_val; +            } +        } +    } +} + +static void WriteCmpData(TCmpStruct * pWork) +{ +    unsigned char * input_data_end;         // Pointer to the end of the input data +    unsigned char * input_data = pWork->work_buff + pWork->dsize_bytes + 0x204; +    unsigned int input_data_ended = 0;      // If 1, then all data from the input stream have been already loaded +    unsigned int save_rep_length;           // Saved length of current repetition +    unsigned int save_distance = 0;         // Saved distance of current repetition +    unsigned int rep_length;                // Length of the found repetition +    unsigned int phase = 0;                 //  + +    // Store the compression type and dictionary size +    pWork->out_buff[0] = (char)pWork->ctype; +    pWork->out_buff[1] = (char)pWork->dsize_bits; +    pWork->out_bytes = 2; + +    // Reset output buffer to zero +    memset(&pWork->out_buff[2], 0, sizeof(pWork->out_buff) - 2); +    pWork->out_bits = 0; + +    while(input_data_ended == 0) +    { +        unsigned int bytes_to_load = 0x1000; +        int total_loaded = 0; +        int bytes_loaded; + +        // Load the bytes from the input stream, up to 0x1000 bytes +        while(bytes_to_load != 0) +        { +            bytes_loaded = pWork->read_buf((char *)pWork->work_buff + pWork->dsize_bytes + 0x204 + total_loaded, +                                                  &bytes_to_load, +                                                   pWork->param); +            if(bytes_loaded == 0) +            { +                if(total_loaded == 0 && phase == 0) +                    goto __Exit; +                input_data_ended = 1; +                break; +            } +            else +            { +                bytes_to_load -= bytes_loaded; +                total_loaded += bytes_loaded; +            } +        } + +        input_data_end = pWork->work_buff + pWork->dsize_bytes + total_loaded; +        if(input_data_ended) +            input_data_end += 0x204; +         +        // +        // Warning: The end of the buffer passed to "SortBuffer" is actually 2 bytes beyond +        // valid data. It is questionable if this is actually a bug or not, +        // but it might cause the compressed data output to be dependent on random bytes +        // that are in the buffer.  +        // To prevent that, the calling application must always zero the compression +        // buffer before passing it to "implode" +        // + +        // Search the PAIR_HASHes of the loaded blocks. Also, include +        // previously compressed data, if any. +        switch(phase) +        { +            case 0:  +                SortBuffer(pWork, input_data, input_data_end + 1); +                phase++; +                if(pWork->dsize_bytes != 0x1000) +                    phase++; +                break; + +            case 1: +                SortBuffer(pWork, input_data - pWork->dsize_bytes + 0x204, input_data_end + 1); +                phase++; +                break; + +            default: +                SortBuffer(pWork, input_data - pWork->dsize_bytes, input_data_end + 1); +                break; +        } + +        // Perform the compression of the current block +        while(input_data < input_data_end) +        { +            // Find if the current byte sequence wasn't there before. +            rep_length = FindRep(pWork, input_data); +            while(rep_length != 0) +            { +                // If we found repetition of 2 bytes, that is 0x100 or fuhrter back, +                // don't bother. Storing the distance of 0x100 bytes would actually +                // take more space than storing the 2 bytes as-is. +                if(rep_length == 2 && pWork->distance >= 0x100) +                    break; + +                // When we are at the end of the input data, we cannot allow +                // the repetition to go past the end of the input data. +                if(input_data_ended && input_data + rep_length > input_data_end) +                { +                    // Shorten the repetition length so that it only covers valid data +                    rep_length = (unsigned long)(input_data_end - input_data); +                    if(rep_length < 2) +                        break; + +                    // If we got repetition of 2 bytes, that is 0x100 or more backward, don't bother +                    if(rep_length == 2 && pWork->distance >= 0x100) +                        break; +                    goto __FlushRepetition; +                } + +                if(rep_length >= 8 || input_data + 1 >= input_data_end) +                    goto __FlushRepetition; + +                // Try to find better repetition 1 byte later. +                // Example: "ARROCKFORT" "AROCKFORT" +                // When "input_data" points to the second string, FindRep +                // returns the occurence of "AR". But there is longer repetition "ROCKFORT", +                // beginning 1 byte after. +                save_rep_length = rep_length; +                save_distance = pWork->distance; +                rep_length = FindRep(pWork, input_data + 1); + +                // Only use the new repetition if it's length is greater than the previous one +                if(rep_length > save_rep_length) +                { +                    // If the new repetition if only 1 byte better +                    // and the previous distance is less than 0x80 bytes, use the previous repetition +                    if(rep_length > save_rep_length + 1 || save_distance > 0x80) +                    { +                        // Flush one byte, so that input_data will point to the secondary repetition +                        OutputBits(pWork, pWork->nChBits[*input_data], pWork->nChCodes[*input_data]); +                        input_data++; +                        continue; +                    } +                } + +                // Revert to the previous repetition +                rep_length = save_rep_length; +                pWork->distance = save_distance; + +                __FlushRepetition: + +                OutputBits(pWork, pWork->nChBits[rep_length + 0xFE], pWork->nChCodes[rep_length + 0xFE]); +                if(rep_length == 2) +                { +                    OutputBits(pWork, pWork->dist_bits[pWork->distance >> 2], +                                      pWork->dist_codes[pWork->distance >> 2]); +                    OutputBits(pWork, 2, pWork->distance & 3); +                } +                else +                { +                    OutputBits(pWork, pWork->dist_bits[pWork->distance >> pWork->dsize_bits], +                                      pWork->dist_codes[pWork->distance >> pWork->dsize_bits]); +                    OutputBits(pWork, pWork->dsize_bits, pWork->dsize_mask & pWork->distance); +                } + +                // Move the begin of the input data by the length of the repetition +                input_data += rep_length; +                goto _00402252; +            } + +            // If there was no previous repetition for the current position in the input data, +            // just output the 9-bit literal for the one character +            OutputBits(pWork, pWork->nChBits[*input_data], pWork->nChCodes[*input_data]); +            input_data++; +_00402252:; +        } + +        if(input_data_ended == 0) +        { +            input_data -= 0x1000; +            memcpy(pWork->work_buff, pWork->work_buff + 0x1000, pWork->dsize_bytes + 0x204); +        } +    } + +__Exit: + +    // Write the termination literal +    OutputBits(pWork, pWork->nChBits[0x305], pWork->nChCodes[0x305]); +    if(pWork->out_bits != 0) +        pWork->out_bytes++; +    pWork->write_buf(pWork->out_buff, &pWork->out_bytes, pWork->param); +    return; +} + +//----------------------------------------------------------------------------- +// Main imploding function + +unsigned int PKEXPORT implode( +    unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), +    void         (*write_buf)(char *buf, unsigned int *size, void *param), +    char         *work_buf, +    void         *param, +    unsigned int *type, +    unsigned int *dsize) +{ +    TCmpStruct * pWork = (TCmpStruct *)work_buf; +    unsigned int nChCode; +    unsigned int nCount; +    unsigned int i; +    int nCount2; + +    // Fill the work buffer information +    // Note: The caller must zero the "work_buff" before passing it to implode +    pWork->read_buf    = read_buf; +    pWork->write_buf   = write_buf; +    pWork->dsize_bytes = *dsize; +    pWork->ctype       = *type; +    pWork->param       = param; +    pWork->dsize_bits  = 4; +    pWork->dsize_mask  = 0x0F; + +    // Test dictionary size +    switch(*dsize) +    { +        case CMP_IMPLODE_DICT_SIZE3:    // 0x1000 bytes +            pWork->dsize_bits++; +            pWork->dsize_mask |= 0x20; +            // No break here !!! + +        case CMP_IMPLODE_DICT_SIZE2:    // 0x800 bytes +            pWork->dsize_bits++; +            pWork->dsize_mask |= 0x10; +            // No break here !!! + +        case CMP_IMPLODE_DICT_SIZE1:    // 0x400 +            break; + +        default: +            return CMP_INVALID_DICTSIZE; +    } + +    // Test the compression type +    switch(*type) +    { +        case CMP_BINARY: // We will compress data with binary compression type +            for(nChCode = 0, nCount = 0; nCount < 0x100; nCount++) +            { +                pWork->nChBits[nCount]  = 9; +                pWork->nChCodes[nCount] = (unsigned short)nChCode; +                nChCode = (nChCode & 0x0000FFFF) + 2; +            } +            break; + + +        case CMP_ASCII: // We will compress data with ASCII compression type +            for(nCount = 0; nCount < 0x100; nCount++) +            { +                pWork->nChBits[nCount]  = (unsigned char )(ChBitsAsc[nCount] + 1); +                pWork->nChCodes[nCount] = (unsigned short)(ChCodeAsc[nCount] * 2); +            } +            break; + +        default: +            return CMP_INVALID_MODE; +    } + +    for(i = 0; i < 0x10; i++) +    { +        if(1 << ExLenBits[i]) +        { +            for(nCount2 = 0; nCount2 < (1 << ExLenBits[i]); nCount2++) +            { +                pWork->nChBits[nCount]  = (unsigned char)(ExLenBits[i] + LenBits[i] + 1); +                pWork->nChCodes[nCount] = (unsigned short)((nCount2 << (LenBits[i] + 1)) | ((LenCode[i] & 0xFFFF00FF) * 2) | 1); +                nCount++; +            } +        } +    } + +    // Copy the distance codes and distance bits and perform the compression +    memcpy(&pWork->dist_codes, DistCode, sizeof(DistCode)); +    memcpy(&pWork->dist_bits, DistBits, sizeof(DistBits)); +    WriteCmpData(pWork); +    return CMP_NO_ERROR; +} diff --git a/src/pklib/pklib.h b/src/pklib/pklib.h new file mode 100644 index 0000000..f43da15 --- /dev/null +++ b/src/pklib/pklib.h @@ -0,0 +1,148 @@ +/*****************************************************************************/ +/* pklib.h                                Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Header file for PKWARE Data Compression Library                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 31.03.03  1.00  Lad  The first version of pkware.h                        */ +/*****************************************************************************/ + +#ifndef __PKLIB_H__ +#define __PKLIB_H__ + +#include "../StormPort.h" + +//----------------------------------------------------------------------------- +// Defines + +#define CMP_BINARY             0            // Binary compression +#define CMP_ASCII              1            // Ascii compression + +#define CMP_NO_ERROR           0 +#define CMP_INVALID_DICTSIZE   1 +#define CMP_INVALID_MODE       2 +#define CMP_BAD_DATA           3 +#define CMP_ABORT              4 + +#define CMP_IMPLODE_DICT_SIZE1   1024       // Dictionary size of 1024 +#define CMP_IMPLODE_DICT_SIZE2   2048       // Dictionary size of 2048 +#define CMP_IMPLODE_DICT_SIZE3   4096       // Dictionary size of 4096 + +//----------------------------------------------------------------------------- +// Define calling convention + +#ifndef PKEXPORT +#ifdef WIN32 +#define PKEXPORT  __cdecl                   // Use for normal __cdecl calling  +#else +#define PKEXPORT +#endif +#endif + +//----------------------------------------------------------------------------- +// Internal structures + +// Compression structure +typedef struct +{ +    unsigned int   distance;                // 0000: Backward distance of the currently found repetition, decreased by 1 +    unsigned int   out_bytes;               // 0004: # bytes available in out_buff             +    unsigned int   out_bits;                // 0008: # of bits available in the last out byte +    unsigned int   dsize_bits;              // 000C: Number of bits needed for dictionary size. 4 = 0x400, 5 = 0x800, 6 = 0x1000 +    unsigned int   dsize_mask;              // 0010: Bit mask for dictionary. 0x0F = 0x400, 0x1F = 0x800, 0x3F = 0x1000 +    unsigned int   ctype;                   // 0014: Compression type (CMP_ASCII or CMP_BINARY) +    unsigned int   dsize_bytes;             // 0018: Dictionary size in bytes +    unsigned char  dist_bits[0x40];         // 001C: Distance bits +    unsigned char  dist_codes[0x40];        // 005C: Distance codes +    unsigned char  nChBits[0x306];          // 009C: Table of literal bit lengths to be put to the output stream +    unsigned short nChCodes[0x306];         // 03A2: Table of literal codes to be put to the output stream +    unsigned short offs09AE;                // 09AE:  + +    void         * param;                   // 09B0: User parameter +    unsigned int (*read_buf)(char *buf, unsigned int *size, void *param);  // 9B4 +    void         (*write_buf)(char *buf, unsigned int *size, void *param); // 9B8 + +    unsigned short offs09BC[0x204];         // 09BC: +    unsigned long  offs0DC4;                // 0DC4:  +    unsigned short phash_to_index[0x900];   // 0DC8: Array of indexes (one for each PAIR_HASH) to the "pair_hash_offsets" table +    unsigned short phash_to_index_end;      // 1FC8: End marker for "phash_to_index" table +    char           out_buff[0x802];         // 1FCA: Compressed data +    unsigned char  work_buff[0x2204];       // 27CC: Work buffer +                                            //  + DICT_OFFSET  => Dictionary +                                            //  + UNCMP_OFFSET => Uncompressed data +    unsigned short phash_offs[0x2204];      // 49D0: Table of offsets for each PAIR_HASH +} TCmpStruct; + +#define CMP_BUFFER_SIZE  sizeof(TCmpStruct) // Size of compression structure. +                                            // Defined as 36312 in pkware header file + + +// Decompression structure +typedef struct +{ +    unsigned long offs0000;                 // 0000 +    unsigned long ctype;                    // 0004: Compression type (CMP_BINARY or CMP_ASCII) +    unsigned long outputPos;                // 0008: Position in output buffer +    unsigned long dsize_bits;               // 000C: Dict size (4, 5, 6 for 0x400, 0x800, 0x1000) +    unsigned long dsize_mask;               // 0010: Dict size bitmask (0x0F, 0x1F, 0x3F for 0x400, 0x800, 0x1000) +    unsigned long bit_buff;                 // 0014: 16-bit buffer for processing input data +    unsigned long extra_bits;               // 0018: Number of extra (above 8) bits in bit buffer +    unsigned int  in_pos;                   // 001C: Position in in_buff +    unsigned long in_bytes;                 // 0020: Number of bytes in input buffer +    void        * param;                    // 0024: Custom parameter +    unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // Pointer to function that reads data from the input stream +    void         (*write_buf)(char *buf, unsigned int *size, void *param);// Pointer to function that writes data to the output stream + +    unsigned char out_buff[0x2204];         // 0030: Output circle buffer. +                                            //       0x0000 - 0x0FFF: Previous uncompressed data, kept for repetitions +                                            //       0x1000 - 0x1FFF: Currently decompressed data +                                            //       0x2000 - 0x2203: Reserve space for the longest possible repetition +    unsigned char in_buff[0x800];           // 2234: Buffer for data to be decompressed +    unsigned char DistPosCodes[0x100];      // 2A34: Table of distance position codes +    unsigned char LengthCodes[0x100];       // 2B34: Table of length codes +    unsigned char offs2C34[0x100];          // 2C34: Buffer for  +    unsigned char offs2D34[0x100];          // 2D34: Buffer for  +    unsigned char offs2E34[0x80];           // 2EB4: Buffer for  +    unsigned char offs2EB4[0x100];          // 2EB4: Buffer for  +    unsigned char ChBitsAsc[0x100];         // 2FB4: Buffer for  +    unsigned char DistBits[0x40];           // 30B4: Numbers of bytes to skip copied block length +    unsigned char LenBits[0x10];            // 30F4: Numbers of bits for skip copied block length +    unsigned char ExLenBits[0x10];          // 3104: Number of valid bits for copied block +    unsigned short LenBase[0x10];           // 3114: Buffer for  +} TDcmpStruct; + +#define EXP_BUFFER_SIZE sizeof(TDcmpStruct) // Size of decompression structure +                                            // Defined as 12596 in pkware headers + +//----------------------------------------------------------------------------- +// Public functions + +#ifdef __cplusplus +   extern "C" { +#endif + +unsigned int PKEXPORT implode( +   unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), +   void         (*write_buf)(char *buf, unsigned int *size, void *param), +   char         *work_buf, +   void         *param, +   unsigned int *type, +   unsigned int *dsize); + + +unsigned int PKEXPORT explode( +   unsigned int (*read_buf)(char *buf, unsigned  int *size, void *param), +   void         (*write_buf)(char *buf, unsigned  int *size, void *param), +   char         *work_buf, +   void         *param); + +// The original name "crc32" was changed to "crc32pk" due +// to compatibility with zlib +unsigned long PKEXPORT crc32_pklib(char *buffer, unsigned int *size, unsigned long *old_crc); + +#ifdef __cplusplus +   }                         // End of 'extern "C"' declaration +#endif + +#endif // __PKLIB_H__ diff --git a/src/sparse/sparse.cpp b/src/sparse/sparse.cpp new file mode 100644 index 0000000..dd65c82 --- /dev/null +++ b/src/sparse/sparse.cpp @@ -0,0 +1,296 @@ +/*****************************************************************************/ +/* huffman.cpp                       Copyright (c) Ladislav Zezula 1998-2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains Huffmann (de)compression methods                     */ +/*                                                                           */ +/* Authors : Ladislav Zezula (ladik.zezula.net)                              */ +/*           ShadowFlare     (BlakFlare@hotmail.com)                         */ +/*                                                                           */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* xx.xx.xx  1.00  Lad  The first version of dcmp.cpp                        */ +/* 03.05.03  1.00  Lad  Added compression methods                            */ +/* 19.11.03  1.01  Dan  Big endian handling                                  */ +/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */ +/*****************************************************************************/ +  +#include <assert.h> +#include <string.h> +  +#include "sparse.h" + +//----------------------------------------------------------------------------- +// Public functions + +void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; +    unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; +    unsigned char * pbLastNonZero = (unsigned char *)pvInBuffer; +    unsigned char * pbOutBuffer0 = (unsigned char *)pvOutBuffer; +    unsigned char * pbInBuffPtr = (unsigned char *)pvInBuffer; +    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; +    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; +    size_t NumberOfNonZeros; +    size_t NumberOfZeros; + +    // There must be at least 4 bytes of free space in the output buffer now +    if((pbInBuffer + 4) >= pbInBufferEnd) +        return; + +    // Put the original data length (in little endian) +    *pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x18); +    *pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x10); +    *pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x08); +    *pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x00); + +    // If there is at least 3 bytes in the input buffer, do this loop +    while(pbInBuffer < (pbInBufferEnd - 3)) +    { +        // Reset the zero count and frontal pointer +        pbLastNonZero = pbInBuffer; +        pbInBuffPtr   = pbInBuffer; +        NumberOfZeros = 0; + +        if(pbInBuffPtr < pbInBufferEnd) +        { +            do +            { +                // Count number of zeros +                if(*pbInBuffPtr == 0) +                { +                    NumberOfZeros++; +                } +                else +                { +                    // Were there at least 3 zeros before? If yes, we need to flush the data +                    if(NumberOfZeros >= 3) +                        break; +                    pbLastNonZero = pbInBuffPtr + 1; +                    NumberOfZeros = 0; +                } +            } +            while(++pbInBuffPtr < pbInBufferEnd); +        } + +        // Get number of nonzeros that we found so far and flush them +        NumberOfNonZeros = pbLastNonZero - pbInBuffer; +        if(NumberOfNonZeros != 0) +        { +            // Process blocks that are longer than 0x81 nonzero bytes +            while(NumberOfNonZeros > 0x81) +            { +                // Verify if we still have enough space in output buffer +                if((pbOutBuffer + 0x81) >= pbOutBufferEnd) +                    return; + +                // Put marker that means "0x80 of nonzeros" +                *pbOutBuffer++ = 0xFF; +                memcpy(pbOutBuffer, pbInBuffer, 0x80); +                 +                // Adjust counter of nonzeros and both pointers +                NumberOfNonZeros -= 0x80; +                pbOutBuffer += 0x80; +                pbInBuffer += 0x80; +            } + +            // BUGBUG: The following code will be triggered if the NumberOfNonZeros +            // was 0x81 before. It will copy just one byte. This seems like a bug to me, +            // but since I want StormLib to be exact like Blizzard code is, I will keep +            // it that way here +            if(NumberOfNonZeros > 0x80) +            { +                // Verify if we still have enough space in output buffer +                if((pbOutBuffer + 2) >= pbOutBufferEnd) +                    return; + +                // Put marker that means "1 nonzero byte" +                *pbOutBuffer++ = 0x80; +                memcpy(pbOutBuffer, pbInBuffer, 1); +                 +                // Adjust counter of nonzeros and both pointers +                NumberOfNonZeros--; +                pbOutBuffer++; +                pbInBuffer++; +            } + +            // If there is 1 nonzero or more, put the block +            if(NumberOfNonZeros >= 0x01) +            { +                // Verify if we still have enough space in output buffer +                if((pbOutBuffer + NumberOfNonZeros + 1) >= pbOutBufferEnd) +                    return; + +                // Put marker that means "Several nonzero bytes" +                *pbOutBuffer++ = (unsigned char)(0x80 | (NumberOfNonZeros - 1)); +                memcpy(pbOutBuffer, pbInBuffer, NumberOfNonZeros); + +                // Adjust pointers +                pbOutBuffer += NumberOfNonZeros; +                pbInBuffer += NumberOfNonZeros; +            } +            else +            { +                // Verify if we still have enough space in output buffer +                if((pbOutBuffer + 2) >= pbOutBufferEnd) +                    return; + +                // Put marker that means "1 nonzero byte" +                *pbOutBuffer++ = 0x80; +                memcpy(pbOutBuffer, pbInBuffer, 1); + +                // Adjust pointers +                pbOutBuffer++; +                pbInBuffer++; +            } +        } + +        // Now flush all zero bytes +        while(NumberOfZeros > 0x85) +        { +            // Do we have at least 2 bytes in the output buffer ? +            if((pbOutBuffer + 1) >= pbOutBufferEnd) +                return; + +            // Put marker that means "0x82 zeros" +            *pbOutBuffer++ = 0x7F; + +            // Adjust zero counter and input pointer +            NumberOfZeros -= 0x82; +            pbInBuffer += 0x82; +        } + +        // If we got more than 0x82 zeros, flush 3 of them now +        if(NumberOfZeros > 0x82) +        { +            // Do we have at least 2 bytes in the output buffer ? +            if((pbOutBuffer + 1) >= pbOutBufferEnd) +                return; + +            // Put marker that means "0x03 zeros" +            *pbOutBuffer++ = 0; + +            // Adjust zero counter and input pointer +            NumberOfZeros -= 0x03; +            pbInBuffer += 0x03; +        } + +        // Is there at least three zeros ? +        if(NumberOfZeros >= 3) +        { +            // Do we have at least 2 bytes in the output buffer ? +            if((pbOutBuffer + 1) >= pbOutBufferEnd) +                return; + +            // Put marker that means "Several zeros" +            *pbOutBuffer++ = (unsigned char)(NumberOfZeros - 3); +     +            // Adjust pointer +            pbInBuffer += NumberOfZeros; +        } +    } + +    // Flush last three bytes +    if(pbInBuffer < pbInBufferEnd) +    { +        pbInBuffPtr = pbInBuffer; + +        for(;;) +        { +            if(*pbInBuffPtr++ != 0) +            { +                // Get number of bytes remaining +                NumberOfNonZeros = (pbInBufferEnd - pbInBuffer); + +                // Not enough space in the output buffer ==> exit +                if((pbOutBuffer + NumberOfNonZeros + 1) >= pbOutBufferEnd) +                    return; + +                // Terminate with a marker that means "0x80 of nonzeros" +                *pbOutBuffer++ = 0xFF; +                memcpy(pbOutBuffer, pbInBuffer, NumberOfNonZeros); +                 +                // Adjust pointer +                pbOutBuffer += NumberOfNonZeros; +                break; +            } +            else +            { +                // Is there are more chars in the input buffer +                if(pbInBuffPtr < pbInBufferEnd) +                    continue; + +                // If the compression will not compress it by even 1 byte, do nothing +                if((pbOutBuffer + 1) >= pbOutBufferEnd) +                    return; + +                // Terminate with a chunk that means "0x82 of zeros" +                *pbOutBuffer++ = 0x7F; +                break; +            } +        } +    } + +    // Out the length of the output buffer +    *pcbOutBuffer = (int)(pbOutBuffer - pbOutBuffer0); +} + +int DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ +    unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; +    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; +    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; +    unsigned int cbChunkSize; +    unsigned int cbOutBuffer = 0; +    unsigned int OneByte; + +    // Don't decompress anything that is shorter than 5 bytes +    if(cbInBuffer < 5) +        return 0; + +    // Get the 32-bits from the input stream +    OneByte = *pbInBuffer++; +    cbOutBuffer |= (OneByte << 0x18); +    OneByte = *pbInBuffer++; +    cbOutBuffer |= (OneByte << 0x10); +    OneByte = *pbInBuffer++; +    cbOutBuffer |= (OneByte << 0x08); +    OneByte = *pbInBuffer++; +    cbOutBuffer |= (OneByte << 0x00); + +    // Verify the size of the stream against the output buffer size +    if(cbOutBuffer > *pcbOutBuffer) +        return 0; + +    // Put the output size to the buffer +    *pcbOutBuffer = cbOutBuffer; + +    // Process the input buffer +    while(pbInBuffer < pbInBufferEnd) +    { +        // Get (next) byte from the stream +        OneByte = *pbInBuffer++; + +        // If highest bit, it means that that normal data follow +        if(OneByte & 0x80) +        { +            cbChunkSize = (OneByte & 0x7F) + 1; +            cbChunkSize = (cbChunkSize < cbOutBuffer) ? cbChunkSize : cbOutBuffer; +            memcpy(pbOutBuffer, pbInBuffer, cbChunkSize); +            pbInBuffer += cbChunkSize; +        } +        else +        { +            cbChunkSize = (OneByte & 0x7F) + 3; +            cbChunkSize = (cbChunkSize < cbOutBuffer) ? cbChunkSize : cbOutBuffer; +            memset(pbOutBuffer, 0, cbChunkSize); +        } + +        // Increment output buffer pointer +        pbOutBuffer += cbChunkSize; +        cbOutBuffer -= cbChunkSize; +    } + +    return 1; +} diff --git a/src/sparse/sparse.h b/src/sparse/sparse.h new file mode 100644 index 0000000..b1cd872 --- /dev/null +++ b/src/sparse/sparse.h @@ -0,0 +1,19 @@ +/*****************************************************************************/ +/* sparse.h                               Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* implementation of Sparse compression, used in Starcraft II                */ +/*---------------------------------------------------------------------------*/ +/*   Date    Ver   Who  Comment                                              */ +/* --------  ----  ---  -------                                              */ +/* 05.03.10  1.00  Lad  The first version of sparse.h                        */ +/*****************************************************************************/ + +#ifndef __SPARSE_H__ +#define __SPARSE_H__ + +#include "../StormPort.h" + +void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int  DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); + +#endif // __SPARSE_H__ diff --git a/src/zlib/adler32.c b/src/zlib/adler32.c new file mode 100644 index 0000000..007ba26 --- /dev/null +++ b/src/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL    /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4); +#define DO16(buf)   DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +#  define MOD(a) \ +    do { \ +        if (a >= (BASE << 16)) a -= (BASE << 16); \ +        if (a >= (BASE << 15)) a -= (BASE << 15); \ +        if (a >= (BASE << 14)) a -= (BASE << 14); \ +        if (a >= (BASE << 13)) a -= (BASE << 13); \ +        if (a >= (BASE << 12)) a -= (BASE << 12); \ +        if (a >= (BASE << 11)) a -= (BASE << 11); \ +        if (a >= (BASE << 10)) a -= (BASE << 10); \ +        if (a >= (BASE << 9)) a -= (BASE << 9); \ +        if (a >= (BASE << 8)) a -= (BASE << 8); \ +        if (a >= (BASE << 7)) a -= (BASE << 7); \ +        if (a >= (BASE << 6)) a -= (BASE << 6); \ +        if (a >= (BASE << 5)) a -= (BASE << 5); \ +        if (a >= (BASE << 4)) a -= (BASE << 4); \ +        if (a >= (BASE << 3)) a -= (BASE << 3); \ +        if (a >= (BASE << 2)) a -= (BASE << 2); \ +        if (a >= (BASE << 1)) a -= (BASE << 1); \ +        if (a >= BASE) a -= BASE; \ +    } while (0) +#  define MOD4(a) \ +    do { \ +        if (a >= (BASE << 4)) a -= (BASE << 4); \ +        if (a >= (BASE << 3)) a -= (BASE << 3); \ +        if (a >= (BASE << 2)) a -= (BASE << 2); \ +        if (a >= (BASE << 1)) a -= (BASE << 1); \ +        if (a >= BASE) a -= BASE; \ +    } while (0) +#else +#  define MOD(a) a %= BASE +#  define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) +    uLong adler; +    const Bytef *buf; +    uInt len; +{ +    unsigned long sum2; +    unsigned n; + +    /* split Adler-32 into component sums */ +    sum2 = (adler >> 16) & 0xffff; +    adler &= 0xffff; + +    /* in case user likes doing a byte at a time, keep it fast */ +    if (len == 1) { +        adler += buf[0]; +        if (adler >= BASE) +            adler -= BASE; +        sum2 += adler; +        if (sum2 >= BASE) +            sum2 -= BASE; +        return adler | (sum2 << 16); +    } + +    /* initial Adler-32 value (deferred check for len == 1 speed) */ +    if (buf == Z_NULL) +        return 1L; + +    /* in case short lengths are provided, keep it somewhat fast */ +    if (len < 16) { +        while (len--) { +            adler += *buf++; +            sum2 += adler; +        } +        if (adler >= BASE) +            adler -= BASE; +        MOD4(sum2);             /* only added so many BASE's */ +        return adler | (sum2 << 16); +    } + +    /* do length NMAX blocks -- requires just one modulo operation */ +    while (len >= NMAX) { +        len -= NMAX; +        n = NMAX / 16;          /* NMAX is divisible by 16 */ +        do { +            DO16(buf);          /* 16 sums unrolled */ +            buf += 16; +        } while (--n); +        MOD(adler); +        MOD(sum2); +    } + +    /* do remaining bytes (less than NMAX, still just one modulo) */ +    if (len) {                  /* avoid modulos if none remaining */ +        while (len >= 16) { +            len -= 16; +            DO16(buf); +            buf += 16; +        } +        while (len--) { +            adler += *buf++; +            sum2 += adler; +        } +        MOD(adler); +        MOD(sum2); +    } + +    /* return recombined sums */ +    return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) +    uLong adler1; +    uLong adler2; +    z_off_t len2; +{ +    unsigned long sum1; +    unsigned long sum2; +    unsigned rem; + +    /* the derivation of this formula is left as an exercise for the reader */ +    rem = (unsigned)(len2 % BASE); +    sum1 = adler1 & 0xffff; +    sum2 = rem * sum1; +    MOD(sum2); +    sum1 += (adler2 & 0xffff) + BASE - 1; +    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; +    if (sum1 > BASE) sum1 -= BASE; +    if (sum1 > BASE) sum1 -= BASE; +    if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); +    if (sum2 > BASE) sum2 -= BASE; +    return sum1 | (sum2 << 16); +} diff --git a/src/zlib/compress2.c b/src/zlib/compress2.c new file mode 100644 index 0000000..df04f01 --- /dev/null +++ b/src/zlib/compress2.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== +     Compresses the source buffer into the destination buffer. The level +   parameter has the same meaning as in deflateInit.  sourceLen is the byte +   length of the source buffer. Upon entry, destLen is the total size of the +   destination buffer, which must be at least 0.1% larger than sourceLen plus +   12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + +     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough +   memory, Z_BUF_ERROR if there was not enough room in the output buffer, +   Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) +    Bytef *dest; +    uLongf *destLen; +    const Bytef *source; +    uLong sourceLen; +    int level; +{ +    z_stream stream; +    int err; + +    stream.next_in = (Bytef*)source; +    stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K +    /* Check for source > 64K on 16-bit machine: */ +    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif +    stream.next_out = dest; +    stream.avail_out = (uInt)*destLen; +    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + +    stream.zalloc = (alloc_func)0; +    stream.zfree = (free_func)0; +    stream.opaque = (voidpf)0; + +    err = deflateInit(&stream, level); +    if (err != Z_OK) return err; + +    err = deflate(&stream, Z_FINISH); +    if (err != Z_STREAM_END) { +        deflateEnd(&stream); +        return err == Z_OK ? Z_BUF_ERROR : err; +    } +    *destLen = stream.total_out; + +    err = deflateEnd(&stream); +    return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) +    Bytef *dest; +    uLongf *destLen; +    const Bytef *source; +    uLong sourceLen; +{ +    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== +     If the default memLevel or windowBits for deflateInit() is changed, then +   this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) +    uLong sourceLen; +{ +    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/src/zlib/crc32.c b/src/zlib/crc32.c new file mode 100644 index 0000000..f658a9e --- /dev/null +++ b/src/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors.  This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* +  Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore +  protection on the static variables used to control the first-use generation +  of the crc tables.  Therefore, if you #define DYNAMIC_CRC_TABLE, you should +  first call get_crc_table() to initialize the tables before allowing more than +  one thread to use crc32(). + */ + +#ifdef MAKECRCH +#  include <stdio.h> +#  ifndef DYNAMIC_CRC_TABLE +#    define DYNAMIC_CRC_TABLE +#  endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h"      /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +#  ifdef STDC           /* need ANSI C limits.h to determine sizes */ +#    include <limits.h> +#    define BYFOUR +#    if (UINT_MAX == 0xffffffffUL) +       typedef unsigned int u4; +#    else +#      if (ULONG_MAX == 0xffffffffUL) +         typedef unsigned long u4; +#      else +#        if (USHRT_MAX == 0xffffffffUL) +           typedef unsigned short u4; +#        else +#          undef BYFOUR     /* can't find a four-byte integer type! */ +#        endif +#      endif +#    endif +#  endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +#  define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ +                (((w)&0xff00)<<8)+(((w)&0xff)<<24)) +   local unsigned long crc32_little OF((unsigned long, +                        const unsigned char FAR *, unsigned)); +   local unsigned long crc32_big OF((unsigned long, +                        const unsigned char FAR *, unsigned)); +#  define TBLS 8 +#else +#  define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, +                                         unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH +   local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* +  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: +  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + +  Polynomials over GF(2) are represented in binary, one bit per coefficient, +  with the lowest powers in the most significant bit.  Then adding polynomials +  is just exclusive-or, and multiplying a polynomial by x is a right shift by +  one.  If we call the above polynomial p, and represent a byte as the +  polynomial q, also with the lowest power in the most significant bit (so the +  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, +  where a mod b means the remainder after dividing a by b. + +  This calculation is done using the shift-register method of multiplying and +  taking the remainder.  The register is initialized to zero, and for each +  incoming bit, x^32 is added mod p to the register if the bit is a one (where +  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by +  x (which is shifting right by one and adding x^32 mod p if the bit shifted +  out is a one).  We start with the highest power (least significant bit) of +  q and repeat for all eight bits of q. + +  The first table is simply the CRC of all possible eight bit values.  This is +  all the information needed to generate CRCs on data a byte at a time for all +  combinations of CRC register values and incoming bytes.  The remaining tables +  allow for word-at-a-time CRC calculation for both big-endian and little- +  endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ +    unsigned long c; +    int n, k; +    unsigned long poly;                 /* polynomial exclusive-or pattern */ +    /* terms of polynomial defining this crc (except x^32): */ +    static volatile int first = 1;      /* flag to limit concurrent making */ +    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + +    /* See if another task is already doing this (not thread-safe, but better +       than nothing -- significantly reduces duration of vulnerability in +       case the advice about DYNAMIC_CRC_TABLE is ignored) */ +    if (first) { +        first = 0; + +        /* make exclusive-or pattern from polynomial (0xedb88320UL) */ +        poly = 0UL; +        for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) +            poly |= 1UL << (31 - p[n]); + +        /* generate a crc for every 8-bit value */ +        for (n = 0; n < 256; n++) { +            c = (unsigned long)n; +            for (k = 0; k < 8; k++) +                c = c & 1 ? poly ^ (c >> 1) : c >> 1; +            crc_table[0][n] = c; +        } + +#ifdef BYFOUR +        /* generate crc for each value followed by one, two, and three zeros, +           and then the byte reversal of those as well as the first table */ +        for (n = 0; n < 256; n++) { +            c = crc_table[0][n]; +            crc_table[4][n] = REV(c); +            for (k = 1; k < 4; k++) { +                c = crc_table[0][c & 0xff] ^ (c >> 8); +                crc_table[k][n] = c; +                crc_table[k + 4][n] = REV(c); +            } +        } +#endif /* BYFOUR */ + +        crc_table_empty = 0; +    } +    else {      /* not first */ +        /* wait for the other guy to finish (not efficient, but rare) */ +        while (crc_table_empty) +            ; +    } + +#ifdef MAKECRCH +    /* write out CRC tables to crc32.h */ +    { +        FILE *out; + +        out = fopen("crc32.h", "w"); +        if (out == NULL) return; +        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); +        fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); +        fprintf(out, "local const unsigned long FAR "); +        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n"); +        write_table(out, crc_table[0]); +#  ifdef BYFOUR +        fprintf(out, "#ifdef BYFOUR\n"); +        for (k = 1; k < 8; k++) { +            fprintf(out, "  },\n  {\n"); +            write_table(out, crc_table[k]); +        } +        fprintf(out, "#endif\n"); +#  endif /* BYFOUR */ +        fprintf(out, "  }\n};\n"); +        fclose(out); +    } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) +    FILE *out; +    const unsigned long FAR *table; +{ +    int n; + +    for (n = 0; n < 256; n++) +        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ", table[n], +                n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE +    if (crc_table_empty) +        make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ +    return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) +    unsigned long crc; +    const unsigned char FAR *buf; +    unsigned len; +{ +    if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE +    if (crc_table_empty) +        make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR +    if (sizeof(void *) == sizeof(ptrdiff_t)) { +        u4 endian; + +        endian = 1; +        if (*((unsigned char *)(&endian))) +            return crc32_little(crc, buf, len); +        else +            return crc32_big(crc, buf, len); +    } +#endif /* BYFOUR */ +    crc = crc ^ 0xffffffffUL; +    while (len >= 8) { +        DO8; +        len -= 8; +    } +    if (len) do { +        DO1; +    } while (--len); +    return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ +        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ +            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) +    unsigned long crc; +    const unsigned char FAR *buf; +    unsigned len; +{ +    register u4 c; +    register const u4 FAR *buf4; + +    c = (u4)crc; +    c = ~c; +    while (len && ((ptrdiff_t)buf & 3)) { +        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); +        len--; +    } + +    buf4 = (const u4 FAR *)(const void FAR *)buf; +    while (len >= 32) { +        DOLIT32; +        len -= 32; +    } +    while (len >= 4) { +        DOLIT4; +        len -= 4; +    } +    buf = (const unsigned char FAR *)buf4; + +    if (len) do { +        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); +    } while (--len); +    c = ~c; +    return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ +        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ +            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) +    unsigned long crc; +    const unsigned char FAR *buf; +    unsigned len; +{ +    register u4 c; +    register const u4 FAR *buf4; + +    c = REV((u4)crc); +    c = ~c; +    while (len && ((ptrdiff_t)buf & 3)) { +        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); +        len--; +    } + +    buf4 = (const u4 FAR *)(const void FAR *)buf; +    buf4--; +    while (len >= 32) { +        DOBIG32; +        len -= 32; +    } +    while (len >= 4) { +        DOBIG4; +        len -= 4; +    } +    buf4++; +    buf = (const unsigned char FAR *)buf4; + +    if (len) do { +        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); +    } while (--len); +    c = ~c; +    return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) +    unsigned long *mat; +    unsigned long vec; +{ +    unsigned long sum; + +    sum = 0; +    while (vec) { +        if (vec & 1) +            sum ^= *mat; +        vec >>= 1; +        mat++; +    } +    return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) +    unsigned long *square; +    unsigned long *mat; +{ +    int n; + +    for (n = 0; n < GF2_DIM; n++) +        square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) +    uLong crc1; +    uLong crc2; +    z_off_t len2; +{ +    int n; +    unsigned long row; +    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */ +    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */ + +    /* degenerate case */ +    if (len2 == 0) +        return crc1; + +    /* put operator for one zero bit in odd */ +    odd[0] = 0xedb88320L;           /* CRC-32 polynomial */ +    row = 1; +    for (n = 1; n < GF2_DIM; n++) { +        odd[n] = row; +        row <<= 1; +    } + +    /* put operator for two zero bits in even */ +    gf2_matrix_square(even, odd); + +    /* put operator for four zero bits in odd */ +    gf2_matrix_square(odd, even); + +    /* apply len2 zeros to crc1 (first square will put the operator for one +       zero byte, eight zero bits, in even) */ +    do { +        /* apply zeros operator for this bit of len2 */ +        gf2_matrix_square(even, odd); +        if (len2 & 1) +            crc1 = gf2_matrix_times(even, crc1); +        len2 >>= 1; + +        /* if no more bits set, then done */ +        if (len2 == 0) +            break; + +        /* another iteration of the loop with odd and even swapped */ +        gf2_matrix_square(odd, even); +        if (len2 & 1) +            crc1 = gf2_matrix_times(odd, crc1); +        len2 >>= 1; + +        /* if no more bits set, then done */ +    } while (len2 != 0); + +    /* return combined crc */ +    crc1 ^= crc2; +    return crc1; +} diff --git a/src/zlib/crc32.h b/src/zlib/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/src/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ +  { +    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, +    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, +    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, +    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, +    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, +    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, +    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, +    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, +    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, +    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, +    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, +    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, +    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, +    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, +    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, +    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, +    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, +    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, +    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, +    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, +    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, +    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, +    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, +    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, +    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, +    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, +    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, +    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, +    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, +    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, +    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, +    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, +    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, +    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, +    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, +    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, +    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, +    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, +    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, +    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, +    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, +    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, +    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, +    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, +    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, +    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, +    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, +    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, +    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, +    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, +    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, +    0x2d02ef8dUL +#ifdef BYFOUR +  }, +  { +    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, +    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, +    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, +    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, +    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, +    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, +    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, +    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, +    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, +    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, +    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, +    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, +    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, +    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, +    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, +    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, +    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, +    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, +    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, +    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, +    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, +    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, +    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, +    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, +    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, +    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, +    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, +    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, +    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, +    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, +    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, +    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, +    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, +    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, +    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, +    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, +    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, +    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, +    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, +    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, +    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, +    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, +    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, +    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, +    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, +    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, +    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, +    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, +    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, +    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, +    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, +    0x9324fd72UL +  }, +  { +    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, +    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, +    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, +    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, +    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, +    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, +    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, +    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, +    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, +    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, +    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, +    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, +    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, +    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, +    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, +    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, +    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, +    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, +    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, +    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, +    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, +    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, +    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, +    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, +    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, +    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, +    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, +    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, +    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, +    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, +    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, +    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, +    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, +    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, +    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, +    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, +    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, +    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, +    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, +    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, +    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, +    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, +    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, +    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, +    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, +    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, +    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, +    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, +    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, +    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, +    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, +    0xbe9834edUL +  }, +  { +    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, +    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, +    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, +    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, +    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, +    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, +    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, +    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, +    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, +    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, +    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, +    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, +    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, +    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, +    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, +    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, +    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, +    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, +    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, +    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, +    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, +    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, +    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, +    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, +    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, +    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, +    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, +    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, +    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, +    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, +    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, +    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, +    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, +    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, +    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, +    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, +    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, +    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, +    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, +    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, +    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, +    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, +    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, +    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, +    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, +    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, +    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, +    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, +    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, +    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, +    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, +    0xde0506f1UL +  }, +  { +    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, +    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, +    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, +    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, +    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, +    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, +    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, +    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, +    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, +    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, +    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, +    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, +    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, +    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, +    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, +    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, +    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, +    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, +    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, +    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, +    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, +    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, +    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, +    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, +    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, +    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, +    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, +    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, +    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, +    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, +    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, +    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, +    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, +    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, +    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, +    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, +    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, +    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, +    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, +    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, +    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, +    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, +    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, +    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, +    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, +    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, +    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, +    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, +    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, +    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, +    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, +    0x8def022dUL +  }, +  { +    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, +    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, +    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, +    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, +    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, +    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, +    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, +    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, +    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, +    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, +    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, +    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, +    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, +    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, +    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, +    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, +    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, +    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, +    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, +    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, +    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, +    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, +    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, +    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, +    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, +    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, +    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, +    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, +    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, +    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, +    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, +    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, +    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, +    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, +    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, +    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, +    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, +    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, +    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, +    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, +    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, +    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, +    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, +    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, +    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, +    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, +    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, +    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, +    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, +    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, +    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, +    0x72fd2493UL +  }, +  { +    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, +    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, +    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, +    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, +    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, +    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, +    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, +    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, +    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, +    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, +    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, +    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, +    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, +    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, +    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, +    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, +    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, +    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, +    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, +    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, +    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, +    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, +    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, +    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, +    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, +    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, +    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, +    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, +    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, +    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, +    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, +    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, +    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, +    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, +    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, +    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, +    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, +    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, +    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, +    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, +    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, +    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, +    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, +    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, +    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, +    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, +    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, +    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, +    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, +    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, +    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, +    0xed3498beUL +  }, +  { +    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, +    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, +    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, +    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, +    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, +    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, +    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, +    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, +    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, +    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, +    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, +    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, +    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, +    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, +    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, +    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, +    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, +    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, +    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, +    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, +    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, +    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, +    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, +    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, +    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, +    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, +    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, +    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, +    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, +    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, +    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, +    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, +    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, +    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, +    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, +    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, +    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, +    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, +    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, +    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, +    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, +    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, +    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, +    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, +    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, +    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, +    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, +    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, +    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, +    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, +    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, +    0xf10605deUL +#endif +  } +}; diff --git a/src/zlib/deflate.c b/src/zlib/deflate.c new file mode 100644 index 0000000..29ce1f6 --- /dev/null +++ b/src/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + *  ALGORITHM + * + *      The "deflation" process depends on being able to identify portions + *      of the input text which are identical to earlier input (within a + *      sliding window trailing behind the input currently being processed). + * + *      The most straightforward technique turns out to be the fastest for + *      most input files: try all possible matches and select the longest. + *      The key feature of this algorithm is that insertions into the string + *      dictionary are very simple and thus fast, and deletions are avoided + *      completely. Insertions are performed at each input character, whereas + *      string matches are performed only when the previous match ends. So it + *      is preferable to spend more time in matches to allow very fast string + *      insertions and avoid deletions. The matching algorithm for small + *      strings is inspired from that of Rabin & Karp. A brute force approach + *      is used to find longer strings when a small match has been found. + *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + *      (by Leonid Broukhis). + *         A previous version of this file used a more sophisticated algorithm + *      (by Fiala and Greene) which is guaranteed to run in linear amortized + *      time, but has a larger average cost, uses more memory and is patented. + *      However the F&G algorithm may be faster for some highly redundant + *      files if the parameter max_chain_length (described below) is too large. + * + *  ACKNOWLEDGEMENTS + * + *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + *      I found it in 'freeze' written by Leonid Broukhis. + *      Thanks to many people for bug reports and testing. + * + *  REFERENCES + * + *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + *      Available in http://www.ietf.org/rfc/rfc1951.txt + * + *      A description of the Rabin and Karp algorithm is given in the book + *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + *      Fiala,E.R., and Greene,D.H. + *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = +   " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* +  If you use the zlib library in a product, an acknowledgment is welcome +  in the documentation of your product. If for some reason you cannot +  include such an acknowledgment, I would appreciate that you keep this +  copyright string in the executable of your product. + */ + +/* =========================================================================== + *  Function prototypes. + */ +typedef enum { +    need_more,      /* block not completed, need more input or more output */ +    block_done,     /* block flush performed */ +    finish_started, /* finish started, need only more output at next deflate */ +    finish_done     /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window    OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast   OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow   OF((deflate_state *s, int flush)); +#endif +local void lm_init        OF((deflate_state *s)); +local void putShortMSB    OF((deflate_state *s, uInt b)); +local void flush_pending  OF((z_streamp strm)); +local int read_buf        OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV +      void match_init OF((void)); /* asm code initialization */ +      uInt longest_match  OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match  OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local  void check_match OF((deflate_state *s, IPos start, IPos match, +                            int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +#  define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { +   ush good_length; /* reduce lazy search above this match length */ +   ush max_lazy;    /* do not perform lazy search above this match length */ +   ush nice_length; /* quit search above this match length */ +   ush max_chain; +   compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/*      good lazy nice chain */ +/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */ +/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/*      good lazy nice chain */ +/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */ +/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4,    5, 16,    8, deflate_fast}, +/* 3 */ {4,    6, 32,   32, deflate_fast}, + +/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */ +/* 5 */ {8,   16, 32,   32, deflate_slow}, +/* 6 */ {8,   16, 128, 128, deflate_slow}, +/* 7 */ {8,   32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN  assertion: all calls to to UPDATE_HASH are made with consecutive + *    input characters, so that a running hash key can be computed from the + *    previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN  assertion: all calls to to INSERT_STRING are made with consecutive + *    input characters and the first MIN_MATCH bytes of str are valid + *    (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ +   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ +    match_head = s->head[s->ins_h], \ +    s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ +   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ +    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ +    s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ +    s->head[s->hash_size-1] = NIL; \ +    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) +    z_streamp strm; +    int level; +    const char *version; +    int stream_size; +{ +    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, +                         Z_DEFAULT_STRATEGY, version, stream_size); +    /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, +                  version, stream_size) +    z_streamp strm; +    int  level; +    int  method; +    int  windowBits; +    int  memLevel; +    int  strategy; +    const char *version; +    int stream_size; +{ +    deflate_state *s; +    int wrap = 1; +    static const char my_version[] = ZLIB_VERSION; + +    ushf *overlay; +    /* We overlay pending_buf and d_buf+l_buf. This works since the average +     * output size for (length,distance) codes is <= 24 bits. +     */ + +    if (version == Z_NULL || version[0] != my_version[0] || +        stream_size != sizeof(z_stream)) { +        return Z_VERSION_ERROR; +    } +    if (strm == Z_NULL) return Z_STREAM_ERROR; + +    strm->msg = Z_NULL; +    if (strm->zalloc == (alloc_func)0) { +        strm->zalloc = zcalloc; +        strm->opaque = (voidpf)0; +    } +    if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST +    if (level != 0) level = 1; +#else +    if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + +    if (windowBits < 0) { /* suppress zlib wrapper */ +        wrap = 0; +        windowBits = -windowBits; +    } +#ifdef GZIP +    else if (windowBits > 15) { +        wrap = 2;       /* write gzip wrapper instead */ +        windowBits -= 16; +    } +#endif +    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || +        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || +        strategy < 0 || strategy > Z_FIXED) { +        return Z_STREAM_ERROR; +    } +    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */ +    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); +    if (s == Z_NULL) return Z_MEM_ERROR; +    strm->state = (struct internal_state FAR *)s; +    s->strm = strm; + +    s->wrap = wrap; +    s->gzhead = Z_NULL; +    s->w_bits = windowBits; +    s->w_size = 1 << s->w_bits; +    s->w_mask = s->w_size - 1; + +    s->hash_bits = memLevel + 7; +    s->hash_size = 1 << s->hash_bits; +    s->hash_mask = s->hash_size - 1; +    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + +    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); +    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos)); +    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos)); + +    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + +    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); +    s->pending_buf = (uchf *) overlay; +    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + +    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || +        s->pending_buf == Z_NULL) { +        s->status = FINISH_STATE; +        strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); +        deflateEnd (strm); +        return Z_MEM_ERROR; +    } +    s->d_buf = overlay + s->lit_bufsize/sizeof(ush); +    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + +    s->level = level; +    s->strategy = strategy; +    s->method = (Byte)method; + +    return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) +    z_streamp strm; +    const Bytef *dictionary; +    uInt  dictLength; +{ +    deflate_state *s; +    uInt length = dictLength; +    uInt n; +    IPos hash_head = 0; + +    if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || +        strm->state->wrap == 2 || +        (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) +        return Z_STREAM_ERROR; + +    s = strm->state; +    if (s->wrap) +        strm->adler = adler32(strm->adler, dictionary, dictLength); + +    if (length < MIN_MATCH) return Z_OK; +    if (length > MAX_DIST(s)) { +        length = MAX_DIST(s); +        dictionary += dictLength - length; /* use the tail of the dictionary */ +    } +    zmemcpy(s->window, dictionary, length); +    s->strstart = length; +    s->block_start = (long)length; + +    /* Insert all strings in the hash table (except for the last two bytes). +     * s->lookahead stays null, so s->ins_h will be recomputed at the next +     * call of fill_window. +     */ +    s->ins_h = s->window[0]; +    UPDATE_HASH(s, s->ins_h, s->window[1]); +    for (n = 0; n <= length - MIN_MATCH; n++) { +        INSERT_STRING(s, n, hash_head); +    } +    if (hash_head) hash_head = 0;  /* to make compiler happy */ +    return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) +    z_streamp strm; +{ +    deflate_state *s; + +    if (strm == Z_NULL || strm->state == Z_NULL || +        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { +        return Z_STREAM_ERROR; +    } + +    strm->total_in = strm->total_out = 0; +    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ +    strm->data_type = Z_UNKNOWN; + +    s = (deflate_state *)strm->state; +    s->pending = 0; +    s->pending_out = s->pending_buf; + +    if (s->wrap < 0) { +        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ +    } +    s->status = s->wrap ? INIT_STATE : BUSY_STATE; +    strm->adler = +#ifdef GZIP +        s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif +        adler32(0L, Z_NULL, 0); +    s->last_flush = Z_NO_FLUSH; + +    _tr_init(s); +    lm_init(s); + +    return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) +    z_streamp strm; +    gz_headerp head; +{ +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    if (strm->state->wrap != 2) return Z_STREAM_ERROR; +    strm->state->gzhead = head; +    return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) +    z_streamp strm; +    int bits; +    int value; +{ +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    strm->state->bi_valid = bits; +    strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); +    return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) +    z_streamp strm; +    int level; +    int strategy; +{ +    deflate_state *s; +    compress_func func; +    int err = Z_OK; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    s = strm->state; + +#ifdef FASTEST +    if (level != 0) level = 1; +#else +    if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif +    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { +        return Z_STREAM_ERROR; +    } +    func = configuration_table[s->level].func; + +    if (func != configuration_table[level].func && strm->total_in != 0) { +        /* Flush the last buffer: */ +        err = deflate(strm, Z_PARTIAL_FLUSH); +    } +    if (s->level != level) { +        s->level = level; +        s->max_lazy_match   = configuration_table[level].max_lazy; +        s->good_match       = configuration_table[level].good_length; +        s->nice_match       = configuration_table[level].nice_length; +        s->max_chain_length = configuration_table[level].max_chain; +    } +    s->strategy = strategy; +    return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) +    z_streamp strm; +    int good_length; +    int max_lazy; +    int nice_length; +    int max_chain; +{ +    deflate_state *s; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    s = strm->state; +    s->good_match = good_length; +    s->max_lazy_match = max_lazy; +    s->nice_match = nice_length; +    s->max_chain_length = max_chain; +    return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well.  The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) +    z_streamp strm; +    uLong sourceLen; +{ +    deflate_state *s; +    uLong destLen; + +    /* conservative upper bound */ +    destLen = sourceLen + +              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + +    /* if can't get parameters, return conservative bound */ +    if (strm == Z_NULL || strm->state == Z_NULL) +        return destLen; + +    /* if not default parameters, return conservative bound */ +    s = strm->state; +    if (s->w_bits != 15 || s->hash_bits != 8 + 7) +        return destLen; + +    /* default settings: return tight bound for that case */ +    return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) +    deflate_state *s; +    uInt b; +{ +    put_byte(s, (Byte)(b >> 8)); +    put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) +    z_streamp strm; +{ +    unsigned len = strm->state->pending; + +    if (len > strm->avail_out) len = strm->avail_out; +    if (len == 0) return; + +    zmemcpy(strm->next_out, strm->state->pending_out, len); +    strm->next_out  += len; +    strm->state->pending_out  += len; +    strm->total_out += len; +    strm->avail_out  -= len; +    strm->state->pending -= len; +    if (strm->state->pending == 0) { +        strm->state->pending_out = strm->state->pending_buf; +    } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) +    z_streamp strm; +    int flush; +{ +    int old_flush; /* value of flush param for previous deflate call */ +    deflate_state *s; + +    if (strm == Z_NULL || strm->state == Z_NULL || +        flush > Z_FINISH || flush < 0) { +        return Z_STREAM_ERROR; +    } +    s = strm->state; + +    if (strm->next_out == Z_NULL || +        (strm->next_in == Z_NULL && strm->avail_in != 0) || +        (s->status == FINISH_STATE && flush != Z_FINISH)) { +        ERR_RETURN(strm, Z_STREAM_ERROR); +    } +    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + +    s->strm = strm; /* just in case */ +    old_flush = s->last_flush; +    s->last_flush = flush; + +    /* Write the header */ +    if (s->status == INIT_STATE) { +#ifdef GZIP +        if (s->wrap == 2) { +            strm->adler = crc32(0L, Z_NULL, 0); +            put_byte(s, 31); +            put_byte(s, 139); +            put_byte(s, 8); +            if (s->gzhead == NULL) { +                put_byte(s, 0); +                put_byte(s, 0); +                put_byte(s, 0); +                put_byte(s, 0); +                put_byte(s, 0); +                put_byte(s, s->level == 9 ? 2 : +                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? +                             4 : 0)); +                put_byte(s, OS_CODE); +                s->status = BUSY_STATE; +            } +            else { +                put_byte(s, (s->gzhead->text ? 1 : 0) + +                            (s->gzhead->hcrc ? 2 : 0) + +                            (s->gzhead->extra == Z_NULL ? 0 : 4) + +                            (s->gzhead->name == Z_NULL ? 0 : 8) + +                            (s->gzhead->comment == Z_NULL ? 0 : 16) +                        ); +                put_byte(s, (Byte)(s->gzhead->time & 0xff)); +                put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); +                put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); +                put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); +                put_byte(s, s->level == 9 ? 2 : +                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? +                             4 : 0)); +                put_byte(s, s->gzhead->os & 0xff); +                if (s->gzhead->extra != NULL) { +                    put_byte(s, s->gzhead->extra_len & 0xff); +                    put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); +                } +                if (s->gzhead->hcrc) +                    strm->adler = crc32(strm->adler, s->pending_buf, +                                        s->pending); +                s->gzindex = 0; +                s->status = EXTRA_STATE; +            } +        } +        else +#endif +        { +            uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; +            uInt level_flags; + +            if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) +                level_flags = 0; +            else if (s->level < 6) +                level_flags = 1; +            else if (s->level == 6) +                level_flags = 2; +            else +                level_flags = 3; +            header |= (level_flags << 6); +            if (s->strstart != 0) header |= PRESET_DICT; +            header += 31 - (header % 31); + +            s->status = BUSY_STATE; +            putShortMSB(s, header); + +            /* Save the adler32 of the preset dictionary: */ +            if (s->strstart != 0) { +                putShortMSB(s, (uInt)(strm->adler >> 16)); +                putShortMSB(s, (uInt)(strm->adler & 0xffff)); +            } +            strm->adler = adler32(0L, Z_NULL, 0); +        } +    } +#ifdef GZIP +    if (s->status == EXTRA_STATE) { +        if (s->gzhead->extra != NULL) { +            uInt beg = s->pending;  /* start of bytes to update crc */ + +            while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { +                if (s->pending == s->pending_buf_size) { +                    if (s->gzhead->hcrc && s->pending > beg) +                        strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                            s->pending - beg); +                    flush_pending(strm); +                    beg = s->pending; +                    if (s->pending == s->pending_buf_size) +                        break; +                } +                put_byte(s, s->gzhead->extra[s->gzindex]); +                s->gzindex++; +            } +            if (s->gzhead->hcrc && s->pending > beg) +                strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                    s->pending - beg); +            if (s->gzindex == s->gzhead->extra_len) { +                s->gzindex = 0; +                s->status = NAME_STATE; +            } +        } +        else +            s->status = NAME_STATE; +    } +    if (s->status == NAME_STATE) { +        if (s->gzhead->name != NULL) { +            uInt beg = s->pending;  /* start of bytes to update crc */ +            int val; + +            do { +                if (s->pending == s->pending_buf_size) { +                    if (s->gzhead->hcrc && s->pending > beg) +                        strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                            s->pending - beg); +                    flush_pending(strm); +                    beg = s->pending; +                    if (s->pending == s->pending_buf_size) { +                        val = 1; +                        break; +                    } +                } +                val = s->gzhead->name[s->gzindex++]; +                put_byte(s, val); +            } while (val != 0); +            if (s->gzhead->hcrc && s->pending > beg) +                strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                    s->pending - beg); +            if (val == 0) { +                s->gzindex = 0; +                s->status = COMMENT_STATE; +            } +        } +        else +            s->status = COMMENT_STATE; +    } +    if (s->status == COMMENT_STATE) { +        if (s->gzhead->comment != NULL) { +            uInt beg = s->pending;  /* start of bytes to update crc */ +            int val; + +            do { +                if (s->pending == s->pending_buf_size) { +                    if (s->gzhead->hcrc && s->pending > beg) +                        strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                            s->pending - beg); +                    flush_pending(strm); +                    beg = s->pending; +                    if (s->pending == s->pending_buf_size) { +                        val = 1; +                        break; +                    } +                } +                val = s->gzhead->comment[s->gzindex++]; +                put_byte(s, val); +            } while (val != 0); +            if (s->gzhead->hcrc && s->pending > beg) +                strm->adler = crc32(strm->adler, s->pending_buf + beg, +                                    s->pending - beg); +            if (val == 0) +                s->status = HCRC_STATE; +        } +        else +            s->status = HCRC_STATE; +    } +    if (s->status == HCRC_STATE) { +        if (s->gzhead->hcrc) { +            if (s->pending + 2 > s->pending_buf_size) +                flush_pending(strm); +            if (s->pending + 2 <= s->pending_buf_size) { +                put_byte(s, (Byte)(strm->adler & 0xff)); +                put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); +                strm->adler = crc32(0L, Z_NULL, 0); +                s->status = BUSY_STATE; +            } +        } +        else +            s->status = BUSY_STATE; +    } +#endif + +    /* Flush as much pending output as possible */ +    if (s->pending != 0) { +        flush_pending(strm); +        if (strm->avail_out == 0) { +            /* Since avail_out is 0, deflate will be called again with +             * more output space, but possibly with both pending and +             * avail_in equal to zero. There won't be anything to do, +             * but this is not an error situation so make sure we +             * return OK instead of BUF_ERROR at next call of deflate: +             */ +            s->last_flush = -1; +            return Z_OK; +        } + +    /* Make sure there is something to do and avoid duplicate consecutive +     * flushes. For repeated and useless calls with Z_FINISH, we keep +     * returning Z_STREAM_END instead of Z_BUF_ERROR. +     */ +    } else if (strm->avail_in == 0 && flush <= old_flush && +               flush != Z_FINISH) { +        ERR_RETURN(strm, Z_BUF_ERROR); +    } + +    /* User must not provide more input after the first FINISH: */ +    if (s->status == FINISH_STATE && strm->avail_in != 0) { +        ERR_RETURN(strm, Z_BUF_ERROR); +    } + +    /* Start a new block or continue the current one. +     */ +    if (strm->avail_in != 0 || s->lookahead != 0 || +        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { +        block_state bstate; + +        bstate = (*(configuration_table[s->level].func))(s, flush); + +        if (bstate == finish_started || bstate == finish_done) { +            s->status = FINISH_STATE; +        } +        if (bstate == need_more || bstate == finish_started) { +            if (strm->avail_out == 0) { +                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ +            } +            return Z_OK; +            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call +             * of deflate should use the same flush parameter to make sure +             * that the flush is complete. So we don't have to output an +             * empty block here, this will be done at next call. This also +             * ensures that for a very small output buffer, we emit at most +             * one empty block. +             */ +        } +        if (bstate == block_done) { +            if (flush == Z_PARTIAL_FLUSH) { +                _tr_align(s); +            } else { /* FULL_FLUSH or SYNC_FLUSH */ +                _tr_stored_block(s, (char*)0, 0L, 0); +                /* For a full flush, this empty block will be recognized +                 * as a special marker by inflate_sync(). +                 */ +                if (flush == Z_FULL_FLUSH) { +                    CLEAR_HASH(s);             /* forget history */ +                } +            } +            flush_pending(strm); +            if (strm->avail_out == 0) { +              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ +              return Z_OK; +            } +        } +    } +    Assert(strm->avail_out > 0, "bug2"); + +    if (flush != Z_FINISH) return Z_OK; +    if (s->wrap <= 0) return Z_STREAM_END; + +    /* Write the trailer */ +#ifdef GZIP +    if (s->wrap == 2) { +        put_byte(s, (Byte)(strm->adler & 0xff)); +        put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); +        put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); +        put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); +        put_byte(s, (Byte)(strm->total_in & 0xff)); +        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); +        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); +        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); +    } +    else +#endif +    { +        putShortMSB(s, (uInt)(strm->adler >> 16)); +        putShortMSB(s, (uInt)(strm->adler & 0xffff)); +    } +    flush_pending(strm); +    /* If avail_out is zero, the application will call deflate again +     * to flush the rest. +     */ +    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ +    return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) +    z_streamp strm; +{ +    int status; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + +    status = strm->state->status; +    if (status != INIT_STATE && +        status != EXTRA_STATE && +        status != NAME_STATE && +        status != COMMENT_STATE && +        status != HCRC_STATE && +        status != BUSY_STATE && +        status != FINISH_STATE) { +      return Z_STREAM_ERROR; +    } + +    /* Deallocate in reverse order of allocations: */ +    TRY_FREE(strm, strm->state->pending_buf); +    TRY_FREE(strm, strm->state->head); +    TRY_FREE(strm, strm->state->prev); +    TRY_FREE(strm, strm->state->window); + +    ZFREE(strm, strm->state); +    strm->state = Z_NULL; + +    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) +    z_streamp dest; +    z_streamp source; +{ +#ifdef MAXSEG_64K +    return Z_STREAM_ERROR; +#else +    deflate_state *ds; +    deflate_state *ss; +    ushf *overlay; + + +    if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { +        return Z_STREAM_ERROR; +    } + +    ss = source->state; + +    zmemcpy(dest, source, sizeof(z_stream)); + +    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); +    if (ds == Z_NULL) return Z_MEM_ERROR; +    dest->state = (struct internal_state FAR *) ds; +    zmemcpy(ds, ss, sizeof(deflate_state)); +    ds->strm = dest; + +    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); +    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos)); +    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos)); +    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); +    ds->pending_buf = (uchf *) overlay; + +    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || +        ds->pending_buf == Z_NULL) { +        deflateEnd (dest); +        return Z_MEM_ERROR; +    } +    /* following zmemcpy do not work for 16-bit MSDOS */ +    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); +    zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); +    zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); +    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + +    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); +    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); +    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + +    ds->l_desc.dyn_tree = ds->dyn_ltree; +    ds->d_desc.dyn_tree = ds->dyn_dtree; +    ds->bl_desc.dyn_tree = ds->bl_tree; + +    return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read.  All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) +    z_streamp strm; +    Bytef *buf; +    unsigned size; +{ +    unsigned len = strm->avail_in; + +    if (len > size) len = size; +    if (len == 0) return 0; + +    strm->avail_in  -= len; + +    if (strm->state->wrap == 1) { +        strm->adler = adler32(strm->adler, strm->next_in, len); +    } +#ifdef GZIP +    else if (strm->state->wrap == 2) { +        strm->adler = crc32(strm->adler, strm->next_in, len); +    } +#endif +    zmemcpy(buf, strm->next_in, len); +    strm->next_in  += len; +    strm->total_in += len; + +    return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) +    deflate_state *s; +{ +    s->window_size = (ulg)2L*s->w_size; + +    CLEAR_HASH(s); + +    /* Set the default configuration parameters: +     */ +    s->max_lazy_match   = configuration_table[s->level].max_lazy; +    s->good_match       = configuration_table[s->level].good_length; +    s->nice_match       = configuration_table[s->level].nice_length; +    s->max_chain_length = configuration_table[s->level].max_chain; + +    s->strstart = 0; +    s->block_start = 0L; +    s->lookahead = 0; +    s->match_length = s->prev_length = MIN_MATCH-1; +    s->match_available = 0; +    s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV +    match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) +    deflate_state *s; +    IPos cur_match;                             /* current match */ +{ +    unsigned chain_length = s->max_chain_length;/* max hash chain length */ +    register Bytef *scan = s->window + s->strstart; /* current string */ +    register Bytef *match;                       /* matched string */ +    register int len;                           /* length of current match */ +    int best_len = s->prev_length;              /* best match length so far */ +    int nice_match = s->nice_match;             /* stop if match long enough */ +    IPos limit = s->strstart > (IPos)MAX_DIST(s) ? +        s->strstart - (IPos)MAX_DIST(s) : NIL; +    /* Stop when cur_match becomes <= limit. To simplify the code, +     * we prevent matches with the string of window index 0. +     */ +    Posf *prev = s->prev; +    uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK +    /* Compare two bytes at a time. Note: this is not always beneficial. +     * Try with and without -DUNALIGNED_OK to check. +     */ +    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; +    register ush scan_start = *(ushf*)scan; +    register ush scan_end   = *(ushf*)(scan+best_len-1); +#else +    register Bytef *strend = s->window + s->strstart + MAX_MATCH; +    register Byte scan_end1  = scan[best_len-1]; +    register Byte scan_end   = scan[best_len]; +#endif + +    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. +     * It is easy to get rid of this optimization if necessary. +     */ +    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + +    /* Do not waste too much time if we already have a good match: */ +    if (s->prev_length >= s->good_match) { +        chain_length >>= 2; +    } +    /* Do not look for matches beyond the end of the input. This is necessary +     * to make deflate deterministic. +     */ +    if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + +    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + +    do { +        Assert(cur_match < s->strstart, "no future"); +        match = s->window + cur_match; + +        /* Skip to next match if the match length cannot increase +         * or if the match length is less than 2.  Note that the checks below +         * for insufficient lookahead only occur occasionally for performance +         * reasons.  Therefore uninitialized memory will be accessed, and +         * conditional jumps will be made that depend on those values. +         * However the length of the match is limited to the lookahead, so +         * the output of deflate is not affected by the uninitialized values. +         */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) +        /* This code assumes sizeof(unsigned short) == 2. Do not use +         * UNALIGNED_OK if your compiler uses a different size. +         */ +        if (*(ushf*)(match+best_len-1) != scan_end || +            *(ushf*)match != scan_start) continue; + +        /* It is not necessary to compare scan[2] and match[2] since they are +         * always equal when the other bytes match, given that the hash keys +         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at +         * strstart+3, +5, ... up to strstart+257. We check for insufficient +         * lookahead only every 4th comparison; the 128th check will be made +         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is +         * necessary to put more guard bytes at the end of the window, or +         * to check more often for insufficient lookahead. +         */ +        Assert(scan[2] == match[2], "scan[2]?"); +        scan++, match++; +        do { +        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) && +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) && +                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) && +                 scan < strend); +        /* The funny "do {}" generates better code on most compilers */ + +        /* Here, scan <= window+strstart+257 */ +        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); +        if (*scan == *match) scan++; + +        len = (MAX_MATCH - 1) - (int)(strend-scan); +        scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + +        if (match[best_len]   != scan_end  || +            match[best_len-1] != scan_end1 || +            *match            != *scan     || +            *++match          != scan[1])      continue; + +        /* The check at best_len-1 can be removed because it will be made +         * again later. (This heuristic is not always a win.) +         * It is not necessary to compare scan[2] and match[2] since they +         * are always equal when the other bytes match, given that +         * the hash keys are equal and that HASH_BITS >= 8. +         */ +        scan += 2, match++; +        Assert(*scan == *match, "match[2]?"); + +        /* We check for insufficient lookahead only every 8th comparison; +         * the 256th check will be made at strstart+258. +         */ +        do { +        } while (*++scan == *++match && *++scan == *++match && +                 *++scan == *++match && *++scan == *++match && +                 *++scan == *++match && *++scan == *++match && +                 *++scan == *++match && *++scan == *++match && +                 scan < strend); + +        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + +        len = MAX_MATCH - (int)(strend - scan); +        scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + +        if (len > best_len) { +            s->match_start = cur_match; +            best_len = len; +            if (len >= nice_match) break; +#ifdef UNALIGNED_OK +            scan_end = *(ushf*)(scan+best_len-1); +#else +            scan_end1  = scan[best_len-1]; +            scan_end   = scan[best_len]; +#endif +        } +    } while ((cur_match = prev[cur_match & wmask]) > limit +             && --chain_length != 0); + +    if ((uInt)best_len <= s->lookahead) return (uInt)best_len; +    return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) +    deflate_state *s; +    IPos cur_match;                             /* current match */ +{ +    register Bytef *scan = s->window + s->strstart; /* current string */ +    register Bytef *match;                       /* matched string */ +    register int len;                           /* length of current match */ +    register Bytef *strend = s->window + s->strstart + MAX_MATCH; + +    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. +     * It is easy to get rid of this optimization if necessary. +     */ +    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + +    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + +    Assert(cur_match < s->strstart, "no future"); + +    match = s->window + cur_match; + +    /* Return failure if the match length is less than 2: +     */ +    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + +    /* The check at best_len-1 can be removed because it will be made +     * again later. (This heuristic is not always a win.) +     * It is not necessary to compare scan[2] and match[2] since they +     * are always equal when the other bytes match, given that +     * the hash keys are equal and that HASH_BITS >= 8. +     */ +    scan += 2, match += 2; +    Assert(*scan == *match, "match[2]?"); + +    /* We check for insufficient lookahead only every 8th comparison; +     * the 256th check will be made at strstart+258. +     */ +    do { +    } while (*++scan == *++match && *++scan == *++match && +             *++scan == *++match && *++scan == *++match && +             *++scan == *++match && *++scan == *++match && +             *++scan == *++match && *++scan == *++match && +             scan < strend); + +    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + +    len = MAX_MATCH - (int)(strend - scan); + +    if (len < MIN_MATCH) return MIN_MATCH - 1; + +    s->match_start = cur_match; +    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) +    deflate_state *s; +    IPos start, match; +    int length; +{ +    /* check that the match is indeed a match */ +    if (zmemcmp(s->window + match, +                s->window + start, length) != EQUAL) { +        fprintf(stderr, " start %u, match %u, length %d\n", +                start, match, length); +        do { +            fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); +        } while (--length != 0); +        z_error("invalid match"); +    } +    if (z_verbose > 1) { +        fprintf(stderr,"\\[%d,%d]", start-match, length); +        do { putc(s->window[start++], stderr); } while (--length != 0); +    } +} +#else +#  define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + *    At least one byte has been read, or avail_in == 0; reads are + *    performed for at least two bytes (required for the zip translate_eol + *    option -- not supported here). + */ +local void fill_window(s) +    deflate_state *s; +{ +    register unsigned n, m; +    register Posf *p; +    unsigned more;    /* Amount of free space at the end of the window. */ +    uInt wsize = s->w_size; + +    do { +        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + +        /* Deal with !@#$% 64K limit: */ +        if (sizeof(int) <= 2) { +            if (more == 0 && s->strstart == 0 && s->lookahead == 0) { +                more = wsize; + +            } else if (more == (unsigned)(-1)) { +                /* Very unlikely, but possible on 16 bit machine if +                 * strstart == 0 && lookahead == 1 (input done a byte at time) +                 */ +                more--; +            } +        } + +        /* If the window is almost full and there is insufficient lookahead, +         * move the upper half to the lower one to make room in the upper half. +         */ +        if (s->strstart >= wsize+MAX_DIST(s)) { + +            zmemcpy(s->window, s->window+wsize, (unsigned)wsize); +            s->match_start -= wsize; +            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */ +            s->block_start -= (long) wsize; + +            /* Slide the hash table (could be avoided with 32 bit values +               at the expense of memory usage). We slide even when level == 0 +               to keep the hash table consistent if we switch back to level > 0 +               later. (Using level 0 permanently is not an optimal usage of +               zlib, so we don't care about this pathological case.) +             */ +            /* %%% avoid this when Z_RLE */ +            n = s->hash_size; +            p = &s->head[n]; +            do { +                m = *--p; +                *p = (Pos)(m >= wsize ? m-wsize : NIL); +            } while (--n); + +            n = wsize; +#ifndef FASTEST +            p = &s->prev[n]; +            do { +                m = *--p; +                *p = (Pos)(m >= wsize ? m-wsize : NIL); +                /* If n is not on any hash chain, prev[n] is garbage but +                 * its value will never be used. +                 */ +            } while (--n); +#endif +            more += wsize; +        } +        if (s->strm->avail_in == 0) return; + +        /* If there was no sliding: +         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && +         *    more == window_size - lookahead - strstart +         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) +         * => more >= window_size - 2*WSIZE + 2 +         * In the BIG_MEM or MMAP case (not yet supported), +         *   window_size == input_size + MIN_LOOKAHEAD  && +         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. +         * Otherwise, window_size == 2*WSIZE so more >= 2. +         * If there was sliding, more >= WSIZE. So in all cases, more >= 2. +         */ +        Assert(more >= 2, "more < 2"); + +        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); +        s->lookahead += n; + +        /* Initialize the hash value now that we have some input: */ +        if (s->lookahead >= MIN_MATCH) { +            s->ins_h = s->window[s->strstart]; +            UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 +            Call UPDATE_HASH() MIN_MATCH-3 more times +#endif +        } +        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, +         * but this is not important since only literal bytes will be emitted. +         */ + +    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ +   _tr_flush_block(s, (s->block_start >= 0L ? \ +                   (charf *)&s->window[(unsigned)s->block_start] : \ +                   (charf *)Z_NULL), \ +                (ulg)((long)s->strstart - s->block_start), \ +                (eof)); \ +   s->block_start = s->strstart; \ +   flush_pending(s->strm); \ +   Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ +   FLUSH_BLOCK_ONLY(s, eof); \ +   if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) +    deflate_state *s; +    int flush; +{ +    /* Stored blocks are limited to 0xffff bytes, pending_buf is limited +     * to pending_buf_size, and each stored block has a 5 byte header: +     */ +    ulg max_block_size = 0xffff; +    ulg max_start; + +    if (max_block_size > s->pending_buf_size - 5) { +        max_block_size = s->pending_buf_size - 5; +    } + +    /* Copy as much as possible from input to output: */ +    for (;;) { +        /* Fill the window as much as possible: */ +        if (s->lookahead <= 1) { + +            Assert(s->strstart < s->w_size+MAX_DIST(s) || +                   s->block_start >= (long)s->w_size, "slide too late"); + +            fill_window(s); +            if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + +            if (s->lookahead == 0) break; /* flush the current block */ +        } +        Assert(s->block_start >= 0L, "block gone"); + +        s->strstart += s->lookahead; +        s->lookahead = 0; + +        /* Emit a stored block if pending_buf will be full: */ +        max_start = s->block_start + max_block_size; +        if (s->strstart == 0 || (ulg)s->strstart >= max_start) { +            /* strstart == 0 is possible when wraparound on 16-bit machine */ +            s->lookahead = (uInt)(s->strstart - max_start); +            s->strstart = (uInt)max_start; +            FLUSH_BLOCK(s, 0); +        } +        /* Flush if we may have to slide, otherwise block_start may become +         * negative and the data will be gone: +         */ +        if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { +            FLUSH_BLOCK(s, 0); +        } +    } +    FLUSH_BLOCK(s, flush == Z_FINISH); +    return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) +    deflate_state *s; +    int flush; +{ +    IPos hash_head = NIL; /* head of the hash chain */ +    int bflush;           /* set if current block must be flushed */ + +    for (;;) { +        /* Make sure that we always have enough lookahead, except +         * at the end of the input file. We need MAX_MATCH bytes +         * for the next match, plus MIN_MATCH bytes to insert the +         * string following the next match. +         */ +        if (s->lookahead < MIN_LOOKAHEAD) { +            fill_window(s); +            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { +                return need_more; +            } +            if (s->lookahead == 0) break; /* flush the current block */ +        } + +        /* Insert the string window[strstart .. strstart+2] in the +         * dictionary, and set hash_head to the head of the hash chain: +         */ +        if (s->lookahead >= MIN_MATCH) { +            INSERT_STRING(s, s->strstart, hash_head); +        } + +        /* Find the longest match, discarding those <= prev_length. +         * At this point we have always match_length < MIN_MATCH +         */ +        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { +            /* To simplify the code, we prevent matches with the string +             * of window index 0 (in particular we have to avoid a match +             * of the string with itself at the start of the input file). +             */ +#ifdef FASTEST +            if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || +                (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { +                s->match_length = longest_match_fast (s, hash_head); +            } +#else +            if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { +                s->match_length = longest_match (s, hash_head); +            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { +                s->match_length = longest_match_fast (s, hash_head); +            } +#endif +            /* longest_match() or longest_match_fast() sets match_start */ +        } +        if (s->match_length >= MIN_MATCH) { +            check_match(s, s->strstart, s->match_start, s->match_length); + +            _tr_tally_dist(s, s->strstart - s->match_start, +                           s->match_length - MIN_MATCH, bflush); + +            s->lookahead -= s->match_length; + +            /* Insert new strings in the hash table only if the match length +             * is not too large. This saves time but degrades compression. +             */ +#ifndef FASTEST +            if (s->match_length <= s->max_insert_length && +                s->lookahead >= MIN_MATCH) { +                s->match_length--; /* string at strstart already in table */ +                do { +                    s->strstart++; +                    INSERT_STRING(s, s->strstart, hash_head); +                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are +                     * always MIN_MATCH bytes ahead. +                     */ +                } while (--s->match_length != 0); +                s->strstart++; +            } else +#endif +            { +                s->strstart += s->match_length; +                s->match_length = 0; +                s->ins_h = s->window[s->strstart]; +                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 +                Call UPDATE_HASH() MIN_MATCH-3 more times +#endif +                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not +                 * matter since it will be recomputed at next deflate call. +                 */ +            } +        } else { +            /* No match, output a literal byte */ +            Tracevv((stderr,"%c", s->window[s->strstart])); +            _tr_tally_lit (s, s->window[s->strstart], bflush); +            s->lookahead--; +            s->strstart++; +        } +        if (bflush) FLUSH_BLOCK(s, 0); +    } +    FLUSH_BLOCK(s, flush == Z_FINISH); +    return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) +    deflate_state *s; +    int flush; +{ +    IPos hash_head = NIL;    /* head of hash chain */ +    int bflush;              /* set if current block must be flushed */ + +    /* Process the input block. */ +    for (;;) { +        /* Make sure that we always have enough lookahead, except +         * at the end of the input file. We need MAX_MATCH bytes +         * for the next match, plus MIN_MATCH bytes to insert the +         * string following the next match. +         */ +        if (s->lookahead < MIN_LOOKAHEAD) { +            fill_window(s); +            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { +                return need_more; +            } +            if (s->lookahead == 0) break; /* flush the current block */ +        } + +        /* Insert the string window[strstart .. strstart+2] in the +         * dictionary, and set hash_head to the head of the hash chain: +         */ +        if (s->lookahead >= MIN_MATCH) { +            INSERT_STRING(s, s->strstart, hash_head); +        } + +        /* Find the longest match, discarding those <= prev_length. +         */ +        s->prev_length = s->match_length, s->prev_match = s->match_start; +        s->match_length = MIN_MATCH-1; + +        if (hash_head != NIL && s->prev_length < s->max_lazy_match && +            s->strstart - hash_head <= MAX_DIST(s)) { +            /* To simplify the code, we prevent matches with the string +             * of window index 0 (in particular we have to avoid a match +             * of the string with itself at the start of the input file). +             */ +            if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { +                s->match_length = longest_match (s, hash_head); +            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { +                s->match_length = longest_match_fast (s, hash_head); +            } +            /* longest_match() or longest_match_fast() sets match_start */ + +            if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 +                || (s->match_length == MIN_MATCH && +                    s->strstart - s->match_start > TOO_FAR) +#endif +                )) { + +                /* If prev_match is also MIN_MATCH, match_start is garbage +                 * but we will ignore the current match anyway. +                 */ +                s->match_length = MIN_MATCH-1; +            } +        } +        /* If there was a match at the previous step and the current +         * match is not better, output the previous match: +         */ +        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { +            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; +            /* Do not insert strings in hash table beyond this. */ + +            check_match(s, s->strstart-1, s->prev_match, s->prev_length); + +            _tr_tally_dist(s, s->strstart -1 - s->prev_match, +                           s->prev_length - MIN_MATCH, bflush); + +            /* Insert in hash table all strings up to the end of the match. +             * strstart-1 and strstart are already inserted. If there is not +             * enough lookahead, the last two strings are not inserted in +             * the hash table. +             */ +            s->lookahead -= s->prev_length-1; +            s->prev_length -= 2; +            do { +                if (++s->strstart <= max_insert) { +                    INSERT_STRING(s, s->strstart, hash_head); +                } +            } while (--s->prev_length != 0); +            s->match_available = 0; +            s->match_length = MIN_MATCH-1; +            s->strstart++; + +            if (bflush) FLUSH_BLOCK(s, 0); + +        } else if (s->match_available) { +            /* If there was no match at the previous position, output a +             * single literal. If there was a match but the current match +             * is longer, truncate the previous match to a single literal. +             */ +            Tracevv((stderr,"%c", s->window[s->strstart-1])); +            _tr_tally_lit(s, s->window[s->strstart-1], bflush); +            if (bflush) { +                FLUSH_BLOCK_ONLY(s, 0); +            } +            s->strstart++; +            s->lookahead--; +            if (s->strm->avail_out == 0) return need_more; +        } else { +            /* There is no previous match to compare with, wait for +             * the next step to decide. +             */ +            s->match_available = 1; +            s->strstart++; +            s->lookahead--; +        } +    } +    Assert (flush != Z_NO_FLUSH, "no flush?"); +    if (s->match_available) { +        Tracevv((stderr,"%c", s->window[s->strstart-1])); +        _tr_tally_lit(s, s->window[s->strstart-1], bflush); +        s->match_available = 0; +    } +    FLUSH_BLOCK(s, flush == Z_FINISH); +    return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one.  Do not maintain a hash table.  (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) +    deflate_state *s; +    int flush; +{ +    int bflush;         /* set if current block must be flushed */ +    uInt run;           /* length of run */ +    uInt max;           /* maximum length of run */ +    uInt prev;          /* byte at distance one to match */ +    Bytef *scan;        /* scan for end of run */ + +    for (;;) { +        /* Make sure that we always have enough lookahead, except +         * at the end of the input file. We need MAX_MATCH bytes +         * for the longest encodable run. +         */ +        if (s->lookahead < MAX_MATCH) { +            fill_window(s); +            if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { +                return need_more; +            } +            if (s->lookahead == 0) break; /* flush the current block */ +        } + +        /* See how many times the previous byte repeats */ +        run = 0; +        if (s->strstart > 0) {      /* if there is a previous byte, that is */ +            max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; +            scan = s->window + s->strstart - 1; +            prev = *scan++; +            do { +                if (*scan++ != prev) +                    break; +            } while (++run < max); +        } + +        /* Emit match if have run of MIN_MATCH or longer, else emit literal */ +        if (run >= MIN_MATCH) { +            check_match(s, s->strstart, s->strstart - 1, run); +            _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); +            s->lookahead -= run; +            s->strstart += run; +        } else { +            /* No match, output a literal byte */ +            Tracevv((stderr,"%c", s->window[s->strstart])); +            _tr_tally_lit (s, s->window[s->strstart], bflush); +            s->lookahead--; +            s->strstart++; +        } +        if (bflush) FLUSH_BLOCK(s, 0); +    } +    FLUSH_BLOCK(s, flush == Z_FINISH); +    return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/src/zlib/deflate.h b/src/zlib/deflate.h new file mode 100644 index 0000000..05a5ab3 --- /dev/null +++ b/src/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is +   part of the implementation of the compression library and is +   subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and +   trailer creation by deflate().  NO_GZIP would be used to avoid linking in +   the crc code when it is not needed.  For shared libraries, gzip encoding +   should be left enabled. */ +#ifndef NO_GZIP +#  define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS  256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES   30 +/* number of distance codes */ + +#define BL_CODES  19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE    42 +#define EXTRA_STATE   69 +#define NAME_STATE    73 +#define COMMENT_STATE 91 +#define HCRC_STATE   103 +#define BUSY_STATE   113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { +    union { +        ush  freq;       /* frequency count */ +        ush  code;       /* bit string */ +    } fc; +    union { +        ush  dad;        /* father node in Huffman tree */ +        ush  len;        /* length of bit string */ +    } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad  dl.dad +#define Len  dl.len + +typedef struct static_tree_desc_s  static_tree_desc; + +typedef struct tree_desc_s { +    ct_data *dyn_tree;           /* the dynamic tree */ +    int     max_code;            /* largest code with non zero frequency */ +    static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { +    z_streamp strm;      /* pointer back to this zlib stream */ +    int   status;        /* as the name implies */ +    Bytef *pending_buf;  /* output still pending */ +    ulg   pending_buf_size; /* size of pending_buf */ +    Bytef *pending_out;  /* next pending byte to output to the stream */ +    uInt   pending;      /* nb of bytes in the pending buffer */ +    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */ +    gz_headerp  gzhead;  /* gzip header information to write */ +    uInt   gzindex;      /* where in extra, name, or comment */ +    Byte  method;        /* STORED (for zip only) or DEFLATED */ +    int   last_flush;    /* value of flush param for previous deflate call */ + +                /* used by deflate.c: */ + +    uInt  w_size;        /* LZ77 window size (32K by default) */ +    uInt  w_bits;        /* log2(w_size)  (8..16) */ +    uInt  w_mask;        /* w_size - 1 */ + +    Bytef *window; +    /* Sliding window. Input bytes are read into the second half of the window, +     * and move to the first half later to keep a dictionary of at least wSize +     * bytes. With this organization, matches are limited to a distance of +     * wSize-MAX_MATCH bytes, but this ensures that IO is always +     * performed with a length multiple of the block size. Also, it limits +     * the window size to 64K, which is quite useful on MSDOS. +     * To do: use the user input buffer as sliding window. +     */ + +    ulg window_size; +    /* Actual size of window: 2*wSize, except when the user input buffer +     * is directly used as sliding window. +     */ + +    Posf *prev; +    /* Link to older string with same hash index. To limit the size of this +     * array to 64K, this link is maintained only for the last 32K strings. +     * An index in this array is thus a window index modulo 32K. +     */ + +    Posf *head; /* Heads of the hash chains or NIL. */ + +    uInt  ins_h;          /* hash index of string to be inserted */ +    uInt  hash_size;      /* number of elements in hash table */ +    uInt  hash_bits;      /* log2(hash_size) */ +    uInt  hash_mask;      /* hash_size-1 */ + +    uInt  hash_shift; +    /* Number of bits by which ins_h must be shifted at each input +     * step. It must be such that after MIN_MATCH steps, the oldest +     * byte no longer takes part in the hash key, that is: +     *   hash_shift * MIN_MATCH >= hash_bits +     */ + +    long block_start; +    /* Window position at the beginning of the current output block. Gets +     * negative when the window is moved backwards. +     */ + +    uInt match_length;           /* length of best match */ +    IPos prev_match;             /* previous match */ +    int match_available;         /* set if previous match exists */ +    uInt strstart;               /* start of string to insert */ +    uInt match_start;            /* start of matching string */ +    uInt lookahead;              /* number of valid bytes ahead in window */ + +    uInt prev_length; +    /* Length of the best match at previous step. Matches not greater than this +     * are discarded. This is used in the lazy match evaluation. +     */ + +    uInt max_chain_length; +    /* To speed up deflation, hash chains are never searched beyond this +     * length.  A higher limit improves compression ratio but degrades the +     * speed. +     */ + +    uInt max_lazy_match; +    /* Attempt to find a better match only when the current match is strictly +     * smaller than this value. This mechanism is used only for compression +     * levels >= 4. +     */ +#   define max_insert_length  max_lazy_match +    /* Insert new strings in the hash table only if the match length is not +     * greater than this length. This saves time but degrades compression. +     * max_insert_length is used only for compression levels <= 3. +     */ + +    int level;    /* compression level (1..9) */ +    int strategy; /* favor or force Huffman coding*/ + +    uInt good_match; +    /* Use a faster search when the previous match is longer than this */ + +    int nice_match; /* Stop searching when current match exceeds this */ + +                /* used by trees.c: */ +    /* Didn't use ct_data typedef below to supress compiler warning */ +    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */ +    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ +    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */ + +    struct tree_desc_s l_desc;               /* desc. for literal tree */ +    struct tree_desc_s d_desc;               /* desc. for distance tree */ +    struct tree_desc_s bl_desc;              /* desc. for bit length tree */ + +    ush bl_count[MAX_BITS+1]; +    /* number of codes at each bit length for an optimal tree */ + +    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */ +    int heap_len;               /* number of elements in the heap */ +    int heap_max;               /* element of largest frequency */ +    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. +     * The same heap array is used to build all trees. +     */ + +    uch depth[2*L_CODES+1]; +    /* Depth of each subtree used as tie breaker for trees of equal frequency +     */ + +    uchf *l_buf;          /* buffer for literals or lengths */ + +    uInt  lit_bufsize; +    /* Size of match buffer for literals/lengths.  There are 4 reasons for +     * limiting lit_bufsize to 64K: +     *   - frequencies can be kept in 16 bit counters +     *   - if compression is not successful for the first block, all input +     *     data is still in the window so we can still emit a stored block even +     *     when input comes from standard input.  (This can also be done for +     *     all blocks if lit_bufsize is not greater than 32K.) +     *   - if compression is not successful for a file smaller than 64K, we can +     *     even emit a stored file instead of a stored block (saving 5 bytes). +     *     This is applicable only for zip (not gzip or zlib). +     *   - creating new Huffman trees less frequently may not provide fast +     *     adaptation to changes in the input data statistics. (Take for +     *     example a binary file with poorly compressible code followed by +     *     a highly compressible string table.) Smaller buffer sizes give +     *     fast adaptation but have of course the overhead of transmitting +     *     trees more frequently. +     *   - I can't count above 4 +     */ + +    uInt last_lit;      /* running index in l_buf */ + +    ushf *d_buf; +    /* Buffer for distances. To simplify the code, d_buf and l_buf have +     * the same number of elements. To use different lengths, an extra flag +     * array would be necessary. +     */ + +    ulg opt_len;        /* bit length of current block with optimal trees */ +    ulg static_len;     /* bit length of current block with static trees */ +    uInt matches;       /* number of string matches in current block */ +    int last_eob_len;   /* bit length of EOB code for last block */ + +#ifdef DEBUG +    ulg compressed_len; /* total bit length of compressed file mod 2^32 */ +    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */ +#endif + +    ush bi_buf; +    /* Output buffer. bits are inserted starting at the bottom (least +     * significant bits). +     */ +    int bi_valid; +    /* Number of valid bits in bi_buf.  All bits above the last valid bit +     * are always zero. +     */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +        /* in trees.c */ +void _tr_init         OF((deflate_state *s)); +int  _tr_tally        OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block  OF((deflate_state *s, charf *buf, ulg stored_len, +                          int eof)); +void _tr_align        OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, +                          int eof)); + +#define d_code(dist) \ +   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) +  extern uch _length_code[]; +  extern uch _dist_code[]; +#else +  extern const uch _length_code[]; +  extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ +  { uch cc = (c); \ +    s->d_buf[s->last_lit] = 0; \ +    s->l_buf[s->last_lit++] = cc; \ +    s->dyn_ltree[cc].Freq++; \ +    flush = (s->last_lit == s->lit_bufsize-1); \ +   } +# define _tr_tally_dist(s, distance, length, flush) \ +  { uch len = (length); \ +    ush dist = (distance); \ +    s->d_buf[s->last_lit] = dist; \ +    s->l_buf[s->last_lit++] = len; \ +    dist--; \ +    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ +    s->dyn_dtree[d_code(dist)].Freq++; \ +    flush = (s->last_lit == s->lit_bufsize-1); \ +  } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ +              flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/zlib/inffast.c b/src/zlib/inffast.c new file mode 100644 index 0000000..bbee92e --- /dev/null +++ b/src/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. +   Based on testing to date, +   Pre-increment preferred for: +   - PowerPC G3 (Adler) +   - MIPS R5000 (Randers-Pehrson) +   Post-increment preferred for: +   - none +   No measurable difference: +   - Pentium III (Anderson) +   - M68060 (Nikl) + */ +#ifdef POSTINC +#  define OFF 0 +#  define PUP(a) *(a)++ +#else +#  define OFF 1 +#  define PUP(a) *++(a) +#endif + +/* +   Decode literal, length, and distance codes and write out the resulting +   literal and match bytes until either not enough input or output is +   available, an end-of-block is encountered, or a data error is encountered. +   When large enough input and output buffers are supplied to inflate(), for +   example, a 16K input buffer and a 64K output buffer, more than 95% of the +   inflate execution time is spent in this routine. + +   Entry assumptions: + +        state->mode == LEN +        strm->avail_in >= 6 +        strm->avail_out >= 258 +        start >= strm->avail_out +        state->bits < 8 + +   On return, state->mode is one of: + +        LEN -- ran out of enough output space or enough available input +        TYPE -- reached end of block code, inflate() to interpret next block +        BAD -- error in block data + +   Notes: + +    - The maximum input bits used by a length/distance pair is 15 bits for the +      length code, 5 bits for the length extra, 15 bits for the distance code, +      and 13 bits for the distance extra.  This totals 48 bits, or six bytes. +      Therefore if strm->avail_in >= 6, then there is enough input to avoid +      checking for available input while decoding. + +    - The maximum bytes that a single length/distance pair can output is 258 +      bytes, which is the maximum length that can be coded.  inflate_fast() +      requires strm->avail_out >= 258 for each loop to avoid checking for +      output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start;         /* inflate()'s starting value for strm->avail_out */ +{ +    struct inflate_state FAR *state; +    unsigned char FAR *in;      /* local strm->next_in */ +    unsigned char FAR *last;    /* while in < last, enough input available */ +    unsigned char FAR *out;     /* local strm->next_out */ +    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */ +    unsigned char FAR *end;     /* while out < end, enough space available */ +#ifdef INFLATE_STRICT +    unsigned dmax;              /* maximum distance from zlib header */ +#endif +    unsigned wsize;             /* window size or zero if not using window */ +    unsigned whave;             /* valid bytes in the window */ +    unsigned write;             /* window write index */ +    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */ +    unsigned long hold;         /* local strm->hold */ +    unsigned bits;              /* local strm->bits */ +    code const FAR *lcode;      /* local strm->lencode */ +    code const FAR *dcode;      /* local strm->distcode */ +    unsigned lmask;             /* mask for first level of length codes */ +    unsigned dmask;             /* mask for first level of distance codes */ +    code this;                  /* retrieved table entry */ +    unsigned op;                /* code bits, operation, extra bits, or */ +                                /*  window position, window bytes to copy */ +    unsigned len;               /* match length, unused bytes */ +    unsigned dist;              /* match distance */ +    unsigned char FAR *from;    /* where to copy match from */ + +    /* copy state to local variables */ +    state = (struct inflate_state FAR *)strm->state; +    in = strm->next_in - OFF; +    last = in + (strm->avail_in - 5); +    out = strm->next_out - OFF; +    beg = out - (start - strm->avail_out); +    end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT +    dmax = state->dmax; +#endif +    wsize = state->wsize; +    whave = state->whave; +    write = state->write; +    window = state->window; +    hold = state->hold; +    bits = state->bits; +    lcode = state->lencode; +    dcode = state->distcode; +    lmask = (1U << state->lenbits) - 1; +    dmask = (1U << state->distbits) - 1; + +    /* decode literals and length/distances until end-of-block or not enough +       input data or output space */ +    do { +        if (bits < 15) { +            hold += (unsigned long)(PUP(in)) << bits; +            bits += 8; +            hold += (unsigned long)(PUP(in)) << bits; +            bits += 8; +        } +        this = lcode[hold & lmask]; +      dolen: +        op = (unsigned)(this.bits); +        hold >>= op; +        bits -= op; +        op = (unsigned)(this.op); +        if (op == 0) {                          /* literal */ +            Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? +                    "inflate:         literal '%c'\n" : +                    "inflate:         literal 0x%02x\n", this.val)); +            PUP(out) = (unsigned char)(this.val); +        } +        else if (op & 16) {                     /* length base */ +            len = (unsigned)(this.val); +            op &= 15;                           /* number of extra bits */ +            if (op) { +                if (bits < op) { +                    hold += (unsigned long)(PUP(in)) << bits; +                    bits += 8; +                } +                len += (unsigned)hold & ((1U << op) - 1); +                hold >>= op; +                bits -= op; +            } +            Tracevv((stderr, "inflate:         length %u\n", len)); +            if (bits < 15) { +                hold += (unsigned long)(PUP(in)) << bits; +                bits += 8; +                hold += (unsigned long)(PUP(in)) << bits; +                bits += 8; +            } +            this = dcode[hold & dmask]; +          dodist: +            op = (unsigned)(this.bits); +            hold >>= op; +            bits -= op; +            op = (unsigned)(this.op); +            if (op & 16) {                      /* distance base */ +                dist = (unsigned)(this.val); +                op &= 15;                       /* number of extra bits */ +                if (bits < op) { +                    hold += (unsigned long)(PUP(in)) << bits; +                    bits += 8; +                    if (bits < op) { +                        hold += (unsigned long)(PUP(in)) << bits; +                        bits += 8; +                    } +                } +                dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT +                if (dist > dmax) { +                    strm->msg = (char *)"invalid distance too far back"; +                    state->mode = BAD; +                    break; +                } +#endif +                hold >>= op; +                bits -= op; +                Tracevv((stderr, "inflate:         distance %u\n", dist)); +                op = (unsigned)(out - beg);     /* max distance in output */ +                if (dist > op) {                /* see if copy from window */ +                    op = dist - op;             /* distance back in window */ +                    if (op > whave) { +                        strm->msg = (char *)"invalid distance too far back"; +                        state->mode = BAD; +                        break; +                    } +                    from = window - OFF; +                    if (write == 0) {           /* very common case */ +                        from += wsize - op; +                        if (op < len) {         /* some from window */ +                            len -= op; +                            do { +                                PUP(out) = PUP(from); +                            } while (--op); +                            from = out - dist;  /* rest from output */ +                        } +                    } +                    else if (write < op) {      /* wrap around window */ +                        from += wsize + write - op; +                        op -= write; +                        if (op < len) {         /* some from end of window */ +                            len -= op; +                            do { +                                PUP(out) = PUP(from); +                            } while (--op); +                            from = window - OFF; +                            if (write < len) {  /* some from start of window */ +                                op = write; +                                len -= op; +                                do { +                                    PUP(out) = PUP(from); +                                } while (--op); +                                from = out - dist;      /* rest from output */ +                            } +                        } +                    } +                    else {                      /* contiguous in window */ +                        from += write - op; +                        if (op < len) {         /* some from window */ +                            len -= op; +                            do { +                                PUP(out) = PUP(from); +                            } while (--op); +                            from = out - dist;  /* rest from output */ +                        } +                    } +                    while (len > 2) { +                        PUP(out) = PUP(from); +                        PUP(out) = PUP(from); +                        PUP(out) = PUP(from); +                        len -= 3; +                    } +                    if (len) { +                        PUP(out) = PUP(from); +                        if (len > 1) +                            PUP(out) = PUP(from); +                    } +                } +                else { +                    from = out - dist;          /* copy direct from output */ +                    do {                        /* minimum length is three */ +                        PUP(out) = PUP(from); +                        PUP(out) = PUP(from); +                        PUP(out) = PUP(from); +                        len -= 3; +                    } while (len > 2); +                    if (len) { +                        PUP(out) = PUP(from); +                        if (len > 1) +                            PUP(out) = PUP(from); +                    } +                } +            } +            else if ((op & 64) == 0) {          /* 2nd level distance code */ +                this = dcode[this.val + (hold & ((1U << op) - 1))]; +                goto dodist; +            } +            else { +                strm->msg = (char *)"invalid distance code"; +                state->mode = BAD; +                break; +            } +        } +        else if ((op & 64) == 0) {              /* 2nd level length code */ +            this = lcode[this.val + (hold & ((1U << op) - 1))]; +            goto dolen; +        } +        else if (op & 32) {                     /* end-of-block */ +            Tracevv((stderr, "inflate:         end of block\n")); +            state->mode = TYPE; +            break; +        } +        else { +            strm->msg = (char *)"invalid literal/length code"; +            state->mode = BAD; +            break; +        } +    } while (in < last && out < end); + +    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ +    len = bits >> 3; +    in -= len; +    bits -= len << 3; +    hold &= (1U << bits) - 1; + +    /* update state and return */ +    strm->next_in = in + OFF; +    strm->next_out = out + OFF; +    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); +    strm->avail_out = (unsigned)(out < end ? +                                 257 + (end - out) : 257 - (out - end)); +    state->hold = hold; +    state->bits = bits; +    return; +} + +/* +   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): +   - Using bit fields for code structure +   - Different op definition to avoid & for extra bits (do & for table bits) +   - Three separate decoding do-loops for direct, window, and write == 0 +   - Special case for distance > 1 copies to do overlapped load and store copy +   - Explicit branch predictions (based on measured branch probabilities) +   - Deferring match copy and interspersed it with decoding subsequent codes +   - Swapping literal/length else +   - Swapping window/direct else +   - Larger unrolled copy loops (three is about right) +   - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/src/zlib/inffast.h b/src/zlib/inffast.h new file mode 100644 index 0000000..1e88d2d --- /dev/null +++ b/src/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is +   part of the implementation of the compression library and is +   subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/zlib/inffixed.h b/src/zlib/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/src/zlib/inffixed.h @@ -0,0 +1,94 @@ +    /* inffixed.h -- table for decoding fixed codes +     * Generated automatically by makefixed(). +     */ + +    /* WARNING: this file should *not* be used by applications. It +       is part of the implementation of the compression library and +       is subject to change. Applications should only use zlib.h. +     */ + +    static const code lenfix[512] = { +        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, +        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, +        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, +        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, +        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, +        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, +        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, +        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, +        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, +        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, +        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, +        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, +        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, +        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, +        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, +        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, +        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, +        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, +        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, +        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, +        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, +        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, +        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, +        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, +        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, +        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, +        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, +        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, +        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, +        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, +        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, +        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, +        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, +        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, +        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, +        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, +        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, +        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, +        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, +        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, +        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, +        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, +        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, +        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, +        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, +        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, +        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, +        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, +        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, +        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, +        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, +        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, +        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, +        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, +        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, +        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, +        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, +        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, +        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, +        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, +        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, +        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, +        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, +        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, +        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, +        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, +        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, +        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, +        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, +        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, +        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, +        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, +        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, +        {0,9,255} +    }; + +    static const code distfix[32] = { +        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, +        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, +        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, +        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, +        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, +        {22,5,193},{64,5,0} +    }; diff --git a/src/zlib/inflate.c b/src/zlib/inflate.c new file mode 100644 index 0000000..792fdee --- /dev/null +++ b/src/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0    24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + *   creation of window when not needed, minimize use of window when it is + *   needed, make inffast.c even faster, implement gzip decoding, and to + *   improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1    25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2    4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + *   to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3    22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + *   buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4    1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + *   source file infback.c to provide a call-back interface to inflate for + *   programs like gzip and unzip -- uses window as output buffer to avoid + *   window copying + * + * 1.2.beta5    1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + *   input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6    4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + *   make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7    27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0        9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + *   for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + *   and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +#  ifndef BUILDFIXED +#    define BUILDFIXED +#  endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED +   void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, +                              unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ +    struct inflate_state FAR *state; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    strm->total_in = strm->total_out = state->total = 0; +    strm->msg = Z_NULL; +    strm->adler = 1;        /* to support ill-conceived Java test suite */ +    state->mode = HEAD; +    state->last = 0; +    state->havedict = 0; +    state->dmax = 32768U; +    state->head = Z_NULL; +    state->wsize = 0; +    state->whave = 0; +    state->write = 0; +    state->hold = 0; +    state->bits = 0; +    state->lencode = state->distcode = state->next = state->codes; +    Tracev((stderr, "inflate: reset\n")); +    return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ +    struct inflate_state FAR *state; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; +    value &= (1L << bits) - 1; +    state->hold += value << state->bits; +    state->bits += bits; +    return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ +    struct inflate_state FAR *state; + +    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || +        stream_size != (int)(sizeof(z_stream))) +        return Z_VERSION_ERROR; +    if (strm == Z_NULL) return Z_STREAM_ERROR; +    strm->msg = Z_NULL;                 /* in case we return an error */ +    if (strm->zalloc == (alloc_func)0) { +        strm->zalloc = zcalloc; +        strm->opaque = (voidpf)0; +    } +    if (strm->zfree == (free_func)0) strm->zfree = zcfree; +    state = (struct inflate_state FAR *) +            ZALLOC(strm, 1, sizeof(struct inflate_state)); +    if (state == Z_NULL) return Z_MEM_ERROR; +    Tracev((stderr, "inflate: allocated\n")); +    strm->state = (struct internal_state FAR *)state; +    if (windowBits < 0) { +        state->wrap = 0; +        windowBits = -windowBits; +    } +    else { +        state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP +        if (windowBits < 48) windowBits &= 15; +#endif +    } +    if (windowBits < 8 || windowBits > 15) { +        ZFREE(strm, state); +        strm->state = Z_NULL; +        return Z_STREAM_ERROR; +    } +    state->wbits = (unsigned)windowBits; +    state->window = Z_NULL; +    return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ +    return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* +   Return state with length and distance decoding tables and index sizes set to +   fixed code decoding.  Normally this returns fixed tables from inffixed.h. +   If BUILDFIXED is defined, then instead this routine builds the tables the +   first time it's called, and returns those tables the first time and +   thereafter.  This reduces the size of the code by about 2K bytes, in +   exchange for a little execution time.  However, BUILDFIXED should not be +   used for threaded applications, since the rewriting of the tables and virgin +   may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED +    static int virgin = 1; +    static code *lenfix, *distfix; +    static code fixed[544]; + +    /* build fixed huffman tables if first call (may not be thread safe) */ +    if (virgin) { +        unsigned sym, bits; +        static code *next; + +        /* literal/length table */ +        sym = 0; +        while (sym < 144) state->lens[sym++] = 8; +        while (sym < 256) state->lens[sym++] = 9; +        while (sym < 280) state->lens[sym++] = 7; +        while (sym < 288) state->lens[sym++] = 8; +        next = fixed; +        lenfix = next; +        bits = 9; +        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + +        /* distance table */ +        sym = 0; +        while (sym < 32) state->lens[sym++] = 5; +        distfix = next; +        bits = 5; +        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + +        /* do this just once */ +        virgin = 0; +    } +#else /* !BUILDFIXED */ +#   include "inffixed.h" +#endif /* BUILDFIXED */ +    state->lencode = lenfix; +    state->lenbits = 9; +    state->distcode = distfix; +    state->distbits = 5; +} + +#ifdef MAKEFIXED +#include <stdio.h> + +/* +   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also +   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes +   those tables to stdout, which would be piped to inffixed.h.  A small program +   can simply call makefixed to do this: + +    void makefixed(void); + +    int main(void) +    { +        makefixed(); +        return 0; +    } + +   Then that can be linked with zlib built with MAKEFIXED defined and run: + +    a.out > inffixed.h + */ +void makefixed() +{ +    unsigned low, size; +    struct inflate_state state; + +    fixedtables(&state); +    puts("    /* inffixed.h -- table for decoding fixed codes"); +    puts("     * Generated automatically by makefixed()."); +    puts("     */"); +    puts(""); +    puts("    /* WARNING: this file should *not* be used by applications."); +    puts("       It is part of the implementation of this library and is"); +    puts("       subject to change. Applications should only use zlib.h."); +    puts("     */"); +    puts(""); +    size = 1U << 9; +    printf("    static const code lenfix[%u] = {", size); +    low = 0; +    for (;;) { +        if ((low % 7) == 0) printf("\n        "); +        printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, +               state.lencode[low].val); +        if (++low == size) break; +        putchar(','); +    } +    puts("\n    };"); +    size = 1U << 5; +    printf("\n    static const code distfix[%u] = {", size); +    low = 0; +    for (;;) { +        if ((low % 6) == 0) printf("\n        "); +        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, +               state.distcode[low].val); +        if (++low == size) break; +        putchar(','); +    } +    puts("\n    };"); +} +#endif /* MAKEFIXED */ + +/* +   Update the window with the last wsize (normally 32K) bytes written before +   returning.  If window does not exist yet, create it.  This is only called +   when a window is already in use, or when output has been written during this +   inflate call, but the end of the deflate stream has not been reached yet. +   It is also called to create a window for dictionary data when a dictionary +   is loaded. + +   Providing output buffers larger than 32K to inflate() should provide a speed +   advantage, since only the last 32K of output is copied to the sliding window +   upon return from inflate(), and since all distances after the first 32K of +   output will fall in the output data, making match copies simpler and faster. +   The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ +    struct inflate_state FAR *state; +    unsigned copy, dist; + +    state = (struct inflate_state FAR *)strm->state; + +    /* if it hasn't been done already, allocate space for the window */ +    if (state->window == Z_NULL) { +        state->window = (unsigned char FAR *) +                        ZALLOC(strm, 1U << state->wbits, +                               sizeof(unsigned char)); +        if (state->window == Z_NULL) return 1; +    } + +    /* if window not in use yet, initialize */ +    if (state->wsize == 0) { +        state->wsize = 1U << state->wbits; +        state->write = 0; +        state->whave = 0; +    } + +    /* copy state->wsize or less output bytes into the circular window */ +    copy = out - strm->avail_out; +    if (copy >= state->wsize) { +        zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); +        state->write = 0; +        state->whave = state->wsize; +    } +    else { +        dist = state->wsize - state->write; +        if (dist > copy) dist = copy; +        zmemcpy(state->window + state->write, strm->next_out - copy, dist); +        copy -= dist; +        if (copy) { +            zmemcpy(state->window, strm->next_out - copy, copy); +            state->write = copy; +            state->whave = state->wsize; +        } +        else { +            state->write += dist; +            if (state->write == state->wsize) state->write = 0; +            if (state->whave < state->wsize) state->whave += dist; +        } +    } +    return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +#  define UPDATE(check, buf, len) \ +    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +#  define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +#  define CRC2(check, word) \ +    do { \ +        hbuf[0] = (unsigned char)(word); \ +        hbuf[1] = (unsigned char)((word) >> 8); \ +        check = crc32(check, hbuf, 2); \ +    } while (0) + +#  define CRC4(check, word) \ +    do { \ +        hbuf[0] = (unsigned char)(word); \ +        hbuf[1] = (unsigned char)((word) >> 8); \ +        hbuf[2] = (unsigned char)((word) >> 16); \ +        hbuf[3] = (unsigned char)((word) >> 24); \ +        check = crc32(check, hbuf, 4); \ +    } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ +    do { \ +        put = strm->next_out; \ +        left = strm->avail_out; \ +        next = strm->next_in; \ +        have = strm->avail_in; \ +        hold = state->hold; \ +        bits = state->bits; \ +    } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ +    do { \ +        strm->next_out = put; \ +        strm->avail_out = left; \ +        strm->next_in = next; \ +        strm->avail_in = have; \ +        state->hold = hold; \ +        state->bits = bits; \ +    } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ +    do { \ +        hold = 0; \ +        bits = 0; \ +    } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() +   if there is no input available. */ +#define PULLBYTE() \ +    do { \ +        if (have == 0) goto inf_leave; \ +        have--; \ +        hold += (unsigned long)(*next++) << bits; \ +        bits += 8; \ +    } while (0) + +/* Assure that there are at least n bits in the bit accumulator.  If there is +   not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ +    do { \ +        while (bits < (unsigned)(n)) \ +            PULLBYTE(); \ +    } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ +    ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ +    do { \ +        hold >>= (n); \ +        bits -= (unsigned)(n); \ +    } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ +    do { \ +        hold >>= bits & 7; \ +        bits -= bits & 7; \ +    } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ +    ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ +     (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* +   inflate() uses a state machine to process as much input data and generate as +   much output data as possible before returning.  The state machine is +   structured roughly as follows: + +    for (;;) switch (state) { +    ... +    case STATEn: +        if (not enough input data or output space to make progress) +            return; +        ... make progress ... +        state = STATEm; +        break; +    ... +    } + +   so when inflate() is called again, the same case is attempted again, and +   if the appropriate resources are provided, the machine proceeds to the +   next state.  The NEEDBITS() macro is usually the way the state evaluates +   whether it can proceed or should return.  NEEDBITS() does the return if +   the requested bits are not available.  The typical use of the BITS macros +   is: + +        NEEDBITS(n); +        ... do something with BITS(n) ... +        DROPBITS(n); + +   where NEEDBITS(n) either returns from inflate() if there isn't enough +   input left to load n bits into the accumulator, or it continues.  BITS(n) +   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops +   the low n bits off the accumulator.  INITBITS() clears the accumulator +   and sets the number of available bits to zero.  BYTEBITS() discards just +   enough bits to put the accumulator on a byte boundary.  After BYTEBITS() +   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + +   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return +   if there is no input available.  The decoding of variable length codes uses +   PULLBYTE() directly in order to pull just enough bytes to decode the next +   code, and no more. + +   Some states loop until they get enough input, making sure that enough +   state information is maintained to continue the loop where it left off +   if NEEDBITS() returns in the loop.  For example, want, need, and keep +   would all have to actually be part of the saved state in case NEEDBITS() +   returns: + +    case STATEw: +        while (want < need) { +            NEEDBITS(n); +            keep[want++] = BITS(n); +            DROPBITS(n); +        } +        state = STATEx; +    case STATEx: + +   As shown above, if the next state is also the next case, then the break +   is omitted. + +   A state may also return if there is not enough output space available to +   complete that state.  Those states are copying stored data, writing a +   literal byte, and copying a matching string. + +   When returning, a "goto inf_leave" is used to update the total counters, +   update the check value, and determine whether any progress has been made +   during that inflate() call in order to return the proper return code. +   Progress is defined as a change in either strm->avail_in or strm->avail_out. +   When there is a window, goto inf_leave will update the window with the last +   output written.  If a goto inf_leave occurs in the middle of decompression +   and there is no window currently, goto inf_leave will create one and copy +   output to the window for the next call of inflate(). + +   In this implementation, the flush parameter of inflate() only affects the +   return code (per zlib.h).  inflate() always writes as much as possible to +   strm->next_out, given the space available and the provided input--the effect +   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers +   the allocation of and copying into a sliding window until necessary, which +   provides the effect documented in zlib.h for Z_FINISH when the entire input +   stream available.  So the only thing the flush parameter actually does is: +   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it +   will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ +    struct inflate_state FAR *state; +    unsigned char FAR *next;    /* next input */ +    unsigned char FAR *put;     /* next output */ +    unsigned have, left;        /* available input and output */ +    unsigned long hold;         /* bit buffer */ +    unsigned bits;              /* bits in bit buffer */ +    unsigned in, out;           /* save starting available input and output */ +    unsigned copy;              /* number of stored or match bytes to copy */ +    unsigned char FAR *from;    /* where to copy match bytes from */ +    code this;                  /* current decoding table entry */ +    code last;                  /* parent table entry */ +    unsigned len;               /* length to copy for repeats, bits to drop */ +    int ret;                    /* return code */ +#ifdef GUNZIP +    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */ +#endif +    static const unsigned short order[19] = /* permutation of code lengths */ +        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +    if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || +        (strm->next_in == Z_NULL && strm->avail_in != 0)) +        return Z_STREAM_ERROR; + +    state = (struct inflate_state FAR *)strm->state; +    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */ +    LOAD(); +    in = have; +    out = left; +    ret = Z_OK; +    for (;;) +        switch (state->mode) { +        case HEAD: +            if (state->wrap == 0) { +                state->mode = TYPEDO; +                break; +            } +            NEEDBITS(16); +#ifdef GUNZIP +            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */ +                state->check = crc32(0L, Z_NULL, 0); +                CRC2(state->check, hold); +                INITBITS(); +                state->mode = FLAGS; +                break; +            } +            state->flags = 0;           /* expect zlib header */ +            if (state->head != Z_NULL) +                state->head->done = -1; +            if (!(state->wrap & 1) ||   /* check if zlib header allowed */ +#else +            if ( +#endif +                ((BITS(8) << 8) + (hold >> 8)) % 31) { +                strm->msg = (char *)"incorrect header check"; +                state->mode = BAD; +                break; +            } +            if (BITS(4) != Z_DEFLATED) { +                strm->msg = (char *)"unknown compression method"; +                state->mode = BAD; +                break; +            } +            DROPBITS(4); +            len = BITS(4) + 8; +            if (len > state->wbits) { +                strm->msg = (char *)"invalid window size"; +                state->mode = BAD; +                break; +            } +            state->dmax = 1U << len; +            Tracev((stderr, "inflate:   zlib header ok\n")); +            strm->adler = state->check = adler32(0L, Z_NULL, 0); +            state->mode = hold & 0x200 ? DICTID : TYPE; +            INITBITS(); +            break; +#ifdef GUNZIP +        case FLAGS: +            NEEDBITS(16); +            state->flags = (int)(hold); +            if ((state->flags & 0xff) != Z_DEFLATED) { +                strm->msg = (char *)"unknown compression method"; +                state->mode = BAD; +                break; +            } +            if (state->flags & 0xe000) { +                strm->msg = (char *)"unknown header flags set"; +                state->mode = BAD; +                break; +            } +            if (state->head != Z_NULL) +                state->head->text = (int)((hold >> 8) & 1); +            if (state->flags & 0x0200) CRC2(state->check, hold); +            INITBITS(); +            state->mode = TIME; +        case TIME: +            NEEDBITS(32); +            if (state->head != Z_NULL) +                state->head->time = hold; +            if (state->flags & 0x0200) CRC4(state->check, hold); +            INITBITS(); +            state->mode = OS; +        case OS: +            NEEDBITS(16); +            if (state->head != Z_NULL) { +                state->head->xflags = (int)(hold & 0xff); +                state->head->os = (int)(hold >> 8); +            } +            if (state->flags & 0x0200) CRC2(state->check, hold); +            INITBITS(); +            state->mode = EXLEN; +        case EXLEN: +            if (state->flags & 0x0400) { +                NEEDBITS(16); +                state->length = (unsigned)(hold); +                if (state->head != Z_NULL) +                    state->head->extra_len = (unsigned)hold; +                if (state->flags & 0x0200) CRC2(state->check, hold); +                INITBITS(); +            } +            else if (state->head != Z_NULL) +                state->head->extra = Z_NULL; +            state->mode = EXTRA; +        case EXTRA: +            if (state->flags & 0x0400) { +                copy = state->length; +                if (copy > have) copy = have; +                if (copy) { +                    if (state->head != Z_NULL && +                        state->head->extra != Z_NULL) { +                        len = state->head->extra_len - state->length; +                        zmemcpy(state->head->extra + len, next, +                                len + copy > state->head->extra_max ? +                                state->head->extra_max - len : copy); +                    } +                    if (state->flags & 0x0200) +                        state->check = crc32(state->check, next, copy); +                    have -= copy; +                    next += copy; +                    state->length -= copy; +                } +                if (state->length) goto inf_leave; +            } +            state->length = 0; +            state->mode = NAME; +        case NAME: +            if (state->flags & 0x0800) { +                if (have == 0) goto inf_leave; +                copy = 0; +                do { +                    len = (unsigned)(next[copy++]); +                    if (state->head != Z_NULL && +                            state->head->name != Z_NULL && +                            state->length < state->head->name_max) +                        state->head->name[state->length++] = len; +                } while (len && copy < have); +                if (state->flags & 0x0200) +                    state->check = crc32(state->check, next, copy); +                have -= copy; +                next += copy; +                if (len) goto inf_leave; +            } +            else if (state->head != Z_NULL) +                state->head->name = Z_NULL; +            state->length = 0; +            state->mode = COMMENT; +        case COMMENT: +            if (state->flags & 0x1000) { +                if (have == 0) goto inf_leave; +                copy = 0; +                do { +                    len = (unsigned)(next[copy++]); +                    if (state->head != Z_NULL && +                            state->head->comment != Z_NULL && +                            state->length < state->head->comm_max) +                        state->head->comment[state->length++] = len; +                } while (len && copy < have); +                if (state->flags & 0x0200) +                    state->check = crc32(state->check, next, copy); +                have -= copy; +                next += copy; +                if (len) goto inf_leave; +            } +            else if (state->head != Z_NULL) +                state->head->comment = Z_NULL; +            state->mode = HCRC; +        case HCRC: +            if (state->flags & 0x0200) { +                NEEDBITS(16); +                if (hold != (state->check & 0xffff)) { +                    strm->msg = (char *)"header crc mismatch"; +                    state->mode = BAD; +                    break; +                } +                INITBITS(); +            } +            if (state->head != Z_NULL) { +                state->head->hcrc = (int)((state->flags >> 9) & 1); +                state->head->done = 1; +            } +            strm->adler = state->check = crc32(0L, Z_NULL, 0); +            state->mode = TYPE; +            break; +#endif +        case DICTID: +            NEEDBITS(32); +            strm->adler = state->check = REVERSE(hold); +            INITBITS(); +            state->mode = DICT; +        case DICT: +            if (state->havedict == 0) { +                RESTORE(); +                return Z_NEED_DICT; +            } +            strm->adler = state->check = adler32(0L, Z_NULL, 0); +            state->mode = TYPE; +        case TYPE: +            if (flush == Z_BLOCK) goto inf_leave; +        case TYPEDO: +            if (state->last) { +                BYTEBITS(); +                state->mode = CHECK; +                break; +            } +            NEEDBITS(3); +            state->last = BITS(1); +            DROPBITS(1); +            switch (BITS(2)) { +            case 0:                             /* stored block */ +                Tracev((stderr, "inflate:     stored block%s\n", +                        state->last ? " (last)" : "")); +                state->mode = STORED; +                break; +            case 1:                             /* fixed block */ +                fixedtables(state); +                Tracev((stderr, "inflate:     fixed codes block%s\n", +                        state->last ? " (last)" : "")); +                state->mode = LEN;              /* decode codes */ +                break; +            case 2:                             /* dynamic block */ +                Tracev((stderr, "inflate:     dynamic codes block%s\n", +                        state->last ? " (last)" : "")); +                state->mode = TABLE; +                break; +            case 3: +                strm->msg = (char *)"invalid block type"; +                state->mode = BAD; +            } +            DROPBITS(2); +            break; +        case STORED: +            BYTEBITS();                         /* go to byte boundary */ +            NEEDBITS(32); +            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { +                strm->msg = (char *)"invalid stored block lengths"; +                state->mode = BAD; +                break; +            } +            state->length = (unsigned)hold & 0xffff; +            Tracev((stderr, "inflate:       stored length %u\n", +                    state->length)); +            INITBITS(); +            state->mode = COPY; +        case COPY: +            copy = state->length; +            if (copy) { +                if (copy > have) copy = have; +                if (copy > left) copy = left; +                if (copy == 0) goto inf_leave; +                zmemcpy(put, next, copy); +                have -= copy; +                next += copy; +                left -= copy; +                put += copy; +                state->length -= copy; +                break; +            } +            Tracev((stderr, "inflate:       stored end\n")); +            state->mode = TYPE; +            break; +        case TABLE: +            NEEDBITS(14); +            state->nlen = BITS(5) + 257; +            DROPBITS(5); +            state->ndist = BITS(5) + 1; +            DROPBITS(5); +            state->ncode = BITS(4) + 4; +            DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND +            if (state->nlen > 286 || state->ndist > 30) { +                strm->msg = (char *)"too many length or distance symbols"; +                state->mode = BAD; +                break; +            } +#endif +            Tracev((stderr, "inflate:       table sizes ok\n")); +            state->have = 0; +            state->mode = LENLENS; +        case LENLENS: +            while (state->have < state->ncode) { +                NEEDBITS(3); +                state->lens[order[state->have++]] = (unsigned short)BITS(3); +                DROPBITS(3); +            } +            while (state->have < 19) +                state->lens[order[state->have++]] = 0; +            state->next = state->codes; +            state->lencode = (code const FAR *)(state->next); +            state->lenbits = 7; +            ret = inflate_table(CODES, state->lens, 19, &(state->next), +                                &(state->lenbits), state->work); +            if (ret) { +                strm->msg = (char *)"invalid code lengths set"; +                state->mode = BAD; +                break; +            } +            Tracev((stderr, "inflate:       code lengths ok\n")); +            state->have = 0; +            state->mode = CODELENS; +        case CODELENS: +            while (state->have < state->nlen + state->ndist) { +                for (;;) { +                    this = state->lencode[BITS(state->lenbits)]; +                    if ((unsigned)(this.bits) <= bits) break; +                    PULLBYTE(); +                } +                if (this.val < 16) { +                    NEEDBITS(this.bits); +                    DROPBITS(this.bits); +                    state->lens[state->have++] = this.val; +                } +                else { +                    if (this.val == 16) { +                        NEEDBITS(this.bits + 2); +                        DROPBITS(this.bits); +                        if (state->have == 0) { +                            strm->msg = (char *)"invalid bit length repeat"; +                            state->mode = BAD; +                            break; +                        } +                        len = state->lens[state->have - 1]; +                        copy = 3 + BITS(2); +                        DROPBITS(2); +                    } +                    else if (this.val == 17) { +                        NEEDBITS(this.bits + 3); +                        DROPBITS(this.bits); +                        len = 0; +                        copy = 3 + BITS(3); +                        DROPBITS(3); +                    } +                    else { +                        NEEDBITS(this.bits + 7); +                        DROPBITS(this.bits); +                        len = 0; +                        copy = 11 + BITS(7); +                        DROPBITS(7); +                    } +                    if (state->have + copy > state->nlen + state->ndist) { +                        strm->msg = (char *)"invalid bit length repeat"; +                        state->mode = BAD; +                        break; +                    } +                    while (copy--) +                        state->lens[state->have++] = (unsigned short)len; +                } +            } + +            /* handle error breaks in while */ +            if (state->mode == BAD) break; + +            /* build code tables */ +            state->next = state->codes; +            state->lencode = (code const FAR *)(state->next); +            state->lenbits = 9; +            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), +                                &(state->lenbits), state->work); +            if (ret) { +                strm->msg = (char *)"invalid literal/lengths set"; +                state->mode = BAD; +                break; +            } +            state->distcode = (code const FAR *)(state->next); +            state->distbits = 6; +            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, +                            &(state->next), &(state->distbits), state->work); +            if (ret) { +                strm->msg = (char *)"invalid distances set"; +                state->mode = BAD; +                break; +            } +            Tracev((stderr, "inflate:       codes ok\n")); +            state->mode = LEN; +        case LEN: +            if (have >= 6 && left >= 258) { +                RESTORE(); +                inflate_fast(strm, out); +                LOAD(); +                break; +            } +            for (;;) { +                this = state->lencode[BITS(state->lenbits)]; +                if ((unsigned)(this.bits) <= bits) break; +                PULLBYTE(); +            } +            if (this.op && (this.op & 0xf0) == 0) { +                last = this; +                for (;;) { +                    this = state->lencode[last.val + +                            (BITS(last.bits + last.op) >> last.bits)]; +                    if ((unsigned)(last.bits + this.bits) <= bits) break; +                    PULLBYTE(); +                } +                DROPBITS(last.bits); +            } +            DROPBITS(this.bits); +            state->length = (unsigned)this.val; +            if ((int)(this.op) == 0) { +                Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? +                        "inflate:         literal '%c'\n" : +                        "inflate:         literal 0x%02x\n", this.val)); +                state->mode = LIT; +                break; +            } +            if (this.op & 32) { +                Tracevv((stderr, "inflate:         end of block\n")); +                state->mode = TYPE; +                break; +            } +            if (this.op & 64) { +                strm->msg = (char *)"invalid literal/length code"; +                state->mode = BAD; +                break; +            } +            state->extra = (unsigned)(this.op) & 15; +            state->mode = LENEXT; +        case LENEXT: +            if (state->extra) { +                NEEDBITS(state->extra); +                state->length += BITS(state->extra); +                DROPBITS(state->extra); +            } +            Tracevv((stderr, "inflate:         length %u\n", state->length)); +            state->mode = DIST; +        case DIST: +            for (;;) { +                this = state->distcode[BITS(state->distbits)]; +                if ((unsigned)(this.bits) <= bits) break; +                PULLBYTE(); +            } +            if ((this.op & 0xf0) == 0) { +                last = this; +                for (;;) { +                    this = state->distcode[last.val + +                            (BITS(last.bits + last.op) >> last.bits)]; +                    if ((unsigned)(last.bits + this.bits) <= bits) break; +                    PULLBYTE(); +                } +                DROPBITS(last.bits); +            } +            DROPBITS(this.bits); +            if (this.op & 64) { +                strm->msg = (char *)"invalid distance code"; +                state->mode = BAD; +                break; +            } +            state->offset = (unsigned)this.val; +            state->extra = (unsigned)(this.op) & 15; +            state->mode = DISTEXT; +        case DISTEXT: +            if (state->extra) { +                NEEDBITS(state->extra); +                state->offset += BITS(state->extra); +                DROPBITS(state->extra); +            } +#ifdef INFLATE_STRICT +            if (state->offset > state->dmax) { +                strm->msg = (char *)"invalid distance too far back"; +                state->mode = BAD; +                break; +            } +#endif +            if (state->offset > state->whave + out - left) { +                strm->msg = (char *)"invalid distance too far back"; +                state->mode = BAD; +                break; +            } +            Tracevv((stderr, "inflate:         distance %u\n", state->offset)); +            state->mode = MATCH; +        case MATCH: +            if (left == 0) goto inf_leave; +            copy = out - left; +            if (state->offset > copy) {         /* copy from window */ +                copy = state->offset - copy; +                if (copy > state->write) { +                    copy -= state->write; +                    from = state->window + (state->wsize - copy); +                } +                else +                    from = state->window + (state->write - copy); +                if (copy > state->length) copy = state->length; +            } +            else {                              /* copy from output */ +                from = put - state->offset; +                copy = state->length; +            } +            if (copy > left) copy = left; +            left -= copy; +            state->length -= copy; +            do { +                *put++ = *from++; +            } while (--copy); +            if (state->length == 0) state->mode = LEN; +            break; +        case LIT: +            if (left == 0) goto inf_leave; +            *put++ = (unsigned char)(state->length); +            left--; +            state->mode = LEN; +            break; +        case CHECK: +            if (state->wrap) { +                NEEDBITS(32); +                out -= left; +                strm->total_out += out; +                state->total += out; +                if (out) +                    strm->adler = state->check = +                        UPDATE(state->check, put - out, out); +                out = left; +                if (( +#ifdef GUNZIP +                     state->flags ? hold : +#endif +                     REVERSE(hold)) != state->check) { +                    strm->msg = (char *)"incorrect data check"; +                    state->mode = BAD; +                    break; +                } +                INITBITS(); +                Tracev((stderr, "inflate:   check matches trailer\n")); +            } +#ifdef GUNZIP +            state->mode = LENGTH; +        case LENGTH: +            if (state->wrap && state->flags) { +                NEEDBITS(32); +                if (hold != (state->total & 0xffffffffUL)) { +                    strm->msg = (char *)"incorrect length check"; +                    state->mode = BAD; +                    break; +                } +                INITBITS(); +                Tracev((stderr, "inflate:   length matches trailer\n")); +            } +#endif +            state->mode = DONE; +        case DONE: +            ret = Z_STREAM_END; +            goto inf_leave; +        case BAD: +            ret = Z_DATA_ERROR; +            goto inf_leave; +        case MEM: +            return Z_MEM_ERROR; +        case SYNC: +        default: +            return Z_STREAM_ERROR; +        } + +    /* +       Return from inflate(), updating the total counts and the check value. +       If there was no progress during the inflate() call, return a buffer +       error.  Call updatewindow() to create and/or update the window state. +       Note: a memory error from inflate() is non-recoverable. +     */ +  inf_leave: +    RESTORE(); +    if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) +        if (updatewindow(strm, out)) { +            state->mode = MEM; +            return Z_MEM_ERROR; +        } +    in -= strm->avail_in; +    out -= strm->avail_out; +    strm->total_in += in; +    strm->total_out += out; +    state->total += out; +    if (state->wrap && out) +        strm->adler = state->check = +            UPDATE(state->check, strm->next_out - out, out); +    strm->data_type = state->bits + (state->last ? 64 : 0) + +                      (state->mode == TYPE ? 128 : 0); +    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) +        ret = Z_BUF_ERROR; +    return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ +    struct inflate_state FAR *state; +    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) +        return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    if (state->window != Z_NULL) ZFREE(strm, state->window); +    ZFREE(strm, strm->state); +    strm->state = Z_NULL; +    Tracev((stderr, "inflate: end\n")); +    return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ +    struct inflate_state FAR *state; +    unsigned long id; + +    /* check state */ +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    if (state->wrap != 0 && state->mode != DICT) +        return Z_STREAM_ERROR; + +    /* check for correct dictionary id */ +    if (state->mode == DICT) { +        id = adler32(0L, Z_NULL, 0); +        id = adler32(id, dictionary, dictLength); +        if (id != state->check) +            return Z_DATA_ERROR; +    } + +    /* copy dictionary to window */ +    if (updatewindow(strm, strm->avail_out)) { +        state->mode = MEM; +        return Z_MEM_ERROR; +    } +    if (dictLength > state->wsize) { +        zmemcpy(state->window, dictionary + dictLength - state->wsize, +                state->wsize); +        state->whave = state->wsize; +    } +    else { +        zmemcpy(state->window + state->wsize - dictLength, dictionary, +                dictLength); +        state->whave = dictLength; +    } +    state->havedict = 1; +    Tracev((stderr, "inflate:   dictionary set\n")); +    return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ +    struct inflate_state FAR *state; + +    /* check state */ +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + +    /* save header structure */ +    state->head = head; +    head->done = 0; +    return Z_OK; +} + +/* +   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found +   or when out of input.  When called, *have is the number of pattern bytes +   found in order so far, in 0..3.  On return *have is updated to the new +   state.  If on return *have equals four, then the pattern was found and the +   return value is how many bytes were read including the last byte of the +   pattern.  If *have is less than four, then the pattern has not been found +   yet and the return value is len.  In the latter case, syncsearch() can be +   called again with more data and the *have state.  *have is initialized to +   zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ +    unsigned got; +    unsigned next; + +    got = *have; +    next = 0; +    while (next < len && got < 4) { +        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) +            got++; +        else if (buf[next]) +            got = 0; +        else +            got = 4 - got; +        next++; +    } +    *have = got; +    return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ +    unsigned len;               /* number of bytes to look at or looked at */ +    unsigned long in, out;      /* temporary to save total_in and total_out */ +    unsigned char buf[4];       /* to restore bit buffer to byte string */ +    struct inflate_state FAR *state; + +    /* check parameters */ +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + +    /* if first time, start search in bit buffer */ +    if (state->mode != SYNC) { +        state->mode = SYNC; +        state->hold <<= state->bits & 7; +        state->bits -= state->bits & 7; +        len = 0; +        while (state->bits >= 8) { +            buf[len++] = (unsigned char)(state->hold); +            state->hold >>= 8; +            state->bits -= 8; +        } +        state->have = 0; +        syncsearch(&(state->have), buf, len); +    } + +    /* search available input */ +    len = syncsearch(&(state->have), strm->next_in, strm->avail_in); +    strm->avail_in -= len; +    strm->next_in += len; +    strm->total_in += len; + +    /* return no joy or set up to restart inflate() on a new block */ +    if (state->have != 4) return Z_DATA_ERROR; +    in = strm->total_in;  out = strm->total_out; +    inflateReset(strm); +    strm->total_in = in;  strm->total_out = out; +    state->mode = TYPE; +    return Z_OK; +} + +/* +   Returns true if inflate is currently at the end of a block generated by +   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP +   implementation to provide an additional safety check. PPP uses +   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored +   block. When decompressing, PPP checks that at the end of input packet, +   inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ +    struct inflate_state FAR *state; + +    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)strm->state; +    return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ +    struct inflate_state FAR *state; +    struct inflate_state FAR *copy; +    unsigned char FAR *window; +    unsigned wsize; + +    /* check input */ +    if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || +        source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) +        return Z_STREAM_ERROR; +    state = (struct inflate_state FAR *)source->state; + +    /* allocate space */ +    copy = (struct inflate_state FAR *) +           ZALLOC(source, 1, sizeof(struct inflate_state)); +    if (copy == Z_NULL) return Z_MEM_ERROR; +    window = Z_NULL; +    if (state->window != Z_NULL) { +        window = (unsigned char FAR *) +                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); +        if (window == Z_NULL) { +            ZFREE(source, copy); +            return Z_MEM_ERROR; +        } +    } + +    /* copy state */ +    zmemcpy(dest, source, sizeof(z_stream)); +    zmemcpy(copy, state, sizeof(struct inflate_state)); +    if (state->lencode >= state->codes && +        state->lencode <= state->codes + ENOUGH - 1) { +        copy->lencode = copy->codes + (state->lencode - state->codes); +        copy->distcode = copy->codes + (state->distcode - state->codes); +    } +    copy->next = copy->codes + (state->next - state->codes); +    if (window != Z_NULL) { +        wsize = 1U << state->wbits; +        zmemcpy(window, state->window, wsize); +    } +    copy->window = window; +    dest->state = (struct internal_state FAR *)copy; +    return Z_OK; +} diff --git a/src/zlib/inflate.h b/src/zlib/inflate.h new file mode 100644 index 0000000..07bd3e7 --- /dev/null +++ b/src/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is +   part of the implementation of the compression library and is +   subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and +   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in +   the crc code when it is not needed.  For shared libraries, gzip decoding +   should be left enabled. */ +#ifndef NO_GZIP +#  define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { +    HEAD,       /* i: waiting for magic header */ +    FLAGS,      /* i: waiting for method and flags (gzip) */ +    TIME,       /* i: waiting for modification time (gzip) */ +    OS,         /* i: waiting for extra flags and operating system (gzip) */ +    EXLEN,      /* i: waiting for extra length (gzip) */ +    EXTRA,      /* i: waiting for extra bytes (gzip) */ +    NAME,       /* i: waiting for end of file name (gzip) */ +    COMMENT,    /* i: waiting for end of comment (gzip) */ +    HCRC,       /* i: waiting for header crc (gzip) */ +    DICTID,     /* i: waiting for dictionary check value */ +    DICT,       /* waiting for inflateSetDictionary() call */ +        TYPE,       /* i: waiting for type bits, including last-flag bit */ +        TYPEDO,     /* i: same, but skip check to exit inflate on new block */ +        STORED,     /* i: waiting for stored size (length and complement) */ +        COPY,       /* i/o: waiting for input or output to copy stored block */ +        TABLE,      /* i: waiting for dynamic block table lengths */ +        LENLENS,    /* i: waiting for code length code lengths */ +        CODELENS,   /* i: waiting for length/lit and distance code lengths */ +            LEN,        /* i: waiting for length/lit code */ +            LENEXT,     /* i: waiting for length extra bits */ +            DIST,       /* i: waiting for distance code */ +            DISTEXT,    /* i: waiting for distance extra bits */ +            MATCH,      /* o: waiting for output space to copy string */ +            LIT,        /* o: waiting for output space to write literal */ +    CHECK,      /* i: waiting for 32-bit check value */ +    LENGTH,     /* i: waiting for 32-bit length (gzip) */ +    DONE,       /* finished check, done -- remain here until reset */ +    BAD,        /* got a data error -- remain here until reset */ +    MEM,        /* got an inflate() memory error -- remain here until reset */ +    SYNC        /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* +    State transitions between above modes - + +    (most modes can go to the BAD or MEM mode -- not shown for clarity) + +    Process header: +        HEAD -> (gzip) or (zlib) +        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME +        NAME -> COMMENT -> HCRC -> TYPE +        (zlib) -> DICTID or TYPE +        DICTID -> DICT -> TYPE +    Read deflate blocks: +            TYPE -> STORED or TABLE or LEN or CHECK +            STORED -> COPY -> TYPE +            TABLE -> LENLENS -> CODELENS -> LEN +    Read deflate codes: +                LEN -> LENEXT or LIT or TYPE +                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN +                LIT -> LEN +    Process trailer: +        CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls.  Approximately 7K bytes. */ +struct inflate_state { +    inflate_mode mode;          /* current inflate mode */ +    int last;                   /* true if processing last block */ +    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip */ +    int havedict;               /* true if dictionary provided */ +    int flags;                  /* gzip header method and flags (0 if zlib) */ +    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */ +    unsigned long check;        /* protected copy of check value */ +    unsigned long total;        /* protected copy of output count */ +    gz_headerp head;            /* where to save gzip header information */ +        /* sliding window */ +    unsigned wbits;             /* log base 2 of requested window size */ +    unsigned wsize;             /* window size or zero if not using window */ +    unsigned whave;             /* valid bytes in the window */ +    unsigned write;             /* window write index */ +    unsigned char FAR *window;  /* allocated sliding window, if needed */ +        /* bit accumulator */ +    unsigned long hold;         /* input bit accumulator */ +    unsigned bits;              /* number of bits in "in" */ +        /* for string and stored block copying */ +    unsigned length;            /* literal or length of data to copy */ +    unsigned offset;            /* distance back to copy string from */ +        /* for table and code decoding */ +    unsigned extra;             /* extra bits needed */ +        /* fixed and dynamic code tables */ +    code const FAR *lencode;    /* starting table for length/literal codes */ +    code const FAR *distcode;   /* starting table for distance codes */ +    unsigned lenbits;           /* index bits for lencode */ +    unsigned distbits;          /* index bits for distcode */ +        /* dynamic table building */ +    unsigned ncode;             /* number of code length code lengths */ +    unsigned nlen;              /* number of length code lengths */ +    unsigned ndist;             /* number of distance code lengths */ +    unsigned have;              /* number of code lengths in lens[] */ +    code FAR *next;             /* next available space in codes[] */ +    unsigned short lens[320];   /* temporary storage for code lengths */ +    unsigned short work[288];   /* work area for code table building */ +    code codes[ENOUGH];         /* space for code tables */ +}; diff --git a/src/zlib/inftrees.c b/src/zlib/inftrees.c new file mode 100644 index 0000000..8a9c13f --- /dev/null +++ b/src/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = +   " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* +  If you use the zlib library in a product, an acknowledgment is welcome +  in the documentation of your product. If for some reason you cannot +  include such an acknowledgment, I would appreciate that you keep this +  copyright string in the executable of your product. + */ + +/* +   Build a set of tables to decode the provided canonical Huffman code. +   The code lengths are lens[0..codes-1].  The result starts at *table, +   whose indices are 0..2^bits-1.  work is a writable array of at least +   lens shorts, which is used as a work area.  type is the type of code +   to be generated, CODES, LENS, or DISTS.  On return, zero is success, +   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table +   on return points to the next available entry's address.  bits is the +   requested root table index bits, and on return it is the actual root +   table index bits.  It will differ if the request is greater than the +   longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ +    unsigned len;               /* a code's length in bits */ +    unsigned sym;               /* index of code symbols */ +    unsigned min, max;          /* minimum and maximum code lengths */ +    unsigned root;              /* number of index bits for root table */ +    unsigned curr;              /* number of index bits for current table */ +    unsigned drop;              /* code bits to drop for sub-table */ +    int left;                   /* number of prefix codes available */ +    unsigned used;              /* code entries in table used */ +    unsigned huff;              /* Huffman code */ +    unsigned incr;              /* for incrementing code, index */ +    unsigned fill;              /* index for replicating entries */ +    unsigned low;               /* low bits for current root entry */ +    unsigned mask;              /* mask for low root bits */ +    code this;                  /* table entry for duplication */ +    code FAR *next;             /* next available space in table */ +    const unsigned short FAR *base;     /* base value table to use */ +    const unsigned short FAR *extra;    /* extra bits table to use */ +    int end;                    /* use base and extra for symbol > end */ +    unsigned short count[MAXBITS+1];    /* number of codes of each length */ +    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */ +    static const unsigned short lbase[31] = { /* Length codes 257..285 base */ +        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, +        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; +    static const unsigned short lext[31] = { /* Length codes 257..285 extra */ +        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, +        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; +    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ +        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, +        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, +        8193, 12289, 16385, 24577, 0, 0}; +    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ +        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, +        23, 23, 24, 24, 25, 25, 26, 26, 27, 27, +        28, 28, 29, 29, 64, 64}; + +    /* +       Process a set of code lengths to create a canonical Huffman code.  The +       code lengths are lens[0..codes-1].  Each length corresponds to the +       symbols 0..codes-1.  The Huffman code is generated by first sorting the +       symbols by length from short to long, and retaining the symbol order +       for codes with equal lengths.  Then the code starts with all zero bits +       for the first code of the shortest length, and the codes are integer +       increments for the same length, and zeros are appended as the length +       increases.  For the deflate format, these bits are stored backwards +       from their more natural integer increment ordering, and so when the +       decoding tables are built in the large loop below, the integer codes +       are incremented backwards. + +       This routine assumes, but does not check, that all of the entries in +       lens[] are in the range 0..MAXBITS.  The caller must assure this. +       1..MAXBITS is interpreted as that code length.  zero means that that +       symbol does not occur in this code. + +       The codes are sorted by computing a count of codes for each length, +       creating from that a table of starting indices for each length in the +       sorted table, and then entering the symbols in order in the sorted +       table.  The sorted table is work[], with that space being provided by +       the caller. + +       The length counts are used for other purposes as well, i.e. finding +       the minimum and maximum length codes, determining if there are any +       codes at all, checking for a valid set of lengths, and looking ahead +       at length counts to determine sub-table sizes when building the +       decoding tables. +     */ + +    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ +    for (len = 0; len <= MAXBITS; len++) +        count[len] = 0; +    for (sym = 0; sym < codes; sym++) +        count[lens[sym]]++; + +    /* bound code lengths, force root to be within code lengths */ +    root = *bits; +    for (max = MAXBITS; max >= 1; max--) +        if (count[max] != 0) break; +    if (root > max) root = max; +    if (max == 0) {                     /* no symbols to code at all */ +        this.op = (unsigned char)64;    /* invalid code marker */ +        this.bits = (unsigned char)1; +        this.val = (unsigned short)0; +        *(*table)++ = this;             /* make a table to force an error */ +        *(*table)++ = this; +        *bits = 1; +        return 0;     /* no symbols, but wait for decoding to report error */ +    } +    for (min = 1; min <= MAXBITS; min++) +        if (count[min] != 0) break; +    if (root < min) root = min; + +    /* check for an over-subscribed or incomplete set of lengths */ +    left = 1; +    for (len = 1; len <= MAXBITS; len++) { +        left <<= 1; +        left -= count[len]; +        if (left < 0) return -1;        /* over-subscribed */ +    } +    if (left > 0 && (type == CODES || max != 1)) +        return -1;                      /* incomplete set */ + +    /* generate offsets into symbol table for each length for sorting */ +    offs[1] = 0; +    for (len = 1; len < MAXBITS; len++) +        offs[len + 1] = offs[len] + count[len]; + +    /* sort symbols by length, by symbol order within each length */ +    for (sym = 0; sym < codes; sym++) +        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + +    /* +       Create and fill in decoding tables.  In this loop, the table being +       filled is at next and has curr index bits.  The code being used is huff +       with length len.  That code is converted to an index by dropping drop +       bits off of the bottom.  For codes where len is less than drop + curr, +       those top drop + curr - len bits are incremented through all values to +       fill the table with replicated entries. + +       root is the number of index bits for the root table.  When len exceeds +       root, sub-tables are created pointed to by the root entry with an index +       of the low root bits of huff.  This is saved in low to check for when a +       new sub-table should be started.  drop is zero when the root table is +       being filled, and drop is root when sub-tables are being filled. + +       When a new sub-table is needed, it is necessary to look ahead in the +       code lengths to determine what size sub-table is needed.  The length +       counts are used for this, and so count[] is decremented as codes are +       entered in the tables. + +       used keeps track of how many table entries have been allocated from the +       provided *table space.  It is checked when a LENS table is being made +       against the space in *table, ENOUGH, minus the maximum space needed by +       the worst case distance code, MAXD.  This should never happen, but the +       sufficiency of ENOUGH has not been proven exhaustively, hence the check. +       This assumes that when type == LENS, bits == 9. + +       sym increments through all symbols, and the loop terminates when +       all codes of length max, i.e. all codes, have been processed.  This +       routine permits incomplete codes, so another loop after this one fills +       in the rest of the decoding tables with invalid code markers. +     */ + +    /* set up for code type */ +    switch (type) { +    case CODES: +        base = extra = work;    /* dummy value--not used */ +        end = 19; +        break; +    case LENS: +        base = lbase; +        base -= 257; +        extra = lext; +        extra -= 257; +        end = 256; +        break; +    default:            /* DISTS */ +        base = dbase; +        extra = dext; +        end = -1; +    } + +    /* initialize state for loop */ +    huff = 0;                   /* starting code */ +    sym = 0;                    /* starting code symbol */ +    len = min;                  /* starting code length */ +    next = *table;              /* current table to fill in */ +    curr = root;                /* current table index bits */ +    drop = 0;                   /* current bits to drop from code for index */ +    low = (unsigned)(-1);       /* trigger new sub-table when len > root */ +    used = 1U << root;          /* use root table entries */ +    mask = used - 1;            /* mask for comparing low */ + +    /* check available table space */ +    if (type == LENS && used >= ENOUGH - MAXD) +        return 1; + +    /* process all codes and make table entries */ +    for (;;) { +        /* create table entry */ +        this.bits = (unsigned char)(len - drop); +        if ((int)(work[sym]) < end) { +            this.op = (unsigned char)0; +            this.val = work[sym]; +        } +        else if ((int)(work[sym]) > end) { +            this.op = (unsigned char)(extra[work[sym]]); +            this.val = base[work[sym]]; +        } +        else { +            this.op = (unsigned char)(32 + 64);         /* end of block */ +            this.val = 0; +        } + +        /* replicate for those indices with low len bits equal to huff */ +        incr = 1U << (len - drop); +        fill = 1U << curr; +        min = fill;                 /* save offset to next table */ +        do { +            fill -= incr; +            next[(huff >> drop) + fill] = this; +        } while (fill != 0); + +        /* backwards increment the len-bit code huff */ +        incr = 1U << (len - 1); +        while (huff & incr) +            incr >>= 1; +        if (incr != 0) { +            huff &= incr - 1; +            huff += incr; +        } +        else +            huff = 0; + +        /* go to next symbol, update count, len */ +        sym++; +        if (--(count[len]) == 0) { +            if (len == max) break; +            len = lens[work[sym]]; +        } + +        /* create new sub-table if needed */ +        if (len > root && (huff & mask) != low) { +            /* if first time, transition to sub-tables */ +            if (drop == 0) +                drop = root; + +            /* increment past last table */ +            next += min;            /* here min is 1 << curr */ + +            /* determine length of next table */ +            curr = len - drop; +            left = (int)(1 << curr); +            while (curr + drop < max) { +                left -= count[curr + drop]; +                if (left <= 0) break; +                curr++; +                left <<= 1; +            } + +            /* check for enough space */ +            used += 1U << curr; +            if (type == LENS && used >= ENOUGH - MAXD) +                return 1; + +            /* point entry in root table to sub-table */ +            low = huff & mask; +            (*table)[low].op = (unsigned char)curr; +            (*table)[low].bits = (unsigned char)root; +            (*table)[low].val = (unsigned short)(next - *table); +        } +    } + +    /* +       Fill in rest of table for incomplete codes.  This loop is similar to the +       loop above in incrementing huff for table indices.  It is assumed that +       len is equal to curr + drop, so there is no loop needed to increment +       through high index bits.  When the current sub-table is filled, the loop +       drops back to the root table to fill in any remaining entries there. +     */ +    this.op = (unsigned char)64;                /* invalid code marker */ +    this.bits = (unsigned char)(len - drop); +    this.val = (unsigned short)0; +    while (huff != 0) { +        /* when done with sub-table, drop back to root table */ +        if (drop != 0 && (huff & mask) != low) { +            drop = 0; +            len = root; +            next = *table; +            this.bits = (unsigned char)len; +        } + +        /* put invalid code marker in table */ +        next[huff >> drop] = this; + +        /* backwards increment the len-bit code huff */ +        incr = 1U << (len - 1); +        while (huff & incr) +            incr >>= 1; +        if (incr != 0) { +            huff &= incr - 1; +            huff += incr; +        } +        else +            huff = 0; +    } + +    /* set return parameters */ +    *table += used; +    *bits = root; +    return 0; +} diff --git a/src/zlib/inftrees.h b/src/zlib/inftrees.h new file mode 100644 index 0000000..b1104c8 --- /dev/null +++ b/src/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is +   part of the implementation of the compression library and is +   subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables.  Each entry provides either the +   information needed to do the operation requested by the code that +   indexed that table entry, or it provides a pointer to another +   table that indexes more bits of the code.  op indicates whether +   the entry is a pointer to another table, a literal, a length or +   distance, an end-of-block, or an invalid code.  For a table +   pointer, the low four bits of op is the number of index bits of +   that table.  For a length or distance, the low four bits of op +   is the number of extra bits to get after the code.  bits is +   the number of bits in this code or part of the code to drop off +   of the bit buffer.  val is the actual byte to output in the case +   of a literal, the base length or distance, or the offset from +   the current table to the next table.  Each entry is four bytes. */ +typedef struct { +    unsigned char op;           /* operation, extra bits, table bits */ +    unsigned char bits;         /* bits in this part of the code */ +    unsigned short val;         /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): +    00000000 - literal +    0000tttt - table link, tttt != 0 is the number of table index bits +    0001eeee - length or distance, eeee is the number of extra bits +    01100000 - end of block +    01000000 - invalid code + */ + +/* Maximum size of dynamic tree.  The maximum found in a long but non- +   exhaustive search was 1444 code structures (852 for length/literals +   and 592 for distances, the latter actually the result of an +   exhaustive search).  The true maximum is not known, but the value +   below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { +    CODES, +    LENS, +    DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, +                             unsigned codes, code FAR * FAR *table, +                             unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/zlib/trees.c b/src/zlib/trees.c new file mode 100644 index 0000000..395e4e1 --- /dev/null +++ b/src/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + *  ALGORITHM + * + *      The "deflation" process uses several Huffman trees. The more + *      common source values are represented by shorter bit sequences. + * + *      Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values).  The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + *  REFERENCES + * + *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + *      Storer, James A. + *          Data Compression:  Methods and Theory, pp. 49-50. + *          Computer Science Press, 1988.  ISBN 0-7167-8156-5. + * + *      Sedgewick, R. + *          Algorithms, p290. + *          Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +#  include <ctype.h> +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6      16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10    17 +/* repeat a zero length 3-10 times  (3 bits of repeat count) */ + +#define REPZ_11_138  18 +/* repeat a zero length 11-138 times  (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ +   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ +   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ +   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] +   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN  512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +#  include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { +    const ct_data *static_tree;  /* static tree or NULL */ +    const intf *extra_bits;      /* extra bits for each code or NULL */ +    int     extra_base;          /* base index for extra_bits */ +    int     elems;               /* max number of elements in the tree */ +    int     max_length;          /* max bit length for the codes */ +}; + +local static_tree_desc  static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc  static_d_desc = +{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS}; + +local static_tree_desc  static_bl_desc = +{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block     OF((deflate_state *s)); +local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen     OF((deflate_state *s, tree_desc *desc)); +local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree     OF((deflate_state *s, tree_desc *desc)); +local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code)); +local int  build_bl_tree  OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, +                              int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, +                              ct_data *dtree)); +local void set_data_type  OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup      OF((deflate_state *s)); +local void bi_flush       OF((deflate_state *s)); +local void copy_block     OF((deflate_state *s, charf *buf, unsigned len, +                              int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) +   /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +#  define send_code(s, c, tree) \ +     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ +       send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ +    put_byte(s, (uch)((w) & 0xff)); \ +    put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits      OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) +    deflate_state *s; +    int value;  /* value to send */ +    int length; /* number of bits */ +{ +    Tracevv((stderr," l %2d v %4x ", length, value)); +    Assert(length > 0 && length <= 15, "invalid length"); +    s->bits_sent += (ulg)length; + +    /* If not enough room in bi_buf, use (valid) bits from bi_buf and +     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) +     * unused bits in value. +     */ +    if (s->bi_valid > (int)Buf_size - length) { +        s->bi_buf |= (value << s->bi_valid); +        put_short(s, s->bi_buf); +        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); +        s->bi_valid += length - Buf_size; +    } else { +        s->bi_buf |= value << s->bi_valid; +        s->bi_valid += length; +    } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ +  if (s->bi_valid > (int)Buf_size - len) {\ +    int val = value;\ +    s->bi_buf |= (val << s->bi_valid);\ +    put_short(s, s->bi_buf);\ +    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ +    s->bi_valid += len - Buf_size;\ +  } else {\ +    s->bi_buf |= (value) << s->bi_valid;\ +    s->bi_valid += len;\ +  }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) +    static int static_init_done = 0; +    int n;        /* iterates over tree elements */ +    int bits;     /* bit counter */ +    int length;   /* length value */ +    int code;     /* code value */ +    int dist;     /* distance index */ +    ush bl_count[MAX_BITS+1]; +    /* number of codes at each bit length for an optimal tree */ + +    if (static_init_done) return; + +    /* For some embedded targets, global variables are not initialized: */ +    static_l_desc.static_tree = static_ltree; +    static_l_desc.extra_bits = extra_lbits; +    static_d_desc.static_tree = static_dtree; +    static_d_desc.extra_bits = extra_dbits; +    static_bl_desc.extra_bits = extra_blbits; + +    /* Initialize the mapping length (0..255) -> length code (0..28) */ +    length = 0; +    for (code = 0; code < LENGTH_CODES-1; code++) { +        base_length[code] = length; +        for (n = 0; n < (1<<extra_lbits[code]); n++) { +            _length_code[length++] = (uch)code; +        } +    } +    Assert (length == 256, "tr_static_init: length != 256"); +    /* Note that the length 255 (match length 258) can be represented +     * in two different ways: code 284 + 5 bits or code 285, so we +     * overwrite length_code[255] to use the best encoding: +     */ +    _length_code[length-1] = (uch)code; + +    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ +    dist = 0; +    for (code = 0 ; code < 16; code++) { +        base_dist[code] = dist; +        for (n = 0; n < (1<<extra_dbits[code]); n++) { +            _dist_code[dist++] = (uch)code; +        } +    } +    Assert (dist == 256, "tr_static_init: dist != 256"); +    dist >>= 7; /* from now on, all distances are divided by 128 */ +    for ( ; code < D_CODES; code++) { +        base_dist[code] = dist << 7; +        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { +            _dist_code[256 + dist++] = (uch)code; +        } +    } +    Assert (dist == 256, "tr_static_init: 256+dist != 512"); + +    /* Construct the codes of the static literal tree */ +    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; +    n = 0; +    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; +    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; +    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; +    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; +    /* Codes 286 and 287 do not exist, but we must include them in the +     * tree construction to get a canonical Huffman tree (longest code +     * all ones) +     */ +    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + +    /* The static distance tree is trivial: */ +    for (n = 0; n < D_CODES; n++) { +        static_dtree[n].Len = 5; +        static_dtree[n].Code = bi_reverse((unsigned)n, 5); +    } +    static_init_done = 1; + +#  ifdef GEN_TREES_H +    gen_trees_header(); +#  endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +#  ifndef DEBUG +#    include <stdio.h> +#  endif + +#  define SEPARATOR(i, last, width) \ +      ((i) == (last)? "\n};\n\n" :    \ +       ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ +    FILE *header = fopen("trees.h", "w"); +    int i; + +    Assert (header != NULL, "Can't open trees.h"); +    fprintf(header, +            "/* header created automatically with -DGEN_TREES_H */\n\n"); + +    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); +    for (i = 0; i < L_CODES+2; i++) { +        fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, +                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); +    } + +    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); +    for (i = 0; i < D_CODES; i++) { +        fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, +                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); +    } + +    fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); +    for (i = 0; i < DIST_CODE_LEN; i++) { +        fprintf(header, "%2u%s", _dist_code[i], +                SEPARATOR(i, DIST_CODE_LEN-1, 20)); +    } + +    fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); +    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { +        fprintf(header, "%2u%s", _length_code[i], +                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); +    } + +    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); +    for (i = 0; i < LENGTH_CODES; i++) { +        fprintf(header, "%1u%s", base_length[i], +                SEPARATOR(i, LENGTH_CODES-1, 20)); +    } + +    fprintf(header, "local const int base_dist[D_CODES] = {\n"); +    for (i = 0; i < D_CODES; i++) { +        fprintf(header, "%5u%s", base_dist[i], +                SEPARATOR(i, D_CODES-1, 10)); +    } + +    fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) +    deflate_state *s; +{ +    tr_static_init(); + +    s->l_desc.dyn_tree = s->dyn_ltree; +    s->l_desc.stat_desc = &static_l_desc; + +    s->d_desc.dyn_tree = s->dyn_dtree; +    s->d_desc.stat_desc = &static_d_desc; + +    s->bl_desc.dyn_tree = s->bl_tree; +    s->bl_desc.stat_desc = &static_bl_desc; + +    s->bi_buf = 0; +    s->bi_valid = 0; +    s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG +    s->compressed_len = 0L; +    s->bits_sent = 0L; +#endif + +    /* Initialize the first block of the first file: */ +    init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) +    deflate_state *s; +{ +    int n; /* iterates over tree elements */ + +    /* Initialize the trees. */ +    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0; +    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0; +    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + +    s->dyn_ltree[END_BLOCK].Freq = 1; +    s->opt_len = s->static_len = 0L; +    s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ +    top = s->heap[SMALLEST]; \ +    s->heap[SMALLEST] = s->heap[s->heap_len--]; \ +    pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ +   (tree[n].Freq < tree[m].Freq || \ +   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) +    deflate_state *s; +    ct_data *tree;  /* the tree to restore */ +    int k;               /* node to move down */ +{ +    int v = s->heap[k]; +    int j = k << 1;  /* left son of k */ +    while (j <= s->heap_len) { +        /* Set j to the smallest of the two sons: */ +        if (j < s->heap_len && +            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { +            j++; +        } +        /* Exit if v is smaller than both sons */ +        if (smaller(tree, v, s->heap[j], s->depth)) break; + +        /* Exchange v with the smallest son */ +        s->heap[k] = s->heap[j];  k = j; + +        /* And continue down the tree, setting j to the left son of k */ +        j <<= 1; +    } +    s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + *    above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + *     array bl_count contains the frequencies for each bit length. + *     The length opt_len is updated; static_len is also updated if stree is + *     not null. + */ +local void gen_bitlen(s, desc) +    deflate_state *s; +    tree_desc *desc;    /* the tree descriptor */ +{ +    ct_data *tree        = desc->dyn_tree; +    int max_code         = desc->max_code; +    const ct_data *stree = desc->stat_desc->static_tree; +    const intf *extra    = desc->stat_desc->extra_bits; +    int base             = desc->stat_desc->extra_base; +    int max_length       = desc->stat_desc->max_length; +    int h;              /* heap index */ +    int n, m;           /* iterate over the tree elements */ +    int bits;           /* bit length */ +    int xbits;          /* extra bits */ +    ush f;              /* frequency */ +    int overflow = 0;   /* number of elements with bit length too large */ + +    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + +    /* In a first pass, compute the optimal bit lengths (which may +     * overflow in the case of the bit length tree). +     */ +    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + +    for (h = s->heap_max+1; h < HEAP_SIZE; h++) { +        n = s->heap[h]; +        bits = tree[tree[n].Dad].Len + 1; +        if (bits > max_length) bits = max_length, overflow++; +        tree[n].Len = (ush)bits; +        /* We overwrite tree[n].Dad which is no longer needed */ + +        if (n > max_code) continue; /* not a leaf node */ + +        s->bl_count[bits]++; +        xbits = 0; +        if (n >= base) xbits = extra[n-base]; +        f = tree[n].Freq; +        s->opt_len += (ulg)f * (bits + xbits); +        if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); +    } +    if (overflow == 0) return; + +    Trace((stderr,"\nbit length overflow\n")); +    /* This happens for example on obj2 and pic of the Calgary corpus */ + +    /* Find the first bit length which could increase: */ +    do { +        bits = max_length-1; +        while (s->bl_count[bits] == 0) bits--; +        s->bl_count[bits]--;      /* move one leaf down the tree */ +        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ +        s->bl_count[max_length]--; +        /* The brother of the overflow item also moves one step up, +         * but this does not affect bl_count[max_length] +         */ +        overflow -= 2; +    } while (overflow > 0); + +    /* Now recompute all bit lengths, scanning in increasing frequency. +     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all +     * lengths instead of fixing only the wrong ones. This idea is taken +     * from 'ar' written by Haruhiko Okumura.) +     */ +    for (bits = max_length; bits != 0; bits--) { +        n = s->bl_count[bits]; +        while (n != 0) { +            m = s->heap[--h]; +            if (m > max_code) continue; +            if ((unsigned) tree[m].Len != (unsigned) bits) { +                Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); +                s->opt_len += ((long)bits - (long)tree[m].Len) +                              *(long)tree[m].Freq; +                tree[m].Len = (ush)bits; +            } +            n--; +        } +    } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + *     zero code length. + */ +local void gen_codes (tree, max_code, bl_count) +    ct_data *tree;             /* the tree to decorate */ +    int max_code;              /* largest code with non zero frequency */ +    ushf *bl_count;            /* number of codes at each bit length */ +{ +    ush next_code[MAX_BITS+1]; /* next code value for each bit length */ +    ush code = 0;              /* running code value */ +    int bits;                  /* bit index */ +    int n;                     /* code index */ + +    /* The distribution counts are first used to generate the code values +     * without bit reversal. +     */ +    for (bits = 1; bits <= MAX_BITS; bits++) { +        next_code[bits] = code = (code + bl_count[bits-1]) << 1; +    } +    /* Check that the bit counts in bl_count are consistent. The last code +     * must be all ones. +     */ +    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, +            "inconsistent bit counts"); +    Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + +    for (n = 0;  n <= max_code; n++) { +        int len = tree[n].Len; +        if (len == 0) continue; +        /* Now reverse the bits */ +        tree[n].Code = bi_reverse(next_code[len]++, len); + +        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", +             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); +    } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + *     and corresponding code. The length opt_len is updated; static_len is + *     also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) +    deflate_state *s; +    tree_desc *desc; /* the tree descriptor */ +{ +    ct_data *tree         = desc->dyn_tree; +    const ct_data *stree  = desc->stat_desc->static_tree; +    int elems             = desc->stat_desc->elems; +    int n, m;          /* iterate over heap elements */ +    int max_code = -1; /* largest code with non zero frequency */ +    int node;          /* new node being created */ + +    /* Construct the initial heap, with least frequent element in +     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. +     * heap[0] is not used. +     */ +    s->heap_len = 0, s->heap_max = HEAP_SIZE; + +    for (n = 0; n < elems; n++) { +        if (tree[n].Freq != 0) { +            s->heap[++(s->heap_len)] = max_code = n; +            s->depth[n] = 0; +        } else { +            tree[n].Len = 0; +        } +    } + +    /* The pkzip format requires that at least one distance code exists, +     * and that at least one bit should be sent even if there is only one +     * possible code. So to avoid special checks later on we force at least +     * two codes of non zero frequency. +     */ +    while (s->heap_len < 2) { +        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); +        tree[node].Freq = 1; +        s->depth[node] = 0; +        s->opt_len--; if (stree) s->static_len -= stree[node].Len; +        /* node is 0 or 1 so it does not have extra bits */ +    } +    desc->max_code = max_code; + +    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, +     * establish sub-heaps of increasing lengths: +     */ +    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + +    /* Construct the Huffman tree by repeatedly combining the least two +     * frequent nodes. +     */ +    node = elems;              /* next internal node of the tree */ +    do { +        pqremove(s, tree, n);  /* n = node of least frequency */ +        m = s->heap[SMALLEST]; /* m = node of next least frequency */ + +        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ +        s->heap[--(s->heap_max)] = m; + +        /* Create a new node father of n and m */ +        tree[node].Freq = tree[n].Freq + tree[m].Freq; +        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? +                                s->depth[n] : s->depth[m]) + 1); +        tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE +        if (tree == s->bl_tree) { +            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", +                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); +        } +#endif +        /* and insert the new node in the heap */ +        s->heap[SMALLEST] = node++; +        pqdownheap(s, tree, SMALLEST); + +    } while (s->heap_len >= 2); + +    s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + +    /* At this point, the fields freq and dad are set. We can now +     * generate the bit lengths. +     */ +    gen_bitlen(s, (tree_desc *)desc); + +    /* The field len is now set, we can generate the bit codes */ +    gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) +    deflate_state *s; +    ct_data *tree;   /* the tree to be scanned */ +    int max_code;    /* and its largest code of non zero frequency */ +{ +    int n;                     /* iterates over all tree elements */ +    int prevlen = -1;          /* last emitted length */ +    int curlen;                /* length of current code */ +    int nextlen = tree[0].Len; /* length of next code */ +    int count = 0;             /* repeat count of the current code */ +    int max_count = 7;         /* max repeat count */ +    int min_count = 4;         /* min repeat count */ + +    if (nextlen == 0) max_count = 138, min_count = 3; +    tree[max_code+1].Len = (ush)0xffff; /* guard */ + +    for (n = 0; n <= max_code; n++) { +        curlen = nextlen; nextlen = tree[n+1].Len; +        if (++count < max_count && curlen == nextlen) { +            continue; +        } else if (count < min_count) { +            s->bl_tree[curlen].Freq += count; +        } else if (curlen != 0) { +            if (curlen != prevlen) s->bl_tree[curlen].Freq++; +            s->bl_tree[REP_3_6].Freq++; +        } else if (count <= 10) { +            s->bl_tree[REPZ_3_10].Freq++; +        } else { +            s->bl_tree[REPZ_11_138].Freq++; +        } +        count = 0; prevlen = curlen; +        if (nextlen == 0) { +            max_count = 138, min_count = 3; +        } else if (curlen == nextlen) { +            max_count = 6, min_count = 3; +        } else { +            max_count = 7, min_count = 4; +        } +    } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) +    deflate_state *s; +    ct_data *tree; /* the tree to be scanned */ +    int max_code;       /* and its largest code of non zero frequency */ +{ +    int n;                     /* iterates over all tree elements */ +    int prevlen = -1;          /* last emitted length */ +    int curlen;                /* length of current code */ +    int nextlen = tree[0].Len; /* length of next code */ +    int count = 0;             /* repeat count of the current code */ +    int max_count = 7;         /* max repeat count */ +    int min_count = 4;         /* min repeat count */ + +    /* tree[max_code+1].Len = -1; */  /* guard already set */ +    if (nextlen == 0) max_count = 138, min_count = 3; + +    for (n = 0; n <= max_code; n++) { +        curlen = nextlen; nextlen = tree[n+1].Len; +        if (++count < max_count && curlen == nextlen) { +            continue; +        } else if (count < min_count) { +            do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + +        } else if (curlen != 0) { +            if (curlen != prevlen) { +                send_code(s, curlen, s->bl_tree); count--; +            } +            Assert(count >= 3 && count <= 6, " 3_6?"); +            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + +        } else if (count <= 10) { +            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + +        } else { +            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); +        } +        count = 0; prevlen = curlen; +        if (nextlen == 0) { +            max_count = 138, min_count = 3; +        } else if (curlen == nextlen) { +            max_count = 6, min_count = 3; +        } else { +            max_count = 7, min_count = 4; +        } +    } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) +    deflate_state *s; +{ +    int max_blindex;  /* index of last bit length code of non zero freq */ + +    /* Determine the bit length frequencies for literal and distance trees */ +    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); +    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + +    /* Build the bit length tree: */ +    build_tree(s, (tree_desc *)(&(s->bl_desc))); +    /* opt_len now includes the length of the tree representations, except +     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. +     */ + +    /* Determine the number of bit length codes to send. The pkzip format +     * requires that at least 4 bit length codes be sent. (appnote.txt says +     * 3 but the actual value used is 4.) +     */ +    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { +        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; +    } +    /* Update opt_len to include the bit length tree and counts */ +    s->opt_len += 3*(max_blindex+1) + 5+5+4; +    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", +            s->opt_len, s->static_len)); + +    return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) +    deflate_state *s; +    int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ +    int rank;                    /* index in bl_order */ + +    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); +    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, +            "too many codes"); +    Tracev((stderr, "\nbl counts: ")); +    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ +    send_bits(s, dcodes-1,   5); +    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */ +    for (rank = 0; rank < blcodes; rank++) { +        Tracev((stderr, "\nbl code %2d ", bl_order[rank])); +        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); +    } +    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + +    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ +    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + +    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ +    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) +    deflate_state *s; +    charf *buf;       /* input block */ +    ulg stored_len;   /* length of input block */ +    int eof;          /* true if this is the last block for a file */ +{ +    send_bits(s, (STORED_BLOCK<<1)+eof, 3);  /* send block type */ +#ifdef DEBUG +    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; +    s->compressed_len += (stored_len + 4) << 3; +#endif +    copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) +    deflate_state *s; +{ +    send_bits(s, STATIC_TREES<<1, 3); +    send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG +    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif +    bi_flush(s); +    /* Of the 10 bits for the empty block, we have already sent +     * (10 - bi_valid) bits. The lookahead for the last real code (before +     * the EOB of the previous block) was thus at least one plus the length +     * of the EOB plus what we have just sent of the empty static block. +     */ +    if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { +        send_bits(s, STATIC_TREES<<1, 3); +        send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG +        s->compressed_len += 10L; +#endif +        bi_flush(s); +    } +    s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) +    deflate_state *s; +    charf *buf;       /* input block, or NULL if too old */ +    ulg stored_len;   /* length of input block */ +    int eof;          /* true if this is the last block for a file */ +{ +    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ +    int max_blindex = 0;  /* index of last bit length code of non zero freq */ + +    /* Build the Huffman trees unless a stored block is forced */ +    if (s->level > 0) { + +        /* Check if the file is binary or text */ +        if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) +            set_data_type(s); + +        /* Construct the literal and distance trees */ +        build_tree(s, (tree_desc *)(&(s->l_desc))); +        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, +                s->static_len)); + +        build_tree(s, (tree_desc *)(&(s->d_desc))); +        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, +                s->static_len)); +        /* At this point, opt_len and static_len are the total bit lengths of +         * the compressed block data, excluding the tree representations. +         */ + +        /* Build the bit length tree for the above two trees, and get the index +         * in bl_order of the last bit length code to send. +         */ +        max_blindex = build_bl_tree(s); + +        /* Determine the best encoding. Compute the block lengths in bytes. */ +        opt_lenb = (s->opt_len+3+7)>>3; +        static_lenb = (s->static_len+3+7)>>3; + +        Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", +                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, +                s->last_lit)); + +        if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + +    } else { +        Assert(buf != (char*)0, "lost buf"); +        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ +    } + +#ifdef FORCE_STORED +    if (buf != (char*)0) { /* force stored block */ +#else +    if (stored_len+4 <= opt_lenb && buf != (char*)0) { +                       /* 4: two words for the lengths */ +#endif +        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. +         * Otherwise we can't have processed more than WSIZE input bytes since +         * the last block flush, because compression would have been +         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to +         * transform a block into a stored block. +         */ +        _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC +    } else if (static_lenb >= 0) { /* force static trees */ +#else +    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif +        send_bits(s, (STATIC_TREES<<1)+eof, 3); +        compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG +        s->compressed_len += 3 + s->static_len; +#endif +    } else { +        send_bits(s, (DYN_TREES<<1)+eof, 3); +        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, +                       max_blindex+1); +        compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG +        s->compressed_len += 3 + s->opt_len; +#endif +    } +    Assert (s->compressed_len == s->bits_sent, "bad compressed size"); +    /* The above check is made mod 2^32, for files larger than 512 MB +     * and uLong implemented on 32 bits. +     */ +    init_block(s); + +    if (eof) { +        bi_windup(s); +#ifdef DEBUG +        s->compressed_len += 7;  /* align on byte boundary */ +#endif +    } +    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, +           s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) +    deflate_state *s; +    unsigned dist;  /* distance of matched string */ +    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ +    s->d_buf[s->last_lit] = (ush)dist; +    s->l_buf[s->last_lit++] = (uch)lc; +    if (dist == 0) { +        /* lc is the unmatched char */ +        s->dyn_ltree[lc].Freq++; +    } else { +        s->matches++; +        /* Here, lc is the match length - MIN_MATCH */ +        dist--;             /* dist = match distance - 1 */ +        Assert((ush)dist < (ush)MAX_DIST(s) && +               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && +               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match"); + +        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; +        s->dyn_dtree[d_code(dist)].Freq++; +    } + +#ifdef TRUNCATE_BLOCK +    /* Try to guess if it is profitable to stop the current block here */ +    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { +        /* Compute an upper bound for the compressed length */ +        ulg out_length = (ulg)s->last_lit*8L; +        ulg in_length = (ulg)((long)s->strstart - s->block_start); +        int dcode; +        for (dcode = 0; dcode < D_CODES; dcode++) { +            out_length += (ulg)s->dyn_dtree[dcode].Freq * +                (5L+extra_dbits[dcode]); +        } +        out_length >>= 3; +        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", +               s->last_lit, in_length, out_length, +               100L - out_length*100L/in_length)); +        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; +    } +#endif +    return (s->last_lit == s->lit_bufsize-1); +    /* We avoid equality with lit_bufsize because of wraparound at 64K +     * on 16 bit machines and because stored blocks are restricted to +     * 64K-1 bytes. +     */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) +    deflate_state *s; +    ct_data *ltree; /* literal tree */ +    ct_data *dtree; /* distance tree */ +{ +    unsigned dist;      /* distance of matched string */ +    int lc;             /* match length or unmatched char (if dist == 0) */ +    unsigned lx = 0;    /* running index in l_buf */ +    unsigned code;      /* the code to send */ +    int extra;          /* number of extra bits to send */ + +    if (s->last_lit != 0) do { +        dist = s->d_buf[lx]; +        lc = s->l_buf[lx++]; +        if (dist == 0) { +            send_code(s, lc, ltree); /* send a literal byte */ +            Tracecv(isgraph(lc), (stderr," '%c' ", lc)); +        } else { +            /* Here, lc is the match length - MIN_MATCH */ +            code = _length_code[lc]; +            send_code(s, code+LITERALS+1, ltree); /* send the length code */ +            extra = extra_lbits[code]; +            if (extra != 0) { +                lc -= base_length[code]; +                send_bits(s, lc, extra);       /* send the extra length bits */ +            } +            dist--; /* dist is now the match distance - 1 */ +            code = d_code(dist); +            Assert (code < D_CODES, "bad d_code"); + +            send_code(s, code, dtree);       /* send the distance code */ +            extra = extra_dbits[code]; +            if (extra != 0) { +                dist -= base_dist[code]; +                send_bits(s, dist, extra);   /* send the extra distance bits */ +            } +        } /* literal or match pair ? */ + +        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ +        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, +               "pendingBuf overflow"); + +    } while (lx < s->last_lit); + +    send_code(s, END_BLOCK, ltree); +    s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) +    deflate_state *s; +{ +    int n; + +    for (n = 0; n < 9; n++) +        if (s->dyn_ltree[n].Freq != 0) +            break; +    if (n == 9) +        for (n = 14; n < 32; n++) +            if (s->dyn_ltree[n].Freq != 0) +                break; +    s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) +    unsigned code; /* the value to invert */ +    int len;       /* its bit length */ +{ +    register unsigned res = 0; +    do { +        res |= code & 1; +        code >>= 1, res <<= 1; +    } while (--len > 0); +    return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) +    deflate_state *s; +{ +    if (s->bi_valid == 16) { +        put_short(s, s->bi_buf); +        s->bi_buf = 0; +        s->bi_valid = 0; +    } else if (s->bi_valid >= 8) { +        put_byte(s, (Byte)s->bi_buf); +        s->bi_buf >>= 8; +        s->bi_valid -= 8; +    } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) +    deflate_state *s; +{ +    if (s->bi_valid > 8) { +        put_short(s, s->bi_buf); +    } else if (s->bi_valid > 0) { +        put_byte(s, (Byte)s->bi_buf); +    } +    s->bi_buf = 0; +    s->bi_valid = 0; +#ifdef DEBUG +    s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) +    deflate_state *s; +    charf    *buf;    /* the input data */ +    unsigned len;     /* its length */ +    int      header;  /* true if block header must be written */ +{ +    bi_windup(s);        /* align on byte boundary */ +    s->last_eob_len = 8; /* enough lookahead for inflate */ + +    if (header) { +        put_short(s, (ush)len); +        put_short(s, (ush)~len); +#ifdef DEBUG +        s->bits_sent += 2*16; +#endif +    } +#ifdef DEBUG +    s->bits_sent += (ulg)len<<3; +#endif +    while (len--) { +        put_byte(s, *buf++); +    } +} diff --git a/src/zlib/trees.h b/src/zlib/trees.h new file mode 100644 index 0000000..72facf9 --- /dev/null +++ b/src/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}}, +{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}}, +{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}}, +{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}}, +{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}}, +{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}}, +{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}}, +{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}}, +{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}}, +{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}}, +{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}}, +{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}}, +{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}}, +{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}}, +{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}}, +{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}}, +{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}}, +{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}}, +{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}}, +{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}}, +{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}}, +{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}}, +{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}}, +{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}}, +{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}}, +{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}}, +{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}}, +{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}}, +{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}}, +{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}}, +{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}}, +{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}}, +{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}}, +{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}}, +{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}}, +{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}}, +{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}}, +{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}}, +{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}}, +{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}}, +{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}}, +{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}}, +{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}}, +{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}}, +{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}}, +{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}}, +{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}}, +{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}}, +{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}}, +{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}}, +{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}}, +{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}}, +{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}}, +{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}}, +{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}}, +{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}}, +{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}}, +{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8, + 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { +    0,     1,     2,     3,     4,     6,     8,    12,    16,    24, +   32,    48,    64,    96,   128,   192,   256,   384,   512,   768, + 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576 +}; + diff --git a/src/zlib/zconf.h b/src/zlib/zconf.h new file mode 100644 index 0000000..03a9431 --- /dev/null +++ b/src/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +#  define deflateInit_          z_deflateInit_ +#  define deflate               z_deflate +#  define deflateEnd            z_deflateEnd +#  define inflateInit_          z_inflateInit_ +#  define inflate               z_inflate +#  define inflateEnd            z_inflateEnd +#  define deflateInit2_         z_deflateInit2_ +#  define deflateSetDictionary  z_deflateSetDictionary +#  define deflateCopy           z_deflateCopy +#  define deflateReset          z_deflateReset +#  define deflateParams         z_deflateParams +#  define deflateBound          z_deflateBound +#  define deflatePrime          z_deflatePrime +#  define inflateInit2_         z_inflateInit2_ +#  define inflateSetDictionary  z_inflateSetDictionary +#  define inflateSync           z_inflateSync +#  define inflateSyncPoint      z_inflateSyncPoint +#  define inflateCopy           z_inflateCopy +#  define inflateReset          z_inflateReset +#  define inflateBack           z_inflateBack +#  define inflateBackEnd        z_inflateBackEnd +#  define compress              z_compress +#  define compress2             z_compress2 +#  define compressBound         z_compressBound +#  define uncompress            z_uncompress +#  define adler32               z_adler32 +#  define crc32                 z_crc32 +#  define get_crc_table         z_get_crc_table +#  define zError                z_zError + +#  define alloc_func            z_alloc_func +#  define free_func             z_free_func +#  define in_func               z_in_func +#  define out_func              z_out_func +#  define Byte                  z_Byte +#  define uInt                  z_uInt +#  define uLong                 z_uLong +#  define Bytef                 z_Bytef +#  define charf                 z_charf +#  define intf                  z_intf +#  define uIntf                 z_uIntf +#  define uLongf                z_uLongf +#  define voidpf                z_voidpf +#  define voidp                 z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +#  define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +#  define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +#  define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +#  ifndef WIN32 +#    define WIN32 +#  endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +#    ifndef SYS16BIT +#      define SYS16BIT +#    endif +#  endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +#  define MAXSEG_64K +#endif +#ifdef MSDOS +#  define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +#  ifndef STDC +#    define STDC +#  endif +#  if __STDC_VERSION__ >= 199901L +#    ifndef STDC99 +#      define STDC99 +#    endif +#  endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +#  define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +#  define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +#  define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +#  define STDC +#endif + +#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */ +#  define STDC +#endif + +#ifndef STDC +#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +#    define const       /* note: need a more gentle solution here */ +#  endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +#  define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +#  ifdef MAXSEG_64K +#    define MAX_MEM_LEVEL 8 +#  else +#    define MAX_MEM_LEVEL 9 +#  endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +#  define MAX_WBITS   15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): +            (1 << (windowBits+2)) +  (1 << (memLevel+9)) + that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with +     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + +   The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + +                        /* Type declarations */ + +#ifndef OF /* function prototypes */ +#  ifdef STDC +#    define OF(args)  args +#  else +#    define OF(args)  () +#  endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +#  if defined(M_I86SM) || defined(M_I86MM) +     /* MSC small or medium model */ +#    define SMALL_MEDIUM +#    ifdef _MSC_VER +#      define FAR _far +#    else +#      define FAR far +#    endif +#  endif +#  if (defined(__SMALL__) || defined(__MEDIUM__)) +     /* Turbo C small or medium model */ +#    define SMALL_MEDIUM +#    ifdef __BORLANDC__ +#      define FAR _far +#    else +#      define FAR far +#    endif +#  endif +#endif + +#if defined(WINDOWS) || defined(WIN32) +   /* If building or using zlib as a DLL, define ZLIB_DLL. +    * This is not mandatory, but it offers a little performance increase. +    */ +#  ifdef ZLIB_DLL +#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +#      ifdef ZLIB_INTERNAL +#        define ZEXTERN extern __declspec(dllexport) +#      else +#        define ZEXTERN extern __declspec(dllimport) +#      endif +#    endif +#  endif  /* ZLIB_DLL */ +   /* If building or using zlib with the WINAPI/WINAPIV calling convention, +    * define ZLIB_WINAPI. +    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. +    */ +#  ifdef ZLIB_WINAPI +#    ifdef FAR +#      undef FAR +#    endif +#    include <windows.h> +     /* No need for _export, use ZLIB.DEF instead. */ +     /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +#    define ZEXPORT WINAPI +#    ifdef WIN32 +#      define ZEXPORTVA WINAPIV +#    else +#      define ZEXPORTVA FAR CDECL +#    endif +#  endif +#endif + +#if defined (__BEOS__) +#  ifdef ZLIB_DLL +#    ifdef ZLIB_INTERNAL +#      define ZEXPORT   __declspec(dllexport) +#      define ZEXPORTVA __declspec(dllexport) +#    else +#      define ZEXPORT   __declspec(dllimport) +#      define ZEXPORTVA __declspec(dllimport) +#    endif +#  endif +#endif + +#ifndef ZEXTERN +#  define ZEXTERN extern +#endif +#ifndef ZEXPORT +#  define ZEXPORT +#endif +#ifndef ZEXPORTVA +#  define ZEXPORTVA +#endif + +#ifndef FAR +#  define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char  Byte;  /* 8 bits */ +#endif +typedef unsigned int   uInt;  /* 16 bits or more */ +typedef unsigned long  uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM +   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +#  define Bytef Byte FAR +#else +   typedef Byte  FAR Bytef; +#endif +typedef char  FAR charf; +typedef int   FAR intf; +typedef uInt  FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC +   typedef void const *voidpc; +   typedef void FAR   *voidpf; +   typedef void       *voidp; +#else +   typedef Byte const *voidpc; +   typedef Byte FAR   *voidpf; +   typedef Byte       *voidp; +#endif + +#if 0           /* HAVE_UNISTD_H -- this line is updated by ./configure */ +#  include <sys/types.h> /* for off_t */ +#  include <unistd.h>    /* for SEEK_* and off_t */ +#  ifdef VMS +#    include <unixio.h>   /* for off_t */ +#  endif +#  define z_off_t off_t +#endif +#ifndef SEEK_SET +#  define SEEK_SET        0       /* Seek from beginning of file.  */ +#  define SEEK_CUR        1       /* Seek from current position.  */ +#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +#  define z_off_t long +#endif + +#if defined(__OS400__) +#  define NO_vsnprintf +#endif + +#if defined(__MVS__) +#  define NO_vsnprintf +#  ifdef FAR +#    undef FAR +#  endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +#   pragma map(deflateInit_,"DEIN") +#   pragma map(deflateInit2_,"DEIN2") +#   pragma map(deflateEnd,"DEEND") +#   pragma map(deflateBound,"DEBND") +#   pragma map(inflateInit_,"ININ") +#   pragma map(inflateInit2_,"ININ2") +#   pragma map(inflateEnd,"INEND") +#   pragma map(inflateSync,"INSY") +#   pragma map(inflateSetDictionary,"INSEDI") +#   pragma map(compressBound,"CMBND") +#   pragma map(inflate_table,"INTABL") +#   pragma map(inflate_fast,"INFA") +#   pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/zlib/zlib.h b/src/zlib/zlib.h new file mode 100644 index 0000000..0228179 --- /dev/null +++ b/src/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library +  version 1.2.3, July 18th, 2005 + +  Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + +  This software is provided 'as-is', without any express or implied +  warranty.  In no event will the authors be held liable for any damages +  arising from the use of this software. + +  Permission is granted to anyone to use this software for any purpose, +  including commercial applications, and to alter it and redistribute it +  freely, subject to the following restrictions: + +  1. The origin of this software must not be misrepresented; you must not +     claim that you wrote the original software. If you use this software +     in a product, an acknowledgment in the product documentation would be +     appreciated but is not required. +  2. Altered source versions must be plainly marked as such, and must not be +     misrepresented as being the original software. +  3. This notice may not be removed or altered from any source distribution. + +  Jean-loup Gailly        Mark Adler +  jloup@gzip.org          madler@alumni.caltech.edu + + +  The data format used by the zlib library is described by RFCs (Request for +  Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt +  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* +     The 'zlib' compression library provides in-memory compression and +  decompression functions, including integrity checks of the uncompressed +  data.  This version of the library supports only one compression method +  (deflation) but other algorithms will be added later and will have the same +  stream interface. + +     Compression can be done in a single step if the buffers are large +  enough (for example if an input file is mmap'ed), or can be done by +  repeated calls of the compression function.  In the latter case, the +  application must provide more input and/or consume the output +  (providing more output space) before each call. + +     The compressed data format used by default by the in-memory functions is +  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped +  around a deflate stream, which is itself documented in RFC 1951. + +     The library also supports reading and writing files in gzip (.gz) format +  with an interface similar to that of stdio using the functions that start +  with "gz".  The gzip format is different from the zlib format.  gzip is a +  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + +     This library can optionally read and write gzip streams in memory as well. + +     The zlib format was designed to be compact and fast for use in memory +  and on communications channels.  The gzip format was designed for single- +  file compression on file systems, has a larger header than zlib to maintain +  directory information, and uses a different, slower check method than zlib. + +     The library does not install any signal handler. The decoder checks +  the consistency of the compressed data, so the library should never +  crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void   (*free_func)  OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { +    Bytef    *next_in;  /* next input byte */ +    uInt     avail_in;  /* number of bytes available at next_in */ +    uLong    total_in;  /* total nb of input bytes read so far */ + +    Bytef    *next_out; /* next output byte should be put there */ +    uInt     avail_out; /* remaining free space at next_out */ +    uLong    total_out; /* total nb of bytes output so far */ + +    char     *msg;      /* last error message, NULL if no error */ +    struct internal_state FAR *state; /* not visible by applications */ + +    alloc_func zalloc;  /* used to allocate the internal state */ +    free_func  zfree;   /* used to free the internal state */ +    voidpf     opaque;  /* private data object passed to zalloc and zfree */ + +    int     data_type;  /* best guess about the data type: binary or text */ +    uLong   adler;      /* adler32 value of the uncompressed data */ +    uLong   reserved;   /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* +     gzip header information passed to and from zlib routines.  See RFC 1952 +  for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { +    int     text;       /* true if compressed data believed to be text */ +    uLong   time;       /* modification time */ +    int     xflags;     /* extra flags (not used when writing a gzip file) */ +    int     os;         /* operating system */ +    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */ +    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */ +    uInt    extra_max;  /* space at extra (only when reading header) */ +    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */ +    uInt    name_max;   /* space at name (only when reading header) */ +    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */ +    uInt    comm_max;   /* space at comment (only when reading header) */ +    int     hcrc;       /* true if there was or will be a header crc */ +    int     done;       /* true when done reading gzip header (not used +                           when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* +   The application must update next_in and avail_in when avail_in has +   dropped to zero. It must update next_out and avail_out when avail_out +   has dropped to zero. The application must initialize zalloc, zfree and +   opaque before calling the init function. All other fields are set by the +   compression library and must not be updated by the application. + +   The opaque value provided by the application will be passed as the first +   parameter for calls of zalloc and zfree. This can be useful for custom +   memory management. The compression library attaches no meaning to the +   opaque value. + +   zalloc must return Z_NULL if there is not enough memory for the object. +   If zlib is used in a multi-threaded application, zalloc and zfree must be +   thread safe. + +   On 16-bit systems, the functions zalloc and zfree must be able to allocate +   exactly 65536 bytes, but will not be required to allocate more than this +   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, +   pointers returned by zalloc for objects of exactly 65536 bytes *must* +   have their offset normalized to zero. The default allocation function +   provided by this library ensures this (see zutil.c). To reduce memory +   requirements and avoid any allocation of 64K objects, at the expense of +   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + +   The fields total_in and total_out can be used for statistics or +   progress reports. After compression, total_in holds the total size of +   the uncompressed data and may be saved for use in the decompressor +   (particularly if the decompressor wants to decompress everything in +   a single step). +*/ + +                        /* constants */ + +#define Z_NO_FLUSH      0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH    2 +#define Z_FULL_FLUSH    3 +#define Z_FINISH        4 +#define Z_BLOCK         5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK            0 +#define Z_STREAM_END    1 +#define Z_NEED_DICT     2 +#define Z_ERRNO        (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR   (-3) +#define Z_MEM_ERROR    (-4) +#define Z_BUF_ERROR    (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION         0 +#define Z_BEST_SPEED             1 +#define Z_BEST_COMPRESSION       9 +#define Z_DEFAULT_COMPRESSION  (-1) +/* compression levels */ + +#define Z_FILTERED            1 +#define Z_HUFFMAN_ONLY        2 +#define Z_RLE                 3 +#define Z_FIXED               4 +#define Z_DEFAULT_STRATEGY    0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY   0 +#define Z_TEXT     1 +#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN  2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED   8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + +                        /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. +   If the first character differs, the library code actually used is +   not compatible with the zlib.h header file used by the application. +   This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + +     Initializes the internal stream state for compression. The fields +   zalloc, zfree and opaque must be initialized before by the caller. +   If zalloc and zfree are set to Z_NULL, deflateInit updates them to +   use default allocation functions. + +     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: +   1 gives best speed, 9 gives best compression, 0 gives no compression at +   all (the input data is simply copied a block at a time). +   Z_DEFAULT_COMPRESSION requests a default compromise between speed and +   compression (currently equivalent to level 6). + +     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not +   enough memory, Z_STREAM_ERROR if level is not a valid compression level, +   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible +   with the version assumed by the caller (ZLIB_VERSION). +   msg is set to null if there is no error message.  deflateInit does not +   perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* +    deflate compresses as much data as possible, and stops when the input +  buffer becomes empty or the output buffer becomes full. It may introduce some +  output latency (reading input without producing any output) except when +  forced to flush. + +    The detailed semantics are as follows. deflate performs one or both of the +  following actions: + +  - Compress more input starting at next_in and update next_in and avail_in +    accordingly. If not all input can be processed (because there is not +    enough room in the output buffer), next_in and avail_in are updated and +    processing will resume at this point for the next call of deflate(). + +  - Provide more output starting at next_out and update next_out and avail_out +    accordingly. This action is forced if the parameter flush is non zero. +    Forcing flush frequently degrades the compression ratio, so this parameter +    should be set only when necessary (in interactive applications). +    Some output may be provided even if flush is not set. + +  Before the call of deflate(), the application should ensure that at least +  one of the actions is possible, by providing more input and/or consuming +  more output, and updating avail_in or avail_out accordingly; avail_out +  should never be zero before the call. The application can consume the +  compressed output when it wants, for example when the output buffer is full +  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK +  and with zero avail_out, it must be called again after making room in the +  output buffer because there might be more output pending. + +    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to +  decide how much data to accumualte before producing output, in order to +  maximize compression. + +    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is +  flushed to the output buffer and the output is aligned on a byte boundary, so +  that the decompressor can get all input data available so far. (In particular +  avail_in is zero after the call if enough output space has been provided +  before the call.)  Flushing may degrade compression for some compression +  algorithms and so it should be used only when necessary. + +    If flush is set to Z_FULL_FLUSH, all output is flushed as with +  Z_SYNC_FLUSH, and the compression state is reset so that decompression can +  restart from this point if previous compressed data has been damaged or if +  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade +  compression. + +    If deflate returns with avail_out == 0, this function must be called again +  with the same value of the flush parameter and more output space (updated +  avail_out), until the flush is complete (deflate returns with non-zero +  avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that +  avail_out is greater than six to avoid repeated flush markers due to +  avail_out == 0 on return. + +    If the parameter flush is set to Z_FINISH, pending input is processed, +  pending output is flushed and deflate returns with Z_STREAM_END if there +  was enough output space; if deflate returns with Z_OK, this function must be +  called again with Z_FINISH and more output space (updated avail_out) but no +  more input data, until it returns with Z_STREAM_END or an error. After +  deflate has returned Z_STREAM_END, the only possible operations on the +  stream are deflateReset or deflateEnd. + +    Z_FINISH can be used immediately after deflateInit if all the compression +  is to be done in a single step. In this case, avail_out must be at least +  the value returned by deflateBound (see below). If deflate does not return +  Z_STREAM_END, then it must be called again as described above. + +    deflate() sets strm->adler to the adler32 checksum of all input read +  so far (that is, total_in bytes). + +    deflate() may update strm->data_type if it can make a good guess about +  the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered +  binary. This field is only for information purposes and does not affect +  the compression algorithm in any manner. + +    deflate() returns Z_OK if some progress has been made (more input +  processed or more output produced), Z_STREAM_END if all input has been +  consumed and all output has been produced (only when flush is set to +  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example +  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible +  (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not +  fatal, and deflate() can be called again with more input and more output +  space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* +     All dynamically allocated data structures for this stream are freed. +   This function discards any unprocessed input and does not flush any +   pending output. + +     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the +   stream state was inconsistent, Z_DATA_ERROR if the stream was freed +   prematurely (some input or output was discarded). In the error case, +   msg may be set but then points to a static string (which must not be +   deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + +     Initializes the internal stream state for decompression. The fields +   next_in, avail_in, zalloc, zfree and opaque must be initialized before by +   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact +   value depends on the compression method), inflateInit determines the +   compression method from the zlib header and allocates all data structures +   accordingly; otherwise the allocation will be deferred to the first call of +   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to +   use default allocation functions. + +     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough +   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the +   version assumed by the caller.  msg is set to null if there is no error +   message. inflateInit does not perform any decompression apart from reading +   the zlib header if present: this will be done by inflate().  (So next_in and +   avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* +    inflate decompresses as much data as possible, and stops when the input +  buffer becomes empty or the output buffer becomes full. It may introduce +  some output latency (reading input without producing any output) except when +  forced to flush. + +  The detailed semantics are as follows. inflate performs one or both of the +  following actions: + +  - Decompress more input starting at next_in and update next_in and avail_in +    accordingly. If not all input can be processed (because there is not +    enough room in the output buffer), next_in is updated and processing +    will resume at this point for the next call of inflate(). + +  - Provide more output starting at next_out and update next_out and avail_out +    accordingly.  inflate() provides as much output as possible, until there +    is no more input data or no more space in the output buffer (see below +    about the flush parameter). + +  Before the call of inflate(), the application should ensure that at least +  one of the actions is possible, by providing more input and/or consuming +  more output, and updating the next_* and avail_* values accordingly. +  The application can consume the uncompressed output when it wants, for +  example when the output buffer is full (avail_out == 0), or after each +  call of inflate(). If inflate returns Z_OK and with zero avail_out, it +  must be called again after making room in the output buffer because there +  might be more output pending. + +    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, +  Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much +  output as possible to the output buffer. Z_BLOCK requests that inflate() stop +  if and when it gets to the next deflate block boundary. When decoding the +  zlib or gzip format, this will cause inflate() to return immediately after +  the header and before the first block. When doing a raw inflate, inflate() +  will go ahead and process the first block, and will return when it gets to +  the end of that block, or when it runs out of data. + +    The Z_BLOCK option assists in appending to or combining deflate streams. +  Also to assist in this, on return inflate() will set strm->data_type to the +  number of unused bits in the last byte taken from strm->next_in, plus 64 +  if inflate() is currently decoding the last block in the deflate stream, +  plus 128 if inflate() returned immediately after decoding an end-of-block +  code or decoding the complete header up to just before the first byte of the +  deflate stream. The end-of-block will not be indicated until all of the +  uncompressed data from that block has been written to strm->next_out.  The +  number of unused bits may in general be greater than seven, except when +  bit 7 of data_type is set, in which case the number of unused bits will be +  less than eight. + +    inflate() should normally be called until it returns Z_STREAM_END or an +  error. However if all decompression is to be performed in a single step +  (a single call of inflate), the parameter flush should be set to +  Z_FINISH. In this case all pending input is processed and all pending +  output is flushed; avail_out must be large enough to hold all the +  uncompressed data. (The size of the uncompressed data may have been saved +  by the compressor for this purpose.) The next operation on this stream must +  be inflateEnd to deallocate the decompression state. The use of Z_FINISH +  is never required, but can be used to inform inflate that a faster approach +  may be used for the single inflate() call. + +     In this implementation, inflate() always flushes as much output as +  possible to the output buffer, and always uses the faster approach on the +  first call. So the only effect of the flush parameter in this implementation +  is on the return value of inflate(), as noted below, or when it returns early +  because Z_BLOCK is used. + +     If a preset dictionary is needed after this call (see inflateSetDictionary +  below), inflate sets strm->adler to the adler32 checksum of the dictionary +  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets +  strm->adler to the adler32 checksum of all output produced so far (that is, +  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described +  below. At the end of the stream, inflate() checks that its computed adler32 +  checksum is equal to that saved by the compressor and returns Z_STREAM_END +  only if the checksum is correct. + +    inflate() will decompress and check either zlib-wrapped or gzip-wrapped +  deflate data.  The header type is detected automatically.  Any information +  contained in the gzip header is not retained, so applications that need that +  information should instead use raw inflate, see inflateInit2() below, or +  inflateBack() and perform their own processing of the gzip header and +  trailer. + +    inflate() returns Z_OK if some progress has been made (more input processed +  or more output produced), Z_STREAM_END if the end of the compressed data has +  been reached and all uncompressed output has been produced, Z_NEED_DICT if a +  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +  corrupted (input stream not conforming to the zlib format or incorrect check +  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example +  if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, +  Z_BUF_ERROR if no progress is possible or if there was not enough room in the +  output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and +  inflate() can be called again with more input and more output space to +  continue decompressing. If Z_DATA_ERROR is returned, the application may then +  call inflateSync() to look for a good compression block if a partial recovery +  of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* +     All dynamically allocated data structures for this stream are freed. +   This function discards any unprocessed input and does not flush any +   pending output. + +     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +   was inconsistent. In the error case, msg may be set but then points to a +   static string (which must not be deallocated). +*/ + +                        /* Advanced functions */ + +/* +    The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, +                                     int  level, +                                     int  method, +                                     int  windowBits, +                                     int  memLevel, +                                     int  strategy)); + +     This is another version of deflateInit with more compression options. The +   fields next_in, zalloc, zfree and opaque must be initialized before by +   the caller. + +     The method parameter is the compression method. It must be Z_DEFLATED in +   this version of the library. + +     The windowBits parameter is the base two logarithm of the window size +   (the size of the history buffer). It should be in the range 8..15 for this +   version of the library. Larger values of this parameter result in better +   compression at the expense of memory usage. The default value is 15 if +   deflateInit is used instead. + +     windowBits can also be -8..-15 for raw deflate. In this case, -windowBits +   determines the window size. deflate() will then generate raw deflate data +   with no zlib header or trailer, and will not compute an adler32 check value. + +     windowBits can also be greater than 15 for optional gzip encoding. Add +   16 to windowBits to write a simple gzip header and trailer around the +   compressed data instead of a zlib wrapper. The gzip header will have no +   file name, no extra data, no comment, no modification time (set to zero), +   no header crc, and the operating system will be set to 255 (unknown).  If a +   gzip stream is being written, strm->adler is a crc32 instead of an adler32. + +     The memLevel parameter specifies how much memory should be allocated +   for the internal compression state. memLevel=1 uses minimum memory but +   is slow and reduces compression ratio; memLevel=9 uses maximum memory +   for optimal speed. The default value is 8. See zconf.h for total memory +   usage as a function of windowBits and memLevel. + +     The strategy parameter is used to tune the compression algorithm. Use the +   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a +   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no +   string match), or Z_RLE to limit match distances to one (run-length +   encoding). Filtered data consists mostly of small values with a somewhat +   random distribution. In this case, the compression algorithm is tuned to +   compress them better. The effect of Z_FILTERED is to force more Huffman +   coding and less string matching; it is somewhat intermediate between +   Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as +   Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy +   parameter only affects the compression ratio but not the correctness of the +   compressed output even if it is not set appropriately.  Z_FIXED prevents the +   use of dynamic Huffman codes, allowing for a simpler decoder for special +   applications. + +      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough +   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid +   method). msg is set to null if there is no error message.  deflateInit2 does +   not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, +                                             const Bytef *dictionary, +                                             uInt  dictLength)); +/* +     Initializes the compression dictionary from the given byte sequence +   without producing any compressed output. This function must be called +   immediately after deflateInit, deflateInit2 or deflateReset, before any +   call of deflate. The compressor and decompressor must use exactly the same +   dictionary (see inflateSetDictionary). + +     The dictionary should consist of strings (byte sequences) that are likely +   to be encountered later in the data to be compressed, with the most commonly +   used strings preferably put towards the end of the dictionary. Using a +   dictionary is most useful when the data to be compressed is short and can be +   predicted with good accuracy; the data can then be compressed better than +   with the default empty dictionary. + +     Depending on the size of the compression data structures selected by +   deflateInit or deflateInit2, a part of the dictionary may in effect be +   discarded, for example if the dictionary is larger than the window size in +   deflate or deflate2. Thus the strings most likely to be useful should be +   put at the end of the dictionary, not at the front. In addition, the +   current implementation of deflate will use at most the window size minus +   262 bytes of the provided dictionary. + +     Upon return of this function, strm->adler is set to the adler32 value +   of the dictionary; the decompressor may later use this value to determine +   which dictionary has been used by the compressor. (The adler32 value +   applies to the whole dictionary even if only a subset of the dictionary is +   actually used by the compressor.) If a raw deflate was requested, then the +   adler32 value is not computed and strm->adler is not set. + +     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a +   parameter is invalid (such as NULL dictionary) or the stream state is +   inconsistent (for example if deflate has already been called for this stream +   or if the compression method is bsort). deflateSetDictionary does not +   perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, +                                    z_streamp source)); +/* +     Sets the destination stream as a complete copy of the source stream. + +     This function can be useful when several compression strategies will be +   tried, for example when there are several ways of pre-processing the input +   data with a filter. The streams that will be discarded should then be freed +   by calling deflateEnd.  Note that deflateCopy duplicates the internal +   compression state which can be quite large, so this strategy is slow and +   can consume lots of memory. + +     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not +   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent +   (such as zalloc being NULL). msg is left unchanged in both source and +   destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* +     This function is equivalent to deflateEnd followed by deflateInit, +   but does not free and reallocate all the internal compression state. +   The stream will keep the same compression level and any other attributes +   that may have been set by deflateInit2. + +      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, +                                      int level, +                                      int strategy)); +/* +     Dynamically update the compression level and compression strategy.  The +   interpretation of level and strategy is as in deflateInit2.  This can be +   used to switch between compression and straight copy of the input data, or +   to switch to a different kind of input data requiring a different +   strategy. If the compression level is changed, the input available so far +   is compressed with the old level (and may be flushed); the new level will +   take effect only at the next call of deflate(). + +     Before the call of deflateParams, the stream state must be set as for +   a call of deflate(), since the currently available input may have to +   be compressed and flushed. In particular, strm->avail_out must be non-zero. + +     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source +   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR +   if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, +                                    int good_length, +                                    int max_lazy, +                                    int nice_length, +                                    int max_chain)); +/* +     Fine tune deflate's internal compression parameters.  This should only be +   used by someone who understands the algorithm used by zlib's deflate for +   searching for the best matching string, and even then only by the most +   fanatic optimizer trying to squeeze out the last compressed bit for their +   specific input data.  Read the deflate.c source code for the meaning of the +   max_lazy, good_length, nice_length, and max_chain parameters. + +     deflateTune() can be called after deflateInit() or deflateInit2(), and +   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, +                                       uLong sourceLen)); +/* +     deflateBound() returns an upper bound on the compressed size after +   deflation of sourceLen bytes.  It must be called after deflateInit() +   or deflateInit2().  This would be used to allocate an output buffer +   for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, +                                     int bits, +                                     int value)); +/* +     deflatePrime() inserts bits in the deflate output stream.  The intent +  is that this function is used to start off the deflate output with the +  bits leftover from a previous deflate stream when appending to it.  As such, +  this function can only be used for raw deflate, and must be used before the +  first deflate() call after a deflateInit2() or deflateReset().  bits must be +  less than or equal to 16, and that many of the least significant bits of +  value will be inserted in the output. + +      deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, +                                         gz_headerp head)); +/* +      deflateSetHeader() provides gzip header information for when a gzip +   stream is requested by deflateInit2().  deflateSetHeader() may be called +   after deflateInit2() or deflateReset() and before the first call of +   deflate().  The text, time, os, extra field, name, and comment information +   in the provided gz_header structure are written to the gzip header (xflag is +   ignored -- the extra flags are set according to the compression level).  The +   caller must assure that, if not Z_NULL, name and comment are terminated with +   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are +   available there.  If hcrc is true, a gzip header crc is included.  Note that +   the current versions of the command-line version of gzip (up through version +   1.3.x) do not support header crc's, and will report that it is a "multi-part +   gzip file" and give up. + +      If deflateSetHeader is not used, the default gzip header has text false, +   the time set to zero, and os set to 255, with no extra, name, or comment +   fields.  The gzip header is returned to the default state by deflateReset(). + +      deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, +                                     int  windowBits)); + +     This is another version of inflateInit with an extra parameter. The +   fields next_in, avail_in, zalloc, zfree and opaque must be initialized +   before by the caller. + +     The windowBits parameter is the base two logarithm of the maximum window +   size (the size of the history buffer).  It should be in the range 8..15 for +   this version of the library. The default value is 15 if inflateInit is used +   instead. windowBits must be greater than or equal to the windowBits value +   provided to deflateInit2() while compressing, or it must be equal to 15 if +   deflateInit2() was not used. If a compressed stream with a larger window +   size is given as input, inflate() will return with the error code +   Z_DATA_ERROR instead of trying to allocate a larger window. + +     windowBits can also be -8..-15 for raw inflate. In this case, -windowBits +   determines the window size. inflate() will then process raw deflate data, +   not looking for a zlib or gzip header, not generating a check value, and not +   looking for any check values for comparison at the end of the stream. This +   is for use with other formats that use the deflate compressed data format +   such as zip.  Those formats provide their own check values. If a custom +   format is developed using the raw deflate format for compressed data, it is +   recommended that a check value such as an adler32 or a crc32 be applied to +   the uncompressed data as is done in the zlib, gzip, and zip formats.  For +   most applications, the zlib format should be used as is. Note that comments +   above on the use in deflateInit2() applies to the magnitude of windowBits. + +     windowBits can also be greater than 15 for optional gzip decoding. Add +   32 to windowBits to enable zlib and gzip decoding with automatic header +   detection, or add 16 to decode only the gzip format (the zlib format will +   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is +   a crc32 instead of an adler32. + +     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough +   memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg +   is set to null if there is no error message.  inflateInit2 does not perform +   any decompression apart from reading the zlib header if present: this will +   be done by inflate(). (So next_in and avail_in may be modified, but next_out +   and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, +                                             const Bytef *dictionary, +                                             uInt  dictLength)); +/* +     Initializes the decompression dictionary from the given uncompressed byte +   sequence. This function must be called immediately after a call of inflate, +   if that call returned Z_NEED_DICT. The dictionary chosen by the compressor +   can be determined from the adler32 value returned by that call of inflate. +   The compressor and decompressor must use exactly the same dictionary (see +   deflateSetDictionary).  For raw inflate, this function can be called +   immediately after inflateInit2() or inflateReset() and before any call of +   inflate() to set the dictionary.  The application must insure that the +   dictionary that was used for compression is provided. + +     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +   parameter is invalid (such as NULL dictionary) or the stream state is +   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +   expected one (incorrect adler32 value). inflateSetDictionary does not +   perform any decompression: this will be done by subsequent calls of +   inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* +    Skips invalid compressed data until a full flush point (see above the +  description of deflate with Z_FULL_FLUSH) can be found, or until all +  available input is skipped. No output is provided. + +    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +  if no more input was provided, Z_DATA_ERROR if no flush point has been found, +  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +  case, the application may save the current current value of total_in which +  indicates where valid compressed data was found. In the error case, the +  application may repeatedly call inflateSync, providing more input each time, +  until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, +                                    z_streamp source)); +/* +     Sets the destination stream as a complete copy of the source stream. + +     This function can be useful when randomly accessing a large stream.  The +   first pass through the stream can periodically record the inflate state, +   allowing restarting inflate at those points when randomly accessing the +   stream. + +     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not +   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent +   (such as zalloc being NULL). msg is left unchanged in both source and +   destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* +     This function is equivalent to inflateEnd followed by inflateInit, +   but does not free and reallocate all the internal decompression state. +   The stream will keep attributes that may have been set by inflateInit2. + +      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, +                                     int bits, +                                     int value)); +/* +     This function inserts bits in the inflate input stream.  The intent is +  that this function is used to start inflating at a bit position in the +  middle of a byte.  The provided bits will be used before any bytes are used +  from next_in.  This function should only be used with raw inflate, and +  should be used before the first inflate() call after inflateInit2() or +  inflateReset().  bits must be less than or equal to 16, and that many of the +  least significant bits of value will be inserted in the input. + +      inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, +                                         gz_headerp head)); +/* +      inflateGetHeader() requests that gzip header information be stored in the +   provided gz_header structure.  inflateGetHeader() may be called after +   inflateInit2() or inflateReset(), and before the first call of inflate(). +   As inflate() processes the gzip stream, head->done is zero until the header +   is completed, at which time head->done is set to one.  If a zlib stream is +   being decoded, then head->done is set to -1 to indicate that there will be +   no gzip header information forthcoming.  Note that Z_BLOCK can be used to +   force inflate() to return immediately after header processing is complete +   and before any actual data is decompressed. + +      The text, time, xflags, and os fields are filled in with the gzip header +   contents.  hcrc is set to true if there is a header CRC.  (The header CRC +   was valid if done is set to one.)  If extra is not Z_NULL, then extra_max +   contains the maximum number of bytes to write to extra.  Once done is true, +   extra_len contains the actual extra field length, and extra contains the +   extra field, or that field truncated if extra_max is less than extra_len. +   If name is not Z_NULL, then up to name_max characters are written there, +   terminated with a zero unless the length is greater than name_max.  If +   comment is not Z_NULL, then up to comm_max characters are written there, +   terminated with a zero unless the length is greater than comm_max.  When +   any of extra, name, or comment are not Z_NULL and the respective field is +   not present in the header, then that field is set to Z_NULL to signal its +   absence.  This allows the use of deflateSetHeader() with the returned +   structure to duplicate the header.  However if those fields are set to +   allocated memory, then the application will need to save those pointers +   elsewhere so that they can be eventually freed. + +      If inflateGetHeader is not used, then the header information is simply +   discarded.  The header is always checked for validity, including the header +   CRC if present.  inflateReset() will reset the process to discard the header +   information.  The application would need to call inflateGetHeader() again to +   retrieve the header from the next gzip stream. + +      inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source +   stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, +                                        unsigned char FAR *window)); + +     Initialize the internal stream state for decompression using inflateBack() +   calls.  The fields zalloc, zfree and opaque in strm must be initialized +   before the call.  If zalloc and zfree are Z_NULL, then the default library- +   derived memory allocation routines are used.  windowBits is the base two +   logarithm of the window size, in the range 8..15.  window is a caller +   supplied buffer of that size.  Except for special applications where it is +   assured that deflate was used with small window sizes, windowBits must be 15 +   and a 32K byte window must be supplied to be able to decompress general +   deflate streams. + +     See inflateBack() for the usage of these routines. + +     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of +   the paramaters are invalid, Z_MEM_ERROR if the internal state could not +   be allocated, or Z_VERSION_ERROR if the version of the library does not +   match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, +                                    in_func in, void FAR *in_desc, +                                    out_func out, void FAR *out_desc)); +/* +     inflateBack() does a raw inflate with a single call using a call-back +   interface for input and output.  This is more efficient than inflate() for +   file i/o applications in that it avoids copying between the output and the +   sliding window by simply making the window itself the output buffer.  This +   function trusts the application to not change the output buffer passed by +   the output function, at least until inflateBack() returns. + +     inflateBackInit() must be called first to allocate the internal state +   and to initialize the state with the user-provided window buffer. +   inflateBack() may then be used multiple times to inflate a complete, raw +   deflate stream with each call.  inflateBackEnd() is then called to free +   the allocated state. + +     A raw deflate stream is one with no zlib or gzip header or trailer. +   This routine would normally be used in a utility that reads zip or gzip +   files and writes out uncompressed files.  The utility would decode the +   header and process the trailer on its own, hence this routine expects +   only the raw deflate stream to decompress.  This is different from the +   normal behavior of inflate(), which expects either a zlib or gzip header and +   trailer around the deflate stream. + +     inflateBack() uses two subroutines supplied by the caller that are then +   called by inflateBack() for input and output.  inflateBack() calls those +   routines until it reads a complete deflate stream and writes out all of the +   uncompressed data, or until it encounters an error.  The function's +   parameters and return types are defined above in the in_func and out_func +   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the +   number of bytes of provided input, and a pointer to that input in buf.  If +   there is no input available, in() must return zero--buf is ignored in that +   case--and inflateBack() will return a buffer error.  inflateBack() will call +   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out() +   should return zero on success, or non-zero on failure.  If out() returns +   non-zero, inflateBack() will return with an error.  Neither in() nor out() +   are permitted to change the contents of the window provided to +   inflateBackInit(), which is also the buffer that out() uses to write from. +   The length written by out() will be at most the window size.  Any non-zero +   amount of input may be provided by in(). + +     For convenience, inflateBack() can be provided input on the first call by +   setting strm->next_in and strm->avail_in.  If that input is exhausted, then +   in() will be called.  Therefore strm->next_in must be initialized before +   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called +   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in +   must also be initialized, and then if strm->avail_in is not zero, input will +   initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + +     The in_desc and out_desc parameters of inflateBack() is passed as the +   first parameter of in() and out() respectively when they are called.  These +   descriptors can be optionally used to pass any information that the caller- +   supplied in() and out() functions need to do their job. + +     On return, inflateBack() will set strm->next_in and strm->avail_in to +   pass back any unused input that was provided by the last in() call.  The +   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR +   if in() or out() returned an error, Z_DATA_ERROR if there was a format +   error in the deflate stream (in which case strm->msg is set to indicate the +   nature of the error), or Z_STREAM_ERROR if the stream was not properly +   initialized.  In the case of Z_BUF_ERROR, an input or output error can be +   distinguished using strm->next_in which will be Z_NULL only if in() returned +   an error.  If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to +   out() returning non-zero.  (in() will always be called before out(), so +   strm->next_in is assured to be defined if out() returns non-zero.)  Note +   that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* +     All memory allocated by inflateBackInit() is freed. + +     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream +   state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + +    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: +     1.0: size of uInt +     3.2: size of uLong +     5.4: size of voidpf (pointer) +     7.6: size of z_off_t + +    Compiler, assembler, and debug options: +     8: DEBUG +     9: ASMV or ASMINF -- use ASM code +     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention +     11: 0 (reserved) + +    One-time table building (smaller code, but not thread-safe if true): +     12: BUILDFIXED -- build static block decoding tables when needed +     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed +     14,15: 0 (reserved) + +    Library content (indicates missing functionality): +     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking +                          deflate code when not needed) +     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect +                    and decode gzip streams (to avoid linking crc code) +     18-19: 0 (reserved) + +    Operation variations (changes in library functionality): +     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate +     21: FASTEST -- deflate algorithm with only one, lowest compression level +     22,23: 0 (reserved) + +    The sprintf variant used by gzprintf (zero is best): +     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format +     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! +     26: 0 = returns value, 1 = void -- 1 means inferred string length returned + +    Remainder: +     27-31: 0 (reserved) + */ + + +                        /* utility functions */ + +/* +     The following utility functions are implemented on top of the +   basic stream-oriented functions. To simplify the interface, some +   default options are assumed (compression level and memory usage, +   standard memory allocation functions). The source code of these +   utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen, +                                 const Bytef *source, uLong sourceLen)); +/* +     Compresses the source buffer into the destination buffer.  sourceLen is +   the byte length of the source buffer. Upon entry, destLen is the total +   size of the destination buffer, which must be at least the value returned +   by compressBound(sourceLen). Upon exit, destLen is the actual size of the +   compressed buffer. +     This function can be used to compress a whole file at once if the +   input file is mmap'ed. +     compress returns Z_OK if success, Z_MEM_ERROR if there was not +   enough memory, Z_BUF_ERROR if there was not enough room in the output +   buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen, +                                  const Bytef *source, uLong sourceLen, +                                  int level)); +/* +     Compresses the source buffer into the destination buffer. The level +   parameter has the same meaning as in deflateInit.  sourceLen is the byte +   length of the source buffer. Upon entry, destLen is the total size of the +   destination buffer, which must be at least the value returned by +   compressBound(sourceLen). Upon exit, destLen is the actual size of the +   compressed buffer. + +     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough +   memory, Z_BUF_ERROR if there was not enough room in the output buffer, +   Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* +     compressBound() returns an upper bound on the compressed size after +   compress() or compress2() on sourceLen bytes.  It would be used before +   a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, +                                   const Bytef *source, uLong sourceLen)); +/* +     Decompresses the source buffer into the destination buffer.  sourceLen is +   the byte length of the source buffer. Upon entry, destLen is the total +   size of the destination buffer, which must be large enough to hold the +   entire uncompressed data. (The size of the uncompressed data must have +   been saved previously by the compressor and transmitted to the decompressor +   by some mechanism outside the scope of this compression library.) +   Upon exit, destLen is the actual size of the compressed buffer. +     This function can be used to decompress a whole file at once if the +   input file is mmap'ed. + +     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not +   enough memory, Z_BUF_ERROR if there was not enough room in the output +   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode)); +/* +     Opens a gzip (.gz) file for reading or writing. The mode parameter +   is as in fopen ("rb" or "wb") but can also include a compression level +   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for +   Huffman only compression as in "wb1h", or 'R' for run-length encoding +   as in "wb1R". (See the description of deflateInit2 for more information +   about the strategy parameter.) + +     gzopen can be used to read a file which is not in gzip format; in this +   case gzread will directly read from the file without decompression. + +     gzopen returns NULL if the file could not be opened or if there was +   insufficient memory to allocate the (de)compression state; errno +   can be checked to distinguish the two cases (if errno is zero, the +   zlib error is Z_MEM_ERROR).  */ + +ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode)); +/* +     gzdopen() associates a gzFile with the file descriptor fd.  File +   descriptors are obtained from calls like open, dup, creat, pipe or +   fileno (in the file has been previously opened with fopen). +   The mode parameter is as in gzopen. +     The next call of gzclose on the returned gzFile will also close the +   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file +   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). +     gzdopen returns NULL if there was insufficient memory to allocate +   the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* +     Dynamically update the compression level or strategy. See the description +   of deflateInit2 for the meaning of these parameters. +     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not +   opened for writing. +*/ + +ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len)); +/* +     Reads the given number of uncompressed bytes from the compressed file. +   If the input file was not in gzip format, gzread copies the given number +   of bytes into the buffer. +     gzread returns the number of uncompressed bytes actually read (0 for +   end of file, -1 for error). */ + +ZEXTERN int ZEXPORT    gzwrite OF((gzFile file, +                                   voidpc buf, unsigned len)); +/* +     Writes the given number of uncompressed bytes into the compressed file. +   gzwrite returns the number of uncompressed bytes actually written +   (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...)); +/* +     Converts, formats, and writes the args to the compressed file under +   control of the format string, as in fprintf. gzprintf returns the number of +   uncompressed bytes actually written (0 in case of error).  The number of +   uncompressed bytes written is limited to 4095. The caller should assure that +   this limit is not exceeded. If it is exceeded, then gzprintf() will return +   return an error (0) with nothing written. In this case, there may also be a +   buffer overflow with unpredictable consequences, which is possible only if +   zlib was compiled with the insecure functions sprintf() or vsprintf() +   because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* +      Writes the given null-terminated string to the compressed file, excluding +   the terminating null character. +      gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* +      Reads bytes from the compressed file until len-1 characters are read, or +   a newline character is read and transferred to buf, or an end-of-file +   condition is encountered.  The string is then terminated with a null +   character. +      gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c)); +/* +      Writes c, converted to an unsigned char, into the compressed file. +   gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT    gzgetc OF((gzFile file)); +/* +      Reads one byte from the compressed file. gzgetc returns this byte +   or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT    gzungetc OF((int c, gzFile file)); +/* +      Push one character back onto the stream to be read again later. +   Only one character of push-back is allowed.  gzungetc() returns the +   character pushed, or -1 on failure.  gzungetc() will fail if a +   character has been pushed but not read yet, or if c is -1. The pushed +   character will be discarded if the stream is repositioned with gzseek() +   or gzrewind(). +*/ + +ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush)); +/* +     Flushes all pending output into the compressed file. The parameter +   flush is as in the deflate() function. The return value is the zlib +   error number (see function gzerror below). gzflush returns Z_OK if +   the flush parameter is Z_FINISH and all output could be flushed. +     gzflush should be called only when strictly necessary because it can +   degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file, +                                      z_off_t offset, int whence)); +/* +      Sets the starting position for the next gzread or gzwrite on the +   given compressed file. The offset represents a number of bytes in the +   uncompressed data stream. The whence parameter is defined as in lseek(2); +   the value SEEK_END is not supported. +     If the file is opened for reading, this function is emulated but can be +   extremely slow. If the file is opened for writing, only forward seeks are +   supported; gzseek then compresses a sequence of zeroes up to the new +   starting position. + +      gzseek returns the resulting offset location as measured in bytes from +   the beginning of the uncompressed stream, or -1 in case of error, in +   particular if the file is opened for writing and the new starting position +   would be before the current position. +*/ + +ZEXTERN int ZEXPORT    gzrewind OF((gzFile file)); +/* +     Rewinds the given file. This function is supported only for reading. + +   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file)); +/* +     Returns the starting position for the next gzread or gzwrite on the +   given compressed file. This position represents a number of bytes in the +   uncompressed data stream. + +   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* +     Returns 1 when EOF has previously been detected reading the given +   input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* +     Returns 1 if file is being read directly without decompression, otherwise +   zero. +*/ + +ZEXTERN int ZEXPORT    gzclose OF((gzFile file)); +/* +     Flushes all pending output if necessary, closes the compressed file +   and deallocates all the (de)compression state. The return value is the zlib +   error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* +     Returns the error message for the last error which occurred on the +   given compressed file. errnum is set to zlib error number. If an +   error occurred in the file system and not in the compression library, +   errnum is set to Z_ERRNO and the application may consult errno +   to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* +     Clears the error and end-of-file flags for file. This is analogous to the +   clearerr() function in stdio. This is useful for continuing to read a gzip +   file that is being written concurrently. +*/ + +                        /* checksum functions */ + +/* +     These functions are not related to compression but are exported +   anyway because they might be useful in applications using the +   compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* +     Update a running Adler-32 checksum with the bytes buf[0..len-1] and +   return the updated checksum. If buf is NULL, this function returns +   the required initial value for the checksum. +   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +   much faster. Usage example: + +     uLong adler = adler32(0L, Z_NULL, 0); + +     while (read_buffer(buffer, length) != EOF) { +       adler = adler32(adler, buffer, length); +     } +     if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, +                                          z_off_t len2)); +/* +     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1 +   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for +   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of +   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len)); +/* +     Update a running CRC-32 with the bytes buf[0..len-1] and return the +   updated CRC-32. If buf is NULL, this function returns the required initial +   value for the for the crc. Pre- and post-conditioning (one's complement) is +   performed within this function so it shouldn't be done by the application. +   Usage example: + +     uLong crc = crc32(0L, Z_NULL, 0); + +     while (read_buffer(buffer, length) != EOF) { +       crc = crc32(crc, buffer, length); +     } +     if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* +     Combine two CRC-32 check values into one.  For two sequences of bytes, +   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were +   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32 +   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and +   len2. +*/ + + +                        /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, +                                     const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, +                                     const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method, +                                      int windowBits, int memLevel, +                                      int strategy, const char *version, +                                      int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits, +                                      const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, +                                         unsigned char FAR *window, +                                         const char *version, +                                         int stream_size)); +#define deflateInit(strm, level) \ +        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ +        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ +        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ +                      (strategy),           ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ +        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ +        inflateBackInit_((strm), (windowBits), (window), \ +        ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) +    struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char   * ZEXPORT zError           OF((int)); +ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/zlib/zutil.c b/src/zlib/zutil.c new file mode 100644 index 0000000..d55f594 --- /dev/null +++ b/src/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state      {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary",     /* Z_NEED_DICT       2  */ +"stream end",          /* Z_STREAM_END      1  */ +"",                    /* Z_OK              0  */ +"file error",          /* Z_ERRNO         (-1) */ +"stream error",        /* Z_STREAM_ERROR  (-2) */ +"data error",          /* Z_DATA_ERROR    (-3) */ +"insufficient memory", /* Z_MEM_ERROR     (-4) */ +"buffer error",        /* Z_BUF_ERROR     (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ +    return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ +    uLong flags; + +    flags = 0; +    switch (sizeof(uInt)) { +    case 2:     break; +    case 4:     flags += 1;     break; +    case 8:     flags += 2;     break; +    default:    flags += 3; +    } +    switch (sizeof(uLong)) { +    case 2:     break; +    case 4:     flags += 1 << 2;        break; +    case 8:     flags += 2 << 2;        break; +    default:    flags += 3 << 2; +    } +    switch (sizeof(voidpf)) { +    case 2:     break; +    case 4:     flags += 1 << 4;        break; +    case 8:     flags += 2 << 4;        break; +    default:    flags += 3 << 4; +    } +    switch (sizeof(z_off_t)) { +    case 2:     break; +    case 4:     flags += 1 << 6;        break; +    case 8:     flags += 2 << 6;        break; +    default:    flags += 3 << 6; +    } +#ifdef DEBUG +    flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) +    flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI +    flags += 1 << 10; +#endif +#ifdef BUILDFIXED +    flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE +    flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS +    flags += 1L << 16; +#endif +#ifdef NO_GZIP +    flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND +    flags += 1L << 20; +#endif +#ifdef FASTEST +    flags += 1L << 21; +#endif +#ifdef STDC +#  ifdef NO_vsnprintf +        flags += 1L << 25; +#    ifdef HAS_vsprintf_void +        flags += 1L << 26; +#    endif +#  else +#    ifdef HAS_vsnprintf_void +        flags += 1L << 26; +#    endif +#  endif +#else +        flags += 1L << 24; +#  ifdef NO_snprintf +        flags += 1L << 25; +#    ifdef HAS_sprintf_void +        flags += 1L << 26; +#    endif +#  else +#    ifdef HAS_snprintf_void +        flags += 1L << 26; +#    endif +#  endif +#endif +    return flags; +} + +#ifdef DEBUG + +#  ifndef verbose +#    define verbose 0 +#  endif +int z_verbose = verbose; + +void z_error (m) +    char *m; +{ +    fprintf(stderr, "%s\n", m); +    exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) +    int err; +{ +    return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) +    /* The Microsoft C Run-Time Library for Windows CE doesn't have +     * errno.  We define it as a global variable to simplify porting. +     * Its value is always 0 and should not be used. +     */ +    int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) +    Bytef* dest; +    const Bytef* source; +    uInt  len; +{ +    if (len == 0) return; +    do { +        *dest++ = *source++; /* ??? to be unrolled */ +    } while (--len != 0); +} + +int zmemcmp(s1, s2, len) +    const Bytef* s1; +    const Bytef* s2; +    uInt  len; +{ +    uInt j; + +    for (j = 0; j < len; j++) { +        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; +    } +    return 0; +} + +void zmemzero(dest, len) +    Bytef* dest; +    uInt  len; +{ +    if (len == 0) return; +    do { +        *dest++ = 0;  /* ??? to be unrolled */ +    } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +#  define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { +    voidpf org_ptr; +    voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ +    voidpf buf = opaque; /* just to make some compilers happy */ +    ulg bsize = (ulg)items*size; + +    /* If we allocate less than 65520 bytes, we assume that farmalloc +     * will return a usable pointer which doesn't have to be normalized. +     */ +    if (bsize < 65520L) { +        buf = farmalloc(bsize); +        if (*(ush*)&buf != 0) return buf; +    } else { +        buf = farmalloc(bsize + 16L); +    } +    if (buf == NULL || next_ptr >= MAX_PTR) return NULL; +    table[next_ptr].org_ptr = buf; + +    /* Normalize the pointer to seg:0 */ +    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; +    *(ush*)&buf = 0; +    table[next_ptr++].new_ptr = buf; +    return buf; +} + +void  zcfree (voidpf opaque, voidpf ptr) +{ +    int n; +    if (*(ush*)&ptr != 0) { /* object < 64K */ +        farfree(ptr); +        return; +    } +    /* Find the original pointer */ +    for (n = 0; n < next_ptr; n++) { +        if (ptr != table[n].new_ptr) continue; + +        farfree(table[n].org_ptr); +        while (++n < next_ptr) { +            table[n-1] = table[n]; +        } +        next_ptr--; +        return; +    } +    ptr = opaque; /* just to make some compilers happy */ +    Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +#  define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +#  define _halloc  halloc +#  define _hfree   hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ +    if (opaque) opaque = 0; /* to make compiler happy */ +    return _halloc((long)items, size); +} + +void  zcfree (voidpf opaque, voidpf ptr) +{ +    if (opaque) opaque = 0; /* to make compiler happy */ +    _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp  malloc OF((uInt size)); +extern voidp  calloc OF((uInt items, uInt size)); +extern void   free   OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) +    voidpf opaque; +    unsigned items; +    unsigned size; +{ +    if (opaque) items += size - size; /* make compiler happy */ +    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : +                              (voidpf)calloc(items, size); +} + +void  zcfree (opaque, ptr) +    voidpf opaque; +    voidpf ptr; +{ +    free(ptr); +    if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/src/zlib/zutil.h b/src/zlib/zutil.h new file mode 100644 index 0000000..b7d5eff --- /dev/null +++ b/src/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is +   part of the implementation of the compression library and is +   subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +#  ifndef _WIN32_WCE +#    include <stddef.h> +#  endif +#  include <string.h> +#  include <stdlib.h> +#endif +#ifdef NO_ERRNO_H +#   ifdef _WIN32_WCE +      /* The Microsoft C Run-Time Library for Windows CE doesn't have +       * errno.  We define it as a global variable to simplify porting. +       * Its value is always 0 and should not be used.  We rename it to +       * avoid conflict with other libraries that use the same workaround. +       */ +#     define errno z_errno +#   endif +    extern int errno; +#else +#  ifndef _WIN32_WCE +#    include <errno.h> +#  endif +#endif + +#ifndef local +#  define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char  uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long  ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ +  return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + +        /* common constants */ + +#ifndef DEF_WBITS +#  define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +#  define DEF_MEM_LEVEL 8 +#else +#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES    2 +/* The three kinds of block type */ + +#define MIN_MATCH  3 +#define MAX_MATCH  258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + +        /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +#  define OS_CODE  0x00 +#  if defined(__TURBOC__) || defined(__BORLANDC__) +#    if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) +       /* Allow compilation with ANSI keywords only enabled */ +       void _Cdecl farfree( void *block ); +       void *_Cdecl farmalloc( unsigned long nbytes ); +#    else +#      include <alloc.h> +#    endif +#  else /* MSC or DJGPP */ +#    include <malloc.h> +#  endif +#endif + +#ifdef AMIGA +#  define OS_CODE  0x01 +#endif + +#if defined(VAXC) || defined(VMS) +#  define OS_CODE  0x02 +#  define F_OPEN(name, mode) \ +     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +#  define OS_CODE  0x05 +#endif + +#ifdef OS2 +#  define OS_CODE  0x06 +#  ifdef M_I86 +     #include <malloc.h> +#  endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +#  define OS_CODE  0x07 +#  if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +#    include <unix.h> /* for fdopen */ +#  else +#    ifndef fdopen +#      define fdopen(fd,mode) NULL /* No fdopen() */ +#    endif +#  endif +#endif + +#ifdef TOPS20 +#  define OS_CODE  0x0a +#endif + +#ifdef WIN32 +#  ifndef __CYGWIN__  /* Cygwin is Unix, not Win32 */ +#    define OS_CODE  0x0b +#  endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +#  define OS_CODE  0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +#  define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +#  if defined(_WIN32_WCE) +#    define fdopen(fd,mode) NULL /* No fdopen() */ +#    ifndef _PTRDIFF_T_DEFINED +       typedef int ptrdiff_t; +#      define _PTRDIFF_T_DEFINED +#    endif +#  else +#    define fdopen(fd,type)  _fdopen(fd,type) +#  endif +#endif + +        /* common defaults */ + +#ifndef OS_CODE +#  define OS_CODE  0x03  /* assume Unix */ +#endif + +#ifndef F_OPEN +#  define F_OPEN(name, mode) fopen((name), (mode)) +#endif + +         /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +#  ifndef HAVE_VSNPRINTF +#    define HAVE_VSNPRINTF +#  endif +#endif +#if defined(__CYGWIN__) +#  ifndef HAVE_VSNPRINTF +#    define HAVE_VSNPRINTF +#  endif +#endif +#ifndef HAVE_VSNPRINTF +#  ifdef MSDOS +     /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), +        but for now we just assume it doesn't. */ +#    define NO_vsnprintf +#  endif +#  ifdef __TURBOC__ +#    define NO_vsnprintf +#  endif +#  ifdef WIN32 +     /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +#    if !defined(vsnprintf) && !defined(NO_vsnprintf) +#      define vsnprintf _vsnprintf +#    endif +#  endif +#  ifdef __SASC +#    define NO_vsnprintf +#  endif +#endif +#ifdef VMS +#  define NO_vsnprintf +#endif + +#if defined(pyr) +#  define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. +  * You may have to use the same strategy for Borland C (untested). +  * The __SC__ check is for Symantec. +  */ +#  define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +#  define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +#    define zmemcpy _fmemcpy +#    define zmemcmp _fmemcmp +#    define zmemzero(dest, len) _fmemset(dest, 0, len) +#  else +#    define zmemcpy memcpy +#    define zmemcmp memcmp +#    define zmemzero(dest, len) memset(dest, 0, len) +#  endif +#else +   extern void zmemcpy  OF((Bytef* dest, const Bytef* source, uInt len)); +   extern int  zmemcmp  OF((const Bytef* s1, const Bytef* s2, uInt len)); +   extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +#  include <stdio.h> +   extern int z_verbose; +   extern void z_error    OF((char *m)); +#  define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#  define Trace(x) {if (z_verbose>=0) fprintf x ;} +#  define Tracev(x) {if (z_verbose>0) fprintf x ;} +#  define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +#  define Assert(cond,msg) +#  define Trace(x) +#  define Tracev(x) +#  define Tracevv(x) +#  define Tracec(c,x) +#  define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void   zcfree  OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ +           (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */  | 
