mirror of
https://github.com/ksherlock/profuse.git
synced 2025-02-03 11:32:49 +00:00
git-svn-id: https://profuse.googlecode.com/svn/branches/v2@68 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
parent
85c84a6a80
commit
a3cc7627b8
197
Bitmap.cpp
Normal file
197
Bitmap.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
#include "Bitmap.h"
|
||||
#include <cstring>
|
||||
|
||||
#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;
|
||||
}
|
44
Bitmap.h
Normal file
44
Bitmap.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef __BITMAP_H__
|
||||
#define __BITMAP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
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
|
167
BlockDevice.cpp
Normal file
167
BlockDevice.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
#include "BlockDevice.h"
|
||||
#include "Exception.h"
|
||||
#include "MappedFile.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
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);
|
||||
}
|
83
BlockDevice.h
Normal file
83
BlockDevice.h
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef __BLOCKDEVICE_H__
|
||||
#define __BLOCKDEVICE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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
|
40
Buffer.cpp
Normal file
40
Buffer.cpp
Normal file
@ -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] );
|
||||
}
|
||||
}
|
33
Buffer.h
Normal file
33
Buffer.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __BUFFER_H__
|
||||
#define __BUFFER_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> _buffer;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
144
DavexDiskImage.cpp
Normal file
144
DavexDiskImage.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "DavexDiskImage.h"
|
||||
#include "MappedFile.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdio>
|
||||
|
||||
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);
|
||||
}
|
30
DavexDiskImage.h
Normal file
30
DavexDiskImage.h
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
}
|
133
Directory.cpp
Normal file
133
Directory.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "Directory"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#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.
|
||||
}
|
151
Directory.h
Normal file
151
Directory.h
Normal file
@ -0,0 +1,151 @@
|
||||
#ifndef __DIRECTORY_H__
|
||||
#define __DIRECTORY_H__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#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<FileEntry *> _children;
|
||||
std::vector<unsigned> _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
|
181
DiskCopy42Image.cpp
Normal file
181
DiskCopy42Image.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
#include "DiskCopy42Image.h"
|
||||
#include "MappedFile.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
32
DiskCopy42Image.h
Normal file
32
DiskCopy42Image.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __DISKCOPY42IMAGE_H__
|
||||
#define __DISKCOPY42IMAGE_H__
|
||||
|
||||
#include "BlockDevice.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
13
Exception.cpp
Normal file
13
Exception.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
#include "Exception.h"
|
||||
|
||||
using namespace ProFUSE;
|
||||
|
||||
Exception::~Exception() throw()
|
||||
{
|
||||
}
|
||||
|
||||
const char *Exception::what()
|
||||
{
|
||||
return _string.c_str();
|
||||
}
|
59
Exception.h
Normal file
59
Exception.h
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef __EXCEPTION_H__
|
||||
#define __EXCEPTION_H__
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
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
|
26
Lock.cpp
Normal file
26
Lock.cpp
Normal file
@ -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;
|
||||
}
|
28
Lock.h
Normal file
28
Lock.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef __LOCK_H__
|
||||
#define __LOCK_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
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
|
307
MappedFile.cpp
Normal file
307
MappedFile.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
#include "MappedFile.h"
|
||||
#include "Exception.h"
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
78
MappedFile.h
Normal file
78
MappedFile.h
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef __MAPPED_FILE__
|
||||
#define __MAPPED_FILE__
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdlib>
|
||||
|
||||
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
|
110
UniversalDiskImage.cpp
Normal file
110
UniversalDiskImage.cpp
Normal file
@ -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);
|
||||
}
|
23
UniversalDiskImage.h
Normal file
23
UniversalDiskImage.h
Normal file
@ -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
|
92
Volume.h
Normal file
92
Volume.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef __VOLUME_H__
|
||||
#define __VOLUME_H__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
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<FileEntry *>_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
|
72
auto.h
Normal file
72
auto.h
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef __AUTO_H__
|
||||
#define __AUTO_H__
|
||||
|
||||
template <class T>
|
||||
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
|
||||
|
27
test.cpp
Normal file
27
test.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "BlockDevice.h"
|
||||
#include "Exception.h"
|
||||
#include "DavexDiskImage.h"
|
||||
#include "UniversalDiskImage.h"
|
||||
#include "DiskCopy42Image.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user