文件/文件夾LZSS壓縮/解壓縮

 相關壓縮算法是在網上找到的,採用Huffman編碼。目前的壓縮比率在10%-50%之間,差於WinZip及WinRar算法。

功能:
1. 將多個文件/文件夾壓縮並打包,生成一個單獨的文件。
2. 解壓縮一個壓縮文件。
3. 解壓縮內存中的壓縮內容。

公開接口和參數都是自解釋的,就沒有多寫註釋了。如果有什麼不清楚的地方,可以發mail給我,如果你做了什麼改進,也請發mail給我。cyifyr(at)gmail(dot)com

聲明:

#ifndef _CZIP_H_
#define _CZIP_H_

#include <vector>

#define N                 4096        /* size of ring buffer */
#define F                   60        /* upper limit for match_length */
#define THRESHOLD            2        /* encode string into position and length
                                         if match_length is greater than this */
#define NIL                  N        /* index for root of binary search trees */
/********** Arithmetic Compression **********/

/*
    If you are not familiar with arithmetic compression, you should read
        I. E. Witten, R. M. Neal, and J. G. Cleary,
        Communications of the ACM, Vol. 30, pp. 520-540 (1987),
        from which much have been borrowed.
 */

#define M   15

/* Q1 (= 2 to the M) must be sufficiently large, but not so
   large as the unsigned long 4 * Q1 * (Q1 - 1) overflows.  */

#define Q1  (1UL << M)
#define Q2  (2 * Q1)
#define Q3  (3 * Q1)
#define Q4  (4 * Q1)
#define MAX_CUM (Q1 - 1)

#define N_CHAR  (256 - THRESHOLD + F)

typedef struct PackItem {
    CHAR   cFileType;
    CHAR   szFilePath[MAX_PATH];
    DWORD  dwUnzipSize;
    DWORD  dwZippedSize;
    LPBYTE pData;
} * LPPACKITEM;

class CZip {
public:
    CZip();
    ~CZip();
public:
    void Pack(const char* lpszInPath[], const char* lpszOutFile);
    void Unpack(const char *lpszInfile, const char* lpszOutPath);
    void Unpack(const BYTE* lpData, const DWORD nDataLength, const char* lpszOutPath);
private:
    CHAR m_szWorkDir[MAX_PATH];

    DWORD textsize;
    DWORD codesize;
    DWORD printcount;
    BYTE  text_buf[N + F - 1]; /* ring buffer of size N,with extra F-1 bytes to facilitate string comparison */
    int        match_position;
    int        match_length;   /* of longest match.  These areset by the InsertNode() procedure. */
    int        lson[N + 1];
    int rson[N + 257];
    int dad[N + 1];            /* left & right children &parents -- These constitute binary search trees. */
    
    /* character code = 0, 1, ..., N_CHAR - 1 */
    
    DWORD low;
    DWORD high;
    DWORD value;
    int  shifts;  /* counts for magnifying low and high around Q2 */
    int  char_to_sym[N_CHAR];
    int sym_to_char[N_CHAR + 1];
    DWORD sym_freq[N_CHAR + 1];  /* frequency for symbols */
    DWORD sym_cum[N_CHAR + 1];   /* cumulative freq for symbols */
    DWORD position_cum[N + 1];   /* cumulative freq for positions */
    
    // Compress in memory;
    bool m_bMem;
    
    std::vector<BYTE> m_OutBuffer;
    int m_nOutLength;

    const BYTE *m_pInBuffer;
    int m_nInLength;
    int m_nInCur;
    
    DWORD  buffer_putbit, mask_putbit;
    DWORD  buffer_getbit, mask_getbit;

    std::vector<LPPACKITEM> m_PackList;
private:
    void Initialize();
    void Release();

    void PutBit(int bit);  /* Output one bit (bit = 0,1) */
    void FlushBitBuffer(void);  /* Send remaining bits */
    int GetBit(void);  /* Get one bit (0 or 1) */

    /********** LZSS with multiple binary trees **********/

    void InitTree(void);  /* Initialize trees */
    void InsertNode(int r);
    void DeleteNode(int p);  /* Delete node p from tree */
    void StartModel(void); /* Initialize model */
    void UpdateModel(int sym);
    void Output(int bit);  /* Output 1 bit, followed by its complements */
    void EncodeChar(int ch);
    void EncodePosition(int position);
    void EncodeEnd(void);
    int BinarySearchSym(unsigned int x);
    int BinarySearchPos(unsigned int x);
    void StartDecode(void);
    int DecodeChar(void);
    int DecodePosition(void);

