From 7fb0604b7665d7e7bec36f22182ffd950d59e130 Mon Sep 17 00:00:00 2001 From: ksherlock Date: Sat, 19 Feb 2011 18:06:42 +0000 Subject: [PATCH] new branch to integrate BlockDevice, BlockCache git-svn-id: https://profuse.googlecode.com/svn/branches/profuse_interim@332 aa027e90-d47c-11dd-86d7-074df07e0730 --- Device/Adaptor.cpp | 485 ++++++++++++++++++++++++++++++++++ Device/Adaptor.h | 81 ++++++ Device/BlockDevice.cpp | 241 +++++++++++++++++ Device/BlockDevice.h | 65 +++++ Device/DavexDiskImage.cpp | 165 ++++++++++++ Device/DavexDiskImage.h | 39 +++ Device/DiskCopy42Image.cpp | 228 ++++++++++++++++ Device/DiskCopy42Image.h | 38 +++ Device/DiskImage.cpp | 192 ++++++++++++++ Device/DiskImage.h | 94 +++++++ Device/RawDevice.cpp | 320 ++++++++++++++++++++++ Device/RawDevice.h | 57 ++++ Device/TrackSector.h | 19 ++ Device/UniversalDiskImage.cpp | 154 +++++++++++ Device/UniversalDiskImage.h | 41 +++ 15 files changed, 2219 insertions(+) create mode 100644 Device/Adaptor.cpp create mode 100644 Device/Adaptor.h create mode 100644 Device/BlockDevice.cpp create mode 100644 Device/BlockDevice.h create mode 100644 Device/DavexDiskImage.cpp create mode 100644 Device/DavexDiskImage.h create mode 100644 Device/DiskCopy42Image.cpp create mode 100644 Device/DiskCopy42Image.h create mode 100644 Device/DiskImage.cpp create mode 100644 Device/DiskImage.h create mode 100644 Device/RawDevice.cpp create mode 100644 Device/RawDevice.h create mode 100644 Device/TrackSector.h create mode 100644 Device/UniversalDiskImage.cpp create mode 100644 Device/UniversalDiskImage.h diff --git a/Device/Adaptor.cpp b/Device/Adaptor.cpp new file mode 100644 index 0000000..75b3846 --- /dev/null +++ b/Device/Adaptor.cpp @@ -0,0 +1,485 @@ + + + +#include +#include + +#include + +using namespace Device; + + +Adaptor::~Adaptor() +{ +} + + + +POAdaptor::POAdaptor(void *address) +{ + _address = (uint8_t *)address; +} + +void POAdaptor::readBlock(unsigned block, void *bp) +{ + std::memcpy(bp, _address + block * 512, 512); +} + +void POAdaptor::writeBlock(unsigned block, const void *bp) +{ + std::memcpy(_address + block * 512, bp, 512); +} + + +unsigned DOAdaptor::Map[] = { + 0x00, 0x0e, 0x0d, 0x0c, + 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, + 0x03, 0x02, 0x01, 0x0f +}; + +DOAdaptor::DOAdaptor(void *address) +{ + _address = (uint8_t *)address; +} + +void DOAdaptor::readBlock(unsigned block, void *bp) +{ + + unsigned track = (block & ~0x07) << 9; + unsigned sector = (block & 0x07) << 1; + + for (unsigned i = 0; i < 2; ++i) + { + size_t offset = track | (Map[sector+i] << 8); + + std::memcpy(bp, _address + offset, 256); + + bp = (uint8_t *)bp + 256; + } +} + +void DOAdaptor::writeBlock(unsigned block, const void *bp) +{ + unsigned track = (block & ~0x07) << 9; + unsigned sector = (block & 0x07) << 1; + + for (unsigned i = 0; i < 2; ++i) + { + size_t offset = track | (Map[sector+i] << 8); + + std::memcpy(_address + offset, bp, 256); + bp = (uint8_t *)bp + 256; + } +} + + + +#pragma mark - +#pragma mark NibbleAdaptor + +class CircleBuffer { + +public: + + CircleBuffer(void *address, unsigned length) + { + _address = (uint8_t *)address; + _length = length; + } + + uint8_t operator[](unsigned i) const + { + if (i >= _length) i %= _length; + return _address[i]; + } + + uint8_t& operator[](unsigned i) + { + if (i >= _length) i %= _length; + return _address[i]; + } + +private: + uint8_t *_address; + unsigned _length; + +}; + + + +uint8_t NibbleAdaptor::decode44(uint8_t x, uint8_t y) +{ + return ((x << 1) | 0x01) & y; +} + +std::pair NibbleAdaptor::encode44(uint8_t val) +{ + uint8_t x = (val >> 1) | 0xaa; + uint8_t y = val | 0xaa; + + return std::make_pair(x,y); +} + +uint8_t NibbleAdaptor::encode62(uint8_t val) +{ +#undef __METHOD__ +#define __METHOD__ "NibbleAdaptor::encode62" + + static uint8_t table[64] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + + if (val > 0x3f) + throw ProFUSE::Exception(__METHOD__ ": Invalid 6-2 value."); + + return table[val]; +} + + +uint8_t NibbleAdaptor::decode62(uint8_t val) +{ +#undef __METHOD__ +#define __METHOD__ "decode62" + + // auto-generated via perl. + static uint8_t table[] = { + -1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, 3, -1, 4, 5, 6, + -1, -1, -1, -1, -1, -1, 7, 8, -1, -1, -1, 9, 10, 11, 12, 13, + -1, -1, 14, 15, 16, 17, 18, 19, -1, 20, 21, 22, 23, 24, 25, 26, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, 28, 29, 30, + -1, -1, -1, 31, -1, -1, 32, 33, -1, 34, 35, 36, 37, 38, 39, 40, + -1, -1, -1, -1, -1, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50, + -1, -1, 51, 52, 53, 54, 55, 56, -1, 57, 58, 59, 60, 61, 62, 63 + }; + + if ((val < 0x90) || (table[val - 0x90] == 0xff)) + throw ProFUSE::Exception(__METHOD__ ": Invalid 6-2 encoding."); + + return table[val - 0x90]; +} + + + +static int FindByte(void *address, uint8_t c, unsigned length, unsigned offset = 0) +{ + + for (unsigned i = offset; i < length; ++i) + { + if ( ((uint8_t *)address)[i] == c) return i; + } + + return -1; +} + + + +/* + * Address Field: + * prologue volume track sector checksum epilogue + * D5 AA 96 XX YY XX YY XX YY XX YY DE AA EB + */ + +/* + * Data Field: + * prologue user data checksum epilogue + * D5 AA AD [6+2 encoded] XX DE AA EB + */ + + + +NibbleAdaptor::NibbleAdaptor(void *address, unsigned length) +{ +#undef __METHOD__ +#define __METHOD__ "NibbleAdaptor::NibbleAdaptor" + + _address = (uint8_t *)address; + _length = length; + + + // build a map of track/sectors. + + unsigned state = 0; + + + _index.resize(35 * 16, -1); + + int offset = 0; + + unsigned track = 0; + unsigned sector = 0; + unsigned volume = 0; + unsigned checksum = 0; + + + CircleBuffer buffer(_address, _length); + for (;;) + { + + offset = FindByte(address, 0xd5, length, offset); + if (offset < 0) break; + + if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0x96 && buffer[offset + 11] == 0xde && buffer[offset + 12] == 0xaa) + { + volume = decode44(buffer[offset + 3], buffer[offset + 4]); + track = decode44(buffer[offset + 5], buffer[offset + 6]); + sector = decode44(buffer[offset + 7], buffer[offset + 8]); + checksum = decode44(buffer[offset + 9], buffer[offset + 10]); + + if (volume ^ track ^ sector ^ checksum) + throw ProFUSE::Exception(__METHOD__ ": Invalid address checksum."); + + if (track > 35 || sector > 16) + throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector."); + + offset += 3 + 8 + 3; + + state = 1; + continue; + + } + + if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad && state == 1) + { + if (_index[track * 16 + sector] != -1) + { + std::fprintf(stderr, "track %u sector %u duplicated.\n", track, sector); + } + _index[track * 16 + sector] = (offset + 3) % _length; + + //offset += 3 + 342 + 1 + 3; + offset++; + + state = 0; + continue; + } + + offset++; //??? + // ???? + + } + + // possible wraparound. + if (state == 1) + { + offset = FindByte(address, 0xd5, length, 0); + + if (offset >= 0) + { + + if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad) + { + _index[track * 16 + sector] = (offset + 3) % _length; + } + } + } + + + // now check _index for offset = -1, which means the sector/track wasn't found. + + for (std::vector::iterator iter = _index.begin(); iter != _index.end(); ++iter) + { + if (*iter == -1) + { + int offset = distance(_index.begin(), iter); + std::fprintf(stderr, "Error: track %u sector %u missing.\n", offset / 16, offset % 16); + //throw ProFUSE::Exception(__METHOD__ ": Sector missing."); + } + } + +} + +NibbleAdaptor::~NibbleAdaptor() +{ +} + +void NibbleAdaptor::readBlock(unsigned block, void *bp) +{ + + unsigned track = (block & ~0x07) << 9; + unsigned b = (block & 0x07) << 1; + + /* + * block sectors + * 0 0, 2 + * 1 4, 6 + * 2 8, 10 + * 3 12,14 + * 4 1, 3 + * 5 5, 7 + * 6 9, 11 + * 7 13, 15 + */ + + unsigned sector = b >> 2; + if (sector >= 16) sector -= 15; + + + readTrackSector(TrackSector(track, sector), bp); + readTrackSector(TrackSector(track, sector + 1), (uint8_t *)bp + 256); +} + +void NibbleAdaptor::writeBlock(unsigned block, const void *bp) +{ + unsigned track = (block & ~0x07) << 9; + unsigned b = (block & 0x07) << 1; + + /* + * block sectors + * 0 0, 2 + * 1 4, 6 + * 2 8, 10 + * 3 12,14 + * 4 1, 3 + * 5 5, 7 + * 6 9, 11 + * 7 13, 15 + */ + + unsigned sector = b >> 2; + if (sector >= 16) sector -= 15; + + + writeTrackSector(TrackSector(track, sector), bp); + writeTrackSector(TrackSector(track, sector + 1), (const uint8_t *)bp + 256); +} + +void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "NibbleAdaptor::readTrackSector" + + if (ts.track > 35 || ts.sector > 16) + throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector."); + + + CircleBuffer buffer(_address, _length); + uint8_t bits[86 * 3]; + + uint8_t checksum = 0; + + + unsigned offset = _index[ts.track * 16 + ts.sector]; + + if (offset == -1) + { + throw ProFUSE::Exception(__METHOD__ ": Missing track/sector."); + } + + // first 86 bytes are in the auxbuffer, backwards. + unsigned index = offset; + for (unsigned i = 0; i < 86; ++i) + { + uint8_t x = buffer[index++]; + x = decode62(x); + + checksum ^= x; + + uint8_t y = checksum; + + /* + for (unsigned j = 0; j < 3; ++j) + { + //bits[i + j * 86] = ((y & 0x01) << 1) | ((y & 0x02) >> 1); + bits[i + j * 86] = "\x00\x01\x02\x03"[y & 0x03]; + y >>= 2; + } + */ + bits[i + 86 * 0] = "\x00\x02\x01\x03"[y & 0x03]; + bits[i + 86 * 1] = "\x00\x02\x01\x03"[(y >> 2) & 0x03]; + bits[i + 86 * 2] = "\x00\x02\x01\x03"[(y >> 4) & 0x03]; + + } + + for (unsigned i = 0; i < 256; ++i) + { + uint8_t x = buffer[index++]; + x = decode62(x); + + checksum ^= x; + + uint8_t y = (checksum << 2) | bits[i]; + + + + ((uint8_t *)bp)[i] = y; + } + + if (checksum != decode62(buffer[index++])) + std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector); + //throw ProFUSE::Exception(__METHOD__ ": Invalid field checksum."); + +} + +void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "NibbleAdaptor::writeTrackSector" + + if (ts.track > 35 || ts.sector > 16) + throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector."); + + uint8_t auxBuffer[86]; + uint8_t checksum = 0; + + // create the aux buffer. + + std::memset(auxBuffer, 0, sizeof(auxBuffer)); + + for (unsigned i = 0, j = 0, shift = 0; i < 256; ++i) + { + uint8_t x = ((const uint8_t *)bp)[i]; + + // grab the bottom 2 bytes and reverse them. + //uint8_t y = ((x & 0x01) << 1) | ((x & 0x02) >> 1); + uint8_t y = "\x00\x02\x01\x03"[x & 0x03]; + + auxBuffer[j++] |= (y << shift); + + if (j == 86) + { + j = 0; + shift += 2; + } + } + + unsigned offset = _index[ts.track * 16 + ts.sector]; + + CircleBuffer buffer(_address, _length); + // create the checksum while writing to disk.. + + // aux buffer + for (unsigned i = 0; i < 86; ++i) + { + uint8_t x = auxBuffer[i]; + + buffer[offset + i] = encode62(x ^ checksum); + + checksum = x; + } + + for (unsigned i = 0; i < 256; ++i) + { + uint8_t x = ((const uint8_t *)bp)[i]; + x >>= 2; + + buffer[offset + 86 + i] = encode62(x ^ checksum); + + checksum = x; + } + + + + buffer[offset + 342] = encode62(checksum); +} + diff --git a/Device/Adaptor.h b/Device/Adaptor.h new file mode 100644 index 0000000..00617ed --- /dev/null +++ b/Device/Adaptor.h @@ -0,0 +1,81 @@ +#ifndef __DEVICE_ADAPTOR_H__ +#define __DEVICE_ADAPTOR_H__ + +#include +#include + +#include + +#include + +namespace Device { + + class Adaptor + { + public: + virtual ~Adaptor(); + virtual void readBlock(unsigned block, void *bp) = 0; + virtual void writeBlock(unsigned block, const void *bp) = 0; + }; + + + class POAdaptor : public Adaptor + { + public: + POAdaptor(void *address); + virtual void readBlock(unsigned block, void *bp); + virtual void writeBlock(unsigned block, const void *bp); + private: + uint8_t *_address; + }; + + class DOAdaptor : public Adaptor + { + public: + DOAdaptor(void *address); + virtual void readBlock(unsigned block, void *bp); + virtual void writeBlock(unsigned block, const void *bp); + + + static unsigned Map[]; + + private: + uint8_t *_address; + + }; + + // TODO -- nibble adaptor. + + + class NibbleAdaptor : public Adaptor + { + public: + + NibbleAdaptor(void *address, unsigned length); + virtual ~NibbleAdaptor(); + + virtual void readBlock(unsigned block, void *bp); + virtual void writeBlock(unsigned block, const void *bp); + + + virtual void readTrackSector(TrackSector ts, void *bp); + virtual void writeTrackSector(TrackSector ts, const void *bp); + + static std::pairencode44(uint8_t); + static uint8_t decode44(uint8_t, uint8_t); + + static uint8_t encode62(uint8_t); + static uint8_t decode62(uint8_t); + + + private: + uint8_t *_address; + unsigned _length; + + std::vector _index; + }; + +} + + +#endif diff --git a/Device/BlockDevice.cpp b/Device/BlockDevice.cpp new file mode 100644 index 0000000..d86f4d7 --- /dev/null +++ b/Device/BlockDevice.cpp @@ -0,0 +1,241 @@ + +#include +#include +#include + +#include +#include +#include +#include + + +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace Device; + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + + + +unsigned BlockDevice::ImageType(const char *type, unsigned defv) +{ + const char *tmp; + + + if (type == 0 || *type == 0) return defv; + + // type could be a path, eg images/file, disk.images/file + + // unix-specifix. + // basename alters the input string + tmp = std::strrchr(type, '/'); + if (tmp) type = tmp + 1; + + // type could be a filename, in which case we check the extension. + tmp = std::strrchr(type, '.'); + if (tmp) type = tmp + 1; + if (*type == 0) return defv; + + + if (::strcasecmp(type, "2mg") == 0) + return '2IMG'; + if (::strcasecmp(type, "2img") == 0) + return '2IMG'; + + if (::strcasecmp(type, "dc42") == 0) + return 'DC42'; + + if (::strcasecmp(type, "po") == 0) + return 'PO__'; + if (::strcasecmp(type, "dmg") == 0) + return 'PO__'; + + if (::strcasecmp(type, "dsk") == 0) + return 'DO__'; + if (::strcasecmp(type, "do") == 0) + return 'DO__'; + + if (::strcasecmp(type, "dvx") == 0) + return 'DVX_'; + if (::strcasecmp(type, "davex") == 0) + return 'DVX_'; + + + // not supported yet. + if (::strcasecmp(type, "sdk") == 0) + return 'SDK_'; + + return defv; +} + +BlockDevice *BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType) +{ +#undef __METHOD__ +#define __METHOD__ "BlockDevice::Open" + + struct stat st; + + std::memset(&st, 0, sizeof(st)); + + if (::stat(name, &st) != 0) + { + throw POSIXException(__METHOD__ ": stat error", errno); + } + + if (!imageType) + { + // /dev/xxxx + if (S_ISBLK(st.st_mode)) + return RawDevice::Open(name, flags); + + + imageType = ImageType(name, 'PO__'); + } + + + // TODO -- if no image type, guess based on file size? + + MappedFile file(name, flags); + + + switch (imageType) + { + case '2IMG': + return UniversalDiskImage::Open(&file); + + case 'DC42': + return DiskCopy42Image::Open(&file); + + case 'DO__': + return DOSOrderDiskImage::Open(&file); + + case 'PO__': + return ProDOSOrderDiskImage::Open(&file); + + case 'DVX_': + return DavexDiskImage::Open(&file); + + } + + // throw an error? + return NULL; + +} + + +// return the basename, without an extension. +static std::string filename(const std::string& src) +{ + unsigned start; + unsigned end; + + + if (src.empty()) return std::string(""); + + start = end = 0; + + for(unsigned i = 0, l = src.length(); i < l; ++i) + { + char c = src[i]; + if (c == '/') start = end = i + 1; + if (c == '.') end = i; + } + + if (start == src.length()) return std::string(""); + + if (start == end) return src.substr(start); + return src.substr(start, end - start); +} + + +BlockDevice *BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType) +{ + std::string xname; + + if (!imageType) imageType = ImageType(fname, 'PO__'); + + if (vname == NULL) + { + xname = filename(std::string(fname)); + vname = xname.c_str(); + } + + + + + switch(imageType) + { + case '2IMG': + return UniversalDiskImage::Create(fname, blocks); + + case 'DC42': + return DiskCopy42Image::Create(fname, blocks, vname); + + case 'DO__': + return DOSOrderDiskImage::Create(fname, blocks); + + case 'PO__': + return ProDOSOrderDiskImage::Create(fname, blocks); + + case 'DVX_': + return DavexDiskImage::Create(fname, blocks, vname); + } + + return NULL; + +} + + + + + +BlockDevice::BlockDevice() +{ +} +BlockDevice::~BlockDevice() +{ +} + +void BlockDevice::zeroBlock(unsigned block) +{ + uint8_t bp[512]; + std::memset(bp, 0, 512); + + write(block, bp); +} + + + +bool BlockDevice::mapped() +{ + return false; +} + +void BlockDevice::sync(unsigned block) +{ + sync(); +} + +/* +void BlockDevice::sync(TrackSector ts) +{ + sync(); +} +*/ + +BlockCache *BlockDevice::createBlockCache() +{ + unsigned b = blocks(); + unsigned size = std::max(16u, b / 16); + return new ConcreteBlockCache(this, size); +} diff --git a/Device/BlockDevice.h b/Device/BlockDevice.h new file mode 100644 index 0000000..8e8feb5 --- /dev/null +++ b/Device/BlockDevice.h @@ -0,0 +1,65 @@ +#ifndef __BLOCKDEVICE_H__ +#define __BLOCKDEVICE_H__ + +#include +#include + +#include + +#include + +#include + +#include + +namespace Device { + +class BlockDevice { +public: + + + // static methods. + static unsigned ImageType(const char *type, unsigned defv = 0); + + static BlockDevice *Open(const char *name, File::FileFlags flags, unsigned imageType = 0); + static BlockDevice *Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 0); + + + + + + virtual ~BlockDevice(); + + virtual BlockCache *createBlockCache(); + + + virtual void read(unsigned block, void *bp) = 0; + //virtual void read(TrackSector ts, void *bp) = 0 + + virtual void write(unsigned block, const void *bp) = 0; + //virtual void write(TrackSector ts, const void *bp) = 0; + + + virtual unsigned blocks() = 0; + + virtual bool mapped(); + + virtual bool readOnly() = 0; + + virtual void sync() = 0; + virtual void sync(unsigned block); + //virtual void sync(TrackSector ts); + + + void zeroBlock(unsigned block); + +protected: + BlockDevice(); + +}; + + + +} + +#endif diff --git a/Device/DavexDiskImage.cpp b/Device/DavexDiskImage.cpp new file mode 100644 index 0000000..e63a04f --- /dev/null +++ b/Device/DavexDiskImage.cpp @@ -0,0 +1,165 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +using namespace Device; +using namespace LittleEndian; + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + +/* + http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004 + */ + +static const char *IdentityCheck = "\x60VSTORE [Davex]\x00"; + + +// private, validation already performed. +DavexDiskImage::DavexDiskImage(MappedFile *file) : + DiskImage(file) +{ + // at this point, file is no longer valid. + + + // 512-bytes header + setBlocks((length() / 512) - 1); + setAdaptor(new POAdaptor(512 + (uint8_t *)address())); +} + + +DavexDiskImage::~DavexDiskImage() +{ + // scan and update usedBlocks? +} + +void DavexDiskImage::Validate(MappedFile *f) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Validate" + + size_t size = f->length(); + const void * data = f->address(); + bool ok = false; + unsigned blocks = (size / 512) - 1; + + do { + if (size < 512) break; + if (size % 512) break; + + // identity. + if (std::memcmp(data, IdentityCheck, 16)) + break; + + // file format. + if (Read8(data, 0x10) != 0) + break; + + // total blocks + if (Read32(data, 33) != blocks) + break; + + // file number -- must be 1 + if (Read8(data, 64) != 1) + break; + + ok = true; + } while (false); + + + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); +} + +DavexDiskImage *DavexDiskImage::Open(MappedFile *file) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Open" + Validate(file); + + return new DavexDiskImage(file); +} + +DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks) +{ + return Create(name, blocks, "Untitled"); +} +DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const char *vname) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Create" + + uint8_t *data; + uint8_t tmp[512]; + IOBuffer header(tmp,512); + + + MappedFile *file = MappedFile::Create(name, blocks * 512 + 512); + + data = (uint8_t *)file->address(); + + header.writeBytes(IdentityCheck, 16); + // file Format + header.write8(0); + //version + header.write8(0); + // version + header.write8(0x10); + + // reserved. + header.setOffset(32, true); + + //deviceNum + header.write8(1); + + // total blocks + header.write32(blocks); + + // unused blocks + header.write32(0); + + // volume Name + if (!vname || !*vname) vname = "Untitled"; + unsigned l = std::strlen(vname); + header.write8(std::min(l, 15u)); + header.writeBytes(vname, std::min(l, 15u)); + + // name + reserved. + header.setOffset(64, true); + + // file number + header.write8(1); + + //starting block + header.write32(0); + + // reserved + header.setOffset(512, true); + + + std::memcpy(file->address(), header.buffer(), 512); + file->sync(); + + return new DavexDiskImage(file); +} + + +BlockCache *DavexDiskImage::createBlockCache() +{ + return new MappedBlockCache(this, 512 + (uint8_t *)address()); + +} diff --git a/Device/DavexDiskImage.h b/Device/DavexDiskImage.h new file mode 100644 index 0000000..ff659e0 --- /dev/null +++ b/Device/DavexDiskImage.h @@ -0,0 +1,39 @@ +#ifndef __DAVEXDISKIMAGE_H__ +#define __DAVEXDISKIMAGE_H__ + +#include + +#include +#include + + +namespace Device { + +// only supports 1 file; may be split over multiple files. + +class DavexDiskImage : public DiskImage { +public: + + virtual ~DavexDiskImage(); + + static DavexDiskImage *Create(const char *name, size_t blocks); + static DavexDiskImage *Create(const char *name, size_t blocks, const char *vname); + static DavexDiskImage *Open(MappedFile *); + + virtual BlockCache *createBlockCache(); + +private: + + DavexDiskImage(); + + DavexDiskImage(MappedFile *); + static void Validate(MappedFile *); + + bool _changed; + std::string _volumeName; +}; + + +} + +#endif diff --git a/Device/DiskCopy42Image.cpp b/Device/DiskCopy42Image.cpp new file mode 100644 index 0000000..9af488b --- /dev/null +++ b/Device/DiskCopy42Image.cpp @@ -0,0 +1,228 @@ + + +#include +#include +#include + +#include + +#include +#include + +#include + + +using namespace Device; +using namespace BigEndian; + + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + + + +enum { + oDataSize = 64, + oDataChecksum = 72, + oPrivate = 82, + oUserData = 84 +}; + +DiskCopy42Image::DiskCopy42Image(MappedFile *f) : + DiskImage(f), + _changed(false) +{ + setAdaptor(new POAdaptor(oUserData + (uint8_t *)f->address())); + setBlocks(Read32(f->address(), oDataSize) / 512); +} + + + +DiskCopy42Image::~DiskCopy42Image() +{ + if (_changed) + { + MappedFile *f = file(); + + if (f) + { + void *data = f->address(); + + uint32_t cs = Checksum(oUserData + (uint8_t *)data, Read32(data, oDataSize)); + + Write32(data, oDataChecksum, cs); + f->sync(); + } + // TODO -- checksum + } +} + +uint32_t DiskCopy42Image::Checksum(void *data, size_t size) +{ + uint32_t rv = 0; + uint8_t *dp = (uint8_t *)data; + + if (size & 0x01) return rv; + + for (size_t i = 0; i < size; i += 2) + { + rv += dp[i] << 8; + rv += dp[i+1]; + + rv = (rv >> 1) + (rv << 31); + } + + return rv; +} + +DiskCopy42Image *DiskCopy42Image::Open(MappedFile *f) +{ + Validate(f); + return new DiskCopy42Image(f); +} + +static uint8_t DiskFormat(size_t blocks) +{ + switch (blocks) + { + case 800: return 0; + case 1600: return 1; + case 1440: return 2; + case 2880: return 3; + default: return 0xff; + } +} + +static uint8_t FormatByte(size_t blocks) +{ + switch(blocks) + { + case 800: return 0x12; + default: return 0x22; + } +} +DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks) +{ + return Create(name, blocks, "Untitled"); +} + +DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname) +{ + MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData); + + + + uint8_t tmp[oUserData]; + IOBuffer header(tmp, oUserData); + + // name -- 64byte pstring. + + if (vname == NULL) vname = "Untitled"; + unsigned l = std::strlen(vname); + header.write8(std::min(l, 63u)); + header.writeBytes(vname, std::min(l, 63u)); + + //header.resize(64); + header.setOffset(oDataSize, true); + + // data size -- number of bytes + header.write32(blocks * 512); + + // tag size + header.write32(0); + + // data checksum + // if data is 0, will be 0. + //header.push32be(Checksum(file->fileData(), blocks * 512)); + header.write32(0); + + // tag checksum + header.write32(0); + + // disk format. + /* + * 0 = 400k + * 1 = 800k + * 2 = 720k + * 3 = 1440k + * 0xff = other + */ + header.write8(DiskFormat(blocks)); + + // formatbyte + /* + * 0x12 = 400k + * 0x22 = >400k mac + * 0x24 = 800k appleII + */ + header.write8(FormatByte(blocks)); + + // private + header.write16(0x100); + + std::memcpy(file->address(), header.buffer(), oUserData); + file->sync(); + + return new DiskCopy42Image(file); +} + +void DiskCopy42Image::Validate(MappedFile *file) +{ +#undef __METHOD__ +#define __METHOD__ "DiskCopy42Image::Validate" + + size_t bytes = 0; + size_t size = file->length(); + const void *data = file->address(); + bool ok = false; + uint32_t checksum = 0; + + do { + if (size < oUserData) break; + + // name must be < 64 + if (Read8(data, 0) > 63) break; + + if (Read32(data, oPrivate) != 0x100) + break; + + // bytes, not blocks. + bytes = Read32(data, oDataSize); + + if (bytes % 512) break; + + if (size < oUserData + bytes) break; + + // todo -- checksum. + checksum = Read32(data, oDataChecksum); + + ok = true; + } while (false); + + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); + + uint32_t cs = Checksum(oUserData + (uint8_t *)data, bytes); + + if (cs != checksum) + { + fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n"); + } + +} + +void DiskCopy42Image::write(unsigned block, const void *bp) +{ + DiskImage::write(block, bp); + _changed = true; +} + + +BlockCache *DiskCopy42Image::createBlockCache() +{ + // if not readonly, mark changed so crc will be updated at close. + + if (!readOnly()) _changed = true; + + return new MappedBlockCache(this, address()); +} \ No newline at end of file diff --git a/Device/DiskCopy42Image.h b/Device/DiskCopy42Image.h new file mode 100644 index 0000000..f4ce443 --- /dev/null +++ b/Device/DiskCopy42Image.h @@ -0,0 +1,38 @@ +#ifndef __DISKCOPY42IMAGE_H__ +#define __DISKCOPY42IMAGE_H__ + +#include +#include + +#include + +namespace Device { + +class DiskCopy42Image : public DiskImage { +public: + virtual ~DiskCopy42Image(); + + static DiskCopy42Image *Create(const char *name, size_t blocks); + static DiskCopy42Image *Create(const char *name, size_t blocks, const char *vname); + + static DiskCopy42Image *Open(MappedFile *); + + static uint32_t Checksum(void *data, size_t size); + + virtual void write(unsigned block, const void *bp); + + + virtual BlockCache *createBlockCache(); + +private: + + DiskCopy42Image(); + + DiskCopy42Image(MappedFile *); + static void Validate(MappedFile *); + bool _changed; +}; + +} + +#endif \ No newline at end of file diff --git a/Device/DiskImage.cpp b/Device/DiskImage.cpp new file mode 100644 index 0000000..113eb22 --- /dev/null +++ b/Device/DiskImage.cpp @@ -0,0 +1,192 @@ + +#include +#include +#include + +#include +#include + +#include + + + +#include + +#include + +#include + + +using namespace Device; + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + + +/* +DiskImage::DiskImage(const char *name, bool readOnly) +{ + File fd(name, readOnly ? O_RDONLY : O_RDWR); + MappedFile mf(fd, readOnly); + _file.adopt(mf); + + _blocks = 0; + _readOnly = readOnly; + _adaptor = NULL; +} +*/ + +DiskImage::DiskImage(MappedFile *file) +{ + _file.adopt(*file); + + _blocks = 0; + _readOnly = _file.readOnly(); + _adaptor = NULL; +} + +DiskImage::~DiskImage() +{ + delete _adaptor; +} + +bool DiskImage::readOnly() +{ + return _readOnly; +} + +unsigned DiskImage::blocks() +{ + return _blocks; +} + + +void DiskImage::setAdaptor(Adaptor *adaptor) +{ + delete _adaptor; + _adaptor = adaptor; +} + + +void DiskImage::read(unsigned block, void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "DiskImage::read" + + if (block >= _blocks) + throw Exception(__METHOD__ ": Invalid block."); + + _adaptor->readBlock(block, bp); +} + +void DiskImage::write(unsigned block, const void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "DiskImage::write" + + if (block >= _blocks) + throw Exception(__METHOD__ ": Invalid block."); + + _adaptor->writeBlock(block, bp); +} + +void DiskImage::sync() +{ + #undef __METHOD__ + #define __METHOD__ "DiskImage::sync" + + if (_file.isValid()) return _file.sync(); + + throw Exception(__METHOD__ ": File not set."); +} + + + + +ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) : + DiskImage(file) +{ + // at this point, file is no longer valid. + + setBlocks(length() / 512); + setAdaptor(new POAdaptor(address())); +} + +ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks) +{ + MappedFile *file = MappedFile::Create(name, blocks * 512); + return new ProDOSOrderDiskImage(file); +} + +ProDOSOrderDiskImage *ProDOSOrderDiskImage::Open(MappedFile *file) +{ + Validate(file); + return new ProDOSOrderDiskImage(file); +} + +void ProDOSOrderDiskImage::Validate(MappedFile *f) +{ + #undef __METHOD__ + #define __METHOD__ "ProDOSOrderDiskImage::Validate" + + if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set."); + + size_t size = f->length(); + + if (size % 512) + throw Exception(__METHOD__ ": Invalid file format."); + +} + +BlockCache *ProDOSOrderDiskImage::createBlockCache() +{ + return new MappedBlockCache(this, address()); +} + +#pragma mark - +#pragma mark DOS Order Disk Image + + +/* +DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) : + DiskImage(name, readOnly) +{ + Validate(file()); +} +*/ + +DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) : + DiskImage(file) +{ + // at this point, file is no longer valid. + + setBlocks(length() / 512); + setAdaptor(new DOAdaptor(address())); +} + + +DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks) +{ + MappedFile *file = MappedFile::Create(name, blocks * 512); + return new DOSOrderDiskImage(file); +} + +DOSOrderDiskImage *DOSOrderDiskImage::Open(MappedFile *file) +{ + Validate(file); + return new DOSOrderDiskImage(file); +} + +void DOSOrderDiskImage::Validate(MappedFile *f) +{ + #undef __METHOD__ + #define __METHOD__ "DOSOrderDiskImage::Validate" + + if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set."); + + size_t size = f->length(); + + if (size % 512) + throw Exception(__METHOD__ ": Invalid file format."); + +} diff --git a/Device/DiskImage.h b/Device/DiskImage.h new file mode 100644 index 0000000..52a7303 --- /dev/null +++ b/Device/DiskImage.h @@ -0,0 +1,94 @@ +#ifndef __DISKIMAGE_H__ +#define __DISKIMAGE_H__ + +#include +#include + +#include + +#include + +#include + +#include + +namespace Device { + + +class DiskImage : public BlockDevice { +public: + + + virtual ~DiskImage(); + + virtual void read(unsigned block, void *bp); + virtual void write(unsigned block, const void *bp); + virtual void sync(); + + virtual bool readOnly(); + virtual unsigned blocks(); + +protected: + + DiskImage(); + + DiskImage(MappedFile *file = 0); + + void setBlocks(unsigned blocks) { _blocks = blocks; } + + void setAdaptor(Adaptor *); + + void *address() const { return _file.address(); } + size_t length() const { return _file.length(); } + + MappedFile *file() { return &_file; } + +private: + + MappedFile _file; + Adaptor *_adaptor; + + bool _readOnly; + unsigned _blocks; +}; + + +class ProDOSOrderDiskImage : public DiskImage { +public: + + + static ProDOSOrderDiskImage *Create(const char *name, size_t blocks); + static ProDOSOrderDiskImage *Open(MappedFile *); + + + virtual BlockCache *createBlockCache(); + +private: + ProDOSOrderDiskImage(); + + + ProDOSOrderDiskImage(MappedFile *); + static void Validate(MappedFile *); +}; + +class DOSOrderDiskImage : public DiskImage { +public: + + + static DOSOrderDiskImage *Create(const char *name, size_t blocks); + static DOSOrderDiskImage *Open(MappedFile *); + +private: + DOSOrderDiskImage(); + + DOSOrderDiskImage(MappedFile *); + static void Validate(MappedFile *); +}; + + + + + +} + +#endif \ No newline at end of file diff --git a/Device/RawDevice.cpp b/Device/RawDevice.cpp new file mode 100644 index 0000000..91a88b3 --- /dev/null +++ b/Device/RawDevice.cpp @@ -0,0 +1,320 @@ + +#include +#include +#include +#include +#include + +#include + + +#ifdef __APPLE__ +#include +#endif + +#ifdef __linux__ +#include +#endif + +#ifdef __SUN__ +#include +#endif + + +#ifdef __FREEBSD__ +#include +#endif + +#ifdef __minix +#include +#endif + +#include + +#include + +using namespace Device; + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + +#ifdef __SUN__ +void RawDevice::devSize(int fd) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::devSize" + + struct dk_minfo minfo; + + if (::ioctl(fd, DKIOCGMEDIAINFO, &minfo) < 0) + throw POSIXException(__METHOD__ ": Unable to determine device size.", errno); + + _size = minfo.dki_lbsize * minfo.dki_capacity; + _blockSize = 512; // not really, but whatever. + _blocks = _size / 512; + +} +#endif + +#ifdef __APPLE__ +void RawDevice::devSize(int fd) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::devSize" + + uint32_t blockSize; // 32 bit + uint64_t blockCount; // 64 bit + + if (::ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize) < 0) + throw POSIXException(__METHOD__ ": Unable to determine block size.", errno); + + + if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &blockCount) < 0) + throw POSIXException(__METHOD__ ": Unable to determine block count.", errno); + + _blockSize = blockSize; + _size = _blockSize * blockCount; + _blocks = _size / 512; +} + +#endif + + +#ifdef __linux__ + +void RawDevice::devSize(int fd) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::devSize" + + int blocks; + + if (::ioctl(fd, BLKGETSIZE, &blocks) < 0) + throw POSIXException(__METHOD__ ": Unable to determine device size.", errno); + + _size = 512 * blocks; + _blockSize = 512; // + _blocks = blocks; + +} + +#endif + +// TODO -- FreeBSD/NetBSD/OpenBSD + +#ifdef __FREEBSD__ + +void RawDevice::devSize(int fd) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::devSize" + + unsigned blockSize; + off_t mediaSize; + + if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize) + throw POSIXException(__METHOD__ ": Unable to determine block size.", errno); + + if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize) + throw POSIXException(__METHOD__ ": Unable to determine media size.", errno); + + _blockSize = blockSize; + _size = mediaSize; + _blocks = mediaSize / 512; + +} + +#endif + + +#ifdef __minix + +void RawDevice::devSize(int fd) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::devSize" + + struct partition entry; + + + if (::ioctl(fd, DIOCGETP, &entry) < 0) + throw POSIXException(__METHOD__ ": Unable to determine device size.", errno); + + _size = entry.size + _blockSize = 512; // not really but whatever. + _blocks = _size / 512; + +} + +#endif + +RawDevice::RawDevice(const char *name, File::FileFlags flags) : + _file(name, flags) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::RawDevice" + + + if (!_file.isValid()) + { + throw Exception(__METHOD__ ": Invalid file handle."); + } + + _readOnly = flags == File::ReadOnly; + _size = 0; + _blocks = 0; + _blockSize = 0; + + + devSize(_file.fd()); +} + +RawDevice::RawDevice(File& file, File::FileFlags flags) : + _file(file) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::RawDevice" + + + if (!_file.isValid()) + { + throw Exception(__METHOD__ ": Invalid file handle."); + } + + _readOnly = flags == File::ReadOnly; + _size = 0; + _blocks = 0; + _blockSize = 0; + + + devSize(_file.fd()); +} + + +RawDevice::~RawDevice() +{ +} + + +RawDevice *RawDevice::Open(const char *name, File::FileFlags flags) +{ + return new RawDevice(name, flags); +} + + +void RawDevice::read(unsigned block, void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::read" + + if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number."); + if (bp == 0) throw Exception(__METHOD__ ": Invalid address."); + + // sun -- use pread + // apple - read full native block(s) ? + + off_t offset = block * 512; + ssize_t ok = ::pread(_file.fd(), bp, 512, offset); + + // TODO -- EINTR? + if (ok != 512) + throw ok < 0 + ? POSIXException(__METHOD__ ": Error reading block.", errno) + : Exception(__METHOD__ ": Error reading block."); +} + + +void RawDevice::read(TrackSector ts, void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::read" + + unsigned block = ts.track * 8 + ts.sector / 2; + if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number."); + if (bp == 0) throw Exception(__METHOD__ ": Invalid address."); + + // sun -- use pread + // apple - read full native block(s) ? + + off_t offset = (ts.track * 16 + ts.sector) * 256; + ssize_t ok = ::pread(_file.fd(), bp, 256, offset); + + // TODO -- EINTR? + if (ok != 256) + throw ok < 0 + ? POSIXException(__METHOD__ ": Error reading block.", errno) + : Exception(__METHOD__ ": Error reading block."); +} + + +void RawDevice::write(unsigned block, const void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::write" + + if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number."); + + if (_readOnly) + throw Exception(__METHOD__ ": File is readonly."); + + + off_t offset = block * 512; + ssize_t ok = ::pwrite(_file.fd(), bp, 512, offset); + + if (ok != 512) + throw ok < 0 + ? POSIXException(__METHOD__ ": Error writing block.", errno) + : Exception(__METHOD__ ": Error writing block."); +} + + +void RawDevice::write(TrackSector ts, const void *bp) +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::write" + + unsigned block = ts.track * 8 + ts.sector / 2; + if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number."); + + if (_readOnly) + throw Exception(__METHOD__ ": File is readonly."); + + + off_t offset = (ts.track * 16 + ts.sector) * 256; + ssize_t ok = ::pwrite(_file.fd(), bp, 256, offset); + + if (ok != 256) + throw ok < 0 + ? POSIXException(__METHOD__ ": Error writing block.", errno) + : Exception(__METHOD__ ": Error writing block."); +} + + +bool RawDevice::readOnly() +{ + return _readOnly; +} + +bool RawDevice::mapped() +{ + return false; +} + + +unsigned RawDevice::blocks() +{ + return _blocks; +} + + +void RawDevice::sync() +{ +#undef __METHOD__ +#define __METHOD__ "RawDevice::sync" + + if (_readOnly) return; + + if (::fsync(_file.fd()) < 0) + throw POSIXException(__METHOD__ ": fsync error.", errno); +} + diff --git a/Device/RawDevice.h b/Device/RawDevice.h new file mode 100644 index 0000000..2b036cc --- /dev/null +++ b/Device/RawDevice.h @@ -0,0 +1,57 @@ +#ifndef __RAWDEVICE_H__ +#define __RAWDEVICE_H__ + +#include + +#include + +#include + +namespace Device { + +// /dev/xxx + + +class RawDevice : public BlockDevice { +public: + + + + static RawDevice *Open(const char *name, File::FileFlags flags); + + + virtual ~RawDevice(); + + virtual void read(unsigned block, void *bp); + virtual void read(TrackSector ts, void *bp); + + virtual void write(unsigned block, const void *bp); + virtual void write(TrackSector ts, const void *bp); + + virtual bool readOnly(); + virtual bool mapped(); + virtual void sync(); + + virtual unsigned blocks(); + +private: + + + RawDevice(const char *name, File::FileFlags flags); + + RawDevice(File& file, File::FileFlags flags); + + void devSize(int fd); + + File _file; + bool _readOnly; + + uint64_t _size; // size of device in bytes. + unsigned _blocks; // # of 512k blocks i.e. _size / 512 + + unsigned _blockSize; // native block size. +}; + +} + +#endif diff --git a/Device/TrackSector.h b/Device/TrackSector.h new file mode 100644 index 0000000..dd96d7e --- /dev/null +++ b/Device/TrackSector.h @@ -0,0 +1,19 @@ +#ifndef __TRACKSECTOR_H__ +#define __TRACKSECTOR_H__ + +namespace Device { + + struct TrackSector { + TrackSector(unsigned, unsigned); + unsigned track; + unsigned sector; + }; + + inline TrackSector::TrackSector(unsigned t, unsigned s) + { + track = t; + sector = s; + } +} + +#endif \ No newline at end of file diff --git a/Device/UniversalDiskImage.cpp b/Device/UniversalDiskImage.cpp new file mode 100644 index 0000000..dedb9c9 --- /dev/null +++ b/Device/UniversalDiskImage.cpp @@ -0,0 +1,154 @@ +#include + +#include +#include + +#include + +#include +#include + +using namespace Device; +using namespace LittleEndian; + +using ProFUSE::Exception; +using ProFUSE::POSIXException; + + + + +UniversalDiskImage::UniversalDiskImage(MappedFile *file) : + DiskImage(file) +{ + + // at this point, file is no longer valid. + + uint8_t * data = (uint8_t *)address(); + + + + + _format = Read32(data, 0x0c); + _flags = Read32(data, 0x10); + _blocks = Read32(data, 0x14); + + _dataOffset = Read32(data, 0x18); + _dataLength = Read32(data, 0x1c); + + + setBlocks(_blocks); + // TODO -- DO, Nibble support. + setAdaptor(new POAdaptor(_dataOffset + data)); +} + +UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks) +{ + // 64-byte header. + MappedFile *file = MappedFile::Create(name, blocks * 512 + 64); + + uint8_t tmp[64]; + + IOBuffer header(tmp, 64); + + + // magic + creator + header.writeBytes("2IMGPRFS", 8); + + // header size. + header.write16(64); + + // version + header.write16(1); + + //image format -- ProDOS order + header.write32(1); + + // flags + header.write32(0); + + // # blocks. s/b 0 unless prodos-order + header.write32(blocks); + + // offset to disk data + header.write32(64); + + // data length + header.write32(512 * blocks); + + // comment offset, creator, reserved -- 0. + header.setOffset(64, true); + + std::memcpy(file->address(), header.buffer(), 64); + + + return new UniversalDiskImage(file); +} + +UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file) +{ + Validate(file); + return new UniversalDiskImage(file); +} + + +/* + * TODO -- support dos-order & nibblized + * TODO -- honor read-only flag. + * + */ +void UniversalDiskImage::Validate(MappedFile *file) +{ +#undef __METHOD__ +#define __METHOD__ "UniversalDiskImage::Validate" + + const void *data = file->address(); + size_t size = file->length(); + bool ok = false; + unsigned blocks = 0; + unsigned offset = 0; + + do { + + if (size < 64) break; + + if (std::memcmp(data, "2IMG", 4)) break; + + // only prodos supported, for now... + // TODO -- Dos Order, Nibble support. + if (Read32(data, 0x0c) != 1) break; + + blocks = Read32(data, 0x14); + offset = Read32(data, 0x18); + + // file size == blocks * 512 + if (Read32(data, 0x1c) != blocks * 512) break; + + if (offset + blocks * 512 > size) break; + + ok = true; + } while (false); + + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); + + +} + + +bool UniversalDiskImage::readOnly() +{ + return (_flags & 0x8000000) || DiskImage::readOnly(); +} + + +BlockCache *UniversalDiskImage::createBlockCache() +{ + if (_format == 1) + { + return new MappedBlockCache(this, _dataOffset + (uint8_t *)address()); + } + + return DiskImage::createBlockCache(); +} + + diff --git a/Device/UniversalDiskImage.h b/Device/UniversalDiskImage.h new file mode 100644 index 0000000..e552af5 --- /dev/null +++ b/Device/UniversalDiskImage.h @@ -0,0 +1,41 @@ +#ifndef __UNIVERSALDISKIMAGE_H__ +#define __UNIVERSALDISKIMAGE_H__ + + +#include +#include + +#include + +namespace Device { + +class UniversalDiskImage : public DiskImage { +public: + + + + static UniversalDiskImage *Create(const char *name, size_t blocks); + static UniversalDiskImage *Open(MappedFile *); + + virtual bool readOnly(); + + virtual BlockCache *createBlockCache(); + + +private: + + UniversalDiskImage(); + + UniversalDiskImage(MappedFile *); + static void Validate(MappedFile *); + + uint32_t _format; + uint32_t _flags; + uint32_t _blocks; + uint32_t _dataOffset; + uint32_t _dataLength; +}; + +} + +#endif