mirror of
https://github.com/fadden/nulib2.git
synced 2025-02-05 20:30:57 +00:00
Added test-twirl to samples.
This commit is contained in:
parent
47e930c7dd
commit
16234a46d1
@ -1,3 +1,6 @@
|
||||
2003/02/23 fadden
|
||||
- Added test-twirl to samples.
|
||||
|
||||
2003/02/22 fadden
|
||||
- Turn off EOL conversion when extracting disk images.
|
||||
- Added NuTestRecord().
|
||||
|
@ -4,7 +4,7 @@
|
||||
* This is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Library General Public License, see the file COPYING.LIB.
|
||||
*
|
||||
* Common functions for NuLib tests
|
||||
* Common functions for NuLib tests.
|
||||
*/
|
||||
#ifndef __Common__
|
||||
#define __Common__
|
||||
|
@ -19,12 +19,13 @@ GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow
|
||||
CFLAGS = @BUILD_FLAGS@ -I. -I.. @DEFS@
|
||||
|
||||
#ALL_SRCS = $(wildcard *.c *.cpp)
|
||||
ALL_SRCS = Exerciser.c Launder.c ImgConv.c TestBasic.c TestExtract.c \
|
||||
TestSimple.c
|
||||
ALL_SRCS = Exerciser.c ImgConv.c Launder.c TestBasic.c \
|
||||
TestExtract.c TestSimple.c TestTwirl.c
|
||||
|
||||
NUFXLIB = -L.. -lnufx
|
||||
|
||||
PRODUCTS = exerciser imgconv launder test-basic test-extract test-simple
|
||||
PRODUCTS = exerciser imgconv launder test-basic test-extract test-simple \
|
||||
test-twirl
|
||||
|
||||
#ifdef PURIFY_BUILD
|
||||
# PURIFY = purify
|
||||
@ -64,6 +65,9 @@ test-simple: TestSimple.o $(LIB_PRODUCT)
|
||||
test-extract: TestExtract.o $(LIB_PRODUCT)
|
||||
$(PURIFY) $(QUANTIFY) $(CC) -o $@ TestExtract.o $(NUFXLIB) @LIBS@
|
||||
|
||||
test-twirl: TestTwirl.o $(LIB_PRODUCT)
|
||||
$(PURIFY) $(QUANTIFY) $(CC) -o $@ TestTwirl.o $(NUFXLIB) @LIBS@
|
||||
|
||||
tags::
|
||||
ctags --totals -R ../*
|
||||
@#ctags *.cpp ../*.c *.h ../*.h
|
||||
|
@ -83,6 +83,9 @@ test-simple.exe: TestSimple.obj $(LIB_PRODUCT)
|
||||
test-extract.exe: TestExtract.obj $(LIB_PRODUCT)
|
||||
$(link) $(ldebug) TestExtract.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
|
||||
|
||||
test-twirl.exe: TestTwirl.obj $(LIB_PRODUCT)
|
||||
$(link) $(ldebug) TestTwirl.obj -out:$@ $(NUFXSRCDIR)\nufxlib.lib $(LIB_FLAGS)
|
||||
|
||||
clean:
|
||||
-del *.obj
|
||||
-del *.pdb
|
||||
@ -94,6 +97,7 @@ clean:
|
||||
-del test-basic.exe
|
||||
-del test-simple.exe
|
||||
-del test-extract.exe
|
||||
-del test-twirl.exe
|
||||
|
||||
Exerciser.obj: Exerciser.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
ImgConv.obj: ImgConv.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
@ -101,4 +105,5 @@ Launder.obj: Launder.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
TestBasic.obj: TestBasic.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
TestSimple.obj: TestSimple.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
TestExtract.obj: TestExtract.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
TestTwirl.obj: TestTwirl.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h
|
||||
|
||||
|
@ -96,3 +96,15 @@ Simple test program. Give it the name of an archive, and it will write
|
||||
all filename threads into "out.buf", "out.fp", and "out.file" using three
|
||||
different kinds of NuDataSinks.
|
||||
|
||||
|
||||
test-twirl
|
||||
==========
|
||||
|
||||
Like "launder", but not meant to be useful. This recompresses the file "in
|
||||
place", deleting and adding threads within existing records several times.
|
||||
The changes are periodically flushed, but the archive is never closed.
|
||||
The goal is to test repeated updates on an open archive.
|
||||
|
||||
This will leave a file called "TwirlCopy678" in the current directory, and
|
||||
overwrite "TwirlTmp789" during processing.
|
||||
|
||||
|
687
nufxlib-0/samples/TestTwirl.c
Normal file
687
nufxlib-0/samples/TestTwirl.c
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* NuFX archive manipulation library
|
||||
* Copyright (C) 2000-2003 by Andy McFadden, All Rights Reserved.
|
||||
* This is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Library General Public License, see the file COPYING.LIB.
|
||||
*
|
||||
* Recompress records in place, several times, possibly deleting records
|
||||
* or threads as we go. The goal is to perform a large number of operations
|
||||
* that modify the archive without closing and reopening it.
|
||||
*
|
||||
* Depending on which #defines are enabled, this can be very destructive,
|
||||
* so a copy of the archive is made before processing begins.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "NufxLib.h"
|
||||
#include "Common.h"
|
||||
|
||||
/* copy the archive to this file before starting */
|
||||
static const char* kWorkFileName = "TwirlCopy678";
|
||||
static const char* kTempFileName = "TwirlTmp789";
|
||||
|
||||
/* after loading this much stuff into memory, flush changes */
|
||||
const int kMaxHeldLen = 1024 * 1024;
|
||||
|
||||
/*
|
||||
* A list of CRCs.
|
||||
*/
|
||||
typedef struct CRCList {
|
||||
int numEntries;
|
||||
unsigned short* entries;
|
||||
} CRCList;
|
||||
|
||||
|
||||
/*
|
||||
* Returns true if the compression type is supported, false otherwise.
|
||||
*/
|
||||
int
|
||||
CompressionSupported(NuValue compression)
|
||||
{
|
||||
int result;
|
||||
|
||||
switch (compression) {
|
||||
case kNuCompressNone:
|
||||
result = true;
|
||||
break;
|
||||
case kNuCompressSQ:
|
||||
result = (NuTestFeature(kNuFeatureCompressSQ) == kNuErrNone);
|
||||
break;
|
||||
case kNuCompressLZW1:
|
||||
case kNuCompressLZW2:
|
||||
result = (NuTestFeature(kNuFeatureCompressLZW) == kNuErrNone);
|
||||
break;
|
||||
case kNuCompressLZC12:
|
||||
case kNuCompressLZC16:
|
||||
result = (NuTestFeature(kNuFeatureCompressLZC) == kNuErrNone);
|
||||
break;
|
||||
case kNuCompressDeflate:
|
||||
result = (NuTestFeature(kNuFeatureCompressDeflate) == kNuErrNone);
|
||||
break;
|
||||
case kNuCompressBzip2:
|
||||
result = (NuTestFeature(kNuFeatureCompressBzip2) == kNuErrNone);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
result = false;
|
||||
}
|
||||
|
||||
/*printf("Returning %d for %ld\n", result, compression);*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This gets called when a buffer DataSource is no longer needed.
|
||||
*/
|
||||
NuResult
|
||||
FreeCallback(NuArchive* pArchive, void* args)
|
||||
{
|
||||
free(args);
|
||||
return kNuOK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dump a CRC list.
|
||||
*/
|
||||
void
|
||||
DumpCRCs(const CRCList* pCRCList)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(" NumEntries: %d\n", pCRCList->numEntries);
|
||||
|
||||
for (i = 0; i < pCRCList->numEntries; i++)
|
||||
printf(" %5d: 0x%04x\n", i, pCRCList->entries[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a CRC list.
|
||||
*/
|
||||
void
|
||||
FreeCRCs(CRCList* pCRCList)
|
||||
{
|
||||
if (pCRCList == nil)
|
||||
return;
|
||||
|
||||
free(pCRCList->entries);
|
||||
free(pCRCList);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather a list of CRCs from the archive.
|
||||
*
|
||||
* We assume there are at most two data threads (e.g. data fork and rsrc
|
||||
* fork) in a record.
|
||||
*
|
||||
* Returns the list on success, nil on failure.
|
||||
*/
|
||||
CRCList*
|
||||
GatherCRCs(NuArchive* pArchive)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
const NuMasterHeader* pMasterHeader;
|
||||
CRCList* pCRCList = nil;
|
||||
unsigned short* pEntries = nil;
|
||||
long recCount, maxCRCs;
|
||||
long recIdx, crcIdx;
|
||||
int i;
|
||||
|
||||
pCRCList = malloc(sizeof(*pCRCList));
|
||||
if (pCRCList == nil) {
|
||||
fprintf(stderr, "ERROR: couldn't alloc CRC list\n");
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
memset(pCRCList, 0, sizeof(*pCRCList));
|
||||
|
||||
/* get record count out of master header, just for fun */
|
||||
err = NuGetMasterHeader(pArchive, &pMasterHeader);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err);
|
||||
goto bail;
|
||||
}
|
||||
recCount = pMasterHeader->mhTotalRecords;
|
||||
maxCRCs = recCount * 2;
|
||||
|
||||
pEntries = malloc(sizeof(*pEntries) * maxCRCs);
|
||||
if (pEntries == nil) {
|
||||
fprintf(stderr, "ERROR: unable to alloc CRC list (%ld entries)\n",
|
||||
maxCRCs);
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
pCRCList->entries = pEntries;
|
||||
|
||||
for (i = 0; i < maxCRCs; i++)
|
||||
pEntries[i] = 0xdead;
|
||||
|
||||
/*
|
||||
* Enumerate our way through the records. If something was disturbed
|
||||
* we should end up in a different place and the CRCs will be off.
|
||||
*/
|
||||
crcIdx = 0;
|
||||
for (recIdx = 0; recIdx < recCount; recIdx++) {
|
||||
NuRecordIdx recordIdx;
|
||||
const NuRecord* pRecord;
|
||||
const NuThread* pThread;
|
||||
|
||||
err = NuGetRecordIdxByPosition(pArchive, recIdx, &recordIdx);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n",
|
||||
recIdx, err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = NuGetRecord(pArchive, recordIdx, &pRecord);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to get recordIdx %ld\n", recordIdx);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (NuRecordGetNumThreads(pRecord) == 0) {
|
||||
fprintf(stderr, "ERROR: not expecting empty record (%ld)!\n",
|
||||
recordIdx);
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (i = 0; i < NuRecordGetNumThreads(pRecord); i++) {
|
||||
pThread = NuGetThread(pRecord, i);
|
||||
if (pThread->thThreadClass == kNuThreadClassData) {
|
||||
if (crcIdx >= maxCRCs) {
|
||||
fprintf(stderr, "ERROR: CRC buffer exceeded\n");
|
||||
assert(false);
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
pEntries[crcIdx++] = pThread->thThreadCRC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pCRCList->numEntries = crcIdx;
|
||||
|
||||
DumpCRCs(pCRCList);
|
||||
|
||||
bail:
|
||||
if (err != kNuErrNone) {
|
||||
FreeCRCs(pCRCList);
|
||||
pCRCList = nil;
|
||||
}
|
||||
return pCRCList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare the current set of CRCs against our saved list.
|
||||
*
|
||||
* Returns 0 on success, nonzero on failure.
|
||||
*/
|
||||
int
|
||||
CompareCRCs(NuArchive* pArchive, const CRCList* pOldCRCList)
|
||||
{
|
||||
CRCList* pNewCRCList = nil;
|
||||
int result = -1;
|
||||
int i;
|
||||
|
||||
pNewCRCList = GatherCRCs(pArchive);
|
||||
if (pNewCRCList == nil) {
|
||||
fprintf(stderr, "ERROR: unable to gather new list\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (pOldCRCList->numEntries != pNewCRCList->numEntries) {
|
||||
fprintf(stderr, "ERROR: numEntries mismatch: %d vs %d\n",
|
||||
pOldCRCList->numEntries, pNewCRCList->numEntries);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (i = 0; i < pNewCRCList->numEntries; i++) {
|
||||
if (pOldCRCList->entries[i] != pNewCRCList->entries[i]) {
|
||||
fprintf(stderr, "ERROR: CRC mismatch: %5d old=0x%04x new=0x%04x\n",
|
||||
i, pOldCRCList->numEntries, pNewCRCList->numEntries);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
printf(" Matched %d CRCs\n", pOldCRCList->numEntries);
|
||||
|
||||
result = 0;
|
||||
|
||||
bail:
|
||||
FreeCRCs(pNewCRCList);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Recompress a single thread.
|
||||
*
|
||||
* This entails (1) extracting the existing thread, (2) deleting the
|
||||
* thread, and (3) adding the extracted data.
|
||||
*
|
||||
* All of this good stuff gets queued up until the next NuFlush call.
|
||||
*/
|
||||
NuError
|
||||
RecompressThread(NuArchive* pArchive, const NuRecord* pRecord,
|
||||
const NuThread* pThread)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
NuDataSource* pDataSource = nil;
|
||||
NuDataSink* pDataSink = nil;
|
||||
unsigned char* buf = nil;
|
||||
|
||||
if (pThread->actualThreadEOF == 0) {
|
||||
buf = malloc(1);
|
||||
if (buf == nil) {
|
||||
fprintf(stderr, "ERROR: failed allocating trivial buffer\n");
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Create a buffer and data sink to hold the data.
|
||||
*/
|
||||
buf = malloc(pThread->actualThreadEOF);
|
||||
if (buf == nil) {
|
||||
fprintf(stderr, "ERROR: failed allocating %ld bytes\n",
|
||||
pThread->actualThreadEOF);
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf,
|
||||
pThread->actualThreadEOF, &pDataSink);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n",err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the data.
|
||||
*/
|
||||
err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: failed extracting thread %ld in '%s': %s\n",
|
||||
pThread->threadIdx, pRecord->filename, NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the existing thread.
|
||||
*/
|
||||
err = NuDeleteThread(pArchive, pThread->threadIdx);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to delete thread %ld\n",
|
||||
pThread->threadIdx);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a data source for the new thread. Specify a callback to free
|
||||
* the buffer when NufxLib is done with it.
|
||||
*/
|
||||
err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed,
|
||||
0, buf, 0, pThread->actualThreadEOF, FreeCallback, &pDataSource);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err);
|
||||
goto bail;
|
||||
}
|
||||
buf = nil;
|
||||
|
||||
/*
|
||||
* Create replacement thread.
|
||||
*/
|
||||
err = NuAddThread(pArchive, pRecord->recordIdx, NuGetThreadID(pThread),
|
||||
pDataSource, nil);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to add new thread ID=0x%08lx (err=%d)\n",
|
||||
NuGetThreadID(pThread), err);
|
||||
goto bail;
|
||||
}
|
||||
pDataSource = nil; /* now owned by NufxLib */
|
||||
|
||||
bail:
|
||||
NuFreeDataSink(pDataSink);
|
||||
NuFreeDataSource(pDataSource);
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recompress a single record.
|
||||
*
|
||||
* The amount of data we're holding in memory as a result of the
|
||||
* recompression is placed in "*pLen".
|
||||
*/
|
||||
NuError
|
||||
RecompressRecord(NuArchive* pArchive, NuRecordIdx recordIdx, long* pLen)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
const NuRecord* pRecord;
|
||||
const NuThread* pThread;
|
||||
int i;
|
||||
|
||||
printf(" Recompressing %ld\n", recordIdx);
|
||||
|
||||
*pLen = 0;
|
||||
|
||||
err = NuGetRecord(pArchive, recordIdx, &pRecord);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to get record %ld (err=%d)\n",
|
||||
recordIdx, err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (i = 0; i < NuRecordGetNumThreads(pRecord); i++) {
|
||||
pThread = NuGetThread(pRecord, i);
|
||||
if (pThread->thThreadClass == kNuThreadClassData) {
|
||||
/*printf(" Recompressing %d (threadID=0x%08lx)\n", i,
|
||||
NuGetThreadID(pThread));*/
|
||||
err = RecompressThread(pArchive, pRecord, pThread);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: failed recompressing thread %ld "
|
||||
" in record %ld (err=%d)\n",
|
||||
pThread->threadIdx, pRecord->recordIdx, err);
|
||||
goto bail;
|
||||
}
|
||||
*pLen += pThread->actualThreadEOF;
|
||||
} else {
|
||||
/*printf(" Skipping %d (threadID=0x%08lx)\n", i,
|
||||
NuGetThreadID(pThread));*/
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recompress every data thread in the archive.
|
||||
*/
|
||||
NuError
|
||||
RecompressArchive(NuArchive* pArchive, NuValue compression)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
NuRecordIdx* pIndices = nil;
|
||||
NuAttr countAttr;
|
||||
long heldLen;
|
||||
long idx;
|
||||
|
||||
err = NuSetValue(pArchive, kNuValueDataCompression, compression);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to set compression to %ld (err=%d)\n",
|
||||
compression, err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
printf("Recompressing threads with compression type %ld\n", compression);
|
||||
|
||||
err = NuGetAttr(pArchive, kNuAttrNumRecords, &countAttr);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to get numRecords (err=%d)\n", err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (countAttr == 0) {
|
||||
printf("No records found!\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get all of the indices up front. This way, if something causes a
|
||||
* record to "disappear" during processing, we will know about it.
|
||||
*/
|
||||
pIndices = malloc(countAttr * sizeof(*pIndices));
|
||||
if (pIndices == nil) {
|
||||
fprintf(stderr, "ERROR: malloc on %ld indices failed\n", countAttr);
|
||||
err = kNuErrGeneric;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < countAttr; idx++) {
|
||||
err = NuGetRecordIdxByPosition(pArchive, idx, &pIndices[idx]);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n",
|
||||
idx, err);
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk through the index list, handling each record individually.
|
||||
*/
|
||||
heldLen = 0;
|
||||
for (idx = 0; idx < countAttr; idx++) {
|
||||
long recHeldLen;
|
||||
|
||||
err = RecompressRecord(pArchive, pIndices[idx], &recHeldLen);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: failed recompressing record %ld (err=%d)\n",
|
||||
pIndices[idx], err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
heldLen += recHeldLen;
|
||||
|
||||
if (heldLen > kMaxHeldLen) {
|
||||
long statusFlags;
|
||||
|
||||
printf(" (flush)\n");
|
||||
err = NuFlush(pArchive, &statusFlags);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: intra-recompress flush failed: %s\n",
|
||||
NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
heldLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
free(pIndices);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate the twirling.
|
||||
*/
|
||||
int
|
||||
TwirlArchive(const char* filename)
|
||||
{
|
||||
NuError err = kNuErrNone;
|
||||
NuArchive* pArchive = nil;
|
||||
CRCList* pCRCList = nil;
|
||||
int compression;
|
||||
int cc;
|
||||
|
||||
/*
|
||||
* Open the archive after removing any temp file remnants.
|
||||
*/
|
||||
cc = unlink(kTempFileName);
|
||||
if (cc == 0)
|
||||
printf("Removed stale temp file '%s'\n", kTempFileName);
|
||||
|
||||
err = NuOpenRW(filename, kTempFileName, 0, &pArchive);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: unable to open archive '%s': %s\n",
|
||||
filename, NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mask records with no data threads, so we don't have to
|
||||
* special-case them.
|
||||
*/
|
||||
err = NuSetValue(pArchive, kNuValueMaskDataless, true);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: couldn't mask dataless (err=%d)\n", err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
pCRCList = GatherCRCs(pArchive);
|
||||
if (pCRCList == nil) {
|
||||
fprintf(stderr, "ERROR: unable to get CRC list\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each type of compression, recompress the entire archive.
|
||||
*/
|
||||
for (compression = kNuCompressNone; compression <= kNuCompressBzip2;
|
||||
compression++)
|
||||
{
|
||||
long statusFlags;
|
||||
|
||||
if (!CompressionSupported(compression))
|
||||
continue;
|
||||
|
||||
err = RecompressArchive(pArchive, compression);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: recompress failed: %s\n", NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = NuFlush(pArchive, &statusFlags);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: post-recompress flush failed: %s\n",
|
||||
NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Same thing, reverse order. We want to start with the same one we
|
||||
* ended on above, so we can practice skipping over things.
|
||||
*/
|
||||
for (compression = kNuCompressBzip2; compression >= kNuCompressNone;
|
||||
compression--)
|
||||
{
|
||||
long statusFlags;
|
||||
|
||||
if (!CompressionSupported(compression))
|
||||
continue;
|
||||
|
||||
err = RecompressArchive(pArchive, compression);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: recompress2 failed: %s\n", NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
err = NuFlush(pArchive, &statusFlags);
|
||||
if (err != kNuErrNone) {
|
||||
fprintf(stderr, "ERROR: post-recompress flush2 failed: %s\n",
|
||||
NuStrError(err));
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
if (CompareCRCs(pArchive, pCRCList) != 0) {
|
||||
fprintf(stderr, "ERROR: CRCs didn't match\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
printf("Done!\n");
|
||||
|
||||
bail:
|
||||
FreeCRCs(pCRCList);
|
||||
if (pArchive != nil) {
|
||||
NuAbort(pArchive);
|
||||
NuClose(pArchive);
|
||||
}
|
||||
|
||||
return (err != kNuErrNone);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Copy from the current offset in "srcfp" to a new file called
|
||||
* "outFileName". Returns a writable file descriptor for the new file
|
||||
* on success, or nil on error.
|
||||
*/
|
||||
FILE*
|
||||
CopyFile(const char* outFileName, FILE* srcfp)
|
||||
{
|
||||
char buf[24576];
|
||||
FILE* outfp;
|
||||
size_t count;
|
||||
|
||||
outfp = fopen(outFileName, kNuFileOpenWriteTrunc);
|
||||
if (outfp == nil) {
|
||||
fprintf(stderr, "ERROR: unable to open '%s' (err=%d)\n", outFileName,
|
||||
errno);
|
||||
return nil;
|
||||
}
|
||||
|
||||
while (!feof(srcfp)) {
|
||||
count = fread(buf, 1, sizeof(buf), srcfp);
|
||||
if (count == 0)
|
||||
break;
|
||||
if (fwrite(buf, 1, count, outfp) != count) {
|
||||
fprintf(stderr, "ERROR: failed writing outfp (err=%d)\n", errno);
|
||||
fclose(outfp);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (ferror(srcfp)) {
|
||||
fprintf(stderr, "ERROR: failed reading srcfp (err=%d)\n", errno);
|
||||
fclose(outfp);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return outfp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's get started.
|
||||
*/
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
long major, minor, bug;
|
||||
const char* pBuildDate;
|
||||
FILE* srcfp = nil;
|
||||
FILE* infp = nil;
|
||||
int cc;
|
||||
|
||||
/* don't buffer output */
|
||||
setvbuf(stdout, nil, _IONBF, 0);
|
||||
setvbuf(stderr, nil, _IONBF, 0);
|
||||
|
||||
(void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil);
|
||||
printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n\n",
|
||||
major, minor, bug, pBuildDate);
|
||||
|
||||
if (argc == 2) {
|
||||
srcfp = fopen(argv[1], kNuFileOpenReadOnly);
|
||||
if (srcfp == nil) {
|
||||
perror("fopen failed");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: you have to specify a filename\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
printf("Copying '%s' to '%s'\n", argv[1], kWorkFileName);
|
||||
|
||||
infp = CopyFile(kWorkFileName, srcfp);
|
||||
if (infp == nil) {
|
||||
fprintf(stderr, "Copy failed, bailing.\n");
|
||||
exit(1);
|
||||
}
|
||||
fclose(srcfp);
|
||||
fclose(infp);
|
||||
|
||||
cc = TwirlArchive(kWorkFileName);
|
||||
|
||||
exit(cc != 0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user