Exemple #1
0
/*
 * Compress "srcLen" bytes from "pStraw" to "fp".
 */
NuError
Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp,
    ulong srcLen, ulong* pDstLen, ushort* pCrc)
{
    NuError err = kNuErrNone;
    z_stream zstream;
    int zerr;
    Bytef* outbuf = nil;

    Assert(pArchive != nil);
    Assert(pStraw != nil);
    Assert(fp != nil);
    Assert(srcLen > 0);
    Assert(pDstLen != nil);
    Assert(pCrc != nil);

    err = Nu_AllocCompressionBufferIFN(pArchive);
    if (err != kNuErrNone)
        return err;

    /* allocate a similarly-sized buffer for the output */
    outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize);
    BailAlloc(outbuf);

    /*
     * Initialize the zlib stream.
     */
    zstream.zalloc = Nu_zalloc;
    zstream.zfree = Nu_zfree;
    zstream.opaque = pArchive;
    zstream.next_in = nil;
    zstream.avail_in = 0;
    zstream.next_out = outbuf;
    zstream.avail_out = kNuGenCompBufSize;
    zstream.data_type = Z_UNKNOWN;

    zerr = deflateInit(&zstream, kNuDeflateLevel);
    if (zerr != Z_OK) {
        err = kNuErrInternal;
        if (zerr == Z_VERSION_ERROR) {
            Nu_ReportError(NU_BLOB, err,
                "installed zlib is not compatible with linked version (%s)",
                ZLIB_VERSION);
        } else {
            Nu_ReportError(NU_BLOB, err,
                "call to deflateInit failed (zerr=%d)", zerr);
        }
        goto bail;
    }

    /*
     * Loop while we have data.
     */
    do {
        ulong getSize;
        int flush;

        /* should be able to read a full buffer every time */
        if (zstream.avail_in == 0 && srcLen) {
            getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen;
            DBUG(("+++ reading %ld bytes\n", getSize));

            err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "deflate read failed");
                goto z_bail;
            }

            srcLen -= getSize;

            *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize);

            zstream.next_in = pArchive->compBuf;
            zstream.avail_in = getSize;
        }

        if (srcLen == 0)
            flush = Z_FINISH;       /* tell zlib that we're done */
        else
            flush = Z_NO_FLUSH;     /* more to come! */

        zerr = deflate(&zstream, flush);
        if (zerr != Z_OK && zerr != Z_STREAM_END) {
            err = kNuErrInternal;
            Nu_ReportError(NU_BLOB, err, "zlib deflate call failed (zerr=%d)",
                zerr);
            goto z_bail;
        }

        /* write when we're full or when we're done */
        if (zstream.avail_out == 0 ||
            (zerr == Z_STREAM_END && zstream.avail_out != kNuGenCompBufSize))
        {
            DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf));
            err = Nu_FWrite(fp, outbuf, zstream.next_out - outbuf);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "fwrite failed in deflate");
                goto z_bail;
            }

            zstream.next_out = outbuf;
            zstream.avail_out = kNuGenCompBufSize;
        }
    } while (zerr == Z_OK);

    Assert(zerr == Z_STREAM_END);       /* other errors should've been caught */

    *pDstLen = zstream.total_out;

z_bail:
    deflateEnd(&zstream);        /* free up any allocated structures */

bail:
    if (outbuf != nil)
        free(outbuf);
    return err;
}
Exemple #2
0
/*
 * Skip past some or all of the thread data in the archive.  For file
 * archives, we scan all the threads, but for streaming archives we only
 * want to scan up to the filename thread.  (If the filename thread comes
 * after one of the data threads, we have a problem!)
 *
 * The tricky part here is that we don't want to skip over a filename
 * thread.  We actually want to read it in, so that we have something to
 * show to the application.  (Someday I'll get AndyN for putting me
 * through this...)
 */
NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord, long numThreads)
{
    NuError err = kNuErrNone;
    NuThread* pThread;
    FILE* fp;

    Assert(pArchive != NULL);
    Assert(pRecord != NULL);

    fp = pArchive->archiveFp;

    Assert(numThreads <= (long)pRecord->recTotalThreads);

    pThread = pRecord->pThreads;
    while (numThreads--) {
        if (pRecord->threadFilenameMOR == NULL &&
            NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) ==
                kNuThreadIDFilename)
        {
            /* it's the first filename thread, read the whole thing */
            if (pThread->thCompThreadEOF > kNuReasonableFilenameLen) {
                err = kNuErrBadRecord;
                Nu_ReportError(NU_BLOB, err, "Bad thread filename len (%u)",
                    pThread->thCompThreadEOF);
                goto bail;
            }
            pRecord->threadFilenameMOR = Nu_Malloc(pArchive,
                                        pThread->thCompThreadEOF +1);
            BailAlloc(pRecord->threadFilenameMOR);

            /* note there is no CRC on a filename thread */
            (void) Nu_ReadBytes(pArchive, fp, pRecord->threadFilenameMOR,
                    pThread->thCompThreadEOF);
            if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "Failed reading filename thread");
                goto bail;
            }

            /* null-terminate on the actual len, not the buffer len */
            pRecord->threadFilenameMOR[pThread->thThreadEOF] = '\0';

            Nu_StripHiIfAllSet(pRecord->threadFilenameMOR);

            /* prefer this one over the record one, but only one should exist */
            if (pRecord->filenameMOR != NULL) {
                DBUG(("--- HEY: got record filename and thread filename\n"));
            }
            pRecord->filenameMOR = pRecord->threadFilenameMOR;

        } else {
            /* not a filename (or not first filename), skip past it */
            err = Nu_SeekArchive(pArchive, pArchive->archiveFp,
                    pThread->thCompThreadEOF, SEEK_CUR);
            BailError(err);
        }

        pThread++;
    }

    /*
     * Should've had one by now.  Supposedly, older versions of ShrinkIt
     * wouldn't prompt for a disk image name on DOS 3.3 volumes, so you'd
     * end up with a disk image that had no name attached.  This will tend
     * to confuse things, so we go ahead and give it a name.
     */
    if (pRecord->filenameMOR == NULL) {
        DBUG(("+++ no filename found, using default record name\n"));
        pRecord->filenameMOR = kNuDefaultRecordName;
    }

    pArchive->currentOffset += pRecord->totalCompLength;

    if (!Nu_IsStreaming(pArchive)) {
        Assert(pArchive->currentOffset == ftell(pArchive->archiveFp));
    }

bail:
    return err;
}
Exemple #3
0
/*
 * Expand from "infp" to "pFunnel".
 */
NuError
Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord,
    const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc)
{
    NuError err = kNuErrNone;
    z_stream zstream;
    int zerr;
    ulong compRemaining;
    Bytef* outbuf;

    Assert(pArchive != nil);
    Assert(pThread != nil);
    Assert(infp != nil);
    Assert(pFunnel != nil);

    err = Nu_AllocCompressionBufferIFN(pArchive);
    if (err != kNuErrNone)
        return err;

    /* allocate a similarly-sized buffer for the output */
    outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize);
    BailAlloc(outbuf);

    compRemaining = pThread->thCompThreadEOF;

    /*
     * Initialize the zlib stream.
     */
    zstream.zalloc = Nu_zalloc;
    zstream.zfree = Nu_zfree;
    zstream.opaque = pArchive;
    zstream.next_in = nil;
    zstream.avail_in = 0;
    zstream.next_out = outbuf;
    zstream.avail_out = kNuGenCompBufSize;
    zstream.data_type = Z_UNKNOWN;

    zerr = inflateInit(&zstream);
    if (zerr != Z_OK) {
        err = kNuErrInternal;
        if (zerr == Z_VERSION_ERROR) {
            Nu_ReportError(NU_BLOB, err,
                "installed zlib is not compatible with linked version (%s)",
                ZLIB_VERSION);
        } else {
            Nu_ReportError(NU_BLOB, err,
                "call to inflateInit failed (zerr=%d)", zerr);
        }
        goto bail;
    }

    /*
     * Loop while we have data.
     */
    do {
        ulong getSize;

        /* read as much as we can */
        if (zstream.avail_in == 0) {
            getSize = (compRemaining > kNuGenCompBufSize) ?
                        kNuGenCompBufSize : compRemaining;
            DBUG(("+++ reading %ld bytes (%ld left)\n", getSize,
                compRemaining));

            err = Nu_FRead(infp, pArchive->compBuf, getSize);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "inflate read failed");
                goto z_bail;
            }

            compRemaining -= getSize;

            zstream.next_in = pArchive->compBuf;
            zstream.avail_in = getSize;
        }

        /* uncompress the data */
        zerr = inflate(&zstream, Z_NO_FLUSH);
        if (zerr != Z_OK && zerr != Z_STREAM_END) {
            err = kNuErrInternal;
            Nu_ReportError(NU_BLOB, err, "zlib inflate call failed (zerr=%d)",
                zerr);
            goto z_bail;
        }

        /* write every time there's anything (buffer will usually be full) */
        if (zstream.avail_out != kNuGenCompBufSize) {
            DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf));
            err = Nu_FunnelWrite(pArchive, pFunnel, outbuf,
                    zstream.next_out - outbuf);
            if (err != kNuErrNone) {
                Nu_ReportError(NU_BLOB, err, "write failed in inflate");
                goto z_bail;
            }

            if (pCrc != nil)
                *pCrc = Nu_CalcCRC16(*pCrc, outbuf, zstream.next_out - outbuf);

            zstream.next_out = outbuf;
            zstream.avail_out = kNuGenCompBufSize;
        }
    } while (zerr == Z_OK);

    Assert(zerr == Z_STREAM_END);       /* other errors should've been caught */

    if (zstream.total_out != pThread->actualThreadEOF) {
        err = kNuErrBadData;
        Nu_ReportError(NU_BLOB, err,
            "size mismatch on inflated file (%ld vs %ld)",
            zstream.total_out, pThread->actualThreadEOF);
        goto z_bail;
    }

