v1/v2 switch-a-roo
git-svn-id: https://profuse.googlecode.com/svn/trunk@408 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
commit
5d1b81ecca
243
Bitmap.cpp
243
Bitmap.cpp
|
@ -1,243 +0,0 @@
|
|||
#include "Bitmap.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace ProDOS {
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
inline static unsigned countLeadingZeros(uint8_t x)
|
||||
{
|
||||
if (x == 0) return sizeof(uint8_t);
|
||||
#ifdef __GNUC__
|
||||
return __builtin_clz(x) + sizeof(uint8_t) - sizeof(unsigned);
|
||||
#endif
|
||||
|
||||
unsigned rv = 0;
|
||||
if ((x & 0xf0) == 0) { x <<= 4; rv = 4; }
|
||||
if (x & 0x80) return rv;
|
||||
if (x & 0x40) return rv + 1;
|
||||
if (x & 0x20) return rv + 2;
|
||||
if (x & 0x10) return rv + 3;
|
||||
// unreachable
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline static unsigned countLeadingOnes(uint8_t x)
|
||||
{
|
||||
return countLeadingZeros(~x);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(unsigned blocks) :
|
||||
_blocks(blocks),
|
||||
_freeBlocks(blocks),
|
||||
_bitmapSize((blocks + 4096 - 1) >> 12),
|
||||
_bitmap(NULL)
|
||||
{
|
||||
// 1 block = 512 bytes = 4096 bits
|
||||
_bitmap = new uint8_t[_bitmapSize];
|
||||
|
||||
// mark every requested block as free.
|
||||
unsigned bytes = blocks >> 3;
|
||||
std::memset(_bitmap, 0xff, bytes);
|
||||
|
||||
// all trailing blocks are marked as used.
|
||||
std::memset(_bitmap + bytes, 0x00, _bitmapSize - bytes);
|
||||
|
||||
// and handle the edge case...
|
||||
/*
|
||||
* 0 -> 0 0 0 0 0 0 0 0
|
||||
* 1 -> 1 0 0 0 0 0 0 0
|
||||
* 2 -> 1 1 0 0 0 0 0 0
|
||||
* 3 -> 1 1 1 0 0 0 0 0
|
||||
* ...
|
||||
* 7 -> 1 1 1 1 1 1 1 0
|
||||
*/
|
||||
//unsigned tmp = (1 << (8 - (blocks & 0x07))) - 1;
|
||||
//_bitmap[bytes] = ~tmp & 0xff;
|
||||
|
||||
_bitmap[bytes] = 0 - (1 << (8 - (blocks & 0x07)));
|
||||
|
||||
//_bitmap[bytes] = (0xff00 >> (blocks & 0x07)) & 0xff;
|
||||
}
|
||||
|
||||
Bitmap::~Bitmap()
|
||||
{
|
||||
delete[] _bitmap;
|
||||
}
|
||||
|
||||
|
||||
bool Bitmap::markBlock(unsigned block, bool inUse)
|
||||
{
|
||||
if (block >= _blocks) return false;
|
||||
|
||||
unsigned index = BlockIndex(block);
|
||||
unsigned mask = BlockMask(block);
|
||||
uint8_t data = _bitmap[index];
|
||||
|
||||
_freeBlocks -= popCount(data);
|
||||
|
||||
if (inUse) data &= ~mask;
|
||||
else data |= mask;
|
||||
|
||||
_bitmap[index] = data;
|
||||
_freeBlocks += popCount(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// find the first block starting from (and including) the
|
||||
// startingBlock
|
||||
|
||||
int Bitmap::firstFreeBlock(unsigned startingBlock) const
|
||||
{
|
||||
if (startingBlock >= _blocks) return -1;
|
||||
if (!_freeBlocks) return -1;
|
||||
|
||||
unsigned index = BlockIndex(startingBlock);
|
||||
unsigned bit = startingBlock & 0x0f;
|
||||
unsigned bytes = (_blocks + 7) >> 3;
|
||||
|
||||
// special case for first (partial) bitmap
|
||||
if (bit != 0)
|
||||
{
|
||||
uint8_t data = _bitmap[index];
|
||||
unsigned mask = BlockMask(startingBlock);
|
||||
// 0 0 1 0 0 0 0 0 -> 0 0 1 1 1 1 1 1
|
||||
mask = (mask - 1) | mask;
|
||||
data &= mask;
|
||||
if (data) return (index << 3) + countLeadingZeros(data);
|
||||
++index;
|
||||
}
|
||||
|
||||
for ( ; index < bytes; ++index)
|
||||
{
|
||||
uint8_t data = _bitmap[index];
|
||||
if (!data) continue;
|
||||
|
||||
return (index << 3) + countLeadingZeros(data);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// count the number of unused blocks.... (including startingBlock)
|
||||
int Bitmap::countUnusedBlocks(unsigned startingBlock, unsigned maxSearch) const
|
||||
{
|
||||
if (startingBlock >= _blocks) return -1;
|
||||
if (!_freeBlocks) return -1;
|
||||
|
||||
unsigned count = 0;
|
||||
|
||||
unsigned index = BlockIndex(startingBlock);
|
||||
unsigned bit = startingBlock & 0x0f;
|
||||
|
||||
unsigned bytes = (_blocks + 7) >> 3;
|
||||
|
||||
|
||||
// special case for the first (partial) byte.
|
||||
if (bit)
|
||||
{
|
||||
uint8_t data = _bitmap[index];
|
||||
if (data == 0) return 0;
|
||||
unsigned mask = BlockMask(startingBlock);
|
||||
// 0 0 1 0 0 0 0 0 -> 1 1 0 0 0 0 0 0
|
||||
mask = ~ ((mask - 1) | mask);
|
||||
data = data | mask;
|
||||
|
||||
if (data == 0xff)
|
||||
{
|
||||
count = 8 - bit;
|
||||
if (count >= maxSearch) return count;
|
||||
++index;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return countLeadingOnes(data) - bit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for ( ; index < bytes; ++index)
|
||||
{
|
||||
uint8_t data = _bitmap[index];
|
||||
|
||||
// no free blocks = end search
|
||||
if (data == 0) break;
|
||||
|
||||
// all free = continue (if necessary)
|
||||
if (data == 0xff)
|
||||
{
|
||||
count += 8;
|
||||
if (count >= maxSearch) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, add on any leading free and terminate.
|
||||
count += countLeadingOnes(data);
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// finds the first free block (with a possible range).
|
||||
int Bitmap::freeBlock(unsigned count) const
|
||||
{
|
||||
if (count == 0 || count > freeBlocks()) return -1;
|
||||
|
||||
// we could keep a linked list/etc of
|
||||
// free ranges
|
||||
// for now, scan the entire bitmap.
|
||||
|
||||
|
||||
int startBlock = 0;
|
||||
--count;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
startBlock = firstFreeBlock(startBlock);
|
||||
if (startBlock < 0) return -1;
|
||||
|
||||
if (count == 0) return startBlock;
|
||||
|
||||
int tmp = countUnusedBlocks(startBlock + 1, count);
|
||||
if (tmp <= 0) break;
|
||||
if (tmp >= count) return startBlock;
|
||||
|
||||
// miss ... jump ahead.
|
||||
startBlock += tmp + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
72
Bitmap.h
72
Bitmap.h
|
@ -1,72 +0,0 @@
|
|||
#ifndef __PRODOS_BITMAP_H__
|
||||
#define __PRODOS_BITMAP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ProDOS {
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
Bitmap(unsigned blocks);
|
||||
~Bitmap();
|
||||
|
||||
bool blockFree(unsigned block) const;
|
||||
bool markBlock(unsigned block, bool inUse);
|
||||
|
||||
unsigned blocks() const;
|
||||
unsigned bitmapBlocks() const;
|
||||
|
||||
unsigned freeBlocks() const;
|
||||
|
||||
int firstFreeBlock(unsigned startingBlock = 0) const;
|
||||
int countUnusedBlocks(unsigned startingBlock = 0, unsigned maxSearch = -1) const;
|
||||
|
||||
int freeBlock(unsigned count = 1) const;
|
||||
|
||||
|
||||
private:
|
||||
static unsigned BlockMask(unsigned block);
|
||||
static unsigned BlockIndex(unsigned block);
|
||||
|
||||
unsigned _blocks;
|
||||
unsigned _freeBlocks;
|
||||
unsigned _bitmapSize;
|
||||
uint8_t *_bitmap;
|
||||
|
||||
};
|
||||
|
||||
inline unsigned Bitmap::blocks() const
|
||||
{
|
||||
return _blocks;
|
||||
}
|
||||
|
||||
inline unsigned Bitmap::freeBlocks() const
|
||||
{
|
||||
return _blocks;
|
||||
}
|
||||
|
||||
inline unsigned Bitmap::bitmapBlocks() const
|
||||
{
|
||||
return _bitmapSize >> 12;
|
||||
}
|
||||
|
||||
inline unsigned Bitmap::BlockMask(unsigned block)
|
||||
{
|
||||
return 0x80 >> (block & 0x07);
|
||||
}
|
||||
|
||||
inline unsigned Bitmap::BlockIndex(unsigned block)
|
||||
{
|
||||
return block >> 3;
|
||||
}
|
||||
|
||||
inline bool Bitmap::blockFree(unsigned block) const
|
||||
{
|
||||
if (block >= _blocks) return false;
|
||||
return (_bitmap[BlockIndex(block)] & BlockMask(block)) != 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
#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::push24be(uint32_t x)
|
||||
{
|
||||
_buffer.push_back((x >> 16) & 0xff);
|
||||
_buffer.push_back((x >> 8) & 0xff);
|
||||
_buffer.push_back(x & 0xff);
|
||||
}
|
||||
|
||||
void Buffer::push24le(uint32_t x)
|
||||
{
|
||||
_buffer.push_back(x & 0xff);
|
||||
_buffer.push_back((x >> 8) & 0xff);
|
||||
_buffer.push_back((x >> 16) & 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] );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#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 clear() { _buffer.clear(); }
|
||||
|
||||
void push8(uint8_t x) { _buffer.push_back(x); }
|
||||
|
||||
void push16be(uint16_t);
|
||||
void push16le(uint16_t);
|
||||
|
||||
|
||||
void push24be(uint32_t);
|
||||
void push24le(uint32_t);
|
||||
|
||||
void push32be(uint32_t);
|
||||
void push32le(uint32_t);
|
||||
|
||||
void pushBytes(const void *data, unsigned size);
|
||||
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> _buffer;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/auto.h>
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
|
||||
BlockCache::BlockCache(BlockDevicePointer device) :
|
||||
_device(device)
|
||||
{
|
||||
_blocks = device->blocks();
|
||||
_readOnly = device->readOnly();
|
||||
}
|
||||
|
||||
BlockCache::~BlockCache()
|
||||
{
|
||||
}
|
||||
|
||||
void BlockCache::write(unsigned block, const void *bp)
|
||||
{
|
||||
void *address = acquire(block);
|
||||
std::memcpy(address, bp, 512);
|
||||
release(block, true);
|
||||
}
|
||||
|
||||
void BlockCache::read(unsigned block, void *bp)
|
||||
{
|
||||
void *address = acquire(block);
|
||||
std::memcpy(bp, address, 512);
|
||||
release(block, false);
|
||||
}
|
||||
|
||||
|
||||
BlockCachePointer BlockCache::Create(BlockDevicePointer device)
|
||||
{
|
||||
// this just calls the device virtual function to create a cache.
|
||||
if (!device) return BlockCachePointer();
|
||||
|
||||
return device->createBlockCache();
|
||||
}
|
||||
|
||||
|
||||
void BlockCache::zeroBlock(unsigned block)
|
||||
{
|
||||
/*
|
||||
void *address = acquire(block);
|
||||
std::memset(address, 0, 512);
|
||||
release(block, true);
|
||||
*/
|
||||
|
||||
uint8_t buffer[512];
|
||||
|
||||
std::memset(buffer, 0, 512);
|
||||
write(block, buffer);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef __BLOCKCACHE_H__
|
||||
#define __BLOCKCACHE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <Device/Device.h>
|
||||
|
||||
|
||||
class MappedFile;
|
||||
|
||||
namespace Device {
|
||||
|
||||
|
||||
enum BlockReleaseFlags {
|
||||
kBlockDirty = 1,
|
||||
kBlockCommitNow = 2,
|
||||
kBlockReuse = 3
|
||||
};
|
||||
|
||||
class BlockCache {
|
||||
public:
|
||||
|
||||
static BlockCachePointer Create(BlockDevicePointer device);
|
||||
|
||||
virtual ~BlockCache();
|
||||
|
||||
bool readOnly() { return _readOnly; }
|
||||
unsigned blocks() { return _blocks; }
|
||||
BlockDevicePointer device() { return _device; }
|
||||
|
||||
|
||||
virtual void sync() = 0;
|
||||
virtual void write(unsigned block, const void *bp);
|
||||
virtual void read(unsigned block, void *bp);
|
||||
|
||||
virtual void *acquire(unsigned block) = 0;
|
||||
virtual void release(unsigned block, int flags) = 0 ;
|
||||
virtual void markDirty(unsigned block) = 0;
|
||||
|
||||
|
||||
virtual void zeroBlock(unsigned block);
|
||||
|
||||
void release(unsigned block) { release(block, 0); }
|
||||
void release(unsigned block, bool dirty)
|
||||
{
|
||||
release(block, dirty ? kBlockDirty : 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
BlockCache(BlockDevicePointer device);
|
||||
|
||||
BlockDevicePointer _device;
|
||||
|
||||
private:
|
||||
unsigned _blocks;
|
||||
bool _readOnly;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,380 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Cache/ConcreteBlockCache.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/auto.h>
|
||||
|
||||
|
||||
/*
|
||||
* Note -- everything is assumed to be single-threaded.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The primary purpose of the block cache is not as a cache
|
||||
* (the OS probably does a decent job of that) but rather to
|
||||
* simplify read/writes. Blocks will always be accessed by
|
||||
* pointer, so any updates will be shared.
|
||||
* For memory mapped prodos-order files, MappedBlockCache just
|
||||
* returns a pointer to the memory.
|
||||
* For dos-order, nibblized, or raw devices, ConcreteBlockCache
|
||||
* uses an approach similar to minix (although the buffer pool will
|
||||
* expand if needed).
|
||||
*
|
||||
* _buffers is a vector of all buffers and only exists to make
|
||||
* freeing them easier.
|
||||
* _hashTable is a simple hashtable of loaded blocks.
|
||||
* _first and _last are a double-linked list of unused blocks, stored
|
||||
* in lru order.
|
||||
*
|
||||
* The Entry struct contains the buffer, the block, a dirty flag, and an in-use
|
||||
* count as well as pointer for the hashtable and lru list.
|
||||
* When a block is loaded, it is stored in the _hashTable. It remains in the
|
||||
* hash table when the in-use count goes to 0 (it will also be added to the
|
||||
* end of the lru list).
|
||||
*
|
||||
* dirty buffers are only written to disk in 3 scenarios:
|
||||
* a) sync() is called
|
||||
* b) the cache is deleted
|
||||
* c) a buffer is re-used from the lru list.
|
||||
*/
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
|
||||
//typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
|
||||
|
||||
|
||||
BlockCachePointer ConcreteBlockCache::Create(BlockDevicePointer device, unsigned size)
|
||||
{
|
||||
//return BlockCachePointer(new ConcreteBlockCache(device, size));
|
||||
// constructor must be accessible to std::make_shared...
|
||||
|
||||
return MAKE_SHARED(ConcreteBlockCache, device, size);
|
||||
}
|
||||
|
||||
ConcreteBlockCache::ConcreteBlockCache(BlockDevicePointer device, unsigned size) :
|
||||
BlockCache(device)
|
||||
{
|
||||
if (size < 16) size = 16;
|
||||
|
||||
std::memset(_hashTable, 0, sizeof(Entry *) * HashTableSize);
|
||||
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
Entry *e = new Entry;
|
||||
|
||||
std::memset(e, 0, sizeof(Entry));
|
||||
_buffers.push_back(e);
|
||||
}
|
||||
_first = _last = NULL;
|
||||
|
||||
_first = _buffers.front();
|
||||
_last = _buffers.back();
|
||||
// form a chain....
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
Entry *e = _buffers[i];
|
||||
|
||||
if (i > 0) e->prev = _buffers[i - 1];
|
||||
|
||||
if (i < size - 1) e->next = _buffers[i + 1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConcreteBlockCache::~ConcreteBlockCache()
|
||||
{
|
||||
EntryIter iter;
|
||||
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
||||
{
|
||||
Entry *e = *iter;
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
}
|
||||
|
||||
delete e;
|
||||
}
|
||||
_device->sync();
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::sync()
|
||||
{
|
||||
EntryIter iter;
|
||||
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
||||
{
|
||||
Entry *e = *iter;
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
}
|
||||
_device->sync();
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::write(unsigned block, const void *bp)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e)
|
||||
{
|
||||
e->dirty = true;
|
||||
std::memcpy(e->buffer, bp, 512);
|
||||
return;
|
||||
}
|
||||
|
||||
// returns a new entry not in the hashtable or the linked list.
|
||||
// we add it to both.
|
||||
e = newEntry(block);
|
||||
|
||||
e->count = 0;
|
||||
e->dirty = true;
|
||||
|
||||
std::memcpy(e->buffer, bp, 512);
|
||||
|
||||
addEntry(e);
|
||||
setLast(e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ConcreteBlockCache::markDirty(unsigned block)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e) e->dirty = true;
|
||||
// error otherwise?
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::release(unsigned block, int flags)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
bool dirty = flags & (kBlockDirty | kBlockCommitNow);
|
||||
|
||||
if (e)
|
||||
{
|
||||
if (dirty) e->dirty = true;
|
||||
|
||||
decrementCount(e);
|
||||
|
||||
if (flags & kBlockCommitNow)
|
||||
{
|
||||
_device->write(block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
|
||||
}
|
||||
// error otherwise?
|
||||
}
|
||||
|
||||
void *ConcreteBlockCache::acquire(unsigned block)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e)
|
||||
{
|
||||
incrementCount(e);
|
||||
return e->buffer;
|
||||
}
|
||||
|
||||
// returns a new entry, not in hash table, not in free list.
|
||||
e = newEntry(block);
|
||||
|
||||
_device->read(block, e->buffer);
|
||||
|
||||
addEntry(e);
|
||||
|
||||
return e->buffer;
|
||||
}
|
||||
|
||||
unsigned ConcreteBlockCache::hashFunction(unsigned block)
|
||||
{
|
||||
return block % HashTableSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ConcreteBlockCache::Entry *ConcreteBlockCache::findEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
unsigned hash = hashFunction(block);
|
||||
|
||||
e = _hashTable[hash];
|
||||
|
||||
while ((e) && (e->block != block))
|
||||
e = e->nextHash;
|
||||
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove a block from the hashtable
|
||||
* and write to dick if dirty.
|
||||
*/
|
||||
void ConcreteBlockCache::removeEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
Entry *prev;
|
||||
unsigned hash = hashFunction(block);
|
||||
|
||||
e = _hashTable[hash];
|
||||
if (!e) return;
|
||||
|
||||
// head pointer, special case.
|
||||
if (e->block == block)
|
||||
{
|
||||
_hashTable[hash] = e->nextHash;
|
||||
e->nextHash = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
prev = e;
|
||||
e = e->nextHash;
|
||||
|
||||
if (!e) break;
|
||||
if (e->block == block)
|
||||
{
|
||||
prev->nextHash = e->nextHash;
|
||||
e->nextHash = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::addEntry(Entry *e)
|
||||
{
|
||||
unsigned hash = hashFunction(e->block);
|
||||
|
||||
e->nextHash = _hashTable[hash];
|
||||
_hashTable[hash] = e;
|
||||
}
|
||||
|
||||
// increment the count and remove from the free list
|
||||
// if necessary.
|
||||
void ConcreteBlockCache::incrementCount(Entry *e)
|
||||
{
|
||||
|
||||
if (e->count == 0)
|
||||
{
|
||||
Entry *prev = e->prev;
|
||||
Entry *next = e->next;
|
||||
|
||||
e->prev = e->next = NULL;
|
||||
|
||||
if (prev) prev->next = next;
|
||||
if (next) next->prev = prev;
|
||||
|
||||
if (_first == e) _first = next;
|
||||
if (_last == e) _last = prev;
|
||||
}
|
||||
|
||||
e->count = e->count + 1;
|
||||
|
||||
}
|
||||
|
||||
// decrement the count. If it goes to 0,
|
||||
// add it as the last block entry.
|
||||
void ConcreteBlockCache::decrementCount(Entry *e)
|
||||
{
|
||||
e->count = e->count - 1;
|
||||
if (e->count == 0)
|
||||
{
|
||||
setLast(e);
|
||||
}
|
||||
}
|
||||
|
||||
ConcreteBlockCache::Entry *ConcreteBlockCache::newEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
if (_first)
|
||||
{
|
||||
e = _first;
|
||||
if (_first == _last)
|
||||
{
|
||||
_first = _last = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
_first = e->next;
|
||||
_first->prev = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
removeEntry(e->block);
|
||||
}
|
||||
else
|
||||
{
|
||||
e = new Entry;
|
||||
_buffers.push_back(e);
|
||||
}
|
||||
|
||||
e->next = NULL;
|
||||
e->prev= NULL;
|
||||
e->nextHash = NULL;
|
||||
e->count = 1;
|
||||
e->block = block;
|
||||
e->dirty = false;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::setLast(Entry *e)
|
||||
{
|
||||
if (_last == NULL)
|
||||
{
|
||||
_first = _last = e;
|
||||
return;
|
||||
}
|
||||
|
||||
e->prev = _last;
|
||||
_last->next = e;
|
||||
_last = e;
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::setFirst(Entry *e)
|
||||
{
|
||||
if (_first == NULL)
|
||||
{
|
||||
_first = _last = e;
|
||||
return;
|
||||
}
|
||||
|
||||
e->next = _first;
|
||||
_first->prev = e;
|
||||
_first = e;
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef __CONCRETE_BLOCK_CACHE_H__
|
||||
#define __CONCRETE_BLOCK_CACHE_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class ConcreteBlockCache : public BlockCache {
|
||||
public:
|
||||
|
||||
static BlockCachePointer Create(BlockDevicePointer device, unsigned size = 16);
|
||||
|
||||
virtual ~ConcreteBlockCache();
|
||||
|
||||
virtual void sync();
|
||||
virtual void write(unsigned block, const void *vp);
|
||||
|
||||
|
||||
virtual void *acquire(unsigned block);
|
||||
virtual void release(unsigned block, int flags);
|
||||
virtual void markDirty(unsigned block);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
ConcreteBlockCache(BlockDevicePointer device, unsigned size);
|
||||
|
||||
struct Entry {
|
||||
unsigned block;
|
||||
unsigned count;
|
||||
bool dirty;
|
||||
|
||||
struct Entry *next;
|
||||
struct Entry *prev;
|
||||
struct Entry *nextHash;
|
||||
|
||||
uint8_t buffer[512];
|
||||
|
||||
};
|
||||
|
||||
typedef std::vector<Entry *>::iterator EntryIter;
|
||||
|
||||
enum { HashTableSize = 23 };
|
||||
|
||||
std::vector<Entry *>_buffers;
|
||||
|
||||
Entry *_hashTable[HashTableSize];
|
||||
|
||||
Entry *_first;
|
||||
Entry *_last;
|
||||
|
||||
|
||||
unsigned hashFunction(unsigned block);
|
||||
|
||||
Entry *findEntry(unsigned block);
|
||||
void removeEntry(unsigned block);
|
||||
void addEntry(Entry *);
|
||||
|
||||
Entry *newEntry(unsigned block);
|
||||
|
||||
void pushEntry(Entry *);
|
||||
|
||||
void setLast(Entry *);
|
||||
void setFirst(Entry *);
|
||||
|
||||
void incrementCount(Entry *);
|
||||
void decrementCount(Entry *);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
BlockCachePointer MappedBlockCache::Create(BlockDevicePointer device, void *data)
|
||||
{
|
||||
//return BlockCachePointer(new MappedBlockCache(device, data));
|
||||
return MAKE_SHARED(MappedBlockCache, device, data);
|
||||
}
|
||||
|
||||
|
||||
MappedBlockCache::MappedBlockCache(BlockDevicePointer device, void *data) :
|
||||
BlockCache(device)
|
||||
{
|
||||
_data = (uint8_t *)data;
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
MappedBlockCache::~MappedBlockCache()
|
||||
{
|
||||
if (_dirty) sync();
|
||||
}
|
||||
|
||||
|
||||
void *MappedBlockCache::acquire(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::load"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
|
||||
return _data + block * 512;
|
||||
}
|
||||
|
||||
void MappedBlockCache::release(unsigned block, int flags)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::unload"
|
||||
|
||||
// kBlockCommitNow implies kBlockDirty.
|
||||
if (flags & kBlockCommitNow)
|
||||
{
|
||||
sync(block);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & kBlockDirty) _dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MappedBlockCache::write(unsigned block, const void *vp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::write"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
_dirty = true;
|
||||
std::memcpy(_data + block * 512, vp, 512);
|
||||
}
|
||||
|
||||
|
||||
void MappedBlockCache::zeroBlock(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::zeroBlock"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
|
||||
_dirty = true;
|
||||
std::memset(_data + block * 512, 0, 512);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// sync everything.
|
||||
void MappedBlockCache::sync()
|
||||
{
|
||||
_device->sync();
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* sync an individual page.
|
||||
*
|
||||
*/
|
||||
void MappedBlockCache::sync(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::sync"
|
||||
|
||||
|
||||
int pagesize = ::getpagesize();
|
||||
|
||||
void *start = _data + block * 512;
|
||||
void *end = _data + 512 + block * 512;
|
||||
|
||||
|
||||
start = (void *)((ptrdiff_t)start / pagesize * pagesize);
|
||||
end = (void *)((ptrdiff_t)end / pagesize * pagesize);
|
||||
|
||||
if (::msync(start, pagesize, MS_SYNC) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": msync", errno);
|
||||
|
||||
if (start != end)
|
||||
{
|
||||
if (::msync(end, pagesize, MS_SYNC) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": msync", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void MappedBlockCache::markDirty(unsigned block)
|
||||
{
|
||||
_dirty = true;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef __MAPPED_BLOCK_CACHE_H__
|
||||
#define __MAPPED_BLOCK_CACHE_H__
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class MappedBlockCache : public BlockCache {
|
||||
public:
|
||||
|
||||
static BlockCachePointer Create(BlockDevicePointer device, void *data);
|
||||
|
||||
virtual ~MappedBlockCache();
|
||||
|
||||
virtual void sync();
|
||||
virtual void write(unsigned block, const void *vp);
|
||||
|
||||
virtual void zeroBlock(unsigned block);
|
||||
|
||||
|
||||
virtual void *acquire(unsigned block);
|
||||
virtual void release(unsigned block, int flags);
|
||||
virtual void markDirty(unsigned block);
|
||||
|
||||
private:
|
||||
|
||||
MappedBlockCache(BlockDevicePointer device, void *data);
|
||||
|
||||
void sync(unsigned block);
|
||||
|
||||
uint8_t *_data;
|
||||
bool _dirty;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
#include "Exception.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
Exception::Exception(const char *cp):
|
||||
_error(0),
|
||||
_string(cp)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::Exception(const std::string& string):
|
||||
_error(0),
|
||||
_string(string)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::Exception(const char *cp, int error):
|
||||
_error(error),
|
||||
_string(cp)
|
||||
{
|
||||
}
|
||||
|
||||
Exception::Exception(const std::string& string, int error):
|
||||
_error(error),
|
||||
_string(string)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Exception::~Exception() throw()
|
||||
{
|
||||
}
|
||||
|
||||
const char *Exception::what() const throw()
|
||||
{
|
||||
return _string.c_str();
|
||||
}
|
||||
|
||||
const char *Exception::errorString()
|
||||
{
|
||||
return "";
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef __COMMON_EXCEPTION_H__
|
||||
#define __COMMON_EXCEPTION_H__
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const char *cp);
|
||||
Exception(const std::string &str);
|
||||
|
||||
|
||||
virtual ~Exception() throw ();
|
||||
|
||||
virtual const char *what() const throw();
|
||||
virtual const char *errorString();
|
||||
|
||||
int error() const { return _error; }
|
||||
|
||||
protected:
|
||||
Exception(const char *cp, int error);
|
||||
Exception(const std::string& string, int error);
|
||||
|
||||
private:
|
||||
int _error;
|
||||
std::string _string;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#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
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef __AUTO_H__
|
||||
#define __AUTO_H__
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
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]; }
|
||||
|
||||
void reset(T *t)
|
||||
{
|
||||
if (t == _t) return;
|
||||
if (_t) delete[] _t;
|
||||
_t = t;
|
||||
}
|
||||
|
||||
private:
|
||||
T *_t;
|
||||
};
|
||||
|
||||
// ::close
|
||||
#if defined(O_CREAT)
|
||||
class auto_fd
|
||||
{
|
||||
public:
|
||||
auto_fd(int fd = -1) : _fd(fd) { }
|
||||
|
||||
~auto_fd() { close(); }
|
||||
|
||||
int release()
|
||||
{ int tmp = _fd; _fd = -1; return tmp; }
|
||||
|
||||
int get() const { return _fd; }
|
||||
operator int() const { return _fd; }
|
||||
|
||||
void reset(int fd)
|
||||
{
|
||||
if (fd != _fd)
|
||||
{
|
||||
close();
|
||||
_fd = fd;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
auto_fd& operator=(const auto_fd&);
|
||||
|
||||
void close()
|
||||
{
|
||||
if (_fd >= 0)
|
||||
{
|
||||
::close(_fd);
|
||||
_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int _fd;
|
||||
};
|
||||
#endif
|
||||
|
||||
// ::mmap, :munmap
|
||||
#if defined(MAP_FAILED)
|
||||
class auto_map
|
||||
{
|
||||
public:
|
||||
auto_map(void *addr, size_t size, int prot, int flags, int fd, off_t offset)
|
||||
:
|
||||
_size(size),
|
||||
_map(::mmap(addr, size, prot, flags, fd, offset))
|
||||
{ }
|
||||
|
||||
~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
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
#ifndef __COMMON_SMART_POINTERS_H__
|
||||
#define __COMMON_SMART_POINTERS_H__
|
||||
|
||||
#ifdef CPP0X
|
||||
//C++0x
|
||||
#include <memory>
|
||||
|
||||
|
||||
#define SHARED_PTR(T) std::shared_ptr<T>
|
||||
#define WEAK_PTR(T) std::weak_ptr<T>
|
||||
|
||||
#define MAKE_SHARED(T, ...) std::make_shared<T>(__VA_ARGS__)
|
||||
#define ENABLE_SHARED_FROM_THIS(T) std::enable_shared_from_this<T>
|
||||
|
||||
#define STATIC_POINTER_CAST(T, ARG) std::static_pointer_cast<T>(ARG)
|
||||
#define DYNAMIC_POINTER_CAST(T, ARG) std::dynamic_pointer_cast<T>(ARG)
|
||||
|
||||
#else
|
||||
|
||||
// tr1
|
||||
#include <tr1/memory>
|
||||
|
||||
#define SHARED_PTR(T) std::tr1::shared_ptr<T>
|
||||
#define WEAK_PTR(T) std::tr1::weak_ptr<T>
|
||||
|
||||
#define MAKE_SHARED(T, ...) std::tr1::shared_ptr<T>(new T(__VA_ARGS__))
|
||||
#define ENABLE_SHARED_FROM_THIS(T) std::tr1::enable_shared_from_this<T>
|
||||
|
||||
#define STATIC_POINTER_CAST(T, ARG) std::tr1::static_pointer_cast<T>(ARG)
|
||||
#define DYNAMIC_POINTER_CAST(T, ARG) std::tr1::dynamic_pointer_cast<T>(ARG)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __COMMON_UNORDERED_MAP_H__
|
||||
#define __COMMON_UNORDERED_MAP_H__
|
||||
|
||||
#ifdef CPP0X
|
||||
//c++0x
|
||||
#include <unordered_map>
|
||||
#define UNORDERED_MAP(...) std::unordered_map(__VA_ARGS__)
|
||||
#else
|
||||
// tr1
|
||||
#include <tr1/unordered_map>
|
||||
#define UNORDERED_MAP(...) std::tr1::unordered_map(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,486 @@
|
|||
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <Device/Adaptor.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
Adaptor::~Adaptor()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
POAdaptor::POAdaptor(void *address)
|
||||
{
|
||||
_address = (uint8_t *)address;
|
||||
}
|
||||
|
||||
void POAdaptor::readBlock(unsigned block, void *bp)
|
||||
{
|
||||
std::memcpy(bp, _address + block * 512, 512);
|
||||
}
|
||||
|
||||
void POAdaptor::writeBlock(unsigned block, const void *bp)
|
||||
{
|
||||
std::memcpy(_address + block * 512, bp, 512);
|
||||
}
|
||||
|
||||
|
||||
unsigned DOAdaptor::Map[] = {
|
||||
0x00, 0x0e, 0x0d, 0x0c,
|
||||
0x0b, 0x0a, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04,
|
||||
0x03, 0x02, 0x01, 0x0f
|
||||
};
|
||||
|
||||
DOAdaptor::DOAdaptor(void *address)
|
||||
{
|
||||
_address = (uint8_t *)address;
|
||||
}
|
||||
|
||||
void DOAdaptor::readBlock(unsigned block, void *bp)
|
||||
{
|
||||
|
||||
unsigned track = (block & ~0x07) << 9;
|
||||
unsigned sector = (block & 0x07) << 1;
|
||||
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
size_t offset = track | (Map[sector+i] << 8);
|
||||
|
||||
std::memcpy(bp, _address + offset, 256);
|
||||
|
||||
bp = (uint8_t *)bp + 256;
|
||||
}
|
||||
}
|
||||
|
||||
void DOAdaptor::writeBlock(unsigned block, const void *bp)
|
||||
{
|
||||
unsigned track = (block & ~0x07) << 9;
|
||||
unsigned sector = (block & 0x07) << 1;
|
||||
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
size_t offset = track | (Map[sector+i] << 8);
|
||||
|
||||
std::memcpy(_address + offset, bp, 256);
|
||||
bp = (uint8_t *)bp + 256;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark NibbleAdaptor
|
||||
|
||||
class CircleBuffer {
|
||||
|
||||
public:
|
||||
|
||||
CircleBuffer(void *address, unsigned length)
|
||||
{
|
||||
_address = (uint8_t *)address;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
uint8_t operator[](unsigned i) const
|
||||
{
|
||||
if (i >= _length) i %= _length;
|
||||
return _address[i];
|
||||
}
|
||||
|
||||
uint8_t& operator[](unsigned i)
|
||||
{
|
||||
if (i >= _length) i %= _length;
|
||||
return _address[i];
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t *_address;
|
||||
unsigned _length;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
uint8_t NibbleAdaptor::decode44(uint8_t x, uint8_t y)
|
||||
{
|
||||
return ((x << 1) | 0x01) & y;
|
||||
}
|
||||
|
||||
std::pair<uint8_t, uint8_t> NibbleAdaptor::encode44(uint8_t val)
|
||||
{
|
||||
uint8_t x = (val >> 1) | 0xaa;
|
||||
uint8_t y = val | 0xaa;
|
||||
|
||||
return std::make_pair(x,y);
|
||||
}
|
||||
|
||||
uint8_t NibbleAdaptor::encode62(uint8_t val)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "NibbleAdaptor::encode62"
|
||||
|
||||
static uint8_t table[64] = {
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
|
||||
if (val > 0x3f)
|
||||
throw ::Exception(__METHOD__ ": Invalid 6-2 value.");
|
||||
|
||||
return table[val];
|
||||
}
|
||||
|
||||
|
||||
uint8_t NibbleAdaptor::decode62(uint8_t val)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "decode62"
|
||||
|
||||
// auto-generated via perl.
|
||||
static uint8_t table[] = {
|
||||
-1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, 3, -1, 4, 5, 6,
|
||||
-1, -1, -1, -1, -1, -1, 7, 8, -1, -1, -1, 9, 10, 11, 12, 13,
|
||||
-1, -1, 14, 15, 16, 17, 18, 19, -1, 20, 21, 22, 23, 24, 25, 26,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, 28, 29, 30,
|
||||
-1, -1, -1, 31, -1, -1, 32, 33, -1, 34, 35, 36, 37, 38, 39, 40,
|
||||
-1, -1, -1, -1, -1, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50,
|
||||
-1, -1, 51, 52, 53, 54, 55, 56, -1, 57, 58, 59, 60, 61, 62, 63
|
||||
};
|
||||
|
||||
if ((val < 0x90) || (table[val - 0x90] == 0xff))
|
||||
throw ::Exception(__METHOD__ ": Invalid 6-2 encoding.");
|
||||
|
||||
return table[val - 0x90];
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int FindByte(void *address, uint8_t c, unsigned length, unsigned offset = 0)
|
||||
{
|
||||
|
||||
for (unsigned i = offset; i < length; ++i)
|
||||
{
|
||||
if ( ((uint8_t *)address)[i] == c) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Address Field:
|
||||
* prologue volume track sector checksum epilogue
|
||||
* D5 AA 96 XX YY XX YY XX YY XX YY DE AA EB
|
||||
*/
|
||||
|
||||
/*
|
||||
* Data Field:
|
||||
* prologue user data checksum epilogue
|
||||
* D5 AA AD [6+2 encoded] XX DE AA EB
|
||||
*/
|
||||
|
||||
|
||||
|
||||
NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "NibbleAdaptor::NibbleAdaptor"
|
||||
|
||||
_address = (uint8_t *)address;
|
||||
_length = length;
|
||||
|
||||
|
||||
// build a map of track/sectors.
|
||||
|
||||
unsigned state = 0;
|
||||
|
||||
|
||||
_index.resize(35 * 16, -1);
|
||||
|
||||
int offset = 0;
|
||||
|
||||
unsigned track = 0;
|
||||
unsigned sector = 0;
|
||||
unsigned volume = 0;
|
||||
unsigned checksum = 0;
|
||||
|
||||
|
||||
CircleBuffer buffer(_address, _length);
|
||||
for (;;)
|
||||
{
|
||||
|
||||
offset = FindByte(address, 0xd5, length, offset);
|
||||
if (offset < 0) break;
|
||||
|
||||
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0x96 && buffer[offset + 11] == 0xde && buffer[offset + 12] == 0xaa)
|
||||
{
|
||||
volume = decode44(buffer[offset + 3], buffer[offset + 4]);
|
||||
track = decode44(buffer[offset + 5], buffer[offset + 6]);
|
||||
sector = decode44(buffer[offset + 7], buffer[offset + 8]);
|
||||
checksum = decode44(buffer[offset + 9], buffer[offset + 10]);
|
||||
|
||||
if (volume ^ track ^ sector ^ checksum)
|
||||
throw ::Exception(__METHOD__ ": Invalid address checksum.");
|
||||
|
||||
if (track > 35 || sector > 16)
|
||||
throw ::Exception(__METHOD__ ": Invalid track/sector.");
|
||||
|
||||
offset += 3 + 8 + 3;
|
||||
|
||||
state = 1;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad && state == 1)
|
||||
{
|
||||
if (_index[track * 16 + sector] != -1)
|
||||
{
|
||||
std::fprintf(stderr, "track %u sector %u duplicated.\n", track, sector);
|
||||
}
|
||||
_index[track * 16 + sector] = (offset + 3) % _length;
|
||||
|
||||
//offset += 3 + 342 + 1 + 3;
|
||||
offset++;
|
||||
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
offset++; //???
|
||||
// ????
|
||||
|
||||
}
|
||||
|
||||
// possible wraparound.
|
||||
if (state == 1)
|
||||
{
|
||||
offset = FindByte(address, 0xd5, length, 0);
|
||||
|
||||
if (offset >= 0)
|
||||
{
|
||||
|
||||
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad)
|
||||
{
|
||||
_index[track * 16 + sector] = (offset + 3) % _length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now check _index for offset = -1, which means the sector/track wasn't found.
|
||||
|
||||
for (std::vector<unsigned>::iterator iter = _index.begin(); iter != _index.end(); ++iter)
|
||||
{
|
||||
if (*iter == -1)
|
||||
{
|
||||
int offset = distance(_index.begin(), iter);
|
||||
std::fprintf(stderr, "Error: track %u sector %u missing.\n", offset / 16, offset % 16);
|
||||
//throw ::Exception(__METHOD__ ": Sector missing.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NibbleAdaptor::~NibbleAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
void NibbleAdaptor::readBlock(unsigned block, void *bp)
|
||||
{
|
||||
|
||||
unsigned track = (block & ~0x07) << 9;
|
||||
unsigned b = (block & 0x07) << 1;
|
||||
|
||||
/*
|
||||
* block sectors
|
||||
* 0 0, 2
|
||||
* 1 4, 6
|
||||
* 2 8, 10
|
||||
* 3 12,14
|
||||
* 4 1, 3
|
||||
* 5 5, 7
|
||||
* 6 9, 11
|
||||
* 7 13, 15
|
||||
*/
|
||||
|
||||
unsigned sector = b >> 2;
|
||||
if (sector >= 16) sector -= 15;
|
||||
|
||||
|
||||
readTrackSector(TrackSector(track, sector), bp);
|
||||
readTrackSector(TrackSector(track, sector + 1), (uint8_t *)bp + 256);
|
||||
}
|
||||
|
||||
void NibbleAdaptor::writeBlock(unsigned block, const void *bp)
|
||||
{
|
||||
unsigned track = (block & ~0x07) << 9;
|
||||
unsigned b = (block & 0x07) << 1;
|
||||
|
||||
/*
|
||||
* block sectors
|
||||
* 0 0, 2
|
||||
* 1 4, 6
|
||||
* 2 8, 10
|
||||
* 3 12,14
|
||||
* 4 1, 3
|
||||
* 5 5, 7
|
||||
* 6 9, 11
|
||||
* 7 13, 15
|
||||
*/
|
||||
|
||||
unsigned sector = b >> 2;
|
||||
if (sector >= 16) sector -= 15;
|
||||
|
||||
|
||||
writeTrackSector(TrackSector(track, sector), bp);
|
||||
writeTrackSector(TrackSector(track, sector + 1), (const uint8_t *)bp + 256);
|
||||
}
|
||||
|
||||
void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "NibbleAdaptor::readTrackSector"
|
||||
|
||||
if (ts.track > 35 || ts.sector > 16)
|
||||
throw ::Exception(__METHOD__ ": Invalid track/sector.");
|
||||
|
||||
|
||||
CircleBuffer buffer(_address, _length);
|
||||
uint8_t bits[86 * 3];
|
||||
|
||||
uint8_t checksum = 0;
|
||||
|
||||
|
||||
unsigned offset = _index[ts.track * 16 + ts.sector];
|
||||
|
||||
if (offset == -1)
|
||||
{
|
||||
throw ::Exception(__METHOD__ ": Missing track/sector.");
|
||||
}
|
||||
|
||||
// first 86 bytes are in the auxbuffer, backwards.
|
||||
unsigned index = offset;
|
||||
for (unsigned i = 0; i < 86; ++i)
|
||||
{
|
||||
uint8_t x = buffer[index++];
|
||||
x = decode62(x);
|
||||
|
||||
checksum ^= x;
|
||||
|
||||
uint8_t y = checksum;
|
||||
|
||||
/*
|
||||
for (unsigned j = 0; j < 3; ++j)
|
||||
{
|
||||
//bits[i + j * 86] = ((y & 0x01) << 1) | ((y & 0x02) >> 1);
|
||||
bits[i + j * 86] = "\x00\x01\x02\x03"[y & 0x03];
|
||||
y >>= 2;
|
||||
}
|
||||
*/
|
||||
bits[i + 86 * 0] = "\x00\x02\x01\x03"[y & 0x03];
|
||||
bits[i + 86 * 1] = "\x00\x02\x01\x03"[(y >> 2) & 0x03];
|
||||
bits[i + 86 * 2] = "\x00\x02\x01\x03"[(y >> 4) & 0x03];
|
||||
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 256; ++i)
|
||||
{
|
||||
uint8_t x = buffer[index++];
|
||||
x = decode62(x);
|
||||
|
||||
checksum ^= x;
|
||||
|
||||
uint8_t y = (checksum << 2) | bits[i];
|
||||
|
||||
|
||||
|
||||
((uint8_t *)bp)[i] = y;
|
||||
}
|
||||
|
||||
if (checksum != decode62(buffer[index++]))
|
||||
std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector);
|
||||
//throw ::Exception(__METHOD__ ": Invalid field checksum.");
|
||||
|
||||
}
|
||||
|
||||
void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "NibbleAdaptor::writeTrackSector"
|
||||
|
||||
if (ts.track > 35 || ts.sector > 16)
|
||||
throw ::Exception(__METHOD__ ": Invalid track/sector.");
|
||||
|
||||
uint8_t auxBuffer[86];
|
||||
uint8_t checksum = 0;
|
||||
|
||||
// create the aux buffer.
|
||||
|
||||
std::memset(auxBuffer, 0, sizeof(auxBuffer));
|
||||
|
||||
for (unsigned i = 0, j = 0, shift = 0; i < 256; ++i)
|
||||
{
|
||||
uint8_t x = ((const uint8_t *)bp)[i];
|
||||
|
||||
// grab the bottom 2 bytes and reverse them.
|
||||
//uint8_t y = ((x & 0x01) << 1) | ((x & 0x02) >> 1);
|
||||
uint8_t y = "\x00\x02\x01\x03"[x & 0x03];
|
||||
|
||||
auxBuffer[j++] |= (y << shift);
|
||||
|
||||
if (j == 86)
|
||||
{
|
||||
j = 0;
|
||||
shift += 2;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned offset = _index[ts.track * 16 + ts.sector];
|
||||
|
||||
CircleBuffer buffer(_address, _length);
|
||||
// create the checksum while writing to disk..
|
||||
|
||||
// aux buffer
|
||||
for (unsigned i = 0; i < 86; ++i)
|
||||
{
|
||||
uint8_t x = auxBuffer[i];
|
||||
|
||||
buffer[offset + i] = encode62(x ^ checksum);
|
||||
|
||||
checksum = x;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 256; ++i)
|
||||
{
|
||||
uint8_t x = ((const uint8_t *)bp)[i];
|
||||
x >>= 2;
|
||||
|
||||
buffer[offset + 86 + i] = encode62(x ^ checksum);
|
||||
|
||||
checksum = x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
buffer[offset + 342] = encode62(checksum);
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef __DEVICE_ADAPTOR_H__
|
||||
#define __DEVICE_ADAPTOR_H__
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <Device/TrackSector.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class Adaptor
|
||||
{
|
||||
public:
|
||||
virtual ~Adaptor();
|
||||
virtual void readBlock(unsigned block, void *bp) = 0;
|
||||
virtual void writeBlock(unsigned block, const void *bp) = 0;
|
||||
};
|
||||
|
||||
|
||||
class POAdaptor : public Adaptor
|
||||
{
|
||||
public:
|
||||
POAdaptor(void *address);
|
||||
virtual void readBlock(unsigned block, void *bp);
|
||||
virtual void writeBlock(unsigned block, const void *bp);
|
||||
private:
|
||||
uint8_t *_address;
|
||||
};
|
||||
|
||||
class DOAdaptor : public Adaptor
|
||||
{
|
||||
public:
|
||||
DOAdaptor(void *address);
|
||||
virtual void readBlock(unsigned block, void *bp);
|
||||
virtual void writeBlock(unsigned block, const void *bp);
|
||||
|
||||
|
||||
static unsigned Map[];
|
||||
|
||||
private:
|
||||
uint8_t *_address;
|
||||
|
||||
};
|
||||
|
||||
// TODO -- nibble adaptor.
|
||||
|
||||
|
||||
class NibbleAdaptor : public Adaptor
|
||||
{
|
||||
public:
|
||||
|
||||
NibbleAdaptor(void *address, unsigned length);
|
||||
virtual ~NibbleAdaptor();
|
||||
|
||||
virtual void readBlock(unsigned block, void *bp);
|
||||
virtual void writeBlock(unsigned block, const void *bp);
|
||||
|
||||
|
||||
virtual void readTrackSector(TrackSector ts, void *bp);
|
||||
virtual void writeTrackSector(TrackSector ts, const void *bp);
|
||||
|
||||
static std::pair<uint8_t, uint8_t>encode44(uint8_t);
|
||||
static uint8_t decode44(uint8_t, uint8_t);
|
||||
|
||||
static uint8_t encode62(uint8_t);
|
||||
static uint8_t decode62(uint8_t);
|
||||
|
||||
|
||||
private:
|
||||
uint8_t *_address;
|
||||
unsigned _length;
|
||||
|
||||
std::vector<unsigned> _index;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,278 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Cache/ConcreteBlockCache.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
#include <Device/DiskImage.h>
|
||||
#include <Device/UniversalDiskImage.h>
|
||||
#include <Device/DiskCopy42Image.h>
|
||||
#include <Device/DavexDiskImage.h>
|
||||
#include <Device/RawDevice.h>
|
||||
|
||||
#ifdef HAVE_NUFX
|
||||
#include <Device/SDKImage.h>
|
||||
#endif
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
|
||||
unsigned BlockDevice::ImageType(MappedFile *f, unsigned defv)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "BlockDevice::ImageType"
|
||||
|
||||
|
||||
if (UniversalDiskImage::Validate(f, std::nothrow))
|
||||
return '2IMG';
|
||||
|
||||
if (DiskCopy42Image::Validate(f, std::nothrow))
|
||||
return 'DC42';
|
||||
|
||||
#ifdef HAVE_NUFX
|
||||
if (SDKImage::Validate(f, std::nothrow))
|
||||
return 'SDK_';
|
||||
#endif
|
||||
|
||||
if (ProDOSOrderDiskImage::Validate(f, std::nothrow))
|
||||
return 'PO__';
|
||||
|
||||
if (DOSOrderDiskImage::Validate(f, std::nothrow))
|
||||
return 'DO__';
|
||||
|
||||
return defv;
|
||||
|
||||
}
|
||||
|
||||
unsigned BlockDevice::ImageType(const char *type, unsigned defv)
|
||||
{
|
||||
const char *tmp;
|
||||
|
||||
|
||||
if (type == 0 || *type == 0) return defv;
|
||||
|
||||
// type could be a path, eg images/file, disk.images/file
|
||||
|
||||
// unix-specifix.
|
||||
// basename alters the input string
|
||||
tmp = std::strrchr(type, '/');
|
||||
if (tmp) type = tmp + 1;
|
||||
|
||||
// type could be a filename, in which case we check the extension.
|
||||
tmp = std::strrchr(type, '.');
|
||||
if (tmp) type = tmp + 1;
|
||||
if (*type == 0) return defv;
|
||||
|
||||
|
||||
if (::strcasecmp(type, "2mg") == 0)
|
||||
return '2IMG';
|
||||
if (::strcasecmp(type, "2img") == 0)
|
||||
return '2IMG';
|
||||
|
||||
if (::strcasecmp(type, "dc") == 0)
|
||||
return 'DC42';
|
||||
if (::strcasecmp(type, "dc42") == 0)
|
||||
return 'DC42';
|
||||
|
||||
if (::strcasecmp(type, "po") == 0)
|
||||
return 'PO__';
|
||||
if (::strcasecmp(type, "dmg") == 0)
|
||||
return 'PO__';
|
||||
|
||||
if (::strcasecmp(type, "dsk") == 0)
|
||||
return 'DO__';
|
||||
if (::strcasecmp(type, "do") == 0)
|
||||
return 'DO__';
|
||||
|
||||
if (::strcasecmp(type, "dvx") == 0)
|
||||
return 'DVX_';
|
||||
if (::strcasecmp(type, "davex") == 0)
|
||||
return 'DVX_';
|
||||
|
||||
|
||||
#ifdef HAVE_NUFX
|
||||
if (::strcasecmp(type, "sdk") == 0)
|
||||
return 'SDK_';
|
||||
if (::strcasecmp(type, "shk") == 0)
|
||||
return 'SDK_';
|
||||
#endif
|
||||
|
||||
return defv;
|
||||
}
|
||||
|
||||
BlockDevicePointer BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "BlockDevice::Open"
|
||||
|
||||
struct stat st;
|
||||
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
|
||||
if (::stat(name, &st) != 0)
|
||||
{
|
||||
throw POSIX::Exception(__METHOD__ ": stat error", errno);
|
||||
}
|
||||
|
||||
// /dev/xxx ignore the type.
|
||||
if (S_ISBLK(st.st_mode))
|
||||
return RawDevice::Open(name, flags);
|
||||
|
||||
MappedFile file(name, flags);
|
||||
|
||||
|
||||
if (!imageType)
|
||||
{
|
||||
imageType = ImageType(&file, 'PO__');
|
||||
}
|
||||
|
||||
|
||||
switch (imageType)
|
||||
{
|
||||
case '2IMG':
|
||||
return UniversalDiskImage::Open(&file);
|
||||
|
||||
case 'DC42':
|
||||
return DiskCopy42Image::Open(&file);
|
||||
|
||||
case 'DO__':
|
||||
return DOSOrderDiskImage::Open(&file);
|
||||
|
||||
case 'PO__':
|
||||
return ProDOSOrderDiskImage::Open(&file);
|
||||
|
||||
case 'DVX_':
|
||||
return DavexDiskImage::Open(&file);
|
||||
|
||||
#if HAVE_NUFX
|
||||
case 'SDK_':
|
||||
return SDKImage::Open(name);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
// throw an error?
|
||||
return BlockDevicePointer();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// return the basename, without an extension.
|
||||
static std::string filename(const std::string& src)
|
||||
{
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
|
||||
|
||||
if (src.empty()) return std::string("");
|
||||
|
||||
start = end = 0;
|
||||
|
||||
for(unsigned i = 0, l = src.length(); i < l; ++i)
|
||||
{
|
||||
char c = src[i];
|
||||
if (c == '/') start = end = i + 1;
|
||||
if (c == '.') end = i;
|
||||
}
|
||||
|
||||
if (start == src.length()) return std::string("");
|
||||
|
||||
if (start == end) return src.substr(start);
|
||||
return src.substr(start, end - start);
|
||||
}
|
||||
|
||||
|
||||
BlockDevicePointer BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
|
||||
{
|
||||
std::string xname;
|
||||
|
||||
if (!imageType) imageType = ImageType(fname, 'PO__');
|
||||
|
||||
if (vname == NULL)
|
||||
{
|
||||
xname = filename(std::string(fname));
|
||||
vname = xname.c_str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
switch(imageType)
|
||||
{
|
||||
case '2IMG':
|
||||
return UniversalDiskImage::Create(fname, blocks);
|
||||
|
||||
case 'DC42':
|
||||
return DiskCopy42Image::Create(fname, blocks, vname);
|
||||
|
||||
case 'DO__':
|
||||
return DOSOrderDiskImage::Create(fname, blocks);
|
||||
|
||||
case 'PO__':
|
||||
return ProDOSOrderDiskImage::Create(fname, blocks);
|
||||
|
||||
case 'DVX_':
|
||||
return DavexDiskImage::Create(fname, blocks, vname);
|
||||
}
|
||||
|
||||
return BlockDevicePointer();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BlockDevice::BlockDevice()
|
||||
{
|
||||
}
|
||||
BlockDevice::~BlockDevice()
|
||||
{
|
||||
}
|
||||
|
||||
void BlockDevice::zeroBlock(unsigned block)
|
||||
{
|
||||
uint8_t bp[512];
|
||||
std::memset(bp, 0, 512);
|
||||
|
||||
write(block, bp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool BlockDevice::mapped()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void BlockDevice::sync(unsigned block)
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
/*
|
||||
void BlockDevice::sync(TrackSector ts)
|
||||
{
|
||||
sync();
|
||||
}
|
||||
*/
|
||||
|
||||
BlockCachePointer BlockDevice::createBlockCache()
|
||||
{
|
||||
unsigned b = blocks();
|
||||
unsigned size = std::max(16u, b / 16);
|
||||
return ConcreteBlockCache::Create(shared_from_this(), size);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef __BLOCKDEVICE_H__
|
||||
#define __BLOCKDEVICE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <Device/Device.h>
|
||||
#include <Device/TrackSector.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/smart_pointers.h>
|
||||
|
||||
#include <File/File.h>
|
||||
|
||||
class MappedFile;
|
||||
|
||||
namespace Device {
|
||||
|
||||
class BlockDevice : public ENABLE_SHARED_FROM_THIS(BlockDevice) {
|
||||
public:
|
||||
|
||||
|
||||
// static methods.
|
||||
static unsigned ImageType(const char *type, unsigned defv = 0);
|
||||
static unsigned ImageType(MappedFile *, unsigned defv = 0);
|
||||
|
||||
|
||||
static BlockDevicePointer Open(const char *name, File::FileFlags flags, unsigned imageType = 0);
|
||||
static BlockDevicePointer Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
virtual ~BlockDevice();
|
||||
|
||||
virtual BlockCachePointer createBlockCache();
|
||||
|
||||
|
||||
virtual void read(unsigned block, void *bp) = 0;
|
||||
//virtual void read(TrackSector ts, void *bp) = 0
|
||||
|
||||
virtual void write(unsigned block, const void *bp) = 0;
|
||||
//virtual void write(TrackSector ts, const void *bp) = 0;
|
||||
|
||||
|
||||
virtual unsigned blocks() = 0;
|
||||
|
||||
virtual bool mapped();
|
||||
|
||||
virtual bool readOnly() = 0;
|
||||
|
||||
virtual void sync() = 0;
|
||||
virtual void sync(unsigned block);
|
||||
//virtual void sync(TrackSector ts);
|
||||
|
||||
|
||||
void zeroBlock(unsigned block);
|
||||
|
||||
protected:
|
||||
BlockDevice();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
|
||||
#include <Device/DavexDiskImage.h>
|
||||
#include <File/MappedFile.h>
|
||||
#include <Device/Adaptor.h>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <Endian/IOBuffer.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
|
||||
using namespace Device;
|
||||
using namespace LittleEndian;
|
||||
|
||||
|
||||
/*
|
||||
http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004
|
||||
*/
|
||||
|
||||
static const char *IdentityCheck = "\x60VSTORE [Davex]\x00";
|
||||
|
||||
|
||||
// private, validation already performed.
|
||||
DavexDiskImage::DavexDiskImage(MappedFile *file) :
|
||||
DiskImage(file)
|
||||
{
|
||||
// at this point, file is no longer valid.
|
||||
|
||||
|
||||
// 512-bytes header
|
||||
setBlocks((length() / 512) - 1);
|
||||
setAdaptor(new POAdaptor(512 + (uint8_t *)address()));
|
||||
}
|
||||
|
||||
|
||||
DavexDiskImage::~DavexDiskImage()
|
||||
{
|
||||
// scan and update usedBlocks?
|
||||
}
|
||||
|
||||
bool DavexDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DavexDiskImage::Validate"
|
||||
|
||||
size_t size = f->length();
|
||||
const void * data = f->address();
|
||||
unsigned blocks = (size / 512) - 1;
|
||||
|
||||
|
||||
if (size < 512) return false;
|
||||
if (size % 512) return false;
|
||||
|
||||
// identity.
|
||||
if (std::memcmp(data, IdentityCheck, 16))
|
||||
return false;
|
||||
|
||||
// file format.
|
||||
if (Read8(data, 0x10) != 0)
|
||||
return false;
|
||||
|
||||
// total blocks
|
||||
if (Read32(data, 33) != blocks)
|
||||
return false;
|
||||
|
||||
// file number -- must be 1
|
||||
if (Read8(data, 64) != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DavexDiskImage::Validate(MappedFile *f)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DavexDiskImage::Validate"
|
||||
|
||||
|
||||
if (!Validate(f, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockDevicePointer DavexDiskImage::Open(MappedFile *file)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DavexDiskImage::Open"
|
||||
Validate(file);
|
||||
|
||||
//return BlockDevicePointer(new DavexDiskImage(file));
|
||||
|
||||
return MAKE_SHARED(DavexDiskImage, file);
|
||||
}
|
||||
|
||||
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks)
|
||||
{
|
||||
return Create(name, blocks, "Untitled");
|
||||
}
|
||||
BlockDevicePointer DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DavexDiskImage::Create"
|
||||
|
||||
uint8_t *data;
|
||||
uint8_t tmp[512];
|
||||
IOBuffer header(tmp,512);
|
||||
|
||||
|
||||
MappedFile *file = MappedFile::Create(name, blocks * 512 + 512);
|
||||
|
||||
data = (uint8_t *)file->address();
|
||||
|
||||
header.writeBytes(IdentityCheck, 16);
|
||||
// file Format
|
||||
header.write8(0);
|
||||
//version
|
||||
header.write8(0);
|
||||
// version
|
||||
header.write8(0x10);
|
||||
|
||||
// reserved.
|
||||
header.setOffset(32, true);
|
||||
|
||||
//deviceNum
|
||||
header.write8(1);
|
||||
|
||||
// total blocks
|
||||
header.write32(blocks);
|
||||
|
||||
// unused blocks
|
||||
header.write32(0);
|
||||
|
||||
// volume Name
|
||||
if (!vname || !*vname) vname = "Untitled";
|
||||
unsigned l = std::strlen(vname);
|
||||
header.write8(std::min(l, 15u));
|
||||
header.writeBytes(vname, std::min(l, 15u));
|
||||
|
||||
// name + reserved.
|
||||
header.setOffset(64, true);
|
||||
|
||||
// file number
|
||||
header.write8(1);
|
||||
|
||||
//starting block
|
||||
header.write32(0);
|
||||
|
||||
// reserved
|
||||
header.setOffset(512, true);
|
||||
|
||||
|
||||
std::memcpy(file->address(), header.buffer(), 512);
|
||||
file->sync();
|
||||
|
||||
//return BlockDevicePointer(new DavexDiskImage(file));
|
||||
|
||||
return MAKE_SHARED(DavexDiskImage, file);
|
||||
}
|
||||
|
||||
|
||||
BlockCachePointer DavexDiskImage::createBlockCache()
|
||||
{
|
||||
// need a smart pointer, but only have this....
|
||||
return MappedBlockCache::Create(shared_from_this(), 512 + (uint8_t *)address());
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef __DAVEXDISKIMAGE_H__
|
||||
#define __DAVEXDISKIMAGE_H__
|
||||
|
||||
#include <string>
|
||||
#include <new>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Device/DiskImage.h>
|
||||
|
||||
|
||||
namespace Device {
|
||||
|
||||
// only supports 1 file; may be split over multiple files.
|
||||
|
||||
class DavexDiskImage : public DiskImage {
|
||||
public:
|
||||
|
||||
virtual ~DavexDiskImage();
|
||||
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks);
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
|
||||
static BlockDevicePointer Open(MappedFile *);
|
||||
|
||||
virtual BlockCachePointer createBlockCache();
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
private:
|
||||
|
||||
DavexDiskImage();
|
||||
|
||||
DavexDiskImage(MappedFile *);
|
||||
|
||||
|
||||
bool _changed;
|
||||
std::string _volumeName;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Device.h
|
||||
// profuse
|
||||
//
|
||||
// Created by Kelvin Sherlock on 2/19/2011.
|
||||
// Copyright 2011 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __DEVICE_DEVICE_H__
|
||||
#define __DEVICE_DEVICE_H__
|
||||
|
||||
#include <Common/smart_pointers.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class BlockDevice;
|
||||
class BlockCache;
|
||||
|
||||
typedef SHARED_PTR(BlockDevice) BlockDevicePointer;
|
||||
typedef SHARED_PTR(BlockCache) BlockCachePointer;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,240 @@
|
|||
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include <Device/DiskCopy42Image.h>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <Endian/IOBuffer.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
|
||||
|
||||
using namespace Device;
|
||||
using namespace BigEndian;
|
||||
|
||||
|
||||
|
||||
|
||||
enum {
|
||||
oDataSize = 64,
|
||||
oDataChecksum = 72,
|
||||
oPrivate = 82,
|
||||
oUserData = 84
|
||||
};
|
||||
|
||||
DiskCopy42Image::DiskCopy42Image(MappedFile *f) :
|
||||
DiskImage(f),
|
||||
_changed(false)
|
||||
{
|
||||
setAdaptor(new POAdaptor(oUserData + (uint8_t *)address()));
|
||||
setBlocks(Read32(address(), oDataSize) / 512);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DiskCopy42Image::~DiskCopy42Image()
|
||||
{
|
||||
if (_changed)
|
||||
{
|
||||
MappedFile *f = file();
|
||||
|
||||
if (f)
|
||||
{
|
||||
void *data = f->address();
|
||||
|
||||
uint32_t cs = Checksum(oUserData + (uint8_t *)data, Read32(data, oDataSize));
|
||||
|
||||
Write32(data, oDataChecksum, cs);
|
||||
f->sync();
|
||||
}
|
||||
// TODO -- checksum
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DiskCopy42Image::Checksum(void *data, size_t size)
|
||||
{
|
||||
uint32_t rv = 0;
|
||||
uint8_t *dp = (uint8_t *)data;
|
||||
|
||||
if (size & 0x01) return rv;
|
||||
|
||||
for (size_t i = 0; i < size; i += 2)
|
||||
{
|
||||
rv += dp[i] << 8;
|
||||
rv += dp[i+1];
|
||||
|
||||
rv = (rv >> 1) + (rv << 31);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
BlockDevicePointer DiskCopy42Image::Open(MappedFile *f)
|
||||
{
|
||||
Validate(f);
|
||||
//return BlockDevicePointer(new DiskCopy42Image(f));
|
||||
|
||||
return MAKE_SHARED(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;
|
||||
}
|
||||
}
|
||||
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks)
|
||||
{
|
||||
return Create(name, blocks, "Untitled");
|
||||
}
|
||||
|
||||
BlockDevicePointer DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
|
||||
{
|
||||
MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData);
|
||||
|
||||
|
||||
|
||||
uint8_t tmp[oUserData];
|
||||
IOBuffer header(tmp, oUserData);
|
||||
|
||||
// name -- 64byte pstring.
|
||||
|
||||
if (vname == NULL) vname = "Untitled";
|
||||
unsigned l = std::strlen(vname);
|
||||
header.write8(std::min(l, 63u));
|
||||
header.writeBytes(vname, std::min(l, 63u));
|
||||
|
||||
//header.resize(64);
|
||||
header.setOffset(oDataSize, true);
|
||||
|
||||
// data size -- number of bytes
|
||||
header.write32(blocks * 512);
|
||||
|
||||
// tag size
|
||||
header.write32(0);
|
||||
|
||||
// data checksum
|
||||
// if data is 0, will be 0.
|
||||
//header.push32be(Checksum(file->fileData(), blocks * 512));
|
||||
header.write32(0);
|
||||
|
||||
// tag checksum
|
||||
header.write32(0);
|
||||
|
||||
// disk format.
|
||||
/*
|
||||
* 0 = 400k
|
||||
* 1 = 800k
|
||||
* 2 = 720k
|
||||
* 3 = 1440k
|
||||
* 0xff = other
|
||||
*/
|
||||
header.write8(DiskFormat(blocks));
|
||||
|
||||
// formatbyte
|
||||
/*
|
||||
* 0x12 = 400k
|
||||
* 0x22 = >400k mac
|
||||
* 0x24 = 800k appleII
|
||||
*/
|
||||
header.write8(FormatByte(blocks));
|
||||
|
||||
// private
|
||||
header.write16(0x100);
|
||||
|
||||
std::memcpy(file->address(), header.buffer(), oUserData);
|
||||
file->sync();
|
||||
|
||||
//return BlockDevicePointer(new DiskCopy42Image(file));
|
||||
|
||||
return MAKE_SHARED(DiskCopy42Image, file);
|
||||
}
|
||||
|
||||
bool DiskCopy42Image::Validate(MappedFile *file, const std::nothrow_t &)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DiskCopy42Image::Validate"
|
||||
|
||||
|
||||
size_t bytes = 0;
|
||||
size_t size = file->length();
|
||||
const void *data = file->address();
|
||||
uint32_t checksum = 0;
|
||||
|
||||
if (size < oUserData)
|
||||
return false;
|
||||
|
||||
// name must be < 64
|
||||
if (Read8(data, 0) > 63)
|
||||
return false;
|
||||
|
||||
if (Read16(data, oPrivate) != 0x100)
|
||||
return false;
|
||||
|
||||
// bytes, not blocks.
|
||||
bytes = Read32(data, oDataSize);
|
||||
|
||||
if (bytes % 512)
|
||||
return false;
|
||||
|
||||
if (size < oUserData + bytes)
|
||||
return false;
|
||||
|
||||
// todo -- checksum.
|
||||
checksum = Read32(data, oDataChecksum);
|
||||
|
||||
uint32_t cs = Checksum(oUserData + (uint8_t *)data, bytes);
|
||||
|
||||
if (cs != checksum)
|
||||
{
|
||||
fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DiskCopy42Image::Validate(MappedFile *file)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DiskCopy42Image::Validate"
|
||||
|
||||
|
||||
if (!Validate(file, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DiskCopy42Image::write(unsigned block, const void *bp)
|
||||
{
|
||||
DiskImage::write(block, bp);
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
|
||||
BlockCachePointer DiskCopy42Image::createBlockCache()
|
||||
{
|
||||
// if not readonly, mark changed so crc will be updated at close.
|
||||
|
||||
if (!readOnly()) _changed = true;
|
||||
|
||||
return MappedBlockCache::Create(shared_from_this(), address());
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef __DISKCOPY42IMAGE_H__
|
||||
#define __DISKCOPY42IMAGE_H__
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Device/DiskImage.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class DiskCopy42Image : public DiskImage {
|
||||
public:
|
||||
virtual ~DiskCopy42Image();
|
||||
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks);
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks, const char *vname);
|
||||
|
||||
static BlockDevicePointer Open(MappedFile *);
|
||||
|
||||
static uint32_t Checksum(void *data, size_t size);
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
|
||||
|
||||
virtual void write(unsigned block, const void *bp);
|
||||
|
||||
|
||||
virtual BlockCachePointer createBlockCache();
|
||||
|
||||
private:
|
||||
|
||||
DiskCopy42Image();
|
||||
|
||||
DiskCopy42Image(MappedFile *);
|
||||
bool _changed;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,226 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <Device/DiskImage.h>
|
||||
|
||||
|
||||
|
||||
#include <File/MappedFile.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
DiskImage::DiskImage(const char *name, bool readOnly)
|
||||
{
|
||||
File fd(name, readOnly ? O_RDONLY : O_RDWR);
|
||||
MappedFile mf(fd, readOnly);
|
||||
_file.adopt(mf);
|
||||
|
||||
_blocks = 0;
|
||||
_readOnly = readOnly;
|
||||
_adaptor = NULL;
|
||||
}
|
||||
*/
|
||||
|
||||
DiskImage::DiskImage(MappedFile *file)
|
||||
{
|
||||
_file.adopt(*file);
|
||||
|
||||
_blocks = 0;
|
||||
_readOnly = _file.readOnly();
|
||||
_adaptor = NULL;
|
||||
}
|
||||
|
||||
DiskImage::~DiskImage()
|
||||
{
|
||||
delete _adaptor;
|
||||
}
|
||||
|
||||
bool DiskImage::readOnly()
|
||||
{
|
||||
return _readOnly;
|
||||
}
|
||||
|
||||
unsigned DiskImage::blocks()
|
||||
{
|
||||
return _blocks;
|
||||
}
|
||||
|
||||
|
||||
void DiskImage::setAdaptor(Adaptor *adaptor)
|
||||
{
|
||||
delete _adaptor;
|
||||
_adaptor = adaptor;
|
||||
}
|
||||
|
||||
|
||||
void DiskImage::read(unsigned block, void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DiskImage::read"
|
||||
|
||||
if (block >= _blocks)
|
||||
throw ::Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
_adaptor->readBlock(block, bp);
|
||||
}
|
||||
|
||||
void DiskImage::write(unsigned block, const void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DiskImage::write"
|
||||
|
||||
if (block >= _blocks)
|
||||
throw ::Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
_adaptor->writeBlock(block, bp);
|
||||
}
|
||||
|
||||
void DiskImage::sync()
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DiskImage::sync"
|
||||
|
||||
if (_file.isValid()) return _file.sync();
|
||||
|
||||
throw ::Exception(__METHOD__ ": File not set.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
|
||||
DiskImage(file)
|
||||
{
|
||||
// at this point, file is no longer valid.
|
||||
|
||||
setBlocks(length() / 512);
|
||||
setAdaptor(new POAdaptor(address()));
|
||||
}
|
||||
|
||||
BlockDevicePointer ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
|
||||
{
|
||||
MappedFile *file = MappedFile::Create(name, blocks * 512);
|
||||
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
|
||||
return MAKE_SHARED(ProDOSOrderDiskImage, file);
|
||||
}
|
||||
|
||||
BlockDevicePointer ProDOSOrderDiskImage::Open(MappedFile *file)
|
||||
{
|
||||
Validate(file);
|
||||
//return BlockDevicePointer(new ProDOSOrderDiskImage(file));
|
||||
return MAKE_SHARED(ProDOSOrderDiskImage, file);
|
||||
}
|
||||
|
||||
|
||||
bool ProDOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
|
||||
|
||||
|
||||
size_t size = f->length();
|
||||
|
||||
if (size % 512)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool ProDOSOrderDiskImage::Validate(MappedFile *f)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
|
||||
|
||||
if (!f || !f->isValid()) throw ::Exception(__METHOD__ ": File not set.");
|
||||
|
||||
if (!Validate(f, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockCachePointer ProDOSOrderDiskImage::createBlockCache()
|
||||
{
|
||||
return MappedBlockCache::Create(shared_from_this(), address());
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark DOS Order Disk Image
|
||||
|
||||
|
||||
/*
|
||||
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
|
||||
DiskImage(name, readOnly)
|
||||
{
|
||||
Validate(file());
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
|
||||
DiskImage(file)
|
||||
{
|
||||
// at this point, file is no longer valid.
|
||||
|
||||
setBlocks(length() / 512);
|
||||
setAdaptor(new DOAdaptor(address()));
|
||||
}
|
||||
|
||||
|
||||
BlockDevicePointer DOSOrderDiskImage::Create(const char *name, size_t blocks)
|
||||
{
|
||||
MappedFile *file = MappedFile::Create(name, blocks * 512);
|
||||
//return BlockDevicePointer(new DOSOrderDiskImage(file));
|
||||
return MAKE_SHARED(DOSOrderDiskImage, file);
|
||||
}
|
||||
|
||||
BlockDevicePointer DOSOrderDiskImage::Open(MappedFile *file)
|
||||
{
|
||||
Validate(file);
|
||||
//return BlockDevicePointer(new DOSOrderDiskImage(file));
|
||||
return MAKE_SHARED(DOSOrderDiskImage, file);
|
||||
|
||||
}
|
||||
|
||||
bool DOSOrderDiskImage::Validate(MappedFile *f, const std::nothrow_t &)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DOSOrderDiskImage::Validate"
|
||||
|
||||
size_t size = f->length();
|
||||
|
||||
if (size % 512)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool DOSOrderDiskImage::Validate(MappedFile *f)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "DOSOrderDiskImage::Validate"
|
||||
|
||||
if (!f || !f->isValid()) throw ::Exception(__METHOD__ ": File not set.");
|
||||
|
||||
|
||||
if (!Validate(f, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef __DISKIMAGE_H__
|
||||
#define __DISKIMAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <Device/Adaptor.h>
|
||||
|
||||
#include <File/MappedFile.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
|
||||
class DiskImage : public BlockDevice {
|
||||
public:
|
||||
|
||||
|
||||
virtual ~DiskImage();
|
||||
|
||||
virtual void read(unsigned block, void *bp);
|
||||
virtual void write(unsigned block, const void *bp);
|
||||
virtual void sync();
|
||||
|
||||
virtual bool readOnly();
|
||||
virtual unsigned blocks();
|
||||
|
||||
protected:
|
||||
|
||||
DiskImage();
|
||||
|
||||
DiskImage(MappedFile *file = 0);
|
||||
|
||||
void setBlocks(unsigned blocks) { _blocks = blocks; }
|
||||
|
||||
void setAdaptor(Adaptor *);
|
||||
|
||||
void *address() const { return _file.address(); }
|
||||
size_t length() const { return _file.length(); }
|
||||
|
||||
MappedFile *file() { return &_file; }
|
||||
|
||||
private:
|
||||
|
||||
MappedFile _file;
|
||||
Adaptor *_adaptor;
|
||||
|
||||
bool _readOnly;
|
||||
unsigned _blocks;
|
||||
};
|
||||
|
||||
|
||||
class ProDOSOrderDiskImage : public DiskImage {
|
||||
public:
|
||||
|
||||
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks);
|
||||
static BlockDevicePointer Open(MappedFile *);
|
||||
|
||||
|
||||
virtual BlockCachePointer createBlockCache();
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
private:
|
||||
ProDOSOrderDiskImage();
|
||||
|
||||
ProDOSOrderDiskImage(MappedFile *);
|
||||
};
|
||||
|
||||
class DOSOrderDiskImage : public DiskImage {
|
||||
public:
|
||||
|
||||
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks);
|
||||
static BlockDevicePointer Open(MappedFile *);
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
private:
|
||||
DOSOrderDiskImage();
|
||||
|
||||
DOSOrderDiskImage(MappedFile *);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,320 @@
|
|||
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#ifdef __SUN__
|
||||
#include <sys/dkio.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __FREEBSD__
|
||||
#include <sys/disk.h>
|
||||
#endif
|
||||
|
||||
#ifdef __minix
|
||||
#include <minix/partition.h>
|
||||
#endif
|
||||
|
||||
#include <Device/RawDevice.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
using namespace Device;
|
||||
|
||||
|
||||
#ifdef __SUN__
|
||||
void RawDevice::devSize(int fd)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::devSize"
|
||||
|
||||
struct dk_minfo minfo;
|
||||
|
||||
if (::ioctl(fd, DKIOCGMEDIAINFO, &minfo) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
|
||||
|
||||
_size = minfo.dki_lbsize * minfo.dki_capacity;
|
||||
_blockSize = 512; // not really, but whatever.
|
||||
_blocks = _size / 512;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
void RawDevice::devSize(int fd)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::devSize"
|
||||
|
||||
uint32_t blockSize; // 32 bit
|
||||
uint64_t blockCount; // 64 bit
|
||||
|
||||
if (::ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
|
||||
|
||||
|
||||
if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &blockCount) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine block count.", errno);
|
||||
|
||||
_blockSize = blockSize;
|
||||
_size = _blockSize * blockCount;
|
||||
_blocks = _size / 512;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
void RawDevice::devSize(int fd)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::devSize"
|
||||
|
||||
int blocks;
|
||||
|
||||
if (::ioctl(fd, BLKGETSIZE, &blocks) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
|
||||
|
||||
_size = 512 * blocks;
|
||||
_blockSize = 512; //
|
||||
_blocks = blocks;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// TODO -- FreeBSD/NetBSD/OpenBSD
|
||||
|
||||
#ifdef __FREEBSD__
|
||||
|
||||
void RawDevice::devSize(int fd)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::devSize"
|
||||
|
||||
unsigned blockSize;
|
||||
off_t mediaSize;
|
||||
|
||||
if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine block size.", errno);
|
||||
|
||||
if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine media size.", errno);
|
||||
|
||||
_blockSize = blockSize;
|
||||
_size = mediaSize;
|
||||
_blocks = mediaSize / 512;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __minix
|
||||
|
||||
void RawDevice::devSize(int fd)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::devSize"
|
||||
|
||||
struct partition entry;
|
||||
|
||||
|
||||
if (::ioctl(fd, DIOCGETP, &entry) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to determine device size.", errno);
|
||||
|
||||
_size = entry.size
|
||||
_blockSize = 512; // not really but whatever.
|
||||
_blocks = _size / 512;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
RawDevice::RawDevice(const char *name, File::FileFlags flags) :
|
||||
_file(name, flags)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::RawDevice"
|
||||
|
||||
|
||||
if (!_file.isValid())
|
||||
{
|
||||
throw ::Exception(__METHOD__ ": Invalid file handle.");
|
||||
}
|
||||
|
||||
_readOnly = flags == File::ReadOnly;
|
||||
_size = 0;
|
||||
_blocks = 0;
|
||||
_blockSize = 0;
|
||||
|
||||
|
||||
devSize(_file.fd());
|
||||
}
|
||||
|
||||
RawDevice::RawDevice(File& file, File::FileFlags flags) :
|
||||
_file(file)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::RawDevice"
|
||||
|
||||
|
||||
if (!_file.isValid())
|
||||
{
|
||||
throw ::Exception(__METHOD__ ": Invalid file handle.");
|
||||
}
|
||||
|
||||
_readOnly = flags == File::ReadOnly;
|
||||
_size = 0;
|
||||
_blocks = 0;
|
||||
_blockSize = 0;
|
||||
|
||||
|
||||
devSize(_file.fd());
|
||||
}
|
||||
|
||||
|
||||
RawDevice::~RawDevice()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BlockDevicePointer RawDevice::Open(const char *name, File::FileFlags flags)
|
||||
{
|
||||
//return BlockDevicePointer(new RawDevice(name, flags));
|
||||
return MAKE_SHARED(RawDevice, name, flags);
|
||||
}
|
||||
|
||||
|
||||
void RawDevice::read(unsigned block, void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::read"
|
||||
|
||||
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
|
||||
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
|
||||
|
||||
// sun -- use pread
|
||||
// apple - read full native block(s) ?
|
||||
|
||||
off_t offset = block * 512;
|
||||
ssize_t ok = ::pread(_file.fd(), bp, 512, offset);
|
||||
|
||||
// TODO -- EINTR?
|
||||
if (ok != 512)
|
||||
throw ok < 0
|
||||
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
|
||||
: ::Exception(__METHOD__ ": Error reading block.");
|
||||
}
|
||||
|
||||
|
||||
void RawDevice::read(TrackSector ts, void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::read"
|
||||
|
||||
unsigned block = ts.track * 8 + ts.sector / 2;
|
||||
if (block >= _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
|
||||
if (bp == 0) throw ::Exception(__METHOD__ ": Invalid address.");
|
||||
|
||||
// sun -- use pread
|
||||
// apple - read full native block(s) ?
|
||||
|
||||
off_t offset = (ts.track * 16 + ts.sector) * 256;
|
||||
ssize_t ok = ::pread(_file.fd(), bp, 256, offset);
|
||||
|
||||
// TODO -- EINTR?
|
||||
if (ok != 256)
|
||||
throw ok < 0
|
||||
? POSIX::Exception(__METHOD__ ": Error reading block.", errno)
|
||||
: ::Exception(__METHOD__ ": Error reading block.");
|
||||
}
|
||||
|
||||
|
||||
void RawDevice::write(unsigned block, const void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::write"
|
||||
|
||||
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
|
||||
|
||||
if (_readOnly)
|
||||
throw ::Exception(__METHOD__ ": File is readonly.");
|
||||
|
||||
|
||||
off_t offset = block * 512;
|
||||
ssize_t ok = ::pwrite(_file.fd(), bp, 512, offset);
|
||||
|
||||
if (ok != 512)
|
||||
throw ok < 0
|
||||
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
|
||||
: ::Exception(__METHOD__ ": Error writing block.");
|
||||
}
|
||||
|
||||
|
||||
void RawDevice::write(TrackSector ts, const void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::write"
|
||||
|
||||
unsigned block = ts.track * 8 + ts.sector / 2;
|
||||
if (block > _blocks) throw ::Exception(__METHOD__ ": Invalid block number.");
|
||||
|
||||
if (_readOnly)
|
||||
throw ::Exception(__METHOD__ ": File is readonly.");
|
||||
|
||||
|
||||
off_t offset = (ts.track * 16 + ts.sector) * 256;
|
||||
ssize_t ok = ::pwrite(_file.fd(), bp, 256, offset);
|
||||
|
||||
if (ok != 256)
|
||||
throw ok < 0
|
||||
? POSIX::Exception(__METHOD__ ": Error writing block.", errno)
|
||||
: ::Exception(__METHOD__ ": Error writing block.");
|
||||
}
|
||||
|
||||
|
||||
bool RawDevice::readOnly()
|
||||
{
|
||||
return _readOnly;
|
||||
}
|
||||
|
||||
bool RawDevice::mapped()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
unsigned RawDevice::blocks()
|
||||
{
|
||||
return _blocks;
|
||||
}
|
||||
|
||||
|
||||
void RawDevice::sync()
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "RawDevice::sync"
|
||||
|
||||
if (_readOnly) return;
|
||||
|
||||
if (::fsync(_file.fd()) < 0)
|
||||
throw POSIX::Exception(__METHOD__ ": fsync error.", errno);
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef __RAWDEVICE_H__
|
||||
#define __RAWDEVICE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <File/File.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
// /dev/xxx
|
||||
|
||||
|
||||
class RawDevice : public BlockDevice {
|
||||
public:
|
||||
|
||||
|
||||
|
||||
static BlockDevicePointer Open(const char *name, File::FileFlags flags);
|
||||
|
||||
|
||||
virtual ~RawDevice();
|
||||
|
||||
virtual void read(unsigned block, void *bp);
|
||||
virtual void read(TrackSector ts, void *bp);
|
||||
|
||||
virtual void write(unsigned block, const void *bp);
|
||||
virtual void write(TrackSector ts, const void *bp);
|
||||
|
||||
virtual bool readOnly();
|
||||
virtual bool mapped();
|
||||
virtual void sync();
|
||||
|
||||
virtual unsigned blocks();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
RawDevice(const char *name, File::FileFlags flags);
|
||||
|
||||
RawDevice(File& file, File::FileFlags flags);
|
||||
|
||||
void devSize(int fd);
|
||||
|
||||
File _file;
|
||||
bool _readOnly;
|
||||
|
||||
uint64_t _size; // size of device in bytes.
|
||||
unsigned _blocks; // # of 512k blocks i.e. _size / 512
|
||||
|
||||
unsigned _blockSize; // native block size.
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,220 @@
|
|||
//
|
||||
// SDKImage.cpp
|
||||
// profuse
|
||||
//
|
||||
// Created by Kelvin Sherlock on 3/6/2011.
|
||||
// Copyright 2011 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#include "SDKImage.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <NufxLib.h>
|
||||
|
||||
|
||||
#include <File/File.h>
|
||||
#include <File/MappedFile.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <NuFX/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
struct record_thread
|
||||
{
|
||||
NuRecordIdx record_index;
|
||||
NuThreadIdx thread_index;
|
||||
};
|
||||
|
||||
|
||||
static record_thread FindDiskImageThread(NuArchive *archive)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "SDKImage::FindThread"
|
||||
|
||||
record_thread rt;
|
||||
NuError e;
|
||||
NuAttr recordCount;
|
||||
|
||||
e = NuGetAttr(archive, kNuAttrNumRecords, &recordCount);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuGetAttr", e);
|
||||
}
|
||||
|
||||
for (unsigned position = 0; position < recordCount; ++position)
|
||||
{
|
||||
NuRecordIdx rIndex;
|
||||
const NuRecord *record;
|
||||
|
||||
e = NuGetRecordIdxByPosition(archive, position, &rIndex);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuGetRecordIdxByPosition", e);
|
||||
}
|
||||
|
||||
e = NuGetRecord(archive, rIndex, &record);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuGetRecord", e);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < NuRecordGetNumThreads(record); ++i)
|
||||
{
|
||||
const NuThread *thread = NuGetThread(record, i);
|
||||
|
||||
if (thread && NuGetThreadID(thread) == kNuThreadIDDiskImage)
|
||||
{
|
||||
rt.thread_index = thread->threadIdx;
|
||||
rt.record_index = record->recordIdx;
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw ::Exception(__METHOD__ ": not a disk image");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* helper function to extract SDK image to /tmp and return a
|
||||
* ProDOSDiskImage of the /tmp file.
|
||||
*
|
||||
*/
|
||||
BlockDevicePointer SDKImage::Open(const char *name)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "SDKImage::Open"
|
||||
|
||||
|
||||
char tmp[] = "/tmp/pfuse.XXXXXXXX";
|
||||
|
||||
int fd = -1;
|
||||
FILE *fp = NULL;
|
||||
NuArchive *archive = NULL;
|
||||
//const NuThread *thread = NULL;
|
||||
//const NuRecord *record = NULL;
|
||||
NuDataSink *sink = NULL;
|
||||
//NuRecordIdx rIndex;
|
||||
//NuThreadIdx tIndex;
|
||||
|
||||
NuError e;
|
||||
|
||||
|
||||
record_thread rt = {0, 0};
|
||||
|
||||
try {
|
||||
|
||||
|
||||
e = NuOpenRO(name, &archive);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuOpenRO", e);
|
||||
}
|
||||
|
||||
rt = FindDiskImageThread(archive);
|
||||
|
||||
fd = mkstemp(tmp);
|
||||
if (fd < 0)
|
||||
{
|
||||
throw POSIX::Exception(__METHOD__ ": mkstemp", errno);
|
||||
}
|
||||
|
||||
fp = fdopen(fd, "w");
|
||||
if (!fp)
|
||||
{
|
||||
::close(fd);
|
||||
throw POSIX::Exception(__METHOD__ ": fdopen", errno);
|
||||
}
|
||||
|
||||
e = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &sink);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuCreateDataSinkForFP", e);
|
||||
}
|
||||
|
||||
|
||||
e = NuExtractThread(archive, rt.thread_index, sink);
|
||||
if (e)
|
||||
{
|
||||
throw NuFX::Exception(__METHOD__ ": NuExtractThread", e);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Extracted disk image to %s\n", tmp);
|
||||
|
||||
fclose(fp);
|
||||
NuClose(archive);
|
||||
NuFreeDataSink(sink);
|
||||
fp = NULL;
|
||||
archive = NULL;
|
||||
sink = NULL;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
if (fp) fclose(fp);
|
||||
if (archive) NuClose(archive);
|
||||
if (sink) NuFreeDataSink(sink);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
// todo -- maybe SDKImage should extend ProDOSOrderDiskImage, have destructor
|
||||
// that unklinks the temp file.
|
||||
|
||||
MappedFile file(tmp, File::ReadOnly);
|
||||
|
||||
return ProDOSOrderDiskImage::Open(&file);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool SDKImage::Validate(MappedFile * f, const std::nothrow_t &)
|
||||
{
|
||||
|
||||
// NuFile, alternating ASCII.
|
||||
static const char IdentityCheck[6] = { 0x4E, 0xF5, 0x46, 0xE9, 0x6C, 0xE5 };
|
||||
static const char BXYIdentityCheck[3] = { 0x0A, 0x47, 0x4C };
|
||||
|
||||
uint8_t *address = (uint8_t *)f->address();
|
||||
size_t length = f->length();
|
||||
|
||||
// check for a BXY header
|
||||
if (length >= 128
|
||||
&& std::memcmp(address, BXYIdentityCheck, sizeof(BXYIdentityCheck)) == 0)
|
||||
{
|
||||
length -= 128;
|
||||
address += 128;
|
||||
}
|
||||
|
||||
|
||||
if (length > sizeof(IdentityCheck)
|
||||
&& std::memcmp(address, IdentityCheck, sizeof(IdentityCheck)) == 0)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool SDKImage::Validate(MappedFile * f)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "SDKImage::Validate"
|
||||
|
||||
if (!Validate(f, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// SDKImage.h
|
||||
// profuse
|
||||
//
|
||||
// Created by Kelvin Sherlock on 3/6/2011.
|
||||
// Copyright 2011 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Device/DiskImage.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class SDKImage : public DiskImage
|
||||
{
|
||||
public:
|
||||
|
||||
static BlockDevicePointer Open(const char *name);
|
||||
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
private:
|
||||
SDKImage();
|
||||
SDKImage(const SDKImage &);
|
||||
~SDKImage();
|
||||
SDKImage & operator=(const SDKImage &);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef __TRACKSECTOR_H__
|
||||
#define __TRACKSECTOR_H__
|
||||
|
||||
namespace Device {
|
||||
|
||||
struct TrackSector {
|
||||
TrackSector(unsigned, unsigned);
|
||||
unsigned track;
|
||||
unsigned sector;
|
||||
};
|
||||
|
||||
inline TrackSector::TrackSector(unsigned t, unsigned s)
|
||||
{
|
||||
track = t;
|
||||
sector = s;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
#include <Device/UniversalDiskImage.h>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <Endian/IOBuffer.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
#include <Cache/ConcreteBlockCache.h>
|
||||
|
||||
using namespace Device;
|
||||
using namespace LittleEndian;
|
||||
|
||||
|
||||
|
||||
UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
|
||||
DiskImage(file)
|
||||
{
|
||||
|
||||
// at this point, file is no longer valid.
|
||||
|
||||
uint8_t * data = (uint8_t *)address();
|
||||
|
||||
|
||||
|
||||
|
||||
_format = Read32(data, 0x0c);
|
||||
_flags = Read32(data, 0x10);
|
||||
_blocks = Read32(data, 0x14);
|
||||
|
||||
_dataOffset = Read32(data, 0x18);
|
||||
_dataLength = Read32(data, 0x1c);
|
||||
|
||||
|
||||
setBlocks(_blocks);
|
||||
// TODO -- DO, Nibble support.
|
||||
setAdaptor(new POAdaptor(_dataOffset + data));
|
||||
}
|
||||
|
||||
BlockDevicePointer UniversalDiskImage::Create(const char *name, size_t blocks)
|
||||
{
|
||||
// 64-byte header.
|
||||
MappedFile *file = MappedFile::Create(name, blocks * 512 + 64);
|
||||
|
||||
uint8_t tmp[64];
|
||||
|
||||
IOBuffer header(tmp, 64);
|
||||
|
||||
|
||||
// magic + creator
|
||||
header.writeBytes("2IMGPRFS", 8);
|
||||
|
||||
// header size.
|
||||
header.write16(64);
|
||||
|
||||
// version
|
||||
header.write16(1);
|
||||
|
||||
//image format -- ProDOS order
|
||||
header.write32(1);
|
||||
|
||||
// flags
|
||||
header.write32(0);
|
||||
|
||||
// # blocks. s/b 0 unless prodos-order
|
||||
header.write32(blocks);
|
||||
|
||||
// offset to disk data
|
||||
header.write32(64);
|
||||
|
||||
// data length
|
||||
header.write32(512 * blocks);
|
||||
|
||||
// comment offset, creator, reserved -- 0.
|
||||
header.setOffset(64, true);
|
||||
|
||||
std::memcpy(file->address(), header.buffer(), 64);
|
||||
|
||||
|
||||
//return BlockDevicePointer(new UniversalDiskImage(file));
|
||||
|
||||
return MAKE_SHARED(UniversalDiskImage, file);
|
||||
}
|
||||
|
||||
BlockDevicePointer UniversalDiskImage::Open(MappedFile *file)
|
||||
{
|
||||
Validate(file);
|
||||
|
||||
//return BlockDevicePointer(new UniversalDiskImage(file));
|
||||
return MAKE_SHARED(UniversalDiskImage, file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TODO -- support dos-order & nibblized
|
||||
* TODO -- honor read-only flag.
|
||||
*
|
||||
*/
|
||||
|
||||
bool UniversalDiskImage::Validate(MappedFile *file, const std::nothrow_t &)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "UniversalDiskImage::Validate"
|
||||
|
||||
const void *data = file->address();
|
||||
size_t size = file->length();
|
||||
|
||||
unsigned blocks = 0;
|
||||
unsigned offset = 0;
|
||||
unsigned fileSize = 0;
|
||||
|
||||
|
||||
if (size < 64) return false;
|
||||
|
||||
if (std::memcmp(data, "2IMG", 4)) return false;
|
||||
|
||||
// only prodos supported, for now...
|
||||
// TODO -- Dos Order, Nibble support.
|
||||
if (Read32(data, 0x0c) != 1) return false;
|
||||
|
||||
blocks = Read32(data, 0x14);
|
||||
offset = Read32(data, 0x18);
|
||||
|
||||
// file size == blocks * 512
|
||||
// file size blank in some cases.
|
||||
//if (Read32(data, 0x1c) != blocks * 512) return false;
|
||||
fileSize = Read32(data, 0x1c);
|
||||
if (fileSize != 0 && fileSize != blocks * 512) return false;
|
||||
|
||||
if (offset + blocks * 512 > size) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UniversalDiskImage::Validate(MappedFile *file)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "UniversalDiskImage::Validate"
|
||||
|
||||
if (!Validate(file, std::nothrow))
|
||||
throw ::Exception(__METHOD__ ": Invalid file format.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool UniversalDiskImage::readOnly()
|
||||
{
|
||||
return (_flags & 0x8000000) || DiskImage::readOnly();
|
||||
}
|
||||
|
||||
|
||||
BlockCachePointer UniversalDiskImage::createBlockCache()
|
||||
{
|
||||
if (_format == 1)
|
||||
{
|
||||
return MappedBlockCache::Create(shared_from_this(), _dataOffset + (uint8_t *)address());
|
||||
}
|
||||
|
||||
return DiskImage::createBlockCache();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef __UNIVERSALDISKIMAGE_H__
|
||||
#define __UNIVERSALDISKIMAGE_H__
|
||||
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Device/DiskImage.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class UniversalDiskImage : public DiskImage {
|
||||
public:
|
||||
|
||||
|
||||
|
||||
static BlockDevicePointer Create(const char *name, size_t blocks);
|
||||
static BlockDevicePointer Open(MappedFile *);
|
||||
|
||||
virtual bool readOnly();
|
||||
|
||||
virtual BlockCachePointer createBlockCache();
|
||||
|
||||
|
||||
static bool Validate(MappedFile *, const std::nothrow_t &);
|
||||
static bool Validate(MappedFile *);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
UniversalDiskImage();
|
||||
|
||||
UniversalDiskImage(MappedFile *);
|
||||
|
||||
uint32_t _format;
|
||||
uint32_t _flags;
|
||||
uint32_t _blocks;
|
||||
uint32_t _dataOffset;
|
||||
uint32_t _dataLength;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "Entry.h"
|
||||
#include "Buffer.h"
|
||||
#include "Endian.h"
|
||||
#include "BlockDevice.h"
|
||||
#include "Exception.h"
|
||||
|
||||
|
||||
using namespace ProFUSE;
|
||||
using namespace LittleEndian;
|
||||
|
||||
|
||||
|
||||
Directory::Directory(unsigned type, const char *name) :
|
||||
Entry(type, name)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "Directory::Directory"
|
||||
|
||||
|
||||
_access = 0xe3;
|
||||
_entryLength = 0x27;
|
||||
_entriesPerBlock = 13;
|
||||
_fileCount = 0;
|
||||
_version = 5; // ProDOS FST uses 5, ProDOS uses 0.
|
||||
_minVersion = 0;
|
||||
}
|
||||
|
||||
Directory::Directory(const void *bp) :
|
||||
Entry(4 + (const uint8_t *)bp),
|
||||
_creation(0, 0)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "Directory::Directory"
|
||||
|
||||
// input is a block pointer. To simplify,
|
||||
// create a new pointer past the 4-byte linked list part.
|
||||
const void *dp = 4 + (const uint8_t *)bp;
|
||||
|
||||
_creation = DateTime(Read16(dp, 0x18), Read16(dp, 0x1a));
|
||||
|
||||
_version = Read8(dp, 0x1c);
|
||||
_minVersion = Read8(dp, 0x1d);
|
||||
|
||||
_access = Read8(dp, 0x1e);
|
||||
_entryLength = Read8(dp, 0x1f);
|
||||
_entriesPerBlock = Read8(dp, 0x20);
|
||||
|
||||
_fileCount = Read16(dp, 0x21);
|
||||
|
||||
// parse child file entries ... requires ability to read other blocks.
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
{
|
||||
}
|
||||
|
||||
void Directory::setAccess(unsigned access)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "Directory::setAccess"
|
||||
|
||||
|
||||
_access = access;
|
||||
|
||||
// todo -- mark dirty? update block?
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Directory::loadChildren(BlockDevice *device, unsigned block)
|
||||
{
|
||||
uint8_t buffer[512];
|
||||
unsigned next;
|
||||
bool first = true;
|
||||
unsigned offset;
|
||||
|
||||
// set of already-visited blocks?
|
||||
|
||||
while(block)
|
||||
{
|
||||
device->read(block, buffer);
|
||||
|
||||
next = Read16(buffer, 2);
|
||||
|
||||
_entryBlocks.push_back(block);
|
||||
|
||||
offset = 4;
|
||||
if (!first)
|
||||
{
|
||||
// storage type 0 is deleted, don't load...
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
first = false;
|
||||
block = next;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* DiskCopy42.cpp
|
||||
* profuse
|
||||
*
|
||||
* Created by Kelvin Sherlock on 1/4/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "DiskCopy42.h"
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
uint32_t DiskCopy42::CheckSum(uint8_t *buffer, unsigned length)
|
||||
{
|
||||
uint32_t checksum = 0;
|
||||
if (length & 0x01) return -1;
|
||||
|
||||
/*
|
||||
* checksum starts at 0
|
||||
* foreach big-endian 16-bit word w:
|
||||
* checksum += w
|
||||
* checksum = checksum rotate right 1 (bit 0 --> bit 31)
|
||||
*/
|
||||
|
||||
for(unsigned i = 0; i < length; i += 2)
|
||||
{
|
||||
checksum += (buffer[i] << 8);
|
||||
checksum += buffer[i + 1];
|
||||
checksum = (checksum >> 1) | (checksum << 31);
|
||||
//if (checksum & 0x01) checksum = (checksum >> 1) | 0x80000000;
|
||||
//else checksum >>= 1;
|
||||
}
|
||||
return checksum;
|
||||
|
||||
}
|
||||
|
||||
bool DiskCopy42::Load(const uint8_t *buffer)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
i = buffer[0];
|
||||
if (i >= 64) return false;
|
||||
|
||||
memcpy(disk_name, &buffer[1], i);
|
||||
disk_name[i] = 0;
|
||||
|
||||
|
||||
data_size = load32_be(&buffer[64]);
|
||||
tag_size = load32_be(&buffer[68]);
|
||||
|
||||
data_checksum = load32_be(&buffer[72]);
|
||||
tag_checksum = load32_be(&buffer[76]);
|
||||
|
||||
disk_format = buffer[80];
|
||||
format_byte = buffer[81];
|
||||
private_word = load16_be(&buffer[82]);
|
||||
|
||||
if (private_word != 0x100) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
31
DiskCopy42.h
31
DiskCopy42.h
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* DiskCopy42.h
|
||||
* profuse
|
||||
*
|
||||
* Created by Kelvin Sherlock on 1/4/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DISKCOPY42__
|
||||
#define __DISKCOPY42__
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct DiskCopy42
|
||||
{
|
||||
bool Load(const uint8_t *buffer);
|
||||
static uint32_t CheckSum(uint8_t *buffer, unsigned length);
|
||||
|
||||
char disk_name[64];
|
||||
uint32_t data_size;
|
||||
uint32_t tag_size;
|
||||
uint32_t data_checksum;
|
||||
uint32_t tag_checksum;
|
||||
unsigned disk_format;
|
||||
unsigned format_byte;
|
||||
unsigned private_word;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
#include <Endian/Endian.h>
|
||||
|
||||
namespace LittleEndian {
|
||||
|
||||
|
||||
uint16_t Read16(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return p[0] | (p[1] << 8);
|
||||
}
|
||||
|
||||
uint32_t Read24(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0]) | (p[1] << 8) | (p[2] << 16);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Read32(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0]) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Write16(void *vp, uint16_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x) & 0xff;
|
||||
p[1] = (x >> 8) & 0xff;
|
||||
}
|
||||
|
||||
void Write24(void *vp, uint32_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x) & 0xff;
|
||||
p[1] = (x >> 8) & 0xff;
|
||||
p[2] = (x >> 16) & 0xff;
|
||||
}
|
||||
|
||||
void Write32(void *vp, uint32_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x) & 0xff;
|
||||
p[1] = (x >> 8) & 0xff;
|
||||
p[2] = (x >> 16) & 0xff;
|
||||
p[3] = (x >> 24) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
namespace BigEndian {
|
||||
|
||||
|
||||
uint16_t Read16(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0] << 8) | (p[1]);
|
||||
}
|
||||
|
||||
uint32_t Read24(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0] << 16) | (p[1] << 8) | (p[2]);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Read32(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
|
||||
}
|
||||
|
||||
|
||||
void Write16(void *vp, uint16_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x >> 8) & 0xff;
|
||||
p[1] = (x) & 0xff;
|
||||
}
|
||||
|
||||
void Write24(void *vp, uint32_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x >> 16) & 0xff;
|
||||
p[1] = (x >> 8) & 0xff;
|
||||
p[2] = (x) & 0xff;
|
||||
}
|
||||
|
||||
void Write32(void *vp, uint32_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = (x >> 24) & 0xff;
|
||||
p[1] = (x >> 16) & 0xff;
|
||||
p[2] = (x >> 8) & 0xff;
|
||||
p[3] = (x) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
#ifndef __ENDIAN_H__
|
||||
#define __ENDIAN_H__
|
||||
|
||||
// utlities to read/write bytes.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace LittleEndian {
|
||||
|
||||
|
||||
inline uint8_t Read8(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return (p[0]);
|
||||
}
|
||||
|
||||
uint16_t Read16(const void *vp);
|
||||
|
||||
uint32_t Read24(const void *vp);
|
||||
|
||||
uint32_t Read32(const void *vp);
|
||||
|
||||
|
||||
inline uint8_t Read8(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read8(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint16_t Read16(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read16(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint32_t Read24(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read24(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint32_t Read32(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read32(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
|
||||
// write
|
||||
inline void Write8(void *vp, uint8_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = x;
|
||||
}
|
||||
|
||||
void Write16(void *vp, uint16_t x);
|
||||
|
||||
void Write24(void *vp, uint32_t x);
|
||||
|
||||
void Write32(void *vp, uint32_t x);
|
||||
|
||||
|
||||
inline void Write8(void *vp, unsigned offset, uint8_t x)
|
||||
{
|
||||
Write8(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write16(void *vp, unsigned offset, uint16_t x)
|
||||
{
|
||||
Write16(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write24(void *vp, unsigned offset, uint32_t x)
|
||||
{
|
||||
Write24(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write32(void *vp, unsigned offset, uint32_t x)
|
||||
{
|
||||
Write32(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
namespace BigEndian {
|
||||
|
||||
|
||||
inline uint8_t Read8(const void *vp)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)vp;
|
||||
return p[0];
|
||||
}
|
||||
|
||||
uint16_t Read16(const void *vp);
|
||||
|
||||
uint32_t Read24(const void *vp);
|
||||
|
||||
uint32_t Read32(const void *vp);
|
||||
|
||||
|
||||
inline uint8_t Read8(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read8(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint16_t Read16(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read16(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint32_t Read24(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read24(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
inline uint32_t Read32(const void *vp, unsigned offset)
|
||||
{
|
||||
return Read32(offset + (const uint8_t *)vp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// write
|
||||
inline void Write8(void *vp, uint8_t x)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)vp;
|
||||
p[0] = x;
|
||||
}
|
||||
|
||||
void Write16(void *vp, uint16_t x);
|
||||
|
||||
void Write24(void *vp, uint32_t x);
|
||||
|
||||
void Write32(void *vp, uint32_t x);
|
||||
|
||||
|
||||
inline void Write8(void *vp, unsigned offset, uint8_t x)
|
||||
{
|
||||
Write8(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write16(void *vp, unsigned offset, uint16_t x)
|
||||
{
|
||||
Write16(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write24(void *vp, unsigned offset, uint32_t x)
|
||||
{
|
||||
Write24(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
inline void Write32(void *vp, unsigned offset, uint32_t x)
|
||||
{
|
||||
Write32(offset + (uint8_t *)vp, x);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
class IOBuffer {
|
||||
public:
|
||||
|
||||
IOBuffer(void *vp, unsigned size)
|
||||
{
|
||||
_buffer = vp;
|
||||
_size = size;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
void write8(uint8_t value)
|
||||
{
|
||||
Write8(_buffer, _offset, value);
|
||||
_offset += 1;
|
||||
}
|
||||
void write16(uint16_t value)
|
||||
{
|
||||
Write16(_buffer, _offset, value);
|
||||
_offset += 2;
|
||||
}
|
||||
|
||||
void write24(uint32_t value)
|
||||
{
|
||||
Write24(_buffer, _offset, value);
|
||||
_offset += 3;
|
||||
}
|
||||
void write32(uint32_t value)
|
||||
{
|
||||
Write32(_buffer, _offset, value);
|
||||
_offset += 4;
|
||||
}
|
||||
|
||||
void writeBytes(const void *src, unsigned count)
|
||||
{
|
||||
std::memcpy(_offset + (uint8_t *)_buffer, src, count);
|
||||
_offset += count;
|
||||
}
|
||||
|
||||
void writeZero(unsigned count)
|
||||
{
|
||||
std::memset(_offset + (uint8_t *)_buffer, 0, count);
|
||||
|
||||
_offset += count;
|
||||
}
|
||||
|
||||
|
||||
uint8_t read8()
|
||||
{
|
||||
uint8_t x = Read8(_buffer, _offset);
|
||||
_offset += 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
uint16_t read16()
|
||||
{
|
||||
uint16_t x = Read16(_buffer, _offset);
|
||||
_offset += 2;
|
||||
return x;
|
||||
}
|
||||
|
||||
uint32_t read24()
|
||||
{
|
||||
uint32_t x = Read24(_buffer, _offset);
|
||||
_offset += 3;
|
||||
return x;
|
||||
}
|
||||
|
||||
uint32_t read32()
|
||||
{
|
||||
uint32_t x = Read32(_buffer, _offset);
|
||||
_offset += 4;
|
||||
return x;
|
||||
}
|
||||
|
||||
void readBytes(void *dest, unsigned count)
|
||||
{
|
||||
std::memcpy(dest, _offset + (uint8_t *)_buffer, count);
|
||||
_offset += count;
|
||||
}
|
||||
|
||||
|
||||
unsigned offset() const { return _offset; }
|
||||
void setOffset(unsigned offset) { _offset = offset; }
|
||||
|
||||
void setOffset(unsigned offset, bool zero)
|
||||
{
|
||||
if (zero && offset > _offset)
|
||||
{
|
||||
writeZero(offset - _offset);
|
||||
}
|
||||
else setOffset(offset);
|
||||
}
|
||||
|
||||
unsigned size() const { return _size; }
|
||||
|
||||
void *buffer() const { return _buffer; }
|
||||
|
||||
private:
|
||||
void *_buffer;
|
||||
unsigned _size;
|
||||
unsigned _offset;
|
||||
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __IOBUFFER_H__
|
||||
#define __IOBUFFER_H__
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace LittleEndian {
|
||||
#include "IOBuffer.cpp.h"
|
||||
}
|
||||
|
||||
namespace BigEndian {
|
||||
#include "IOBuffer.cpp.h"
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,7 @@
|
|||
CC = g++
|
||||
CPPFLAGS += -g -Wall -I../
|
||||
|
||||
|
||||
all : Endian.o
|
||||
|
||||
Endian.o : Endian.cpp Endian.h
|
|
@ -0,0 +1,173 @@
|
|||
#include "Entry.h"
|
||||
#include "Endian.h"
|
||||
#include "Buffer.h"
|
||||
#include "Exception.h"
|
||||
|
||||
using namespace ProFUSE;
|
||||
using namespace LittleEndian;
|
||||
|
||||
// do it ourselves since could be different locale or something.
|
||||
#undef isalpha
|
||||
#undef isalnumdot
|
||||
#undef islower
|
||||
#undef tolower
|
||||
#undef toupper
|
||||
|
||||
static bool isalpha(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z')
|
||||
|| (c >= 'a' && c <= 'z') ;
|
||||
}
|
||||
|
||||
static bool isalnumdot(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z')
|
||||
|| (c >= 'a' && c <= 'z')
|
||||
|| (c >= '0' && c <='9')
|
||||
|| (c == '.') ;
|
||||
|
||||
}
|
||||
|
||||
static bool islower(char c)
|
||||
{
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
|
||||
inline char tolower(char c) { return c | 0x20; }
|
||||
inline char toupper(char c) { return c & ~0x20; }
|
||||
|
||||
/*
|
||||
* ProDOS names:
|
||||
* 1-15 letters, digits, or '.'
|
||||
* first character must be a letter.
|
||||
*/
|
||||
unsigned Entry::ValidName(const char *name)
|
||||
{
|
||||
unsigned length = 1;
|
||||
|
||||
if (!name) return 0;
|
||||
|
||||
if (!isalpha(*name)) return 0;
|
||||
|
||||
for (length = 1; length < 17; ++length)
|
||||
{
|
||||
char c = name[length];
|
||||
if (isalnumdot(c)) continue;
|
||||
if (c == 0) break;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (length > 15) return 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
Entry::Entry(unsigned type, const char *name)
|
||||
{
|
||||
_address = 0;
|
||||
_index = 0;
|
||||
_volume = NULL;
|
||||
_nameLength = 0;
|
||||
_caseFlag = 0;
|
||||
_storageType = type;
|
||||
|
||||
// everything else initialized in setName.
|
||||
setName(name);
|
||||
}
|
||||
|
||||
Entry::Entry(const void *bp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "Entry::Entry"
|
||||
|
||||
uint8_t x;
|
||||
uint16_t xcase = 0;
|
||||
|
||||
_address = 0;
|
||||
_index = 0;
|
||||
_volume = NULL;
|
||||
|
||||
_caseFlag = 0;
|
||||
_nameLength = 0;
|
||||
|
||||
std::memset(_name, 0, 16);
|
||||
std::memset(_namei, 0, 16);
|
||||
|
||||
x = Read8(bp, 0x00);
|
||||
_storageType = x >> 4;
|
||||
_nameLength = x & 0x0f;
|
||||
|
||||
for (unsigned i = 0; i < _nameLength; ++i)
|
||||
{
|
||||
_name[i] = _namei[i] = Read8(bp, i + 1);
|
||||
// check legality?
|
||||
}
|
||||
|
||||
switch (_storageType)
|
||||
{
|
||||
case SeedlingFile:
|
||||
case SaplingFile:
|
||||
case TreeFile:
|
||||
case PascalFile:
|
||||
case ExtendedFile:
|
||||
case DirectoryFile:
|
||||
xcase = Read16(bp, 0x1c);
|
||||
break;
|
||||
|
||||
case VolumeHeader:
|
||||
xcase = Read16(bp, 0x16);
|
||||
break;
|
||||
}
|
||||
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
_caseFlag = xcase;
|
||||
xcase <<= 1;
|
||||
for (unsigned i = 0; i < _nameLength; ++i, xcase <<= 1)
|
||||
{
|
||||
if (xcase & 0x8000)
|
||||
_name[i] = tolower(_name[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entry::~Entry()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* IIgs Technote #8:
|
||||
*
|
||||
* bit 15 set
|
||||
* bit 14 + i set if name[i] is lowercase.
|
||||
*/
|
||||
void Entry::setName(const char *name)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "Entry::setName"
|
||||
|
||||
unsigned caseFlag = 0x8000;
|
||||
unsigned length = ValidName(name);
|
||||
if (!length) throw Exception("Invalid name.");
|
||||
|
||||
std::memset(_name, 0, 16);
|
||||
std::memset(_namei, 0, 16);
|
||||
|
||||
for (unsigned i = 0; i < length; ++i)
|
||||
{
|
||||
char c = name[i];
|
||||
_name[i] = c;
|
||||
_namei[i] = toupper(c);
|
||||
if (islower(c))
|
||||
{
|
||||
caseFlag |= (0x4000 >> i);
|
||||
}
|
||||
}
|
||||
|
||||
_nameLength = length;
|
||||
_caseFlag = caseFlag;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
#ifndef __DIRECTORY_H__
|
||||
#define __DIRECTORY_H__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "DateTime.h"
|
||||
|
||||
|
||||
namespace ProFUSE {
|
||||
|
||||
class BlockDevice;
|
||||
class Bitmap;
|
||||
class FileEntry;
|
||||
class Volume;
|
||||
class Buffer;
|
||||
|
||||
|
||||
enum Access {
|
||||
DestroyEnabled = 0x80,
|
||||
RenameEnabled = 0x40,
|
||||
BackupNeeded = 0x20,
|
||||
Invisible = 0x04,
|
||||
WriteEnabled = 0x02,
|
||||
ReadEnabled = 0x01
|
||||
};
|
||||
|
||||
enum StorageType {
|
||||
DeletedFile = 0x00,
|
||||
SeedlingFile = 0x01,
|
||||
SaplingFile = 0x02,
|
||||
TreeFile = 0x03,
|
||||
PascalFile = 0x04,
|
||||
ExtendedFile = 0x05,
|
||||
|
||||
DirectoryFile = 0x0d,
|
||||
DirectoryHeader = 0x0e,
|
||||
VolumeHeader = 0x0f
|
||||
};
|
||||
|
||||
|
||||
class Entry {
|
||||
public:
|
||||
|
||||
|
||||
virtual ~Entry();
|
||||
|
||||
virtual void write(Buffer *) = 0;
|
||||
|
||||
|
||||
unsigned storageType() const { return _storageType; }
|
||||
|
||||
unsigned nameLength() const { return _nameLength; }
|
||||
const char *name() const { return _name; }
|
||||
const char *namei() const { return _namei; }
|
||||
|
||||
unsigned caseFlag() const { return _caseFlag; }
|
||||
|
||||
|
||||
void setName(const char *name);
|
||||
|
||||
|
||||
|
||||
// returns strlen() on success, 0 on failure.
|
||||
static unsigned ValidName(const char *);
|
||||
|
||||
|
||||
unsigned block() const { return _address / 512; }
|
||||
unsigned offset() const { return _address % 512; }
|
||||
|
||||
unsigned address() const { return _address; }
|
||||
|
||||
unsigned index() const { return _index; }
|
||||
|
||||
Volume *volume() { return _volume; }
|
||||
|
||||
protected:
|
||||
Entry(unsigned storageType, const char *name);
|
||||
Entry(const void *bp);
|
||||
|
||||
|
||||
void setStorageType(unsigned type)
|
||||
{ _storageType = type; }
|
||||
|
||||
void setAddress(unsigned address)
|
||||
{ _address = address; }
|
||||
|
||||
void setIndex(unsigned index)
|
||||
{ _index = index; }
|
||||
|
||||
void setVolume(Volume *v)
|
||||
{ _volume = v; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
unsigned _address;
|
||||
unsigned _index;
|
||||
|
||||
Volume *_volume;
|
||||
|
||||
unsigned _storageType;
|
||||
unsigned _nameLength;
|
||||
char _namei[15+1]; // insensitive, ie, uppercase.
|
||||
char _name[15+1];
|
||||
|
||||
unsigned _caseFlag;
|
||||
};
|
||||
|
||||
class Directory : public Entry {
|
||||
public:
|
||||
|
||||
enum {
|
||||
|
||||
OffsetCreation = 0x18,
|
||||
OffsetVersion = 0x1c,
|
||||
OffsetMinVersion = 0x1d,
|
||||
OffsetAccess = 0x1e,
|
||||
OffsetEntryLength = 0x1f,
|
||||
OffsetEntriesPerBlock = 0x20,
|
||||
OffsetFileCount = 0x21
|
||||
};
|
||||
|
||||
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; }
|
||||
|
||||
unsigned version() const { return _version; }
|
||||
unsigned minVersion() const { return _minVersion; }
|
||||
|
||||
void setAccess(unsigned access);
|
||||
|
||||
|
||||
protected:
|
||||
Directory(unsigned type, const char *name);
|
||||
Directory(const void *bp);
|
||||
|
||||
std::vector<FileEntry *> _children;
|
||||
std::vector<unsigned> _entryBlocks;
|
||||
|
||||
void loadChildren(BlockDevice *, unsigned block);
|
||||
|
||||
private:
|
||||
|
||||
DateTime _creation;
|
||||
unsigned _version;
|
||||
unsigned _minVersion;
|
||||
unsigned _access;
|
||||
unsigned _entryLength; // always 0x27
|
||||
unsigned _entriesPerBlock; //always 0x0d
|
||||
|
||||
unsigned _fileCount;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class VolumeDirectory: public Directory {
|
||||
public:
|
||||
|
||||
enum {
|
||||
OffsetLastMod = 0x12,
|
||||
OffsetFileNameCaseFlag = 0x16,
|
||||
|
||||
OffsetBitmapPointer = 0x23,
|
||||
OffsetTotalBlocks = 0x25
|
||||
};
|
||||
|
||||
static VolumeDirectory *Create(const char *name, BlockDevice *device);
|
||||
static VolumeDirectory *Create(BlockDevice *);
|
||||
|
||||
|
||||
virtual ~VolumeDirectory();
|
||||
|
||||
unsigned bitmapPointer() const { return _bitmapPointer; }
|
||||
unsigned totalBlocks() const { return _totalBlocks; }
|
||||
|
||||
// bitmap stuff...
|
||||
int allocBlock();
|
||||
void freeBlock(unsigned block);
|
||||
|
||||
virtual void write(Buffer *);
|
||||
|
||||
BlockDevice *device() const { return _device; }
|
||||
|
||||
private:
|
||||
|
||||
VolumeDirectory(const char *name, BlockDevice *device);
|
||||
VolumeDirectory(BlockDevice *device, const void *bp);
|
||||
|
||||
Bitmap *_bitmap;
|
||||
BlockDevice *_device;
|
||||
|
||||
DateTime _modification;
|
||||
unsigned _totalBlocks;
|
||||
unsigned _bitmapPointer;
|
||||
|
||||
// inode / free inode list?
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SubDirectory : public Directory {
|
||||
public:
|
||||
|
||||
enum {
|
||||
OffsetPasswordEnabled = 0x10,
|
||||
OffsetParentPointer = 0x23,
|
||||
OffsetParentEntryNumber = 0x25,
|
||||
OffsetParentEntryLength = 0x26
|
||||
};
|
||||
|
||||
SubDirectory(FileEntry *);
|
||||
private:
|
||||
unsigned _parentPointer;
|
||||
unsigned _parentEntryNumber;
|
||||
unsigned _parentEntryLength;
|
||||
};
|
||||
|
||||
|
||||
class FileEntry : public Entry {
|
||||
public:
|
||||
|
||||
enum {
|
||||
OffsetFileType = 0x10,
|
||||
OffsetKeyPointer = 0x11,
|
||||
OffsetBlocksUsed = 0x13,
|
||||
OffsetEOF = 0x15,
|
||||
OffsetCreation = 0x18,
|
||||
|
||||
OffsetVersion = 0x1c,
|
||||
OffsetMinVersion = 0x1d,
|
||||
|
||||
OffsetFileNameCaseFlag = 0x1c,
|
||||
|
||||
OffsetAccess = 0x1e,
|
||||
OffsetAuxType = 0x1f,
|
||||
OffsetLastMod = 0x21,
|
||||
OffsetHeaderPointer = 0x25
|
||||
|
||||
};
|
||||
|
||||
|
||||
unsigned fileType() const { return _fileType; }
|
||||
unsigned auxType() const { return _auxType; }
|
||||
unsigned blocksUsed() const { return _blocksUsed; }
|
||||
unsigned eof() const { return _eof; }
|
||||
|
||||
unsigned access() const { return _access; }
|
||||
|
||||
|
||||
DateTime creation() const { return _creation; }
|
||||
DateTime modification() const { return _modification; }
|
||||
|
||||
private:
|
||||
|
||||
void *acquirePointer();
|
||||
void releasePointer();
|
||||
|
||||
unsigned _fileType;
|
||||
unsigned _keyPointer;
|
||||
unsigned _blocksUsed;
|
||||
unsigned _eof;
|
||||
DateTime _creation;
|
||||
//version
|
||||
//min version
|
||||
unsigned _access;
|
||||
unsigned _auxType;
|
||||
DateTime _modification;
|
||||
unsigned _headerPointer;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,124 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
|
||||
#include <File/File.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
|
||||
File::File()
|
||||
{
|
||||
_fd = -1;
|
||||
}
|
||||
|
||||
File::File(int fd)
|
||||
{
|
||||
_fd = fd;
|
||||
}
|
||||
|
||||
File::File(File& f)
|
||||
{
|
||||
_fd = f._fd;
|
||||
f._fd = -1;
|
||||
}
|
||||
|
||||
File::File(const char *name, int flags, const std::nothrow_t&)
|
||||
{
|
||||
_fd = ::open(name, flags);
|
||||
}
|
||||
|
||||
|
||||
File::File(const char *name, int flags, mode_t mode, const std::nothrow_t&)
|
||||
{
|
||||
_fd = ::open(name, flags, mode);
|
||||
}
|
||||
|
||||
File::File(const char *name, FileFlags flags, const std::nothrow_t&)
|
||||
{
|
||||
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
|
||||
}
|
||||
|
||||
File::File(const char *name, int flags)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "File::File"
|
||||
|
||||
_fd = ::open(name, flags);
|
||||
if (_fd < 0)
|
||||
throw POSIX::Exception( __METHOD__ ": open", errno);
|
||||
}
|
||||
|
||||
File::File(const char *name, int flags, mode_t mode)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "File::File"
|
||||
|
||||
_fd = ::open(name, flags, mode);
|
||||
if (_fd < 0)
|
||||
throw POSIX::Exception( __METHOD__ ": open", errno);
|
||||
}
|
||||
|
||||
|
||||
File::File(const char *name, FileFlags flags)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "File::File"
|
||||
|
||||
_fd = ::open(name, flags == ReadOnly ? O_RDONLY : O_RDWR);
|
||||
if (_fd < 0)
|
||||
throw POSIX::Exception( __METHOD__ ": open", errno);
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
int File::release()
|
||||
{
|
||||
int tmp = _fd;
|
||||
_fd = -1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void File::close()
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "File::close"
|
||||
|
||||
if (_fd >= 0)
|
||||
{
|
||||
::close(_fd);
|
||||
_fd = -1;
|
||||
|
||||
// destructor shouldn't throw.
|
||||
/*
|
||||
if (::close(fd) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": close", errno);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void File::adopt(File &f)
|
||||
{
|
||||
if (&f == this) return;
|
||||
|
||||
close();
|
||||
_fd = f._fd;
|
||||
f._fd = -1;
|
||||
}
|
||||
|
||||
void File::adopt(int fd)
|
||||
{
|
||||
if (fd == _fd) return;
|
||||
close();
|
||||
_fd = fd;
|
||||
}
|
||||
|
||||
|
||||
void File::swap(File &f)
|
||||
{
|
||||
std::swap(_fd, f._fd);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef __FILE_H__
|
||||
#define __FILE_H__
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
|
||||
class File {
|
||||
|
||||
public:
|
||||
|
||||
enum FileFlags {
|
||||
ReadOnly = 1,
|
||||
ReadWrite = 2
|
||||
};
|
||||
|
||||
File();
|
||||
File(File &);
|
||||
File(int fd);
|
||||
|
||||
File(const char *name, int flags);
|
||||
File(const char *name, int flags, mode_t mode);
|
||||
File(const char *name, FileFlags flags);
|
||||
|
||||
File(const char *name, int flags, const std::nothrow_t &);
|
||||
File(const char *name, int flags, mode_t mode, const std::nothrow_t &);
|
||||
File(const char *name, FileFlags flags, const std::nothrow_t &);
|
||||
|
||||
~File();
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return _fd >= 0;
|
||||
}
|
||||
|
||||
int fd() const { return _fd; }
|
||||
|
||||
int release();
|
||||
|
||||
void close();
|
||||
|
||||
void adopt(File &f);
|
||||
void adopt(int fd);
|
||||
|
||||
void swap(File &f);
|
||||
|
||||
private:
|
||||
|
||||
// could call dup() or something.
|
||||
File& operator=(const File &f);
|
||||
int _fd;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,185 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <File/MappedFile.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
MappedFile::MappedFile()
|
||||
{
|
||||
_length = -1;
|
||||
_address = MAP_FAILED;
|
||||
_readOnly = true;
|
||||
}
|
||||
|
||||
MappedFile::MappedFile(MappedFile &mf)
|
||||
{
|
||||
_address = mf._address;
|
||||
_length = mf._length;
|
||||
_readOnly = mf._readOnly;
|
||||
|
||||
mf._address = MAP_FAILED;
|
||||
mf._length = -1;
|
||||
mf._readOnly = true;
|
||||
}
|
||||
|
||||
MappedFile::MappedFile(const File &f, File::FileFlags flags, size_t size)
|
||||
{
|
||||
_length = -1;
|
||||
_address = MAP_FAILED;
|
||||
_readOnly = true;
|
||||
|
||||
init(f, flags == File::ReadOnly, size);
|
||||
}
|
||||
|
||||
|
||||
MappedFile::MappedFile(const char *name, File::FileFlags flags)
|
||||
{
|
||||
File f(name, flags);
|
||||
|
||||
_length = -1;
|
||||
_address = MAP_FAILED;
|
||||
_readOnly = true;
|
||||
|
||||
init(f, flags == File::ReadOnly, 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
MappedFile::MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t ¬hrow)
|
||||
{
|
||||
File f(name, flags, nothrow);
|
||||
|
||||
_length = -1;
|
||||
_address = MAP_FAILED;
|
||||
_readOnly = true;
|
||||
|
||||
if (f.isValid())
|
||||
init(f, flags == File::ReadOnly, 0);
|
||||
}
|
||||
|
||||
|
||||
MappedFile::~MappedFile()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MappedFile::init(const File &f, bool readOnly, size_t size)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedFile::init"
|
||||
|
||||
struct stat st;
|
||||
int prot = readOnly ? PROT_READ : PROT_READ | PROT_WRITE;
|
||||
int flags = MAP_FILE | MAP_SHARED;
|
||||
|
||||
// close enough
|
||||
if (f.fd() < 0)
|
||||
throw POSIX::Exception( __METHOD__, EBADF);
|
||||
|
||||
|
||||
if (!size)
|
||||
{
|
||||
if (::fstat(f.fd(), &st) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": fstat", errno);
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
throw POSIX::Exception(__METHOD__, ENODEV);
|
||||
|
||||
size = st.st_size;
|
||||
}
|
||||
|
||||
_length = size;
|
||||
_address = ::mmap(0, _length, prot, flags, f.fd(), 0);
|
||||
|
||||
if (_address == MAP_FAILED)
|
||||
throw POSIX::Exception(__METHOD__ ": mmap", errno);
|
||||
|
||||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MappedFile::close()
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedFile::close"
|
||||
|
||||
if (_address != MAP_FAILED)
|
||||
{
|
||||
/*
|
||||
void *address = _address;
|
||||
size_t length = _length;
|
||||
*/
|
||||
|
||||
::munmap(_address, _length);
|
||||
|
||||
_address = MAP_FAILED;
|
||||
_length = -1;
|
||||
_readOnly = true;
|
||||
|
||||
// destructor shouldn't throw.
|
||||
/*
|
||||
if (::munmap(address, length) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": munmap", errno);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void MappedFile::sync()
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedFile::sync"
|
||||
|
||||
if (_address != MAP_FAILED)
|
||||
{
|
||||
if (::msync(_address, _length, MS_SYNC) != 0)
|
||||
throw POSIX::Exception(__METHOD__ ": msync", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void MappedFile::adopt(MappedFile &mf)
|
||||
{
|
||||
close();
|
||||
_address = mf._address;
|
||||
_length = mf._length;
|
||||
_readOnly = mf._readOnly;
|
||||
|
||||
mf._address = MAP_FAILED;
|
||||
mf._length = -1;
|
||||
mf._readOnly = true;
|
||||
}
|
||||
|
||||
void MappedFile::swap(MappedFile &mf)
|
||||
{
|
||||
std::swap(_address, mf._address);
|
||||
std::swap(_length, mf._length);
|
||||
std::swap(_readOnly, mf._readOnly);
|
||||
}
|
||||
|
||||
|
||||
MappedFile *MappedFile::Create(const char *name, size_t size)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedFile::Create"
|
||||
|
||||
File fd(::open(name, O_CREAT | O_TRUNC | O_RDWR, 0644));
|
||||
|
||||
if (!fd.isValid())
|
||||
{
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to create file.", errno);
|
||||
}
|
||||
|
||||
// TODO -- is ftruncate portable?
|
||||
if (::ftruncate(fd.fd(), size) < 0)
|
||||
{
|
||||
// TODO -- unlink?
|
||||
throw POSIX::Exception(__METHOD__ ": Unable to truncate file.", errno);
|
||||
}
|
||||
|
||||
return new MappedFile(fd, File::ReadWrite, size);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef __MAPPED_FILE_H__
|
||||
#define __MAPPED_FILE_H__
|
||||
|
||||
|
||||
#include <new>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <File/File.h>
|
||||
|
||||
class File;
|
||||
|
||||
class MappedFile {
|
||||
public:
|
||||
|
||||
MappedFile();
|
||||
MappedFile(MappedFile&);
|
||||
MappedFile(const File &f, File::FileFlags flags, size_t size = -1);
|
||||
MappedFile(const char *name, File::FileFlags flags);
|
||||
MappedFile(const char *name, File::FileFlags flags, const std::nothrow_t ¬hrow);
|
||||
|
||||
~MappedFile();
|
||||
|
||||
|
||||
static MappedFile *Create(const char *name, size_t size);
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return _address != MAP_FAILED;
|
||||
}
|
||||
|
||||
void sync();
|
||||
void close();
|
||||
|
||||
void *address() const { return _address; }
|
||||
size_t length() const { return _length; }
|
||||
bool readOnly() const { return _readOnly; }
|
||||
|
||||
void swap(MappedFile &);
|
||||
void adopt(MappedFile &);
|
||||
|
||||
private:
|
||||
|
||||
MappedFile& operator=(MappedFile &);
|
||||
|
||||
void init(const File &f, bool readOnly, size_t size);
|
||||
|
||||
void *_address;
|
||||
size_t _length;
|
||||
bool _readOnly;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
192
MacRoman.cpp
192
MacRoman.cpp
|
@ -1,192 +0,0 @@
|
|||
|
||||
#include "MacRoman.h"
|
||||
|
||||
/*
|
||||
* mapping of MacRoman characters (0x80-0xff) to unicode.
|
||||
*/
|
||||
static unsigned m2u[] = {
|
||||
0x00C4,
|
||||
0x00C5,
|
||||
0x00C7,
|
||||
0x00C9,
|
||||
0x00D1,
|
||||
0x00D6,
|
||||
0x00DC,
|
||||
0x00E1,
|
||||
0x00E0,
|
||||
0x00E2,
|
||||
0x00E4,
|
||||
0x00E3,
|
||||
0x00E5,
|
||||
0x00E7,
|
||||
0x00E9,
|
||||
0x00E8,
|
||||
0x00EA,
|
||||
0x00EB,
|
||||
0x00ED,
|
||||
0x00EC,
|
||||
0x00EE,
|
||||
0x00EF,
|
||||
0x00F1,
|
||||
0x00F3,
|
||||
0x00F2,
|
||||
0x00F4,
|
||||
0x00F6,
|
||||
0x00F5,
|
||||
0x00FA,
|
||||
0x00F9,
|
||||
0x00FB,
|
||||
0x00FC,
|
||||
0x2020,
|
||||
0x00B0,
|
||||
0x00A2,
|
||||
0x00A3,
|
||||
0x00A7,
|
||||
0x2022,
|
||||
0x00B6,
|
||||
0x00DF,
|
||||
0x00AE,
|
||||
0x00A9,
|
||||
0x2122,
|
||||
0x00B4,
|
||||
0x00A8,
|
||||
0x2260,
|
||||
0x00C6,
|
||||
0x00D8,
|
||||
0x221E,
|
||||
0x00B1,
|
||||
0x2264,
|
||||
0x2265,
|
||||
0x00A5,
|
||||
0x00B5,
|
||||
0x2202,
|
||||
0x2211,
|
||||
0x220F,
|
||||
0x03C0,
|
||||
0x222B,
|
||||
0x00AA,
|
||||
0x00BA,
|
||||
0x03A9,
|
||||
0x00E6,
|
||||
0x00F8,
|
||||
0x00BF,
|
||||
0x00A1,
|
||||
0x00AC,
|
||||
0x221A,
|
||||
0x0192,
|
||||
0x2248,
|
||||
0x2206,
|
||||
0x00AB,
|
||||
0x00BB,
|
||||
0x2026,
|
||||
0x00A0,
|
||||
0x00C0,
|
||||
0x00C3,
|
||||
0x00D5,
|
||||
0x0152,
|
||||
0x0153,
|
||||
0x2013,
|
||||
0x2014,
|
||||
0x201C,
|
||||
0x201D,
|
||||
0x2018,
|
||||
0x2019,
|
||||
0x00F7,
|
||||
0x25CA,
|
||||
0x00FF,
|
||||
0x0178,
|
||||
0x2044,
|
||||
0x20AC,
|
||||
0x2039,
|
||||
0x203A,
|
||||
0xFB01,
|
||||
0xFB02,
|
||||
0x2021,
|
||||
0x00B7,
|
||||
0x201A,
|
||||
0x201E,
|
||||
0x2030,
|
||||
0x00C2,
|
||||
0x00CA,
|
||||
0x00C1,
|
||||
0x00CB,
|
||||
0x00C8,
|
||||
0x00CD,
|
||||
0x00CE,
|
||||
0x00CF,
|
||||
0x00CC,
|
||||
0x00D3,
|
||||
0x00D4,
|
||||
0xF8FF,
|
||||
0x00D2,
|
||||
0x00DA,
|
||||
0x00DB,
|
||||
0x00D9,
|
||||
0x0131,
|
||||
0x02C6,
|
||||
0x02DC,
|
||||
0x00AF,
|
||||
0x02D8,
|
||||
0x02D9,
|
||||
0x02DA,
|
||||
0x00B8,
|
||||
0x02DD,
|
||||
0x02DB,
|
||||
0x02C7
|
||||
};
|
||||
|
||||
|
||||
|
||||
bool MacRoman::isASCII() const
|
||||
{
|
||||
for (std::string::const_iterator iter = _string.begin(); iter != _string.end(); ++iter)
|
||||
{
|
||||
if (*iter & 0x80) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacRoman::toUTF8() const
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(_string.length());
|
||||
|
||||
for (std::string::const_iterator iter = _string.begin(); iter != _string.end(); ++iter)
|
||||
{
|
||||
char c = *iter;
|
||||
if (c & 0x80 == 0) out.push_back(c);
|
||||
else
|
||||
{
|
||||
unsigned uc = m2u[c & 0x7f];
|
||||
if (uc <= 0x7ff)
|
||||
{
|
||||
out.push_back(0xC0 | (uc >> 6));
|
||||
out.push_back(0x80 | (uc & 0x3f));
|
||||
}
|
||||
else // nothing larger than 0xffff
|
||||
{
|
||||
out.push_back(0xe0 | (uc >> 12));
|
||||
out.push_back(0x80 | ((uc >> 6) & 0x3f));
|
||||
out.push_back(0x80 | (uc & 0x3f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::wstring MacRoman::toWString() const
|
||||
{
|
||||
std::wstring out;
|
||||
out.reserve(_string.length());
|
||||
|
||||
for (std::string::const_iterator iter = _string.begin(); iter != _string.end(); ++iter)
|
||||
{
|
||||
char c = *iter;
|
||||
if (c & 0x80 == 0) out.push_back(c);
|
||||
else out.push_back(m2u[c & 0x7f]);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
26
MacRoman.h
26
MacRoman.h
|
@ -1,26 +0,0 @@
|
|||
|
||||
#ifndef __MACROMAN_H__
|
||||
#define __MACROMAN_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
class MacRoman {
|
||||
public:
|
||||
MacRoman(const std::string& string);
|
||||
|
||||
bool isASCII() const;
|
||||
std::string toUTF8() const;
|
||||
std::wstring toWString() const;
|
||||
|
||||
|
||||
private:
|
||||
std::string _string;
|
||||
};
|
||||
|
||||
inline MacRoman::MacRoman(const std::string& string) :
|
||||
_string(string)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
CC = g++
|
||||
CPPFLAGS += -Wall -W -Wno-multichar -I. -O2 -g
|
||||
LIBS += -lpthread
|
||||
UNAME = $(shell uname -s)
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
FUSE_LIBS += -losxfuse
|
||||
CPPFLAGS += -I/usr/local/include/osxfuse/fuse
|
||||
else
|
||||
FUSE_LIBS += -lfuse
|
||||
endif
|
||||
|
||||
ifdef HAVE_NUFX
|
||||
DEVICE_OBJECTS += Device/SDKImage.o
|
||||
EXCEPTION_OBJECTS += NuFX/Exception.o
|
||||
LDFLAGS += -L/usr/local/lib/
|
||||
LIBS += -lnufx -lz
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
LIBS += -framework carbon
|
||||
endif
|
||||
|
||||
CPPFLAGS += -DHAVE_NUFX=1
|
||||
endif
|
||||
|
||||
|
||||
OBJECTS += ${wildcard *.o}
|
||||
OBJECTS += ${wildcard bin/*.o}
|
||||
OBJECTS += ${wildcard Cache/*.o}
|
||||
OBJECTS += ${wildcard Device/*.o}
|
||||
OBJECTS += ${wildcard Endian/*.o}
|
||||
OBJECTS += ${wildcard File/*.o}
|
||||
OBJECTS += ${wildcard Pascal/*.o}
|
||||
OBJECTS += ${wildcard Common/*.o}
|
||||
OBJECTS += ${wildcard ProDOS/*.o}
|
||||
OBJECTS += ${wildcard POSIX/*.o}
|
||||
OBJECTS += ${wildcard NuFX/*.o}
|
||||
|
||||
|
||||
TARGETS = o/apfm o/newfs_pascal o/fuse_pascal o/profuse o/xattr
|
||||
|
||||
BIN_OBJECTS += bin/apfm.o
|
||||
BIN_OBJECTS += bin/fuse_pascal_ops.o
|
||||
BIN_OBJECTS += bin/newfs_prodos.o
|
||||
BIN_OBJECTS += bin/fuse_pascal.o
|
||||
BIN_OBJECTS += bin/newfs_pascal.o
|
||||
BIN_OBJECTS += bin/xattr.o
|
||||
BIN_OBJECTS += bin/profuse.o
|
||||
BIN_OBJECTS += bin/profuse_dirent.o
|
||||
BIN_OBJECTS += bin/profuse_file.o
|
||||
BIN_OBJECTS += bin/profuse_stat.o
|
||||
BIN_OBJECTS += bin/profuse_xattr.o
|
||||
|
||||
|
||||
|
||||
CACHE_OBJECTS += Cache/BlockCache.o
|
||||
CACHE_OBJECTS += Cache/ConcreteBlockCache.o
|
||||
CACHE_OBJECTS += Cache/MappedBlockCache.o
|
||||
|
||||
DEVICE_OBJECTS += Device/Adaptor.o
|
||||
DEVICE_OBJECTS += Device/BlockDevice.o
|
||||
DEVICE_OBJECTS += Device/DavexDiskImage.o
|
||||
DEVICE_OBJECTS += Device/DiskCopy42Image.o
|
||||
DEVICE_OBJECTS += Device/DiskImage.o
|
||||
DEVICE_OBJECTS += Device/RawDevice.o
|
||||
DEVICE_OBJECTS += Device/UniversalDiskImage.o
|
||||
|
||||
|
||||
ENDIAN_OBJECTS += Endian/Endian.o
|
||||
|
||||
FILE_OBJECTS += File/File.o
|
||||
FILE_OBJECTS += File/MappedFile.o
|
||||
|
||||
PASCAL_OBJECTS += Pascal/Date.o
|
||||
PASCAL_OBJECTS += Pascal/FileEntry.o
|
||||
PASCAL_OBJECTS += Pascal/TextWriter.o
|
||||
PASCAL_OBJECTS += Pascal/Entry.o
|
||||
PASCAL_OBJECTS += Pascal/VolumeEntry.o
|
||||
|
||||
COMMON_OBJECTS += Common/Lock.o
|
||||
|
||||
PRODOS_OBJECTS += ProDOS/DateTime.o
|
||||
PRODOS_OBJECTS += ProDOS/Disk.o
|
||||
PRODOS_OBJECTS += ProDOS/File.o
|
||||
|
||||
EXCEPTION_OBJECTS += Common/Exception.o
|
||||
EXCEPTION_OBJECTS += ProDOS/Exception.o
|
||||
EXCEPTION_OBJECTS += POSIX/Exception.o
|
||||
|
||||
|
||||
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
|
||||
apfm: o/apfm
|
||||
@true
|
||||
|
||||
fuse_pascal: o/fuse_pascal
|
||||
@true
|
||||
|
||||
newfs_pascal: o/newfs_pascal
|
||||
@true
|
||||
|
||||
profuse: o/profuse
|
||||
@true
|
||||
|
||||
xattr: o/xattr
|
||||
@true
|
||||
|
||||
|
||||
|
||||
o/xattr: bin/xattr.o
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
|
||||
|
||||
o/newfs_pascal: bin/newfs_pascal.o \
|
||||
${CACHE_OBJECTS} \
|
||||
${DEVICE_OBJECTS} \
|
||||
${ENDIAN_OBJECTS} \
|
||||
${FILE_OBJECTS} \
|
||||
${COMMON_OBJECTS} \
|
||||
${EXCEPTION_OBJECTS} \
|
||||
${PASCAL_OBJECTS}
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
|
||||
|
||||
o/apfm: bin/apfm.o \
|
||||
${CACHE_OBJECTS} \
|
||||
${DEVICE_OBJECTS} \
|
||||
${ENDIAN_OBJECTS} \
|
||||
${FILE_OBJECTS} \
|
||||
${COMMON_OBJECTS} \
|
||||
${EXCEPTION_OBJECTS} \
|
||||
${PASCAL_OBJECTS}
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
|
||||
|
||||
|
||||
o/fuse_pascal: bin/fuse_pascal.o bin/fuse_pascal_ops.o \
|
||||
${CACHE_OBJECTS} \
|
||||
${DEVICE_OBJECTS} \
|
||||
${ENDIAN_OBJECTS} \
|
||||
${FILE_OBJECTS} \
|
||||
${COMMON_OBJECTS} \
|
||||
${EXCEPTION_OBJECTS} \
|
||||
${PASCAL_OBJECTS}
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
|
||||
|
||||
|
||||
o/profuse: bin/profuse.o bin/profuse_dirent.o bin/profuse_file.o \
|
||||
bin/profuse_stat.o bin/profuse_xattr.o \
|
||||
${CACHE_OBJECTS} \
|
||||
${DEVICE_OBJECTS} \
|
||||
${ENDIAN_OBJECTS} \
|
||||
${FILE_OBJECTS} \
|
||||
${COMMON_OBJECTS} \
|
||||
${EXCEPTION_OBJECTS} \
|
||||
${PRODOS_OBJECTS}
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) $(FUSE_LIBS) -o $@
|
||||
|
||||
|
||||
clean:
|
||||
rm -f ${OBJECTS} ${TARGETS}
|
||||
|
||||
xattr.o: bin/xattr.cpp
|
||||
|
||||
newfs_pascal.o: bin/newfs_pascal.cpp Device/BlockDevice.h Common/Exception.h \
|
||||
Device/TrackSector.h Cache/BlockCache.h Device/RawDevice.h File/File.h \
|
||||
Pascal/Pascal.h Pascal/Date.h
|
||||
|
||||
fuse_pascal.o: bin/fuse_pascal.cpp Pascal/Pascal.h Pascal/Date.h \
|
||||
Common/Exception.h Device/BlockDevice.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h
|
||||
|
||||
fuse_pascal_ops.o: bin/fuse_pascal_ops.cpp Pascal/Pascal.h Pascal/Date.h \
|
||||
Common/auto.h Common/Exception.h
|
||||
|
||||
apfm.o: bin/apfm.cpp Pascal/Pascal.h Pascal/Date.h Device/BlockDevice.h \
|
||||
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h
|
||||
|
||||
File/File.o: File/File.cpp File/File.h Common/Exception.h
|
||||
|
||||
File/MappedFile.o: File/MappedFile.cpp File/MappedFile.h File/File.h \
|
||||
Common/Exception.h
|
||||
|
||||
Device/Adaptor.o: Device/Adaptor.cpp Device/Adaptor.h Device/TrackSector.h \
|
||||
Common/Exception.h
|
||||
|
||||
Device/BlockDevice.o: Device/BlockDevice.cpp Device/BlockDevice.h \
|
||||
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
|
||||
Cache/ConcreteBlockCache.h Device/DiskImage.h Device/Adaptor.h \
|
||||
File/MappedFile.h File/File.h Device/UniversalDiskImage.h \
|
||||
Device/DiskCopy42Image.h Device/DavexDiskImage.h Device/RawDevice.h
|
||||
|
||||
Device/DavexDiskImage.o: Device/DavexDiskImage.cpp \
|
||||
Device/DavexDiskImage.h \
|
||||
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
|
||||
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
|
||||
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
|
||||
|
||||
Device/DiskCopy42Image.o: Device/DiskCopy42Image.cpp \
|
||||
Device/DiskCopy42Image.h \
|
||||
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h Device/DiskImage.h Device/Adaptor.h \
|
||||
File/MappedFile.h File/File.h Endian/Endian.h Endian/IOBuffer.h \
|
||||
Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h
|
||||
|
||||
Device/DiskImage.o: Device/DiskImage.cpp Device/DiskImage.h \
|
||||
Common/Exception.h \
|
||||
Device/BlockDevice.h Device/TrackSector.h Cache/BlockCache.h \
|
||||
Device/Adaptor.h File/MappedFile.h File/File.h Cache/MappedBlockCache.h
|
||||
|
||||
Device/RawDevice.o: Device/RawDevice.cpp Device/RawDevice.h \
|
||||
Device/BlockDevice.h \
|
||||
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h File/File.h
|
||||
|
||||
Device/UniversalDiskImage.o: Device/UniversalDiskImage.cpp \
|
||||
Device/UniversalDiskImage.h Device/BlockDevice.h Common/Exception.h \
|
||||
Device/TrackSector.h Cache/BlockCache.h Device/DiskImage.h \
|
||||
Device/Adaptor.h File/MappedFile.h File/File.h Endian/Endian.h \
|
||||
Endian/IOBuffer.h Endian/IOBuffer.cpp.h Cache/MappedBlockCache.h \
|
||||
Cache/ConcreteBlockCache.h
|
||||
|
||||
Endian/Endian.o: Endian/Endian.cpp Endian/Endian.h
|
||||
|
||||
Cache/BlockCache.o: Cache/BlockCache.cpp Cache/BlockCache.h \
|
||||
Device/BlockDevice.h Common/Exception.h Device/TrackSector.h \
|
||||
Common/auto.h
|
||||
|
||||
Cache/ConcreteBlockCache.o: Cache/ConcreteBlockCache.cpp \
|
||||
Device/BlockDevice.h \
|
||||
Common/Exception.h Device/TrackSector.h Cache/BlockCache.h \
|
||||
Cache/ConcreteBlockCache.h Common/auto.h
|
||||
|
||||
Cache/MappedBlockCache.o: Cache/MappedBlockCache.cpp \
|
||||
Cache/MappedBlockCache.h \
|
||||
Cache/BlockCache.h Device/BlockDevice.h Common/Exception.h \
|
||||
Device/TrackSector.h
|
||||
|
||||
Common/Exception.o: Common/Exception.cpp Common/Exception.h
|
||||
|
||||
Common/Lock.o: Common/Lock.cpp Common/Lock.h
|
||||
|
||||
|
||||
Pascal/Date.o: Pascal/Date.cpp Pascal/Date.h
|
||||
|
||||
Pascal/Entry.o: Pascal/Entry.cpp Pascal/Entry.h Pascal/Date.h \
|
||||
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
|
||||
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h
|
||||
|
||||
Pascal/FileEntry.o: Pascal/FileEntry.cpp Pascal/Pascal.h Pascal/Date.h \
|
||||
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
|
||||
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
|
||||
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h Pascal/TextWriter.h
|
||||
|
||||
Pascal/VolumeEntry.o: Pascal/VolumeEntry.cpp Pascal/Pascal.h Pascal/Date.h \
|
||||
Pascal/Entry.h Pascal/FileEntry.h Pascal/VolumeEntry.h Common/auto.h \
|
||||
Common/Exception.h Endian/Endian.h Endian/IOBuffer.h \
|
||||
Endian/IOBuffer.cpp.h Device/BlockDevice.h Device/TrackSector.h \
|
||||
Cache/BlockCache.h
|
||||
|
||||
Pascal/TextWriter.o: Pascal/TextWriter.cpp Pascal/TextWriter.h \
|
||||
Pascal/FileEntry.h Pascal/Entry.h Pascal/Date.h Common/Exception.h
|
||||
|
||||
|
||||
|
||||
ProDOS/DateTime.o: ProDOS/DateTime.cpp ProDOS/DateTime.h
|
||||
|
||||
ProDOS/Disk.o: ProDOS/Disk.cpp ProDOS/Disk.h
|
||||
|
||||
ProDOS/File.o: ProDOS/File.cpp ProDOS/File.h
|
||||
|
||||
|
||||
ProDOS/Exception.o: ProDOS/Exception.cpp ProDOS/Exception.h Common/Exception.h
|
||||
|
||||
NuFX/Exception.o: NuFX/Exception.cpp NuFX/Exception.h Common/Exception.h
|
||||
|
||||
POSIX/Exception.o: POSIX/Exception.cpp POSIX/Exception.h Common/Exception.h
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#include "DirectoryEntry.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace NuFX;
|
||||
|
||||
EntryPointer DirectoryEntry::lookup(const std::string& name) const
|
||||
{
|
||||
EntryIterator iter;
|
||||
|
||||
for (iter = _children.begin(); iter != _children.end(); ++iter)
|
||||
{
|
||||
|
||||
EntryPointer e = *iter;
|
||||
|
||||
if (e->name() == name) return e;
|
||||
}
|
||||
|
||||
return EntryPointer(); // empty.
|
||||
}
|
||||
|
||||
DirectoryEntryPointer DirectoryEntry::dir_lookup(const std::string &name)
|
||||
{
|
||||
EntryIterator iter;
|
||||
|
||||
for (iter = _children.begin(); iter != _children.end(); ++iter)
|
||||
{
|
||||
|
||||
EntryPointer e = *iter;
|
||||
|
||||
if (e->name() == name)
|
||||
{
|
||||
// dynamic cast, will return as empty pointer if
|
||||
// not a directory.
|
||||
|
||||
return DYNAMIC_POINTER_CAST(DirectoryEntryPointer, e);
|
||||
}
|
||||
}
|
||||
|
||||
// not found, insert it..
|
||||
|
||||
DirectoryEntryPointer e(new DirectoryEntryPointer(name));
|
||||
VolumeEntryPointer v = volume().lock();
|
||||
|
||||
_children.add(e);
|
||||
|
||||
if (v)
|
||||
{
|
||||
v->addEntry(e);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark fuse-support
|
||||
|
||||
EntryPointer DirectoryEntry::childAtIndex(unsigned index) const
|
||||
{
|
||||
if (index >= _children.size()) return EntryPointer();
|
||||
|
||||
return _children[index];
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef __NUFX_DIRECTORYENTRY_H__
|
||||
#define __NUFX_DIRECTORYENTRY_H__
|
||||
|
||||
#include "Entry.h"
|
||||
|
||||
#include <vector>
|
||||
#include <dirent.t>
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
|
||||
class DirectoryEntry : public Entry
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
EntryPointer lookup(const std::string & name) const;
|
||||
|
||||
EntryPointer childAtIndex(unsigned index) const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// creates directory if it does not exist.
|
||||
DirectoryEntryPointer dir_lookup(const std::string &name);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
std::vector<EntryPointer> _children;
|
||||
|
||||
typedef std::vector<EntryPointer>::iterator EntryIterator;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,66 @@
|
|||
#include "Entry.h"
|
||||
#include "Exception.h"
|
||||
|
||||
using namespace NuFX;
|
||||
|
||||
|
||||
Entry::Entry() :
|
||||
_inode(0)
|
||||
{
|
||||
}
|
||||
|
||||
Entry::Entry(const std::string& name) :
|
||||
_name(name), _inode(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Entry::~Entry()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
unsigned Entry::inode() const
|
||||
{
|
||||
return _inode;
|
||||
}
|
||||
|
||||
const std::string& Entry::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
void Entry::setName(const std::string& name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
VolumeEntryWeakPointer Entry::volume() const
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
|
||||
void Entry::setVolume(VolumeEntryWeakPointer volume)
|
||||
{
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
int Entry::stat(struct stat *st) const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t Entry::read(size_t size, off_t offset) const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t Entry::listxattr(char *namebuf, size_t size, int options) const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t Entry::getxattr(const std::string &name, void *value, size_t size, u_int32_t position, int options) const
|
||||
{
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef __NUFX_ENTRY_H__
|
||||
#define __NUFX_ENTRY_H__
|
||||
|
||||
|
||||
#include <Common/smart_pointers.h>
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#include <NufxLib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <dirent.h>
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
class DirectoryEntry;
|
||||
class Entry;
|
||||
class FileEntry;
|
||||
class VolumeEntry;
|
||||
|
||||
typedef SHARED_PTR(DirectoryEntry) DirectoryEntryPointer;
|
||||
typedef SHARED_PTR(Entry) EntryPointer;
|
||||
typedef SHARED_PTR(FileEntry) FileEntryPointer;
|
||||
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
|
||||
|
||||
typedef WEAK_PTR(Entry) EntryWeakPointer;
|
||||
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
|
||||
|
||||
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Entry();
|
||||
|
||||
virtual unsigned inode() const;
|
||||
virtual const std::string& name() const;
|
||||
|
||||
|
||||
// operations...
|
||||
virtual int stat(VolumeEntryPointer, struct stat *) const;
|
||||
virtual ssize_t read(VolumeEntryPointer, size_t size, off_t offset) const;
|
||||
virtual ssize_t listxattr(VolumeEntryPointer, char *namebuf, size_t size, int options) const;
|
||||
virtual ssize_t getxattr(VolumeEntryPointer, const std::string &name, void *value, size_t size, u_int32_t position, int options) const;
|
||||
|
||||
virtual int open(VolumeEntryPointer, int flags);
|
||||
virtual int close(VolumeEntryPointer);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
Entry();
|
||||
Entry(const std::string& name);
|
||||
|
||||
void setName(const std::string&);
|
||||
|
||||
private:
|
||||
Entry(const Entry&);
|
||||
Entry& operator=(const Entry&);
|
||||
|
||||
friend VolumeEntry;
|
||||
|
||||
std::string _name;
|
||||
unsigned _inode;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,10 @@
|
|||
#include "Exception.h"
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
const char *NuFX::Exception::errorString()
|
||||
{
|
||||
return ::NuStrError((NuError)error());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef __NUFX_EXCEPTION_H__
|
||||
#define __NUFX_EXCEPTION_H__
|
||||
|
||||
#include <Common/Exception.h>
|
||||
#include <NufxLib.h>
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
|
||||
class Exception : public ::Exception
|
||||
{
|
||||
public:
|
||||
Exception(const char *cp, NuError error);
|
||||
Exception(const std::string& string, NuError error);
|
||||
|
||||
virtual const char *errorString();
|
||||
|
||||
private:
|
||||
typedef ::Exception super;
|
||||
};
|
||||
|
||||
|
||||
inline Exception::Exception(const char *cp, NuError error) :
|
||||
super(cp, error)
|
||||
{
|
||||
}
|
||||
|
||||
inline Exception::Exception(const std::string& string, NuError error) :
|
||||
super(string, error)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef __NUFX_FILEENTRY_H__
|
||||
#define __NUFX_FILEENTRY_H__
|
||||
|
||||
#include "Entry.h"
|
||||
#include <NufxLib.h>
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
|
||||
class FileEntry : public Entry
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
|
||||
NuRecordIdx _recordID;
|
||||
unsigned _flags; // threads
|
||||
size_t _size; // data size
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#include "VolumeEntry.h"
|
||||
|
||||
using namespace NuFX;
|
||||
|
||||
void VolumeEntry::addEntry(EntryPointer e)
|
||||
{
|
||||
if (!e) return;
|
||||
|
||||
e->setVolume(pointer());
|
||||
|
||||
_inodeIndex->push_back(e);
|
||||
|
||||
e->_inode = _inodeIndex->length() + 100 - 1;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef __NUFX_VOLUMEENTRY_H__
|
||||
#define __NUFX_VOLUMEENTRY_H__
|
||||
|
||||
#include "DirectoryEntry.h"
|
||||
|
||||
#include <Common/unordered_map.h>
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
|
||||
class VolumeEntry : public DirectoryEntry
|
||||
{
|
||||
public:
|
||||
|
||||
void addEntry(EntryPointer);
|
||||
|
||||
private:
|
||||
|
||||
VolumeEntryPointer pointer() const
|
||||
{
|
||||
return STATIC_POINTER_CAST(VolumeEntryPointer, shared_from_this());
|
||||
}
|
||||
|
||||
void parse();
|
||||
|
||||
NuArchive *_archive;
|
||||
|
||||
//unsigned _inodeGenerator;
|
||||
|
||||
std::vector<WeakPointer> _inodeIndex;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
#include "Exception.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace POSIX {
|
||||
|
||||
const char *Exception::errorString()
|
||||
{
|
||||
return strerror(error());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef __POSIX_EXCEPTION_H__
|
||||
#define __POSIX_EXCEPTION_H__
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
namespace POSIX {
|
||||
|
||||
|
||||
class Exception : public ::Exception {
|
||||
public:
|
||||
Exception(const char *cp, int error);
|
||||
Exception(const std::string& string, int error);
|
||||
|
||||
virtual const char *errorString();
|
||||
|
||||
private:
|
||||
typedef ::Exception super;
|
||||
};
|
||||
|
||||
|
||||
inline Exception::Exception(const char *cp, int error) :
|
||||
super(cp, error)
|
||||
{
|
||||
}
|
||||
|
||||
inline Exception::Exception(const std::string& string, int error) :
|
||||
super(string, error)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
#include <Pascal/Date.h>
|
||||
#include <cstring>
|
||||
|
||||
using namespace Pascal;
|
||||
|
||||
|
||||
|
||||
Date::Date(unsigned val)
|
||||
{
|
||||
// yyyy yyym mmmm dddd
|
||||
_month = val & 0xf;
|
||||
_day = (val >> 4) & 0x1f;
|
||||
_year = (val >> 9) & 0x7f;
|
||||
}
|
||||
|
||||
Date::operator std::time_t() const {
|
||||
struct tm tm;
|
||||
|
||||
if (_day == 0 || _month == 0) return (std::time_t)-1;
|
||||
|
||||
std::memset(&tm, 0, sizeof(tm));
|
||||
tm.tm_hour = 12;
|
||||
tm.tm_mday = _day;
|
||||
tm.tm_mon = _month;
|
||||
tm.tm_year = _year;
|
||||
tm.tm_isdst = -1;
|
||||
|
||||
// ProDOS standard for dealing w/ y2k.
|
||||
if (_year <= 39) tm.tm_year += 100;
|
||||
|
||||
return std::mktime(&tm);
|
||||
}
|
||||
|
||||
Date::operator uint16_t() const {
|
||||
// year must be 0 .. 127
|
||||
return (_year << 9) | (_day << 4) | _month;
|
||||
}
|
||||
|
||||
Date Date::Today()
|
||||
{
|
||||
struct tm tm;
|
||||
std::time_t t = std::time(NULL);
|
||||
|
||||
::localtime_r(&t, &tm);
|
||||
|
||||
return Date(tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __PASCAL_DATE_H__
|
||||
#define __PASCAL_DATE_H__
|
||||
|
||||
#include <ctime>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Pascal {
|
||||
|
||||
class Date {
|
||||
public:
|
||||
|
||||
static Date Today();
|
||||
|
||||
Date();
|
||||
Date(unsigned yy, unsigned mm, unsigned dd);
|
||||
Date(unsigned);
|
||||
|
||||
operator std::time_t() const;
|
||||
operator uint16_t() const;
|
||||
|
||||
unsigned month() const { return _month; }
|
||||
unsigned day() const { return _day; }
|
||||
unsigned year() const { return _year; }
|
||||
|
||||
private:
|
||||
unsigned _year;
|
||||
unsigned _month;
|
||||
unsigned _day;
|
||||
};
|
||||
|
||||
|
||||
inline Date::Date()
|
||||
{
|
||||
_year = 0;
|
||||
_month = 0;
|
||||
_day = 0;
|
||||
}
|
||||
|
||||
inline Date::Date(unsigned yy, unsigned mm, unsigned dd)
|
||||
{
|
||||
_year = yy;
|
||||
_month = mm;
|
||||
_day = dd;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
#include <Pascal/Entry.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <Endian/IOBuffer.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
|
||||
using namespace LittleEndian;
|
||||
using namespace Pascal;
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark DirEntry
|
||||
|
||||
unsigned Entry::ValidName(const char *cp, unsigned maxLength)
|
||||
{
|
||||
|
||||
// any printable char except:
|
||||
// white space
|
||||
// $ = ? , (file only)
|
||||
// : (volume only)
|
||||
if (!cp || !*cp) return 0;
|
||||
|
||||
for (unsigned i = 0; ; ++i)
|
||||
{
|
||||
unsigned c = cp[i];
|
||||
if (c == 0) return i;
|
||||
if (i >= maxLength) return 0;
|
||||
|
||||
switch(c)
|
||||
{
|
||||
|
||||
case ':':
|
||||
if (maxLength == 7) return 0;
|
||||
break;
|
||||
case '$':
|
||||
case '=':
|
||||
case ',':
|
||||
case '?':
|
||||
if (maxLength == 15) return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!::isascii(c)) return 0;
|
||||
if (!std::isgraph(c)) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Entry::Entry()
|
||||
{
|
||||
_firstBlock = 0;
|
||||
_lastBlock = 0;
|
||||
_fileKind = 0;
|
||||
_inode = 0;
|
||||
_address = NULL;
|
||||
}
|
||||
|
||||
Entry::Entry(void *vp)
|
||||
{
|
||||
init(vp);
|
||||
}
|
||||
|
||||
Entry::~Entry()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void Entry::init(void *vp)
|
||||
{
|
||||
_firstBlock = Read16(vp, 0x00);
|
||||
_lastBlock = Read16(vp, 0x02);
|
||||
_fileKind = Read8(vp, 0x04);
|
||||
|
||||
_inode = 0;
|
||||
}
|
||||
|
||||
void Entry::writeDirectoryEntry(IOBuffer *b)
|
||||
{
|
||||
b->write16(_firstBlock);
|
||||
b->write16(_lastBlock);
|
||||
b->write8(_fileKind);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
#ifndef __PASCAL_ENTRY_H__
|
||||
#define __PASCAL_ENTRY_H__
|
||||
|
||||
#include <Common/smart_pointers.h>
|
||||
|
||||
|
||||
namespace Device {
|
||||
class BlockDevice;
|
||||
class BlockCache;
|
||||
}
|
||||
|
||||
namespace LittleEndian {
|
||||
class IOBuffer;
|
||||
}
|
||||
|
||||
namespace Pascal {
|
||||
|
||||
|
||||
enum {
|
||||
kUntypedFile,
|
||||
kBadBlockFile,
|
||||
kCodeFile,
|
||||
kTextFile,
|
||||
kInfoFile,
|
||||
kDataFile,
|
||||
kGrafFile,
|
||||
kFotoFile,
|
||||
kSecureDir
|
||||
};
|
||||
|
||||
class FileEntry;
|
||||
class VolumeEntry;
|
||||
|
||||
|
||||
typedef SHARED_PTR(FileEntry) FileEntryPointer;
|
||||
typedef SHARED_PTR(VolumeEntry) VolumeEntryPointer;
|
||||
|
||||
typedef WEAK_PTR(FileEntry) FileEntryWeakPointer;
|
||||
typedef WEAK_PTR(VolumeEntry) VolumeEntryWeakPointer;
|
||||
|
||||
class Entry : public ENABLE_SHARED_FROM_THIS(Entry) {
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Entry();
|
||||
|
||||
unsigned blocks() const { return _lastBlock - _firstBlock; }
|
||||
|
||||
unsigned firstBlock() const { return _firstBlock; }
|
||||
unsigned lastBlock() const { return _lastBlock; }
|
||||
|
||||
unsigned fileKind() const { return _fileKind; }
|
||||
|
||||
unsigned inode() const { return _inode; }
|
||||
void setInode(unsigned inode) { _inode = inode; }
|
||||
|
||||
VolumeEntryWeakPointer parent() { return _parent; }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
static unsigned ValidName(const char *name, unsigned maxSize);
|
||||
|
||||
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
|
||||
|
||||
Entry();
|
||||
Entry(void *);
|
||||
void init(void *);
|
||||
|
||||
unsigned _firstBlock;
|
||||
unsigned _lastBlock;
|
||||
unsigned _fileKind;
|
||||
|
||||
unsigned _inode;
|
||||
|
||||
private:
|
||||
|
||||
friend class VolumeEntry;
|
||||
|
||||
VolumeEntryWeakPointer _parent;
|
||||
unsigned _address;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,734 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <cerrno>
|
||||
|
||||
#include <Pascal/Pascal.h>
|
||||
#include <Pascal/TextWriter.h>
|
||||
|
||||
#include <Common/auto.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <ProDOS/Exception.h>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
#include <Endian/IOBuffer.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
|
||||
|
||||
using namespace LittleEndian;
|
||||
using namespace Pascal;
|
||||
|
||||
|
||||
enum {
|
||||
kDLE = 16
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* _lastByte is in the range 1..512 and indicates how many bytes in the last
|
||||
* block are in use.
|
||||
* _lastBlock is the block *after* the actual last block.
|
||||
* _maxFileSize is the maximum file size the file can grow to.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
unsigned FileEntry::ValidName(const char *cp)
|
||||
{
|
||||
return Entry::ValidName(cp, 15);
|
||||
}
|
||||
|
||||
FileEntryPointer FileEntry::Open(void *vp)
|
||||
{
|
||||
//return FileEntryPointer(new FileEntry(vp));
|
||||
return MAKE_SHARED(FileEntry, vp);
|
||||
}
|
||||
|
||||
FileEntryPointer FileEntry::Create(const char *name, unsigned fileKind)
|
||||
{
|
||||
//return FileEntryPointer(new FileEntry(name, fileKind));
|
||||
return MAKE_SHARED(FileEntry, name, fileKind);
|
||||
}
|
||||
|
||||
FileEntry::FileEntry(void *vp) :
|
||||
Entry(vp)
|
||||
{
|
||||
_status = Read8(vp, 0x05) & 0x01;
|
||||
_fileNameLength = Read8(vp, 0x06);
|
||||
std::memset(_fileName, 0, 16);
|
||||
std::memcpy(_fileName, 0x07 + (uint8_t *)vp, _fileNameLength);
|
||||
_lastByte = Read16(vp, 0x16);
|
||||
_modification = Date(Read16(vp, 0x18));
|
||||
|
||||
_fileSize = 0;
|
||||
_pageSize = NULL;
|
||||
_maxFileSize = 0;
|
||||
}
|
||||
|
||||
FileEntry::FileEntry(const char *name, unsigned fileKind)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "FileEntry::FileEntry"
|
||||
|
||||
unsigned length = ValidName(name);
|
||||
|
||||
if (!length)
|
||||
throw ::Exception(__METHOD__ ": Invalid file name.");
|
||||
|
||||
_fileKind = fileKind;
|
||||
_status = 0;
|
||||
|
||||
_fileNameLength = length;
|
||||
std::memset(_fileName, 0, sizeof(_fileName));
|
||||
for (unsigned i = 0; i < length; ++i)
|
||||
_fileName[i] = std::toupper(name[i]);
|
||||
|
||||
_modification = Date::Today();
|
||||
_lastByte = 0;
|
||||
|
||||
_fileSize = 0;
|
||||
_pageSize = NULL;
|
||||
_maxFileSize = 0;
|
||||
}
|
||||
|
||||
FileEntry::~FileEntry()
|
||||
{
|
||||
delete _pageSize;
|
||||
}
|
||||
|
||||
|
||||
void FileEntry::setFileKind(unsigned kind)
|
||||
{
|
||||
_fileKind = kind;
|
||||
if (_pageSize)
|
||||
{
|
||||
delete _pageSize;
|
||||
_fileSize = 0;
|
||||
_pageSize = NULL;
|
||||
}
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
|
||||
// throw if expired?
|
||||
if (v) v->writeEntry(this);
|
||||
}
|
||||
|
||||
|
||||
void FileEntry::setName(const char *name)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "FileEntry::setName"
|
||||
|
||||
unsigned length = ValidName(name);
|
||||
|
||||
if (!length)
|
||||
throw ProDOS::Exception(__METHOD__ ": Invalid file name.", ProDOS::badPathSyntax);
|
||||
|
||||
_fileNameLength = length;
|
||||
for (unsigned i = 0; i < length; ++i)
|
||||
_fileName[i] = std::toupper(name[i]);
|
||||
|
||||
|
||||
// parent's responsibility.
|
||||
//parent()->writeEntry(this);
|
||||
}
|
||||
|
||||
unsigned FileEntry::fileSize()
|
||||
{
|
||||
switch(fileKind())
|
||||
{
|
||||
case kTextFile:
|
||||
return textFileSize();
|
||||
break;
|
||||
default:
|
||||
return dataFileSize();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FileEntry::writeDirectoryEntry(IOBuffer *b)
|
||||
{
|
||||
Entry::writeDirectoryEntry(b);
|
||||
|
||||
b->write8(_status ? 0x01 : 0x00);
|
||||
b->write8(_fileNameLength);
|
||||
b->writeBytes(_fileName, 15);
|
||||
b->write16(_lastByte);
|
||||
b->write16(_modification);
|
||||
}
|
||||
|
||||
int FileEntry::read(uint8_t *buffer, unsigned size, unsigned offset)
|
||||
{
|
||||
unsigned fsize = fileSize();
|
||||
|
||||
if (offset + size > fsize) size = fsize - offset;
|
||||
if (offset >= fsize) return 0;
|
||||
|
||||
switch(fileKind())
|
||||
{
|
||||
case kTextFile:
|
||||
return textRead(buffer, size, offset);
|
||||
break;
|
||||
default:
|
||||
return dataRead(buffer, size, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int FileEntry::truncate(unsigned newSize)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "FileEntry::truncate"
|
||||
|
||||
unsigned currentSize = fileSize();
|
||||
|
||||
if (currentSize == newSize) return 0;
|
||||
|
||||
if (fileKind() == kTextFile)
|
||||
{
|
||||
if (newSize)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
newSize = 2; // text files have a 2-page scratch buffer for the editor.
|
||||
|
||||
if (_pageSize)
|
||||
{
|
||||
_pageSize->clear();
|
||||
_fileSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (truncateCommon(newSize) != 0)
|
||||
return -1;
|
||||
|
||||
_modification = Date::Today();
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
if (v) v->writeEntry(this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* truncateCommon -- common truncation code.
|
||||
* updates _lastByte and _lastBlock but does
|
||||
* not update _modification or commit to disk.
|
||||
*/
|
||||
int FileEntry::truncateCommon(unsigned newSize)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "FileEntry::truncateCommon"
|
||||
|
||||
unsigned currentSize = fileSize();
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
|
||||
if (newSize == currentSize) return 0;
|
||||
if (newSize > currentSize)
|
||||
{
|
||||
|
||||
if (newSize > _maxFileSize)
|
||||
{
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned remainder = newSize - currentSize;
|
||||
unsigned block = _lastBlock - 1;
|
||||
|
||||
if (_lastByte != 512)
|
||||
{
|
||||
// last page not full
|
||||
unsigned count = std::min(512 - _lastByte, remainder);
|
||||
|
||||
if (v)
|
||||
{
|
||||
uint8_t *address = (uint8_t *)v->loadBlock(block);
|
||||
std::memset(address + _lastByte, 0, count);
|
||||
v->unloadBlock(block, true);
|
||||
}
|
||||
remainder -= count;
|
||||
}
|
||||
block++;
|
||||
|
||||
while (remainder)
|
||||
{
|
||||
unsigned count = std::min(512u, remainder);
|
||||
if (v)
|
||||
{
|
||||
uint8_t *address = (uint8_t *)v->loadBlock(block);
|
||||
std::memset(address, 0, count);
|
||||
v->unloadBlock(block, true);
|
||||
}
|
||||
remainder -= count;
|
||||
block++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
setFileSize(newSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int FileEntry::write(TextWriter &text)
|
||||
{
|
||||
unsigned blocks = text.blocks();
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
if (!v)
|
||||
{
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (v->readOnly())
|
||||
{
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileKind() != kTextFile)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (blocks * 512 > _maxFileSize)
|
||||
{
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < blocks; ++i)
|
||||
{
|
||||
void *buffer = text.data(i);
|
||||
v->writeBlock(_firstBlock + i, buffer);
|
||||
|
||||
}
|
||||
|
||||
_modification = Date::Today();
|
||||
|
||||
setFileSize(blocks * 512);
|
||||
|
||||
v->writeEntry(this);
|
||||
v->sync();
|
||||
|
||||
return blocks * 512;
|
||||
}
|
||||
|
||||
int FileEntry::write(const uint8_t *buffer, unsigned size, unsigned offset)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "FileEntry::write"
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
if (!v)
|
||||
{
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (v->readOnly())
|
||||
{
|
||||
errno = EROFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fileKind() == kTextFile)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned currentSize = fileSize();
|
||||
|
||||
unsigned newSize = std::max(offset + size, currentSize);
|
||||
|
||||
|
||||
if (newSize > _maxFileSize)
|
||||
{
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (offset > currentSize)
|
||||
{
|
||||
if (truncateCommon(offset) != 0) return -1;
|
||||
}
|
||||
|
||||
// now write the data...
|
||||
|
||||
unsigned block = firstBlock() + offset / 512;
|
||||
unsigned start = offset % 512;
|
||||
unsigned remainder = size;
|
||||
|
||||
if (start)
|
||||
{
|
||||
unsigned count = std::min(512 - start, remainder);
|
||||
uint8_t *address = (uint8_t *)v->loadBlock(block);
|
||||
|
||||
std::memcpy(address + start, buffer, count);
|
||||
v->unloadBlock(block, true);
|
||||
|
||||
remainder -= count;
|
||||
buffer += count;
|
||||
block++;
|
||||
}
|
||||
|
||||
while (remainder)
|
||||
{
|
||||
uint8_t *address = (uint8_t *)v->loadBlock(block);
|
||||
|
||||
unsigned count = std::min(512u, size);
|
||||
|
||||
std::memcpy(address, buffer, count);
|
||||
v->unloadBlock(block, true);
|
||||
|
||||
remainder -= count;
|
||||
buffer += count;
|
||||
block++;
|
||||
}
|
||||
|
||||
|
||||
if (newSize > currentSize) setFileSize(newSize);
|
||||
|
||||
|
||||
_modification = Date::Today();
|
||||
v->writeEntry(this);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* private
|
||||
* set the file size. Does not check if > _maxFileSize.
|
||||
*
|
||||
*/
|
||||
void FileEntry::setFileSize(unsigned size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
// TODO -- verify how 0 byte files are handled.
|
||||
_lastBlock = _firstBlock + 1;
|
||||
_lastByte = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_lastBlock = 1 + _firstBlock + size / 512;
|
||||
_lastByte = size % 512;
|
||||
if (_lastByte == 0) _lastByte = 512;
|
||||
}
|
||||
|
||||
unsigned FileEntry::dataFileSize()
|
||||
{
|
||||
return blocks() * 512 - 512 + _lastByte;
|
||||
}
|
||||
|
||||
unsigned FileEntry::textFileSize()
|
||||
{
|
||||
if (!_pageSize) textInit();
|
||||
return _fileSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
|
||||
{
|
||||
uint8_t tmp[512];
|
||||
|
||||
unsigned count = 0;
|
||||
unsigned block = 0;
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
if (!v)
|
||||
{
|
||||
errno = EROFS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
block = _firstBlock + (offset / 512);
|
||||
|
||||
// returned value (count) is equal to size at this point.
|
||||
// (no partial reads).
|
||||
|
||||
/*
|
||||
* 1. Block align everything
|
||||
*/
|
||||
|
||||
if (offset % 512)
|
||||
{
|
||||
unsigned bytes = std::min(offset % 512, size);
|
||||
|
||||
v->readBlock(block++, tmp);
|
||||
|
||||
std::memcpy(buffer, tmp + 512 - bytes, bytes);
|
||||
|
||||
buffer += bytes;
|
||||
count += bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. read full blocks into the buffer.
|
||||
*/
|
||||
|
||||
while (size >= 512)
|
||||
{
|
||||
v->readBlock(block++, buffer);
|
||||
|
||||
buffer += 512;
|
||||
count += 512;
|
||||
size -= 512;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3. Read any trailing blocks.
|
||||
*/
|
||||
if (size)
|
||||
{
|
||||
v->readBlock(block, tmp);
|
||||
std::memcpy(buffer, tmp, size);
|
||||
|
||||
count += size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
|
||||
{
|
||||
unsigned page = 0;
|
||||
unsigned to = 0;
|
||||
unsigned block;
|
||||
unsigned l;
|
||||
unsigned count = 0;
|
||||
|
||||
::auto_array<uint8_t> tmp;
|
||||
unsigned tmpSize = 0;
|
||||
|
||||
if (!_pageSize) textInit();
|
||||
|
||||
l = _pageSize->size();
|
||||
|
||||
|
||||
// find the first page.
|
||||
for (page = 0; page < l; ++page)
|
||||
{
|
||||
unsigned pageSize = (*_pageSize)[page];
|
||||
if (to + pageSize > offset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
to += pageSize;
|
||||
}
|
||||
|
||||
// first 2 pages are spare, for editor use, not actually text.
|
||||
block = _firstBlock + 2 + (page * 2);
|
||||
|
||||
|
||||
// offset not needed anymore,
|
||||
// convert to offset from *this* page.
|
||||
offset -= to;
|
||||
|
||||
while (size)
|
||||
{
|
||||
unsigned pageSize = (*_pageSize)[page];
|
||||
unsigned bytes = std::min(size, pageSize - offset);
|
||||
|
||||
if (pageSize > tmpSize)
|
||||
{
|
||||
tmp.reset(new uint8_t[pageSize]);
|
||||
tmpSize = pageSize;
|
||||
}
|
||||
|
||||
|
||||
// can decode straight to buffer if size >= bytes && offset = 0.
|
||||
textDecodePage(block, tmp.get());
|
||||
|
||||
|
||||
std::memcpy(buffer, tmp.get() + offset, bytes);
|
||||
|
||||
|
||||
block += 2;
|
||||
page += 1;
|
||||
|
||||
size -= bytes;
|
||||
buffer += bytes;
|
||||
count += bytes;
|
||||
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned FileEntry::textDecodePage(unsigned block, uint8_t *out)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
unsigned size = 0;
|
||||
bool dle = false;
|
||||
|
||||
unsigned bytes = textReadPage(block, buffer);
|
||||
|
||||
for (unsigned i = 0; i < bytes; ++i)
|
||||
{
|
||||
uint8_t c = buffer[i];
|
||||
|
||||
if (!c) continue;
|
||||
|
||||
|
||||
if (dle)
|
||||
{
|
||||
if (c > 32)
|
||||
{
|
||||
unsigned x = c - 32;
|
||||
size += x;
|
||||
if (out) for (unsigned j = 0; j < x; ++j)
|
||||
*out++ = ' ';
|
||||
}
|
||||
dle = false;
|
||||
continue;
|
||||
}
|
||||
if (c == kDLE) { dle = true; continue; }
|
||||
|
||||
//if (c & 0x80) continue; // ascii only.
|
||||
|
||||
|
||||
if (c == 0x0d) c = 0x0a; // convert to unix format.
|
||||
if (out) *out++ = c;
|
||||
size += 1;
|
||||
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned FileEntry::textReadPage(unsigned block, uint8_t *in)
|
||||
{
|
||||
// reads up to 2 blocks.
|
||||
// assumes block within _startBlock ... _lastBlock - 1
|
||||
|
||||
VolumeEntryPointer v = parent().lock();
|
||||
if (!v)
|
||||
{
|
||||
errno = EROFS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
v->readBlock(block, in);
|
||||
if (block + 1 == _lastBlock)
|
||||
{
|
||||
return _lastByte;
|
||||
}
|
||||
|
||||
v->readBlock(block + 1, in + 512);
|
||||
if (block +2 == _lastBlock)
|
||||
{
|
||||
return 512 + _lastByte;
|
||||
}
|
||||
|
||||
return 1024;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FileEntry::textInit()
|
||||
{
|
||||
// calculate the file size and page offsets.
|
||||
_pageSize = new std::vector<unsigned>;
|
||||
_pageSize->reserve((_lastBlock - _firstBlock + 1 - 2) / 2);
|
||||
|
||||
_fileSize = 0;
|
||||
for (unsigned block = _firstBlock + 2; block < _lastBlock; block += 2)
|
||||
{
|
||||
unsigned size = textDecodePage(block, NULL);
|
||||
//printf("%u: %u\n", block, size);
|
||||
_fileSize += size;
|
||||
_pageSize->push_back(size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* compress white space into a dle.
|
||||
* returns true if altered.
|
||||
* nb -- only leading white space is compressed.
|
||||
*/
|
||||
bool FileEntry::Compress(std::string& text)
|
||||
{
|
||||
std::string out;
|
||||
|
||||
size_t pos;
|
||||
size_t count;
|
||||
|
||||
|
||||
if (text.length() < 3) return false;
|
||||
if (text[0] != ' ') return false;
|
||||
|
||||
pos = text.find_first_not_of(' ');
|
||||
|
||||
if (pos == std::string::npos)
|
||||
count = text.length();
|
||||
else count = pos;
|
||||
|
||||
if (count < 3) return false;
|
||||
|
||||
count = std::min((int)count, 255 - 32);
|
||||
|
||||
out.push_back(kDLE);
|
||||
out.push_back(32 + count);
|
||||
out.append(text.begin() + count, text.end());
|
||||
|
||||
text.swap(out);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* dle will only occur at start.
|
||||
*
|
||||
*/
|
||||
bool FileEntry::Uncompress(std::string& text)
|
||||
{
|
||||
std::string out;
|
||||
|
||||
unsigned c;
|
||||
|
||||
if (text.length() < 2) return false;
|
||||
|
||||
if (text[0] != kDLE) return false;
|
||||
|
||||
c = text[1];
|
||||
if (c < 32) c = 32;
|
||||
|
||||
out.append(c - 32, ' ');
|
||||
out.append(text.begin() + 2, text.end());
|
||||
|
||||
text.swap(out);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef __PASCAL_FILEENTRY_H__
|
||||
#define __PASCAL_FILEENTRY_H__
|
||||
|
||||
#include <Pascal/Entry.h>
|
||||
#include <Pascal/Date.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Pascal {
|
||||
|
||||
class TextWriter;
|
||||
|
||||
class FileEntry : public Entry {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
static unsigned ValidName(const char *);
|
||||
|
||||
static bool Compress(std::string& text);
|
||||
static bool Uncompress(std::string& text);
|
||||
|
||||
|
||||
static FileEntryPointer Create(const char *name, unsigned fileKind);
|
||||
static FileEntryPointer Open(void *vp);
|
||||
|
||||
|
||||
|
||||
virtual ~FileEntry();
|
||||
|
||||
unsigned fileSize();
|
||||
|
||||
unsigned lastByte() const { return _lastByte; }
|
||||
|
||||
const char *name() const { return _fileName; }
|
||||
Date modification() const { return _modification; }
|
||||
|
||||
|
||||
void setFileKind(unsigned kind);
|
||||
|
||||
int read(uint8_t *buffer, unsigned size, unsigned offset);
|
||||
int write(const uint8_t *buffer, unsigned size, unsigned offset);
|
||||
|
||||
int write(TextWriter& text);
|
||||
|
||||
int truncate(unsigned newSize);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
friend class VolumeEntry;
|
||||
|
||||
FileEntry(const char *name, unsigned fileKind);
|
||||
FileEntry(void *vp);
|
||||
|
||||
FileEntryPointer thisPointer()
|
||||
{
|
||||
return STATIC_POINTER_CAST(FileEntry, shared_from_this());
|
||||
}
|
||||
|
||||
|
||||
void setName(const char *name);
|
||||
|
||||
int truncateCommon(unsigned newSize);
|
||||
|
||||
void setFileSize(unsigned size);
|
||||
|
||||
unsigned _status;
|
||||
|
||||
unsigned _fileNameLength;
|
||||
char _fileName[16];
|
||||
|
||||
unsigned _lastByte;
|
||||
Date _modification;
|
||||
|
||||
|
||||
unsigned _maxFileSize; // maximum file size.
|
||||
|
||||
|
||||
|
||||
|
||||
// non-text files
|
||||
unsigned dataFileSize();
|
||||
int dataRead(uint8_t *buffer, unsigned size, unsigned offset);
|
||||
|
||||
// for text files.
|
||||
void textInit();
|
||||
unsigned textFileSize();
|
||||
int textRead(uint8_t *buffer, unsigned size, unsigned offset);
|
||||
|
||||
unsigned textReadPage(unsigned block, uint8_t *in);
|
||||
unsigned textDecodePage(unsigned block, uint8_t *out);
|
||||
|
||||
std::vector<unsigned> *_pageSize;
|
||||
unsigned _fileSize;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
CC = g++
|
||||
CPPFLAGS += -g -Wall -I../
|
||||
|
||||
|
||||
all : Date.o Entry.o FileEntry.o VolumeEntry.o
|
||||
|
||||
Date.o : Date.cpp Date.h
|
||||
|
||||
Entry.o : Entry.cpp File.h
|
||||
|
||||
FileEntry.o : FileEntry.cpp File.h Date.h
|
||||
|
||||
VolumeEntry.o : VolumeEntry.cpp File.h Date.h
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
CC = g++
|
||||
CPPFLAGS += -Wall -O2 -g -fexceptions \
|
||||
-D__FreeBSD__=10 \
|
||||
-D_FILE_OFFSET_BITS=64 \
|
||||
-D__DARWIN_64_BIT_INO_T=1 \
|
||||
-DHAVE_STAT_BIRTHTIME
|
||||
|
||||
LDFLAGS += -L/usr/local/lib -lfuse_ino64
|
||||
|
||||
|
||||
newfs_pascal: \
|
||||
newfs_pascal.o \
|
||||
Date.o \
|
||||
File.o \
|
||||
../Exception.o \
|
||||
../BlockDevice.o \
|
||||
../BlockCache.o \
|
||||
../DiskCopy42Image.o \
|
||||
../RawDevice.o \
|
||||
../MappedFile.o \
|
||||
../Buffer.o
|
||||
|
||||
|
||||
fileman : \
|
||||
FileMan.o \
|
||||
Date.o \
|
||||
File.o \
|
||||
../Exception.o \
|
||||
../BlockDevice.o \
|
||||
../BlockCache.o \
|
||||
../DiskCopy42Image.o \
|
||||
../RawDevice.o \
|
||||
../MappedFile.o \
|
||||
../Buffer.o
|
||||
|
||||
|
||||
profuse_pascal : \
|
||||
profuse_pascal.o \
|
||||
profuse_pascal_ops.o \
|
||||
Date.o \
|
||||
File.o \
|
||||
../Exception.o \
|
||||
../BlockDevice.o \
|
||||
../BlockCache.o \
|
||||
../DiskCopy42Image.o \
|
||||
../RawDevice.o \
|
||||
../MappedFile.o \
|
||||
../Buffer.o
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __PASCAL_FILE_H__
|
||||
#define __PASCAL_FILE_H__
|
||||
|
||||
#include <Pascal/Date.h>
|
||||
#include <Pascal/Entry.h>
|
||||
#include <Pascal/FileEntry.h>
|
||||
#include <Pascal/VolumeEntry.h>
|
||||
|
||||
#endif
|
|
@ -0,0 +1,92 @@
|
|||
|
||||
|
||||
|
||||
class TextFile {
|
||||
|
||||
public:
|
||||
|
||||
|
||||
unsigned size() const;
|
||||
|
||||
unsigned read(void *buffer, unsigned size, unsigned offset);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
static unsigned decodeBlock(uint8_t *in, unsigned inSize, uint8_t *out);
|
||||
|
||||
unsigned _size;
|
||||
std::vector<unsigned> _pageSize
|
||||
};
|
||||
|
||||
|
||||
unsigned decodeBlock(uint8_t *in, unsigned inSize, uint8_t *out)
|
||||
{
|
||||
const unsigned DLE = 16;
|
||||
|
||||
unsigned size = 0;
|
||||
|
||||
for (unsigned i = 0; i < inSize; ++i)
|
||||
{
|
||||
uint8_t c = in[i];
|
||||
if (!c) break;
|
||||
|
||||
if ((c == DLE) && (i + 1 < inSize))
|
||||
{
|
||||
unsigned x = in[++i] - 32;
|
||||
|
||||
if (out)
|
||||
{
|
||||
for (unsigned i = 0; i < x; ++i)
|
||||
*out++ = ' ';
|
||||
}
|
||||
size += x;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out) *out++ = c;
|
||||
++size;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
|
||||
// first 2 blocks are header information
|
||||
|
||||
_fileSize = 0;
|
||||
|
||||
unsigned pages = (_endBlock - _startBlock - 2) >> 1
|
||||
uint8_t buffer[1024];
|
||||
unsigned offset = 0;
|
||||
|
||||
for (unsigned i _startBlock + 2; i <= _endBlock; i += 2)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
unsigned dataSize = 0;
|
||||
unsigned offset = 0;
|
||||
unsigned size;
|
||||
|
||||
// load a 2-block page.
|
||||
for (j = i; j <= _endBlock; ++j)
|
||||
{
|
||||
_device->readBlock(j, buffer + offset);
|
||||
dataSize += j == _endBlock ? _lastByte : 512;
|
||||
|
||||
}
|
||||
|
||||
size = decodeBlock(buffer, dataSize, NULL);
|
||||
|
||||
_pageSize.push_back(size);
|
||||
_fileSize += size;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
#include <Pascal/TextWriter.h>
|
||||
#include <Pascal/FileEntry.h>
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
using namespace Pascal;
|
||||
|
||||
|
||||
TextWriter::TextWriter()
|
||||
{
|
||||
_offset = 0;
|
||||
_current = new uint8_t[1024];
|
||||
_blocks.push_back(new uint8_t[1024]); // 1024k for editor scratch data.
|
||||
|
||||
std::memset(_blocks.back(), 0, 1024);
|
||||
std::memset(_current, 0, 1024);
|
||||
}
|
||||
|
||||
TextWriter::~TextWriter()
|
||||
{
|
||||
std::vector<uint8_t *>::iterator iter;
|
||||
|
||||
if (_current) delete[] _current;
|
||||
|
||||
for (iter = _blocks.begin(); iter != _blocks.end(); ++iter)
|
||||
{
|
||||
delete[] *iter;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned TextWriter::blocks() const
|
||||
{
|
||||
if (_offset == 0) return _blocks.size() * 2;
|
||||
if (_offset <= 512) return _blocks.size() * 2 + 1;
|
||||
return _blocks.size() * 2 + 2;
|
||||
}
|
||||
|
||||
void *TextWriter::data(unsigned block) const
|
||||
{
|
||||
unsigned offset = (block & 0x01) * 512;
|
||||
unsigned halfBlock = block >> 1;
|
||||
|
||||
if (halfBlock < _blocks.size())
|
||||
{
|
||||
return _blocks[halfBlock] + offset;
|
||||
}
|
||||
if (halfBlock == _blocks.size())
|
||||
{
|
||||
if (offset > _offset) return NULL;
|
||||
return _current + offset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TextWriter::writeLine(const char *line)
|
||||
{
|
||||
writeLine(line, std::strlen(line));
|
||||
}
|
||||
|
||||
void TextWriter::writeLine(const char *line, unsigned length)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "TextWriter::writeLine"
|
||||
|
||||
|
||||
if (line == NULL) line = "";
|
||||
|
||||
std::string text(line, length);
|
||||
|
||||
|
||||
if (length)
|
||||
{
|
||||
char c = text[length - 1];
|
||||
if (c == 0x0a) text[length - 1] = 0x0d;
|
||||
else if (c != 0x0d) text.push_back(0x0d);
|
||||
|
||||
FileEntry::Compress(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.push_back(0x0d);
|
||||
}
|
||||
|
||||
length = text.length();
|
||||
|
||||
if (length > 1024)
|
||||
{
|
||||
throw ::Exception(__METHOD__ ": String is too long.");
|
||||
}
|
||||
if (_offset + length > 1024)
|
||||
{
|
||||
_blocks.push_back(_current);
|
||||
_offset = 0;
|
||||
_current = new uint8_t[1024];
|
||||
std::memset(_current, 0, 1024);
|
||||
}
|
||||
|
||||
std::memcpy(_current + _offset, text.data(), length);
|
||||
_offset += length;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#ifndef __PASCAL_TEXTWRITER_H__
|
||||
#define __PASCAL_TEXTWRITER_H__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Pascal {
|
||||
|
||||
|
||||
class TextWriter {
|
||||
|
||||
public:
|
||||
|
||||
TextWriter();
|
||||
~TextWriter();
|
||||
|
||||
unsigned blocks() const;
|
||||
|
||||
void *data(unsigned block) const;
|
||||
|
||||
void writeLine(const char *);
|
||||
void writeLine(const char *, unsigned length);
|
||||
|
||||
private:
|
||||
|
||||
std::vector<uint8_t *> _blocks;
|
||||
unsigned _offset;
|
||||
|
||||
uint8_t *_current;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,116 @@
|
|||
#ifndef __PASCAL_VOLUMEENTRY_H__
|
||||
#define __PASCAL_VOLUMEENTRY_H__
|
||||
|
||||
#include <Pascal/Entry.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
namespace Pascal {
|
||||
|
||||
|
||||
class VolumeEntry : public Entry {
|
||||
|
||||
public:
|
||||
|
||||
static unsigned ValidName(const char *);
|
||||
|
||||
static VolumeEntryPointer Open(Device::BlockDevicePointer);
|
||||
static VolumeEntryPointer Create(Device::BlockDevicePointer, const char *name);
|
||||
|
||||
//
|
||||
|
||||
virtual ~VolumeEntry();
|
||||
|
||||
const char *name() const { return _fileName; }
|
||||
unsigned fileCount() const { return _fileCount; }
|
||||
unsigned volumeBlocks() const { return _lastVolumeBlock; }
|
||||
|
||||
Pascal::Date lastBoot() const { return _lastBoot; }
|
||||
|
||||
unsigned freeBlocks(bool krunched = false) const;
|
||||
unsigned maxContiguousBlocks() const;
|
||||
bool canKrunch() const;
|
||||
|
||||
|
||||
FileEntryPointer fileAtIndex(unsigned i) const;
|
||||
FileEntryPointer fileByName(const char *name) const;
|
||||
|
||||
|
||||
void *loadBlock(unsigned block);
|
||||
void unloadBlock(unsigned block, bool dirty = false);
|
||||
|
||||
void readBlock(unsigned block, void *);
|
||||
void writeBlock(unsigned block, void *);
|
||||
|
||||
void sync();
|
||||
|
||||
bool readOnly() { return _device->readOnly(); }
|
||||
|
||||
int unlink(const char *name);
|
||||
int rename(const char *oldName, const char *newName);
|
||||
int copy(const char *oldName, const char *newName);
|
||||
FileEntryPointer create(const char *name, unsigned blocks);
|
||||
|
||||
|
||||
int krunch();
|
||||
|
||||
|
||||
protected:
|
||||
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
|
||||
|
||||
private:
|
||||
|
||||
friend class FileEntry;
|
||||
|
||||
|
||||
VolumeEntry();
|
||||
VolumeEntry(Device::BlockDevicePointer, const char *name);
|
||||
VolumeEntry(Device::BlockDevicePointer);
|
||||
|
||||
VolumeEntryPointer thisPointer()
|
||||
{
|
||||
return STATIC_POINTER_CAST(VolumeEntry, shared_from_this());
|
||||
}
|
||||
|
||||
void init(void *);
|
||||
void setParents();
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t *readDirectoryHeader();
|
||||
void writeDirectoryHeader(void *);
|
||||
|
||||
uint8_t *readBlocks(unsigned startingBlock, unsigned count);
|
||||
void writeBlocks(void *buffer, unsigned startingBlock, unsigned count);
|
||||
|
||||
void writeEntry(FileEntry *e);
|
||||
void writeEntry();
|
||||
|
||||
void calcMaxFileSize();
|
||||
|
||||
|
||||
unsigned _fileNameLength;
|
||||
char _fileName[8];
|
||||
unsigned _lastVolumeBlock;
|
||||
unsigned _fileCount;
|
||||
unsigned _accessTime;
|
||||
Pascal::Date _lastBoot;
|
||||
|
||||
std::vector<FileEntryPointer> _files;
|
||||
unsigned _inodeGenerator;
|
||||
|
||||
Device::BlockDevicePointer _device;
|
||||
Device::BlockCachePointer _cache;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
#include <cstring>
|
||||
|
||||
#include <ProDOS/Bitmap.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Cache/BlockCache.h>
|
||||
|
||||
using namespace ProDOS;
|
||||
|
||||
// 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;
|
||||
|
||||
_bitmap.reserve(bitmapSize);
|
||||
|
||||
// mark blocks as free..
|
||||
_bitmap.resize(blocks / 8, 0xff);
|
||||
|
||||
// edge case...
|
||||
|
||||
if (blocks & 0x0f)
|
||||
{
|
||||
_bitmap.push_back( ~(0xff >> (blocks & 0x0f)) );
|
||||
}
|
||||
|
||||
// mark any trailing blocks as in use.
|
||||
|
||||
_bitmap.resize(bitmapSize, 0x00);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks)
|
||||
{
|
||||
_blocks = blocks;
|
||||
_freeBlocks = 0;
|
||||
_freeIndex = 0;
|
||||
|
||||
_bitmapBlocks = (blocks + 4095) / 4096;
|
||||
|
||||
unsigned bitmapSize = _bitmapBlocks * 512;
|
||||
unsigned blockSize = blocks / 8;
|
||||
|
||||
_bitmap.reserve(bitmapSize);
|
||||
|
||||
|
||||
// load the full block(s).
|
||||
for (unsigned i = 0; i < blockSize; ++i)
|
||||
{
|
||||
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
|
||||
|
||||
_bitmap.insert(_bitmap.end(), buffer, buffer + 512);
|
||||
|
||||
cache->release(keyPointer);
|
||||
|
||||
keyPointer++;
|
||||
}
|
||||
|
||||
// and any remaining partial block.
|
||||
|
||||
if (blocks & 4095)
|
||||
{
|
||||
|
||||
uint8_t *buffer = (uint8_t *)cache->acquire(keyPointer);
|
||||
|
||||
unsigned bits = blocks & 4095;
|
||||
unsigned bytes = bits / 8;
|
||||
|
||||
//for (unsigned i = 0; i < bits / 8; ++i) _bitmap.push_back(buffer[i]);
|
||||
|
||||
_bitmap.insert(_bitmap.end(), buffer, buffer + bytes);
|
||||
// partial...
|
||||
|
||||
if (blocks & 0x0f)
|
||||
{
|
||||
uint8_t tmp = buffer[bytes];
|
||||
tmp &= ~(0xff >> (blocks & 0x0f));
|
||||
|
||||
_bitmap.push_back(tmp);
|
||||
}
|
||||
|
||||
// remainder set to in use.
|
||||
_bitmap.resize(bitmapSize, 0x00);
|
||||
|
||||
cache->release(keyPointer);
|
||||
|
||||
keyPointer++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// now set _freeBlocks and _freeIndex;
|
||||
std::vector<uint8_t>::iterator iter;
|
||||
|
||||
_freeIndex = -1;
|
||||
for (iter = _bitmap.begin(); iter != _bitmap.end(); ++iter)
|
||||
{
|
||||
_freeBlocks += popCount(*iter);
|
||||
if (_freeIndex == -1 && *iter)
|
||||
_freeIndex = std::distance(_bitmap.begin(), iter);
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap::~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 freeIndex = _freeIndex;
|
||||
unsigned maxIndex = (_blocks + 7) / 8;
|
||||
|
||||
|
||||
for (unsigned index = _freeIndex; index < maxIndex; ++index)
|
||||
{
|
||||
uint8_t tmp = _bitmap[index];
|
||||
if (!tmp) continue;
|
||||
|
||||
unsigned mask = 0x80;
|
||||
for (unsigned offset = 0; offset < 8; ++offset)
|
||||
{
|
||||
if (tmp & mask)
|
||||
{
|
||||
_freeIndex = index;
|
||||
_bitmap[index] = tmp & ~mask;
|
||||
--_freeBlocks;
|
||||
return index * 8 + offset;
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned index = 0; index < freeIndex; ++index)
|
||||
{
|
||||
uint8_t tmp = _bitmap[index];
|
||||
if (!tmp) continue;
|
||||
|
||||
unsigned mask = 0x80;
|
||||
for (unsigned offset = 0; offset < 8; ++offset)
|
||||
{
|
||||
if (tmp & mask)
|
||||
{
|
||||
_freeIndex = index;
|
||||
_bitmap[index] = tmp & ~mask;
|
||||
--_freeBlocks;
|
||||
return index * 8 + offset;
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// should never happen...
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef __BITMAP_H__
|
||||
#define __BITMAP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Device
|
||||
{
|
||||
class BlockDevice;
|
||||
class BlockCache;
|
||||
}
|
||||
|
||||
|
||||
namespace ProDOS {
|
||||
|
||||
|
||||
class Bitmap {
|
||||
public:
|
||||
|
||||
Bitmap(unsigned blocks);
|
||||
Bitmap(Device::BlockCache *cache, unsigned keyPointer, unsigned blocks);
|
||||
|
||||
~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[0]; }
|
||||
|
||||
private:
|
||||
|
||||
unsigned _freeIndex;
|
||||
unsigned _freeBlocks;
|
||||
|
||||
unsigned _blocks;
|
||||
unsigned _bitmapBlocks;
|
||||
|
||||
std::vector<uint8_t> _bitmap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,11 +1,12 @@
|
|||
#include "DateTime.h"
|
||||
|
||||
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
|
||||
namespace ProDOS {
|
||||
#include <ProDOS/DateTime.h>
|
||||
|
||||
using namespace ProDOS;
|
||||
|
||||
/*
|
||||
* date is a 16 bit value:
|
||||
|
@ -141,4 +142,3 @@ std::time_t DateTime::toUnix() const
|
|||
// convert back via locatime & fudge for dst?
|
||||
}
|
||||
|
||||
} // namespace
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
#include "Disk.h"
|
||||
#include "DiskCopy42.h"
|
||||
#include "UniversalDiskImage.h"
|
||||
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -17,13 +16,15 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <Endian/Endian.h>
|
||||
|
||||
struct ucmp
|
||||
{
|
||||
bool operator()(unsigned a, unsigned b) const
|
||||
|
@ -35,127 +36,32 @@ struct ucmp
|
|||
using std::set;
|
||||
using std::vector;
|
||||
|
||||
using namespace LittleEndian;
|
||||
|
||||
typedef set<unsigned, ucmp> uset;
|
||||
|
||||
Disk::Disk()
|
||||
{
|
||||
_data = (uint8_t *)-1;
|
||||
_blocks = 0;
|
||||
_offset = 0;
|
||||
_size = 0;
|
||||
_flags = 0;
|
||||
|
||||
}
|
||||
|
||||
Disk::~Disk()
|
||||
{
|
||||
if (_data != (uint8_t *)-1)
|
||||
munmap(_data, _size);
|
||||
}
|
||||
|
||||
|
||||
Disk *Disk::OpenFile(const char *file, unsigned flags)
|
||||
Disk::Disk(Device::BlockDevicePointer device) :
|
||||
_device(device)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
size_t size;
|
||||
unsigned blocks;
|
||||
|
||||
unsigned offset;
|
||||
|
||||
|
||||
void *map;
|
||||
Disk *d = NULL;
|
||||
|
||||
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd >= 0)
|
||||
{
|
||||
|
||||
if (fstat(fd, &st) == 0)
|
||||
{
|
||||
size = st.st_size;
|
||||
|
||||
// raw disk images must be a blocksize multiple and <= 32 Meg.
|
||||
|
||||
if ( (size & 0x1ff) == 0
|
||||
&& size > 0
|
||||
&& size <= 32 * 1024 * 1024
|
||||
)
|
||||
{
|
||||
blocks = size >> 9;
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
|
||||
// check for disk copy4.2 / universal disk image.
|
||||
uint8_t buffer[1024];
|
||||
|
||||
if (read(fd, buffer, 1024) != 1024)
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
do {
|
||||
|
||||
DiskCopy42 dc;
|
||||
|
||||
if (dc.Load(buffer)
|
||||
&& size == 84 + dc.data_size + dc.tag_size
|
||||
&& (dc.data_size & 0x1ff) == 0)
|
||||
{
|
||||
offset = 84;
|
||||
blocks = dc.data_size >> 9;
|
||||
ok = true;
|
||||
flags |= P8_DC42;
|
||||
break;
|
||||
}
|
||||
|
||||
UniversalDiskImage udi;
|
||||
|
||||
if (udi.Load(buffer)
|
||||
//&& udi.version == 1
|
||||
&& udi.image_format == UDI_FORMAT_PRODOS_ORDER)
|
||||
{
|
||||
|
||||
blocks = udi.data_blocks;
|
||||
offset = udi.data_offset;
|
||||
ok = true;
|
||||
flags |= P8_2MG;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (false);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
map = mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
|
||||
if (map != (void *)-1)
|
||||
{
|
||||
d = new Disk();
|
||||
d->_size = size;
|
||||
d->_data = (uint8_t *)map;
|
||||
d->_blocks = blocks;
|
||||
d->_offset = offset;
|
||||
d->_flags = flags;
|
||||
}
|
||||
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return d;
|
||||
_blocks = _device->blocks();
|
||||
}
|
||||
|
||||
DiskPointer Disk::OpenFile(Device::BlockDevicePointer device)
|
||||
{
|
||||
DiskPointer disk(new Disk(device));
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
||||
// load the mini entry into the regular entry.
|
||||
int Disk::Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee)
|
||||
|
@ -200,31 +106,11 @@ int Disk::Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee)
|
|||
|
||||
int Disk::Read(unsigned block, void *buffer)
|
||||
{
|
||||
if (block > _blocks) return -P8_INVALID_BLOCK;
|
||||
|
||||
|
||||
if (_flags & P8_DOS_ORDER)
|
||||
{
|
||||
static unsigned do_map[] = {0x00, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0f };
|
||||
|
||||
unsigned track = (block & ~0x07) << 9;
|
||||
unsigned sector = (block & 0x07) << 1;
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
unsigned offset = track | (do_map[sector+i] << 8);
|
||||
|
||||
memcpy(buffer, _data + _offset + offset, 256);
|
||||
|
||||
buffer = (char *)buffer + 256;
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (block > _blocks) return -P8_INVALID_BLOCK;
|
||||
|
||||
_device->read(block, buffer);
|
||||
|
||||
memcpy(buffer, _data + _offset + (block << 9), BLOCK_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -321,6 +207,9 @@ void *Disk::ReadFile(const FileEntry &f, unsigned fork, uint32_t *size, int *err
|
|||
case TREE_FILE:
|
||||
ok = ReadIndex(f.key_pointer, buffer, 2, 0, blocks);
|
||||
break;
|
||||
|
||||
default:
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (ok < 0)
|
||||
|
@ -458,8 +347,8 @@ int Disk::ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files)
|
|||
|
||||
if (ok < 0) return ok;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
prev = Read16(&buffer[0x00]);
|
||||
next = Read16(&buffer[0x02]);
|
||||
|
||||
VolumeEntry v;
|
||||
v.Load(buffer + 0x04);
|
||||
|
@ -505,8 +394,8 @@ int Disk::ReadVolume(VolumeEntry *volume, std::vector<FileEntry> *files)
|
|||
if (ok < 0) return ok;
|
||||
block = next;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
prev = Read16(&buffer[0x00]);
|
||||
next = Read16(&buffer[0x02]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
|
@ -535,8 +424,8 @@ int Disk::ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry>
|
|||
|
||||
if (ok < 0) return ok;
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
prev = Read16(&buffer[0x00]);
|
||||
next = Read16(&buffer[0x02]);
|
||||
|
||||
SubdirEntry v;
|
||||
v.Load(buffer + 0x04);
|
||||
|
@ -585,8 +474,8 @@ int Disk::ReadDirectory(unsigned block, SubdirEntry *dir, std::vector<FileEntry>
|
|||
block = next;
|
||||
|
||||
|
||||
prev = load16(&buffer[0x00]);
|
||||
next = load16(&buffer[0x02]);
|
||||
prev = Read16(&buffer[0x00]);
|
||||
next = Read16(&buffer[0x02]);
|
||||
|
||||
index = 0;
|
||||
}
|
|
@ -13,7 +13,10 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "File.h"
|
||||
#include <ProDOS/File.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <tr1/memory>
|
||||
|
||||
|
||||
enum {
|
||||
|
@ -40,6 +43,8 @@ enum {
|
|||
|
||||
};
|
||||
|
||||
class Disk;
|
||||
typedef std::tr1::shared_ptr<Disk> DiskPointer;
|
||||
|
||||
class Disk {
|
||||
|
||||
|
@ -47,7 +52,7 @@ public:
|
|||
~Disk();
|
||||
|
||||
//static Disk *Open2MG(const char *file);
|
||||
static Disk *OpenFile(const char *file, unsigned flags);
|
||||
static DiskPointer OpenFile(Device::BlockDevicePointer device);
|
||||
|
||||
|
||||
int Normalize(FileEntry &f, unsigned fork, ExtendedEntry *ee = NULL);
|
||||
|
@ -65,13 +70,14 @@ public:
|
|||
|
||||
private:
|
||||
Disk();
|
||||
uint8_t *_data;
|
||||
unsigned _offset;
|
||||
unsigned _blocks;
|
||||
size_t _size;
|
||||
Disk(Device::BlockDevicePointer device);
|
||||
|
||||
unsigned _flags;
|
||||
unsigned _blocks;
|
||||
|
||||
Device::BlockDevicePointer _device;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
|
||||
#include "Exception.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace ProDOS {
|
||||
|
||||
|
||||
|
||||
const char *Exception::errorString()
|
||||
{
|
||||
|
||||
switch (error())
|
||||
{
|
||||
case badSystemCall:
|
||||
return "Bad System Call";
|
||||
case invalidPcount:
|
||||
return "Invalid Parameter Count";
|
||||
case gsosActive:
|
||||
return "GS/OS Active";
|
||||
case devNotFound:
|
||||
return "Device Not Found";
|
||||
case invalidDevNum:
|
||||
return "Invalid Device Number";
|
||||
case drvrBadReq:
|
||||
return "Driver Bad Request";
|
||||
case drvrBadCode:
|
||||
return "Driver Bad Code";
|
||||
case drvrBadParm:
|
||||
return "Driver Bad Parameter";
|
||||
case drvrNotOpen:
|
||||
return "Driver Not Open";
|
||||
case drvrPriorOpen:
|
||||
return "Driver Prior Open";
|
||||
case irqTableFull:
|
||||
return "IRQ Table Full";
|
||||
case drvrNoResrc:
|
||||
return "Driver No Resource";
|
||||
case drvrIOError:
|
||||
return "Driver IO Error";
|
||||
case drvrNoDevice:
|
||||
return "Driver No Device";
|
||||
case drvrBusy:
|
||||
return "Driver Busy";
|
||||
case drvrWrtProt:
|
||||
return "Driver Write Protected";
|
||||
case drvrBadCount:
|
||||
return "Driver Bad Count";
|
||||
case drvrBadBlock:
|
||||
return "Driver Bad Block";
|
||||
case drvrDiskSwitch:
|
||||
return "Driver Disk Switch";
|
||||
case drvrOffLine:
|
||||
return "Driver Off Line";
|
||||
case badPathSyntax:
|
||||
return "Bad Path Syntax";
|
||||
case invalidRefNum:
|
||||
return "Invalid Ref Num";
|
||||
case pathNotFound:
|
||||
return "Path Not Found";
|
||||
case volNotFound:
|
||||
return "Volume Not Found";
|
||||
case fileNotFound:
|
||||
return "File Not Found";
|
||||
case dupPathName:
|
||||
return "Duplicate Path Name";
|
||||
case volumeFull:
|
||||
return "Volume Full";
|
||||
case volDirFull:
|
||||
return "Volume Directory Full";
|
||||
case badFileFormat:
|
||||
return "Bad File Format";
|
||||
case badStoreType:
|
||||
return "Bad Storage Type";
|
||||
case eofEncountered:
|
||||
return "End of File";
|
||||
case outOfRange:
|
||||
return "Out of Range";
|
||||
case invalidAccess:
|
||||
return "Invalid Access";
|
||||
case buffTooSmall:
|
||||
return "Buffer Too Small";
|
||||
case fileBusy:
|
||||
return "File Busy";
|
||||
case dirError:
|
||||
return "Directory Error";
|
||||
case unknownVol:
|
||||
return "Unknown Volume";
|
||||
case paramRangeError:
|
||||
return "Parameter Range Error";
|
||||
case outOfMem:
|
||||
return "Out of Memory";
|
||||
case dupVolume:
|
||||
return "Duplicate Volume";
|
||||
case notBlockDev:
|
||||
return "Not a Block Device";
|
||||
case invalidLevel:
|
||||
return "Invalid Level";
|
||||
case damagedBitMap:
|
||||
return "Damaged Bit Map";
|
||||
case badPathNames:
|
||||
return "Bad Path Names";
|
||||
case notSystemFile:
|
||||
return "Not a System File";
|
||||
case osUnsupported:
|
||||
return "OS Unsupported";
|
||||
case stackOverflow:
|
||||
return "Stack Overflow";
|
||||
case dataUnavail:
|
||||
return "Data Unavailable";
|
||||
case endOfDir:
|
||||
return "End Of Directory";
|
||||
case invalidClass:
|
||||
return "Invalid Class";
|
||||
case resForkNotFound:
|
||||
return "Resource Fork Not Found";
|
||||
case invalidFSTID:
|
||||
return "Invalid FST ID";
|
||||
case devNameErr:
|
||||
return "Device Name Error";
|
||||
case resExistsErr:
|
||||
return "Resource Exists Error";
|
||||
case resAddErr:
|
||||
return "Resource Add Error";
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef __PRODOS_EXCEPTION_H__
|
||||
#define __PRODOS_EXCEPTION_H__
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
namespace ProDOS {
|
||||
|
||||
|
||||
// ProDOS Errors
|
||||
enum
|
||||
{
|
||||
badSystemCall = 0x01,
|
||||
invalidPcount = 0x04,
|
||||
gsosActive = 0x07,
|
||||
devNotFound = 0x10,
|
||||
invalidDevNum = 0x11,
|
||||
drvrBadReq = 0x20,
|
||||
drvrBadCode = 0x21,
|
||||
drvrBadParm = 0x22,
|
||||
drvrNotOpen = 0x23,
|
||||
drvrPriorOpen = 0x24,
|
||||
irqTableFull = 0x25,
|
||||
drvrNoResrc = 0x26,
|
||||
drvrIOError = 0x27,
|
||||
drvrNoDevice = 0x28,
|
||||
drvrBusy = 0x29,
|
||||
drvrWrtProt = 0x2b,
|
||||
drvrBadCount = 0x2c,
|
||||
drvrBadBlock = 0x2d,
|
||||
drvrDiskSwitch = 0x2e,
|
||||
drvrOffLine = 0x2f,
|
||||
badPathSyntax = 0x40,
|
||||
invalidRefNum = 0x43,
|
||||
pathNotFound = 0x44,
|
||||
volNotFound = 0x45,
|
||||
fileNotFound = 0x46,
|
||||
dupPathName = 0x47,
|
||||
volumeFull = 0x48,
|
||||
volDirFull = 0x49,
|
||||
badFileFormat = 0x4a,
|
||||
badStoreType = 0x4b,
|
||||
eofEncountered = 0x4c,
|
||||
outOfRange = 0x4d,
|
||||
invalidAccess = 0x4e,
|
||||
buffTooSmall = 0x4f,
|
||||
fileBusy = 0x50,
|
||||
dirError = 0x51,
|
||||
unknownVol = 0x52,
|
||||
paramRangeError = 0x53,
|
||||
outOfMem = 0x54,
|
||||
dupVolume = 0x57,
|
||||
notBlockDev = 0x58,
|
||||
invalidLevel = 0x59,
|
||||
damagedBitMap = 0x5a,
|
||||
badPathNames = 0x5b,
|
||||
notSystemFile = 0x5c,
|
||||
osUnsupported = 0x5d,
|
||||
stackOverflow = 0x5f,
|
||||
dataUnavail = 0x60,
|
||||
endOfDir = 0x61,
|
||||
invalidClass = 0x62,
|
||||
resForkNotFound = 0x63,
|
||||
invalidFSTID = 0x64,
|
||||
devNameErr = 0x67,
|
||||
resExistsErr = 0x70,
|
||||
resAddErr = 0x71
|
||||
};
|
||||
|
||||
|
||||
class Exception : public ::Exception {
|
||||
public:
|
||||
Exception(const char *cp, int error);
|
||||
Exception(const std::string& string, int error);
|
||||
|
||||
virtual const char *errorString();
|
||||
|
||||
private:
|
||||
typedef ::Exception super;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline Exception::Exception(const char *cp, int error) :
|
||||
super(cp, error)
|
||||
{
|
||||
}
|
||||
|
||||
inline Exception::Exception(const std::string& string, int error) :
|
||||
super(string, error)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,15 +6,18 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "File.h"
|
||||
#include <ProDOS/File.h>
|
||||
#include <ProDOS/DateTime.h>
|
||||
#include <Endian/Endian.h>
|
||||
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
|
||||
#include "DateTime.h"
|
||||
#include <stdint.h>
|
||||
|
||||
using namespace LittleEndian;
|
||||
|
||||
|
||||
bool FileEntry::Load(const void *data)
|
||||
|
@ -31,18 +34,18 @@ bool FileEntry::Load(const void *data)
|
|||
|
||||
file_type = cp[0x10];
|
||||
|
||||
key_pointer = load16(&cp[0x11]);
|
||||
key_pointer = Read16(&cp[0x11]);
|
||||
|
||||
blocks_used = load16(&cp[0x13]);
|
||||
blocks_used = Read16(&cp[0x13]);
|
||||
|
||||
eof = load24(&cp[0x15]);
|
||||
eof = Read24(&cp[0x15]);
|
||||
|
||||
creation = ProDOS::DateTime(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
|
||||
unsigned xcase = load16(&cp[0x1c]);
|
||||
unsigned xcase = Read16(&cp[0x1c]);
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
// gsos technote #8
|
||||
|
@ -59,11 +62,11 @@ bool FileEntry::Load(const void *data)
|
|||
access = cp[0x1e];
|
||||
|
||||
|
||||
aux_type = load16(&cp[0x1f]);
|
||||
aux_type = Read16(&cp[0x1f]);
|
||||
|
||||
last_mod = ProDOS::DateTime(load16(&cp[0x21]), load16(&cp[0x23]));
|
||||
last_mod = ProDOS::DateTime(Read16(&cp[0x21]), Read16(&cp[0x23]));
|
||||
|
||||
header_pointer = load16(&cp[0x25]);
|
||||
header_pointer = Read16(&cp[0x25]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -79,16 +82,16 @@ bool ExtendedEntry::Load(const void *data)
|
|||
// offset 0 - mini entry for data fork
|
||||
|
||||
dataFork.storage_type = cp[0x00] & 0x0f;
|
||||
dataFork.key_block = load16(&cp[0x01]);
|
||||
dataFork.blocks_used = load16(&cp[0x03]);
|
||||
dataFork.eof = load24(&cp[0x05]);
|
||||
dataFork.key_block = Read16(&cp[0x01]);
|
||||
dataFork.blocks_used = Read16(&cp[0x03]);
|
||||
dataFork.eof = Read24(&cp[0x05]);
|
||||
|
||||
// offset 256 - mini entry for resource fork.
|
||||
|
||||
resourceFork.storage_type = cp[256 + 0x00] & 0x0f;
|
||||
resourceFork.key_block = load16(&cp[256 + 0x01]);
|
||||
resourceFork.blocks_used = load16(&cp[256 + 0x03]);
|
||||
resourceFork.eof = load24(&cp[256 + 0x05]);
|
||||
resourceFork.key_block = Read16(&cp[256 + 0x01]);
|
||||
resourceFork.blocks_used = Read16(&cp[256 + 0x03]);
|
||||
resourceFork.eof = Read24(&cp[256 + 0x05]);
|
||||
|
||||
// xFInfo may be missing.
|
||||
bzero(FInfo, sizeof(FInfo));
|
||||
|
@ -135,15 +138,15 @@ bool VolumeEntry::Load(const void *data)
|
|||
|
||||
// 0x14--0x1b reserved
|
||||
|
||||
creation = ProDOS::DateTime(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
last_mod = ProDOS::DateTime(load16(&cp[0x12]), load16(&cp[0x14]));
|
||||
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
|
||||
last_mod = ProDOS::DateTime(Read16(&cp[0x12]), Read16(&cp[0x14]));
|
||||
|
||||
if (last_mod == 0) last_mod = creation;
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
|
||||
unsigned xcase = load16(&cp[0x16]);
|
||||
unsigned xcase = Read16(&cp[0x16]);
|
||||
if (xcase & 0x8000)
|
||||
{
|
||||
// gsos technote #8
|
||||
|
@ -162,11 +165,11 @@ bool VolumeEntry::Load(const void *data)
|
|||
|
||||
entries_per_block = cp[0x20];
|
||||
|
||||
file_count = load16(&cp[0x21]);
|
||||
file_count = Read16(&cp[0x21]);
|
||||
|
||||
bit_map_pointer = load16(&cp[0x23]);
|
||||
bit_map_pointer = Read16(&cp[0x23]);
|
||||
|
||||
total_blocks = load16(&cp[0x25]);
|
||||
total_blocks = Read16(&cp[0x25]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -192,7 +195,7 @@ bool SubdirEntry::Load(const void *data)
|
|||
|
||||
// 0x145-0x1b reserved
|
||||
|
||||
creation = ProDOS::DateTime(load16(&cp[0x18]), load16(&cp[0x1a]));
|
||||
creation = ProDOS::DateTime(Read16(&cp[0x18]), Read16(&cp[0x1a]));
|
||||
|
||||
//version = cp[0x1c];
|
||||
//min_version = cp[0x1d];
|
||||
|
@ -216,9 +219,9 @@ bool SubdirEntry::Load(const void *data)
|
|||
|
||||
entries_per_block = cp[0x20];
|
||||
|
||||
file_count = load16(&cp[0x21]);
|
||||
file_count = Read16(&cp[0x21]);
|
||||
|
||||
parent_pointer = load16(&cp[0x23]);
|
||||
parent_pointer = Read16(&cp[0x23]);
|
||||
|
||||
parent_entry = cp[0x25];
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef __FILE_H__
|
||||
#define __FILE_H__
|
||||
#ifndef __PRODOS_FILE_H__
|
||||
#define __PRODOS_FILE_H__
|
||||
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* common.h
|
||||
* ProFUSE
|
||||
*
|
||||
* Created by Kelvin Sherlock on 12/20/08.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BLOCK_SIZE 512
|
||||
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* UniversalDiskImage.cpp
|
||||
* profuse
|
||||
*
|
||||
* Created by Kelvin Sherlock on 1/6/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "UniversalDiskImage.h"
|
||||
|
||||
#include "common.h"
|
||||
#include <string.h>
|
||||
|
||||
bool UniversalDiskImage::Load(const uint8_t *buffer)
|
||||
{
|
||||
if (strncmp((const char *)buffer, "2IMG", 4) != 0) return false;
|
||||
|
||||
// all numbers little-endian.
|
||||
|
||||
magic_word = load32(&buffer[0]);
|
||||
|
||||
creator = load32(&buffer[0x04]);
|
||||
|
||||
header_size = load16(&buffer[0x08]);
|
||||
|
||||
version = load16(&buffer[0x0a]);
|
||||
|
||||
image_format = load32(&buffer[0x0c]);
|
||||
|
||||
flags = load32(&buffer[0x10]);
|
||||
|
||||
data_blocks = load32(&buffer[0x14]);
|
||||
|
||||
data_offset = load32(&buffer[0x18]);
|
||||
data_size = load32(&buffer[0x1c]);
|
||||
|
||||
|
||||
comment_offset = load32(&buffer[0x20]);
|
||||
comment_size = load32(&buffer[0x24]);
|
||||
|
||||
|
||||
|
||||
creator_data_offset = load32(&buffer[0x28]);
|
||||
creator_data_size = load32(&buffer[0x2c]);
|
||||
|
||||
// 16 bytes reserved.
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* UniversalDiskImage.h
|
||||
* profuse
|
||||
*
|
||||
* Created by Kelvin Sherlock on 1/6/09.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UNIVERSAL_DISK_IMAGE_H__
|
||||
#define __UNIVERSAL_DISK_IMAGE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define UDI_FORMAT_DOS_ORDER 0
|
||||
#define UDI_FORMAT_PRODOS_ORDER 1
|
||||
#define UDI_FORMAT_NIBBLIZED 2
|
||||
|
||||
struct UniversalDiskImage
|
||||
{
|
||||
bool Load(const uint8_t * buffer);
|
||||
|
||||
uint32_t magic_word;
|
||||
uint32_t creator;
|
||||
unsigned header_size;
|
||||
unsigned version;
|
||||
unsigned image_format;
|
||||
uint32_t flags;
|
||||
unsigned data_blocks;
|
||||
unsigned data_offset;
|
||||
unsigned data_size;
|
||||
unsigned comment_offset;
|
||||
unsigned comment_size;
|
||||
unsigned creator_data_offset;
|
||||
unsigned creator_data_size;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -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
|
|
@ -0,0 +1,179 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "Bitmap.h"
|
||||
#include "BlockDevice.h"
|
||||
#include "Buffer.h"
|
||||
#include "Endian.h"
|
||||
#include "Entry.h"
|
||||
#include "Exception.h"
|
||||
|
||||
using namespace ProFUSE;
|
||||
using namespace LittleEndian;
|
||||
|
||||
|
||||
#pragma mark VolumeDirectory
|
||||
|
||||
|
||||
|
||||
VolumeDirectory *VolumeDirectory::Create(const char *name, BlockDevice *device)
|
||||
{
|
||||
return new VolumeDirectory(name, device);
|
||||
}
|
||||
|
||||
VolumeDirectory *VolumeDirectory::Create(BlockDevice *device)
|
||||
{
|
||||
uint8_t block[512];
|
||||
// always block 2.
|
||||
|
||||
device->read(2, block);
|
||||
|
||||
return new VolumeDirectory(device, block);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
VolumeDirectory::VolumeDirectory(const char *name, BlockDevice *device) :
|
||||
Directory(VolumeHeader, name)
|
||||
{
|
||||
_totalBlocks = device->blocks();
|
||||
_bitmapPointer = 6;
|
||||
|
||||
_entryBlocks.push_back(2);
|
||||
_entryBlocks.push_back(3);
|
||||
_entryBlocks.push_back(4);
|
||||
_entryBlocks.push_back(5);
|
||||
|
||||
std::auto_ptr<Bitmap> bitmap(new Bitmap(_totalBlocks));
|
||||
|
||||
Buffer buffer(512);
|
||||
|
||||
// 2 bootcode blocks
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{
|
||||
bitmap->allocBlock(i);
|
||||
device->zeroBlock(i);
|
||||
}
|
||||
|
||||
//4 volume header blocks.
|
||||
for (unsigned i = 2; i < 6; ++i)
|
||||
{
|
||||
bitmap->allocBlock(i);
|
||||
|
||||
buffer.clear();
|
||||
// prev block, next block
|
||||
buffer.push16le(i == 2 ? 0 : i - 1);
|
||||
buffer.push16le(i == 5 ? 0 : i + 1);
|
||||
|
||||
if (i == 2)
|
||||
{
|
||||
// create the volume header.
|
||||
// all ivars must be set.
|
||||
write(&buffer);
|
||||
}
|
||||
|
||||
buffer.resize(512);
|
||||
device->write(i, buffer.buffer());
|
||||
}
|
||||
|
||||
|
||||
// allocate blocks for the bitmap itself
|
||||
unsigned bb = bitmap->bitmapBlocks();
|
||||
for (unsigned i = 0; i < bb; ++i)
|
||||
bitmap->allocBlock(_bitmapPointer + i);
|
||||
|
||||
// now write the bitmap...
|
||||
const uint8_t *bm = (const uint8_t *)bitmap->bitmap();
|
||||
for (unsigned i = 0; i < bb; ++i)
|
||||
{
|
||||
device->write(_bitmapPointer + i, 512 * i + bm);
|
||||
}
|
||||
|
||||
_device = device;
|
||||
_bitmap = bitmap.release();
|
||||
}
|
||||
|
||||
|
||||
VolumeDirectory::VolumeDirectory(BlockDevice *device, const void *bp) :
|
||||
Directory(bp),
|
||||
_modification(0,0)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "VolumeDirectory::VolumeDirectory"
|
||||
|
||||
// + 4 to skip over the block poitners.
|
||||
std::auto_ptr<Bitmap> bitmap;
|
||||
const void *vp = 4 + (const uint8_t *)bp;
|
||||
|
||||
|
||||
if (storageType() != VolumeHeader)
|
||||
throw ProDOSException(__METHOD__ ": Invalid storage type.", 0x4b);
|
||||
|
||||
|
||||
|
||||
_modification = DateTime(Read16(vp, 0x12), Read16(vp, 0x14));
|
||||
|
||||
_bitmapPointer = Read16(vp, 0x23);
|
||||
|
||||
_totalBlocks = Read16(vp, 0x25);
|
||||
|
||||
|
||||
// verify totalBlocks <= device->blocks() ?
|
||||
|
||||
if (_bitmapPointer >= _totalBlocks)
|
||||
throw ProDOSException(__METHOD__ ": Invalid bitmap pointer.", 0x5a);
|
||||
|
||||
|
||||
// bitmap pointer...
|
||||
bitmap.reset(new Bitmap(device, _bitmapPointer, _totalBlocks));
|
||||
|
||||
|
||||
// parse the directory header....
|
||||
|
||||
_bitmap = bitmap.release();
|
||||
}
|
||||
|
||||
VolumeDirectory::~VolumeDirectory()
|
||||
{
|
||||
if (_device)
|
||||
{
|
||||
_device->sync();
|
||||
delete _device;
|
||||
}
|
||||
delete _bitmap;
|
||||
}
|
||||
|
||||
void VolumeDirectory::write(Buffer *out)
|
||||
{
|
||||
out->push8((VolumeHeader << 4 ) | nameLength());
|
||||
out->pushBytes(namei(), 15);
|
||||
|
||||
// reserved. SOS uses 0x75 for the first byte [?]
|
||||
out->push8(0);
|
||||
out->push8(0);
|
||||
|
||||
// last mod
|
||||
out->push16le(_modification.date());
|
||||
out->push16le(_modification.time());
|
||||
|
||||
// filename case bits
|
||||
out->push16le(caseFlag());
|
||||
|
||||
// creation
|
||||
out->push16le(creation().date());
|
||||
out->push16le(creation().time());
|
||||
|
||||
out->push8(version());
|
||||
out->push8(minVersion());
|
||||
|
||||
|
||||
out->push8(access());
|
||||
out->push8(entryLength());
|
||||
out->push8(entriesPerBlock());
|
||||
out->push16le(fileCount());
|
||||
|
||||
out->push16le(_bitmapPointer);
|
||||
out->push16le(_totalBlocks);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,310 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define __FreeBSD__ 10
|
||||
#define __DARWIN_64_BIT_INO_T 1
|
||||
#endif
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define FUSE_USE_VERSION 27
|
||||
|
||||
#include <fuse/fuse_opt.h>
|
||||
#include <fuse/fuse_lowlevel.h>
|
||||
|
||||
#include <Pascal/Pascal.h>
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <File/File.h>
|
||||
|
||||
#include <Device/Device.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
std::string fDiskImage;
|
||||
|
||||
|
||||
|
||||
void usage()
|
||||
{
|
||||
std::printf("profuse_pascal 0.1\n\n");
|
||||
std::printf(
|
||||
"usage:\n"
|
||||
"profuse_pascal [options] diskimage [mountpoint]\n"
|
||||
"Options:\n"
|
||||
" -d debug\n"
|
||||
" -r readonly\n"
|
||||
" -w mount writable [not yet]\n"
|
||||
" -v verbose\n"
|
||||
" --format=format specify the disk image format. Valid values are:\n"
|
||||
" dc42 DiskCopy 4.2 Image\n"
|
||||
" davex Davex Disk Image\n"
|
||||
" 2img Universal Disk Image\n"
|
||||
#ifdef HAVE_NUFX
|
||||
" sdk ShrinkIt Disk Image\n"
|
||||
#endif
|
||||
" do DOS Order Disk Image\n"
|
||||
" po ProDOS Order Disk Image (default)\n"
|
||||
" -o opt1,opt2... other mount parameters.\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static struct fuse_lowlevel_ops pascal_ops;
|
||||
|
||||
enum {
|
||||
PASCAL_OPT_HELP,
|
||||
PASCAL_OPT_VERSION,
|
||||
PASCAL_OPT_WRITE,
|
||||
PASCAL_OPT_FORMAT,
|
||||
PASCAL_OPT_VERBOSE
|
||||
};
|
||||
|
||||
struct options {
|
||||
char *format;
|
||||
int readOnly;
|
||||
int readWrite;
|
||||
int verbose;
|
||||
} options;
|
||||
|
||||
#define PASCAL_OPT_KEY(T, P, V) {T, offsetof(struct options, P), V}
|
||||
|
||||
static struct fuse_opt pascal_options[] = {
|
||||
FUSE_OPT_KEY("-h", PASCAL_OPT_HELP),
|
||||
FUSE_OPT_KEY("--help", PASCAL_OPT_HELP),
|
||||
|
||||
FUSE_OPT_KEY("-V", PASCAL_OPT_VERSION),
|
||||
FUSE_OPT_KEY("--version", PASCAL_OPT_VERSION),
|
||||
|
||||
PASCAL_OPT_KEY("-v", verbose, 1),
|
||||
|
||||
PASCAL_OPT_KEY("-w", readWrite, 1),
|
||||
PASCAL_OPT_KEY("rw", readWrite, 1),
|
||||
|
||||
PASCAL_OPT_KEY("--format=%s", format, 0),
|
||||
PASCAL_OPT_KEY("format=%s", format, 0),
|
||||
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
static int pascal_option_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case PASCAL_OPT_HELP:
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case PASCAL_OPT_VERSION:
|
||||
// TODO
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
// first arg is the disk image.
|
||||
if (fDiskImage.empty())
|
||||
{
|
||||
fDiskImage = arg;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// create a dir in /Volumes/diskname.
|
||||
bool make_mount_dir(std::string name, std::string &path)
|
||||
{
|
||||
path = "";
|
||||
|
||||
if (name.find('/') != std::string::npos
|
||||
|| name.find('\\') != std::string::npos
|
||||
|| name.find(':') != std::string::npos )
|
||||
{
|
||||
name = "Pascal Volume";
|
||||
}
|
||||
|
||||
path = "";
|
||||
path = "/Volumes/" + name;
|
||||
rmdir(path.c_str());
|
||||
if (mkdir(path.c_str(), 0777) == 0) return true;
|
||||
|
||||
for (unsigned i = 0; i < 26; i++)
|
||||
{
|
||||
path = "/Volumes/" + name + " " + (char)('a' + i);
|
||||
|
||||
rmdir(path.c_str());
|
||||
if (mkdir(path.c_str(), 0777) == 0) return true;
|
||||
}
|
||||
|
||||
path = "";
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
extern void init_ops(fuse_lowlevel_ops *ops);
|
||||
struct options options;
|
||||
|
||||
std::memset(&options, 0, sizeof(options));
|
||||
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
struct fuse_chan *ch;
|
||||
char *mountpoint = NULL;
|
||||
int err = -1;
|
||||
std::string mountPath;
|
||||
unsigned format = 0;
|
||||
|
||||
int foreground = false;
|
||||
int multithread = false;
|
||||
|
||||
|
||||
Pascal::VolumeEntryPointer volume;
|
||||
|
||||
|
||||
init_ops(&pascal_ops);
|
||||
|
||||
|
||||
// scan the argument list, looking for the name of the disk image.
|
||||
if (fuse_opt_parse(&args, &options ,pascal_options, pascal_option_proc) == -1)
|
||||
exit(1);
|
||||
|
||||
if (fDiskImage.empty())
|
||||
{
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// default prodos-order disk image.
|
||||
if (options.format)
|
||||
{
|
||||
format = Device::BlockDevice::ImageType(options.format);
|
||||
if (!format)
|
||||
std::fprintf(stderr, "Warning: Unknown image type ``%s''\n", options.format);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Device::BlockDevicePointer device;
|
||||
|
||||
device = Device::BlockDevice::Open(fDiskImage.c_str(), File::ReadOnly, format);
|
||||
|
||||
|
||||
if (!device.get())
|
||||
{
|
||||
std::fprintf(stderr, "Error: Unknown or unsupported device type.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
volume = Pascal::VolumeEntry::Open(device);
|
||||
}
|
||||
catch (::Exception &e)
|
||||
{
|
||||
std::fprintf(stderr, "%s\n", e.what());
|
||||
std::fprintf(stderr, "%s\n", std::strerror(e.error()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
{
|
||||
// Macfuse supports custom volume names (displayed in Finder)
|
||||
std::string str("-ovolname=");
|
||||
str += volume->name();
|
||||
fuse_opt_add_arg(&args, str.c_str());
|
||||
|
||||
// 512 byte blocksize.
|
||||
fuse_opt_add_arg(&args, "-oiosize=512");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
fuse_opt_add_arg(&args, "-ofsname=PascalFS");
|
||||
|
||||
if (!options.readOnly)
|
||||
fuse_opt_add_arg(&args, "-ordonly");
|
||||
|
||||
if (options.readWrite)
|
||||
{
|
||||
std::fprintf(stderr, "Warning: write support is not yet enabled.\n");
|
||||
}
|
||||
|
||||
if (fuse_parse_cmdline(&args, &mountpoint, &multithread, &foreground) == -1)
|
||||
{
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
if (mountpoint == NULL || *mountpoint == 0)
|
||||
{
|
||||
if (make_mount_dir(volume->name(), mountPath))
|
||||
mountpoint = (char *)mountPath.c_str();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
if ((ch = fuse_mount(mountpoint, &args)) != NULL)
|
||||
{
|
||||
struct fuse_session* se;
|
||||
|
||||
std::printf("Mounting ``%s'' on ``%s''\n", volume->name(), mountpoint);
|
||||
|
||||
se = fuse_lowlevel_new(&args, &pascal_ops, sizeof(pascal_ops), volume.get());
|
||||
|
||||
if (se) do {
|
||||
|
||||
|
||||
err = fuse_daemonize(foreground);
|
||||
if (err < 0 ) break;
|
||||
|
||||
err = fuse_set_signal_handlers(se);
|
||||
if (err < 0) break;
|
||||
|
||||
fuse_session_add_chan(se, ch);
|
||||
|
||||
if (multithread) err = fuse_session_loop_mt(se);
|
||||
else err = fuse_session_loop(se);
|
||||
|
||||
fuse_remove_signal_handlers(se);
|
||||
fuse_session_remove_chan(ch);
|
||||
|
||||
} while (false);
|
||||
if (se) fuse_session_destroy(se);
|
||||
fuse_unmount(mountpoint, ch);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fuse_opt_free_args(&args);
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (!mountPath.empty()) rmdir(mountPath.c_str());
|
||||
#endif
|
||||
|
||||
|
||||
return err ? 1 : 0;
|
||||
}
|
|
@ -0,0 +1,553 @@
|
|||
|
||||
#ifdef __APPLE__
|
||||
#define __FreeBSD__ 10
|
||||
#define __DARWIN_64_BIT_INO_T 1
|
||||
#endif
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define FUSE_USE_VERSION 27
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
|
||||
|
||||
|
||||
#include <fuse/fuse_opt.h>
|
||||
#include <fuse/fuse_lowlevel.h>
|
||||
|
||||
|
||||
|
||||
#include <Pascal/Pascal.h>
|
||||
#include <Common/auto.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <POSIX/Exception.h>
|
||||
|
||||
#define NO_ATTR() \
|
||||
{ \
|
||||
if (size) fuse_reply_buf(req, NULL, 0); \
|
||||
else fuse_reply_xattr(req, 0); \
|
||||
return; \
|
||||
}
|
||||
|
||||
#undef ERROR
|
||||
#define ERROR(cond,errno) if ( (cond) ){ fuse_reply_err(req, errno); return; }
|
||||
|
||||
|
||||
#define RETURN_XATTR(DATA, SIZE) \
|
||||
{ \
|
||||
if (size == 0) { fuse_reply_xattr(req, SIZE); return; } \
|
||||
if (size < SIZE) { fuse_reply_err(req, ERANGE); return; } \
|
||||
fuse_reply_buf(req, DATA, SIZE); return; \
|
||||
}
|
||||
|
||||
|
||||
#define DEBUGNAME() \
|
||||
if (0) { std::fprintf(stderr, "%s\n", __func__); }
|
||||
|
||||
// linux doesn't have ENOATTR.
|
||||
#ifndef ENOATTR
|
||||
#define ENOATTR ENOENT
|
||||
#endif
|
||||
|
||||
using namespace Pascal;
|
||||
|
||||
|
||||
// fd_table is files which have been open.
|
||||
// fd_table_available is a list of indexes in fd_table which are not currently used.
|
||||
static std::vector<FileEntryPointer> fd_table;
|
||||
static std::vector<unsigned> fd_table_available;
|
||||
|
||||
static FileEntryPointer findChild(VolumeEntry *volume, unsigned inode)
|
||||
{
|
||||
|
||||
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
|
||||
{
|
||||
FileEntryPointer child = volume->fileAtIndex(i);
|
||||
if (!child) continue;
|
||||
|
||||
if (inode == child->inode()) return child;
|
||||
}
|
||||
|
||||
return FileEntryPointer();
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark fs
|
||||
|
||||
|
||||
static void pascal_init(void *userdata, struct fuse_conn_info *conn)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
// nop
|
||||
|
||||
// text files have a non-thread safe index.
|
||||
// which is initialized via read() or fileSize()
|
||||
VolumeEntry *volume = (VolumeEntry *)userdata;
|
||||
|
||||
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
|
||||
{
|
||||
FileEntryPointer child = volume->fileAtIndex(i);
|
||||
child->fileSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void pascal_destroy(void *userdata)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
// nop
|
||||
}
|
||||
|
||||
|
||||
static void pascal_statfs(fuse_req_t req, fuse_ino_t ino)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
struct statvfs vst;
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
|
||||
ERROR(!volume, EIO)
|
||||
|
||||
// returns statvfs for the mount path or any file in the fs
|
||||
// therefore, ignore ino.
|
||||
std::memset(&vst, 0, sizeof(vst));
|
||||
|
||||
vst.f_bsize = 512; // fs block size
|
||||
vst.f_frsize = 512; // fundamental fs block size
|
||||
vst.f_blocks = volume->volumeBlocks();
|
||||
vst.f_bfree = volume->freeBlocks(true); // free blocks
|
||||
vst.f_bavail = volume->freeBlocks(false); // free blocks (non-root)
|
||||
vst.f_files = volume->fileCount();
|
||||
vst.f_ffree = -1; // free inodes.
|
||||
vst.f_favail = -1; // free inodes (non-root)
|
||||
vst.f_fsid = 0; // file system id?
|
||||
vst.f_flag = volume->readOnly() ? ST_RDONLY | ST_NOSUID : ST_NOSUID;
|
||||
vst.f_namemax = 15;
|
||||
|
||||
fuse_reply_statfs(req, &vst);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark xattr
|
||||
|
||||
static void pascal_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
FileEntryPointer file;
|
||||
std::string attr;
|
||||
unsigned attrSize;
|
||||
|
||||
if (ino == 1) NO_ATTR()
|
||||
|
||||
file = findChild(volume, ino);
|
||||
|
||||
ERROR(file == NULL, ENOENT)
|
||||
|
||||
attr += "pascal.fileKind";
|
||||
attr.append(1, 0);
|
||||
|
||||
if (file->fileKind() == kTextFile)
|
||||
{
|
||||
attr += "com.apple.TextEncoding";
|
||||
attr.append(1, 0);
|
||||
|
||||
attr += "user.charset";
|
||||
attr.append(1, 0);
|
||||
}
|
||||
// user.mime_type ? text/plain, ...
|
||||
|
||||
attrSize = attr.size();
|
||||
if (size == 0)
|
||||
{
|
||||
fuse_reply_xattr(req, attrSize);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR(size < attrSize, ERANGE)
|
||||
|
||||
fuse_reply_buf(req, attr.data(), attrSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
FileEntryPointer file;
|
||||
std::string attr(name);
|
||||
|
||||
ERROR(ino == 1, ENOATTR)
|
||||
|
||||
file = findChild(volume, ino);
|
||||
|
||||
ERROR(file == NULL, ENOENT)
|
||||
|
||||
if (attr == "pascal.fileKind")
|
||||
{
|
||||
uint8_t fk = file->fileKind();
|
||||
|
||||
RETURN_XATTR((const char *)&fk, 1)
|
||||
}
|
||||
|
||||
if (file->fileKind() == kTextFile)
|
||||
{
|
||||
|
||||
if (attr == "user.charset")
|
||||
{
|
||||
static const char data[]="ascii";
|
||||
static unsigned dataSize = sizeof(data) - 1;
|
||||
|
||||
RETURN_XATTR(data, dataSize)
|
||||
}
|
||||
|
||||
if (attr == "com.apple.TextEncoding")
|
||||
{
|
||||
static const char data[] = "us-ascii;1536";
|
||||
static unsigned dataSize = sizeof(data) - 1;
|
||||
|
||||
RETURN_XATTR(data, dataSize)
|
||||
}
|
||||
}
|
||||
|
||||
ERROR(true, ENOATTR);
|
||||
return;
|
||||
}
|
||||
|
||||
// OS X version.
|
||||
static void pascal_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size, uint32_t off)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
pascal_getxattr(req, ino, name, size);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark dirent
|
||||
|
||||
static void pascal_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
ERROR(ino != 1, ENOTDIR)
|
||||
|
||||
fi->fh = 0;
|
||||
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
static void pascal_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
}
|
||||
|
||||
static void pascal_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
::auto_array<uint8_t> buffer(new uint8_t[size]);
|
||||
unsigned count = volume->fileCount();
|
||||
|
||||
|
||||
struct stat st;
|
||||
unsigned currentSize = 0;
|
||||
|
||||
std::memset(&st, 0, sizeof(struct stat));
|
||||
|
||||
// . and .. need to be added in here but are handled by the fs elsewhere.
|
||||
|
||||
do {
|
||||
if (off == 0)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
st.st_mode = S_IFDIR | 0555;
|
||||
st.st_ino = 1;
|
||||
|
||||
tmp = fuse_add_direntry(req, NULL, 0, ".", NULL, 0);
|
||||
|
||||
if (tmp + currentSize > size) break;
|
||||
|
||||
fuse_add_direntry(req, (char *)buffer.get() + currentSize, size, ".", &st, ++off);
|
||||
currentSize += tmp;
|
||||
}
|
||||
|
||||
if (off == 1)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
st.st_mode = S_IFDIR | 0555;
|
||||
st.st_ino = 1;
|
||||
|
||||
tmp = fuse_add_direntry(req, NULL, 0, "..", NULL, 0);
|
||||
|
||||
if (tmp + currentSize > size) break;
|
||||
|
||||
fuse_add_direntry(req, (char *)buffer.get() + currentSize, size, "..", &st, ++off);
|
||||
currentSize += tmp;
|
||||
}
|
||||
|
||||
for (unsigned i = off - 2; i < count; ++i)
|
||||
{
|
||||
unsigned tmp;
|
||||
|
||||
FileEntryPointer file = volume->fileAtIndex(i);
|
||||
if (file == NULL) break; //?
|
||||
|
||||
// only these fields are used.
|
||||
st.st_mode = S_IFREG | 0444;
|
||||
st.st_ino = file->inode();
|
||||
|
||||
|
||||
tmp = fuse_add_direntry(req, NULL, 0, file->name(), NULL, 0);
|
||||
|
||||
if (tmp + currentSize > size) break;
|
||||
|
||||
fuse_add_direntry(req, (char *)buffer.get() + currentSize, size, file->name(), &st, ++off);
|
||||
currentSize += tmp;
|
||||
}
|
||||
|
||||
} while (false);
|
||||
|
||||
|
||||
fuse_reply_buf(req, (char *)buffer.get(), currentSize);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark stat
|
||||
|
||||
|
||||
static void stat(FileEntry *file, struct stat *st)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
std::memset(st, 0, sizeof(struct stat));
|
||||
|
||||
time_t t = file->modification();
|
||||
|
||||
st->st_ino = file->inode();
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = S_IFREG | 0444;
|
||||
st->st_size = file->fileSize();
|
||||
st->st_blocks = file->blocks();
|
||||
st->st_blksize = 512;
|
||||
|
||||
st->st_atime = t;
|
||||
st->st_mtime = t;
|
||||
st->st_ctime = t;
|
||||
// st.st_birthtime not yet supported by MacFUSE.
|
||||
}
|
||||
|
||||
static void stat(VolumeEntry *volume, struct stat *st)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
std::memset(st, 0, sizeof(struct stat));
|
||||
|
||||
time_t t = volume->lastBoot();
|
||||
|
||||
st->st_ino = volume->inode();
|
||||
st->st_nlink = 1 + volume->fileCount();
|
||||
st->st_mode = S_IFDIR | 0555;
|
||||
st->st_size = volume->blocks() * 512;
|
||||
st->st_blocks = volume->blocks();
|
||||
st->st_blksize = 512;
|
||||
|
||||
st->st_atime = t;
|
||||
st->st_mtime = t;
|
||||
st->st_ctime = t;
|
||||
// st.st_birthtime not yet supported by MacFUSE.
|
||||
}
|
||||
|
||||
static void pascal_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
struct fuse_entry_param entry;
|
||||
|
||||
ERROR(parent != 1, ENOTDIR)
|
||||
ERROR(!FileEntry::ValidName(name), ENOENT)
|
||||
|
||||
|
||||
for (unsigned i = 0, l = volume->fileCount(); i < l; ++i)
|
||||
{
|
||||
FileEntryPointer file = volume->fileAtIndex(i);
|
||||
if (file == NULL) break;
|
||||
|
||||
if (::strcasecmp(file->name(), name)) continue;
|
||||
|
||||
// found it!
|
||||
std::memset(&entry, 0, sizeof(entry));
|
||||
entry.attr_timeout = 0.0;
|
||||
entry.entry_timeout = 0.0;
|
||||
entry.ino = file->inode();
|
||||
|
||||
stat(file.get(), &entry.attr);
|
||||
fuse_reply_entry(req, &entry);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR(true, ENOENT)
|
||||
}
|
||||
|
||||
|
||||
static void pascal_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
struct stat st;
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
FileEntryPointer file;
|
||||
|
||||
if (ino == 1)
|
||||
{
|
||||
stat(volume, &st);
|
||||
fuse_reply_attr(req, &st, 0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
file = findChild(volume, ino);
|
||||
|
||||
ERROR(file == NULL, ENOENT)
|
||||
//printf("\t%s\n", file->name());
|
||||
|
||||
stat(file.get(), &st);
|
||||
fuse_reply_attr(req, &st, 0.0);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark file
|
||||
|
||||
static void pascal_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
unsigned index;
|
||||
DEBUGNAME()
|
||||
|
||||
VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
FileEntryPointer file;
|
||||
|
||||
ERROR(ino == 1, EISDIR)
|
||||
|
||||
file = findChild(volume, ino);
|
||||
|
||||
ERROR(file == NULL, ENOENT)
|
||||
|
||||
ERROR((fi->flags & O_ACCMODE) != O_RDONLY, EACCES)
|
||||
|
||||
// insert the FileEntryPointer into fd_table.
|
||||
if (fd_table_available.size())
|
||||
{
|
||||
index = fd_table_available.back();
|
||||
fd_table_available.pop_back();
|
||||
fd_table[index] = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = fd_table.size();
|
||||
fd_table.push_back(file);
|
||||
}
|
||||
|
||||
fi->fh = index;
|
||||
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
|
||||
static void pascal_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
|
||||
{
|
||||
unsigned index = fi->fh;
|
||||
DEBUGNAME()
|
||||
|
||||
fd_table[index].reset();
|
||||
fd_table_available.push_back(index);
|
||||
|
||||
fuse_reply_err(req, 0);
|
||||
}
|
||||
|
||||
|
||||
static void pascal_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
|
||||
{
|
||||
unsigned index = fi->fh;
|
||||
|
||||
DEBUGNAME()
|
||||
|
||||
|
||||
//VolumeEntry *volume = (VolumeEntry *)fuse_req_userdata(req);
|
||||
FileEntryPointer file = fd_table[index];
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
::auto_array<uint8_t> buffer(new uint8_t[size]);
|
||||
unsigned rsize = file->read(buffer.get(), size, off);
|
||||
|
||||
fuse_reply_buf(req, (char *)(buffer.get()), rsize);
|
||||
return;
|
||||
}
|
||||
|
||||
catch (POSIX::Exception &e)
|
||||
{
|
||||
printf("posix error...\n");
|
||||
ERROR(true, e.error());
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
printf("error...\n");
|
||||
ERROR(true, EIO)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void init_ops(fuse_lowlevel_ops *ops)
|
||||
{
|
||||
DEBUGNAME()
|
||||
|
||||
std::memset(ops, 0, sizeof(fuse_lowlevel_ops));
|
||||
|
||||
ops->init = pascal_init;
|
||||
ops->destroy = pascal_destroy;
|
||||
ops->statfs = pascal_statfs;
|
||||
|
||||
// returns pascal.filekind, text encoding.
|
||||
ops->listxattr = pascal_listxattr;
|
||||
ops->getxattr = pascal_getxattr;
|
||||
|
||||
// volume is a dir.
|
||||
ops->opendir = pascal_opendir;
|
||||
ops->releasedir = pascal_releasedir;
|
||||
ops->readdir = pascal_readdir;
|
||||
|
||||
|
||||
ops->lookup = pascal_lookup;
|
||||
ops->getattr = pascal_getattr;
|
||||
|
||||
ops->open = pascal_open;
|
||||
ops->release = pascal_release;
|
||||
ops->read = pascal_read;
|
||||
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Device/RawDevice.h>
|
||||
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <Pascal/Pascal.h>
|
||||
|
||||
#include <File/File.h>
|
||||
#include <File/MappedFile.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace Pascal;
|
||||
using namespace Device;
|
||||
|
||||
#define NEWFS_VERSION "0.1"
|
||||
|
||||
|
||||
|
||||
bool yes_or_no()
|
||||
{
|
||||
int ch, first;
|
||||
(void)fflush(stderr);
|
||||
|
||||
first = ch = getchar();
|
||||
while (ch != '\n' && ch != EOF)
|
||||
ch = getchar();
|
||||
return (first == 'y' || first == 'Y');
|
||||
}
|
||||
|
||||
/*
|
||||
* \d+ by block
|
||||
* \d+[Kk] by kilobyte
|
||||
* \d+[Mm] by megabyte
|
||||
*/
|
||||
unsigned parseBlocks(const char *cp)
|
||||
{
|
||||
unsigned long blocks = 0;
|
||||
char *mod;
|
||||
|
||||
errno = 0;
|
||||
blocks = std::strtoul(cp, &mod, 10);
|
||||
if (errno) return -1;
|
||||
|
||||
if (mod == cp) return -1;
|
||||
if (blocks > 0xffff) return -1;
|
||||
|
||||
if (mod)
|
||||
{
|
||||
switch(*mod)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 'm': // 1m = 1024*1024b = 2048 blocks
|
||||
case 'M':
|
||||
blocks *= 2048;
|
||||
break;
|
||||
|
||||
case 'k': // 1k = 1024b = 2 blocks
|
||||
case 'K':
|
||||
blocks *= 2;
|
||||
break;
|
||||
}
|
||||
if (blocks > 0xffff) return -1;
|
||||
}
|
||||
|
||||
|
||||
return (unsigned)blocks;
|
||||
}
|
||||
|
||||
|
||||
// return the basename, without an extension.
|
||||
std::string filename(const std::string& src)
|
||||
{
|
||||
unsigned start;
|
||||
unsigned end;
|
||||
|
||||
|
||||
if (src.empty()) return std::string("");
|
||||
|
||||
start = end = 0;
|
||||
|
||||
for(unsigned i = 0, l = src.length(); i < l; ++i)
|
||||
{
|
||||
char c = src[i];
|
||||
if (c == '/') start = end = i + 1;
|
||||
if (c == '.') end = i;
|
||||
}
|
||||
|
||||
if (start == src.length()) return std::string("");
|
||||
|
||||
if (start == end) return src.substr(start);
|
||||
return src.substr(start, end - start);
|
||||
}
|
||||
|
||||
|
||||
void usage()
|
||||
{
|
||||
std::printf("newfs_pascal %s\n", NEWFS_VERSION);
|
||||
std::printf("\n");
|
||||
|
||||
|
||||
std::printf("newfs_pascal [-v volume_name] [-s size] [-f format] file\n");
|
||||
std::printf("\n");
|
||||
std::printf(" -v volume_name Specify the volume name.\n"
|
||||
" Default is Untitled.\n"
|
||||
" -s size Specify size in blocks.\n"
|
||||
" Default is 1600 blocks (800K)\n"
|
||||
" -b bootfile Specify a file that contains the boot block\n"
|
||||
" -f format Specify the disk image format. Valid values are:\n"
|
||||
" 2img Universal Disk Image\n"
|
||||
" dc42 DiskCopy 4.2 Image\n"
|
||||
" davex Davex Disk Image\n"
|
||||
" do DOS Order Disk Image\n"
|
||||
" po ProDOS Order Disk Image (default)\n"
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned blocks = 1600;
|
||||
std::string volumeName;
|
||||
std::string fileName;
|
||||
std::string bootFile;
|
||||
|
||||
int format = 0;
|
||||
const char *fname;
|
||||
int c;
|
||||
|
||||
|
||||
while ( (c = ::getopt(argc, argv, "hf:s:v:")) != -1)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
return c == 'h' ? 0 : 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
volumeName = optarg;
|
||||
// make sure it's legal.
|
||||
if (!VolumeEntry::ValidName(optarg))
|
||||
{
|
||||
std::fprintf(stderr, "Error: `%s' is not a valid Pascal volume name.\n", optarg);
|
||||
return 0x40;
|
||||
}
|
||||
break;
|
||||
|
||||
case 's':
|
||||
blocks = parseBlocks(optarg);
|
||||
if (blocks > 0xffff)
|
||||
{
|
||||
std::fprintf(stderr, "Error: `%s' is not a valid disk size.\n", optarg);
|
||||
return 0x5a;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
format = Device::BlockDevice::ImageType(optarg);
|
||||
if (format == 0)
|
||||
{
|
||||
std::fprintf(stderr, "Error: `%s' is not a supported disk image format.\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
bootFile = optarg;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc != 1)
|
||||
{
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
fname = argv[0];
|
||||
fileName = argv[0];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
struct stat st;
|
||||
bool rawDevice = false;
|
||||
|
||||
BlockDevicePointer device;
|
||||
VolumeEntryPointer volume;
|
||||
|
||||
// Check for block device. if so, verify.
|
||||
// if file exists, verify before overwrite.
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
|
||||
if (::stat(fname, &st) == 0)
|
||||
{
|
||||
if (S_ISBLK(st.st_mode))
|
||||
{
|
||||
fprintf(stderr, "`%s' is a raw device. Are you sure you want to initialize it? ", fname);
|
||||
if (!yes_or_no()) return -1;
|
||||
|
||||
device = RawDevice::Open(fname, File::ReadWrite);
|
||||
blocks = device->blocks();
|
||||
rawDevice = true;
|
||||
|
||||
if (blocks > 0xffff)
|
||||
{
|
||||
std::fprintf(stderr, "Error: device is too large.\n");
|
||||
return 0x5a;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
// file exists, verify we want to destroy it.
|
||||
|
||||
fprintf(stderr, "`%s' already exists. Are you sure you want to overwrite it? ", fname);
|
||||
if (!yes_or_no()) return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// generate a filename.
|
||||
if (volumeName.empty())
|
||||
{
|
||||
if (!rawDevice)
|
||||
volumeName = filename(fileName);
|
||||
if (volumeName.empty() || !VolumeEntry::ValidName(volumeName.c_str()))
|
||||
volumeName = "PASCAL";
|
||||
}
|
||||
|
||||
if (!rawDevice)
|
||||
device = BlockDevice::Create(fname, volumeName.c_str(), blocks, format);
|
||||
|
||||
if (!device.get())
|
||||
{
|
||||
std::fprintf(stderr, "Error: Unsupported diskimage format.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!bootFile.empty())
|
||||
{
|
||||
MappedFile bf(bootFile.c_str(), File::ReadOnly, std::nothrow);
|
||||
|
||||
if (!bf.isValid())
|
||||
{
|
||||
std::fprintf(stderr, "Warning: unable to open boot file `%s'.\n", bootFile.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t length = bf.length();
|
||||
// either 1 or 2 blocks.
|
||||
if (length == 512)
|
||||
{
|
||||
device->write(0, bf.address());
|
||||
}
|
||||
else if (length == 1024)
|
||||
{
|
||||
device->write(0, bf.address());
|
||||
device->write(1, (uint8_t*)bf.address() + 512);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fprintf(stderr, "Warning: boot file must be 512 or 1024 bytes.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
volume = VolumeEntry::Create(device, volumeName.c_str());
|
||||
|
||||
|
||||
}
|
||||
catch (::Exception& e)
|
||||
{
|
||||
std::fprintf(stderr, "%s\n", e.what());
|
||||
std::fprintf(stderr, "%s\n", ::strerror(e.error()));
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue