mirror of
https://github.com/ksherlock/profuse.git
synced 2024-06-10 17:29:32 +00:00
new branch to integrate BlockDevice, BlockCache
git-svn-id: https://profuse.googlecode.com/svn/branches/profuse_interim@332 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
parent
14e9b43f32
commit
7fb0604b76
485
Device/Adaptor.cpp
Normal file
485
Device/Adaptor.cpp
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <Device/Adaptor.h>
|
||||||
|
|
||||||
|
#include <ProFUSE/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 ProFUSE::Exception(__METHOD__ ": Invalid 6-2 value.");
|
||||||
|
|
||||||
|
return table[val];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t NibbleAdaptor::decode62(uint8_t val)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "decode62"
|
||||||
|
|
||||||
|
// auto-generated via perl.
|
||||||
|
static uint8_t table[] = {
|
||||||
|
-1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, 3, -1, 4, 5, 6,
|
||||||
|
-1, -1, -1, -1, -1, -1, 7, 8, -1, -1, -1, 9, 10, 11, 12, 13,
|
||||||
|
-1, -1, 14, 15, 16, 17, 18, 19, -1, 20, 21, 22, 23, 24, 25, 26,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, 28, 29, 30,
|
||||||
|
-1, -1, -1, 31, -1, -1, 32, 33, -1, 34, 35, 36, 37, 38, 39, 40,
|
||||||
|
-1, -1, -1, -1, -1, 41, 42, 43, -1, 44, 45, 46, 47, 48, 49, 50,
|
||||||
|
-1, -1, 51, 52, 53, 54, 55, 56, -1, 57, 58, 59, 60, 61, 62, 63
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((val < 0x90) || (table[val - 0x90] == 0xff))
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Invalid 6-2 encoding.");
|
||||||
|
|
||||||
|
return table[val - 0x90];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static int FindByte(void *address, uint8_t c, unsigned length, unsigned offset = 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
for (unsigned i = offset; i < length; ++i)
|
||||||
|
{
|
||||||
|
if ( ((uint8_t *)address)[i] == c) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Address Field:
|
||||||
|
* prologue volume track sector checksum epilogue
|
||||||
|
* D5 AA 96 XX YY XX YY XX YY XX YY DE AA EB
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data Field:
|
||||||
|
* prologue user data checksum epilogue
|
||||||
|
* D5 AA AD [6+2 encoded] XX DE AA EB
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NibbleAdaptor::NibbleAdaptor(void *address, unsigned length)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "NibbleAdaptor::NibbleAdaptor"
|
||||||
|
|
||||||
|
_address = (uint8_t *)address;
|
||||||
|
_length = length;
|
||||||
|
|
||||||
|
|
||||||
|
// build a map of track/sectors.
|
||||||
|
|
||||||
|
unsigned state = 0;
|
||||||
|
|
||||||
|
|
||||||
|
_index.resize(35 * 16, -1);
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
unsigned track = 0;
|
||||||
|
unsigned sector = 0;
|
||||||
|
unsigned volume = 0;
|
||||||
|
unsigned checksum = 0;
|
||||||
|
|
||||||
|
|
||||||
|
CircleBuffer buffer(_address, _length);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
|
||||||
|
offset = FindByte(address, 0xd5, length, offset);
|
||||||
|
if (offset < 0) break;
|
||||||
|
|
||||||
|
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0x96 && buffer[offset + 11] == 0xde && buffer[offset + 12] == 0xaa)
|
||||||
|
{
|
||||||
|
volume = decode44(buffer[offset + 3], buffer[offset + 4]);
|
||||||
|
track = decode44(buffer[offset + 5], buffer[offset + 6]);
|
||||||
|
sector = decode44(buffer[offset + 7], buffer[offset + 8]);
|
||||||
|
checksum = decode44(buffer[offset + 9], buffer[offset + 10]);
|
||||||
|
|
||||||
|
if (volume ^ track ^ sector ^ checksum)
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Invalid address checksum.");
|
||||||
|
|
||||||
|
if (track > 35 || sector > 16)
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
|
||||||
|
|
||||||
|
offset += 3 + 8 + 3;
|
||||||
|
|
||||||
|
state = 1;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad && state == 1)
|
||||||
|
{
|
||||||
|
if (_index[track * 16 + sector] != -1)
|
||||||
|
{
|
||||||
|
std::fprintf(stderr, "track %u sector %u duplicated.\n", track, sector);
|
||||||
|
}
|
||||||
|
_index[track * 16 + sector] = (offset + 3) % _length;
|
||||||
|
|
||||||
|
//offset += 3 + 342 + 1 + 3;
|
||||||
|
offset++;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++; //???
|
||||||
|
// ????
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// possible wraparound.
|
||||||
|
if (state == 1)
|
||||||
|
{
|
||||||
|
offset = FindByte(address, 0xd5, length, 0);
|
||||||
|
|
||||||
|
if (offset >= 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (buffer[offset + 1] == 0xaa && buffer[offset + 2] == 0xad)
|
||||||
|
{
|
||||||
|
_index[track * 16 + sector] = (offset + 3) % _length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// now check _index for offset = -1, which means the sector/track wasn't found.
|
||||||
|
|
||||||
|
for (std::vector<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 ProFUSE::Exception(__METHOD__ ": Sector missing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
NibbleAdaptor::~NibbleAdaptor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void NibbleAdaptor::readBlock(unsigned block, void *bp)
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned track = (block & ~0x07) << 9;
|
||||||
|
unsigned b = (block & 0x07) << 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* block sectors
|
||||||
|
* 0 0, 2
|
||||||
|
* 1 4, 6
|
||||||
|
* 2 8, 10
|
||||||
|
* 3 12,14
|
||||||
|
* 4 1, 3
|
||||||
|
* 5 5, 7
|
||||||
|
* 6 9, 11
|
||||||
|
* 7 13, 15
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned sector = b >> 2;
|
||||||
|
if (sector >= 16) sector -= 15;
|
||||||
|
|
||||||
|
|
||||||
|
readTrackSector(TrackSector(track, sector), bp);
|
||||||
|
readTrackSector(TrackSector(track, sector + 1), (uint8_t *)bp + 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NibbleAdaptor::writeBlock(unsigned block, const void *bp)
|
||||||
|
{
|
||||||
|
unsigned track = (block & ~0x07) << 9;
|
||||||
|
unsigned b = (block & 0x07) << 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* block sectors
|
||||||
|
* 0 0, 2
|
||||||
|
* 1 4, 6
|
||||||
|
* 2 8, 10
|
||||||
|
* 3 12,14
|
||||||
|
* 4 1, 3
|
||||||
|
* 5 5, 7
|
||||||
|
* 6 9, 11
|
||||||
|
* 7 13, 15
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned sector = b >> 2;
|
||||||
|
if (sector >= 16) sector -= 15;
|
||||||
|
|
||||||
|
|
||||||
|
writeTrackSector(TrackSector(track, sector), bp);
|
||||||
|
writeTrackSector(TrackSector(track, sector + 1), (const uint8_t *)bp + 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NibbleAdaptor::readTrackSector(TrackSector ts, void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "NibbleAdaptor::readTrackSector"
|
||||||
|
|
||||||
|
if (ts.track > 35 || ts.sector > 16)
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
|
||||||
|
|
||||||
|
|
||||||
|
CircleBuffer buffer(_address, _length);
|
||||||
|
uint8_t bits[86 * 3];
|
||||||
|
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
|
||||||
|
|
||||||
|
unsigned offset = _index[ts.track * 16 + ts.sector];
|
||||||
|
|
||||||
|
if (offset == -1)
|
||||||
|
{
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Missing track/sector.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// first 86 bytes are in the auxbuffer, backwards.
|
||||||
|
unsigned index = offset;
|
||||||
|
for (unsigned i = 0; i < 86; ++i)
|
||||||
|
{
|
||||||
|
uint8_t x = buffer[index++];
|
||||||
|
x = decode62(x);
|
||||||
|
|
||||||
|
checksum ^= x;
|
||||||
|
|
||||||
|
uint8_t y = checksum;
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (unsigned j = 0; j < 3; ++j)
|
||||||
|
{
|
||||||
|
//bits[i + j * 86] = ((y & 0x01) << 1) | ((y & 0x02) >> 1);
|
||||||
|
bits[i + j * 86] = "\x00\x01\x02\x03"[y & 0x03];
|
||||||
|
y >>= 2;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
bits[i + 86 * 0] = "\x00\x02\x01\x03"[y & 0x03];
|
||||||
|
bits[i + 86 * 1] = "\x00\x02\x01\x03"[(y >> 2) & 0x03];
|
||||||
|
bits[i + 86 * 2] = "\x00\x02\x01\x03"[(y >> 4) & 0x03];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t x = buffer[index++];
|
||||||
|
x = decode62(x);
|
||||||
|
|
||||||
|
checksum ^= x;
|
||||||
|
|
||||||
|
uint8_t y = (checksum << 2) | bits[i];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
((uint8_t *)bp)[i] = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checksum != decode62(buffer[index++]))
|
||||||
|
std::fprintf(stderr, "Invalid checksum on track %u, sector %u\n", ts.track, ts.sector);
|
||||||
|
//throw ProFUSE::Exception(__METHOD__ ": Invalid field checksum.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NibbleAdaptor::writeTrackSector(TrackSector ts, const void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "NibbleAdaptor::writeTrackSector"
|
||||||
|
|
||||||
|
if (ts.track > 35 || ts.sector > 16)
|
||||||
|
throw ProFUSE::Exception(__METHOD__ ": Invalid track/sector.");
|
||||||
|
|
||||||
|
uint8_t auxBuffer[86];
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
|
||||||
|
// create the aux buffer.
|
||||||
|
|
||||||
|
std::memset(auxBuffer, 0, sizeof(auxBuffer));
|
||||||
|
|
||||||
|
for (unsigned i = 0, j = 0, shift = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t x = ((const uint8_t *)bp)[i];
|
||||||
|
|
||||||
|
// grab the bottom 2 bytes and reverse them.
|
||||||
|
//uint8_t y = ((x & 0x01) << 1) | ((x & 0x02) >> 1);
|
||||||
|
uint8_t y = "\x00\x02\x01\x03"[x & 0x03];
|
||||||
|
|
||||||
|
auxBuffer[j++] |= (y << shift);
|
||||||
|
|
||||||
|
if (j == 86)
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
shift += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned offset = _index[ts.track * 16 + ts.sector];
|
||||||
|
|
||||||
|
CircleBuffer buffer(_address, _length);
|
||||||
|
// create the checksum while writing to disk..
|
||||||
|
|
||||||
|
// aux buffer
|
||||||
|
for (unsigned i = 0; i < 86; ++i)
|
||||||
|
{
|
||||||
|
uint8_t x = auxBuffer[i];
|
||||||
|
|
||||||
|
buffer[offset + i] = encode62(x ^ checksum);
|
||||||
|
|
||||||
|
checksum = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 256; ++i)
|
||||||
|
{
|
||||||
|
uint8_t x = ((const uint8_t *)bp)[i];
|
||||||
|
x >>= 2;
|
||||||
|
|
||||||
|
buffer[offset + 86 + i] = encode62(x ^ checksum);
|
||||||
|
|
||||||
|
checksum = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
buffer[offset + 342] = encode62(checksum);
|
||||||
|
}
|
||||||
|
|
81
Device/Adaptor.h
Normal file
81
Device/Adaptor.h
Normal file
|
@ -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
|
241
Device/BlockDevice.cpp
Normal file
241
Device/BlockDevice.cpp
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
|
||||||
|
#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 <ProFUSE/Exception.h>
|
||||||
|
|
||||||
|
#include <Device/DiskImage.h>
|
||||||
|
#include <Device/UniversalDiskImage.h>
|
||||||
|
#include <Device/DiskCopy42Image.h>
|
||||||
|
#include <Device/DavexDiskImage.h>
|
||||||
|
#include <Device/RawDevice.h>
|
||||||
|
|
||||||
|
using namespace Device;
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unsigned BlockDevice::ImageType(const char *type, unsigned defv)
|
||||||
|
{
|
||||||
|
const char *tmp;
|
||||||
|
|
||||||
|
|
||||||
|
if (type == 0 || *type == 0) return defv;
|
||||||
|
|
||||||
|
// type could be a path, eg images/file, disk.images/file
|
||||||
|
|
||||||
|
// unix-specifix.
|
||||||
|
// basename alters the input string
|
||||||
|
tmp = std::strrchr(type, '/');
|
||||||
|
if (tmp) type = tmp + 1;
|
||||||
|
|
||||||
|
// type could be a filename, in which case we check the extension.
|
||||||
|
tmp = std::strrchr(type, '.');
|
||||||
|
if (tmp) type = tmp + 1;
|
||||||
|
if (*type == 0) return defv;
|
||||||
|
|
||||||
|
|
||||||
|
if (::strcasecmp(type, "2mg") == 0)
|
||||||
|
return '2IMG';
|
||||||
|
if (::strcasecmp(type, "2img") == 0)
|
||||||
|
return '2IMG';
|
||||||
|
|
||||||
|
if (::strcasecmp(type, "dc42") == 0)
|
||||||
|
return 'DC42';
|
||||||
|
|
||||||
|
if (::strcasecmp(type, "po") == 0)
|
||||||
|
return 'PO__';
|
||||||
|
if (::strcasecmp(type, "dmg") == 0)
|
||||||
|
return 'PO__';
|
||||||
|
|
||||||
|
if (::strcasecmp(type, "dsk") == 0)
|
||||||
|
return 'DO__';
|
||||||
|
if (::strcasecmp(type, "do") == 0)
|
||||||
|
return 'DO__';
|
||||||
|
|
||||||
|
if (::strcasecmp(type, "dvx") == 0)
|
||||||
|
return 'DVX_';
|
||||||
|
if (::strcasecmp(type, "davex") == 0)
|
||||||
|
return 'DVX_';
|
||||||
|
|
||||||
|
|
||||||
|
// not supported yet.
|
||||||
|
if (::strcasecmp(type, "sdk") == 0)
|
||||||
|
return 'SDK_';
|
||||||
|
|
||||||
|
return defv;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockDevice *BlockDevice::Open(const char *name, File::FileFlags flags, unsigned imageType)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "BlockDevice::Open"
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
std::memset(&st, 0, sizeof(st));
|
||||||
|
|
||||||
|
if (::stat(name, &st) != 0)
|
||||||
|
{
|
||||||
|
throw POSIXException(__METHOD__ ": stat error", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imageType)
|
||||||
|
{
|
||||||
|
// /dev/xxxx
|
||||||
|
if (S_ISBLK(st.st_mode))
|
||||||
|
return RawDevice::Open(name, flags);
|
||||||
|
|
||||||
|
|
||||||
|
imageType = ImageType(name, 'PO__');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO -- if no image type, guess based on file size?
|
||||||
|
|
||||||
|
MappedFile file(name, flags);
|
||||||
|
|
||||||
|
|
||||||
|
switch (imageType)
|
||||||
|
{
|
||||||
|
case '2IMG':
|
||||||
|
return UniversalDiskImage::Open(&file);
|
||||||
|
|
||||||
|
case 'DC42':
|
||||||
|
return DiskCopy42Image::Open(&file);
|
||||||
|
|
||||||
|
case 'DO__':
|
||||||
|
return DOSOrderDiskImage::Open(&file);
|
||||||
|
|
||||||
|
case 'PO__':
|
||||||
|
return ProDOSOrderDiskImage::Open(&file);
|
||||||
|
|
||||||
|
case 'DVX_':
|
||||||
|
return DavexDiskImage::Open(&file);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw an error?
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return the basename, without an extension.
|
||||||
|
static std::string filename(const std::string& src)
|
||||||
|
{
|
||||||
|
unsigned start;
|
||||||
|
unsigned end;
|
||||||
|
|
||||||
|
|
||||||
|
if (src.empty()) return std::string("");
|
||||||
|
|
||||||
|
start = end = 0;
|
||||||
|
|
||||||
|
for(unsigned i = 0, l = src.length(); i < l; ++i)
|
||||||
|
{
|
||||||
|
char c = src[i];
|
||||||
|
if (c == '/') start = end = i + 1;
|
||||||
|
if (c == '.') end = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == src.length()) return std::string("");
|
||||||
|
|
||||||
|
if (start == end) return src.substr(start);
|
||||||
|
return src.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockDevice *BlockDevice::Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType)
|
||||||
|
{
|
||||||
|
std::string xname;
|
||||||
|
|
||||||
|
if (!imageType) imageType = ImageType(fname, 'PO__');
|
||||||
|
|
||||||
|
if (vname == NULL)
|
||||||
|
{
|
||||||
|
xname = filename(std::string(fname));
|
||||||
|
vname = xname.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
switch(imageType)
|
||||||
|
{
|
||||||
|
case '2IMG':
|
||||||
|
return UniversalDiskImage::Create(fname, blocks);
|
||||||
|
|
||||||
|
case 'DC42':
|
||||||
|
return DiskCopy42Image::Create(fname, blocks, vname);
|
||||||
|
|
||||||
|
case 'DO__':
|
||||||
|
return DOSOrderDiskImage::Create(fname, blocks);
|
||||||
|
|
||||||
|
case 'PO__':
|
||||||
|
return ProDOSOrderDiskImage::Create(fname, blocks);
|
||||||
|
|
||||||
|
case 'DVX_':
|
||||||
|
return DavexDiskImage::Create(fname, blocks, vname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BlockDevice::BlockDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
BlockDevice::~BlockDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockDevice::zeroBlock(unsigned block)
|
||||||
|
{
|
||||||
|
uint8_t bp[512];
|
||||||
|
std::memset(bp, 0, 512);
|
||||||
|
|
||||||
|
write(block, bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool BlockDevice::mapped()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockDevice::sync(unsigned block)
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void BlockDevice::sync(TrackSector ts)
|
||||||
|
{
|
||||||
|
sync();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
BlockCache *BlockDevice::createBlockCache()
|
||||||
|
{
|
||||||
|
unsigned b = blocks();
|
||||||
|
unsigned size = std::max(16u, b / 16);
|
||||||
|
return new ConcreteBlockCache(this, size);
|
||||||
|
}
|
65
Device/BlockDevice.h
Normal file
65
Device/BlockDevice.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#ifndef __BLOCKDEVICE_H__
|
||||||
|
#define __BLOCKDEVICE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <ProFUSE/Exception.h>
|
||||||
|
|
||||||
|
#include <Device/TrackSector.h>
|
||||||
|
|
||||||
|
#include <Cache/BlockCache.h>
|
||||||
|
|
||||||
|
#include <File/File.h>
|
||||||
|
|
||||||
|
namespace Device {
|
||||||
|
|
||||||
|
class BlockDevice {
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
// static methods.
|
||||||
|
static unsigned ImageType(const char *type, unsigned defv = 0);
|
||||||
|
|
||||||
|
static BlockDevice *Open(const char *name, File::FileFlags flags, unsigned imageType = 0);
|
||||||
|
static BlockDevice *Create(const char *fname, const char *vname, unsigned blocks, unsigned imageType = 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
virtual ~BlockDevice();
|
||||||
|
|
||||||
|
virtual BlockCache *createBlockCache();
|
||||||
|
|
||||||
|
|
||||||
|
virtual void read(unsigned block, void *bp) = 0;
|
||||||
|
//virtual void read(TrackSector ts, void *bp) = 0
|
||||||
|
|
||||||
|
virtual void write(unsigned block, const void *bp) = 0;
|
||||||
|
//virtual void write(TrackSector ts, const void *bp) = 0;
|
||||||
|
|
||||||
|
|
||||||
|
virtual unsigned blocks() = 0;
|
||||||
|
|
||||||
|
virtual bool mapped();
|
||||||
|
|
||||||
|
virtual bool readOnly() = 0;
|
||||||
|
|
||||||
|
virtual void sync() = 0;
|
||||||
|
virtual void sync(unsigned block);
|
||||||
|
//virtual void sync(TrackSector ts);
|
||||||
|
|
||||||
|
|
||||||
|
void zeroBlock(unsigned block);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BlockDevice();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
165
Device/DavexDiskImage.cpp
Normal file
165
Device/DavexDiskImage.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
/*
|
||||||
|
http://www.umich.edu/~archive/apple2/technotes/ftn/FTN.E0.8004
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *IdentityCheck = "\x60VSTORE [Davex]\x00";
|
||||||
|
|
||||||
|
|
||||||
|
// private, validation already performed.
|
||||||
|
DavexDiskImage::DavexDiskImage(MappedFile *file) :
|
||||||
|
DiskImage(file)
|
||||||
|
{
|
||||||
|
// at this point, file is no longer valid.
|
||||||
|
|
||||||
|
|
||||||
|
// 512-bytes header
|
||||||
|
setBlocks((length() / 512) - 1);
|
||||||
|
setAdaptor(new POAdaptor(512 + (uint8_t *)address()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DavexDiskImage::~DavexDiskImage()
|
||||||
|
{
|
||||||
|
// scan and update usedBlocks?
|
||||||
|
}
|
||||||
|
|
||||||
|
void DavexDiskImage::Validate(MappedFile *f)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DavexDiskImage::Validate"
|
||||||
|
|
||||||
|
size_t size = f->length();
|
||||||
|
const void * data = f->address();
|
||||||
|
bool ok = false;
|
||||||
|
unsigned blocks = (size / 512) - 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (size < 512) break;
|
||||||
|
if (size % 512) break;
|
||||||
|
|
||||||
|
// identity.
|
||||||
|
if (std::memcmp(data, IdentityCheck, 16))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// file format.
|
||||||
|
if (Read8(data, 0x10) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// total blocks
|
||||||
|
if (Read32(data, 33) != blocks)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// file number -- must be 1
|
||||||
|
if (Read8(data, 64) != 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
throw Exception(__METHOD__ ": Invalid file format.");
|
||||||
|
}
|
||||||
|
|
||||||
|
DavexDiskImage *DavexDiskImage::Open(MappedFile *file)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DavexDiskImage::Open"
|
||||||
|
Validate(file);
|
||||||
|
|
||||||
|
return new DavexDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks)
|
||||||
|
{
|
||||||
|
return Create(name, blocks, "Untitled");
|
||||||
|
}
|
||||||
|
DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const char *vname)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DavexDiskImage::Create"
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
uint8_t tmp[512];
|
||||||
|
IOBuffer header(tmp,512);
|
||||||
|
|
||||||
|
|
||||||
|
MappedFile *file = MappedFile::Create(name, blocks * 512 + 512);
|
||||||
|
|
||||||
|
data = (uint8_t *)file->address();
|
||||||
|
|
||||||
|
header.writeBytes(IdentityCheck, 16);
|
||||||
|
// file Format
|
||||||
|
header.write8(0);
|
||||||
|
//version
|
||||||
|
header.write8(0);
|
||||||
|
// version
|
||||||
|
header.write8(0x10);
|
||||||
|
|
||||||
|
// reserved.
|
||||||
|
header.setOffset(32, true);
|
||||||
|
|
||||||
|
//deviceNum
|
||||||
|
header.write8(1);
|
||||||
|
|
||||||
|
// total blocks
|
||||||
|
header.write32(blocks);
|
||||||
|
|
||||||
|
// unused blocks
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// volume Name
|
||||||
|
if (!vname || !*vname) vname = "Untitled";
|
||||||
|
unsigned l = std::strlen(vname);
|
||||||
|
header.write8(std::min(l, 15u));
|
||||||
|
header.writeBytes(vname, std::min(l, 15u));
|
||||||
|
|
||||||
|
// name + reserved.
|
||||||
|
header.setOffset(64, true);
|
||||||
|
|
||||||
|
// file number
|
||||||
|
header.write8(1);
|
||||||
|
|
||||||
|
//starting block
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// reserved
|
||||||
|
header.setOffset(512, true);
|
||||||
|
|
||||||
|
|
||||||
|
std::memcpy(file->address(), header.buffer(), 512);
|
||||||
|
file->sync();
|
||||||
|
|
||||||
|
return new DavexDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockCache *DavexDiskImage::createBlockCache()
|
||||||
|
{
|
||||||
|
return new MappedBlockCache(this, 512 + (uint8_t *)address());
|
||||||
|
|
||||||
|
}
|
39
Device/DavexDiskImage.h
Normal file
39
Device/DavexDiskImage.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef __DAVEXDISKIMAGE_H__
|
||||||
|
#define __DAVEXDISKIMAGE_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 DavexDiskImage *Create(const char *name, size_t blocks);
|
||||||
|
static DavexDiskImage *Create(const char *name, size_t blocks, const char *vname);
|
||||||
|
static DavexDiskImage *Open(MappedFile *);
|
||||||
|
|
||||||
|
virtual BlockCache *createBlockCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DavexDiskImage();
|
||||||
|
|
||||||
|
DavexDiskImage(MappedFile *);
|
||||||
|
static void Validate(MappedFile *);
|
||||||
|
|
||||||
|
bool _changed;
|
||||||
|
std::string _volumeName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
228
Device/DiskCopy42Image.cpp
Normal file
228
Device/DiskCopy42Image.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
oDataSize = 64,
|
||||||
|
oDataChecksum = 72,
|
||||||
|
oPrivate = 82,
|
||||||
|
oUserData = 84
|
||||||
|
};
|
||||||
|
|
||||||
|
DiskCopy42Image::DiskCopy42Image(MappedFile *f) :
|
||||||
|
DiskImage(f),
|
||||||
|
_changed(false)
|
||||||
|
{
|
||||||
|
setAdaptor(new POAdaptor(oUserData + (uint8_t *)f->address()));
|
||||||
|
setBlocks(Read32(f->address(), oDataSize) / 512);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DiskCopy42Image::~DiskCopy42Image()
|
||||||
|
{
|
||||||
|
if (_changed)
|
||||||
|
{
|
||||||
|
MappedFile *f = file();
|
||||||
|
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
void *data = f->address();
|
||||||
|
|
||||||
|
uint32_t cs = Checksum(oUserData + (uint8_t *)data, Read32(data, oDataSize));
|
||||||
|
|
||||||
|
Write32(data, oDataChecksum, cs);
|
||||||
|
f->sync();
|
||||||
|
}
|
||||||
|
// TODO -- checksum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DiskCopy42Image::Checksum(void *data, size_t size)
|
||||||
|
{
|
||||||
|
uint32_t rv = 0;
|
||||||
|
uint8_t *dp = (uint8_t *)data;
|
||||||
|
|
||||||
|
if (size & 0x01) return rv;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; i += 2)
|
||||||
|
{
|
||||||
|
rv += dp[i] << 8;
|
||||||
|
rv += dp[i+1];
|
||||||
|
|
||||||
|
rv = (rv >> 1) + (rv << 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiskCopy42Image *DiskCopy42Image::Open(MappedFile *f)
|
||||||
|
{
|
||||||
|
Validate(f);
|
||||||
|
return new DiskCopy42Image(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t DiskFormat(size_t blocks)
|
||||||
|
{
|
||||||
|
switch (blocks)
|
||||||
|
{
|
||||||
|
case 800: return 0;
|
||||||
|
case 1600: return 1;
|
||||||
|
case 1440: return 2;
|
||||||
|
case 2880: return 3;
|
||||||
|
default: return 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t FormatByte(size_t blocks)
|
||||||
|
{
|
||||||
|
switch(blocks)
|
||||||
|
{
|
||||||
|
case 800: return 0x12;
|
||||||
|
default: return 0x22;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks)
|
||||||
|
{
|
||||||
|
return Create(name, blocks, "Untitled");
|
||||||
|
}
|
||||||
|
|
||||||
|
DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname)
|
||||||
|
{
|
||||||
|
MappedFile *file = MappedFile::Create(name, blocks * 512 + oUserData);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t tmp[oUserData];
|
||||||
|
IOBuffer header(tmp, oUserData);
|
||||||
|
|
||||||
|
// name -- 64byte pstring.
|
||||||
|
|
||||||
|
if (vname == NULL) vname = "Untitled";
|
||||||
|
unsigned l = std::strlen(vname);
|
||||||
|
header.write8(std::min(l, 63u));
|
||||||
|
header.writeBytes(vname, std::min(l, 63u));
|
||||||
|
|
||||||
|
//header.resize(64);
|
||||||
|
header.setOffset(oDataSize, true);
|
||||||
|
|
||||||
|
// data size -- number of bytes
|
||||||
|
header.write32(blocks * 512);
|
||||||
|
|
||||||
|
// tag size
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// data checksum
|
||||||
|
// if data is 0, will be 0.
|
||||||
|
//header.push32be(Checksum(file->fileData(), blocks * 512));
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// tag checksum
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// disk format.
|
||||||
|
/*
|
||||||
|
* 0 = 400k
|
||||||
|
* 1 = 800k
|
||||||
|
* 2 = 720k
|
||||||
|
* 3 = 1440k
|
||||||
|
* 0xff = other
|
||||||
|
*/
|
||||||
|
header.write8(DiskFormat(blocks));
|
||||||
|
|
||||||
|
// formatbyte
|
||||||
|
/*
|
||||||
|
* 0x12 = 400k
|
||||||
|
* 0x22 = >400k mac
|
||||||
|
* 0x24 = 800k appleII
|
||||||
|
*/
|
||||||
|
header.write8(FormatByte(blocks));
|
||||||
|
|
||||||
|
// private
|
||||||
|
header.write16(0x100);
|
||||||
|
|
||||||
|
std::memcpy(file->address(), header.buffer(), oUserData);
|
||||||
|
file->sync();
|
||||||
|
|
||||||
|
return new DiskCopy42Image(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiskCopy42Image::Validate(MappedFile *file)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DiskCopy42Image::Validate"
|
||||||
|
|
||||||
|
size_t bytes = 0;
|
||||||
|
size_t size = file->length();
|
||||||
|
const void *data = file->address();
|
||||||
|
bool ok = false;
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (size < oUserData) break;
|
||||||
|
|
||||||
|
// name must be < 64
|
||||||
|
if (Read8(data, 0) > 63) break;
|
||||||
|
|
||||||
|
if (Read32(data, oPrivate) != 0x100)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// bytes, not blocks.
|
||||||
|
bytes = Read32(data, oDataSize);
|
||||||
|
|
||||||
|
if (bytes % 512) break;
|
||||||
|
|
||||||
|
if (size < oUserData + bytes) break;
|
||||||
|
|
||||||
|
// todo -- checksum.
|
||||||
|
checksum = Read32(data, oDataChecksum);
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
throw Exception(__METHOD__ ": Invalid file format.");
|
||||||
|
|
||||||
|
uint32_t cs = Checksum(oUserData + (uint8_t *)data, bytes);
|
||||||
|
|
||||||
|
if (cs != checksum)
|
||||||
|
{
|
||||||
|
fprintf(stderr, __METHOD__ ": Warning: checksum invalid.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiskCopy42Image::write(unsigned block, const void *bp)
|
||||||
|
{
|
||||||
|
DiskImage::write(block, bp);
|
||||||
|
_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockCache *DiskCopy42Image::createBlockCache()
|
||||||
|
{
|
||||||
|
// if not readonly, mark changed so crc will be updated at close.
|
||||||
|
|
||||||
|
if (!readOnly()) _changed = true;
|
||||||
|
|
||||||
|
return new MappedBlockCache(this, address());
|
||||||
|
}
|
38
Device/DiskCopy42Image.h
Normal file
38
Device/DiskCopy42Image.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#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 DiskCopy42Image *Create(const char *name, size_t blocks);
|
||||||
|
static DiskCopy42Image *Create(const char *name, size_t blocks, const char *vname);
|
||||||
|
|
||||||
|
static DiskCopy42Image *Open(MappedFile *);
|
||||||
|
|
||||||
|
static uint32_t Checksum(void *data, size_t size);
|
||||||
|
|
||||||
|
virtual void write(unsigned block, const void *bp);
|
||||||
|
|
||||||
|
|
||||||
|
virtual BlockCache *createBlockCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
DiskCopy42Image();
|
||||||
|
|
||||||
|
DiskCopy42Image(MappedFile *);
|
||||||
|
static void Validate(MappedFile *);
|
||||||
|
bool _changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
192
Device/DiskImage.cpp
Normal file
192
Device/DiskImage.cpp
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
|
||||||
|
#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 <ProFUSE/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace Device;
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
DiskImage::DiskImage(const char *name, bool readOnly)
|
||||||
|
{
|
||||||
|
File fd(name, readOnly ? O_RDONLY : O_RDWR);
|
||||||
|
MappedFile mf(fd, readOnly);
|
||||||
|
_file.adopt(mf);
|
||||||
|
|
||||||
|
_blocks = 0;
|
||||||
|
_readOnly = readOnly;
|
||||||
|
_adaptor = NULL;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
DiskImage::DiskImage(MappedFile *file)
|
||||||
|
{
|
||||||
|
_file.adopt(*file);
|
||||||
|
|
||||||
|
_blocks = 0;
|
||||||
|
_readOnly = _file.readOnly();
|
||||||
|
_adaptor = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DiskImage::~DiskImage()
|
||||||
|
{
|
||||||
|
delete _adaptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskImage::readOnly()
|
||||||
|
{
|
||||||
|
return _readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned DiskImage::blocks()
|
||||||
|
{
|
||||||
|
return _blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DiskImage::setAdaptor(Adaptor *adaptor)
|
||||||
|
{
|
||||||
|
delete _adaptor;
|
||||||
|
_adaptor = adaptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DiskImage::read(unsigned block, void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DiskImage::read"
|
||||||
|
|
||||||
|
if (block >= _blocks)
|
||||||
|
throw Exception(__METHOD__ ": Invalid block.");
|
||||||
|
|
||||||
|
_adaptor->readBlock(block, bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiskImage::write(unsigned block, const void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DiskImage::write"
|
||||||
|
|
||||||
|
if (block >= _blocks)
|
||||||
|
throw Exception(__METHOD__ ": Invalid block.");
|
||||||
|
|
||||||
|
_adaptor->writeBlock(block, bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiskImage::sync()
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DiskImage::sync"
|
||||||
|
|
||||||
|
if (_file.isValid()) return _file.sync();
|
||||||
|
|
||||||
|
throw Exception(__METHOD__ ": File not set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ProDOSOrderDiskImage::ProDOSOrderDiskImage(MappedFile *file) :
|
||||||
|
DiskImage(file)
|
||||||
|
{
|
||||||
|
// at this point, file is no longer valid.
|
||||||
|
|
||||||
|
setBlocks(length() / 512);
|
||||||
|
setAdaptor(new POAdaptor(address()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Create(const char *name, size_t blocks)
|
||||||
|
{
|
||||||
|
MappedFile *file = MappedFile::Create(name, blocks * 512);
|
||||||
|
return new ProDOSOrderDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProDOSOrderDiskImage *ProDOSOrderDiskImage::Open(MappedFile *file)
|
||||||
|
{
|
||||||
|
Validate(file);
|
||||||
|
return new ProDOSOrderDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProDOSOrderDiskImage::Validate(MappedFile *f)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "ProDOSOrderDiskImage::Validate"
|
||||||
|
|
||||||
|
if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set.");
|
||||||
|
|
||||||
|
size_t size = f->length();
|
||||||
|
|
||||||
|
if (size % 512)
|
||||||
|
throw Exception(__METHOD__ ": Invalid file format.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockCache *ProDOSOrderDiskImage::createBlockCache()
|
||||||
|
{
|
||||||
|
return new MappedBlockCache(this, address());
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark DOS Order Disk Image
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
DOSOrderDiskImage::DOSOrderDiskImage(const char *name, bool readOnly) :
|
||||||
|
DiskImage(name, readOnly)
|
||||||
|
{
|
||||||
|
Validate(file());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
DOSOrderDiskImage::DOSOrderDiskImage(MappedFile *file) :
|
||||||
|
DiskImage(file)
|
||||||
|
{
|
||||||
|
// at this point, file is no longer valid.
|
||||||
|
|
||||||
|
setBlocks(length() / 512);
|
||||||
|
setAdaptor(new DOAdaptor(address()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DOSOrderDiskImage *DOSOrderDiskImage::Create(const char *name, size_t blocks)
|
||||||
|
{
|
||||||
|
MappedFile *file = MappedFile::Create(name, blocks * 512);
|
||||||
|
return new DOSOrderDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
DOSOrderDiskImage *DOSOrderDiskImage::Open(MappedFile *file)
|
||||||
|
{
|
||||||
|
Validate(file);
|
||||||
|
return new DOSOrderDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DOSOrderDiskImage::Validate(MappedFile *f)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "DOSOrderDiskImage::Validate"
|
||||||
|
|
||||||
|
if (!f || !f->isValid()) throw Exception(__METHOD__ ": File not set.");
|
||||||
|
|
||||||
|
size_t size = f->length();
|
||||||
|
|
||||||
|
if (size % 512)
|
||||||
|
throw Exception(__METHOD__ ": Invalid file format.");
|
||||||
|
|
||||||
|
}
|
94
Device/DiskImage.h
Normal file
94
Device/DiskImage.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#ifndef __DISKIMAGE_H__
|
||||||
|
#define __DISKIMAGE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <ProFUSE/Exception.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 ProDOSOrderDiskImage *Create(const char *name, size_t blocks);
|
||||||
|
static ProDOSOrderDiskImage *Open(MappedFile *);
|
||||||
|
|
||||||
|
|
||||||
|
virtual BlockCache *createBlockCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProDOSOrderDiskImage();
|
||||||
|
|
||||||
|
|
||||||
|
ProDOSOrderDiskImage(MappedFile *);
|
||||||
|
static void Validate(MappedFile *);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DOSOrderDiskImage : public DiskImage {
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
static DOSOrderDiskImage *Create(const char *name, size_t blocks);
|
||||||
|
static DOSOrderDiskImage *Open(MappedFile *);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DOSOrderDiskImage();
|
||||||
|
|
||||||
|
DOSOrderDiskImage(MappedFile *);
|
||||||
|
static void Validate(MappedFile *);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
320
Device/RawDevice.cpp
Normal file
320
Device/RawDevice.cpp
Normal file
|
@ -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 <ProFUSE/Exception.h>
|
||||||
|
|
||||||
|
using namespace Device;
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
#ifdef __SUN__
|
||||||
|
void RawDevice::devSize(int fd)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::devSize"
|
||||||
|
|
||||||
|
struct dk_minfo minfo;
|
||||||
|
|
||||||
|
if (::ioctl(fd, DKIOCGMEDIAINFO, &minfo) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
|
||||||
|
|
||||||
|
_size = minfo.dki_lbsize * minfo.dki_capacity;
|
||||||
|
_blockSize = 512; // not really, but whatever.
|
||||||
|
_blocks = _size / 512;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
void RawDevice::devSize(int fd)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::devSize"
|
||||||
|
|
||||||
|
uint32_t blockSize; // 32 bit
|
||||||
|
uint64_t blockCount; // 64 bit
|
||||||
|
|
||||||
|
if (::ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine block size.", errno);
|
||||||
|
|
||||||
|
|
||||||
|
if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &blockCount) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine block count.", errno);
|
||||||
|
|
||||||
|
_blockSize = blockSize;
|
||||||
|
_size = _blockSize * blockCount;
|
||||||
|
_blocks = _size / 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
void RawDevice::devSize(int fd)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::devSize"
|
||||||
|
|
||||||
|
int blocks;
|
||||||
|
|
||||||
|
if (::ioctl(fd, BLKGETSIZE, &blocks) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
|
||||||
|
|
||||||
|
_size = 512 * blocks;
|
||||||
|
_blockSize = 512; //
|
||||||
|
_blocks = blocks;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO -- FreeBSD/NetBSD/OpenBSD
|
||||||
|
|
||||||
|
#ifdef __FREEBSD__
|
||||||
|
|
||||||
|
void RawDevice::devSize(int fd)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::devSize"
|
||||||
|
|
||||||
|
unsigned blockSize;
|
||||||
|
off_t mediaSize;
|
||||||
|
|
||||||
|
if (::ioctl(fd, DIOCGSECTORSIZE, &blockSize)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine block size.", errno);
|
||||||
|
|
||||||
|
if (::ioctl(fd, DIOCGMEDIASIZE, &mediaSize)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine media size.", errno);
|
||||||
|
|
||||||
|
_blockSize = blockSize;
|
||||||
|
_size = mediaSize;
|
||||||
|
_blocks = mediaSize / 512;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __minix
|
||||||
|
|
||||||
|
void RawDevice::devSize(int fd)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::devSize"
|
||||||
|
|
||||||
|
struct partition entry;
|
||||||
|
|
||||||
|
|
||||||
|
if (::ioctl(fd, DIOCGETP, &entry) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": Unable to determine device size.", errno);
|
||||||
|
|
||||||
|
_size = entry.size
|
||||||
|
_blockSize = 512; // not really but whatever.
|
||||||
|
_blocks = _size / 512;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RawDevice::RawDevice(const char *name, File::FileFlags flags) :
|
||||||
|
_file(name, flags)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::RawDevice"
|
||||||
|
|
||||||
|
|
||||||
|
if (!_file.isValid())
|
||||||
|
{
|
||||||
|
throw Exception(__METHOD__ ": Invalid file handle.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_readOnly = flags == File::ReadOnly;
|
||||||
|
_size = 0;
|
||||||
|
_blocks = 0;
|
||||||
|
_blockSize = 0;
|
||||||
|
|
||||||
|
|
||||||
|
devSize(_file.fd());
|
||||||
|
}
|
||||||
|
|
||||||
|
RawDevice::RawDevice(File& file, File::FileFlags flags) :
|
||||||
|
_file(file)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::RawDevice"
|
||||||
|
|
||||||
|
|
||||||
|
if (!_file.isValid())
|
||||||
|
{
|
||||||
|
throw Exception(__METHOD__ ": Invalid file handle.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_readOnly = flags == File::ReadOnly;
|
||||||
|
_size = 0;
|
||||||
|
_blocks = 0;
|
||||||
|
_blockSize = 0;
|
||||||
|
|
||||||
|
|
||||||
|
devSize(_file.fd());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RawDevice::~RawDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RawDevice *RawDevice::Open(const char *name, File::FileFlags flags)
|
||||||
|
{
|
||||||
|
return new RawDevice(name, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RawDevice::read(unsigned block, void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::read"
|
||||||
|
|
||||||
|
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
|
||||||
|
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
|
||||||
|
|
||||||
|
// sun -- use pread
|
||||||
|
// apple - read full native block(s) ?
|
||||||
|
|
||||||
|
off_t offset = block * 512;
|
||||||
|
ssize_t ok = ::pread(_file.fd(), bp, 512, offset);
|
||||||
|
|
||||||
|
// TODO -- EINTR?
|
||||||
|
if (ok != 512)
|
||||||
|
throw ok < 0
|
||||||
|
? POSIXException(__METHOD__ ": Error reading block.", errno)
|
||||||
|
: Exception(__METHOD__ ": Error reading block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RawDevice::read(TrackSector ts, void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::read"
|
||||||
|
|
||||||
|
unsigned block = ts.track * 8 + ts.sector / 2;
|
||||||
|
if (block >= _blocks) throw Exception(__METHOD__ ": Invalid block number.");
|
||||||
|
if (bp == 0) throw Exception(__METHOD__ ": Invalid address.");
|
||||||
|
|
||||||
|
// sun -- use pread
|
||||||
|
// apple - read full native block(s) ?
|
||||||
|
|
||||||
|
off_t offset = (ts.track * 16 + ts.sector) * 256;
|
||||||
|
ssize_t ok = ::pread(_file.fd(), bp, 256, offset);
|
||||||
|
|
||||||
|
// TODO -- EINTR?
|
||||||
|
if (ok != 256)
|
||||||
|
throw ok < 0
|
||||||
|
? POSIXException(__METHOD__ ": Error reading block.", errno)
|
||||||
|
: Exception(__METHOD__ ": Error reading block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RawDevice::write(unsigned block, const void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::write"
|
||||||
|
|
||||||
|
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
|
||||||
|
|
||||||
|
if (_readOnly)
|
||||||
|
throw Exception(__METHOD__ ": File is readonly.");
|
||||||
|
|
||||||
|
|
||||||
|
off_t offset = block * 512;
|
||||||
|
ssize_t ok = ::pwrite(_file.fd(), bp, 512, offset);
|
||||||
|
|
||||||
|
if (ok != 512)
|
||||||
|
throw ok < 0
|
||||||
|
? POSIXException(__METHOD__ ": Error writing block.", errno)
|
||||||
|
: Exception(__METHOD__ ": Error writing block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RawDevice::write(TrackSector ts, const void *bp)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::write"
|
||||||
|
|
||||||
|
unsigned block = ts.track * 8 + ts.sector / 2;
|
||||||
|
if (block > _blocks) throw Exception(__METHOD__ ": Invalid block number.");
|
||||||
|
|
||||||
|
if (_readOnly)
|
||||||
|
throw Exception(__METHOD__ ": File is readonly.");
|
||||||
|
|
||||||
|
|
||||||
|
off_t offset = (ts.track * 16 + ts.sector) * 256;
|
||||||
|
ssize_t ok = ::pwrite(_file.fd(), bp, 256, offset);
|
||||||
|
|
||||||
|
if (ok != 256)
|
||||||
|
throw ok < 0
|
||||||
|
? POSIXException(__METHOD__ ": Error writing block.", errno)
|
||||||
|
: Exception(__METHOD__ ": Error writing block.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RawDevice::readOnly()
|
||||||
|
{
|
||||||
|
return _readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RawDevice::mapped()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned RawDevice::blocks()
|
||||||
|
{
|
||||||
|
return _blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RawDevice::sync()
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "RawDevice::sync"
|
||||||
|
|
||||||
|
if (_readOnly) return;
|
||||||
|
|
||||||
|
if (::fsync(_file.fd()) < 0)
|
||||||
|
throw POSIXException(__METHOD__ ": fsync error.", errno);
|
||||||
|
}
|
||||||
|
|
57
Device/RawDevice.h
Normal file
57
Device/RawDevice.h
Normal file
|
@ -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 RawDevice *Open(const char *name, File::FileFlags flags);
|
||||||
|
|
||||||
|
|
||||||
|
virtual ~RawDevice();
|
||||||
|
|
||||||
|
virtual void read(unsigned block, void *bp);
|
||||||
|
virtual void read(TrackSector ts, void *bp);
|
||||||
|
|
||||||
|
virtual void write(unsigned block, const void *bp);
|
||||||
|
virtual void write(TrackSector ts, const void *bp);
|
||||||
|
|
||||||
|
virtual bool readOnly();
|
||||||
|
virtual bool mapped();
|
||||||
|
virtual void sync();
|
||||||
|
|
||||||
|
virtual unsigned blocks();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
RawDevice(const char *name, File::FileFlags flags);
|
||||||
|
|
||||||
|
RawDevice(File& file, File::FileFlags flags);
|
||||||
|
|
||||||
|
void devSize(int fd);
|
||||||
|
|
||||||
|
File _file;
|
||||||
|
bool _readOnly;
|
||||||
|
|
||||||
|
uint64_t _size; // size of device in bytes.
|
||||||
|
unsigned _blocks; // # of 512k blocks i.e. _size / 512
|
||||||
|
|
||||||
|
unsigned _blockSize; // native block size.
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
19
Device/TrackSector.h
Normal file
19
Device/TrackSector.h
Normal file
|
@ -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
|
154
Device/UniversalDiskImage.cpp
Normal file
154
Device/UniversalDiskImage.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
#include <Device/UniversalDiskImage.h>
|
||||||
|
|
||||||
|
#include <Endian/Endian.h>
|
||||||
|
#include <Endian/IOBuffer.h>
|
||||||
|
|
||||||
|
#include <ProFUSE/Exception.h>
|
||||||
|
|
||||||
|
#include <Cache/MappedBlockCache.h>
|
||||||
|
#include <Cache/ConcreteBlockCache.h>
|
||||||
|
|
||||||
|
using namespace Device;
|
||||||
|
using namespace LittleEndian;
|
||||||
|
|
||||||
|
using ProFUSE::Exception;
|
||||||
|
using ProFUSE::POSIXException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
|
||||||
|
DiskImage(file)
|
||||||
|
{
|
||||||
|
|
||||||
|
// at this point, file is no longer valid.
|
||||||
|
|
||||||
|
uint8_t * data = (uint8_t *)address();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_format = Read32(data, 0x0c);
|
||||||
|
_flags = Read32(data, 0x10);
|
||||||
|
_blocks = Read32(data, 0x14);
|
||||||
|
|
||||||
|
_dataOffset = Read32(data, 0x18);
|
||||||
|
_dataLength = Read32(data, 0x1c);
|
||||||
|
|
||||||
|
|
||||||
|
setBlocks(_blocks);
|
||||||
|
// TODO -- DO, Nibble support.
|
||||||
|
setAdaptor(new POAdaptor(_dataOffset + data));
|
||||||
|
}
|
||||||
|
|
||||||
|
UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
|
||||||
|
{
|
||||||
|
// 64-byte header.
|
||||||
|
MappedFile *file = MappedFile::Create(name, blocks * 512 + 64);
|
||||||
|
|
||||||
|
uint8_t tmp[64];
|
||||||
|
|
||||||
|
IOBuffer header(tmp, 64);
|
||||||
|
|
||||||
|
|
||||||
|
// magic + creator
|
||||||
|
header.writeBytes("2IMGPRFS", 8);
|
||||||
|
|
||||||
|
// header size.
|
||||||
|
header.write16(64);
|
||||||
|
|
||||||
|
// version
|
||||||
|
header.write16(1);
|
||||||
|
|
||||||
|
//image format -- ProDOS order
|
||||||
|
header.write32(1);
|
||||||
|
|
||||||
|
// flags
|
||||||
|
header.write32(0);
|
||||||
|
|
||||||
|
// # blocks. s/b 0 unless prodos-order
|
||||||
|
header.write32(blocks);
|
||||||
|
|
||||||
|
// offset to disk data
|
||||||
|
header.write32(64);
|
||||||
|
|
||||||
|
// data length
|
||||||
|
header.write32(512 * blocks);
|
||||||
|
|
||||||
|
// comment offset, creator, reserved -- 0.
|
||||||
|
header.setOffset(64, true);
|
||||||
|
|
||||||
|
std::memcpy(file->address(), header.buffer(), 64);
|
||||||
|
|
||||||
|
|
||||||
|
return new UniversalDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file)
|
||||||
|
{
|
||||||
|
Validate(file);
|
||||||
|
return new UniversalDiskImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO -- support dos-order & nibblized
|
||||||
|
* TODO -- honor read-only flag.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void UniversalDiskImage::Validate(MappedFile *file)
|
||||||
|
{
|
||||||
|
#undef __METHOD__
|
||||||
|
#define __METHOD__ "UniversalDiskImage::Validate"
|
||||||
|
|
||||||
|
const void *data = file->address();
|
||||||
|
size_t size = file->length();
|
||||||
|
bool ok = false;
|
||||||
|
unsigned blocks = 0;
|
||||||
|
unsigned offset = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
if (size < 64) break;
|
||||||
|
|
||||||
|
if (std::memcmp(data, "2IMG", 4)) break;
|
||||||
|
|
||||||
|
// only prodos supported, for now...
|
||||||
|
// TODO -- Dos Order, Nibble support.
|
||||||
|
if (Read32(data, 0x0c) != 1) break;
|
||||||
|
|
||||||
|
blocks = Read32(data, 0x14);
|
||||||
|
offset = Read32(data, 0x18);
|
||||||
|
|
||||||
|
// file size == blocks * 512
|
||||||
|
if (Read32(data, 0x1c) != blocks * 512) break;
|
||||||
|
|
||||||
|
if (offset + blocks * 512 > size) break;
|
||||||
|
|
||||||
|
ok = true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
throw Exception(__METHOD__ ": Invalid file format.");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool UniversalDiskImage::readOnly()
|
||||||
|
{
|
||||||
|
return (_flags & 0x8000000) || DiskImage::readOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockCache *UniversalDiskImage::createBlockCache()
|
||||||
|
{
|
||||||
|
if (_format == 1)
|
||||||
|
{
|
||||||
|
return new MappedBlockCache(this, _dataOffset + (uint8_t *)address());
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiskImage::createBlockCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
41
Device/UniversalDiskImage.h
Normal file
41
Device/UniversalDiskImage.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#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 UniversalDiskImage *Create(const char *name, size_t blocks);
|
||||||
|
static UniversalDiskImage *Open(MappedFile *);
|
||||||
|
|
||||||
|
virtual bool readOnly();
|
||||||
|
|
||||||
|
virtual BlockCache *createBlockCache();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
UniversalDiskImage();
|
||||||
|
|
||||||
|
UniversalDiskImage(MappedFile *);
|
||||||
|
static void Validate(MappedFile *);
|
||||||
|
|
||||||
|
uint32_t _format;
|
||||||
|
uint32_t _flags;
|
||||||
|
uint32_t _blocks;
|
||||||
|
uint32_t _dataOffset;
|
||||||
|
uint32_t _dataLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user