From ff0bc115b57dc19f4860014bc3c7d72b67c9ce38 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Fri, 19 Aug 2016 15:45:44 +0200 Subject: [PATCH] ADL: Add support for 13-sector reading --- engines/adl/disk.cpp | 172 ++++++++++++++++++++++------------------- engines/adl/disk.h | 54 ++++--------- engines/adl/hires2.cpp | 10 ++- engines/adl/hires6.cpp | 6 +- engines/adl/hires6.h | 2 +- 5 files changed, 119 insertions(+), 125 deletions(-) diff --git a/engines/adl/disk.cpp b/engines/adl/disk.cpp index 214f76aeae9..6476fc5b095 100644 --- a/engines/adl/disk.cpp +++ b/engines/adl/disk.cpp @@ -28,98 +28,63 @@ namespace Adl { -const DataBlockPtr DiskImage_DSK::getDataBlock(uint track, uint sector, uint offset, uint size) const { - return Common::SharedPtr(new DiskImage::DataBlock(this, track, sector, offset, size)); -} +#define TRACKS 35 +// The Apple II uses either 13- or 16-sector disks. We currently pad out +// 13-sector disks, so we set SECTORS_PER_TRACK to 16 here. +#define SECTORS_PER_TRACK 16 +#define BYTES_PER_SECTOR 256 +#define RAW_IMAGE_SIZE(S) (TRACKS * (S) * BYTES_PER_SECTOR) +#define NIB_IMAGE_SIZE (RAW_IMAGE_SIZE(13) * 2) -Common::SeekableReadStream *DiskImage_DSK::createReadStream(uint track, uint sector, uint offset, uint size) const { - _f->seek((track * _sectorsPerTrack + sector) * _bytesPerSector + offset); - Common::SeekableReadStream *stream = _f->readStream(size * _bytesPerSector + _bytesPerSector - offset); +static Common::SeekableReadStream *readImage_DSK(const Common::String &filename) { + Common::File *f = new Common::File; - if (_f->eos() || _f->err()) - error("Error reading disk image"); - - return stream; -} - -bool DiskImage_DSK::open(const Common::String &filename) { - assert(!_f->isOpen()); - - if (!_f->open(filename)) - return false; - - uint filesize = _f->size(); - switch (filesize) { - case 143360: - _tracks = 35; - _sectorsPerTrack = 16; - _bytesPerSector = 256; - break; - default: - warning("Unrecognized disk image '%s' of size %d bytes", filename.c_str(), filesize); - return false; + if (!f->open(filename)) { + delete f; + return nullptr; } - return true; -} + if (f->size() != RAW_IMAGE_SIZE(16)) + error("Unrecognized DSK image '%s' of size %d bytes", filename.c_str(), f->size()); -const DataBlockPtr DiskImage_NIB::getDataBlock(uint track, uint sector, uint offset, uint size) const { - return Common::SharedPtr(new DiskImage::DataBlock(this, track, sector, offset, size)); -} - -Common::SeekableReadStream *DiskImage_NIB::createReadStream(uint track, uint sector, uint offset, uint size) const { - _memStream->seek((track * _sectorsPerTrack + sector) * _bytesPerSector + offset); - Common::SeekableReadStream *stream = _memStream->readStream(size * _bytesPerSector + _bytesPerSector - offset); - - if (_memStream->eos() || _memStream->err()) - error("Error reading NIB image"); - - return stream; + return f; } // 4-and-4 encoding (odd-even) -static uint8 read44(Common::SeekableReadStream *f) { +static uint8 read44(Common::SeekableReadStream &f) { // 1s in the other fields, so we can just AND - uint8 ret = f->readByte(); - return ((ret << 1) | 1) & f->readByte(); + uint8 ret = f.readByte(); + return ((ret << 1) | 1) & f.readByte(); } -bool DiskImage_NIB::open(const Common::String &filename) { - assert(!_f->isOpen()); +static Common::SeekableReadStream *readImage_NIB(const Common::String &filename) { + Common::File f; - if (!_f->open(filename)) - return false; + if (!f.open(filename)) + return nullptr; - uint filesize = _f->size(); - switch (filesize) { - case 232960: - _tracks = 35; - _sectorsPerTrack = 16; // we always pad it out - _bytesPerSector = 256; - break; - default: - error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), filesize); - } + if (f.size() != NIB_IMAGE_SIZE) + error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), f.size()); // starting at 0xaa, 32 is invalid (see below) const byte c_5and3_lookup[] = { 32, 0, 32, 1, 2, 3, 32, 32, 32, 32, 32, 4, 5, 6, 32, 32, 7, 8, 32, 9, 10, 11, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 12, 13, 32, 32, 14, 15, 32, 16, 17, 18, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 19, 20, 32, 21, 22, 23, 32, 32, 32, 32, 32, 24, 25, 26, 32, 32, 27, 28, 32, 29, 30, 31 }; // starting at 0x96, 64 is invalid (see below) const byte c_6and2_lookup[] = { 0, 1, 64, 64, 2, 3, 64, 4, 5, 6, 64, 64, 64, 64, 64, 64, 7, 8, 64, 64, 64, 9, 10, 11, 12, 13, 64, 64, 14, 15, 16, 17, 18, 19, 64, 20, 21, 22, 23, 24, 25, 26, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 27, 64, 28, 29, 30, 64, 64, 64, 31, 64, 64, 32, 33, 64, 34, 35, 36, 37, 38, 39, 40, 64, 64, 64, 64, 64, 41, 42, 43, 64, 44, 45, 46, 47, 48, 49, 50, 64, 64, 51, 52, 53, 54, 55, 56, 64, 57, 58, 59, 60, 61, 62, 63 }; - uint32 diskSize = _tracks * _sectorsPerTrack * _bytesPerSector; - byte *diskImage = (byte *)calloc(diskSize, 1); - _memStream = new Common::MemoryReadStream(diskImage, diskSize, DisposeAfterUse::YES); + // we always pad it out + const uint sectorsPerTrack = 16; + byte *diskImage = (byte *)calloc(RAW_IMAGE_SIZE(sectorsPerTrack), 1); bool sawAddress = false; uint8 volNo, track, sector; bool newStyle; - while (_f->pos() < _f->size()) { + while (f.pos() < f.size()) { // Read until we find two sync bytes. - if (_f->readByte() != 0xd5 || _f->readByte() != 0xaa) + if (f.readByte() != 0xd5 || f.readByte() != 0xaa) continue; - byte prologue = _f->readByte(); + byte prologue = f.readByte(); if (sawAddress && (prologue == 0xb5 || prologue == 0x96)) { warning("NIB: data for %02x/%02x/%02x missing", volNo, track, sector); @@ -140,10 +105,10 @@ bool DiskImage_NIB::open(const Common::String &filename) { } } - volNo = read44(_f); - track = read44(_f); - sector = read44(_f); - uint8 checksum = read44(_f); + volNo = read44(f); + track = read44(f); + sector = read44(f); + uint8 checksum = read44(f); if ((volNo ^ track ^ sector) != checksum) error("invalid NIB checksum"); @@ -163,17 +128,17 @@ bool DiskImage_NIB::open(const Common::String &filename) { // We should always find the data field after an address field. // TODO: we ignore volNo? - byte *output = diskImage + (track * _sectorsPerTrack + sector) * _bytesPerSector; + byte *output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; if (newStyle) { // We hardcode the DOS 3.3 mapping here. TODO: Do we also need raw/prodos? int raw2dos[16] = { 0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15 }; sector = raw2dos[sector]; - output = diskImage + (track * _sectorsPerTrack + sector) * _bytesPerSector; + output = diskImage + (track * sectorsPerTrack + sector) * BYTES_PER_SECTOR; // 6-and-2 uses 342 on-disk bytes byte inbuffer[342]; - _f->read(inbuffer, 342); + f.read(inbuffer, 342); byte oldVal = 0; for (uint n = 0; n < 342; ++n) { @@ -188,7 +153,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { inbuffer[n] = oldVal; } - byte checksum = _f->readByte(); + byte checksum = f.readByte(); if (checksum < 0x96 || oldVal != c_6and2_lookup[checksum - 0x96]) warning("NIB: checksum mismatch @ (%x, %x)", track, sector); @@ -208,7 +173,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { } else { // 5-and-3 uses 410 on-disk bytes, decoding to just over 256 bytes byte inbuffer[410]; - _f->read(inbuffer, 410); + f.read(inbuffer, 410); bool truncated = false; byte oldVal = 0; @@ -218,16 +183,16 @@ bool DiskImage_NIB::open(const Common::String &filename) { if (inbuffer[n] == 0xd5) { // Early end of block. truncated = true; - _f->seek(-(410 - n), SEEK_CUR); - warning("NIB: early end of block @ 0x%x (%x, %x)", _f->pos(), track, sector); + f.seek(-(410 - n), SEEK_CUR); + warning("NIB: early end of block @ 0x%x (%x, %x)", f.pos(), track, sector); break; } byte val = c_5and3_lookup[inbuffer[n] - 0xaa]; if (val == 0x20) { // Badly-encoded nibbles, stop trying to decode here. truncated = true; - warning("NIB: bad nibble %02x @ 0x%x (%x, %x)", inbuffer[n], _f->pos(), track, sector); - _f->seek(-(410 - n), SEEK_CUR); + warning("NIB: bad nibble %02x @ 0x%x (%x, %x)", inbuffer[n], f.pos(), track, sector); + f.seek(-(410 - n), SEEK_CUR); break; } // undo checksum @@ -235,7 +200,7 @@ bool DiskImage_NIB::open(const Common::String &filename) { inbuffer[n] = oldVal; } if (!truncated) { - byte checksum = _f->readByte(); + byte checksum = f.readByte(); if (checksum < 0xaa || oldVal != c_5and3_lookup[checksum - 0xaa]) warning("NIB: checksum mismatch @ (%x, %x)", track, sector); } @@ -259,7 +224,52 @@ bool DiskImage_NIB::open(const Common::String &filename) { } } - return true; + return new Common::MemoryReadStream(diskImage, RAW_IMAGE_SIZE(sectorsPerTrack), DisposeAfterUse::YES); +} + +bool DiskImage::open(const Common::String &filename) { + Common::String lcName(filename); + lcName.toLowercase(); + + if (lcName.hasSuffix(".dsk")) + _stream = readImage_DSK(filename); + else if (lcName.hasSuffix(".nib")) + _stream = readImage_NIB(filename); + + return _stream != nullptr; +} + +const DataBlockPtr DiskImage::getDataBlock(uint track, uint sector, uint offset, uint size) const { + return DataBlockPtr(new DiskImage::DataBlock(this, track, sector, offset, size, _mode13)); +} + +Common::SeekableReadStream *DiskImage::createReadStream(uint track, uint sector, uint offset, uint size, uint sectorsPerTrackToRead) const { + const uint bytesToRead = size * BYTES_PER_SECTOR + BYTES_PER_SECTOR - offset; + byte *const data = (byte *)malloc(bytesToRead); + uint dataOffset = 0; + + if (sector > sectorsPerTrackToRead - 1) + error("Sector %i is out of bounds for %i-sector reading", sector, sectorsPerTrackToRead); + + while (dataOffset < bytesToRead) { + uint bytesRemInTrack = (sectorsPerTrackToRead - 1 - sector) * BYTES_PER_SECTOR + BYTES_PER_SECTOR - offset; + _stream->seek((track * SECTORS_PER_TRACK + sector) * BYTES_PER_SECTOR + offset); + + if (bytesToRead - dataOffset < bytesRemInTrack) + bytesRemInTrack = bytesToRead - dataOffset; + + if (_stream->read(data + dataOffset, bytesRemInTrack) < bytesRemInTrack) + error("Error reading disk image"); + + ++track; + + sector = 0; + offset = 0; + + dataOffset += bytesRemInTrack; + } + + return new Common::MemoryReadStream(data, bytesToRead, DisposeAfterUse::YES); } const DataBlockPtr Files_Plain::getDataBlock(const Common::String &filename, uint offset) const { @@ -449,7 +459,7 @@ Common::SeekableReadStream *Files_DOS33::createReadStream(const Common::String & } bool Files_DOS33::open(const Common::String &filename) { - _disk = new DiskImage_DSK(); + _disk = new DiskImage(); if (!_disk->open(filename)) return false; diff --git a/engines/adl/disk.h b/engines/adl/disk.h index 43b9e387ba0..1041f0cebdc 100644 --- a/engines/adl/disk.h +++ b/engines/adl/disk.h @@ -73,41 +73,41 @@ protected: class DiskImage { public: DiskImage() : - _tracks(0), - _sectorsPerTrack(0), - _bytesPerSector(0) { - _f = new Common::File(); + _stream(nullptr), + _mode13(false) { } + + ~DiskImage() { + delete _stream; } - virtual ~DiskImage() { - delete _f; - } - - virtual bool open(const Common::String &filename) = 0; - virtual const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const = 0; - virtual Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0) const = 0; + bool open(const Common::String &filename); + const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const; + Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0, uint sectorsPerTrackToRead = 16) const; + void setMode13(bool enable) { _mode13 = enable; } protected: class DataBlock : public Adl::DataBlock { public: - DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size) : + DataBlock(const DiskImage *disk, uint track, uint sector, uint offset, uint size, bool mode13) : _track(track), _sector(sector), _offset(offset), _size(size), + _mode13(mode13), _disk(disk) { } Common::SeekableReadStream *createReadStream() const { - return _disk->createReadStream(_track, _sector, _offset, _size); + return _disk->createReadStream(_track, _sector, _offset, _size, (_mode13 ? 13 : 16)); } private: uint _track, _sector, _offset, _size; + bool _mode13; const DiskImage *_disk; }; - Common::File *_f; - uint _tracks, _sectorsPerTrack, _bytesPerSector; + Common::SeekableReadStream *_stream; + bool _mode13; // Older 13-sector format }; // Data in plain files @@ -117,30 +117,6 @@ public: Common::SeekableReadStream *createReadStream(const Common::String &filename, uint offset = 0) const; }; -// .DSK disk image - 35 tracks, 16 sectors per track, 256 bytes per sector -class DiskImage_DSK : public DiskImage { -public: - bool open(const Common::String &filename); - const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const; - Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0) const; -}; - -// .NIB disk image -class DiskImage_NIB : public DiskImage { -public: - DiskImage_NIB() : _memStream(nullptr) { } - virtual ~DiskImage_NIB() { - delete _memStream; - } - - bool open(const Common::String &filename); - const DataBlockPtr getDataBlock(uint track, uint sector, uint offset = 0, uint size = 0) const; - Common::SeekableReadStream *createReadStream(uint track, uint sector, uint offset = 0, uint size = 0) const; - -private: - Common::SeekableReadStream *_memStream; -}; - // Data in files contained in Apple DOS 3.3 disk image class Files_DOS33 : public Files { public: diff --git a/engines/adl/hires2.cpp b/engines/adl/hires2.cpp index d8e8a65e29a..14db237d820 100644 --- a/engines/adl/hires2.cpp +++ b/engines/adl/hires2.cpp @@ -34,6 +34,10 @@ namespace Adl { void HiRes2Engine::runIntro() const { + // This only works for the 16-sector re-release. The original + // release is not supported at this time, because we don't have + // access to it. + _disk->setMode13(false); StreamPtr stream(_disk->createReadStream(0x00, 0xd, 0x17, 1)); _display->setMode(DISPLAY_MODE_TEXT); @@ -45,15 +49,19 @@ void HiRes2Engine::runIntro() const { _display->printString(str); delay(2000); + + _disk->setMode13(true); } void HiRes2Engine::init() { _graphics = new Graphics_v2(*_display); - _disk = new DiskImage_DSK(); + _disk = new DiskImage(); if (!_disk->open(IDS_HR2_DISK_IMAGE)) error("Failed to open disk image '" IDS_HR2_DISK_IMAGE "'"); + _disk->setMode13(true); + StreamPtr stream(_disk->createReadStream(0x1f, 0x2, 0x00, 4)); for (uint i = 0; i < IDI_HR2_NUM_MESSAGES; ++i) diff --git a/engines/adl/hires6.cpp b/engines/adl/hires6.cpp index c42b4165a6f..465538fe263 100644 --- a/engines/adl/hires6.cpp +++ b/engines/adl/hires6.cpp @@ -69,7 +69,7 @@ static Common::MemoryReadStream *loadSectors(DiskImage *disk, byte track, byte s } void HiRes6Engine::runIntro() const { - DiskImage_DSK *boot(new DiskImage_DSK()); + DiskImage *boot(new DiskImage()); if (!boot->open(disks[0])) error("Failed to open disk image '%s'", disks[0]); @@ -109,7 +109,7 @@ void HiRes6Engine::runIntro() const { } void HiRes6Engine::init() { - _boot = new DiskImage_DSK(); + _boot = new DiskImage(); _graphics = new Graphics_v2(*_display); if (!_boot->open(disks[0])) @@ -177,7 +177,7 @@ void HiRes6Engine::init() { void HiRes6Engine::loadDisk(byte disk) { delete _disk; - _disk = new DiskImage_NIB(); + _disk = new DiskImage(); if (!_disk->open(disks[disk])) error("Failed to open disk image '%s'", disks[disk]); diff --git a/engines/adl/hires6.h b/engines/adl/hires6.h index 4bd2bcc7ccc..0f604d848c9 100644 --- a/engines/adl/hires6.h +++ b/engines/adl/hires6.h @@ -82,7 +82,7 @@ private: void loadDisk(byte disk); - DiskImage_DSK *_boot; + DiskImage *_boot; byte _currVerb, _currNoun; Common::Array _diskDataDesc; };