/* * 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. * * Debugging functions. These are omitted from the non-debug build. */ #include "NufxLibPriv.h" #if defined(DEBUG_MSGS) /* pull a string out of one of the static arrays */ #define GetStaticString(index, staticArray) ( \ (index) >= NELEM(staticArray) ? "" : staticArray[index] \ ) /* thread's thread_class */ static const char* gThreadClassNames[] = { "message_thread", "control_thread", "data_thread", "filename_thread", }; /* thread's thread_format */ static const char* gThreadFormatNames[] = { "uncompressed", "Huffman Squeeze", "dynamic LZW/1", "dynamic LZW/2", "12-bit LZC", "16-bit LZC", "deflate", "bzip2" }; /* days of the week */ static const char* gDayNames[] = { "[ null ]", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; /* months of the year */ static const char* gMonths[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; #define kNuDateOutputLen 64 /* file_sys_id values */ static const char* gFileSysIDs[] = { "Reserved/unknown ($00)", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", "Apple II Pascal", "Macintosh (HFS)", "Macintosh (MFS)", "LISA file system", "Apple CP/M", "Reserved 0x09", "MS-DOS", "High-Sierra", "ISO 9660", "AppleShare" }; /* * Convert a DateTime structure into something printable. * * The buffer passed in must hold at least kNuDateOutputLen bytes. * * Returns "buffer" for the benefit of printf() calls. */ static char* Nu_DebugDumpDate(const NuDateTime* pDateTime, char* buffer) { char* cp; /* is it valid? */ if (pDateTime->day > 30 || pDateTime->month > 11 || pDateTime->hour > 24 || pDateTime->minute > 59) { strcpy(buffer, " "); goto bail; } /* is it empty? */ if ((pDateTime->second | pDateTime->minute | pDateTime->hour | pDateTime->year | pDateTime->day | pDateTime->month | pDateTime->extra | pDateTime->weekDay) == 0) { strcpy(buffer, " [No Date] "); goto bail; } cp = buffer; /* only print weekDay if one was stored */ if (pDateTime->weekDay) { if (pDateTime->weekDay < NELEM(gDayNames)) sprintf(cp, "%s, ", gDayNames[pDateTime->weekDay]); else sprintf(cp, "??%d, ", pDateTime->weekDay); cp += strlen(cp); } sprintf(cp, "%02d-%s-%04d %02d:%02d:%02d", pDateTime->day+1, gMonths[pDateTime->month], pDateTime->year < 40 ? pDateTime->year + 2000 : pDateTime->year + 1900, pDateTime->hour, pDateTime->minute, pDateTime->second); bail: sprintf(buffer + strlen(buffer), " [s%d m%d h%d Y%d D%d M%d x%d w%d]", pDateTime->second, pDateTime->minute, pDateTime->hour, pDateTime->year, pDateTime->day, pDateTime->month, pDateTime->extra, pDateTime->weekDay); return buffer; } /* * Convert a buffer into a hexadecimal character string. * * The result will be 2x the size of the original, +1 for a null byte. */ static void ConvertToHexStr(const uint8_t* inBuf, int inLen, char* outBuf) { while (inLen--) { *outBuf++ = HexConv((*inBuf >> 4) & 0x0f); *outBuf++ = HexConv(*inBuf & 0x0f); inBuf++; } *outBuf = '\0'; } /* * Dump everything we know about pThread. */ void Nu_DebugDumpThread(const NuThread* pThread) { static const char* kInd = " "; NuThreadID threadID; const char* descr; Assert(pThread != NULL); printf("%sThreadClass: 0x%04x (%s)\n", kInd, pThread->thThreadClass, GetStaticString(pThread->thThreadClass, gThreadClassNames)); printf("%sThreadFormat: 0x%04x (%s)\n", kInd, pThread->thThreadFormat, GetStaticString(pThread->thThreadFormat, gThreadFormatNames)); threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind); switch (threadID) { case kNuThreadIDOldComment: descr = "old comment"; break; case kNuThreadIDComment: descr = "comment"; break; case kNuThreadIDIcon: descr = "icon"; break; case kNuThreadIDMkdir: descr = "mkdir"; break; case kNuThreadIDDataFork: descr = "data fork"; break; case kNuThreadIDDiskImage: descr = "disk image"; break; case kNuThreadIDRsrcFork: descr = "rsrc fork"; break; case kNuThreadIDFilename: descr = "filename"; break; default: descr = ""; break; } printf("%sThreadKind: 0x%04x (%s)\n", kInd, pThread->thThreadKind, descr); printf("%sThreadCRC: 0x%04x ThreadEOF: %u CompThreadEOF: %u\n", kInd, pThread->thThreadCRC, pThread->thThreadEOF, pThread->thCompThreadEOF); printf("%s*File data offset: %ld actualThreadEOF: %d\n", kInd, pThread->fileOffset, pThread->actualThreadEOF); } /* * Dump everything we know about pRecord, including its threads and ThreadMods. * * Changes to existing records are made to the "copy" set, not the "orig" * set. Pass in the "orig" copy in "pRecord", and optionally pass in the * "copy" set in "pXrefRecord" to glean data from both. */ static void Nu_DebugDumpRecord(NuArchive* pArchive, const NuRecord* pRecord, const NuRecord* pXrefRecord, Boolean isDeleted) { NuError err; /* dummy */ static const char* kInd = " "; char dateBuf[kNuDateOutputLen]; const NuThreadMod* pThreadMod; const NuThread* pThread; uint32_t idx; Assert(pRecord != NULL); /*printf("PTR: pRecord=0x%08lx pXrefRecord=0x%08lx\n", (long) pRecord, (long) pXrefRecord);*/ UNICHAR* filenameUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); printf("%s%s%sFilename: '%s' (idx=%u)\n", kInd, isDeleted ? "[DEL] " : "", pXrefRecord != NULL && pXrefRecord->pThreadMods != NULL ? "[MOD] " : "", filenameUNI == NULL ? "" : filenameUNI, pRecord->recordIdx); free(filenameUNI); printf("%sHeaderID: '%.4s' VersionNumber: 0x%04x HeaderCRC: 0x%04x\n", kInd, pRecord->recNufxID, pRecord->recVersionNumber, pRecord->recHeaderCRC); printf("%sAttribCount: %u TotalThreads: %u\n", kInd, pRecord->recAttribCount, pRecord->recTotalThreads); printf("%sFileSysID: %u (%s) FileSysInfo: 0x%04x ('%c')\n", kInd, pRecord->recFileSysID, GetStaticString(pRecord->recFileSysID, gFileSysIDs), pRecord->recFileSysInfo, NuGetSepFromSysInfo(pRecord->recFileSysInfo)); /* do something fancy for ProDOS? */ printf("%sFileType: 0x%08x ExtraType: 0x%08x Access: 0x%08x\n", kInd, pRecord->recFileType, pRecord->recExtraType, pRecord->recAccess); printf("%sCreateWhen: %s\n", kInd, Nu_DebugDumpDate(&pRecord->recCreateWhen, dateBuf)); printf("%sModWhen: %s\n", kInd, Nu_DebugDumpDate(&pRecord->recModWhen, dateBuf)); printf("%sArchiveWhen: %s\n", kInd, Nu_DebugDumpDate(&pRecord->recArchiveWhen, dateBuf)); printf("%sStorageType: %u OptionSize: %u FilenameLength: %u\n", kInd, pRecord->recStorageType, pRecord->recOptionSize, pRecord->recFilenameLength); if (pRecord->recOptionSize) { char* outBuf = Nu_Malloc(pArchive, pRecord->recOptionSize * 2 +1); BailAlloc(outBuf); Assert(pRecord->recOptionList != NULL); ConvertToHexStr(pRecord->recOptionList, pRecord->recOptionSize, outBuf); printf("%sOptionList: [%s]\n", kInd, outBuf); Nu_Free(pArchive, outBuf); } printf("%s*ExtraCount: %d RecFileOffset: %ld RecHeaderLength: %d\n", kInd, pRecord->extraCount, pRecord->fileOffset, pRecord->recHeaderLength); for (idx = 0; idx < pRecord->recTotalThreads; idx++) { Boolean isFake; isFake = (idx >= pRecord->recTotalThreads - pRecord->fakeThreads); pThread = Nu_GetThread(pRecord, idx); Assert(pThread != NULL); printf("%s--Thread #%u (idx=%u)%s\n", kInd, idx, pThread->threadIdx, isFake ? " [FAKE]" : ""); Nu_DebugDumpThread(pThread); } if (pXrefRecord != NULL) pThreadMod = pXrefRecord->pThreadMods; else pThreadMod = pRecord->pThreadMods; /* probably empty */ if (pThreadMod != NULL) printf("%s*ThreadMods -----\n", kInd); while (pThreadMod != NULL) { switch (pThreadMod->entry.kind) { case kNuThreadModAdd: printf("%s *-ThreadMod ADD 0x%08x 0x%04x (sourceType=%d)\n", kInd, pThreadMod->entry.add.threadID, pThreadMod->entry.add.threadFormat, Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource)); break; case kNuThreadModUpdate: printf("%s *-ThreadMod UPDATE %6d\n", kInd, pThreadMod->entry.update.threadIdx); break; case kNuThreadModDelete: printf("%s *-ThreadMod DELETE %6d\n", kInd, pThreadMod->entry.delete.threadIdx); break; case kNuThreadModUnknown: default: Assert(0); printf("%s++ThreadMod UNKNOWN\n", kInd); break; } pThreadMod = pThreadMod->pNext; } /*printf("%s*TotalLength: %ld TotalCompLength: %ld\n", kInd, pRecord->totalLength, pRecord->totalCompLength);*/ printf("%s*TotalCompLength: %u\n", kInd, pRecord->totalCompLength); printf("\n"); bail: return; } /* * Dump the records in a RecordSet. */ static void Nu_DebugDumpRecordSet(NuArchive* pArchive, const NuRecordSet* pRecordSet, const NuRecordSet* pXrefSet) { const NuRecord* pRecord; const NuRecord* pXrefRecord; Boolean doXref; long count; doXref = false; pXrefRecord = NULL; if (pXrefSet != NULL && Nu_RecordSet_GetLoaded(pXrefSet)) { pXrefRecord = Nu_RecordSet_GetListHead(pXrefSet); doXref = true; } /* dump every record, if we've loaded them */ count = Nu_RecordSet_GetNumRecords(pRecordSet); pRecord = Nu_RecordSet_GetListHead(pRecordSet); if (pRecord != NULL) { Assert(count != 0); while (count--) { Assert(pRecord != NULL); if (pXrefRecord != NULL && pRecord->recordIdx == pXrefRecord->recordIdx) { Nu_DebugDumpRecord(pArchive, pRecord, pXrefRecord, false); pXrefRecord = pXrefRecord->pNext; } else { Nu_DebugDumpRecord(pArchive, pRecord, NULL, doXref); } pRecord = pRecord->pNext; } } else { Assert(count == 0); } } /* * Dump the master header block. */ static void Nu_DebugDumpMH(const NuMasterHeader* pMasterHeader) { static const char* kInd = " "; char dateBuf1[kNuDateOutputLen]; Assert(pMasterHeader != NULL); printf("%sNufileID: '%.6s' MasterCRC: 0x%04x TotalRecords: %u\n", kInd, pMasterHeader->mhNufileID, pMasterHeader->mhMasterCRC, pMasterHeader->mhTotalRecords); printf("%sArchiveCreateWhen: %s\n", kInd, Nu_DebugDumpDate(&pMasterHeader->mhArchiveCreateWhen, dateBuf1)); printf("%sArchiveModWhen: %s\n", kInd, Nu_DebugDumpDate(&pMasterHeader->mhArchiveModWhen, dateBuf1)); printf("%sMasterVersion: %u MasterEOF: %u\n", kInd, pMasterHeader->mhMasterVersion, pMasterHeader->mhMasterEOF); } /* * Dump everything we know about pArchive. * * This will only print the records that we have seen so far. If the * application hasn't caused us to scan through all of the records in * the archive, then this won't be very interesting. This will never * show any records for streaming-mode archives. */ void Nu_DebugDumpAll(NuArchive* pArchive) { Assert(pArchive != NULL); printf("*Archive pathname: '%s'\n", pArchive->archivePathnameUNI); printf("*Archive type: %d\n", pArchive->archiveType); printf("*Header offset: %ld (junk offset=%ld)\n", pArchive->headerOffset, pArchive->junkOffset); printf("*Num records: %u orig, %u copy, %u new\n", Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet), Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet), Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet)); printf("*NuRecordIdx seed: %u NuRecordIdx next: %u\n", pArchive->recordIdxSeed, pArchive->nextRecordIdx); /* master header */ Nu_DebugDumpMH(&pArchive->masterHeader); printf(" *ORIG record set (x-ref with COPY):\n"); Nu_DebugDumpRecordSet(pArchive, &pArchive->origRecordSet, &pArchive->copyRecordSet); printf(" *NEW record set:\n"); Nu_DebugDumpRecordSet(pArchive, &pArchive->newRecordSet, NULL); if (!Nu_RecordSet_GetLoaded(&pArchive->origRecordSet) && !Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { printf("*** DEBUG: original records not loaded yet? ***\n"); } } #endif /*DEBUG_MSGS*/