z_bail:
    inflateEnd(&zstream);        /* free up any allocated structures */

bail:
    if (outbuf != nil)
        free(outbuf);
    return err;
}
Exemple #4
0
/*
 * Read the threads from the current archive file position.
 *
 * The storage for the threads is allocated here, in one block.  We could
 * have used a linked list like NuLib, but that doesn't really provide any
 * benefit for us, and adds complexity.
 */
NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
    uint16_t* pCrc)
{
    NuError err = kNuErrNone;
    NuThread* pThread;
    long count;
    Boolean needFakeData, needFakeRsrc;

    needFakeData = true;
    needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended);

    Assert(pArchive != NULL);
    Assert(pRecord != NULL);
    Assert(pCrc != NULL);

    if (!pRecord->recTotalThreads) {
        /* not sure if this is reasonable, but we can handle it */
        DBUG(("--- WEIRD: no threads in the record?\n"));
        goto bail;
    }

    pRecord->pThreads = Nu_Malloc(pArchive,
                            pRecord->recTotalThreads * sizeof(NuThread));
    BailAlloc(pRecord->pThreads);

    count = pRecord->recTotalThreads;
    pThread = pRecord->pThreads;
    while (count--) {
        err = Nu_ReadThreadHeader(pArchive, pThread, pCrc);
        BailError(err);

        if (pThread->thThreadClass == kNuThreadClassData) {
            if (pThread->thThreadKind == kNuThreadKindDataFork) {
                needFakeData = false;
            } else if (pThread->thThreadKind == kNuThreadKindRsrcFork) {
                needFakeRsrc = false;
            } else if (pThread->thThreadKind == kNuThreadKindDiskImage) {
                /* needFakeRsrc shouldn't be set, but clear anyway */
                needFakeData = needFakeRsrc = false;
            }
        }

        /*
         * Some versions of ShrinkIt write an invalid thThreadEOF for disks,
         * so we have to figure out what it's supposed to be.
         */
        if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) ==
            kNuThreadIDDiskImage)
        {
            if (pRecord->recStorageType <= 13) {
                /* supposed to be block size, but SHK v3.0.1 stored it wrong */
                pThread->actualThreadEOF = pRecord->recExtraType * 512;

            } else if (pRecord->recStorageType == 256 &&
                       pRecord->recExtraType == 280 &&
                       pRecord->recFileSysID == kNuFileSysDOS33)
            {
                /*
                 * Fix for less-common ShrinkIt problem: looks like an old
                 * version of GS/ShrinkIt used 256 as the block size when
                 * compressing DOS 3.3 images from 5.25" disks.  If that
                 * appears to be the case here, crank up the block size.
                 */
                DBUG(("--- no such thing as a 70K disk image!\n"));
                pThread->actualThreadEOF = pRecord->recExtraType * 512;

            } else {
                pThread->actualThreadEOF =
                    pRecord->recExtraType * pRecord->recStorageType;
            }
        } else {
            pThread->actualThreadEOF = pThread->thThreadEOF;
        }

        pThread->used = false;
        pThread++;
    }

    /*
     * If "mask threadless" is set, create "fake" threads with empty
     * data and resource forks as needed.
     */
    if ((needFakeData || needFakeRsrc) && pArchive->valMaskDataless) {
        int firstNewThread = pRecord->recTotalThreads;

        if (needFakeData) {
            pRecord->recTotalThreads++;
            pRecord->fakeThreads++;
        }
        if (needFakeRsrc) {
            pRecord->recTotalThreads++;
            pRecord->fakeThreads++;
        }

        pRecord->pThreads = Nu_Realloc(pArchive, pRecord->pThreads,
                                pRecord->recTotalThreads * sizeof(NuThread));
        BailAlloc(pRecord->pThreads);

        pThread = pRecord->pThreads + firstNewThread;

        if (needFakeData) {
            pThread->thThreadClass = kNuThreadClassData;
            pThread->thThreadFormat = kNuThreadFormatUncompressed;
            pThread->thThreadKind = kNuThreadKindDataFork;
            pThread->thThreadCRC = kNuInitialThreadCRC;
            pThread->thThreadEOF = 0;
            pThread->thCompThreadEOF = 0;
            pThread->threadIdx = Nu_GetNextThreadIdx(pArchive);
            pThread->actualThreadEOF = 0;
            pThread->fileOffset = -99999999;
            pThread->used = false;
            pThread++;
        }
        if (needFakeRsrc) {
            pThread->thThreadClass = kNuThreadClassData;
            pThread->thThreadFormat = kNuThreadFormatUncompressed;
            pThread->thThreadKind = kNuThreadKindRsrcFork;
            pThread->thThreadCRC = kNuInitialThreadCRC;
            pThread->thThreadEOF = 0;
            pThread->thCompThreadEOF = 0;
            pThread->threadIdx = Nu_GetNextThreadIdx(pArchive);
            pThread->actualThreadEOF = 0;
            pThread->fileOffset = -99999999;
            pThread->used = false;
        }
    }

bail:
    return err;
}