mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 02:49:20 +00:00
891 lines
34 KiB
C
891 lines
34 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.
|
|
*
|
|
* External interface (types, defines, and function prototypes).
|
|
*/
|
|
#ifndef NUFXLIB_NUFXLIB_H
|
|
#define NUFXLIB_NUFXLIB_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
* NufxLib version number. Compare these values (which represent the
|
|
* version against which your application was compiled) to the values
|
|
* returned by NuGetVersion (representing the version against which
|
|
* your application is statically or dynamically linked). If the major
|
|
* number doesn't match exactly, an existing interface has changed and you
|
|
* should halt immediately. If the minor number from NuGetVersion is
|
|
* less, there may be new interfaces, new features, or bug fixes missing
|
|
* upon which your application depends, so you should halt immediately.
|
|
* (If the minor number is greater, there are new features, but your
|
|
* application will not be affected by them.)
|
|
*
|
|
* The "bug" version can usually be ignored, since it represents minor
|
|
* fixes. Unless, of course, your code depends upon that fix.
|
|
*/
|
|
#define kNuVersionMajor 3
|
|
#define kNuVersionMinor 1
|
|
#define kNuVersionBug 0
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Types
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Unicode character type. For Linux and Mac OS X, filenames use "narrow"
|
|
* characters and UTF-8 encoding, which allows them to use standard file I/O
|
|
* functions like fopen(). Windows uses UTF-16, which requires a different
|
|
* character type and an alternative set of I/O functions like _wfopen().
|
|
*
|
|
* The idea is that NufxLib API functions will operate on filenames with
|
|
* the OS dominant method, so on Windows the API accepts UTF-16. This
|
|
* definition is a bit like Windows TCHAR, but it's dependent on the OS, not
|
|
* on whether _MBCS or _UNICODE is defined.
|
|
*
|
|
* The app can include "Unichar.h" to get definitions for functions that
|
|
* switch between narrow and wide functions (e.g. "unistrlen()" becomes
|
|
* strlen() or wcslen() as appropriate).
|
|
*
|
|
* We switch based on _WIN32, because we're not really switching on
|
|
* filename-character size; the key issue is all the pesky wide I/O calls.
|
|
*/
|
|
#if defined(_WIN32)
|
|
// TODO: complete this
|
|
//# include <wchar.h>
|
|
//# define UNICHAR wchar_t
|
|
# define UNICHAR char
|
|
#else
|
|
# define UNICHAR char
|
|
#endif
|
|
|
|
/*
|
|
* Error values returned from functions.
|
|
*
|
|
* These are negative so that they don't conflict with system-defined
|
|
* errors (like ENOENT). A NuError can hold either.
|
|
*/
|
|
typedef enum NuError {
|
|
kNuErrNone = 0,
|
|
|
|
kNuErrGeneric = -1,
|
|
kNuErrInternal = -2,
|
|
kNuErrUsage = -3,
|
|
kNuErrSyntax = -4,
|
|
kNuErrMalloc = -5,
|
|
kNuErrInvalidArg = -6,
|
|
kNuErrBadStruct = -7,
|
|
kNuErrUnexpectedNil = -8,
|
|
kNuErrBusy = -9,
|
|
|
|
kNuErrSkipped = -10, /* processing skipped by request */
|
|
kNuErrAborted = -11, /* processing aborted by request */
|
|
kNuErrRename = -12, /* user wants to rename before extracting */
|
|
|
|
kNuErrFile = -20,
|
|
kNuErrFileOpen = -21,
|
|
kNuErrFileClose = -22,
|
|
kNuErrFileRead = -23,
|
|
kNuErrFileWrite = -24,
|
|
kNuErrFileSeek = -25,
|
|
kNuErrFileExists = -26, /* existed when it shouldn't */
|
|
kNuErrFileNotFound = -27, /* didn't exist when it should have */
|
|
kNuErrFileStat = -28, /* some sort of GetFileInfo failure */
|
|
kNuErrFileNotReadable = -29, /* bad access permissions */
|
|
|
|
kNuErrDirExists = -30, /* dir exists, don't need to create it */
|
|
kNuErrNotDir = -31, /* expected a dir, got a regular file */
|
|
kNuErrNotRegularFile = -32, /* expected regular file, got weirdness */
|
|
kNuErrDirCreate = -33, /* unable to create a directory */
|
|
kNuErrOpenDir = -34, /* error opening directory */
|
|
kNuErrReadDir = -35, /* error reading directory */
|
|
kNuErrFileSetDate = -36, /* unable to set file date */
|
|
kNuErrFileSetAccess = -37, /* unable to set file access permissions */
|
|
kNuErrFileAccessDenied = -38, /* equivalent to EACCES */
|
|
|
|
kNuErrNotNuFX = -40, /* 'NuFile' missing; not a NuFX archive? */
|
|
kNuErrBadMHVersion = -41, /* bad master header version */
|
|
kNuErrRecHdrNotFound = -42, /* 'NuFX' missing; corrupted archive? */
|
|
kNuErrNoRecords = -43, /* archive doesn't have any records */
|
|
kNuErrBadRecord = -44, /* something about the record looked bad */
|
|
kNuErrBadMHCRC = -45, /* bad master header CRC */
|
|
kNuErrBadRHCRC = -46, /* bad record header CRC */
|
|
kNuErrBadThreadCRC = -47, /* bad thread header CRC */
|
|
kNuErrBadDataCRC = -48, /* bad CRC detected in the data */
|
|
|
|
kNuErrBadFormat = -50, /* compression type not supported */
|
|
kNuErrBadData = -51, /* expansion func didn't like input */
|
|
kNuErrBufferOverrun = -52, /* overflowed a user buffer */
|
|
kNuErrBufferUnderrun = -53, /* underflowed a user buffer */
|
|
kNuErrOutMax = -54, /* output limit exceeded */
|
|
|
|
kNuErrNotFound = -60, /* (generic) search unsuccessful */
|
|
kNuErrRecordNotFound = -61, /* search for specific record failed */
|
|
kNuErrRecIdxNotFound = -62, /* search by NuRecordIdx failed */
|
|
kNuErrThreadIdxNotFound = -63, /* search by NuThreadIdx failed */
|
|
kNuErrThreadIDNotFound = -64, /* search by NuThreadID failed */
|
|
kNuErrRecNameNotFound = -65, /* search by storageName failed */
|
|
kNuErrRecordExists = -66, /* found existing record with same name */
|
|
|
|
kNuErrAllDeleted = -70, /* attempt to delete everything */
|
|
kNuErrArchiveRO = -71, /* archive is open in read-only mode */
|
|
kNuErrModRecChange = -72, /* tried to change modified record */
|
|
kNuErrModThreadChange = -73, /* tried to change modified thread */
|
|
kNuErrThreadAdd = -74, /* adding that thread creates a conflict */
|
|
kNuErrNotPreSized = -75, /* tried to update a non-pre-sized thread */
|
|
kNuErrPreSizeOverflow = -76, /* too much data */
|
|
kNuErrInvalidFilename = -77, /* invalid filename */
|
|
|
|
kNuErrLeadingFssep = -80, /* names in archives must not start w/sep */
|
|
kNuErrNotNewer = -81, /* item same age or older than existing */
|
|
kNuErrDuplicateNotFound = -82, /* "must overwrite" was set, but item DNE */
|
|
kNuErrDamaged = -83, /* original archive may have been damaged */
|
|
|
|
kNuErrIsBinary2 = -90, /* this looks like a Binary II archive */
|
|
|
|
kNuErrUnknownFeature =-100, /* attempt to test unknown feature */
|
|
kNuErrUnsupFeature = -101, /* feature not supported */
|
|
} NuError;
|
|
|
|
/*
|
|
* Return values from callback functions.
|
|
*/
|
|
typedef enum NuResult {
|
|
kNuOK = 0,
|
|
kNuSkip = 1,
|
|
kNuAbort = 2,
|
|
/*kNuAbortAll = 3,*/
|
|
kNuRetry = 4,
|
|
kNuIgnore = 5,
|
|
kNuRename = 6,
|
|
kNuOverwrite = 7
|
|
} NuResult;
|
|
|
|
/*
|
|
* NuRecordIdxs are assigned to records in an archive. You may assume that
|
|
* the values are unique, but that is all.
|
|
*/
|
|
typedef uint32_t NuRecordIdx;
|
|
|
|
/*
|
|
* NuThreadIdxs are assigned to threads within a record. Again, you may
|
|
* assume that the values are unique within a record, but that is all.
|
|
*/
|
|
typedef uint32_t NuThreadIdx;
|
|
|
|
/*
|
|
* Thread ID, a combination of thread_class and thread_kind. Standard
|
|
* values have explicit identifiers.
|
|
*/
|
|
typedef uint32_t NuThreadID;
|
|
#define NuMakeThreadID(class, kind) /* construct a NuThreadID */ \
|
|
((uint32_t)(class) << 16 | (uint32_t)(kind))
|
|
#define NuGetThreadID(pThread) /* pull NuThreadID out of NuThread */ \
|
|
(NuMakeThreadID((pThread)->thThreadClass, (pThread)->thThreadKind))
|
|
#define NuThreadIDGetClass(threadID) /* get threadClass from NuThreadID */ \
|
|
((uint16_t) ((uint32_t)(threadID) >> 16))
|
|
#define NuThreadIDGetKind(threadID) /* get threadKind from NuThreadID */ \
|
|
((uint16_t) ((threadID) & 0xffff))
|
|
#define kNuThreadClassMessage 0x0000
|
|
#define kNuThreadClassControl 0x0001
|
|
#define kNuThreadClassData 0x0002
|
|
#define kNuThreadClassFilename 0x0003
|
|
#define kNuThreadKindDataFork 0x0000 /* when class=data */
|
|
#define kNuThreadKindDiskImage 0x0001 /* when class=data */
|
|
#define kNuThreadKindRsrcFork 0x0002 /* when class=data */
|
|
#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000)
|
|
#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001)
|
|
#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002)
|
|
#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000)
|
|
#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindDataFork)
|
|
#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, kNuThreadKindDiskImage)
|
|
#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindRsrcFork)
|
|
#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000)
|
|
#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff)
|
|
|
|
/* enumerate the possible values for thThreadFormat */
|
|
typedef enum NuThreadFormat {
|
|
kNuThreadFormatUncompressed = 0x0000,
|
|
kNuThreadFormatHuffmanSQ = 0x0001,
|
|
kNuThreadFormatLZW1 = 0x0002,
|
|
kNuThreadFormatLZW2 = 0x0003,
|
|
kNuThreadFormatLZC12 = 0x0004,
|
|
kNuThreadFormatLZC16 = 0x0005,
|
|
kNuThreadFormatDeflate = 0x0006, /* NOTE: not in NuFX standard */
|
|
kNuThreadFormatBzip2 = 0x0007, /* NOTE: not in NuFX standard */
|
|
} NuThreadFormat;
|
|
|
|
|
|
/* extract the filesystem separator char from the "file_sys_info" field */
|
|
#define NuGetSepFromSysInfo(sysInfo) \
|
|
((UNICHAR) ((sysInfo) & 0xff))
|
|
/* return a file_sys_info with a replaced filesystem separator */
|
|
#define NuSetSepInSysInfo(sysInfo, newSep) \
|
|
((uint16_t) (((sysInfo) & 0xff00) | ((newSep) & 0xff)) )
|
|
|
|
/* GS/OS-defined file system identifiers; sadly, UNIX is not among them */
|
|
typedef enum NuFileSysID {
|
|
kNuFileSysUnknown = 0, /* NuFX spec says use this */
|
|
kNuFileSysProDOS = 1,
|
|
kNuFileSysDOS33 = 2,
|
|
kNuFileSysDOS32 = 3,
|
|
kNuFileSysPascal = 4,
|
|
kNuFileSysMacHFS = 5,
|
|
kNuFileSysMacMFS = 6,
|
|
kNuFileSysLisa = 7,
|
|
kNuFileSysCPM = 8,
|
|
kNuFileSysCharFST = 9,
|
|
kNuFileSysMSDOS = 10,
|
|
kNuFileSysHighSierra = 11,
|
|
kNuFileSysISO9660 = 12,
|
|
kNuFileSysAppleShare = 13
|
|
} NuFileSysID;
|
|
|
|
/* simplified definition of storage types */
|
|
typedef enum NuStorageType {
|
|
kNuStorageUnknown = 0, /* (used by ProDOS for deleted files) */
|
|
kNuStorageSeedling = 1, /* <= 512 bytes */
|
|
kNuStorageSapling = 2, /* < 128KB */
|
|
kNuStorageTree = 3, /* < 16MB */
|
|
kNuStoragePascalVol = 4, /* (embedded pascal volume; rare) */
|
|
kNuStorageExtended = 5, /* forked (any size) */
|
|
kNuStorageDirectory = 13, /* directory */
|
|
kNuStorageSubdirHeader = 14, /* (only used in subdir headers) */
|
|
kNuStorageVolumeHeader = 15, /* (only used in volume dir header) */
|
|
} NuStorageType;
|
|
|
|
/* bit flags for NuOpenRW */
|
|
enum {
|
|
kNuOpenCreat = 0x0001,
|
|
kNuOpenExcl = 0x0002
|
|
};
|
|
|
|
|
|
/*
|
|
* The actual NuArchive structure is opaque, and should only be visible
|
|
* to the library. We define it here as an ambiguous struct.
|
|
*/
|
|
typedef struct NuArchive NuArchive;
|
|
|
|
/*
|
|
* Generic callback prototype.
|
|
*/
|
|
typedef NuResult (*NuCallback)(NuArchive* pArchive, void* args);
|
|
|
|
/*
|
|
* Parameters that affect archive operations.
|
|
*/
|
|
typedef enum NuValueID {
|
|
kNuValueInvalid = 0,
|
|
kNuValueIgnoreCRC = 1,
|
|
kNuValueDataCompression = 2,
|
|
kNuValueDiscardWrapper = 3,
|
|
kNuValueEOL = 4,
|
|
kNuValueConvertExtractedEOL = 5,
|
|
kNuValueOnlyUpdateOlder = 6,
|
|
kNuValueAllowDuplicates = 7,
|
|
kNuValueHandleExisting = 8,
|
|
kNuValueModifyOrig = 9,
|
|
kNuValueMimicSHK = 10,
|
|
kNuValueMaskDataless = 11,
|
|
kNuValueStripHighASCII = 12,
|
|
kNuValueJunkSkipMax = 13,
|
|
kNuValueIgnoreLZW2Len = 14,
|
|
kNuValueHandleBadMac = 15
|
|
} NuValueID;
|
|
typedef uint32_t NuValue;
|
|
|
|
/*
|
|
* Enumerated values for things you pass in a NuValue.
|
|
*/
|
|
enum NuValueValue {
|
|
/* for the truly retentive */
|
|
kNuValueFalse = 0,
|
|
kNuValueTrue = 1,
|
|
|
|
/* for kNuValueDataCompression */
|
|
kNuCompressNone = 10,
|
|
kNuCompressSQ = 11,
|
|
kNuCompressLZW1 = 12,
|
|
kNuCompressLZW2 = 13,
|
|
kNuCompressLZC12 = 14,
|
|
kNuCompressLZC16 = 15,
|
|
kNuCompressDeflate = 16,
|
|
kNuCompressBzip2 = 17,
|
|
|
|
/* for kNuValueEOL */
|
|
kNuEOLUnknown = 50,
|
|
kNuEOLCR = 51,
|
|
kNuEOLLF = 52,
|
|
kNuEOLCRLF = 53,
|
|
|
|
/* for kNuValueConvertExtractedEOL */
|
|
kNuConvertOff = 60,
|
|
kNuConvertOn = 61,
|
|
kNuConvertAuto = 62,
|
|
|
|
/* for kNuValueHandleExisting */
|
|
kNuMaybeOverwrite = 90,
|
|
kNuNeverOverwrite = 91,
|
|
kNuAlwaysOverwrite = 93,
|
|
kNuMustOverwrite = 94
|
|
};
|
|
|
|
|
|
/*
|
|
* Pull out archive attributes.
|
|
*/
|
|
typedef enum NuAttrID {
|
|
kNuAttrInvalid = 0,
|
|
kNuAttrArchiveType = 1,
|
|
kNuAttrNumRecords = 2,
|
|
kNuAttrHeaderOffset = 3,
|
|
kNuAttrJunkOffset = 4,
|
|
} NuAttrID;
|
|
typedef uint32_t NuAttr;
|
|
|
|
/*
|
|
* Archive types.
|
|
*/
|
|
typedef enum NuArchiveType {
|
|
kNuArchiveUnknown, /* .??? */
|
|
kNuArchiveNuFX, /* .SHK (sometimes .SDK) */
|
|
kNuArchiveNuFXInBNY, /* .BXY */
|
|
kNuArchiveNuFXSelfEx, /* .SEA */
|
|
kNuArchiveNuFXSelfExInBNY, /* .BSE */
|
|
|
|
kNuArchiveBNY /* .BNY, .BQY - not supported */
|
|
} NuArchiveType;
|
|
|
|
|
|
/*
|
|
* Some common values for "locked" and "unlocked". Under ProDOS each bit
|
|
* can be set independently, so don't use these defines to *interpret*
|
|
* what you see. They're reasonable things to *set* the access field to.
|
|
*
|
|
* The defined bits are:
|
|
* 0x80 'D' destroy enabled
|
|
* 0x40 'N' rename enabled
|
|
* 0x20 'B' file needs to be backed up
|
|
* 0x10 (reserved, must be zero)
|
|
* 0x08 (reserved, must be zero)
|
|
* 0x04 'I' file is invisible
|
|
* 0x02 'W' write enabled
|
|
* 0x01 'R' read enabled
|
|
*/
|
|
#define kNuAccessLocked 0x21
|
|
#define kNuAccessUnlocked 0xe3
|
|
|
|
|
|
/*
|
|
* NuFlush result flags.
|
|
*/
|
|
#define kNuFlushSucceeded (1L)
|
|
#define kNuFlushAborted (1L << 1)
|
|
#define kNuFlushCorrupted (1L << 2)
|
|
#define kNuFlushReadOnly (1L << 3)
|
|
#define kNuFlushInaccessible (1L << 4)
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* NuFX archive defintions
|
|
* ===========================================================================
|
|
*/
|
|
|
|
typedef struct NuThreadMod NuThreadMod; /* dummy def for internal struct */
|
|
typedef union NuDataSource NuDataSource; /* dummy def for internal struct */
|
|
typedef union NuDataSink NuDataSink; /* dummy def for internal struct */
|
|
|
|
/*
|
|
* NuFX Date/Time structure; same as TimeRec from IIgs "misctool.h".
|
|
*/
|
|
typedef struct NuDateTime {
|
|
uint8_t second; /* 0-59 */
|
|
uint8_t minute; /* 0-59 */
|
|
uint8_t hour; /* 0-23 */
|
|
uint8_t year; /* year - 1900 */
|
|
uint8_t day; /* 0-30 */
|
|
uint8_t month; /* 0-11 */
|
|
uint8_t extra; /* (must be zero) */
|
|
uint8_t weekDay; /* 1-7 (1=sunday) */
|
|
} NuDateTime;
|
|
|
|
/*
|
|
* NuFX "thread" definition.
|
|
*
|
|
* Guaranteed not to have pointers in it. Can be copied with memcpy or
|
|
* assignment.
|
|
*/
|
|
typedef struct NuThread {
|
|
/* from the archive */
|
|
uint16_t thThreadClass;
|
|
NuThreadFormat thThreadFormat;
|
|
uint16_t thThreadKind;
|
|
uint16_t thThreadCRC; /* comp or uncomp data; see rec vers */
|
|
uint32_t thThreadEOF;
|
|
uint32_t thCompThreadEOF;
|
|
|
|
/* extra goodies */
|
|
NuThreadIdx threadIdx;
|
|
uint32_t actualThreadEOF; /* disk images might be off */
|
|
long fileOffset; /* fseek offset to data in shk */
|
|
|
|
/* internal use only */
|
|
uint16_t used; /* mark as uninteresting */
|
|
} NuThread;
|
|
|
|
/*
|
|
* NuFX "record" definition.
|
|
*
|
|
* (Note to developers: update Nu_AddRecord if this changes.)
|
|
*
|
|
* The filenames are in Mac OS Roman format. It's arguable whether MOR
|
|
* strings should be part of the interface at all. However, the API
|
|
* pre-dates the inclusion of Unicode support, and I'm leaving it alone.
|
|
*/
|
|
#define kNufxIDLen 4 /* len of 'NuFX' with funky MSBs */
|
|
#define kNuReasonableAttribCount 256
|
|
#define kNuReasonableFilenameLen 1024
|
|
#define kNuReasonableTotalThreads 16
|
|
#define kNuMaxRecordVersion 3 /* max we can handle */
|
|
#define kNuOurRecordVersion 3 /* what we write */
|
|
typedef struct NuRecord {
|
|
/* version 0+ */
|
|
uint8_t recNufxID[kNufxIDLen];
|
|
uint16_t recHeaderCRC;
|
|
uint16_t recAttribCount;
|
|
uint16_t recVersionNumber;
|
|
uint32_t recTotalThreads;
|
|
NuFileSysID recFileSysID;
|
|
uint16_t recFileSysInfo;
|
|
uint32_t recAccess;
|
|
uint32_t recFileType;
|
|
uint32_t recExtraType;
|
|
uint16_t recStorageType; /* NuStorage*,file_sys_block_size */
|
|
NuDateTime recCreateWhen;
|
|
NuDateTime recModWhen;
|
|
NuDateTime recArchiveWhen;
|
|
|
|
/* option lists only in version 1+ */
|
|
uint16_t recOptionSize;
|
|
uint8_t* recOptionList; /* NULL if v0 or recOptionSize==0 */
|
|
|
|
/* data specified by recAttribCount, not accounted for by option list */
|
|
int32_t extraCount;
|
|
uint8_t* extraBytes;
|
|
|
|
uint16_t recFilenameLength; /* usually zero */
|
|
char* recFilenameMOR; /* doubles as disk volume_name */
|
|
|
|
/* extra goodies; "dirtyHeader" does not apply to anything below */
|
|
NuRecordIdx recordIdx; /* session-unique record index */
|
|
char* threadFilenameMOR; /* extracted from filename thread */
|
|
char* newFilenameMOR; /* memorized during "add file" call */
|
|
const char* filenameMOR; /* points at recFilen or threadFilen */
|
|
uint32_t recHeaderLength; /* size of rec hdr, incl thread hdrs */
|
|
uint32_t totalCompLength; /* total len of data in archive file */
|
|
uint32_t fakeThreads; /* used by "MaskDataless" */
|
|
int isBadMac; /* malformed "bad mac" header */
|
|
|
|
long fileOffset; /* file offset of record header */
|
|
|
|
/* use provided interface to access this */
|
|
struct NuThread* pThreads; /* ptr to thread array */
|
|
|
|
/* private -- things the application shouldn't look at */
|
|
struct NuRecord* pNext; /* used internally */
|
|
NuThreadMod* pThreadMods; /* used internally */
|
|
short dirtyHeader; /* set in "copy" when hdr fields uptd */
|
|
short dropRecFilename; /* if set, we're dropping this name */
|
|
} NuRecord;
|
|
|
|
/*
|
|
* NuFX "master header" definition.
|
|
*
|
|
* The "mhReserved2" entry doesn't appear in my copy of the $e0/8002 File
|
|
* Type Note, but as best as I can recall the MH block must be 48 bytes.
|
|
*/
|
|
#define kNufileIDLen 6 /* length of 'NuFile' with funky MSBs */
|
|
#define kNufileMasterReserved1Len 8
|
|
#define kNufileMasterReserved2Len 6
|
|
#define kNuMaxMHVersion 2 /* max we can handle */
|
|
#define kNuOurMHVersion 2 /* what we write */
|
|
typedef struct NuMasterHeader {
|
|
uint8_t mhNufileID[kNufileIDLen];
|
|
uint16_t mhMasterCRC;
|
|
uint32_t mhTotalRecords;
|
|
NuDateTime mhArchiveCreateWhen;
|
|
NuDateTime mhArchiveModWhen;
|
|
uint16_t mhMasterVersion;
|
|
uint8_t mhReserved1[kNufileMasterReserved1Len];
|
|
uint32_t mhMasterEOF;
|
|
uint8_t mhReserved2[kNufileMasterReserved2Len];
|
|
|
|
/* private -- internal use only */
|
|
short isValid;
|
|
} NuMasterHeader;
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Misc declarations
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Record attributes that can be changed with NuSetRecordAttr. This is
|
|
* a small subset of the full record.
|
|
*/
|
|
typedef struct NuRecordAttr {
|
|
NuFileSysID fileSysID;
|
|
/*uint16_t fileSysInfo;*/
|
|
uint32_t access;
|
|
uint32_t fileType;
|
|
uint32_t extraType;
|
|
NuDateTime createWhen;
|
|
NuDateTime modWhen;
|
|
NuDateTime archiveWhen;
|
|
} NuRecordAttr;
|
|
|
|
/*
|
|
* Some additional details about a file.
|
|
*
|
|
* Ideally (from an API cleanliness perspective) the storage name would
|
|
* be passed around as UTF-8 and converted internally. Passing it as
|
|
* MOR required fewer changes to the library, and allows us to avoid
|
|
* having to deal with illegal characters.
|
|
*/
|
|
typedef struct NuFileDetails {
|
|
/* used during AddFile call */
|
|
NuThreadID threadID; /* data, rsrc, disk img? */
|
|
const void* origName; /* arbitrary pointer, usually a string */
|
|
|
|
/* these go straight into the NuRecord */
|
|
const char* storageNameMOR;
|
|
NuFileSysID fileSysID;
|
|
uint16_t fileSysInfo;
|
|
uint32_t access;
|
|
uint32_t fileType;
|
|
uint32_t extraType;
|
|
uint16_t storageType; /* use Unknown, or disk block size */
|
|
NuDateTime createWhen;
|
|
NuDateTime modWhen;
|
|
NuDateTime archiveWhen;
|
|
} NuFileDetails;
|
|
|
|
|
|
/*
|
|
* Passed into the SelectionFilter callback.
|
|
*/
|
|
typedef struct NuSelectionProposal {
|
|
const NuRecord* pRecord;
|
|
const NuThread* pThread;
|
|
} NuSelectionProposal;
|
|
|
|
/*
|
|
* Passed into the OutputPathnameFilter callback.
|
|
*/
|
|
typedef struct NuPathnameProposal {
|
|
const UNICHAR* pathnameUNI;
|
|
UNICHAR filenameSeparator;
|
|
const NuRecord* pRecord;
|
|
const NuThread* pThread;
|
|
|
|
const UNICHAR* newPathnameUNI;
|
|
UNICHAR newFilenameSeparator;
|
|
/*NuThreadID newStorage;*/
|
|
NuDataSink* newDataSink;
|
|
} NuPathnameProposal;
|
|
|
|
|
|
/* used by error handler and progress updater to indicate what we're doing */
|
|
typedef enum NuOperation {
|
|
kNuOpUnknown = 0,
|
|
kNuOpAdd,
|
|
kNuOpExtract,
|
|
kNuOpTest,
|
|
kNuOpDelete, /* not used for progress updates */
|
|
kNuOpContents /* not used for progress updates */
|
|
} NuOperation;
|
|
|
|
/* state of progress when adding or extracting */
|
|
typedef enum NuProgressState {
|
|
kNuProgressPreparing, /* not started yet */
|
|
kNuProgressOpening, /* opening files */
|
|
|
|
kNuProgressAnalyzing, /* analyzing data */
|
|
kNuProgressCompressing, /* compressing data */
|
|
kNuProgressStoring, /* storing (no compression) data */
|
|
kNuProgressExpanding, /* expanding data */
|
|
kNuProgressCopying, /* copying data (in or out) */
|
|
|
|
kNuProgressDone, /* all done, success */
|
|
kNuProgressSkipped, /* all done, we skipped this one */
|
|
kNuProgressAborted, /* all done, user cancelled the operation */
|
|
kNuProgressFailed /* all done, failure */
|
|
} NuProgressState;
|
|
|
|
/*
|
|
* Passed into the ProgressUpdater callback. All pointers become
|
|
* invalid when the callback returns.
|
|
*
|
|
* [ Thought for the day: add an optional flag that causes us to only
|
|
* call the progressFunc when the "percentComplete" changes by more
|
|
* than a specified amount. ]
|
|
*/
|
|
typedef struct NuProgressData {
|
|
/* what are we doing */
|
|
NuOperation operation;
|
|
/* what specifically are we doing */
|
|
NuProgressState state;
|
|
/* how far along are we */
|
|
short percentComplete; /* 0-100 */
|
|
|
|
/* original pathname (in archive for expand, on disk for compress) */
|
|
const UNICHAR* origPathnameUNI;
|
|
/* processed pathname (PathnameFilter for expand, in-record for compress) */
|
|
const UNICHAR* pathnameUNI;
|
|
/* basename of "pathname" (for convenience) */
|
|
const UNICHAR* filenameUNI;
|
|
/* pointer to the record we're expanding from */
|
|
const NuRecord* pRecord;
|
|
|
|
uint32_t uncompressedLength; /* size of uncompressed data */
|
|
uint32_t uncompressedProgress; /* #of bytes in/out */
|
|
|
|
struct {
|
|
NuThreadFormat threadFormat; /* compression being applied */
|
|
} compress;
|
|
|
|
struct {
|
|
uint32_t totalCompressedLength; /* all "data" threads */
|
|
uint32_t totalUncompressedLength;
|
|
|
|
/*uint32_t compressedLength; * size of compressed data */
|
|
/*uint32_t compressedProgress; * #of compressed bytes in/out*/
|
|
const NuThread* pThread; /* thread we're working on */
|
|
NuValue convertEOL; /* set if LF/CR conv is on */
|
|
} expand;
|
|
|
|
/* pay no attention */
|
|
NuCallback progressFunc;
|
|
} NuProgressData;
|
|
|
|
/*
|
|
* Passed into the ErrorHandler callback.
|
|
*/
|
|
typedef struct NuErrorStatus {
|
|
NuOperation operation; /* were we adding, extracting, ?? */
|
|
NuError err; /* library error code */
|
|
int sysErr; /* system error code, if applicable */
|
|
const UNICHAR* message; /* (optional) message to user */
|
|
const NuRecord* pRecord; /* relevant record, if any */
|
|
const UNICHAR* pathnameUNI; /* problematic pathname, if any */
|
|
const void* origPathname; /* original pathname ref, if any */
|
|
UNICHAR filenameSeparator; /* fssep for pathname, if any */
|
|
/*char origArchiveTouched;*/
|
|
|
|
char canAbort; /* give option to abort */
|
|
/*char canAbortAll;*/ /* abort + discard all recent changes */
|
|
char canRetry; /* give option to retry same op */
|
|
char canIgnore; /* give option to ignore error */
|
|
char canSkip; /* give option to skip this file/rec */
|
|
char canRename; /* give option to rename file */
|
|
char canOverwrite; /* give option to overwrite file */
|
|
} NuErrorStatus;
|
|
|
|
/*
|
|
* Error message callback gets one of these.
|
|
*/
|
|
typedef struct NuErrorMessage {
|
|
const char* message; /* the message itself (UTF-8) */
|
|
NuError err; /* relevant error code (may be none) */
|
|
short isDebug; /* set for debug-only messages */
|
|
|
|
/* these identify where the message originated if lib built w/debug set */
|
|
const char* file; /* source file (UTF-8) */
|
|
int line; /* line number */
|
|
const char* function; /* function name (might be NULL) */
|
|
} NuErrorMessage;
|
|
|
|
|
|
/*
|
|
* Options for the NuTestFeature function.
|
|
*/
|
|
typedef enum NuFeature {
|
|
kNuFeatureUnknown = 0,
|
|
|
|
kNuFeatureCompressSQ = 1, /* kNuThreadFormatHuffmanSQ */
|
|
kNuFeatureCompressLZW = 2, /* kNuThreadFormatLZW1 and LZW2 */
|
|
kNuFeatureCompressLZC = 3, /* kNuThreadFormatLZC12 and LZC16 */
|
|
kNuFeatureCompressDeflate = 4, /* kNuThreadFormatDeflate */
|
|
kNuFeatureCompressBzip2 = 5, /* kNuThreadFormatBzip2 */
|
|
} NuFeature;
|
|
|
|
|
|
/*
|
|
* ===========================================================================
|
|
* Function prototypes
|
|
* ===========================================================================
|
|
*/
|
|
|
|
/*
|
|
* Win32 dll magic.
|
|
*/
|
|
#if defined(_WIN32)
|
|
# include <windows.h>
|
|
# if defined(NUFXLIB_EXPORTS)
|
|
/* building the NufxLib DLL */
|
|
# define NUFXLIB_API __declspec(dllexport)
|
|
# elif defined (NUFXLIB_DLL)
|
|
/* building to link against the NufxLib DLL */
|
|
# define NUFXLIB_API __declspec(dllimport)
|
|
# else
|
|
/* using static libs */
|
|
# define NUFXLIB_API
|
|
# endif
|
|
#else
|
|
/* not using Win32... hooray! */
|
|
# define NUFXLIB_API
|
|
#endif
|
|
|
|
/* streaming and non-streaming read-only interfaces */
|
|
NUFXLIB_API NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive);
|
|
NUFXLIB_API NuError NuContents(NuArchive* pArchive, NuCallback contentFunc);
|
|
NUFXLIB_API NuError NuExtract(NuArchive* pArchive);
|
|
NUFXLIB_API NuError NuTest(NuArchive* pArchive);
|
|
|
|
/* strictly non-streaming read-only interfaces */
|
|
NUFXLIB_API NuError NuOpenRO(const UNICHAR* archivePathnameUNI,
|
|
NuArchive** ppArchive);
|
|
NUFXLIB_API NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx);
|
|
NUFXLIB_API NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx,
|
|
NuDataSink* pDataSink);
|
|
NUFXLIB_API NuError NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx);
|
|
NUFXLIB_API NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx,
|
|
const NuRecord** ppRecord);
|
|
NUFXLIB_API NuError NuGetRecordIdxByName(NuArchive* pArchive,
|
|
const char* nameMOR, NuRecordIdx* pRecordIdx);
|
|
NUFXLIB_API NuError NuGetRecordIdxByPosition(NuArchive* pArchive,
|
|
uint32_t position, NuRecordIdx* pRecordIdx);
|
|
|
|
/* read/write interfaces */
|
|
NUFXLIB_API NuError NuOpenRW(const UNICHAR* archivePathnameUNI,
|
|
const UNICHAR* tempPathnameUNI, uint32_t flags,
|
|
NuArchive** ppArchive);
|
|
NUFXLIB_API NuError NuFlush(NuArchive* pArchive, uint32_t* pStatusFlags);
|
|
NUFXLIB_API NuError NuAddRecord(NuArchive* pArchive,
|
|
const NuFileDetails* pFileDetails, NuRecordIdx* pRecordIdx);
|
|
NUFXLIB_API NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx,
|
|
NuThreadID threadID, NuDataSource* pDataSource,
|
|
NuThreadIdx* pThreadIdx);
|
|
NUFXLIB_API NuError NuAddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI,
|
|
const NuFileDetails* pFileDetails, short fromRsrcFork,
|
|
NuRecordIdx* pRecordIdx);
|
|
NUFXLIB_API NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx,
|
|
const char* pathnameMOR, char fssep);
|
|
NUFXLIB_API NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx,
|
|
const NuRecordAttr* pRecordAttr);
|
|
NUFXLIB_API NuError NuUpdatePresizedThread(NuArchive* pArchive,
|
|
NuThreadIdx threadIdx, NuDataSource* pDataSource, int32_t* pMaxLen);
|
|
NUFXLIB_API NuError NuDelete(NuArchive* pArchive);
|
|
NUFXLIB_API NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx);
|
|
NUFXLIB_API NuError NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx);
|
|
|
|
/* general interfaces */
|
|
NUFXLIB_API NuError NuClose(NuArchive* pArchive);
|
|
NUFXLIB_API NuError NuAbort(NuArchive* pArchive);
|
|
NUFXLIB_API NuError NuGetMasterHeader(NuArchive* pArchive,
|
|
const NuMasterHeader** ppMasterHeader);
|
|
NUFXLIB_API NuError NuGetExtraData(NuArchive* pArchive, void** ppData);
|
|
NUFXLIB_API NuError NuSetExtraData(NuArchive* pArchive, void* pData);
|
|
NUFXLIB_API NuError NuGetValue(NuArchive* pArchive, NuValueID ident,
|
|
NuValue* pValue);
|
|
NUFXLIB_API NuError NuSetValue(NuArchive* pArchive, NuValueID ident,
|
|
NuValue value);
|
|
NUFXLIB_API NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident,
|
|
NuAttr* pAttr);
|
|
NUFXLIB_API NuError NuDebugDumpArchive(NuArchive* pArchive);
|
|
|
|
/* sources and sinks */
|
|
NUFXLIB_API NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat,
|
|
uint32_t otherLen, const UNICHAR* pathnameUNI,
|
|
short isFromRsrcFork, NuDataSource** ppDataSource);
|
|
NUFXLIB_API NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat,
|
|
uint32_t otherLen, FILE* fp, long offset, long length,
|
|
NuCallback closeFunc, NuDataSource** ppDataSource);
|
|
NUFXLIB_API NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat,
|
|
uint32_t otherLen, const uint8_t* buffer, long offset,
|
|
long length, NuCallback freeFunc, NuDataSource** ppDataSource);
|
|
NUFXLIB_API NuError NuFreeDataSource(NuDataSource* pDataSource);
|
|
NUFXLIB_API NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource,
|
|
uint16_t crc);
|
|
NUFXLIB_API NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL,
|
|
const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink);
|
|
NUFXLIB_API NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL,
|
|
FILE* fp, NuDataSink** ppDataSink);
|
|
NUFXLIB_API NuError NuCreateDataSinkForBuffer(short doExpand,
|
|
NuValue convertEOL, uint8_t* buffer, uint32_t bufLen,
|
|
NuDataSink** ppDataSink);
|
|
NUFXLIB_API NuError NuFreeDataSink(NuDataSink* pDataSink);
|
|
NUFXLIB_API NuError NuDataSinkGetOutCount(NuDataSink* pDataSink,
|
|
uint32_t* pOutCount);
|
|
|
|
/* miscellaneous non-archive operations */
|
|
NUFXLIB_API NuError NuGetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion,
|
|
int32_t* pBugVersion, const char** ppBuildDate,
|
|
const char** ppBuildFlags);
|
|
NUFXLIB_API const char* NuStrError(NuError err);
|
|
NUFXLIB_API NuError NuTestFeature(NuFeature feature);
|
|
NUFXLIB_API void NuRecordCopyAttr(NuRecordAttr* pRecordAttr,
|
|
const NuRecord* pRecord);
|
|
NUFXLIB_API NuError NuRecordCopyThreads(const NuRecord* pRecord,
|
|
NuThread** ppThreads);
|
|
NUFXLIB_API uint32_t NuRecordGetNumThreads(const NuRecord* pRecord);
|
|
NUFXLIB_API const NuThread* NuThreadGetByIdx(const NuThread* pThread,
|
|
int32_t idx);
|
|
NUFXLIB_API short NuIsPresizedThreadID(NuThreadID threadID);
|
|
NUFXLIB_API size_t NuConvertMORToUNI(const char* stringMOR,
|
|
UNICHAR* bufUNI, size_t bufSize);
|
|
NUFXLIB_API size_t NuConvertUNIToMOR(const UNICHAR* stringUNI,
|
|
char* bufMOR, size_t bufSize);
|
|
|
|
#define NuGetThread(pRecord, idx) ( (const NuThread*) \
|
|
((uint32_t) (idx) < (pRecord)->recTotalThreads ? \
|
|
&(pRecord)->pThreads[(idx)] : NULL) \
|
|
)
|
|
|
|
|
|
/* callback setters */
|
|
#define kNuInvalidCallback ((NuCallback) 1)
|
|
NUFXLIB_API NuCallback NuSetSelectionFilter(NuArchive* pArchive,
|
|
NuCallback filterFunc);
|
|
NUFXLIB_API NuCallback NuSetOutputPathnameFilter(NuArchive* pArchive,
|
|
NuCallback filterFunc);
|
|
NUFXLIB_API NuCallback NuSetProgressUpdater(NuArchive* pArchive,
|
|
NuCallback updateFunc);
|
|
NUFXLIB_API NuCallback NuSetErrorHandler(NuArchive* pArchive,
|
|
NuCallback errorFunc);
|
|
NUFXLIB_API NuCallback NuSetErrorMessageHandler(NuArchive* pArchive,
|
|
NuCallback messageHandlerFunc);
|
|
NUFXLIB_API NuCallback NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /*NUFXLIB_NUFXLIB_H*/
|