ciderpress/nufxlib/samples/TestExtract.c

474 lines
12 KiB
C

/*
* NuFX archive manipulation library
* Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the BSD License, see the file COPYING.LIB.
*
* Test extraction of individual threads in various ways. The net result
* of this is three files (out.file, out.fp, out.buf) that contain the
* result of writing all filenames in an archive to the same data sink.
*
* This gathers up information on the contents of the archive via a
* callback, and then emits all of the data at once.
*
* (This was originally written in C++, and converted to C after I repented.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "NufxLib.h"
#include "Common.h"
/*#define false 0*/
/*#define true (!false)*/
#define kHappySize 2408
/*
* ===========================================================================
* ArchiveRecord
* ===========================================================================
*/
/*
* Track an archive record.
*/
typedef struct ArchiveRecord {
char* filenameMOR;
NuRecordIdx recordIdx;
long numThreads;
NuThread* pThreads;
struct ArchiveRecord* pNext;
} ArchiveRecord;
/*
* Alloc a new ArchiveRecord.
*/
ArchiveRecord* ArchiveRecord_New(const NuRecord* pRecord)
{
ArchiveRecord* pArcRec = NULL;
pArcRec = malloc(sizeof(*pArcRec));
if (pArcRec == NULL)
return NULL;
if (pRecord->filenameMOR == NULL)
pArcRec->filenameMOR = strdup("<unknown>");
else
pArcRec->filenameMOR = strdup(pRecord->filenameMOR);
pArcRec->recordIdx = pRecord->recordIdx;
pArcRec->numThreads = NuRecordGetNumThreads(pRecord);
(void) NuRecordCopyThreads(pRecord, &pArcRec->pThreads);
pArcRec->pNext = NULL;
return pArcRec;
}
/*
* Free up an ArchiveRecord.
*/
void ArchiveRecord_Free(ArchiveRecord* pArcRec)
{
if (pArcRec == NULL)
return;
if (pArcRec->filenameMOR != NULL)
free(pArcRec->filenameMOR);
if (pArcRec->pThreads != NULL)
free(pArcRec->pThreads);
free(pArcRec);
}
/*
* Find a thread with a matching NuThreadID.
*/
const NuThread* ArchiveRecord_FindThreadByID(const ArchiveRecord* pArcRec,
NuThreadID threadID)
{
const NuThread* pThread;
int i;
for (i = 0; i < pArcRec->numThreads; i++) {
pThread = NuThreadGetByIdx(pArcRec->pThreads, i);
if (NuGetThreadID(pThread) == threadID)
return pThread;
}
return NULL;
}
const char* ArchiveRecord_GetFilename(const ArchiveRecord* pArcRec)
{
return pArcRec->filenameMOR;
}
NuRecordIdx ArchiveRecord_GetRecordIdx(const ArchiveRecord* pArcRec)
{
return pArcRec->recordIdx;
}
long ArchiveRecord_GetNumThreads(const ArchiveRecord* pArcRec)
{
return pArcRec->numThreads;
}
const NuThread* ArchiveRecord_GetThread(const ArchiveRecord* pArcRec, int idx)
{
if (idx < 0 || idx >= pArcRec->numThreads)
return NULL;
return NuThreadGetByIdx(pArcRec->pThreads, idx);
}
void ArchiveRecord_SetNext(ArchiveRecord* pArcRec, ArchiveRecord* pNextRec)
{
pArcRec->pNext = pNextRec;
}
ArchiveRecord* ArchiveRecord_GetNext(const ArchiveRecord* pArcRec)
{
return pArcRec->pNext;
}
/*
* ===========================================================================
* ArchiveData
* ===========================================================================
*/
/*
* A collection of records.
*/
typedef struct ArchiveData {
long numRecords;
ArchiveRecord* pRecordHead;
ArchiveRecord* pRecordTail;
} ArchiveData;
ArchiveData* ArchiveData_New(void)
{
ArchiveData* pArcData;
pArcData = malloc(sizeof(*pArcData));
if (pArcData == NULL)
return NULL;
pArcData->numRecords = 0;
pArcData->pRecordHead = pArcData->pRecordTail = NULL;
return pArcData;
}
void ArchiveData_Free(ArchiveData* pArcData)
{
ArchiveRecord* pNext;
if (pArcData == NULL)
return;
printf("*** Deleting %ld records!\n", pArcData->numRecords);
while (pArcData->pRecordHead != NULL) {
pNext = ArchiveRecord_GetNext(pArcData->pRecordHead);
ArchiveRecord_Free(pArcData->pRecordHead);
pArcData->pRecordHead = pNext;
}
free(pArcData);
}
ArchiveRecord* ArchiveData_GetRecordHead(const ArchiveData* pArcData)
{
return pArcData->pRecordHead;
}
/* add an ArchiveRecord to the list pointed at by ArchiveData */
void ArchiveData_AddRecord(ArchiveData* pArcData, ArchiveRecord* pRecord)
{
assert(pRecord != NULL);
assert((pArcData->pRecordHead == NULL && pArcData->pRecordTail == NULL) ||
(pArcData->pRecordHead != NULL && pArcData->pRecordTail != NULL));
if (pArcData->pRecordHead == NULL) {
/* first */
pArcData->pRecordHead = pArcData->pRecordTail = pRecord;
} else {
/* not first, add to end */
ArchiveRecord_SetNext(pArcData->pRecordTail, pRecord);
pArcData->pRecordTail = pRecord;
}
pArcData->numRecords++;
}
/* dump the contents of the ArchiveData to stdout */
void ArchiveData_DumpContents(const ArchiveData* pArcData)
{
ArchiveRecord* pArcRec;
pArcRec = pArcData->pRecordHead;
while (pArcRec != NULL) {
const NuThread* pThread;
int i, count;
printf("%5u '%s'\n",
ArchiveRecord_GetRecordIdx(pArcRec),
ArchiveRecord_GetFilename(pArcRec));
count = ArchiveRecord_GetNumThreads(pArcRec);
for (i = 0; i < count; i++) {
pThread = ArchiveRecord_GetThread(pArcRec, i);
printf(" %5u 0x%04x 0x%04x\n", pThread->threadIdx,
pThread->thThreadClass, pThread->thThreadKind);
}
pArcRec = ArchiveRecord_GetNext(pArcRec);
}
}
/*
* ===========================================================================
* Main stuff
* ===========================================================================
*/
/*
* Callback function to collect archive information.
*/
NuResult GatherContents(NuArchive* pArchive, void* vpRecord)
{
NuRecord* pRecord = (NuRecord*) vpRecord;
ArchiveData* pArchiveData = NULL;
ArchiveRecord* pArchiveRecord = ArchiveRecord_New(pRecord);
NuGetExtraData(pArchive, (void**)&pArchiveData);
assert(pArchiveData != NULL);
printf("*** Filename = '%s'\n",
pRecord->filenameMOR == NULL ?
"<unknown>" : pRecord->filenameMOR);
ArchiveData_AddRecord(pArchiveData, pArchiveRecord);
return kNuOK;
}
/*
* Copy the filename thread from every record to "pDataSink".
*/
NuError ReadAllFilenameThreads(NuArchive* pArchive, ArchiveData* pArchiveData,
NuDataSink* pDataSink)
{
NuError err = kNuErrNone;
ArchiveRecord* pArchiveRecord;
const NuThread* pThread;
pArchiveRecord = ArchiveData_GetRecordHead(pArchiveData);
while (pArchiveRecord != NULL) {
pThread = ArchiveRecord_FindThreadByID(pArchiveRecord,
kNuThreadIDFilename);
if (pThread != NULL) {
err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink);
if (err != kNuErrNone) {
fprintf(stderr, "*** Extract failed (%d)\n", err);
goto bail;
}
}
pArchiveRecord = ArchiveRecord_GetNext(pArchiveRecord);
}
bail:
return err;
}
/* extract every filename thread into a single file, overwriting each time */
NuError ExtractToFile(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
NuDataSink* pDataSink = NULL;
err = NuCreateDataSinkForFile(true, kNuConvertOff, "out.file", PATH_SEP,
&pDataSink);
if (err != kNuErrNone)
goto bail;
err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone)
goto bail;
bail:
(void) NuFreeDataSink(pDataSink);
if (err == kNuErrNone)
printf("*** File write complete\n");
return err;
}
/* extract every filename thread into a FILE*, appending */
NuError ExtractToFP(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
FILE* fp = NULL;
NuDataSink* pDataSink = NULL;
if ((fp = fopen("out.fp", kNuFileOpenWriteTrunc)) == NULL)
return kNuErrFileOpen;
err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone)
goto bail;
bail:
(void) NuFreeDataSink(pDataSink);
if (fp != NULL)
fclose(fp);
if (err == kNuErrNone)
printf("*** FP write complete\n");
return err;
}
/* extract every filename thread into a buffer, advancing as we go */
NuError ExtractToBuffer(NuArchive* pArchive, ArchiveData* pArchiveData)
{
NuError err;
uint8_t buffer[kHappySize];
NuDataSink* pDataSink = NULL;
uint32_t count;
err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, kHappySize,
&pDataSink);
if (err != kNuErrNone)
goto bail;
err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink);
if (err != kNuErrNone) {
if (err == kNuErrBufferOverrun)
fprintf(stderr, "*** Hey, buffer wasn't big enough!\n");
goto bail;
}
/* write the buffer to a file */
(void) NuDataSinkGetOutCount(pDataSink, &count);
if (count > 0) {
FILE* fp;
if ((fp = fopen("out.buf", kNuFileOpenWriteTrunc)) != NULL) {
printf("*** Writing %u bytes\n", count);
if (fwrite(buffer, count, 1, fp) != 1)
err = kNuErrFileWrite;
fclose(fp);
}
} else {
printf("*** No data found!\n");
}
bail:
(void) NuFreeDataSink(pDataSink);
return err;
}
/*
* Do file stuff.
*/
int DoFileStuff(const UNICHAR* filenameUNI)
{
NuError err;
NuArchive* pArchive = NULL;
ArchiveData* pArchiveData = ArchiveData_New();
err = NuOpenRO(filenameUNI, &pArchive);
if (err != kNuErrNone)
goto bail;
NuSetExtraData(pArchive, pArchiveData);
printf("*** Gathering contents!\n");
err = NuContents(pArchive, GatherContents);
if (err != kNuErrNone)
goto bail;
printf("*** Dumping contents!\n");
ArchiveData_DumpContents(pArchiveData);
err = ExtractToFile(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
err = ExtractToFP(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
err = ExtractToBuffer(pArchive, pArchiveData);
if (err != kNuErrNone)
goto bail;
bail:
if (err != kNuErrNone)
fprintf(stderr, "*** ERROR: got error %d\n", err);
if (pArchive != NULL) {
NuError err2 = NuClose(pArchive);
if (err == kNuErrNone && err2 != kNuErrNone)
err = err2;
}
ArchiveData_Free(pArchiveData);
return err;
}
/*
* Grab the name of an archive to read. If no name was provided, use stdin.
*/
int main(int argc, char** argv)
{
int32_t major, minor, bug;
const char* pBuildDate;
FILE* infp = NULL;
int cc;
(void) NuGetVersion(&major, &minor, &bug, &pBuildDate, NULL);
printf("Using NuFX lib %d.%d.%d built on or after %s\n",
major, minor, bug, pBuildDate);
if (argc == 2) {
infp = fopen(argv[1], kNuFileOpenReadOnly);
if (infp == NULL) {
perror("fopen failed");
exit(1);
}
} else {
fprintf(stderr, "ERROR: you have to specify a filename\n");
exit(2);
}
cc = DoFileStuff(argv[1]);
if (infp != NULL)
fclose(infp);
exit(cc != 0);
}