Adding read capability for Gutenberg word processor formatted disks

This commit is contained in:
David Schmidt 2009-01-04 22:23:04 +00:00
parent aa89adb2d9
commit abd3515424
6 changed files with 12533 additions and 11590 deletions

View File

@ -1,5 +1,6 @@
/*
* CiderPress
* Copyright (C) 2009 by CiderPress authors. All Rights Reserved.
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
@ -1158,6 +1159,7 @@ DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName)
case DiskImg::kFormatDOS33:
case DiskImg::kFormatDOS32:
case DiskImg::kFormatUNIDOS:
case DiskImg::kFormatGutenberg:
pNewEntry->SetFormatStr("DOS");
break;
case DiskImg::kFormatProDOS:

View File

@ -1,5 +1,6 @@
/*
* CiderPress
* Copyright (C) 2009 by CiderPress authors. All Rights Reserved.
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
@ -1404,6 +1405,10 @@ DiskImg::AnalyzeImageFS(void)
{
assert(fFormat == kFormatMacHFS);
WMSG1(" DI found HFS, order=%d\n", fOrder);
} else if (DiskFSGutenberg::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone)
{
assert(fFormat == kFormatGutenberg);
WMSG1(" DI found Gutenberg, order=%d\n", fOrder);
} else {
fFormat = kFormatUnknown;
WMSG1(" DI no recognizeable filesystem found (fOrder=%d)\n",
@ -1578,6 +1583,7 @@ DiskImg::CalcFSSectorOrder(void) const
case kFormatDOS32:
case kFormatUNIDOS:
case kFormatOzDOS:
case kFormatGutenberg:
return kSectorOrderDOS;
case kFormatGenericCPMOrd:
@ -1633,6 +1639,7 @@ DiskImg::ShowAsBlocks(void) const
case kFormatRDOS33:
case kFormatUNIDOS:
case kFormatOzDOS:
case kFormatGutenberg:
return false;
case kFormatGenericProDOSOrd:
@ -3158,6 +3165,9 @@ DiskImg::OpenAppropriateDiskFS(bool allowUnknown)
case DiskImg::kFormatRDOS3:
pDiskFS = new DiskFSRDOS();
break;
case DiskImg::kFormatGutenberg:
pDiskFS = new DiskFSGutenberg();
break;
default:
WMSG1("WARNING: unhandled DiskFS case %d\n", GetFSFormat());

View File

@ -1,5 +1,6 @@
/*
* CiderPress
* Copyright (C) 2009 by CiderPress authors. All Rights Reserved.
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
@ -344,6 +345,7 @@ public:
kFormatMacPart = 44, // Macintosh-style partitioned disk
kFormatMicroDrive = 45, // ///SHH Systeme's MicroDrive format
kFormatFocusDrive = 46, // Parsons Engineering FocusDrive format
kFormatGutenberg = 47, // Gutenberg word processor format
// try to keep this in an unsigned char, e.g. for CP clipboard
} FSFormat;
@ -573,6 +575,7 @@ public:
static bool UsesDOSFileStructure(FSFormat format) {
return (format == kFormatDOS33 ||
format == kFormatDOS32 ||
format == kFormatGutenberg ||
format == kFormatUNIDOS ||
format == kFormatOzDOS ||
format == kFormatRDOS33 ||

View File

@ -1,5 +1,6 @@
/*
* CiderPress
* Copyright (C) 2009 by CiderPress authors. All Rights Reserved.
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
@ -2806,6 +2807,258 @@ private:
};
/*
* ===========================================================================
* Gutenberg
* ===========================================================================
*/
class A2FileGutenberg;
/*
* Gutenberg disk.
*/
class DISKIMG_API DiskFSGutenberg : public DiskFS {
public:
DiskFSGutenberg(void) : DiskFS() {
fVTOCLoaded = false;
fDiskIsGood = false;
}
virtual ~DiskFSGutenberg(void) {}
static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder,
DiskImg::FSFormat* pFormat, FSLeniency leniency);
virtual DIError Initialize(DiskImg* pImg, InitMode initMode) {
SetDiskImg(pImg);
return Initialize(initMode);
}
virtual const char* GetVolumeName(void) const { return fDiskVolumeName; }
virtual const char* GetVolumeID(void) const { return fDiskVolumeID; }
virtual const char* GetBareVolumeName(void) const {
return fDiskVolumeName;
}
virtual bool GetReadWriteSupported(void) const { return true; }
virtual bool GetFSDamaged(void) const { return !fDiskIsGood; }
virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits,
int* pUnitSize) const;
static bool IsValidFileName(const char* name);
static bool IsValidVolumeName(const char* name);
// utility function
static void LowerASCII(unsigned char* buf, long len);
static void ReplaceFssep(char* str, char replacement);
enum {
kMinTracks = 17, // need to put the catalog track here
kMaxTracks = 50,
kMaxCatalogSectors = 64, // two tracks on a 32-sector disk
};
/* a T/S pair */
typedef struct TrackSector {
char track;
char sector;
} TrackSector;
friend class A2FDGutenberg; // for Write
private:
DIError Initialize(InitMode initMode);
DIError ReadVTOC(void);
void UpdateVolumeNum(void);
void DumpVTOC(void);
void SetSectorUsage(long track, long sector,
VolumeUsage::ChunkPurpose purpose);
void FixVolumeUsageMap(void);
DIError ReadCatalog(void);
DIError ProcessCatalogSector(int catTrack, int catSect,
const unsigned char* sctBuf);
DIError GetFileLengths(void);
DIError ComputeLength(A2FileGutenberg* pFile, const TrackSector* tsList,
int tsCount);
DIError TrimLastSectorUp(A2FileGutenberg* pFile, TrackSector lastTS);
void MarkFileUsage(A2FileGutenberg* pFile, TrackSector* tsList, int tsCount,
TrackSector* indexList, int indexCount);
DIError MakeFileNameUnique(char* fileName);
DIError GetFreeCatalogEntry(TrackSector* pCatSect, int* pCatEntry,
unsigned char* sctBuf, A2FileGutenberg** ppPrevEntry);
void CreateDirEntry(unsigned char* sctBuf, int catEntry,
const char* fileName, TrackSector* pTSSect, unsigned char fileType,
int access);
void FreeTrackSectors(TrackSector* pList, int count);
bool CheckDiskIsGood(void);
DIError WriteDOSTracks(int sectPerTrack);
DIError ScanVolBitmap(void);
DIError LoadVolBitmap(void);
DIError SaveVolBitmap(void);
void FreeVolBitmap(void);
DIError AllocSector(TrackSector* pTS);
DIError CreateEmptyBlockMap(bool withDOS);
bool GetSectorUseEntry(long track, int sector) const;
void SetSectorUseEntry(long track, int sector, bool inUse);
inline unsigned long GetVTOCEntry(const unsigned char* pVTOC,
long track) const;
// Largest interesting volume is 400K (50 tracks, 32 sectors), but
// we may be looking at it in 16-sector mode, so max tracks is 100.
enum {
kMaxInterestingTracks = 100,
kSectorSize = 256,
kDefaultVolumeNum = 254,
kMaxExtensionLen = 4, // used when normalizing; ".gif" is 4
};
/* some fields from the VTOC */
int fFirstCatTrack;
int fFirstCatSector;
int fVTOCVolumeNumber;
int fVTOCNumTracks;
int fVTOCNumSectors;
/* private data */
char fDiskVolumeName[10]; //
char fDiskVolumeID[11+12+1]; // sizeof "Gutenberg: " + 12 + null
unsigned char fVTOC[kSectorSize];
bool fVTOCLoaded;
/*
* There are some things we need to be careful of when reading the
* catalog track, like bad links and infinite loops. By storing a list
* of known good catalog sectors, we only have to handle that stuff once.
* The catalog doesn't grow or shrink, so this never needs to be updated.
*/
TrackSector fCatalogSectors[kMaxCatalogSectors];
bool fDiskIsGood;
};
/*
* File descriptor for an open Gutenberg file.
*/
class DISKIMG_API A2FDGutenberg : public A2FileDescr {
public:
A2FDGutenberg(A2File* pFile) : A2FileDescr(pFile) {
fOffset = 0;
fModified = false;
}
virtual ~A2FDGutenberg(void) {
}
friend class A2FileGutenberg;
virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL);
virtual DIError Write(const void* buf, size_t len, size_t* pActual = NULL);
virtual DIError Seek(di_off_t offset, DIWhence whence);
virtual di_off_t Tell(void);
virtual DIError Close(void);
virtual long GetSectorCount(void) const;
virtual long GetBlockCount(void) const;
virtual DIError GetStorage(long sectorIdx, long* pTrack, long* pSector) const;
virtual DIError GetStorage(long blockIdx, long* pBlock) const;
private:
typedef DiskFSGutenberg::TrackSector TrackSector;
int fTSCount;
di_off_t fOffset; // current position in file
di_off_t fOpenEOF; // how big the file currently is
long fOpenSectorsUsed; // how many sectors it occupies
bool fModified; // if modified, update stuff on Close
void DumpTSList(void) const;
};
/*
* Holds Gutenberg files.
*
*/
class DISKIMG_API A2FileGutenberg : public A2File {
public:
A2FileGutenberg(DiskFS* pDiskFS);
virtual ~A2FileGutenberg(void);
// assorted constants
enum {
kMaxFileName = 12,
};
typedef enum {
kTypeText = 0x00, // 'T'
} FileType;
/*
* Implementations of standard interfaces.
*/
virtual const char* GetFileName(void) const { return fFileName; }
virtual const char* GetPathName(void) const { return fFileName; }
virtual char GetFssep(void) const { return '\0'; }
virtual long GetFileType(void) const;
virtual long GetAuxType(void) const { return fAuxType; }
virtual long GetAccess(void) const { return DiskFS::kFileAccessUnlocked; }
virtual time_t GetCreateWhen(void) const { return 0; }
virtual time_t GetModWhen(void) const { return 0; }
virtual di_off_t GetDataLength(void) const { return fLength; }
virtual di_off_t GetDataSparseLength(void) const { return fSparseLength; }
virtual di_off_t GetRsrcLength(void) const { return -1; }
virtual di_off_t GetRsrcSparseLength(void) const { return -1; }
virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly,
bool rsrcFork = false);
virtual void CloseDescr(A2FileDescr* pOpenFile) {
assert(pOpenFile == fpOpenFile);
delete fpOpenFile;
fpOpenFile = NULL;
}
virtual bool IsFileOpen(void) const { return fpOpenFile != NULL; }
void Dump(void) const;
typedef DiskFSGutenberg::TrackSector TrackSector;
/*
* Contents of directory entry.
*
* We don't hold deleted or unused entries, so fTSListTrack is always
* valid.
*/
short fTrack; // (could use TrackSector here)
short fSector;
unsigned short fLengthInSectors;
bool fLocked;
char fFileName[kMaxFileName+1]; // "fixed" version
FileType fFileType;
TrackSector fCatTS; // track/sector for our catalog entry
int fCatEntryNum; // entry number within cat sector
// these are computed or determined from the file contents
unsigned short fAuxType; // addr for bin, etc.
short fDataOffset; // for 'A'/'B'/'I' with embedded len
di_off_t fLength; // file length, in bytes
di_off_t fSparseLength; // file length, factoring sparse out
void FixFilename(void);
static FileType ConvertFileType(long prodosType, di_off_t fileLen);
static bool IsValidType(long prodosType);
static void MakeDOSName(char* buf, const char* name);
static void TrimTrailingSpaces(char* filename);
private:
DIError ExtractTSPairs(const unsigned char* sctBuf, TrackSector* tsList,
int* pLastNonZero);
A2FDGutenberg* fpOpenFile;
};
/*
* ===========================================================================
* FAT (including FAT12, FAT16, and FAT32)

671
diskimg/Gutenberg.cpp Normal file
View File

@ -0,0 +1,671 @@
/*
* CiderPress
* Copyright (C) 2009 by CiderPress authors. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Implementation of DiskFSGutenberg and A2FileGutenberg classes.
*
*/
#include "StdAfx.h"
#include "DiskImgPriv.h"
/*
* ===========================================================================
* DiskFSGutenberg
* ===========================================================================
*/
const int kMaxSectors = 32;
const int kMaxVolNameLen = 9;
const int kSctSize = 256;
const int kVTOCTrack = 17;
const int kVTOCSector = 7;
const int kCatalogEntryOffset = 0x20; // first entry in cat sect starts here
const int kCatalogEntrySize = 16; // length in bytes of catalog entries
const int kCatalogEntriesPerSect = 15; // #of entries per catalog sector
const int kEntryDeleted = 0x40; // this is used to designate deleted files
const int kEntryUnused = 0x00; // this is track# in never-used entries
const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors
const int kTSOffset = 0x0c; // first T/S entry in a T/S list
const int kMaxTSIterations = 32;
/*
* Get a pointer to the Nth entry in a catalog sector.
*/
static inline unsigned char*
GetCatalogEntryPtr(unsigned char* basePtr, int entryNum)
{
assert(entryNum >= 0 && entryNum < kCatalogEntriesPerSect);
return basePtr + kCatalogEntryOffset + entryNum * kCatalogEntrySize;
}
/*
* Test this image for Gutenberg-ness.
*
*/
static DIError
TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, int* pGoodCount)
{
DIError dierr = kDIErrNone;
unsigned char sctBuf[kSctSize];
// int numTracks, numSectors;
int catTrack = kVTOCTrack;
int catSect = kVTOCSector;
int foundGood = 0;
int iterations = 0;
*pGoodCount = 0;
/*
* Walk through the catalog track to try to figure out ordering.
*/
while (iterations < DiskFSGutenberg::kMaxCatalogSectors)
{
dierr = pImg->ReadTrackSectorSwapped(catTrack, catSect, sctBuf,
imageOrder, DiskImg::kSectorOrderDOS);
if (dierr != kDIErrNone) {
dierr = kDIErrNone;
break; /* allow it if earlier stuff was okay */
}
if (catTrack == sctBuf[0] && catSect == sctBuf[1]) {
foundGood++;
if (sctBuf[0x0f] == 0x8d && sctBuf[0x1f] == 0x8d &&
sctBuf[0x2f] == 0x8d && sctBuf[0x3f] == 0x8d &&
sctBuf[0x4f] == 0x8d && sctBuf[0x5f] == 0x8d &&
sctBuf[0x6f] == 0x8d && sctBuf[0x7f] == 0x8d &&
sctBuf[0x8f] == 0x8d && sctBuf[0x9f] == 0x8d)
foundGood++;
}
else if (catTrack >0x80) {
WMSG2(" Gutenberg detected end-of-catalog on cat (%d,%d)\n",
catTrack, catSect);
break;
}
catTrack = sctBuf[0x04];
catSect = sctBuf[0x05];
iterations++; // watch for infinite loops
}
if (iterations >= DiskFSGutenberg::kMaxCatalogSectors) {
/* possible cause: LF->CR conversion screws up link to sector $0a */
dierr = kDIErrDirectoryLoop;
WMSG1(" Gutenberg directory links cause a loop (order=%d)\n", imageOrder);
goto bail;
}
WMSG2(" Gutenberg foundGood=%d order=%d\n", foundGood, imageOrder);
*pGoodCount = foundGood;
bail:
return dierr;
}
/*
* Test to see if the image is a Gutenberg word processor data disk.
*/
/*static*/ DIError
DiskFSGutenberg::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder,
DiskImg::FSFormat* pFormat, FSLeniency leniency)
{
if (pImg->GetNumTracks() > kMaxInterestingTracks)
return kDIErrFilesystemNotFound;
DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax];
DiskImg::GetSectorOrderArray(ordering, *pOrder);
DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown;
int bestCount = 0;
for (int i = 0; i < DiskImg::kSectorOrderMax; i++) {
int goodCount = 0;
if (ordering[i] == DiskImg::kSectorOrderUnknown)
continue;
if (TestImage(pImg, ordering[i], &goodCount) == kDIErrNone) {
if (goodCount > bestCount) {
bestCount = goodCount;
bestOrder = ordering[i];
}
}
}
if (bestCount >= 2 ||
(leniency == kLeniencyVery && bestCount >= 1))
{
WMSG2(" Gutenberg test: bestCount=%d for order=%d\n", bestCount, bestOrder);
assert(bestOrder != DiskImg::kSectorOrderUnknown);
*pOrder = bestOrder;
*pFormat = DiskImg::kFormatGutenberg;
return kDIErrNone;
}
WMSG0(" Gutenberg didn't find a valid filesystem.\n");
return kDIErrFilesystemNotFound;
}
/*
* Get things rolling.
*
* Since we're assured that this is a valid disk, errors encountered from here
* on out must be handled somehow, possibly by claiming that the disk is
* completely full and has no files on it.
*/
DIError
DiskFSGutenberg::Initialize(InitMode initMode)
{
DIError dierr = kDIErrNone;
fVolumeUsage.Create(fpImg->GetNumTracks(), fpImg->GetNumSectPerTrack());
/* read the contents of the catalog, creating our A2File list */
dierr = ReadCatalog();
if (dierr != kDIErrNone)
goto bail;
/* run through and get file lengths and data offsets */
dierr = GetFileLengths();
if (dierr != kDIErrNone)
goto bail;
sprintf(fDiskVolumeID, "Gutenberg: %s\0", fDiskVolumeName);
fDiskIsGood = CheckDiskIsGood();
fVolumeUsage.Dump();
bail:
return dierr;
}
/*
* Get the amount of free space remaining.
*/
DIError
DiskFSGutenberg::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits,
int* pUnitSize) const
{
*pTotalUnits = fpImg->GetNumTracks() * fpImg->GetNumSectPerTrack();
*pFreeUnits = 0;
*pUnitSize = kSectorSize;
return kDIErrNone;
}
/*
* Read the disk's catalog.
*
*/
DIError
DiskFSGutenberg::ReadCatalog(void)
{
DIError dierr = kDIErrNone;
unsigned char sctBuf[kSctSize];
int catTrack, catSect;
int iterations;
catTrack = 17;
catSect = 7;
iterations = 0;
memset(fCatalogSectors, 0, sizeof(fCatalogSectors));
while (catTrack < 35 && catSect < 16 && iterations < kMaxCatalogSectors)
{
WMSG2(" Gutenberg reading catalog sector T=%d S=%d\n", catTrack, catSect);
dierr = fpImg->ReadTrackSector(catTrack, catSect, sctBuf);
if (dierr != kDIErrNone)
goto bail;
sprintf(fDiskVolumeName, (const char *)&sctBuf[6], kMaxVolNameLen);
fDiskVolumeName[kMaxVolNameLen] = 0x00;
DiskFSGutenberg::LowerASCII((unsigned char*)fDiskVolumeName, kMaxVolNameLen);
A2FileGutenberg::TrimTrailingSpaces(fDiskVolumeName);
dierr = ProcessCatalogSector(catTrack, catSect, sctBuf);
if (dierr != kDIErrNone)
goto bail;
fCatalogSectors[iterations].track = catTrack;
fCatalogSectors[iterations].sector = catSect;
catTrack = sctBuf[0x04];
catSect = sctBuf[0x05];
iterations++; // watch for infinite loops
}
if (iterations >= kMaxCatalogSectors) {
dierr = kDIErrDirectoryLoop;
goto bail;
}
bail:
return dierr;
}
/*
* Process the list of files in one sector of the catalog.
*
* Pass in the track, sector, and the contents of that track and sector.
* (We only use "catTrack" and "catSect" to fill out some fields.)
*/
DIError
DiskFSGutenberg::ProcessCatalogSector(int catTrack, int catSect,
const unsigned char* sctBuf)
{
A2FileGutenberg* pFile;
const unsigned char* pEntry;
int i;
pEntry = &sctBuf[kCatalogEntryOffset];
for (i = 0; i < kCatalogEntriesPerSect; i++) {
if (pEntry[0x0d] != kEntryDeleted && pEntry[0x00] != 0xa0 && pEntry[0x00] != 0x00) {
pFile = new A2FileGutenberg(this);
pFile->SetQuality(A2File::kQualityGood);
pFile->fTrack = pEntry[0x0c];
pFile->fSector = pEntry[0x0d];
memcpy(pFile->fFileName, &pEntry[0x00], A2FileGutenberg::kMaxFileName);
pFile->fFileName[A2FileGutenberg::kMaxFileName] = '\0';
pFile->FixFilename();
//pFile->fCatTS.track = catTrack;
//pFile->fCatTS.sector = catSect;
pFile->fCatEntryNum = i;
/* can't do these yet, so just set to defaults */
pFile->fLength = 0;
pFile->fSparseLength = 0;
pFile->fDataOffset = 0;
pFile->fLengthInSectors = 0;
pFile->fLengthInSectors = 0;
AddFileToList(pFile);
}
//if (pEntry[0x00] == 0xa0)
// break;
pEntry += kCatalogEntrySize;
}
return kDIErrNone;
}
/*
* Perform consistency checks on the filesystem.
*
* Returns "true" if disk appears to be perfect, "false" otherwise.
*/
bool
DiskFSGutenberg::CheckDiskIsGood(void)
{
bool result = true;
return result;
}
/*
* Run through our list of files, computing the lengths and marking file
* usage in the VolumeUsage object.
*/
DIError
DiskFSGutenberg::GetFileLengths(void)
{
A2FileGutenberg* pFile;
unsigned char sctBuf[kSctSize];
int tsCount = 0;
unsigned short currentTrack, currentSector;
pFile = (A2FileGutenberg*) GetNextFile(nil);
while (pFile != nil) {
DIError dierr;
tsCount = 0;
currentTrack = pFile->fTrack;
currentSector = pFile->fSector;
while (currentTrack < 0x80) {
tsCount ++;
dierr = fpImg->ReadTrackSector(currentTrack, currentSector, sctBuf);
if (dierr != kDIErrNone) {
WMSG1("Gutenberg failed loading track/sector for '%s'\n",
pFile->GetPathName());
goto bail;
}
currentTrack = sctBuf[0x04];
currentSector = sctBuf[0x05];
}
pFile->fLengthInSectors = tsCount;
pFile->fLength = tsCount * 250; // First six bytes of sector are t/s pointers
pFile = (A2FileGutenberg*) GetNextFile(pFile);
}
bail:
return kDIErrNone;
}
/*
* Convert high ASCII to low ASCII.
*
* Some people put inverse and flashing text into filenames, not to mention
* control characters, so we have to cope with those too.
*
* We modify the first "len" bytes of "buf" in place.
*/
/*static*/ void
DiskFSGutenberg::LowerASCII(unsigned char* buf, long len)
{
while (len--) {
if (*buf & 0x80) {
if (*buf >= 0xa0)
*buf &= 0x7f;
else
*buf = (*buf & 0x7f) + 0x20;
} else
*buf = ((*buf & 0x3f) ^ 0x20) + 0x20;
buf++;
}
}
/*
* ===========================================================================
* A2FileGutenberg
* ===========================================================================
*/
/*
* Constructor.
*/
A2FileGutenberg::A2FileGutenberg(DiskFS* pDiskFS) : A2File(pDiskFS)
{
fTrack = -1;
fSector = -1;
fLengthInSectors = 0;
fLocked = true;
fFileName[0] = '\0';
fFileType = kTypeText;
fCatTS.track = fCatTS.sector = 0;
fCatEntryNum = -1;
fAuxType = 0;
fDataOffset = 0;
fLength = -1;
fSparseLength = -1;
fpOpenFile = nil;
}
/*
* Destructor. Make sure an "open" file gets "closed".
*/
A2FileGutenberg::~A2FileGutenberg(void)
{
delete fpOpenFile;
}
/*
* Convert the filetype enum to a ProDOS type.
*
*/
long
A2FileGutenberg::GetFileType(void) const
{
return 0x04; // TXT;
}
/*
* "Fix" a filename. Convert DOS-ASCII to normal ASCII, and strip
* trailing spaces.
*/
void
A2FileGutenberg::FixFilename(void)
{
DiskFSGutenberg::LowerASCII((unsigned char*)fFileName, kMaxFileName);
TrimTrailingSpaces(fFileName);
}
/*
* Trim the spaces off the end of a filename.
*
* Assumes the filename has already been converted to low ASCII.
*/
/*static*/ void
A2FileGutenberg::TrimTrailingSpaces(char* filename)
{
char* lastspc = filename + strlen(filename);
assert(*lastspc == '\0');
while (--lastspc) {
if (*lastspc != ' ')
break;
}
*(lastspc+1) = '\0';
}
/*
* Encode a filename into high ASCII, padded out with spaces to
* kMaxFileName chars. Lower case is converted to upper case. This
* does not filter out control characters or other chunk.
*
* "buf" must be able to hold kMaxFileName+1 chars.
*/
/*static*/ void
A2FileGutenberg::MakeDOSName(char* buf, const char* name)
{
for (int i = 0; i < kMaxFileName; i++) {
if (*name == '\0')
*buf++ = (char) 0xa0;
else
*buf++ = toupper(*name++) | 0x80;
}
*buf = '\0';
}
/*
* Set up state for this file.
*/
DIError
A2FileGutenberg::Open(A2FileDescr** ppOpenFile, bool readOnly,
bool rsrcFork /*=false*/)
{
DIError dierr = kDIErrNone;
A2FDGutenberg* pOpenFile = nil;
if (!readOnly) {
if (fpDiskFS->GetDiskImg()->GetReadOnly())
return kDIErrAccessDenied;
if (fpDiskFS->GetFSDamaged())
return kDIErrBadDiskImage;
}
if (fpOpenFile != nil) {
dierr = kDIErrAlreadyOpen;
goto bail;
}
if (rsrcFork)
return kDIErrForkNotFound;
pOpenFile = new A2FDGutenberg(this);
pOpenFile->fOffset = 0;
pOpenFile->fOpenEOF = fLength;
pOpenFile->fOpenSectorsUsed = fLengthInSectors;
fpOpenFile = pOpenFile; // add it to our single-member "open file set"
*ppOpenFile = pOpenFile;
pOpenFile = nil;
bail:
delete pOpenFile;
return dierr;
}
/*
* Dump the contents of an A2FileGutenberg.
*/
void
A2FileGutenberg::Dump(void) const
{
WMSG1("A2FileGutenberg '%s'\n", fFileName);
WMSG2(" TS T=%-2d S=%-2d\n", fTrack, fSector);
WMSG2(" Cat T=%-2d S=%-2d\n", fCatTS.track, fCatTS.sector);
WMSG3(" type=%d lck=%d slen=%d\n", fFileType, fLocked, fLengthInSectors);
WMSG2(" auxtype=0x%04x length=%ld\n",
fAuxType, (long) fLength);
}
/*
* ===========================================================================
* A2FDGutenberg
* ===========================================================================
*/
/*
* Read data from the current offset.
*
*/
DIError
A2FDGutenberg::Read(void* buf, size_t len, size_t* pActual)
{
WMSG3(" Gutenberg reading %d bytes from '%s' (offset=%ld)\n",
len, fpFile->GetPathName(), (long) fOffset);
A2FileGutenberg* pFile = (A2FileGutenberg*) fpFile;
DIError dierr = kDIErrNone;
unsigned char sctBuf[kSctSize];
short currentTrack, currentSector;
di_off_t actualOffset = fOffset + pFile->fDataOffset; // adjust for embedded len
int bufOffset = 6;
size_t thisCount;
if (len == 0)
return kDIErrNone;
assert(fOpenEOF != 0);
currentTrack = pFile->fTrack;
currentSector = pFile->fSector;
/* could be more clever in here and avoid double-buffering */
while (len) {
dierr = pFile->GetDiskFS()->GetDiskImg()->ReadTrackSector(
currentTrack,
currentSector,
sctBuf);
if (dierr != kDIErrNone) {
WMSG1(" Gutenberg error reading file '%s'\n", pFile->GetPathName());
return dierr;
}
thisCount = kSctSize - bufOffset;
if (thisCount > len)
thisCount = len;
memcpy(buf, sctBuf + bufOffset, thisCount);
len -= thisCount;
buf = (char*)buf + thisCount;
currentTrack = sctBuf[0x04];
currentSector = sctBuf[0x05];
}
return dierr;
}
/*
* Writing Gutenberg files isn't supported.
*
*/
DIError
A2FDGutenberg::Write(const void* buf, size_t len, size_t* pActual)
{
return kDIErrNotSupported;
}
/*
* Seek to the specified offset.
*/
DIError
A2FDGutenberg::Seek(di_off_t offset, DIWhence whence)
{
return kDIErrNotSupported;
}
/*
* Return current offset.
*/
di_off_t
A2FDGutenberg::Tell(void)
{
return kDIErrNotSupported;
}
/*
* Release file state.
*
* If the file was modified, we need to update the sector usage count in
* the catalog track, and possibly a length word in the first sector of
* the file (for A/I/B).
*
* Given the current "write all at once" implementation of Write, we could
* have handled the length word back when initially writing the data, but
* someday we may fix that and I don't want to have to rewrite this part.
*
* Most applications don't check the value of "Close", or call it from a
* destructor, so we call CloseDescr whether we succeed or not.
*/
DIError
A2FDGutenberg::Close(void)
{
DIError dierr = kDIErrNone;
fpFile->CloseDescr(this);
return dierr;
}
/*
* Return the #of sectors/blocks in the file.
*/
long
A2FDGutenberg::GetSectorCount(void) const
{
return fTSCount;
}
long
A2FDGutenberg::GetBlockCount(void) const
{
return (fTSCount+1)/2;
}
/*
* Return the Nth track/sector in this file.
*
* Returns (0,0) for a sparse sector.
*/
DIError
A2FDGutenberg::GetStorage(long sectorIdx, long* pTrack, long* pSector) const
{
return kDIErrInvalidIndex;
}
/*
* Unimplemented
*/
DIError
A2FDGutenberg::GetStorage(long blockIdx, long* pBlock) const
{
return kDIErrInvalidIndex;
}

View File

@ -162,6 +162,10 @@ SOURCE=.\Global.cpp
# End Source File
# Begin Source File
SOURCE=.\Gutenberg.cpp
# End Source File
# Begin Source File
SOURCE=.\HFS.cpp
# End Source File
# Begin Source File