diff options
-rw-r--r-- | engines/adl/disk.cpp | 153 | ||||
-rw-r--r-- | engines/adl/disk.h | 16 |
2 files changed, 169 insertions, 0 deletions
diff --git a/engines/adl/disk.cpp b/engines/adl/disk.cpp index 22a7d7a431..1ec121831c 100644 --- a/engines/adl/disk.cpp +++ b/engines/adl/disk.cpp @@ -63,6 +63,159 @@ bool DiskImage_DSK::open(const Common::String &filename) { return true; } +const DataBlockPtr DiskImage_NIB::getDataBlock(uint track, uint sector, uint offset, uint size) const { + return Common::SharedPtr<DiskImage::DataBlock>(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; +} + +// 4-and-4 encoding (odd-even) +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(); +} + +bool DiskImage_NIB::open(const Common::String &filename) { + assert(!_f->isOpen()); + + if (!_f->open(filename)) + return false; + + uint filesize = _f->size(); + switch (filesize) { + case 232960: + _tracks = 35; + _sectorsPerTrack = 13; + _bytesPerSector = 256; + break; + default: + error("Unrecognized NIB image '%s' of size %d bytes", filename.c_str(), filesize); + } + + // 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 }; + + uint32 diskSize = _tracks * _sectorsPerTrack * _bytesPerSector; + byte *diskImage = (byte *)calloc(diskSize, 1); + _memStream = new Common::MemoryReadStream(diskImage, diskSize, DisposeAfterUse::YES); + + bool sawAddress = false; + uint8 volNo, track, sector; + + while (_f->pos() < _f->size()) { + // Read until we find two sync bytes. + if (_f->readByte() != 0xd5 || _f->readByte() != 0xaa) + continue; + + byte prologue = _f->readByte(); + + if (sawAddress && prologue == 0xb5) { + warning("NIB: data for %02x/%02x/%02x missing", volNo, track, sector); + sawAddress = false; + } + + if (!sawAddress) { + sawAddress = true; + + // We should always find the address field first. + if (prologue != 0xb5) { + // Accept a DOS 3.3(?) header at the start. + if (_f->pos() == 3 || prologue == 0x96) { + // But skip it. + _f->skip(20); + sawAddress = false; + continue; + } else { + error("unknown NIB field prologue %02x", prologue); + } + } + + volNo = read44(_f); + track = read44(_f); + sector = read44(_f); + uint8 checksum = read44(_f); + if ((volNo ^ track ^ sector) != checksum) + error("invalid NIB checksum"); + + // FIXME: This is a hires0/hires2-specific hack. + if (sector == 1) + sector = 2; + else if (sector == 2) + sector = 1; + + // Epilogue is de/aa plus a gap, but we don't care. + } else { + sawAddress = false; + + // We should always find the data field after an address field. + // TODO: we ignore volNo? + byte *output = diskImage + (track * _sectorsPerTrack + sector) * _bytesPerSector; + + // 5-and-3 uses 410 on-disk bytes, decoding to just over 256 bytes + byte inbuffer[410]; + _f->read(inbuffer, 410); + + bool truncated = false; + byte oldVal = 0; + for (uint n = 0; n < 410; ++n) { + // expand + assert(inbuffer[n] >= 0xaa); // corrupt file (TODO: assert?) + 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); + 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); + break; + } + // undo checksum + oldVal = val ^ oldVal; + inbuffer[n] = oldVal; + } + if (!truncated) { + byte checksum = _f->readByte(); + if (checksum < 0xaa || oldVal != c_5and3_lookup[checksum - 0xaa]) + warning("NIB: checksum mismatch @ (%x, %x)", track, sector); + } + + // 8 bytes of nibbles expand to 5 bytes + // so we have 51 of these batches (255 bytes), plus 2 bytes of 'leftover' nibbles for byte 256 + for (uint n = 0; n < 51; ++n) { + // e.g. figure 3.18 of Beneath Apple DOS + byte lowbits1 = inbuffer[51*3 - n]; + byte lowbits2 = inbuffer[51*2 - n]; + byte lowbits3 = inbuffer[51*1 - n]; + byte lowbits4 = (lowbits1 & 2) << 1 | (lowbits2 & 2) | (lowbits3 & 2) >> 1; + byte lowbits5 = (lowbits1 & 1) << 2 | (lowbits2 & 1) << 1 | (lowbits3 & 1); + output[250 - 5*n] = (inbuffer[n + 51*3 + 1] << 3) | ((lowbits1 >> 2) & 0x7); + output[251 - 5*n] = (inbuffer[n + 51*4 + 1] << 3) | ((lowbits2 >> 2) & 0x7); + output[252 - 5*n] = (inbuffer[n + 51*5 + 1] << 3) | ((lowbits3 >> 2) & 0x7); + output[253 - 5*n] = (inbuffer[n + 51*6 + 1] << 3) | lowbits4; + output[254 - 5*n] = (inbuffer[n + 51*7 + 1] << 3) | lowbits5; + } + output[255] = (inbuffer[409] << 3) | (inbuffer[0] & 0x7); + } + } + + return true; +} + const DataBlockPtr Files_Plain::getDataBlock(const Common::String &filename, uint offset) const { return Common::SharedPtr<Files::DataBlock>(new Files::DataBlock(this, filename, offset)); } diff --git a/engines/adl/disk.h b/engines/adl/disk.h index 4cab81d720..43b9e387ba 100644 --- a/engines/adl/disk.h +++ b/engines/adl/disk.h @@ -125,6 +125,22 @@ public: 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: |