encode.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 949 行 · 第 1/2 页

C
949
字号
    int     num;
    byte    runlen;
    byte    prevlen;
    byte *  lenptr;

    num = 0;
    prevlen = 0xFF;
    lenptr = len;
    runlen = 0;
    for( index = 0; index < NUM_CHARS; index++ ) {
        if( *lenptr > MAX_CODE_BITS ) {
            WriteMsg( "horrible shannon code overflow\n" );
            exit( 1 );
        }
        if( *lenptr != prevlen || (prevlen == 0 && runlen == 128)
                                    || (prevlen != 0 && runlen == 8)) {
            prevlen = *lenptr;
            num++;
            runlen = 0;
        }
        runlen++;
        lenptr++;
    }
    EncWriteByte( num  - 1 );
    codesize++;
    lenptr = len;
    prevlen = *lenptr;
    num = 0;
    for( index = 0; index < NUM_CHARS; index++ ) {
        if( *lenptr != prevlen || (prevlen != 0 && num == 8)
                               || (prevlen == 0 && num == 128) ) {
            if( prevlen == 0 ) {
                EncWriteByte( 0x80 + num - 1 );
            } else {
                EncWriteByte( ((num - 1) << 4) + prevlen - 1 );
            }
            codesize++;
            prevlen = *lenptr;
            num = 0;
        }
        lenptr++;
        num++;
    }
    if( prevlen == 0 ) {        // write out the last block
        EncWriteByte( 0x80 + num - 1 );
    } else {
        EncWriteByte( ((num - 1) << 4) + prevlen - 1 );
    }
    codesize++;
}

static void DoSecondPass( bool doshannon )
/****************************************/
{
    runlist *       rlptr;
    bool            inliteral;
    char *          data;
    int             index;
    int             num;
    unsigned        c;

    QSeek( RunHandle, 0, SEEK_SET );
    rlptr = RunList;
    while( rlptr != NULL ) {
        num = MAX_COPYLIST_SIZE;
        data = rlptr->data;
        if( rlptr->next == NULL ) {
            if( NumSpilled > 0 ) {
                QRead( RunHandle, AltBuffer, MAX_COPYLIST_SIZE );
                data = AltBuffer;
                NumSpilled--;
            } else {
                num = LastRunLen;
                rlptr = rlptr->next;
            }
        } else {
            rlptr = rlptr->next;
        }
        while( num > 0 ) {
            inliteral = !(*data & 0x80);
            for( index = *data & 0x7F; index > 0; index-- ) {
                c = EncReadByte();
                if( inliteral ) {
                    if( doshannon ) {
                        Putcode( len[ c ], code[ c ] );
                    } else {
                        Putcode( 9, c << 7 );  /* 0 bit, then code word. */
                    }
                } else {
                    if( doshannon ) {
                        c = 255 - THRESHOLD + c;
                        Putcode( len[ c ], code[ c ] );
                    } else {
                        Putcode( 7, (c + 0x40) << 9 ); /* 1, then length */
                    }
                    EncodePosition();
                }
            }
            num--;
            data++;
        }
    }
}

/* Compression */

static int DoEncode( arccmd *cmd )
/********************************/
{
    int             c, num, r, s, last_match_length;
    int             index;
    unsigned        currvalue;
    unsigned *      uptr;
    unsigned long   total;
    bool            inliteral;
    bool            doshannon;

    codesize = 0;
    WasLiteral = TRUE;
    CurrRunLen = 0;
    StartRunList();
    QSeek( TmpHandle, 0, SEEK_SET );
    QSeek( RunHandle, 0, SEEK_SET );
    SwitchBuffer( TmpHandle, TRUE, AltBuffer );
    memset( code, 0, NUM_CHARS * sizeof( unsigned ) );
    InitTree();
    s = 0;
    r = N - F;
    for (index = s; index < r; index++) {
        text_buf[index] = ' ';
    }
    for (num = 0; num < F && (c = EncReadByte(),IOStatus == OK); num++) {
        text_buf[r + num] = c;
    }
    if( IOStatus == IO_PROBLEM ) return( -1 );
    for (index = 1; index <= F; index++) {
        InsertNode(r - index);
    }
    InsertNode(r);
    do {
        if (match_length > num)
            match_length = num;
        if (match_length <= THRESHOLD) {
            match_length = 1;
            inliteral = TRUE;
            currvalue = text_buf[r];
            DecWriteByte( currvalue );
        } else {
            inliteral = FALSE;
            currvalue = 255 - THRESHOLD + match_length;
            DecWriteByte( match_length );
            DecWriteByte( match_position >> 6 );
            DecWriteByte( match_position & 0x3F );
        }
        WriteTmpByte( inliteral, currvalue );
        last_match_length = match_length;
        for (index = 0;
             index < last_match_length && (c=EncReadByte(),IOStatus == OK);
                                                                 index++ ) {
            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( IOStatus == IO_PROBLEM ) return( -1 );
        while (index++ < last_match_length) {
            DeleteNode(s);
            s = (s + 1) & (N - 1);
            r = (r + 1) & (N - 1);
            if (--num) InsertNode(r);
        }
    } while (num > 0);
    AddRunEntry();
    FlushWrite();
    RestoreBuffer( TRUE );
    SwitchBuffer( TmpHandle, FALSE, NULL );
// now set up the compression stuff & do the second pass.
    num = 0;
    total = 0;
    uptr = code;
    for( index = 0; index < NUM_CHARS; index++ ) {
        len[ index ] = 0;
        if( *uptr != 0 ) {
            total += *uptr;
            indicies[ num ] = index;
            num++;
        }
        uptr++;
    }
    QSeek( infile, 0, SEEK_SET );
    CalcLengths( total, 0, num - 1, 0 );
    doshannon = AssignCodes( num, cmd );
    if( doshannon ) {       // we have shannon codes,
        WriteCodes();       // so write out file using these
    }
    DoSecondPass( doshannon );
    EncodeEnd();
    RestoreBuffer( FALSE );
    FreeRunList();
    return( !doshannon );
}

static void InitHeader( arc_header *header, arccmd *cmd )
/******************************************/
{
    header->signature = SIGNATURE;
    header->major_ver = MAJOR_VERSION;
    header->minor_ver = MINOR_VERSION;
    header->num_files = 0;
    header->info_offset = 0;
    header->info_len = 0;
    if( cmd->flags & SECURE_PACK ) {
        header->major_ver += cmd->internal;// so incompat. with previous vers
        header->internal = cmd->internal;
    }

}

static void WriteHeaders( arc_header *header, info_list *list, int numfiles,
                                                                   int file )
/***************************************************************************/
{
    info_list *     info;           // node of the info_list
    int             entrylen;       // length of the file_info entry

    while( numfiles > 0 ) {     //NYI buffer this stuff.
        entrylen = sizeof(file_info) + (list->i.namelen & NAMELEN_MASK) - 1;
        QWrite( file, &list->i, entrylen );
        header->info_len += entrylen;
        info = list->next;
        WPMemFree( list );
        list = info;
        numfiles--;
    }
    QSeek( file, 0L, SEEK_SET );
    QWrite( file, header, sizeof( arc_header ) );
}

static void ReplaceExt( char * name, char * new_ext )
/***************************************************/
{
    char p[ _MAX_DIR ];
    char d[ _MAX_DRIVE ];
    char n[ _MAX_FNAME ];

    _splitpath( name, d, p, n, NULL );
    _makepath( name, d, p, n, new_ext );
}

// this is used for keeping track of the different archives while multipacking

typedef struct arc_list {
    struct arc_list *   next;
    info_list *         info;
    unsigned long       length;
    unsigned            info_len;
    int                 handle;
    int                 num_files;
} arc_list;

static void MultiPack( arccmd *cmd, info_list *list )
/***************************************************/
{
    arc_list *      archead;
    arc_list *      currarc;
    int             numarcs;
    unsigned long   compressed;
    unsigned long   currspot;
    unsigned        entrylen;
    unsigned long   limit;
    char            extension[ 4 ];
    char *          arcfname;
    info_list *     nextfile;
    arc_header      header;

    arcfname = alloca( strlen( cmd->arcname ) + 4 );
    strcpy( arcfname, cmd->arcname );
    limit = (unsigned long)cmd->u.limit << 10;
    numarcs = 0;
    archead = NULL;
    QSeek( outfile, sizeof( arc_header ), SEEK_SET );
    currspot = sizeof( arc_header );
    while( list != NULL ) {             // first get length of compressed file
        entrylen = sizeof(file_info) + (list->i.namelen & NAMELEN_MASK) - 1;
        nextfile = list->next;
        if( nextfile == NULL ) {
            compressed = QFileLen( outfile ) - currspot;
        } else {
            compressed = nextfile->i.disk_addr - currspot;
            currspot = nextfile->i.disk_addr;
        }
        currarc = archead;
        while( currarc != NULL ) {      // find an archive to pack file in.
            if( currarc->length + currarc->info_len + compressed
                                                  + entrylen < limit ) break;
            currarc = currarc->next;
        }
        if( currarc == NULL ) {         // need to make a new archive
            currarc = alloca( sizeof( arc_list ) );
            if( currarc == NULL ) Error( -1, "insufficient stack space!" );
            currarc->next = NULL;
            currarc->info = NULL;
            currarc->info_len = 0;
            currarc->length = sizeof( arc_header );
            currarc->num_files = 0;
            numarcs++;
            itoa( numarcs, extension, 36 );
            ReplaceExt( arcfname, extension );
            currarc->handle = QOpenW( arcfname );
            if( currarc->handle < 0 ) PackExit();
            QSeek( currarc->handle, sizeof( arc_header ), SEEK_SET );
            LinkList( &archead, currarc );
        }
        currarc->num_files++;       // update arc info & write data
        list->i.disk_addr = currarc->length;
        currarc->length += compressed;
        currarc->info_len += entrylen;
        LinkList( &currarc->info, list );
        CopyInfo( currarc->handle, outfile, compressed );
        list->next = NULL;
        list = nextfile;
    }
    currarc = archead;          // now fill in header information in archives
    while( currarc != NULL ) {
        InitHeader( &header, cmd );
        header.num_files = currarc->num_files;
        header.info_offset = currarc->length;
        WriteHeaders( &header, currarc->info, currarc->num_files,
                                                             currarc->handle );
        QClose( currarc->handle );
        currarc = currarc->next;
    }
    QClose( outfile );
    remove( cmd->arcname );
}

extern void Encode( arccmd *cmd )
/*******************************/
{
    wpackfile *     currname;       // current file name being processed.
    unsigned long   length;         // unencrypted length of the file.
    unsigned long   amtwrote;       // amount wrote to archive files
    unsigned long   amtread;        // amout read from all files.
    char *          name;           // stored name of the file
    char *          temp;
    unsigned        numfiles;       // number of files stored in archive
    int             namelen;        // length of the filename
    int             result;         // result of encoding the file.
    info_list *     info;           // node of the info_list
    info_list *     liststart;      // beginning of the info_list
    arc_header      header;         // archive main header.
    file_info **    filedata;       // block of file infos from old archive.
    char            tmpfile[ L_tmpnam ];    // pass1 info temp file name;
    char            runfile[ L_tmpnam ];    // run length temp file name;

    if( cmd->files == NULL  ||  cmd->files->filename == NULL ) {
        Error( -1, "No files to pack\n" );
    }
    filedata = ReadHeader( cmd, &header );
    if( filedata != NULL ) {    // there is an old file to add to.
        QClose( infile );
        if( cmd->flags & PACK_LIMIT ) {
            Error( -1, "old archive already exists");
        }
        outfile = QOpenM( cmd->arcname );
        if( outfile == -1 ) PackExit();
        WriteSeek( header.info_offset );
        amtwrote = header.info_offset;
    } else {
        outfile = QOpenW( cmd->arcname );
        WriteFiller( sizeof( arc_header ) );    // reserve space for header.
        amtwrote = sizeof( arc_header );
    }
    tmpnam( tmpfile );
    TmpHandle = QOpenW( tmpfile );
    tmpnam( runfile );
    RunHandle = QOpenW( runfile );
    if( TmpHandle < 0 || RunHandle < 0 ) {
        Error( -1, "problem opening temporary files" );
    }
    AltBuffer = WPMemAlloc( MAX_COPYLIST_SIZE );      // must be >= WRITE_SIZE
    liststart = NULL;
    amtread = 0;
    numfiles = 0;
    for( currname = cmd->files; currname->filename != NULL; currname++ ) {
        FlushRead();
        if( !(cmd->flags & PRESERVE_FNAME_CASE) ) {
            strlwr( currname->filename );
        }
        infile = QOpenR( currname->filename );
        if( infile == -1 ) {
            WriteMsg( "could not open file: " );
            WriteMsg( currname->filename );
            WriteMsg( "\n" );
        } else {
            numfiles++;
            if( !(cmd->flags & BE_QUIET) ) {
                WriteMsg( "packing file '" );
                WriteMsg( currname->filename );
                WriteMsg( "'\n" );
            }
            result = DoEncode( cmd );
            if( result == -1 ) continue;      // don't archive if error.
            if( currname->packname != NULL ) {
                name = currname->packname;
            } else if( !(cmd->flags & KEEP_PATHNAME) ) { // search for actual file name
                temp = currname->filename;
                name = temp;
                while( *temp != '\0' ) {
                    if( *temp == '\\' || *temp == ':' ) {
                        name = temp + 1;
                    }
                    temp++;
                }
            } else {
                name = currname->filename;
            }
            length = QFileLen( infile );
            amtread += length;
            namelen = strlen( name );
            info = WPMemAlloc( sizeof( info_list ) + namelen - 1);
            info->i.length = length;
            info->i.disk_addr = amtwrote;
            amtwrote += codesize;
            if( cmd->flags & USE_DATE_TIME ) {
                info->i.stamp = cmd->time;
            } else {
                info->i.stamp = QGetDate( infile );
            }
            memcpy( info->i.name, name, namelen );
            if( result ) {
                namelen |= NO_SHANNON_CODE;
            }
            info->i.namelen = namelen;
            info->i.crc = GetCRC();
            info->next = NULL;
            LinkList( &liststart, info );
            QClose( infile );
        }  // end if
    } // end for
    QClose( TmpHandle );
    QClose( RunHandle );
    remove( tmpfile );
    remove( runfile );
    if( numfiles > 0 ) {
        FlushWrite();
        if( cmd->flags & PACK_LIMIT ) {
            MultiPack( cmd, liststart );
        } else {    // add files to current archive
            if( filedata == NULL ) {
                InitHeader( &header, cmd );
            } else {        // write stuff in old header.
                QWrite( outfile, *filedata, header.info_len );
            }
            header.num_files += numfiles;
            header.info_offset = amtwrote;
            WriteHeaders( &header, liststart, numfiles, outfile );
            if( filedata == NULL ) {
                amtwrote += header.info_len;
                if( !(cmd->flags & BE_QUIET ) ) {
                    WriteNumeric( "uncompressed size: ", amtread );
                    WriteNumeric( "compressed size: ", amtwrote );
                }
            }
            QClose( outfile );      // close the archive file.
        }
    }
}


⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?