    void Encode(void);
    void Decode(void);

    void Compress(const BYTE *pInBuffer, DWORD nInLength, const BYTE *&pOutBuffer, DWORD &nOutLength);
    void UnCompress(const BYTE *pInBuffer, DWORD nInLength, const BYTE *&pOutBuffer, DWORD &nOutLength);

    void AddEntry(const char* lpszInPath);
    void FetchEntry(const char* lpszOutPath);
};

#endif // _CZIP_H_

實現:

// Zip.cpp : Defines the entry point for the application.
//

#include <windows.h>
#include <mstask.h>
#include "zip.h"

CZip::CZip() {
    Initialize();
}

CZip::~CZip() {
    Release();
}

void CZip::Initialize() {
    textsize = 0;
    codesize = 0;
    printcount = 0;
    
    low = 0;
    high = Q4;
    value = 0;
    shifts = 0;/* counts for magnifying low and high around Q2 */
    
    m_pInBuffer = NULL;
    m_nInLength = 0;
    m_nInCur = 0;
    
    m_nOutLength = 0;
    
    buffer_putbit = 0;
    mask_putbit = 128;
    
    buffer_getbit = 0;
    mask_getbit = 0;
    
    memset(text_buf, 0, N + F - 1);
    memset(lson, 0, N + 1);
    memset(rson, 0, N + 257);
    memset(dad, 0, N + 1);
    memset(char_to_sym, 0, N_CHAR * sizeof(int));
    memset(sym_to_char, 0 , (N_CHAR + 1) * sizeof(int));
    memset(sym_freq, 0, (N_CHAR + 1) * sizeof(DWORD));
    memset(sym_cum, 0, (N_CHAR + 1) * sizeof(DWORD));
    memset(position_cum, 0, (N_CHAR + 1) * sizeof(DWORD));

    GetModuleFileName(NULL,m_szWorkDir,MAX_PATH); 
    (strrchr(m_szWorkDir,'\\'))[1] = 0;
    //GetCurrentDirectory(MAX_PATH, m_szWorkDir);
}

void CZip::Release() {
    if(!m_OutBuffer.empty()) {
        textsize = 0;
        codesize = 0;
        printcount = 0;
        
        low = 0;
        high = Q4;
        value = 0;
        shifts = 0;
        
        m_pInBuffer = NULL;
        m_nInLength = 0;
        m_nInCur = 0;
        
        m_OutBuffer.clear();
        m_nOutLength = 0;
        
        buffer_putbit = 0;
        mask_putbit = 128;
        
        buffer_getbit = 0;
        mask_getbit = 0;
    }
}

/*
 * Output one bit (bit = 0,1)
 */
void CZip::PutBit(int bit) {
    if(bit) {
        buffer_putbit |= mask_putbit;
    }
    if((mask_putbit >>= 1) == 0) {
           m_OutBuffer.push_back(buffer_putbit);
        buffer_putbit = 0;  
        mask_putbit = 128;  
        codesize++;
    }
}

/*
 * Send remaining bits
 */
void CZip::FlushBitBuffer(void) {
    for(int i = 0; i < 7; i++) {
        PutBit(0);
    }
}

/*
 * Get one bit (0 or 1)
 */
int CZip::GetBit(void) {
    if((mask_getbit >>= 1) == 0) {
        buffer_getbit = m_pInBuffer[m_nInCur++];
        mask_getbit = 128;
    }
    return ((buffer_getbit & mask_getbit) != 0);
}

/********        LZSS with multiple binary trees        ********/

/*
 * Initialize trees
 */
void CZip::InitTree(void) {
    int  i;

    /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
       left children of node i.  These nodes need not be initialized.
       Also, dad[i] is the parent of node i.  These are initialized to
       NIL (= N), which stands for 'not used.'
       For i = 0 to 255, rson[N + i + 1] is the root of the tree
       for strings that begin with character i.  These are initialized
       to NIL.  Note there are 256 trees. */

    for(i = N + 1; i <= N + 256; i++) { /* root */
        rson[i] = NIL;
    }
    for(i = 0; i < N; i++) { /* node */
        dad[i] = NIL;
    }
}

