ciderpress/app/ACUArchive.h
Andy McFadden 814872966e Rewrote "open archive" logic
The previous code was a stickler for only opening files whose type
matched what was selected in the filter pop-up.  The original goal
was to allow you to choose whether a BXY or SDK file was interpreted
as Binary II, ShrinkIt, or disk image, since they could go either
way.  Unfortunately, its refusal to consider types other than what
was selected made it kind of annoying.

The new code will start by trying to open the file with the selected
filter, so that it's still possible to choose how SDK and BXY files
are opened.  However, it now continues on, trying all other types
before finally giving up.

If the generic ("*.*") filter is selected, CiderPress will start by
trying to open the file as a disk image.

This seems to produce good results with a variety of known and
unknown files.
2015-01-13 15:54:10 -08:00

298 lines
10 KiB
C++

/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* AppleLink Compression Utility archive support (read-only).
*/
#ifndef APP_ACUARCHIVE_H
#define APP_ACUARCHIVE_H
#include "GenericArchive.h"
class AcuArchive;
/*
* One file in an ACU archive.
*/
class AcuEntry : public GenericEntry {
public:
AcuEntry(AcuArchive* pArchive) :
fpArchive(pArchive), fIsSqueezed(false), fOffset(-1)
{}
virtual ~AcuEntry(void) {}
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const override;
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
ConvertHighASCII convHA, CString* pErrMsg) const override;
// doesn't matter
virtual long GetSelectionSerial(void) const override { return -1; }
virtual bool GetFeatureFlag(Feature feature) const override {
if (feature == kFeatureHasFullAccess ||
feature == kFeatureCanChangeType ||
feature == kFeatureHasInvisibleFlag)
{
return true;
} else {
return false;
}
}
/*
* Test this entry by extracting it.
*
* If the file isn't compressed, just make sure the file is big enough. If
* it's squeezed, invoke the un-squeeze function with a "NULL" buffer pointer.
*/
NuError TestEntry(CWnd* pMsgWnd);
bool GetSqueezed(void) const { return fIsSqueezed; }
void SetSqueezed(bool val) { fIsSqueezed = val; }
long GetOffset(void) const { return fOffset; }
void SetOffset(long offset) { fOffset = offset; }
private:
/*
* Copy data from the seeked archive to outfp, possibly converting EOL along
* the way.
*/
NuError CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA,
CString* pMsg) const;
AcuArchive* fpArchive; // holds FILE* for archive
bool fIsSqueezed;
long fOffset;
};
/*
* ACU archive definition.
*/
class AcuArchive : public GenericArchive {
public:
AcuArchive(void) : fFp(NULL) {}
virtual ~AcuArchive(void) { (void) Close(); }
/*
* Perform one-time initialization. There really isn't any for us.
*
* Returns an error string on failure.
*/
static CString AppInit(void);
/*
* Open an ACU archive.
*
* Returns an error string on failure, or "" on success.
*/
virtual OpenResult Open(const WCHAR* filename, bool readOnly,
CString* pErrMsg) override;
/*
* Finish instantiating an AcuArchive object by creating a new archive.
*
* This isn't implemented, and will always return an error.
*/
virtual CString New(const WCHAR* filename, const void* options) override;
virtual CString Flush(void) override { return L""; }
virtual CString Reload(void) override;
virtual bool IsReadOnly(void) const override { return true; };
virtual bool IsModified(void) const override { return false; }
virtual CString GetDescription() const override { return L"AppleLink ACU"; }
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts) override
{ ASSERT(false); return false; }
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts) override
{ ASSERT(false); return false; }
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
const WCHAR* newName) override
{ ASSERT(false); return false; }
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override;
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override
{ ASSERT(false); return false; }
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override
{ ASSERT(false); return false; }
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
const WCHAR* newName) override
{ ASSERT(false); return false; }
virtual CString TestVolumeName(const DiskFS* pDiskFS,
const WCHAR* newName) const override
{ ASSERT(false); return L"!"; }
virtual CString TestPathName(const GenericEntry* pGenericEntry,
const CString& basePath, const CString& newName, char newFssep) const override
{ ASSERT(false); return L"!"; }
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
const RecompressOptionsDialog* pRecompOpts) override
{ ASSERT(false); return false; }
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
ActionProgressDialog* pActionProgress,
const XferFileOptions* pXferOpts) override
{ ASSERT(false); return kXferFailed; }
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
CString* pStr) override
{ ASSERT(false); return false; }
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
const CString& str) override
{ ASSERT(false); return false; }
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override
{ ASSERT(false); return false; }
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
const FileProps* pProps) override
{ ASSERT(false); return false; }
virtual void PreferencesChanged(void) override {}
virtual long GetCapability(Capability cap) override;
friend class AcuEntry;
private:
virtual CString Close(void) {
if (fFp != NULL) {
fclose(fFp);
fFp = NULL;
}
return L"";
}
virtual void XferPrepare(const XferFileOptions* pXferOpts) override
{ ASSERT(false); }
virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf,
long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override
{ ASSERT(false); return L"!"; }
virtual void XferAbort(CWnd* pMsgWnd) override
{ ASSERT(false); }
virtual void XferFinish(CWnd* pMsgWnd) override
{ ASSERT(false); }
virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
LocalFileDetails* pDetails) override
{ ASSERT(false); return kNuErrGeneric; }
enum {
kAcuMaxFileName = 256, // nice big number
kAcuMasterHeaderLen = 20,
kAcuEntryHeaderLen = 54,
};
/*
* The header at the front of an ACU archive.
*/
struct AcuMasterHeader {
uint16_t fileCount;
uint16_t unknown1; // 0x01 00 -- might be "version 1?"
uint8_t fZink[6]; // "fZink", low ASCII
uint8_t unknown2[11]; // 0x01 36 00 00 00 00 00 00 00 00 dd
};
/*
* An entry in an ACU archive. Each archive is essentially a stream
* of files; only the "filesToFollow" value gives any indication that
* something else follows this entry.
*
* We read this from the archive and then unpack the interesting parts
* into GenericEntry fields in an AcuEntry.
*/
//struct AcuFileEntry;
//friend struct AcuFileEntry;
struct AcuFileEntry {
uint8_t compressionType;
uint16_t dataChecksum; // ??
uint16_t blockCount; // total blocks req'd to hold file
uint32_t dataStorageLen; // length of data within archive
uint16_t access;
uint16_t fileType;
uint32_t auxType;
uint8_t storageType;
uint32_t dataEof;
uint16_t prodosModDate;
uint16_t prodosModTime;
NuDateTime modWhen; // computed from previous two fields
uint16_t prodosCreateDate;
uint16_t prodosCreateTime;
NuDateTime createWhen; // computed from previous two fields
uint16_t fileNameLen;
uint16_t headerChecksum; // ??
char fileName[kAcuMaxFileName+1]; // ASCII
// possibilities for mystery fields:
// - OS type (note ProDOS is $00)
// - forked file support
};
/* known compression types */
enum CompressionType {
kAcuCompNone = 0,
kAcuCompSqueeze = 3,
};
/*
* Load the contents of the archive.
*
* Returns 0 on success, < 0 if this is not an ACU archive, or > 0 if
* this appears to be an ACU archive but it's damaged.
*/
int LoadContents(void);
/*
* Read the archive header. The archive file is left seeked to the point
* at the end of the header.
*
* Returns 0 on success, -1 on failure. Sets *pNumEntries to the number of
* entries in the archive.
*/
int ReadMasterHeader(int* pNumEntries);
/*
* Read and decode an AppleLink Compression Utility file entry header.
* This leaves the file seeked to the point immediately past the filename.
*/
NuError ReadFileHeader(AcuFileEntry* pEntry);
/*
* Dump the contents of an AcuFileEntry struct.
*/
void DumpFileHeader(const AcuFileEntry* pEntry);
/*
* Given an AcuFileEntry structure, add an appropriate entry to the list.
*/
int CreateEntry(const AcuFileEntry* pEntry);
/*
* Test if this entry is a directory.
*/
bool IsDir(const AcuFileEntry* pEntry);
/*
* Wrapper for fread(). Note the arguments resemble read(2) rather
* than fread(3S).
*/
NuError AcuRead(void* buf, size_t nbyte);
/*
* Seek within an archive. Because we need to handle streaming archives,
* and don't need to special-case anything, we only allow relative
* forward seeks.
*/
NuError AcuSeek(long offset);
/*
* Convert from ProDOS compact date format to the expanded DateTime format.
*/
void AcuConvertDateTime(uint16_t prodosDate,
uint16_t prodosTime, NuDateTime* pWhen);
FILE* fFp;
//bool fIsReadOnly;
};
#endif /*APP_ACUARCHIVE_H*/