From 71ca8de7e65d535e97d87bb1d30cbc1d9098cc07 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Thu, 24 Mar 2016 19:01:01 +0100 Subject: ADL: Add support for Apple DOS 3.3 disk images --- engines/adl/disk.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++++++++-- engines/adl/disk.h | 46 ++++++++++++- 2 files changed, 227 insertions(+), 6 deletions(-) (limited to 'engines') diff --git a/engines/adl/disk.cpp b/engines/adl/disk.cpp index e2c3da3d5c..b7de55a78e 100644 --- a/engines/adl/disk.cpp +++ b/engines/adl/disk.cpp @@ -22,6 +22,7 @@ #include "common/stream.h" #include "common/substream.h" +#include "common/memstream.h" #include "adl/disk.h" @@ -80,12 +81,11 @@ bool DiskImage_DSK::open(const Common::String &filename) { _tracks = 35; _sectorsPerTrack = 16; _bytesPerSector = 256; - break; - default: - warning("Unrecognized disk image '%s' of size %d bytes", filename.c_str(), filesize); + return true; } - return true; + warning("Unrecognized disk image '%s' of size %d bytes", filename.c_str(), filesize); + return false; } Common::SeekableReadStream *FilesDataBlock::createReadStream() const { @@ -108,4 +108,183 @@ Common::SeekableReadStream *PlainFiles::createReadStream(const Common::String &f return new Common::SeekableSubReadStream(f, offset, f->size(), DisposeAfterUse::YES); } +Files_DOS33::~Files_DOS33() { + delete _disk; +} + +Files_DOS33::Files_DOS33() : + _disk(nullptr) { +} + +void Files_DOS33::readSectorList(TrackSector start, Common::Array &list) { + TrackSector index = start; + + while (index.track != 0) { + Common::ScopedPtr stream(_disk->createReadStream(index.track, index.sector)); + + stream->readByte(); + index.track = stream->readByte(); + index.sector = stream->readByte(); + + stream->seek(9, SEEK_CUR); + + // This only handles sequential files + TrackSector ts; + ts.track = stream->readByte(); + ts.sector = stream->readByte(); + + while (ts.track != 0) { + list.push_back(ts); + + ts.track = stream->readByte(); + ts.sector = stream->readByte(); + + if (stream->err()) + error("Error reading sector list"); + + if (stream->eos()) + break; + } + } +} + +void Files_DOS33::readVTOC() { + Common::ScopedPtr stream(_disk->createReadStream(0x11, 0x00)); + stream->readByte(); + byte track = stream->readByte(); + byte sector = stream->readByte(); + + while (track != 0) { + char name[kFilenameLen + 1] = { }; + + stream.reset(_disk->createReadStream(track, sector)); + stream->readByte(); + track = stream->readByte(); + sector = stream->readByte(); + stream->seek(8, SEEK_CUR); + + for (uint i = 0; i < 7; ++i) { + TOCEntry entry; + TrackSector sectorList; + sectorList.track = stream->readByte(); + sectorList.sector = stream->readByte(); + entry.type = stream->readByte(); + stream->read(name, kFilenameLen); + + // Convert to ASCII + for (uint j = 0; j < kFilenameLen; j++) + name[j] &= 0x7f; + + // Strip trailing spaces + for (int j = kFilenameLen - 1; j >= 0; --j) { + if (name[j] == ' ') + name[j] = 0; + else + break; + } + + entry.totalSectors = stream->readUint16BE(); + + if (sectorList.track != 0) { + readSectorList(sectorList, entry.sectors); + _toc[name] = entry; + } + } + } +} + +const DataBlockPtr Files_DOS33::getDataBlock(const Common::String &filename, uint offset) const { + return Common::SharedPtr(new FilesDataBlock(this, filename, offset)); +} + +Common::SeekableReadStream *Files_DOS33::createReadStreamText(const TOCEntry &entry) const { + byte *buf = (byte *)malloc(entry.sectors.size() * kSectorSize); + byte *p = buf; + + for (uint i = 0; i < entry.sectors.size(); ++i) { + Common::ScopedPtr stream(_disk->createReadStream(entry.sectors[i].track, entry.sectors[i].sector)); + + assert(stream->size() == kSectorSize); + + while (true) { + byte textChar = stream->readByte(); + + if (stream->eos() || textChar == 0) + break; + + if (stream->err()) + error("Error reading text file"); + + *p++ = textChar; + } + } + + return new Common::MemoryReadStream(buf, p - buf, DisposeAfterUse::YES); +} + +Common::SeekableReadStream *Files_DOS33::createReadStreamBinary(const TOCEntry &entry) const { + byte *buf = (byte *)malloc(entry.sectors.size() * kSectorSize); + + Common::ScopedPtr stream(_disk->createReadStream(entry.sectors[0].track, entry.sectors[0].sector)); + + if (entry.type == kFileTypeBinary) + stream->readUint16LE(); // Skip start address + + uint16 size = stream->readUint16LE(); + uint16 offset = 0; + uint16 sectorIdx = 1; + + while (true) { + offset += stream->read(buf + offset, size - offset); + + if (offset == size) + break; + + if (stream->err()) + error("Error reading binary file"); + + assert(stream->eos()); + + if (sectorIdx == entry.sectors.size()) + error("Not enough sectors for binary file size"); + + stream.reset(_disk->createReadStream(entry.sectors[sectorIdx].track, entry.sectors[sectorIdx].sector)); + ++sectorIdx; + } + + return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES); +} + +Common::SeekableReadStream *Files_DOS33::createReadStream(const Common::String &filename, uint offset) const { + if (!_toc.contains(filename)) + error("Failed to locate '%s'", filename.c_str()); + + const TOCEntry &entry = _toc[filename]; + + Common::SeekableReadStream *stream; + + switch(entry.type) { + case kFileTypeText: + stream = createReadStreamText(entry); + break; + case kFileTypeAppleSoft: + case kFileTypeBinary: + stream = createReadStreamBinary(entry); + break; + default: + error("Unsupported file type %i", entry.type); + } + + return new Common::SeekableSubReadStream(stream, offset, stream->size(), DisposeAfterUse::YES); +} + +bool Files_DOS33::open(const Common::String &filename) { + _disk = new DiskImage_DSK(); + if (!_disk->open(filename)) + return false; + + readVTOC(); + return true; +} + } // End of namespace Adl diff --git a/engines/adl/disk.h b/engines/adl/disk.h index b6a5c60262..799fa371df 100644 --- a/engines/adl/disk.h +++ b/engines/adl/disk.h @@ -22,6 +22,7 @@ #include "common/ptr.h" #include "common/file.h" +#include "common/debug.h" #ifndef ADL_DISK_H #define ADL_DISK_H @@ -47,8 +48,8 @@ class Files { public: virtual ~Files() { } - virtual const DataBlockPtr getDataBlock(const Common::String &filename, uint offset) const = 0; - virtual Common::SeekableReadStream *createReadStream(const Common::String &filename, uint offset) const = 0; + virtual const DataBlockPtr getDataBlock(const Common::String &filename, uint offset = 0) const = 0; + virtual Common::SeekableReadStream *createReadStream(const Common::String &filename, uint offset = 0) const = 0; }; class FilesDataBlock : public DataBlock { @@ -103,6 +104,47 @@ public: Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0) const; }; +class Files_DOS33 : public Files { +public: + Files_DOS33(); + ~Files_DOS33(); + + bool open(const Common::String &filename); + const DataBlockPtr getDataBlock(const Common::String &filename, uint offset = 0) const; + Common::SeekableReadStream *createReadStream(const Common::String &filename, uint offset = 0) const; + +private: + enum FileType { + kFileTypeText = 0, + kFileTypeAppleSoft = 2, + kFileTypeBinary = 4 + }; + + enum { + kSectorSize = 256, + kFilenameLen = 30 + }; + + struct TrackSector { + byte track; + byte sector; + }; + + struct TOCEntry { + byte type; + uint16 totalSectors; + Common::Array sectors; + }; + + void readVTOC(); + void readSectorList(TrackSector start, Common::Array &list); + Common::SeekableReadStream *createReadStreamText(const TOCEntry &entry) const; + Common::SeekableReadStream *createReadStreamBinary(const TOCEntry &entry) const; + + DiskImage_DSK *_disk; + Common::HashMap _toc; +}; + } // End of namespace Adl #endif -- cgit v1.2.3