mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-26 17:49:21 +00:00
5946481b4e
This handles version 1 and 2, and copes with the broken files created by the Mac OS X "applesingle" command-line tool (which is unable to decode the broken files it creates). I get the sense that many AppleSingle files don't end with ".AS", so the filespec includes "*.*" as well. Some AppleSingle files don't include a filename. In that case, we use the file's name as the entry name, minus any ".as" extension. The current implementation doesn't convert from Unicode to Mac OS Roman, so non-ASCII characters are mishandled unless the file was generated by GS/ShrinkIt. (We assume version 1 AppleSingle files use MOR name strings.) Also, version bump to 4.0.0d3.
1027 lines
36 KiB
C++
1027 lines
36 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Generic Apple II archive handling. In the beginning we only handled
|
|
* NuFX archives, and the code continues to reflect that heritage.
|
|
*
|
|
* These are abstract base classes.
|
|
*/
|
|
#ifndef APP_GENERICARCHIVE_H
|
|
#define APP_GENERICARCHIVE_H
|
|
|
|
#include "Preferences.h"
|
|
#include "../util/UtilLib.h"
|
|
#include "../diskimg/DiskImg.h"
|
|
#include "../nufxlib/NufxLib.h"
|
|
#include "../reformat/Reformat.h"
|
|
#include <time.h>
|
|
#include <string.h>
|
|
|
|
// this shouldn't be in a header file, but in here it's probably okay
|
|
using namespace DiskImgLib;
|
|
|
|
class ActionProgressDialog;
|
|
class AddFilesDialog;
|
|
class RecompressOptionsDialog;
|
|
class SelectionSet;
|
|
struct Win32dirent;
|
|
class GenericArchive;
|
|
|
|
const int kFileTypeTXT = 0x04;
|
|
const int kFileTypeBIN = 0x06;
|
|
const int kFileTypeSRC = 0xb0;
|
|
const int kFileTypeINT = 0xfa;
|
|
const int kFileTypeBAS = 0xfc;
|
|
|
|
/*
|
|
* Set of data allowed in file property "set file info" calls.
|
|
*/
|
|
struct FileProps {
|
|
uint32_t fileType;
|
|
uint32_t auxType;
|
|
uint32_t access;
|
|
time_t createWhen;
|
|
time_t modWhen;
|
|
};
|
|
|
|
/*
|
|
* Options for converting between file archives and disk archives.
|
|
*/
|
|
class XferFileOptions {
|
|
public:
|
|
XferFileOptions(void) :
|
|
fTarget(NULL), fPreserveEmptyFolders(false), fpTargetFS(NULL)
|
|
{}
|
|
~XferFileOptions(void) {}
|
|
|
|
/* where the stuff is going */
|
|
GenericArchive* fTarget;
|
|
|
|
/* these really only have meaning when converting disk to file */
|
|
//bool fConvDOSText;
|
|
//bool fConvPascalText;
|
|
bool fPreserveEmptyFolders;
|
|
|
|
/* only useful when converting files to a disk image */
|
|
//CString fStoragePrefix;
|
|
DiskImgLib::DiskFS* fpTargetFS;
|
|
};
|
|
|
|
|
|
/*
|
|
* Generic description of an Apple II file.
|
|
*
|
|
* Everything returned by the basic "get" calls for display in the ContentList
|
|
* must be held in local storage (i.e. not just pointers into DiskFS data).
|
|
* Otherwise, we run the risk of doing some DiskFS updates and having weird
|
|
* things happen in the ContentList.
|
|
*/
|
|
class GenericEntry {
|
|
public:
|
|
GenericEntry(void);
|
|
virtual ~GenericEntry(void);
|
|
|
|
/* kinds of files found in archives */
|
|
enum RecordKind {
|
|
kRecordKindUnknown = 0,
|
|
kRecordKindDisk,
|
|
kRecordKindFile,
|
|
kRecordKindForkedFile,
|
|
kRecordKindDirectory,
|
|
kRecordKindVolumeDir,
|
|
};
|
|
/*
|
|
* Threads we will view or extract (threadMask). This is no longer used
|
|
* for viewing files, but still plays a role when extracting.
|
|
*/
|
|
enum {
|
|
// create one entry for each matching thread
|
|
kDataThread = 0x01,
|
|
kRsrcThread = 0x02,
|
|
kDiskImageThread = 0x04,
|
|
kCommentThread = 0x08,
|
|
|
|
// grab any of the above threads
|
|
kAnyThread = 0x10,
|
|
|
|
// set this if we allow matches on directory entries
|
|
kAllowDirectory = 0x20,
|
|
|
|
// and volume directory entries
|
|
kAllowVolumeDir = 0x40,
|
|
|
|
// set to include "damaged" files
|
|
kAllowDamaged = 0x80,
|
|
};
|
|
/* EOL conversion mode for threads being extracted */
|
|
enum ConvertEOL {
|
|
kConvertUnknown = 0, kConvertEOLOff, kConvertEOLOn, kConvertEOLAuto
|
|
};
|
|
enum EOLType {
|
|
kEOLUnknown = 0, kEOLCR, kEOLLF, kEOLCRLF
|
|
};
|
|
/* high ASCII conversion mode for threads being extracted */
|
|
enum ConvertHighASCII {
|
|
kConvertHAUnknown = 0, kConvertHAOff, kConvertHAOn, kConvertHAAuto
|
|
};
|
|
|
|
/* ProDOS access flags, used for all filesystems */
|
|
enum {
|
|
kAccessRead = 0x01,
|
|
kAccessWrite = 0x02,
|
|
kAccessInvisible = 0x04,
|
|
kAccessBackup = 0x20,
|
|
kAccessRename = 0x40,
|
|
kAccessDelete = 0x80
|
|
};
|
|
|
|
/*
|
|
* Features supported by underlying archive. Primarily of interest
|
|
* to EditPropsDialog, which needs to know what sort of attributes can
|
|
* be altered in the file type and access flags.
|
|
*/
|
|
enum Feature {
|
|
kFeatureCanChangeType,
|
|
kFeaturePascalTypes,
|
|
kFeatureDOSTypes,
|
|
kFeatureHFSTypes,
|
|
kFeatureHasFullAccess,
|
|
kFeatureHasSimpleAccess, // mutually exclusive with FullAccess
|
|
kFeatureHasInvisibleFlag,
|
|
};
|
|
|
|
/*
|
|
* Extract data from an archive (NuFX, disk image, etc).
|
|
*
|
|
* If "*ppText" is non-NULL, the data will be read into the pointed-to buffer
|
|
* so long as it's shorter than *pLength bytes. The value in "*pLength"
|
|
* will be set to the actual length used.
|
|
*
|
|
* If "*ppText" is NULL, the uncompressed data will be placed into a buffer
|
|
* allocated with "new[]".
|
|
*
|
|
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
|
|
* user, and -1 value on failure. On failure, "*pErrMsg" holds an error
|
|
* message.
|
|
*
|
|
* "which" is an anonymous GenericArchive enum (e.g. "kDataThread").
|
|
*/
|
|
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
|
|
CString* pErrMsg) const = 0;
|
|
|
|
/*
|
|
* Extract data from a thread or disk file to a Windows file. Since we're
|
|
* not copying to a buffer we can't assume that we're able to hold the
|
|
* entire file in memory all at once.
|
|
*
|
|
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
|
|
* user, and -1 value on failure. On failure, "*pMsg" holds an
|
|
* error message.
|
|
*/
|
|
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
|
|
ConvertHighASCII convHA, CString* pErrMsg) const = 0;
|
|
|
|
// This helps us retain the ContentList selection across a Reload(). Only
|
|
// necessary for read-write archives, since those are the only ones that
|
|
// ever need to be reloaded. Value must be nonzero to be used.
|
|
virtual long GetSelectionSerial(void) const = 0;
|
|
|
|
/* what operations are possible with this entry? */
|
|
virtual bool GetFeatureFlag(Feature feature) const = 0;
|
|
|
|
long GetIndex(void) const { return fIndex; }
|
|
void SetIndex(long idx) { fIndex = idx; }
|
|
|
|
/*
|
|
* Set the pathname. This comes from a file archive or disk image,
|
|
* so it's always in Mac OS Roman format.
|
|
*
|
|
* Calling this will invalidate any strings previously returned by
|
|
* GetPathName*(), GetFileName*(), and GetDisplayName().
|
|
*/
|
|
void SetPathNameMOR(const char* pathNameMOR);
|
|
|
|
const CStringA& GetPathNameMOR(void) const { return fPathNameMOR; }
|
|
const CString& GetPathNameUNI(void) const { return fPathNameUNI; }
|
|
const CString& GetFileName(void);
|
|
const CString& GetFileNameExtension(void); // returns e.g. ".SHK"
|
|
const CStringA& GetFileNameExtensionMOR(void);
|
|
/*
|
|
* Returns the "display" name. This is a combination of the sub-volume
|
|
* name and the path name. This string is intended for display only,
|
|
* and may include characters that aren't legal on the filesystem.
|
|
*/
|
|
const CString& GetDisplayName(void) const;
|
|
|
|
void SetSubVolName(const WCHAR* name);
|
|
const CString& GetSubVolName(void) const { return fSubVolName; }
|
|
|
|
char GetFssep(void) const { return fFssep; }
|
|
void SetFssep(char fssep) { fFssep = fssep; }
|
|
uint32_t GetFileType(void) const { return fFileType; }
|
|
void SetFileType(uint32_t type) { fFileType = type; }
|
|
uint32_t GetAuxType(void) const { return fAuxType; }
|
|
void SetAuxType(uint32_t type) { fAuxType = type; }
|
|
uint32_t GetAccess(void) const { return fAccess; }
|
|
void SetAccess(uint32_t access) { fAccess = access; }
|
|
time_t GetCreateWhen(void) const { return fCreateWhen; }
|
|
void SetCreateWhen(time_t when) { fCreateWhen = when; }
|
|
time_t GetModWhen(void) const { return fModWhen; }
|
|
void SetModWhen(time_t when) { fModWhen = when; }
|
|
RecordKind GetRecordKind(void) const { return fRecordKind; }
|
|
void SetRecordKind(RecordKind recordKind) { fRecordKind = recordKind; }
|
|
const WCHAR* GetFormatStr(void) const { return fFormatStr; }
|
|
void SetFormatStr(const WCHAR* str) { fFormatStr = str; } // arg not copied, must be static!
|
|
LONGLONG GetCompressedLen(void) const { return fCompressedLen; }
|
|
void SetCompressedLen(LONGLONG len) { fCompressedLen = len; }
|
|
LONGLONG GetUncompressedLen(void) const {
|
|
return fDataForkLen + fRsrcForkLen;
|
|
}
|
|
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
|
|
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
|
|
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
|
|
void SetRsrcForkLen(LONGLONG len) { fRsrcForkLen = len; }
|
|
|
|
DiskImg::FSFormat GetSourceFS(void) const { return fSourceFS; }
|
|
void SetSourceFS(DiskImg::FSFormat fmt) { fSourceFS = fmt; }
|
|
|
|
bool GetHasDataFork(void) const { return fHasDataFork; }
|
|
void SetHasDataFork(bool val) { fHasDataFork = val; }
|
|
bool GetHasRsrcFork(void) const { return fHasRsrcFork; }
|
|
void SetHasRsrcFork(bool val) { fHasRsrcFork = val; }
|
|
bool GetHasDiskImage(void) const { return fHasDiskImage; }
|
|
void SetHasDiskImage(bool val) { fHasDiskImage = val; }
|
|
bool GetHasComment(void) const { return fHasComment; }
|
|
void SetHasComment(bool val) { fHasComment = val; }
|
|
bool GetHasNonEmptyComment(void) const { return fHasNonEmptyComment; }
|
|
void SetHasNonEmptyComment(bool val) { fHasNonEmptyComment = val; }
|
|
|
|
bool GetDamaged(void) const { return fDamaged; }
|
|
void SetDamaged(bool val) { fDamaged = val; }
|
|
bool GetSuspicious(void) const { return fSuspicious; }
|
|
void SetSuspicious(bool val) { fSuspicious = val; }
|
|
|
|
GenericEntry* GetPrev(void) const { return fpPrev; }
|
|
void SetPrev(GenericEntry* pEntry) { fpPrev = pEntry; }
|
|
GenericEntry* GetNext(void) const { return fpNext; }
|
|
void SetNext(GenericEntry* pEntry) { fpNext = pEntry; }
|
|
|
|
/*
|
|
* Get a string for this entry's filetype.
|
|
*/
|
|
const WCHAR* GetFileTypeString(void) const;
|
|
|
|
/*
|
|
* Check to see if this is a high-ASCII file.
|
|
*/
|
|
static bool CheckHighASCII(const uint8_t* buffer, size_t count);
|
|
|
|
/*
|
|
* Decide, based on the contents of the buffer, whether we should do an
|
|
* EOL conversion on the data.
|
|
*
|
|
* Returns kConvEOLOff or kConvEOLOn.
|
|
*/
|
|
static ConvertEOL DetermineConversion(const uint8_t* buffer,
|
|
size_t count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
|
|
|
|
/*
|
|
* Write data to a file, possibly converting EOL markers to Windows CRLF
|
|
* and stripping high ASCII.
|
|
*
|
|
* If "*pConv" is kConvertEOLAuto, this will try to auto-detect whether
|
|
* the input is a text file or not by scanning the input buffer.
|
|
*
|
|
* Ditto for "*pConvHA".
|
|
*
|
|
* "fp" is the output file, "buf" is the input, "len" is the buffer length.
|
|
* "*pLastCR" should initially be "false", and carried across invocations.
|
|
*
|
|
* Returns 0 on success, or an errno value on error.
|
|
*/
|
|
static int GenericEntry::WriteConvert(FILE* fp, const char* buf,
|
|
size_t len, ConvertEOL* pConv, ConvertHighASCII* pConvHA,
|
|
bool* pLastCR);
|
|
|
|
private:
|
|
/*
|
|
* Convert spaces to underscores, modifying the string.
|
|
*/
|
|
static void SpacesToUnderscores(CStringA* pStr);
|
|
|
|
private:
|
|
/*
|
|
* This represents a file from an archive or disk image, so the Mac OS
|
|
* Roman representation is the "true" version. The Unicode version
|
|
* is how we will use it on Windows (e.g. for file extraction), so
|
|
* it will be a CP-1252 conversion until the various libraries
|
|
* support UTF-16 filenames.
|
|
*
|
|
* The "display name" is only used for display, and should do a proper
|
|
* MOR to Unicode conversion so the file name looks right.
|
|
*/
|
|
|
|
CStringA fPathNameMOR; // original path name, Mac OS Roman chars
|
|
CString fPathNameUNI; // Unicode conversion
|
|
CString fFileName; // filename component of fPathNameUNI
|
|
CString fFileNameExtension; // filename extension from fPathNameUNI
|
|
CStringA fFileNameExtensionMOR;
|
|
char fFssep;
|
|
CString fSubVolName; // sub-volume prefix, or NULL if none
|
|
mutable CString fDisplayName; // combination of sub-vol and path
|
|
uint32_t fFileType;
|
|
uint32_t fAuxType;
|
|
uint32_t fAccess;
|
|
time_t fCreateWhen;
|
|
time_t fModWhen;
|
|
RecordKind fRecordKind; // forked file, disk image, ??
|
|
const WCHAR* fFormatStr; // static str; compression or fs format
|
|
LONGLONG fDataForkLen; // also for disk images
|
|
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
|
|
LONGLONG fCompressedLen; // data/disk + rsrc
|
|
|
|
DiskImg::FSFormat fSourceFS; // if DOS3.3, text files have funky format
|
|
|
|
bool fHasDataFork;
|
|
bool fHasRsrcFork;
|
|
bool fHasDiskImage;
|
|
bool fHasComment;
|
|
bool fHasNonEmptyComment; // set if fHasComment and it isn't empty
|
|
|
|
bool fDamaged; // if set, don't try to open file
|
|
bool fSuspicious; // if set, file *might* be damaged
|
|
|
|
long fIndex; // serial index, for sorting "unsorted" view
|
|
GenericEntry* fpPrev;
|
|
GenericEntry* fpNext;
|
|
};
|
|
|
|
/*
|
|
* Generic representation of a collection of Apple II files.
|
|
*
|
|
* This raises the "reload" flag whenever its data is reloaded. Any code that
|
|
* keeps pointers to stuff (e.g. local copies of pointers to GenericEntry
|
|
* objects) needs to check the "reload" flag before dereferencing them.
|
|
*/
|
|
class GenericArchive {
|
|
public:
|
|
GenericArchive(void) {
|
|
fPathName = NULL;
|
|
fNumEntries = 0;
|
|
fEntryHead = fEntryTail = NULL;
|
|
fReloadFlag = true;
|
|
//fEntryIndex = NULL;
|
|
}
|
|
virtual ~GenericArchive(void) {
|
|
//LOGI("Deleting GenericArchive");
|
|
DeleteEntries();
|
|
delete fPathName;
|
|
}
|
|
|
|
virtual GenericEntry* GetEntries(void) const {
|
|
return fEntryHead;
|
|
}
|
|
virtual long GetNumEntries(void) const {
|
|
return fNumEntries;
|
|
}
|
|
|
|
enum OpenResult {
|
|
kResultUnknown = 0,
|
|
kResultSuccess, // open succeeded
|
|
kResultFailure, // open failed
|
|
kResultCancel, // open was cancelled by user
|
|
kResultFileArchive, // found a file archive rather than disk image
|
|
};
|
|
|
|
// Open an archive and do fun things with the innards.
|
|
virtual OpenResult Open(const WCHAR* filename, bool readOnly,
|
|
CString* pErrMsg) = 0;
|
|
// Create a new archive with the specified name.
|
|
virtual CString New(const WCHAR* filename, const void* options) = 0;
|
|
// Flush any unwritten data to disk
|
|
virtual CString Flush(void) = 0;
|
|
// Force a re-read from the underlying storage.
|
|
virtual CString Reload(void) = 0;
|
|
// Do we allow modification?
|
|
virtual bool IsReadOnly(void) const = 0;
|
|
// Does the underlying storage have un-flushed modifications?
|
|
virtual bool IsModified(void) const = 0;
|
|
|
|
virtual bool GetReloadFlag(void) { return fReloadFlag; }
|
|
virtual void ClearReloadFlag(void) { fReloadFlag = false; }
|
|
|
|
// One of these for every sub-class.
|
|
enum ArchiveKind {
|
|
kArchiveUnknown = 0,
|
|
kArchiveNuFX,
|
|
kArchiveBNY,
|
|
kArchiveACU,
|
|
kArchiveAppleSingle,
|
|
kArchiveDiskImage,
|
|
};
|
|
|
|
// Returns the kind of archive this is (disk image, NuFX, BNY, etc).
|
|
virtual ArchiveKind GetArchiveKind(void) = 0;
|
|
|
|
// Get a nice description for the title bar.
|
|
virtual void GetDescription(CString* pStr) const = 0;
|
|
|
|
// Do a bulk add.
|
|
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
|
|
const AddFilesDialog* pAddOpts) = 0;
|
|
|
|
// Add a single disk to the archive.
|
|
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
|
|
const AddFilesDialog* pAddOpts) = 0;
|
|
|
|
// Create a subdirectory with name newName in pParentEntry.
|
|
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
|
|
const WCHAR* newName) = 0;
|
|
|
|
// Test a set of files.
|
|
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
|
|
|
|
// Delete a set of files.
|
|
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
|
|
|
|
// Rename a set of files.
|
|
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
|
|
|
|
// Verify that a name is suitable. Called by RenameEntryDialog and
|
|
// CreateSubdirDialog.
|
|
//
|
|
// Tests for context-specific syntax and checks for duplicates.
|
|
//
|
|
// Returns an empty string on success, or an error message on failure.
|
|
virtual CString TestPathName(const GenericEntry* pGenericEntry,
|
|
const CString& basePath, const CString& newName, char newFssep) const = 0;
|
|
|
|
// Rename a volume (or sub-volume)
|
|
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
|
|
const WCHAR* newName) = 0;
|
|
virtual CString TestVolumeName(const DiskFS* pDiskFS,
|
|
const WCHAR* newName) const = 0;
|
|
|
|
// Recompress a set of files.
|
|
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
|
|
const RecompressOptionsDialog* pRecompOpts) = 0;
|
|
|
|
// return result from XferSelection()
|
|
enum XferStatus {
|
|
kXferOK = 0, kXferFailed = 1, kXferCancelled = 2, kXferOutOfSpace = 3
|
|
};
|
|
|
|
// Transfer selected files out of this archive and into another.
|
|
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
|
|
ActionProgressDialog* pActionProgress,
|
|
const XferFileOptions* pXferOpts) = 0;
|
|
|
|
// Extract a comment from the archive, converting line terminators to CRLF.
|
|
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
|
|
CString* pStr) = 0;
|
|
|
|
// Set a comment on an entry.
|
|
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
|
|
const CString& str) = 0;
|
|
|
|
// Delete the comment from the entry.
|
|
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) = 0;
|
|
|
|
// Set ProDOS file properties (file type, aux type, access flags).
|
|
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
|
|
const FileProps* pProps) = 0;
|
|
|
|
// Preferences have changed, update library state as needed. Also called
|
|
// the first time though.
|
|
virtual void PreferencesChanged(void) = 0;
|
|
|
|
// Determine an archive's capabilities. This is specific to the object
|
|
// instance, so this must not be made a static function.
|
|
enum Capability {
|
|
kCapUnknown = 0,
|
|
|
|
kCapCanTest, // NuFX, BNY
|
|
kCapCanRenameFullPath, // NuFX, BNY
|
|
kCapCanRecompress, // NuFX, BNY
|
|
kCapCanEditComment, // NuFX
|
|
kCapCanAddDisk, // NuFX
|
|
kCapCanConvEOLOnAdd, // Disk
|
|
kCapCanCreateSubdir, // Disk
|
|
kCapCanRenameVolume, // Disk
|
|
};
|
|
virtual long GetCapability(Capability cap) = 0;
|
|
|
|
// Get the pathname of the file we opened.
|
|
const WCHAR* GetPathName(void) const { return fPathName; }
|
|
|
|
/*
|
|
* Generate a temp name from a file name.
|
|
*/
|
|
static CString GenDerivedTempName(const WCHAR* filename);
|
|
|
|
/*
|
|
* Do a strcasecmp-like comparison, taking equivalent fssep chars into
|
|
* account.
|
|
*
|
|
* The tricky part is with files like "foo:bar" ':' -- "foo:bar" '/'. The
|
|
* names appear to match, but the fssep chars are different, so they don't.
|
|
* If we just return (char1 - char2), though, we'll be returning 0 because
|
|
* the ASCII values match even if the character *meanings* don't.
|
|
*
|
|
* This assumes that the fssep char is not affected by tolower().
|
|
*
|
|
* [This may not sort correctly...haven't verified that I'm returning the
|
|
* right thing for ascending ASCII sort.]
|
|
*/
|
|
static int ComparePaths(const CString& name1, char fssep1,
|
|
const CString& name2, char fssep2);
|
|
|
|
/*
|
|
* Add a new entry to the end of the list.
|
|
*/
|
|
void AddEntry(GenericEntry* pEntry);
|
|
|
|
/*
|
|
* This class holds details about a file that we're adding from local disk.
|
|
*/
|
|
class LocalFileDetails {
|
|
public:
|
|
LocalFileDetails(void);
|
|
virtual ~LocalFileDetails(void) {}
|
|
|
|
/*
|
|
* Set the various fields, based on the pathname and characteristics
|
|
* of the file.
|
|
*/
|
|
NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
|
struct _stat* psb);
|
|
|
|
/*
|
|
* What kind of file this is. Files being added to NuFX from Windows
|
|
* can't be "BothForks" because the forks are stored in
|
|
* separate files. However, files being transferred from a NuFX
|
|
* archive, a disk image, or in from the clipboard can be both.
|
|
*
|
|
* (NOTE: this gets embedded into clipboard data. If you change
|
|
* these values, update the version number in Clipboard.cpp.)
|
|
*/
|
|
enum FileKind {
|
|
kFileKindUnknown = 0,
|
|
kFileKindDataFork,
|
|
kFileKindRsrcFork,
|
|
kFileKindDiskImage,
|
|
kFileKindBothForks,
|
|
kFileKindDirectory,
|
|
};
|
|
|
|
/*
|
|
* Provide operator= and copy constructor.
|
|
*/
|
|
LocalFileDetails& operator=(const LocalFileDetails& src) {
|
|
if (&src != this)
|
|
CopyFields(this, &src);
|
|
return *this;
|
|
}
|
|
LocalFileDetails(const LocalFileDetails& src) {
|
|
CopyFields(this, &src);
|
|
}
|
|
|
|
/*
|
|
* Returns a reference to a NuFileDetails structure with the contents
|
|
* of the LocalFileDetails.
|
|
*
|
|
* The returned structure may not be used after the LocalFileDetails
|
|
* is modified or released.
|
|
*/
|
|
const NuFileDetails& GetNuFileDetails();
|
|
|
|
/*
|
|
* Returns a reference to a NuFileDetails structure with the contents
|
|
* of the LocalFileDetails.
|
|
*
|
|
* The returned structure may not be used after the LocalFileDetails
|
|
* is modified or released.
|
|
*/
|
|
const DiskFS::CreateParms& GetCreateParms();
|
|
|
|
/*
|
|
* Returns the "local" pathname, i.e. the name of a Windows file.
|
|
*/
|
|
const CString& GetLocalPathName() const {
|
|
return fLocalPathName;
|
|
}
|
|
|
|
void SetLocalPathName(const CString& newName) {
|
|
fLocalPathName = newName;
|
|
}
|
|
|
|
/*
|
|
* This is the "local" pathname with any file type preservation
|
|
* strings removed.
|
|
*/
|
|
const CString& GetStrippedLocalPathName() const {
|
|
return fStrippedLocalPathName;
|
|
}
|
|
|
|
/*
|
|
* Sets the "stripped" local path name, and updates the MOR version.
|
|
* Does not alter the full local path name.
|
|
*/
|
|
void SetStrippedLocalPathName(const CString& newName) {
|
|
fStrippedLocalPathName = newName;
|
|
GenerateStoragePathName();
|
|
}
|
|
|
|
/*
|
|
* Returns a copy of the stripped local pathname that has been
|
|
* converted to Mac OS Roman.
|
|
*
|
|
* The returned string will be invalid if SetStrippedLocalPathName
|
|
* is subsequently called.
|
|
*/
|
|
const CStringA& GetStoragePathNameMOR() const {
|
|
return fStoragePathNameMOR;
|
|
}
|
|
|
|
FileKind GetEntryKind() const { return fEntryKind; }
|
|
void SetEntryKind(FileKind kind) { fEntryKind = kind; }
|
|
|
|
DiskImg::FSFormat GetFileSysFmt() const { return fFileSysFmt; }
|
|
void SetFileSysFmt(DiskImg::FSFormat fmt) { fFileSysFmt = fmt; }
|
|
|
|
char GetFssep() const { return fFssep; }
|
|
void SetFssep(char fssep) { fFssep = fssep; }
|
|
|
|
uint32_t GetFileType() const { return fFileType; }
|
|
void SetFileType(uint32_t type) { fFileType = type; }
|
|
|
|
uint32_t GetExtraType() const { return fExtraType; }
|
|
void SetExtraType(uint32_t type) { fExtraType = type; }
|
|
|
|
uint32_t GetAccess() const { return fAccess; }
|
|
void SetAccess(uint32_t access) { fAccess = access; }
|
|
|
|
uint16_t GetStorageType() const { return fStorageType; }
|
|
void SetStorageType(uint16_t type) { fStorageType = type; }
|
|
|
|
const NuDateTime& GetArchiveWhen() const { return fArchiveWhen; }
|
|
void SetArchiveWhen(const NuDateTime& when) { fArchiveWhen = when; }
|
|
|
|
const NuDateTime& GetModWhen() const { return fModWhen; }
|
|
void SetModWhen(const NuDateTime& when) { fModWhen = when; }
|
|
|
|
const NuDateTime& GetCreateWhen() const { return fCreateWhen; }
|
|
void SetCreateWhen(const NuDateTime& when) { fCreateWhen = when; }
|
|
|
|
private:
|
|
/*
|
|
* Copy the contents of our object to a new object, field by field,
|
|
* so the CStrings copy correctly.
|
|
*
|
|
* Useful for operator= and copy construction.
|
|
*/
|
|
static void CopyFields(LocalFileDetails* pDst,
|
|
const LocalFileDetails* pSrc);
|
|
|
|
/*
|
|
* Generates fStoragePathNameMOR from fStrippedLocalPathName.
|
|
*/
|
|
void GenerateStoragePathName();
|
|
|
|
/*
|
|
* These are the structs that the libraries want, so we provide calls
|
|
* that populate them and return a reference. These are "hairy"
|
|
* structures, so we have to place limitations on their lifetime.
|
|
*
|
|
* Ideally these would either be proper objects with destructors,
|
|
* so we could create them and not worry about who will free up the
|
|
* hairy bits, or we would omit the hairy bits from the structure and
|
|
* pass them separately.
|
|
*/
|
|
NuFileDetails fNuFileDetails;
|
|
DiskFS::CreateParms fCreateParms;
|
|
|
|
/*
|
|
* What does this entry represent?
|
|
*/
|
|
FileKind fEntryKind;
|
|
|
|
/*
|
|
* Full pathname of the Windows file.
|
|
*/
|
|
CString fLocalPathName; // was origName
|
|
|
|
/*
|
|
* "Stripped" pathname. This is the full path with any of our
|
|
* added bits removed (e.g. file type & fork identifiers).
|
|
*
|
|
* This is generated by PathProposal::LocalToArchive().
|
|
*/
|
|
CString fStrippedLocalPathName; // was storageName
|
|
|
|
/*
|
|
* The storage name, generated by converting strippedLocalPathName
|
|
* from Unicode to Mac OS Roman. This is what will be passed to
|
|
* DiskImg and NufxLib as the name of the file to create in the
|
|
* archive. For DiskImg there's an additional "normalization" pass
|
|
* make the filename suitable for use on the filesystem; that name
|
|
* is stored in a FileAddData object, not here.
|
|
*/
|
|
CStringA fStoragePathNameMOR;
|
|
|
|
DiskImg::FSFormat fFileSysFmt; // only set for xfers?
|
|
uint8_t fFssep;
|
|
uint32_t fFileType;
|
|
uint32_t fExtraType;
|
|
uint32_t fAccess;
|
|
uint16_t fStorageType; // "Unknown" or disk block size
|
|
NuDateTime fCreateWhen;
|
|
NuDateTime fModWhen;
|
|
NuDateTime fArchiveWhen;
|
|
};
|
|
|
|
// Prepare for file transfers.
|
|
virtual void XferPrepare(const XferFileOptions* pXferOpts) = 0;
|
|
|
|
// Transfer files, one at a time, into this archive from another. Called
|
|
// from XferSelection and clipboard "paste".
|
|
//
|
|
// "dataLen" and "rsrcLen" will be -1 if the corresponding fork doesn't exist.
|
|
// Returns 0 on success, nonzero on failure.
|
|
//
|
|
// On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's
|
|
// necessary for the interface to work this way because the NufxArchive
|
|
// version just tucks the pointers into NufxLib structures.)
|
|
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
|
|
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0;
|
|
|
|
// Abort progress. Not all subclasses are capable of "undo".
|
|
virtual void XferAbort(CWnd* pMsgWnd) = 0;
|
|
|
|
// Transfer is finished.
|
|
virtual void XferFinish(CWnd* pMsgWnd) = 0;
|
|
|
|
/*
|
|
* Convert from time in seconds to Apple IIgs DateTime format.
|
|
*/
|
|
static void UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime);
|
|
|
|
/*
|
|
* Reads a 16-bit big-endian value from a buffer. Does not guard
|
|
* against buffer overrun.
|
|
*/
|
|
static uint16_t Get16BE(const uint8_t* ptr) {
|
|
return ptr[1] | (ptr[0] << 8);
|
|
}
|
|
|
|
/*
|
|
* Reads a 32-bit big-endian value from a buffer. Does not guard
|
|
* against buffer overrun.
|
|
*/
|
|
static uint32_t Get32BE(const uint8_t* ptr) {
|
|
return ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24);
|
|
}
|
|
|
|
/*
|
|
* Reads a 16-bit little-endian value from a buffer. Does not guard
|
|
* against buffer overrun.
|
|
*/
|
|
static uint16_t Get16LE(const uint8_t* ptr) {
|
|
return ptr[0] | (ptr[1] << 8);
|
|
}
|
|
|
|
/*
|
|
* Reads a 32-bit little-endian value from a buffer. Does not guard
|
|
* against buffer overrun.
|
|
*/
|
|
static uint32_t Get32LE(const uint8_t* ptr) {
|
|
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
|
|
}
|
|
|
|
protected:
|
|
/*
|
|
* Delete the "entries" list.
|
|
*/
|
|
virtual void DeleteEntries(void);
|
|
|
|
void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst);
|
|
|
|
/*
|
|
* Prepare a directory for reading.
|
|
*
|
|
* Allocates a Win32dirent struct that must be freed by the caller.
|
|
*/
|
|
Win32dirent* OpenDir(const WCHAR* name);
|
|
|
|
/*
|
|
* Get an entry from an open directory.
|
|
*
|
|
* Returns a NULL pointer after the last entry has been read.
|
|
*/
|
|
Win32dirent* ReadDir(Win32dirent* dir);
|
|
|
|
/*
|
|
* Close a directory.
|
|
*/
|
|
void CloseDir(Win32dirent* dir);
|
|
|
|
/*
|
|
* Win32 recursive directory descent. Scan the contents of a directory.
|
|
* If a subdirectory is found, follow it; otherwise, call Win32AddFile to
|
|
* add the file.
|
|
*/
|
|
NuError Win32AddDirectory(const AddFilesDialog* pAddOpts,
|
|
const WCHAR* dirName, CString* pErrMsg);
|
|
|
|
/*
|
|
* Add a file to the list we're adding to the archive. If it's a directory,
|
|
* and the recursive descent feature is enabled, call Win32AddDirectory to
|
|
* add the contents of the dir.
|
|
*
|
|
* Returns with an error if the file doesn't exist or isn't readable.
|
|
*/
|
|
NuError Win32AddFile(const AddFilesDialog* pAddOpts,
|
|
const WCHAR* pathname, CString* pErrMsg);
|
|
|
|
/*
|
|
* External entry point; just calls the system-specific version.
|
|
*/
|
|
NuError AddFile(const AddFilesDialog* pAddOpts, const WCHAR* pathname,
|
|
CString* pErrMsg);
|
|
|
|
/*
|
|
* Each implementation must provide this. It's called from the generic
|
|
* AddFile function with the high-level add options, a partial pathname,
|
|
* and a FileDetails structure filled in using Win32 calls.
|
|
*
|
|
* One call to AddFile can result in multiple calls to DoAddFile if
|
|
* the subject of the AddFile call is a directory (and fIncludeSubdirs
|
|
* is set).
|
|
*
|
|
* DoAddFile is not called for subdirectories. The underlying code must
|
|
* create directories as needed.
|
|
*
|
|
* In some cases (such as renaming a file as it is being added) the
|
|
* information in "*pDetails" may be modified.
|
|
*/
|
|
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
|
|
LocalFileDetails* pDetails) = 0;
|
|
|
|
void SetPathName(const WCHAR* pathName) {
|
|
free(fPathName);
|
|
if (pathName != NULL) {
|
|
fPathName = _wcsdup(pathName);
|
|
} else {
|
|
fPathName = NULL;
|
|
}
|
|
}
|
|
|
|
bool fReloadFlag; // set after Reload called
|
|
|
|
private:
|
|
//virtual void CreateIndex(void);
|
|
|
|
//CString fNewPathHolder;
|
|
//CString fOrigPathHolder;
|
|
|
|
WCHAR* fPathName;
|
|
long fNumEntries;
|
|
GenericEntry* fEntryHead;
|
|
GenericEntry* fEntryTail;
|
|
//GenericEntry** fEntryIndex;
|
|
};
|
|
|
|
/*
|
|
* One entry in a SelectionSet.
|
|
*/
|
|
class SelectionEntry {
|
|
public:
|
|
SelectionEntry(GenericEntry* pEntry) {
|
|
fpEntry = pEntry;
|
|
//fThreadKind = threadKind;
|
|
//fFilter = filter;
|
|
//fReformatName = "";
|
|
fpPrev = fpNext = NULL;
|
|
}
|
|
~SelectionEntry(void) {}
|
|
|
|
int Reformat(ReformatHolder* pHolder);
|
|
|
|
GenericEntry* GetEntry(void) const { return fpEntry; }
|
|
//int GetThreadKind(void) const { return fThreadKind; }
|
|
//int GetFilter(void) const { return fFilter; }
|
|
//const char* GetReformatName(void) const { return fReformatName; }
|
|
|
|
SelectionEntry* GetPrev(void) const { return fpPrev; }
|
|
void SetPrev(SelectionEntry* pPrev) { fpPrev = pPrev; }
|
|
SelectionEntry* GetNext(void) const { return fpNext; }
|
|
void SetNext(SelectionEntry* pNext) { fpNext = pNext; }
|
|
|
|
private:
|
|
GenericEntry* fpEntry;
|
|
//int fThreadKind; // data, rsrc, etc (threadMask)
|
|
//int fFilter; // fAllowedFilters, really
|
|
//const char* fReformatName; // name of formatting actually applied
|
|
|
|
SelectionEntry* fpPrev;
|
|
SelectionEntry* fpNext;
|
|
};
|
|
|
|
class ContentList;
|
|
|
|
/*
|
|
* A set of selected files.
|
|
*
|
|
* Each entry represents one item that can be displayed, such as a data
|
|
* fork, resource fork, or comment thread. Thus, a single file may have
|
|
* multiple entries in the set.
|
|
*/
|
|
class SelectionSet {
|
|
public:
|
|
SelectionSet(void) {
|
|
fNumEntries = 0;
|
|
fEntryHead = fEntryTail = fIterCurrent = NULL;
|
|
}
|
|
~SelectionSet(void) {
|
|
DeleteEntries();
|
|
}
|
|
|
|
// create the set from the selected members of a ContentList
|
|
void CreateFromSelection(ContentList* pContentList, int threadMask);
|
|
// create the set from all members of a ContentList
|
|
void CreateFromAll(ContentList* pContentList, int threadMask);
|
|
|
|
// get the head of the list
|
|
SelectionEntry* GetEntries(void) const { return fEntryHead; }
|
|
|
|
void IterReset(void) {
|
|
fIterCurrent = NULL;
|
|
}
|
|
// move to the next or previous entry as part of iterating
|
|
SelectionEntry* IterPrev(void) {
|
|
if (fIterCurrent == NULL)
|
|
fIterCurrent = fEntryTail;
|
|
else
|
|
fIterCurrent = fIterCurrent->GetPrev();
|
|
return fIterCurrent;
|
|
}
|
|
SelectionEntry* IterNext(void) {
|
|
if (fIterCurrent == NULL)
|
|
fIterCurrent = fEntryHead;
|
|
else
|
|
fIterCurrent = fIterCurrent->GetNext();
|
|
return fIterCurrent;
|
|
}
|
|
SelectionEntry* IterCurrent(void) {
|
|
return fIterCurrent;
|
|
}
|
|
bool IterHasPrev(void) const {
|
|
if (fIterCurrent == NULL)
|
|
return fEntryTail != NULL;
|
|
else
|
|
return (fIterCurrent->GetPrev() != NULL);
|
|
}
|
|
bool IterHasNext(void) const {
|
|
if (fIterCurrent == NULL)
|
|
return fEntryHead != NULL;
|
|
else
|
|
return (fIterCurrent->GetNext() != NULL);
|
|
}
|
|
|
|
int GetNumEntries(void) const { return fNumEntries; }
|
|
|
|
// count the #of entries whose display name matches "prefix"
|
|
int CountMatchingPrefix(const WCHAR* prefix);
|
|
|
|
// debug dump the contents of the selection set
|
|
void Dump(void);
|
|
|
|
private:
|
|
/*
|
|
* Add a GenericEntry to the set, but only if we can find a thread that
|
|
* matches the flags in "threadMask".
|
|
*/
|
|
void AddToSet(GenericEntry* pEntry, int threadMask);
|
|
|
|
/*
|
|
* Add a new entry to the end of the list.
|
|
*/
|
|
void AddEntry(SelectionEntry* pEntry);
|
|
|
|
/*
|
|
* Delete the "entries" list.
|
|
*/
|
|
void DeleteEntries(void);
|
|
|
|
int fNumEntries;
|
|
SelectionEntry* fIterCurrent;
|
|
|
|
SelectionEntry* fEntryHead;
|
|
SelectionEntry* fEntryTail;
|
|
};
|
|
|
|
#endif /*APP_GENERICARCHIVE_H*/
|