From a3cc7627b860705c98c88cf457128a18747ebbfe Mon Sep 17 00:00:00 2001 From: ksherlock Date: Sat, 21 Nov 2009 01:45:08 +0000 Subject: [PATCH] git-svn-id: https://profuse.googlecode.com/svn/branches/v2@68 aa027e90-d47c-11dd-86d7-074df07e0730 --- Bitmap.cpp | 197 ++++++++++++++++++++++++++ Bitmap.h | 44 ++++++ BlockDevice.cpp | 167 ++++++++++++++++++++++ BlockDevice.h | 83 +++++++++++ Buffer.cpp | 40 ++++++ Buffer.h | 33 +++++ DavexDiskImage.cpp | 144 +++++++++++++++++++ DavexDiskImage.h | 30 ++++ Directory.cpp | 133 ++++++++++++++++++ Directory.h | 151 ++++++++++++++++++++ DiskCopy42Image.cpp | 181 ++++++++++++++++++++++++ DiskCopy42Image.h | 32 +++++ Exception.cpp | 13 ++ Exception.h | 59 ++++++++ Lock.cpp | 26 ++++ Lock.h | 28 ++++ MappedFile.cpp | 307 +++++++++++++++++++++++++++++++++++++++++ MappedFile.h | 78 +++++++++++ UniversalDiskImage.cpp | 110 +++++++++++++++ UniversalDiskImage.h | 23 +++ Volume.h | 92 ++++++++++++ auto.h | 72 ++++++++++ test.cpp | 27 ++++ 23 files changed, 2070 insertions(+) create mode 100644 Bitmap.cpp create mode 100644 Bitmap.h create mode 100644 BlockDevice.cpp create mode 100644 BlockDevice.h create mode 100644 Buffer.cpp create mode 100644 Buffer.h create mode 100644 DavexDiskImage.cpp create mode 100644 DavexDiskImage.h create mode 100644 Directory.cpp create mode 100644 Directory.h create mode 100644 DiskCopy42Image.cpp create mode 100644 DiskCopy42Image.h create mode 100644 Exception.cpp create mode 100644 Exception.h create mode 100644 Lock.cpp create mode 100644 Lock.h create mode 100644 MappedFile.cpp create mode 100644 MappedFile.h create mode 100644 UniversalDiskImage.cpp create mode 100644 UniversalDiskImage.h create mode 100644 Volume.h create mode 100644 auto.h create mode 100644 test.cpp diff --git a/Bitmap.cpp b/Bitmap.cpp new file mode 100644 index 0000000..3605d9e --- /dev/null +++ b/Bitmap.cpp @@ -0,0 +1,197 @@ +#include "Bitmap.h" +#include + +#include "auto.h" + + +// returns # of 1-bits set (0-8) +inline static unsigned popCount(uint8_t x) +{ + #ifdef __GNUC__ + return __builtin_popcount(x); + #endif + + // Brian Kernighan / Peter Wegner in CACM 3 (1960), 322. + + unsigned count; + for (count = 0; x; ++count) + { + x &= x - 1; + } + return count; +} + + + +Bitmap::Bitmap(unsigned blocks) +{ + _blocks = _freeBlocks = blocks; + + _bitmapBlocks = (blocks + 4095) / 4096; + _freeIndex = 0; + + unsigned bitmapSize = _bitmapBlocks * 512; + unsigned blockSize = blocks / 8; + + auto_array bitmap(new uint8_t[bitmapSize]); + + // mark overflow in use, everything else free. + + std::memset(bitmap, 0xff, blocks / 8); + std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize); + + // edge case + unsigned tmp = blocks & 0x07; + + bitmap[blocks / 8] = ~(0xff >> tmp); + + _bitmap = bitmap.release(); + + +} + +Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks) +{ + _blocks = blocks; + _freeBlocks = 0; + _freeIndex = 0; + + _bitmapBlocks = (blocks + 4095) / 4096; + + unsigned bitmapSize = _bitmapBlocks * 512; + unsigned blockSize = blocks / 8; + + auto_array bitmap(new uint8_t[bitmapSize]); + + for (unsigned i = 0; i < blockSize; ++i) + { + device->readBlock(keyPointer + i, bitmap + 512 * i); + } + + // make sure all trailing bits are marked in use. + + // edge case + unsigned tmp = blocks & 0x07; + + bitmap[blocks / 8] &= ~(0xff >> tmp); + + std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize); + + // set _freeBlocks and _freeIndex; + for (unsigned i = 0; i < (blocks + 7) / 8; ++i) + { + _freeBlocks += popCount(bitmap[i]); + } + + if (_freeblocks) + { + for (unsigned i = 0; i < (blocks + 7) / 8; ++i) + { + if (tmp[i]) + { + _freeIndex = i; + break; + } + } + } + + + _bitmap = bitmap.release(); +} + +Bitmap::~Bitmap() +{ + if (_bitmap) delete []_bitmap; +} + + +void Bitmap::freeBlock(unsigned block) +{ + if (block >= _blocks) return; + + unsigned index = block / 8; + unsigned offset = block & 0x07; + unsigned mask = 0x80 >> offset; + + uint8_t tmp = _bitmap[index]; + + if ((tmp & mask) == 0) + { + ++_freeBlocks; + _bitmap[index] = tmp | mask; + } +} + + +int Bitmap::allocBlock(unsigned block) +{ + if (block >= _blocks) return -1; + + + unsigned index = block / 8; + unsigned offset = block & 0x07; + unsigned mask = 0x80 >> offset; + + uint8_t tmp = _bitmap[index]; + + if ((tmp & mask)) + { + --_freeBlocks; + _bitmap[index] = tmp & ~mask; + return block; + } + + return -1; +} + + +int Bitmap::allocBlock() +{ + if (!_freeBlocks) return -1; + + unsigned firstIndex = _firstIndex; + unsigned maxIndex = (_blocks + 7) / 8; + + + for (unsigned index = _firstIndex; index < maxIndex; ++index) + { + uint8_t tmp = _bitmap[index]; + if (!tmp) continue; + + unsigned mask = 0x80; + for (unsigned offset = 0; offset < 8; ++offset) + { + if (tmp & mask) + { + _firstIndex = index; + _bitmap[index] = tmp & ~mask; + --_freeBlocks; + return index * 8 + offset; + } + mask = mask >> 1; + } + } + + for (unsigned index = 0; index < _firstIndex; ++index) + { + uint8_t tmp = _bitmap[index]; + if (!tmp) continue; + + unsigned mask = 0x80; + for (unsigned offset = 0; offset < 8; ++offset) + { + if (tmp & mask) + { + _firstIndex = index; + _bitmap[index] = tmp & ~mask; + --_freeBlocks; + return index * 8 + offset; + } + mask = mask >> 1; + } + } + + + // should never happen... + return -1; +} \ No newline at end of file diff --git a/Bitmap.h b/Bitmap.h new file mode 100644 index 0000000..b4dcf40 --- /dev/null +++ b/Bitmap.h @@ -0,0 +1,44 @@ +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +#include +class BlockDevice; + +namespace ProFUSE { + +class Bitmap { +public: + + Bitmap(unsigned blocks); + Bitmap(BlockDevice *device, int keyPointer, int blocks); + //todo -- constructor by loading fro, block device... + ~Bitmap(); + + int allocBlock(); + int allocBlock(unsigned block); + + void freeBlock(unsigned block); + + + unsigned freeBlocks() const { return _freeBlocks; } + unsigned blocks() const { return _blocks; } + unsigned bitmapBlocks() const { return _bitmapBlocks; } + unsigned bitmapSize() const { return _bitmapBlocks * 512; } + const void *bitmap() const { return _bitmap; } + +private: + + unsigned _freeIndex; + unsigned _freeBlocks; + + unsigned _blocks; + unsigned _bitmapBlocks; + + uint8_t *_bitmap; +}; + + + +} + +#endif diff --git a/BlockDevice.cpp b/BlockDevice.cpp new file mode 100644 index 0000000..feeae82 --- /dev/null +++ b/BlockDevice.cpp @@ -0,0 +1,167 @@ +#include "BlockDevice.h" +#include "Exception.h" +#include "MappedFile.h" + +#include +#include +#include + +#include +#include +#include + +using namespace ProFUSE; + + + +BlockDevice::~BlockDevice() +{ +} + + +#pragma mark DiskImage + +DiskImage::DiskImage(const char *name, bool readOnly) : + _file(NULL) +{ + _file = new MappedFile(name, readOnly); +} + +DiskImage::DiskImage(MappedFile *file) : + _file(file) +{ +} + +DiskImage::~DiskImage() +{ + delete _file; +} + +bool DiskImage::readOnly() +{ + #undef __METHOD__ + #define __METHOD__ "DiskImage::readOnly" + + if (_file) return _file->readOnly(); + + throw Exception(__METHOD__ ": File not set."); +} + + + + + +void DiskImage::read(unsigned block, void *bp) +{ + #undef __METHOD__ + #define __METHOD__ "DiskImage::read" + + + if (_file) return _file->readBlock(block, bp); + + throw Exception(__METHOD__ ": File not set."); +} + +void DiskImage::write(unsigned block, const void *bp) +{ + #undef __METHOD__ + #define __METHOD__ "DiskImage::write" + + if (_file) return _file->writeBlock(block, bp); + + throw Exception(__METHOD__ ": File not set."); +} + + + +void DiskImage::sync() +{ + #undef __METHOD__ + #define __METHOD__ "DiskImage::sync" + + if (_file) return _file->sync(); + + throw Exception(__METHOD__ ": File not set."); +} + + + +ProDOSOrderDiskImage::ProDOSOrderDiskImage(const char *name, bool readOnly) : + DiskImage(name, readOnly) +{ + Validate(file()); +} + +ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) : + DiskImage(file) +{ +} + +ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks) +{ + MappedFile *file = new MappedFile(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) throw Exception(__METHOD__ ": File not set."); + + size_t size = f->fileSize(); + + if (size % 512) + throw Exception(__METHOD__ ": Invalid file format."); + + f->reset(); + f->setBlocks(size / 512); +} + +DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) : + DiskImage(name, readOnly) +{ + Validate(file()); +} + +DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) : + DiskImage(file) +{ +} + +DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks) +{ + MappedFile *file = new MappedFile(name, blocks * 512); + file->setDosOrder(true); + 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) throw Exception(__METHOD__ ": File not set."); + + size_t size = f->fileSize(); + + if (size % 512) + throw Exception(__METHOD__ ": Invalid file format."); + + f->reset(); + f->setDosOrder(true); + f->setBlocks(size / 512); +} diff --git a/BlockDevice.h b/BlockDevice.h new file mode 100644 index 0000000..e024161 --- /dev/null +++ b/BlockDevice.h @@ -0,0 +1,83 @@ +#ifndef __BLOCKDEVICE_H__ +#define __BLOCKDEVICE_H__ + +#include +#include + +#include "Exception.h" + +namespace ProFUSE { + +class MappedFile; + +class BlockDevice { +public: + virtual ~BlockDevice(); + + virtual void read(unsigned block, void *bp) = 0; + virtual void write(unsigned block, const void *bp) = 0; + + virtual bool readOnly() = 0; + virtual void sync() = 0; +}; + + + +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(); + + +protected: + + DiskImage(MappedFile * = NULL); + DiskImage(const char *name, bool readOnly); + + MappedFile *file() { return _file; } + void setFile(MappedFile *); + +private: + MappedFile *_file; +}; + + +class ProDOSOrderDiskImage : public DiskImage { +public: + + ProDOSOrderDiskImage(const char *name, bool readOnly); + + static ProDOSOrderDiskImage *Create(const char *name, size_t blocks); + static ProDOSOrderDiskImage *Open(MappedFile *); + +private: + ProDOSOrderDiskImage(MappedFile *); + static void Validate(MappedFile *); +}; + +class DOSOrderDiskImage : public DiskImage { +public: + + DOSOrderDiskImage(const char *name, bool readOnly); + + static DOSOrderDiskImage *Create(const char *name, size_t blocks); + static DOSOrderDiskImage *Open(MappedFile *); + +private: + DOSOrderDiskImage(MappedFile *); + static void Validate(MappedFile *); +}; + + + + + +} + +#endif \ No newline at end of file diff --git a/Buffer.cpp b/Buffer.cpp new file mode 100644 index 0000000..9e855ad --- /dev/null +++ b/Buffer.cpp @@ -0,0 +1,40 @@ + +#include "Buffer.h" + +using namespace ProFUSE; + +void Buffer::push16be(uint16_t x) +{ + _buffer.push_back((x >> 8) & 0xff); + _buffer.push_back(x & 0xff); +} + +void Buffer::push16le(uint16_t x) +{ + _buffer.push_back(x & 0xff); + _buffer.push_back((x >> 8) & 0xff); +} + +void Buffer::push32be(uint32_t x) +{ + _buffer.push_back((x >> 24) & 0xff); + _buffer.push_back((x >> 16) & 0xff); + _buffer.push_back((x >> 8) & 0xff); + _buffer.push_back(x & 0xff); +} + +void Buffer::push32le(uint32_t x) +{ + _buffer.push_back(x & 0xff); + _buffer.push_back((x >> 8) & 0xff); + _buffer.push_back((x >> 16) & 0xff); + _buffer.push_back((x >> 24) & 0xff); +} + +void Buffer::pushBytes(const void *data, unsigned size) +{ + for (unsigned i = 0; i < size; ++i) + { + _buffer.push_back( ((uint8_t *)data)[i] ); + } +} \ No newline at end of file diff --git a/Buffer.h b/Buffer.h new file mode 100644 index 0000000..5a0ebf1 --- /dev/null +++ b/Buffer.h @@ -0,0 +1,33 @@ +#ifndef __BUFFER_H__ +#define __BUFFER_H__ + +#include + +namespace ProFUSE { +class Buffer { + +public: + Buffer() {} + Buffer(unsigned size) { _buffer.reserve(size); } + + void *buffer() const { return (void *)&_buffer[0]; } + unsigned size() const { return _buffer.size(); } + void resize(unsigned size) { _buffer.resize(size); } + + void push8(uint8_t x) { _buffer.push_back(x); } + + void push16be(uint16_t); + void push16le(uint16_t); + + void push32be(uint32_t); + void push32le(uint32_t); + + void pushBytes(const void *data, unsigned size); + + +private: + std::vector _buffer; +}; + +} +#endif diff --git a/DavexDiskImage.cpp b/DavexDiskImage.cpp new file mode 100644 index 0000000..ac8818b --- /dev/null +++ b/DavexDiskImage.cpp @@ -0,0 +1,144 @@ +#include "DavexDiskImage.h" +#include "MappedFile.h" +#include "Buffer.h" + +#include +#include +#include +#include +#include +#include + +using namespace ProFUSE; + +/* + http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004 + */ + +static const char *IdentityCheck = "\x60VSTORE [Davex]\x00"; + +DavexDiskImage::DavexDiskImage(const char *name, bool readOnly) : + DiskImage(name, readOnly) +{ + Validate(file()); +} + +// private, validation already performed. +DavexDiskImage::DavexDiskImage(MappedFile *f) : + DiskImage(f) +{ +} + + +DavexDiskImage::~DavexDiskImage() +{ + // scan and update usedBlocks? +} + +void DavexDiskImage::Validate(MappedFile *f) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Validate" + + size_t size = f->fileSize(); + void * data = f->fileData(); + 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 (f->read8(16) != 0) + break; + + // total blocks + if (f->read32(33, LittleEndian) != blocks) + break; + + // file number -- must be 1 + if (f->read8(64) != 1) + break; + + ok = true; + } while (false); + + + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); + + f->setBlocks(blocks); + f->setOffset(512); + f->setDosOrder(false); +} + +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) +{ +#undef __METHOD__ +#define __METHOD__ "DavexDiskImage::Create" + + uint8_t *data; + + MappedFile *file = new MappedFile(name, blocks * 512 + 512); + + data = (uint8_t *)file->fileData(); + + Buffer header(512); + + header.pushBytes(IdentityCheck, 16); + // file Format + header.push8(0); + //version + header.push8(0); + // version + header.push8(0x10); + + // reserved. + header.resize(32); + + //deviceNum + header.push8(1); + + // total blocks + header.push32le(blocks); + + // unused blocks + header.push32le(0); + + // volume Name + header.pushBytes("\x08Untitled", 9); + + // name + reserved. + header.resize(64); + + // file number + header.push8(1); + + //starting block + header.push32le(0); + + // reserved + header.resize(512); + + + std::memcpy(file->fileData(), header.buffer(), 512); + file->sync(); + + file->setOffset(512); + file->setBlocks(blocks); + return new DavexDiskImage(file); +} diff --git a/DavexDiskImage.h b/DavexDiskImage.h new file mode 100644 index 0000000..0541428 --- /dev/null +++ b/DavexDiskImage.h @@ -0,0 +1,30 @@ + + +#include "BlockDevice.h" +#include + +namespace ProFUSE { + +// only supports 1 file; may be split over multiple files. + +class DavexDiskImage : public DiskImage { +public: + + DavexDiskImage(const char *, bool readOnly); + virtual ~DavexDiskImage(); + + static DavexDiskImage *Create(const char *name, size_t blocks); + static DavexDiskImage *Open(MappedFile *); + + +private: + + DavexDiskImage(MappedFile *); + static void Validate(MappedFile *); + + bool _changed; + std::string _volumeName; +}; + + +} \ No newline at end of file diff --git a/Directory.cpp b/Directory.cpp new file mode 100644 index 0000000..83d7058 --- /dev/null +++ b/Directory.cpp @@ -0,0 +1,133 @@ +#include "Directory" + +#include + +#include "Exception.h" + +using namespace ProFUSE; + +static bool isalpha(unsigned c) +{ + return (c >= 'A' && c <= 'Z') + || (c >= 'a' && x <= 'z') ; +} + +static bool isalnumdot(unsigned c) +{ + return (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <='9') + || (c == '.') ; + +} + +static uint16_t read16(uint8_t *data, unsigned offset) +{ + return data[offset + 0] + | (data[offset + 1] << 8) ; +} + +static uint32_t read24(uint8_t *data, unsigned offset) +{ + return data[offset + 0] + | (data[offset + 1] << 8) + | (data[offset + 2] << 16) ; +} + + +static uint32_t read32(uint8_t *data, unsigned offset) +{ + return data[offset + 0] + | (data[offset + 1] << 8) + | (data[offset + 2] << 16) ; + | (data[offset + 3] << 24) ; +} + +unsigned ValidName(const char *name) +{ + unsigned length; + + if (!name) return 0; + + if (!isalpha(*name)) return 0; + length = 1; + + for (length = 1; length < 17; ++length) + { + if (!isalnumdot(name[length])) return 0; + } + + if (length > 15) return 0; + return length; +} + +Directory::Directory(unsigned type, const char *name) +{ +#undef __METHOD__ +#define __METHOD__ "Directory::Directory" + + _nameLength = ValidName(name); + + if (!_length) + throw Exception(__METHOD__ ": Invalid name."); + + _storageType = type; + std::strncpy(_name, name, 16); + + _access = 0xc3; + _entryLength = 0x27; + _entriesPerBlock = 13; + _fileCount = 0; + + _device = NULL; +} + +Directory::Directory(const void *bp) : + _creation(0, 0) +{ +#undef __METHOD__ +#define __METHOD__ "Directory::Directory" + + const uint8_t *data = (const uint8_t *)bp; + + _storageType = data[0x00] >> 4; + _nameLength = data[0x00] & 0x0f; + std::memcpy(_name, data + 1, _nameLength); + _name[_nameLength] = 0; + _creation = DateTime(read16(data, 0x1c), read16(data, 0x1e)); + + _access = data[0x22]; + _entryLength = data[0x23]; + _entriesPerBlock = data[0x24]; + + _fileCount = read16(data, 0x25); + + // parse child file entries. +} + +Directory::~Directory() +{ +} + +Directory::setAccess(unsigned access) +{ +#undef __METHOD__ +#define __METHOD__ "Directory::setAccess" + + if ((access & 0xe3) != access) + throw Exception(__METHOD__ ": Illegal access."); + _access = access; + + // todo -- mark dirty? update block? +} + +Directory::setName(const char *name) +{ + unsigned length = ValidName(name); + if (!length) + throw Exception(__METHOD__ ": Invalid name."); + _nameLength = length; + std::strncpy(_name, name, 16); + + // todo -- update or mark dirty. +} diff --git a/Directory.h b/Directory.h new file mode 100644 index 0000000..8fe229c --- /dev/null +++ b/Directory.h @@ -0,0 +1,151 @@ +#ifndef __DIRECTORY_H__ +#define __DIRECTORY_H__ + +#include +#include + +#include "DateTime.h" + + +namespace ProFUSE { + +class BlockDevice; +class Bitmap; +class FileEntry; +class Volume; + + +enum { + DestroyEnabled = 0x80, + RenameEnabled = 0x40, + BackupNeeded = 0x20, + WriteEnabled = 0x02, + ReadEnabled = 0x01 +}; + + +class Entry { +public: + virtual ~Entry(); + + + + unsigned storageType() const { return _storageType; } + + unsigned nameLength() const { return _nameLength; } + const char *name() const { return _name; } + void setName(const char *name); + + + + // returns strlen() on success, 0 on failure. + static unsigned ValidName(const char *); + +protected: + Entry(int storageType, const char *name); + +private: + + unsigned _address; // absolute address on disk. + Volume *_volume; + + unsigned _storageType; + unsigned _nameLength; + char _name[15+1]; + +}; + +class Directory public Entry { +public: + virtual ~Directory(); + + + DateTime creation() const { return _creation; } + + unsigned access() const { return _access; } + unsigned entryLength() const { return _entryLength; } + unsigned entriesPerBlock() const { return _entriesPerBlock; } + + unsigned fileCount() const { return _fileCount; } + + void setAccess(unsigned access); + + +protected: + Directory(unsigned type, const char *name); + Directory(BlockDevice *, unsigned block); + + std::vector _children; + std::vector _entryBlocks; + + BlockDevice *_device; + + +private: + + DateTime _creation; + // version + // min version + unsigned _access; + usnigned _entryLength; // always 0x27 + unsigned _entriesPerBlock; //always 0x0d + + unsigned _fileCount; +}; + + + +class VolumeDirectory: public Directory { +public: + + virtual ~VolumeDirectory(); + + unsigned bitmapPointer() const { return _bitmapPointer; } + unsigned totalBlocks() const { return _totalBlocks; } + + // bitmap stuff... + int allocBlock(); + void freeBlock(unsigned block); + +private: + Bitmap *_bitmap; + + unsigned _totalBlocks; + unsigned _bitmapPointer; + + // inode / free inode list? + +}; + + +class SubDirectory : public Directory { +public: + +private: + unsigned _parentPointer; + unsigned _parentEntryNumber; + unsigned _parentEntryLength; +}; + + +class FileEntry : public Entry { +public: + + +private: + unsigned _fileType; + unsigned _keyPointer; + unsigned _blocksUsed; + unsigned _eof; + DateTime _creation; + //version + //min version + unsigned _access; + unsigned _auxType; + DateTime _modification; + unsigned _headerPointer; + +}; + +} +#endif diff --git a/DiskCopy42Image.cpp b/DiskCopy42Image.cpp new file mode 100644 index 0000000..10f1d94 --- /dev/null +++ b/DiskCopy42Image.cpp @@ -0,0 +1,181 @@ +#include "DiskCopy42Image.h" +#include "MappedFile.h" +#include "Buffer.h" + +#include + +using namespace ProFUSE; + +DiskCopy42Image::DiskCopy42Image(MappedFile *f) : + DiskImage(f), + _changed(false) +{ +} + +DiskCopy42Image::DiskCopy42Image(const char *name, bool readOnly) : + DiskImage(name, readOnly), + _changed(false) +{ + Validate(file()); +} + +DiskCopy42Image::~DiskCopy42Image() +{ + if (_changed) + { + MappedFile *f = file(); + if (f) + { + uint32_t cs = Checksum(f->offset() + (uint8_t *)f->fileData(), + f->blocks() * 512); + + f->write32(72, cs, BigEndian); + 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) +{ + MappedFile *file = new MappedFile(name, blocks * 512 + 84); + file->setOffset(84); + file->setBlocks(blocks); + + Buffer header(84); + + // name -- 64byte pstring. + header.pushBytes("\x08Untitled", 9); + header.resize(64); + + // data size -- number of bytes + header.push32be(blocks * 512); + + // tag size + header.push32be(0); + + // data checksum + // if data is 0, will be 0. + //header.push32be(Checksum(file->fileData(), blocks * 512)); + header.push32be(0); + + // tag checksum + header.push32be(0); + + // disk format. + /* + * 0 = 400k + * 1 = 800k + * 2 = 720k + * 3 = 1440k + * 0xff = other + */ + header.push8(DiskFormat(blocks)); + + // formatbyte + /* + * 0x12 = 400k + * 0x22 = >400k mac + * 0x24 = 800k appleII + */ + header.push8(FormatByte(blocks)); + + // private + header.push16be(0x100); + + std::memcpy(file->fileData(), header.buffer(), 84); + file->sync(); + + return new DiskCopy42Image(file); +} + +void DiskCopy42Image::Validate(MappedFile *file) +{ + size_t bytes = 0; + size_t size = file->fileSize(); + bool ok = false; + uint32_t checksum; + + do { + if (size < 84) break; + + // name must be < 64 + if (file->read8(0) > 63) break; + + if (file->read32(82, BigEndian) != 0x100) + break; + + // bytes, not blocks. + bytes = file->read32(64, BigEndian); + + if (bytes % 512) break; + + if (size < 84 + bytes) break; + + // todo -- checksum. + checksum = file->read32(72, BigEndian); + + ok = true; + } while (false); + + + uint32_t cs = Checksum(64 + (uint8_t *)file->fileData(), bytes); + + if (cs != checksum) + { + fprintf(stderr, "Warning: checksum invalid.\n"); + } + file->reset(); + file->setOffset(64); + file->setBlocks(bytes / 512); +} + +void DiskCopy42Image::write(unsigned block, const void *bp) +{ + DiskImage::write(block, bp); + _changed = true; +} \ No newline at end of file diff --git a/DiskCopy42Image.h b/DiskCopy42Image.h new file mode 100644 index 0000000..689deca --- /dev/null +++ b/DiskCopy42Image.h @@ -0,0 +1,32 @@ +#ifndef __DISKCOPY42IMAGE_H__ +#define __DISKCOPY42IMAGE_H__ + +#include "BlockDevice.h" + +#include + +namespace ProFUSE { + +class DiskCopy42Image : public DiskImage { +public: + DiskCopy42Image(const char *name, bool readOnly); + virtual ~DiskCopy42Image(); + + static DiskCopy42Image *Create(const char *name, size_t blocks); + static DiskCopy42Image *Open(MappedFile *); + + static uint32_t Checksum(void *data, size_t size); + + virtual void write(unsigned block, const void *bp); + + +private: + + DiskCopy42Image(MappedFile *); + static void Validate(MappedFile *); + bool _changed; +}; + +} + +#endif \ No newline at end of file diff --git a/Exception.cpp b/Exception.cpp new file mode 100644 index 0000000..ee0cbee --- /dev/null +++ b/Exception.cpp @@ -0,0 +1,13 @@ + +#include "Exception.h" + +using namespace ProFUSE; + +Exception::~Exception() throw() +{ +} + +const char *Exception::what() +{ + return _string.c_str(); +} \ No newline at end of file diff --git a/Exception.h b/Exception.h new file mode 100644 index 0000000..cf9ffb7 --- /dev/null +++ b/Exception.h @@ -0,0 +1,59 @@ +#ifndef __EXCEPTION_H__ +#define __EXCEPTION_H__ + +#include +#include + +namespace ProFUSE { + +class Exception : public std::exception +{ +public: + Exception(const char *cp); + Exception(const std::string &str); + Exception(const char *cp, int error); + Exception(const std::string& string, int error); + + virtual ~Exception() throw (); + + virtual const char *what(); + + int error() const; +private: + int _error; + std::string _string; + +}; + +inline Exception::Exception(const char *cp): + _error(0), + _string(cp) +{ +} + +inline Exception::Exception(const std::string& string): + _error(0), + _string(string) +{ +} + +inline Exception::Exception(const char *cp, int error): + _error(error), + _string(cp) +{ +} + +inline Exception::Exception(const std::string& string, int error): + _error(error), + _string(string) +{ +} + +inline int Exception::error() const +{ + return _error; +} + +} + +#endif \ No newline at end of file diff --git a/Lock.cpp b/Lock.cpp new file mode 100644 index 0000000..ddd9fa4 --- /dev/null +++ b/Lock.cpp @@ -0,0 +1,26 @@ +#include "Lock.h" + +Lock::Lock() +{ + pthread_mutex_init(&_mutex, NULL); +} + +Lock::~Lock() +{ + pthread_mutex_destroy(&_mutex); +} + +void Lock::lock() +{ + pthread_mutex_lock(&_mutex); +} + +void Lock::unlock() +{ + pthread_mutex_unlock(&_mutex); +} + +bool Lock::tryLock() +{ + return pthread_mutex_trylock(&_mutex) == 0; +} \ No newline at end of file diff --git a/Lock.h b/Lock.h new file mode 100644 index 0000000..cbb3bc7 --- /dev/null +++ b/Lock.h @@ -0,0 +1,28 @@ +#ifndef __LOCK_H__ +#define __LOCK_H__ + +#include + +class Lock { +public: + Lock(); + ~Lock(); + + void lock(); + void unlock(); + + bool tryLock(); + +private: + pthread_mutex_t _mutex; +}; + +class Locker { +public: + Locker(Lock& lock) : _lock(lock) { _lock.lock(); } + ~Locker() { _lock.unlock(); } +private: + Lock &_lock; +}; + +#endif \ No newline at end of file diff --git a/MappedFile.cpp b/MappedFile.cpp new file mode 100644 index 0000000..fa3b198 --- /dev/null +++ b/MappedFile.cpp @@ -0,0 +1,307 @@ +#include "MappedFile.h" +#include "Exception.h" +#include +#include +#include + +#include +#include +#include + +#include "auto.h" + +using namespace ProFUSE; + + + +MappedFile::MappedFile(const char *name, bool readOnly) : + _fd(-1), + _map(MAP_FAILED) +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::MappedFile" + + auto_fd fd(::open(name, readOnly ? O_RDONLY : O_RDWR)); + + if (fd < 0) + { + throw Exception(__METHOD__ ": Unable to open file.", errno); + } + // + init(fd.release(), readOnly); +} + +MappedFile::MappedFile(int fd, bool readOnly) : + _fd(-1), + _map(MAP_FAILED) +{ + init(fd, readOnly); +} + +// todo -- verify throw calls destructor. +MappedFile::MappedFile(const char *name, size_t size) : + _fd(-1), + _map(MAP_FAILED) +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::MappedFile" + + _size = size; + _readOnly = false; + auto_fd fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644)); + + if (fd < 0) + throw Exception(__METHOD__ ": Unable to create file.", errno); + + // TODO -- is ftruncate portable? + if (::ftruncate(fd, _size) < 0) + { + throw Exception(__METHOD__ ": Unable to truncate file.", errno); + } + + //_map = ::mmap(NULL, _size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, _fd, 0); + + auto_map map( + fd, + _size, + PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED + ); + + if (map == MAP_FAILED) throw Exception(__METHOD__ ": Unable to map file.", errno); + + _fd = fd.release(); + _map = map.release(); +} + +MappedFile::~MappedFile() +{ + if (_map != MAP_FAILED) ::munmap(_map, _size); + if (_fd >= 0) ::close(_fd); +} + +void MappedFile::init(int f, bool readOnly) +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::init" + + auto_fd fd(f); + + _offset = 0; + _blocks = 0; + _size = 0; + _readOnly = readOnly; + _dosOrder = false; + + _size = ::lseek(_fd, 0, SEEK_END); + + if (_size < 0) + throw Exception(__METHOD__ ": Unable to determine file size.", errno); + +/* + _map = ::mmap(NULL, _size, readOnly ? PROT_READ : PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); +*/ + auto_map map( + fd, + _size, + readOnly ? PROT_READ : PROT_READ | PROT_WRITE, + readOnly ? MAP_FILE : MAP_FILE | MAP_SHARED + ); + + if (map == MAP_FAILED) throw Exception(__METHOD__ ": Unable to map file.", errno); + + _fd = fd.release(); + _map = map.release(); +} + + +const unsigned MappedFile::DOSMap[] = { + 0x00, 0x0e, 0x0d, 0x0c, + 0x0b, 0x0a, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, + 0x03, 0x02, 0x01, 0x0f +}; + + +void MappedFile::readBlock(unsigned block, void *bp) +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::readBlock" + + + if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number."); + if (bp == 0) throw Exception(__METHOD__ ": Invalid address."); + + + if (_dosOrder) + { + unsigned track = (block & ~0x07) << 9; + unsigned sector = (block & 0x07) << 1; + + for (unsigned i = 0; i < 2; ++i) + { + size_t address = track | (DOSMap[sector+i] << 8); + + std::memcpy(bp, (uint8_t *)_map + _offset + address, 256); + + bp = (uint8_t *)bp + 256; + } + + } + else + { + size_t address = block * 512; + std::memcpy(bp, (uint8_t *)_map + _offset + address, 512); + } +} + +void MappedFile::writeBlock(unsigned block, const void *bp) +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::writeBlock" + + if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number."); + + if ( (_readOnly) || (_map == MAP_FAILED)) + throw Exception(__METHOD__ ": File is readonly."); + + if (_dosOrder) + { + unsigned track = (block & ~0x07) << 9; + unsigned sector = (block & 0x07) << 1; + + for (unsigned i = 0; i < 2; ++i) + { + size_t address = track | (DOSMap[sector+i] << 8); + + std::memcpy((uint8_t *)_map + _offset + address, bp, 256); + bp = (uint8_t *)bp + 256; + } + } + else + { + size_t address = block * 512; + std::memcpy((uint8_t *)_map + _offset + address , bp, 512); + } +} + +void MappedFile::sync() +{ + #undef __METHOD__ + #define __METHOD__ "MappedFile::sync" + + if ( (_readOnly) || (_map == MAP_FAILED)) + return; + + if (::msync(_map, _size, MS_SYNC) < 0) + throw Exception(__METHOD__ ": msync error.", errno); +} + +void MappedFile::reset() +{ + _offset = 0; + _blocks = 0; + _dosOrder = false; +} + +/* +uint8_t MappedFile::read8(size_t location) const +{ + // check for size? + uint8_t *map = (uint8_t *)_map; + + return map[location]; +} +*/ + +uint16_t MappedFile::read16(size_t location, int byteOrder) const +{ + // check for size? + uint8_t *map = (uint8_t *)_map; + + switch(byteOrder) + { + case LittleEndian: + return (map[location + 1] << 8) + | (map[location]); + case BigEndian: + return (map[location] << 8) + | (map[location+1]); + default: + return 0; + } +} + +uint32_t MappedFile::read32(size_t location, int byteOrder) const +{ + // check for size? + uint8_t *map = (uint8_t *)_map; + + switch(byteOrder) + { + case LittleEndian: + return (map[location+3] << 24) + | (map[location+2] << 16) + | (map[location+1] << 8) + | (map[location]) + ; + case BigEndian: + return (map[location] << 24) + | (map[location+1] << 16) + | (map[location+2] << 8) + | (map[location+3]) + ; + + default: + return 0; + } +} + + +/* +void MappedFile::write8(size_t location, uint8_t data) +{ + uint8_t *map = (uint8_t *)_map; + + map[location] = data; +} +*/ + +void MappedFile::write16(size_t location, uint16_t data, int byteOrder) +{ + uint8_t *map = (uint8_t *)_map; + + switch(byteOrder) + { + case LittleEndian: + map[location] = data & 0xff; + map[location+1] = (data >> 8) & 0xff; + break; + case BigEndian: + map[location] = (data >> 8) & 0xff; + map[location+1] = data & 0xff; + break; + } +} + +void MappedFile::write32(size_t location, uint32_t data, int byteOrder) +{ + uint8_t *map = (uint8_t *)_map; + + switch(byteOrder) + { + case LittleEndian: + map[location] = data & 0xff; + map[location+1] = (data >> 8) & 0xff; + map[location+2] = (data >> 16) & 0xff; + map[location+3] = (data >> 24) & 0xff; + break; + case BigEndian: + map[location] = (data >> 24) & 0xff; + map[location+1] = (data >> 16) & 0xff; + map[location+2] = (data >> 8) & 0xff; + map[location+3] = data & 0xff; + break; + } +} diff --git a/MappedFile.h b/MappedFile.h new file mode 100644 index 0000000..9414067 --- /dev/null +++ b/MappedFile.h @@ -0,0 +1,78 @@ +#ifndef __MAPPED_FILE__ +#define __MAPPED_FILE__ + + +#include +#include + +namespace ProFUSE { + + +enum { + LittleEndian = 0x3412, + BigEndian = 0x1234 +}; + +class MappedFile { +public: + MappedFile(const char *name, bool ReadOnly); + MappedFile(int fd, bool readOnly); + MappedFile(const char *name, size_t size); + + ~MappedFile(); + + void readBlock(unsigned block, void *bp); + void writeBlock(unsigned block, const void *bp); + + void sync(); + + void reset(); + + bool dosOrder() const { return _dosOrder; } + void setDosOrder(bool tf) { _dosOrder = tf; } + + unsigned offset() const { return _offset; } + void setOffset(unsigned o) { _offset = o; } + + unsigned blocks() const { return _blocks; } + void setBlocks(unsigned b) { _blocks = b; } + + bool readOnly() const { return _readOnly; } + size_t fileSize() const { return _size; } + void *fileData() const { return _map; } + + uint8_t read8(size_t location) const + { + return ((uint8_t *)_map)[location]; + } + uint16_t read16(size_t location, int byteOrder) const; + uint32_t read32(size_t location, int byteOrder) const; + + void write8(size_t location, uint8_t data) + { + ((uint8_t *)_map)[location] = data; + } + void write16(size_t location, uint16_t data, int byteOrder); + void write32(size_t location, uint32_t data, int byteOrder); + + +private: + MappedFile& operator=(const MappedFile& other); + + void init(int fd, bool readOnly); + + static const unsigned DOSMap[]; + + int _fd; + void *_map; + + size_t _size; + bool _readOnly; + + bool _dosOrder; + unsigned _offset; + unsigned _blocks; + +}; +} +#endif \ No newline at end of file diff --git a/UniversalDiskImage.cpp b/UniversalDiskImage.cpp new file mode 100644 index 0000000..688b93f --- /dev/null +++ b/UniversalDiskImage.cpp @@ -0,0 +1,110 @@ +#include "UniversalDiskImage.h" +#include "MappedFile.h" +#include "Buffer.h" + +using namespace ProFUSE; + +UniversalDiskImage::UniversalDiskImage(const char *name, bool readOnly) : + DiskImage(name, readOnly) +{ + Validate(file()); +} + +UniversalDiskImage::UniversalDiskImage(MappedFile *file) : + DiskImage(file) +{ +} + +UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks) +{ + // 64-byte header. + MappedFile *file = new MappedFile(name, blocks * 512 + 64); + + Buffer header(64); + + + // magic + creator + header.pushBytes("2IMGPRFS", 8); + + // header size. + header.push16le(64); + + // version + header.push16le(1); + + //image format -- ProDOS order + header.push32le(1); + + // flags + header.push32le(0); + + // # blocks. s/b 0 unless prodos-order + header.push32le(blocks); + + // offset to disk data + header.push32le(64); + + // data length + header.push32le(512 * blocks); + + // comment offset, creator, reserved -- 0. + header.resize(64); + + std::memcpy(file->fileData(), header.buffer(), 64); + + + file->setOffset(64); + file->setBlocks(blocks); + 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__ "DavexDiskImage::Validate" + + uint8_t *data = (uint8_t *)file->fileData(); + size_t size = file->fileSize(); + 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... + if (file->read32(0x0c, LittleEndian) != 1) break; + + offset = file->read32(0x20, LittleEndian); + blocks = file->read32(0x14, LittleEndian); + + // file size == blocks * 512 + if (file->read32(0x1c, LittleEndian) != blocks * 512) break; + + if (offset + blocks * 512 > size) break; + + ok = true; + } while (false); + + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); + + file->reset(); + file->setOffset(offset); + file->setBlocks(blocks); +} \ No newline at end of file diff --git a/UniversalDiskImage.h b/UniversalDiskImage.h new file mode 100644 index 0000000..cfcd887 --- /dev/null +++ b/UniversalDiskImage.h @@ -0,0 +1,23 @@ +#ifndef __UNIVERSALDISKIMAGE_H__ +#define __UNIVERSALDISKIMAGE_H__ + + +#include "BlockDevice.h" + +namespace ProFUSE { + +class UniversalDiskImage : public DiskImage { +public: + UniversalDiskImage(const char *name, bool readOnly); + + static UniversalDiskImage *Create(const char *name, size_t blocks); + static UniversalDiskImage *Open(MappedFile *); + +private: + UniversalDiskImage(MappedFile *); + static void Validate(MappedFile *); +}; + +} + +#endif diff --git a/Volume.h b/Volume.h new file mode 100644 index 0000000..47c6973 --- /dev/null +++ b/Volume.h @@ -0,0 +1,92 @@ +#ifndef __VOLUME_H__ +#define __VOLUME_H__ + +#include +#include + +namespace ProFUSE { + +class Bitmap; +class BlockDevice; + +class Volume; +class Directory; +class FileEntry; + + +class Entry { +public: + Entry() : _address(0), _volume(NULL) { } + virtual ~Entry(); + + unsigned address() { return _address; } + void setAddress(unsigned address) { _address = address; } + + Volume *volume() const { return _volume; } + void setVolume(Volume *v) { _volume = v; } + +private: + // physical location on disk (block * 512 + offset) + unsigned _address; + Volume *_volume; +}; + +class Directory { + + FileEntry *childAtIndex(unsigned index); + +private: + unsigned _childCount; + + std::vector_children; +}; + +class FileEntry { + virtual ~FileEntry(); + + virtual unsigned inode(); + Directory *directory(); + Directory *parent(); + +private: + Directory *_parent; + Volume *_volume; +} + +class Volume { +public: + + ~Volume(); + + Volume *Create(BlockDevice *); + Volume *Open(BlockDevice *); + + + int allocBlock(); + +private: + + Volume(BlockDevice *, int); + + + Bitmap *_bitmap; + BlockDevice *_device; + + +}; + + +class FileEntry : public Entry { +public: + virtual ~FileEntry(); + + unsigned inode(); + +private: + _unsigned _inode; + _unsigned _lookupCount; + _unsigned _openCount; +}; +} + +#endif \ No newline at end of file diff --git a/auto.h b/auto.h new file mode 100644 index 0000000..73f6768 --- /dev/null +++ b/auto.h @@ -0,0 +1,72 @@ +#ifndef __AUTO_H__ +#define __AUTO_H__ + +template +class auto_array +{ +public: + auto_array() : _t(NULL) {} + auto_array(T *t) : _t(t) {} + ~auto_array() { if (_t) delete []_t; } + + + T* release() + { T *tmp = _t; _t = NULL; return tmp; } + + T* get() const { return _t; } + operator T*() const { return _t; } + T& operator[](int index) { return _t[index]; } + +private: + T *_t; +}; + +// ::close +#if defined(O_CREAT) +class auto_fd +{ +public: + auto_fd(int fd) : _fd(fd) { } + + ~auto_fd() + { if (_fd != -1) ::close(_fd); } + + int release() + { int tmp = _fd; _fd = -1; return tmp; } + + int get() const { return _fd; } + operator int() const { return _fd; } + +private: + int _fd; +}; +#endif + +// ::mmap, :munmap +#if defined(MAP_FAILED) +class auto_map +{ +public: + auto_map(int fd, size_t size, int prot, int flags) : + _size(size), + _map(::mmap(NULL, size, prot, flags, fd, 0)) + { } + + ~auto_map() + { if (_map != MAP_FAILED) ::munmap(_map, _size); } + + void *release() + { void *tmp = _map; _map = MAP_FAILED; return tmp; } + + void *get() const { return _map; } + operator void *() const { return _map; } + +private: + size_t _size; + void *_map; +}; +#endif + + +#endif + diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..3a657f8 --- /dev/null +++ b/test.cpp @@ -0,0 +1,27 @@ +#include "BlockDevice.h" +#include "Exception.h" +#include "DavexDiskImage.h" +#include "UniversalDiskImage.h" +#include "DiskCopy42Image.h" + +#include + +int main(int argc, char **argv) +{ + if (argc == 2) + { + try { + //ProFUSE::BlockDevice *dev = ProFUSE::ProDOSOrderDiskImage::Create(argv[1], 1600); + //ProFUSE::BlockDevice *dev = ProFUSE::DavexDiskImage::Create(argv[1], 1600); + //ProFUSE::BlockDevice *dev = ProFUSE::UniversalDiskImage::Create(argv[1], 1600); + ProFUSE::BlockDevice *dev = ProFUSE::DiskCopy42Image::Create(argv[1], 1600); + + delete dev; + } catch (ProFUSE::Exception e) { + if (e.error()) printf("%s %s", e.what(), strerror(e.error())); + else printf("%s\n", e.what()); + } + + } + return 0; +}