void CZip::InsertNode(int r) {
    /* Inserts string of length F, text_buf[r..r+F-1], into one of the
       trees (text_buf[r]'th tree) and returns the longest-match position
       and length via the global variables match_position and match_length.
       If match_length = F, then removes the old node in favor of the new
       one, because the old one will be deleted sooner.
       Note r plays double role, as tree node and position in buffer. */
    int  i, p, cmp, temp;
    unsigned char  *key;

    cmp = 1;  key = &text_buf[r];  p = N + 1 + key[0];
    rson[r] = lson[r] = NIL;  match_length = 0;
    while(TRUE) {
        if(cmp >= 0) {
            if(rson[p] != NIL) {
                p = rson[p];
            } else {
                rson[p] = r;
                dad[r] = p;
                return;
            }
        } else {
            if(lson[p] != NIL) {
                p = lson[p];
            } else {
                lson[p] = r;
                dad[r] = p;
                return;
            }
        }
        for(i = 1; i < F; i++) {
            if((cmp = key[i] - text_buf[p + i]) != 0) {
                break;
            }
        }
        if(i > THRESHOLD) {
            if(i > match_length) {
                match_position = (r - p) & (N - 1);
                if((match_length = i) >= F) {
                    break;
                }
            } else if(i == match_length) {
                if((temp = (r - p) & (N - 1)) < match_position) {
                    match_position = temp;
                }
            }
        }
    }
    dad[r] = dad[p];
    lson[r] = lson[p];
    rson[r] = rson[p];

    dad[lson[p]] = r;
    dad[rson[p]] = r;
    if(rson[dad[p]] == p) {
       rson[dad[p]] = r;
    } else {
       lson[dad[p]] = r;
    }
    dad[p] = NIL;  /* remove p */
}

/*
 * Delete node p from tree
 */
void CZip::DeleteNode(int p) {
    int  q;

    if(dad[p] == NIL) { /* not in tree */
        return; 
    }
    if(rson[p] == NIL) {
        q = lson[p];
    } else if(lson[p] == NIL) {
        q = rson[p];
    } else {
        q = lson[p];
        if(rson[q] != NIL) {
            do {
                q = rson[q];
            } while(rson[q] != NIL);
            rson[dad[q]] = lson[q];
            dad[lson[q]] = dad[q];
            lson[q] = lson[p];
            dad[lson[p]] = q;
        }
        rson[q] = rson[p];
        dad[rson[p]] = q;
    }
    dad[q] = dad[p];
    if(rson[dad[p]] == p) {
        rson[dad[p]] = q;
    } else {
        lson[dad[p]] = q;
    }
    dad[p] = NIL;
}

/********           Arithmetic Compression           ********/

/*
    If you are not familiar with arithmetic compression, you should read
        I. E. Witten, R. M. Neal, and J. G. Cleary,
        Communications of the ACM, Vol. 30, pp. 520-540 (1987),
        from which much have been borrowed.
 */

/* character code = 0, 1, ..., N_CHAR - 1 */

/*
 * Initialize model
 */
void CZip::StartModel(void) {
    int ch, sym, i;

    sym_cum[N_CHAR] = 0;
    for(sym = N_CHAR; sym >= 1; sym--) {
        ch = sym - 1;
        char_to_sym[ch] = sym;
        sym_to_char[sym] = ch;
        sym_freq[sym] = 1;
        sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym];
    }
    sym_freq[0] = 0;  /* sentinel (!= sym_freq[1]) */
    position_cum[N] = 0;
    for(i = N; i >= 1; i--) {
        position_cum[i - 1] = position_cum[i] + 10000 / (i + 200);
    }
    /* empirical distribution function (quite tentative) */
    /* Please devise a better mechanism! */
}

void CZip::UpdateModel(int sym) {
    int i, c, ch_i, ch_sym;
    
    if(sym_cum[0] >= MAX_CUM) {
        c = 0;
        for(i = N_CHAR; i > 0; i--) {
            sym_cum[i] = c;
            c += (sym_freq[i] = (sym_freq[i] + 1) >> 1);
        }
        sym_cum[0] = c;
    }
    for(i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ;
    if(i < sym) {
        ch_i = sym_to_char[i];    ch_sym = sym_to_char[sym];
        sym_to_char[i] = ch_sym;  sym_to_char[sym] = ch_i;
        char_to_sym[ch_i] = sym;  char_to_sym[ch_sym] = i;
    }
    sym_freq[i]++;
    while(--i >= 0) {
        sym_cum[i]++;
    }
}

/*
 * Output 1 bit, followed by its complements
 */
void CZip::Output(int bit) {
    PutBit(bit);
    for( ; shifts > 0; shifts--) {
        PutBit(! bit);
    }
}

void CZip::EncodeChar(int ch) {
    int  sym;
    unsigned long int  range;

    sym = char_to_sym[ch];
    range = high - low;
    high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
    low +=       (range * sym_cum[sym    ]) / sym_cum[0];
    while(TRUE) {
        if(high <= Q2) {
            Output(0);
        } else if(low >= Q2) {
            Output(1);
            low -= Q2;
            high -= Q2;
        } else if(low >= Q1 && high <= Q3) {
            shifts++;
            low -= Q1;
            high -= Q1;
        } else {
            break;
        }
        low += low;
        high += high;
    }
    UpdateModel(sym);
}

void CZip::EncodePosition(int position) {
    unsigned long int  range;

    range = high - low;
    high = low + (range * position_cum[position    ]) / position_cum[0];
    low +=       (range * position_cum[position + 1]) / position_cum[0];
    while(TRUE) {
        if(high <= Q2) {
            Output(0);
        } else if(low >= Q2) {
            Output(1);  low -= Q2;  high -= Q2;
        } else if(low >= Q1 && high <= Q3) {
            shifts++;  low -= Q1;  high -= Q1;
        } else {
            break;
        }
        low += low;
        high += high;
    }
}

void CZip::EncodeEnd(void) {
    shifts++;
    if(low < Q1) {
        Output(0);
    } else {
        Output(1);
    }
    FlushBitBuffer();  /* flush bits remaining in buffer */
}

int CZip::BinarySearchSym(unsigned int x) {
    /* 1      if x >= sym_cum[1],
       N_CHAR if sym_cum[N_CHAR] > x,
       i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */
    int i, j, k;
    
    i = 1;  j = N_CHAR;
    while(i < j) {
        k = (i + j) / 2;
        if(sym_cum[k] > x) {
            i = k + 1;
        } else {
            j = k;
        }
    }
    return i;
}

int CZip::BinarySearchPos(unsigned int x) {
    /* 0 if x >= position_cum[1],
       N - 1 if position_cum[N] > x,
       i such that position_cum[i] > x >= position_cum[i + 1] otherwise */
    int i, j, k;
    
    i = 1;  j = N;
    while(i < j) {
        k = (i + j) / 2;
        if(position_cum[k] > x) {
            i = k + 1;
        } else {
            j = k;
        }
    }
    return i - 1;
}

void CZip::StartDecode(void) {
    int i;

    for(i = 0; i < M + 2; i++) {
        value = 2 * value + GetBit();
    }
}

int CZip::DecodeChar(void) {
    int     sym, ch;
    unsigned long int  range;
    
    range = high - low;
    sym = BinarySearchSym((unsigned int)(((value - low + 1) * sym_cum[0] - 1) / range));
    high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
    low +=       (range * sym_cum[sym    ]) / sym_cum[0];
    while(TRUE) {
        if(low >= Q2) {
            value -= Q2;
            low -= Q2;
            high -= Q2;
        } else if(low >= Q1 && high <= Q3) {
            value -= Q1;
            low -= Q1;
            high -= Q1;
        } else if(high > Q2) {
            break;
        }
        low += low;
        high += high;
        value = 2 * value + GetBit();
    }
    ch = sym_to_char[sym];
    UpdateModel(sym);
    return ch;
}

int CZip::DecodePosition(void) {
    int position;
    unsigned long int  range;
    
    range = high - low;
    position = BinarySearchPos((unsigned int)(((value - low + 1) * position_cum[0] - 1) / range));
    high = low + (range * position_cum[position    ]) / position_cum[0];
    low +=       (range * position_cum[position + 1]) / position_cum[0];
    while(TRUE) {
        if(low >= Q2) {
            value -= Q2;
            low -= Q2;
            high -= Q2;
        } else if(low >= Q1 && high <= Q3) {
            value -= Q1;
            low -= Q1;
            high -= Q1;
        } else if(high > Q2) {
            break;
        }
        low += low;
        high += high;
        value = 2 * value + GetBit();
    }
    return position;
}

/********           Encode and Decode           ********/

void CZip::Encode(void) {
    int  i, c, len, r, s, last_match_length;
    
    textsize = m_nInLength;
    m_OutBuffer.resize(sizeof textsize);
    memcpy(&m_OutBuffer[0],&textsize,sizeof textsize);
    codesize += sizeof textsize;
    if(textsize == 0) return;
    m_nInCur = 0;
    textsize = 0;
    
    StartModel(); 
    InitTree();
    s = 0;  r = N - F;
    for(i = s; i < r; i++) {
        text_buf[i] = ' ';
    }
    for(len = 0; len < F && m_nInCur < m_nInLength ; len++) {
        c = m_pInBuffer[m_nInCur++];
        text_buf[r + len] = c;
    }
    
    textsize = len;
    for(i = 1; i <= F; i++) {
        InsertNode(r - i);
    }
    
    InsertNode(r);
    
    do {
        if(match_length > len) {
            match_length = len;
        }
        if(match_length <= THRESHOLD) {
            match_length = 1;  EncodeChar(text_buf[r]);
        } else {
            EncodeChar(255 - THRESHOLD + match_length);
            EncodePosition(match_position - 1);
        }
        last_match_length = match_length;
        for(i = 0; i < last_match_length && m_nInCur < m_nInLength ; i++) 
        {
            c = m_pInBuffer[m_nInCur++];
            DeleteNode(s);  
            text_buf[s] = c;
            if(s < F - 1) {
                text_buf[s + N] = c;
            }
            s = (s + 1) & (N - 1);
            r = (r + 1) & (N - 1);
            InsertNode(r);
        }
        if((textsize += i) > printcount) {
            printcount += 1024;
        }
        while(i++ < last_match_length) {
            DeleteNode(s);
            s = (s + 1) & (N - 1);
            r = (r + 1) & (N - 1);
            if(--len) {
                InsertNode(r);
            }
        }
    } while(len > 0);
    
    EncodeEnd();
}

void CZip::Decode(void) {
    int  i, j, k, r, c;
    unsigned long int  count;

    if(m_nInLength < sizeof textsize) {
        return;
    }
    memcpy(&textsize,m_pInBuffer + m_nInCur,sizeof textsize);

    m_nOutLength = textsize;

    m_nInCur += sizeof textsize;

    if(textsize == 0) {
        return;
    }

    StartDecode();
    StartModel();

    for(i = 0; i < N - F; i++) {
        text_buf[i] = ' ';
    }
    r = N - F;
    for(count = 0; count < textsize; ) {
        c = DecodeChar();
        if(c < 256) {
            m_OutBuffer.push_back(c);
            text_buf[r++] = c;
            r &= (N - 1);
            count++;
        } else {
            i = (r - DecodePosition() - 1) & (N - 1);
            j = c - 255 + THRESHOLD;
            for(k = 0; k < j; k++) {
                c = text_buf[(i + k) & (N - 1)];
                m_OutBuffer.push_back(c);
                text_buf[r++] = c;
                r &= (N - 1);
                count++;
            }
        }
        if(count > printcount) {
            printcount += 1024;
        }
    }
}

void CZip::Compress(const BYTE *pInBuffer,DWORD nInLength,const BYTE *&pOutBuffer, DWORD &nOutLength) {
    Initialize();
    m_pInBuffer = pInBuffer;
    m_nInLength = nInLength;
    m_nInCur = 0;

    Encode();
    nOutLength = m_OutBuffer.size();
    pOutBuffer = new BYTE[nOutLength];
    memcpy((void *)pOutBuffer, &m_OutBuffer[0], nOutLength);
    Release();
}

void CZip::UnCompress(const BYTE *pInBuffer,DWORD nInLength,const BYTE *&pOutBuffer, DWORD &nOutLength) {
    Initialize();
    m_pInBuffer = pInBuffer;
    m_nInLength = nInLength;
    m_nInCur = 0;

    Decode();
    nOutLength = m_OutBuffer.size();
    pOutBuffer = new BYTE[nOutLength];
    memcpy((void *)pOutBuffer, &m_OutBuffer[0], nOutLength);
    Release();
}

void CZip::AddEntry(const char* lpszInPath) {
    OFSTRUCT os;
    DWORD dwFileAttributes = GetFileAttributes(lpszInPath);

    LPPACKITEM item = new PackItem;
    memset(item, 0, sizeof(PackItem));

    if(dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
        char szAbsPath[MAX_PATH], szFind[MAX_PATH], szRefPath[MAX_PATH];
        strcpy(szAbsPath, lpszInPath);
        if(lpszInPath[strlen(lpszInPath) - 1] != '\\') {
            strcat(szAbsPath, "\\");
        }

        strcpy(szFind, szAbsPath);
        strcat(szFind, "*");

        strcpy(szRefPath, szAbsPath);
        if(strncmp(szRefPath, m_szWorkDir, strlen(m_szWorkDir)) == 0) {
            strcpy(szRefPath, szRefPath + strlen(m_szWorkDir));
        }
        item->cFileType = 1;
        strcpy(item->szFilePath, szRefPath);
        m_PackList.push_back(item);

        HANDLE hFind = INVALID_HANDLE_VALUE;
        WIN32_FIND_DATA FindFileData;
        hFind = FindFirstFile(szFind, &FindFileData);
        if(hFind != INVALID_HANDLE_VALUE) {
            if(strcmp(FindFileData.cFileName, ".") != 0
                && strcmp(FindFileData.cFileName, "..") != 0) {
                    char szFile[MAX_PATH];
                    strcpy(szFile, szAbsPath);
                    strcat(szFile, FindFileData.cFileName);
                    AddEntry(szFile);
                }
            while(FindNextFile(hFind, &FindFileData) != 0) {
                if(strcmp(FindFileData.cFileName, ".") != 0
                    && strcmp(FindFileData.cFileName, "..") != 0) {
                    char szFile[MAX_PATH];
                    strcpy(szFile, szAbsPath);
                    strcat(szFile, FindFileData.cFileName);
                    AddEntry(szFile);
                }
            }
            FindClose(hFind);
        }
    } else {
        char szAbsPath[MAX_PATH], szRefPath[MAX_PATH];
        strcpy(szAbsPath, lpszInPath);
        strcpy(szRefPath, lpszInPath);

        if(strncmp(szRefPath, m_szWorkDir, strlen(m_szWorkDir)) == 0) {
            strcpy(szRefPath, szRefPath + strlen(m_szWorkDir));
        }
        strcpy(item->szFilePath, szRefPath);
        item->cFileType = 0;

        HANDLE hFile = (HANDLE)OpenFile(szAbsPath, &os, OF_READ | OF_SHARE_DENY_WRITE);
        if((HFILE)hFile != HFILE_ERROR) {
            BY_HANDLE_FILE_INFORMATION info;
            if(GetFileInformationByHandle(hFile, &info)) {
                // no more than 4G
                item->dwUnzipSize = info.nFileSizeLow;
                LPBYTE inBuffer = new BYTE[item->dwUnzipSize];
                DWORD dwReaded;
                LPBYTE outBuffer;
                DWORD dwZipped;
                if(ReadFile(hFile, inBuffer, item->dwUnzipSize, &dwReaded, NULL) && dwReaded == item->dwUnzipSize) {
                    Compress(inBuffer, dwReaded, outBuffer, dwZipped);
                    item->dwZippedSize = dwZipped;
                    item->pData = outBuffer;
                    m_PackList.push_back(item);
                } else {
                    delete item;
                }
                delete[] inBuffer;
            }
        }
        CloseHandle(hFile);
    }
}

void CZip::FetchEntry(const char* lpszOutPath) {
    int i = 0;
    CHAR szPath[MAX_PATH];
    if(lpszOutPath == NULL) {
        strcpy(szPath, m_szWorkDir);
    } else {
        OFSTRUCT os;
        HFILE hFile = OpenFile(lpszOutPath, &os, OF_EXIST);
        if(hFile != HFILE_ERROR) {
            DWORD dwFileAttributes = GetFileAttributes(lpszOutPath);
            if(dwFileAttributes != FILE_ATTRIBUTE_DIRECTORY) {
                return;
            }
        } else {
            CreateDirectory(lpszOutPath, NULL);
        }
        strcpy(szPath, lpszOutPath);
    }
    if(szPath[strlen(szPath) - 1] != '\\') {
        strcat(szPath, "\\");
    }
    for(i = 0; i < m_PackList.size(); i ++) {
        LPPACKITEM pItem = (LPPACKITEM)m_PackList[i];
        CHAR szFile[MAX_PATH];
        strcpy(szFile, szPath);
        strcat(szFile, pItem->szFilePath);
        if(pItem->cFileType == 1) {
            CreateDirectory(szFile, NULL);
        } else {
            LPBYTE pData = NULL;
            DWORD dwOutLength;
            UnCompress(pItem->pData, pItem->dwZippedSize, pData, dwOutLength);
            if(dwOutLength == pItem->dwUnzipSize) {
                OFSTRUCT os;
                HANDLE hFile = (HANDLE)OpenFile(szFile, &os, OF_CREATE | OF_WRITE);
                if((HFILE)hFile != HFILE_ERROR) {
                    DWORD dwWritten;
                    WriteFile(hFile, pData, dwOutLength, &dwWritten, NULL);
                }
                CloseHandle(hFile);
            }
            delete[] pData;
        }
    }
}

/********           Public Interfaces           ********/

/*
 * Pack Files/Directorys into a single file.
 * This can be modified into a much more smart interface.
 */
void CZip::Pack(const char* lpszInPath[], const char* lpszOutFile) {
    int iItem = 0;
    while(lpszInPath[iItem] != NULL) {
        AddEntry(lpszInPath[iItem]);
        iItem ++;
    }
    if(!m_PackList.empty()) {
        OFSTRUCT os;
        HANDLE hFile = (HANDLE)OpenFile(lpszOutFile, &os, OF_CREATE | OF_WRITE);
        if((HFILE)hFile == HFILE_ERROR) {
            return;
        }
        LPSTR pData = "IF"; // filename list separator
        DWORD dwWritten;
        for(iItem = 0; iItem < m_PackList.size(); iItem ++) {
            WriteFile(hFile, pData, 2, &dwWritten, NULL);
            LPPACKITEM pItem = (LPPACKITEM)m_PackList[iItem];
            WriteFile(hFile, &pItem->cFileType, sizeof(CHAR), &dwWritten, NULL);
            WriteFile(hFile, &pItem->szFilePath, strlen(pItem->szFilePath) + 1, &dwWritten, NULL);
            WriteFile(hFile, &pItem->dwUnzipSize, sizeof(DWORD), &dwWritten, NULL);
            WriteFile(hFile, &pItem->dwZippedSize, sizeof(DWORD), &dwWritten, NULL);
        }
        pData = "YR"; // data list separator
        for(iItem = 0; iItem < m_PackList.size(); iItem ++) {
            LPPACKITEM pItem = (LPPACKITEM)m_PackList[iItem];
            while(pItem->cFileType == 1 && iItem < m_PackList.size()) { // Directory
                iItem ++;
                pItem = (LPPACKITEM)m_PackList[iItem];
            }
            if(iItem >= m_PackList.size()) {
                break;
            }
            WriteFile(hFile, pData, 2, &dwWritten, NULL);
            LPBYTE pBody = pItem->pData;
            WriteFile(hFile, pBody, pItem->dwZippedSize, &dwWritten, NULL);
        }
        CloseHandle(hFile);

        for(iItem = 0; iItem < m_PackList.size(); iItem ++) {
            LPPACKITEM pItem = (LPPACKITEM)m_PackList[iItem];
            if(pItem->cFileType != 1) { // Directory
                LPBYTE pData = pItem->pData;
                delete[] pData;
            }
            delete pItem;
        }
    }
}

/*
 * Unpack a memory segment into Files/Directories.
 */
void CZip::Unpack(const BYTE* lpData, const DWORD nDataLength, const char* lpszOutPath) {
    DWORD dwFileSize = nDataLength; // less than 4G
    LPBYTE pData = (LPBYTE)lpData;
    DWORD dwPos = 0;

    while(dwPos < dwFileSize && strncmp((LPSTR)(pData + dwPos), "IF", 2) == 0) {
        dwPos += 2;
        LPPACKITEM pItem = new PackItem;
        pItem->cFileType = *(LPSTR)(pData + dwPos);
        dwPos += sizeof(CHAR);
        strcpy(pItem->szFilePath, (LPSTR)(pData + dwPos));
        dwPos += strlen(pItem->szFilePath) + 1;
        pItem->dwUnzipSize = *(LPDWORD)(pData + dwPos);
        dwPos += sizeof(DWORD);
        pItem->dwZippedSize = *(LPDWORD)(pData + dwPos);
        dwPos += sizeof(DWORD);
        m_PackList.push_back(pItem);
    }

    int iItem = 0;
    while(dwPos < dwFileSize && strncmp((LPSTR)(pData + dwPos), "YR", 2) == 0 && iItem < m_PackList.size()) {
        dwPos += 2;
        LPPACKITEM pItem = m_PackList[iItem];
        while(pItem->cFileType == 1 && iItem < m_PackList.size()) { // Directory
            iItem ++;
            pItem = m_PackList[iItem];
        }
        if(iItem >= m_PackList.size()) {
            break;
        }
        pItem->pData = (pData + dwPos);
        dwPos += pItem->dwZippedSize;
        iItem ++;
    }
    FetchEntry(lpszOutPath);
    for(iItem = 0; iItem < m_PackList.size(); iItem ++) {
        LPPACKITEM pItem = (LPPACKITEM)m_PackList[iItem];
        delete pItem;
    }
}

/*
 * Unpack a packed file into Files/Directories.
 */
void CZip::Unpack(const char *lpszInfile, const char* lpszOutPath) {
    OFSTRUCT os;
    HANDLE hFile = (HANDLE)OpenFile(lpszInfile, &os, OF_READ | OF_SHARE_DENY_WRITE);
    if((HFILE)hFile != HFILE_ERROR) {
        BY_HANDLE_FILE_INFORMATION info;
        if(GetFileInformationByHandle(hFile, &info)) {
            DWORD dwFileSize = info.nFileSizeLow; // less than 4G
            LPBYTE pData = new BYTE[dwFileSize];
            DWORD dwReaded;
            if(ReadFile(hFile, pData, dwFileSize, &dwReaded, NULL)) {
                Unpack(pData, dwReaded, lpszOutPath);
            }
            delete[] pData;
        }
        CloseHandle(hFile);
    }
}

用法:

#include <windows.h>
#include "zip.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow) {
    CZip zip;
    if(lpCmdLine != NULL && strlen(lpCmdLine) != 0) {
        LPSTR paramDecompress = strstr(lpCmdLine, "-d ");
        LPSTR paramTarget = strstr(lpCmdLine, "-t ");
        if(paramDecompress != NULL) {
            LPSTR szFileZipped = strdup(paramDecompress + 3);
            LPSTR szUnzipTarget = strdup(paramTarget + 3);
            if(paramTarget != NULL && paramTarget > paramDecompress) {
                szFileZipped[paramTarget - paramDecompress - 4] = 0;
            } else if(paramTarget != NULL && paramDecompress > paramTarget) {
                szUnzipTarget[paramDecompress - paramTarget - 4] = 0;
            }
            std::vector<LPSTR> fileList;
            LPCSTR szSep = " ,\t\n";
            LPSTR szToken = strtok(szFileZipped, szSep);
            while(szToken) {
                zip.Unpack(szFileZipped, szUnzipTarget);
                szToken = strtok(NULL, szSep);
            }
            free(szUnzipTarget);
            free(szFileZipped);
        } else {
            std::vector<LPSTR> fileList;
            LPSTR fileUnzipped = lpCmdLine;
            LPCSTR szSep = " ,\t\n";
            LPSTR szToken = strtok(fileUnzipped, szSep);
            while(szToken) {
                fileList.push_back(szToken);
                szToken = strtok(NULL, szSep);
            }
            if(fileList.empty()) {
                return 0;
            }
            int nItemCount = fileList.size();
            LPCSTR* szFileList = (LPCTSTR *)malloc(sizeof(LPTSTR) * (nItemCount + 1));
            memcpy(szFileList, &fileList[0], sizeof(LPTSTR) * nItemCount);
            szFileList[nItemCount] = NULL;
            if(nItemCount == 1) {
                LPSTR fileZipped = (CHAR *)malloc(strlen(szFileList[0]) + 5);
                strcpy(fileZipped, fileUnzipped);
                (strrchr(fileZipped, '.'))[0] = 0;
                strcat(fileZipped, ".zpd");
                zip.Pack(szFileList, fileZipped);
                free(fileZipped);
            } else {
                zip.Pack(szFileList, "zip.zpd");
            }
            free(szFileList);
        }
    } else {
        MessageBox(NULL,
            "Compress Files:\n\tZip FileList\nDecompress Files:\n\tZip [-t TargetPath] -d ZippedFileList",
            "Usage",
            MB_OK);
    }
    return 0;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章