mirror of
https://github.com/ksherlock/profuse.git
synced 2026-04-23 06:31:22 +00:00
Compare commits
248 Commits
v1_trunk_2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cc4d44292 | ||
|
|
936c9b3983 | ||
|
|
4cf3294c26 | ||
|
|
3ae1edd5a9 | ||
|
|
a6531a0a61 | ||
|
|
0b7d67de2b | ||
|
|
1971c33ddf | ||
|
|
a40d29590a | ||
|
|
b4ce209d83 | ||
|
|
3885c06698 | ||
|
|
184b69747a | ||
|
|
5d1b81ecca | ||
|
|
d4a7107bde | ||
|
|
25ad5cf7f5 | ||
|
|
818ab5f602 | ||
|
|
9ad93a51a7 | ||
|
|
ef1874977b | ||
|
|
7508097176 | ||
|
|
1dd7becb2f | ||
|
|
5d219195bb | ||
|
|
e3aea1657f | ||
|
|
a686f7d646 | ||
|
|
3af649af7c | ||
|
|
82ecf63db6 | ||
|
|
1fe6327ab1 | ||
|
|
b39aeba7ce | ||
|
|
8778d0dcf2 | ||
|
|
b4db3e59ab | ||
|
|
02e3c4c532 | ||
|
|
ad6fd87c3e | ||
|
|
304164ecfa | ||
|
|
84e0c8e08c | ||
|
|
e6f655a2c9 | ||
|
|
c3f02b28da | ||
|
|
f70b5897de | ||
|
|
32bb5d79b9 | ||
|
|
5f893f78fc | ||
|
|
3a671f985c | ||
|
|
5930e28f54 | ||
|
|
efd5e7bd98 | ||
|
|
e68854ff58 | ||
|
|
9746117f71 | ||
|
|
89e80dcc10 | ||
|
|
e71ab41481 | ||
|
|
011c3b7b44 | ||
|
|
2243d8d136 | ||
|
|
78d35bba08 | ||
|
|
8e259ef1f2 | ||
|
|
f373fa436e | ||
|
|
b07c64361d | ||
|
|
539aa1310c | ||
|
|
6b6ad10ba0 | ||
|
|
06bd252ebf | ||
|
|
8bda443e0e | ||
|
|
d9c9d49872 | ||
|
|
70b921ffa4 | ||
|
|
98fd4a1af9 | ||
|
|
670254034a | ||
|
|
cd1e227fc5 | ||
|
|
1894a178d1 | ||
|
|
345927d4b2 | ||
|
|
fedcc06840 | ||
|
|
79d8352018 | ||
|
|
7a327fb024 | ||
|
|
96f1de7206 | ||
|
|
f81205b63f | ||
|
|
6a4e4f4f7f | ||
|
|
911ca8637d | ||
|
|
1b0f3ba4f5 | ||
|
|
3d8bed44b3 | ||
|
|
756dd6b769 | ||
|
|
f07a6ecd8b | ||
|
|
e3d0514c85 | ||
|
|
e66058320f | ||
|
|
e2b20ccfba | ||
|
|
c4ea7cc0f8 | ||
|
|
2a87b480a1 | ||
|
|
2f307b3f19 | ||
|
|
d46406ff84 | ||
|
|
7df4d4840e | ||
|
|
126327d019 | ||
|
|
9098a442c9 | ||
|
|
56ee715e01 | ||
|
|
80a0061d92 | ||
|
|
525fe7810a | ||
|
|
7cb873bfcf | ||
|
|
d4a8391650 | ||
|
|
e4efb135ed | ||
|
|
54a75af101 | ||
|
|
7be0fc5c56 | ||
|
|
10190d13a2 | ||
|
|
0fda850b94 | ||
|
|
9cf52398a2 | ||
|
|
776475f33e | ||
|
|
42c6839fcb | ||
|
|
c4849f78e5 | ||
|
|
4de285c9e1 | ||
|
|
724d3c5ac3 | ||
|
|
fdd622535b | ||
|
|
0d8ca19a1a | ||
|
|
6468724573 | ||
|
|
adab6265b8 | ||
|
|
0db1d92287 | ||
|
|
e59ebb4d53 | ||
|
|
d6f90048d1 | ||
|
|
d1cf4ebfff | ||
|
|
5f252e4dac | ||
|
|
624af11ca1 | ||
|
|
06166c2d72 | ||
|
|
8acc47bb87 | ||
|
|
7b80e2e089 | ||
|
|
2e4b2961eb | ||
|
|
ef4f906d70 | ||
|
|
4a98605123 | ||
|
|
defd397d1b | ||
|
|
77653743ec | ||
|
|
1e09c72aba | ||
|
|
895bab385c | ||
|
|
deec48b728 | ||
|
|
949ed1e16c | ||
|
|
433ed40922 | ||
|
|
a7a625dc3f | ||
|
|
ca431e43f9 | ||
|
|
c901f55b50 | ||
|
|
0af8ce500a | ||
|
|
cff29a4736 | ||
|
|
91e2aaac70 | ||
|
|
da84b5bca2 | ||
|
|
9c95d74cba | ||
|
|
f530a9d748 | ||
|
|
e7b4c96984 | ||
|
|
d9bc18bdee | ||
|
|
9cd20f791f | ||
|
|
955986bec0 | ||
|
|
31e5f9091b | ||
|
|
f91009d870 | ||
|
|
bc5ffeab05 | ||
|
|
fc7c205fe0 | ||
|
|
ec9ac20528 | ||
|
|
a2bea3d8f7 | ||
|
|
4fe15a8e99 | ||
|
|
6dd25c9be5 | ||
|
|
1a27d76d67 | ||
|
|
e8582d0edb | ||
|
|
545dd02842 | ||
|
|
e53674e015 | ||
|
|
77fda944ae | ||
|
|
22afe5d128 | ||
|
|
699a6e02fb | ||
|
|
629efe6da5 | ||
|
|
be7a7bafc1 | ||
|
|
f606f8c0f5 | ||
|
|
03c0e34201 | ||
|
|
f13263d5ad | ||
|
|
56ff80afcc | ||
|
|
bcf6cd3890 | ||
|
|
33f209690e | ||
|
|
7b729f088f | ||
|
|
3b2bbb4e99 | ||
|
|
ea4f0c4485 | ||
|
|
035d802d70 | ||
|
|
e1d01c5298 | ||
|
|
82167f07f9 | ||
|
|
07f1244834 | ||
|
|
1ac9b03e47 | ||
|
|
12385a19dc | ||
|
|
b83d2df14b | ||
|
|
6d4e06b403 | ||
|
|
2754cdd24a | ||
|
|
a750f2ff5f | ||
|
|
fbc49a398e | ||
|
|
58ffa151e3 | ||
|
|
9bca60e977 | ||
|
|
525b69a333 | ||
|
|
616411809b | ||
|
|
ae52676bf2 | ||
|
|
07e7fef676 | ||
|
|
21906f7b9b | ||
|
|
77d7021e90 | ||
|
|
3a0871a158 | ||
|
|
b18551dea9 | ||
|
|
2e2d38e8c5 | ||
|
|
7fa5712533 | ||
|
|
d10aacbe93 | ||
|
|
bd063227e2 | ||
|
|
70dfd5b485 | ||
|
|
c127bd185c | ||
|
|
6400af5fc8 | ||
|
|
5a0fc071a5 | ||
|
|
81e532b25b | ||
|
|
b29c12d047 | ||
|
|
98d80e4e35 | ||
|
|
c80ef92da8 | ||
|
|
7e9e3a5b44 | ||
|
|
7d8a4e9907 | ||
|
|
23e6ad6592 | ||
|
|
e2c27e72f4 | ||
|
|
102998ec66 | ||
|
|
777c1c3424 | ||
|
|
ff90d3d8d3 | ||
|
|
e40355ecc5 | ||
|
|
96eb6a4d36 | ||
|
|
d3b89e328d | ||
|
|
332910a2ab | ||
|
|
e99ae3b596 | ||
|
|
4544a7d649 | ||
|
|
fbe95823d3 | ||
|
|
c2518dbccc | ||
|
|
e8c1af2d2c | ||
|
|
05e13f91a5 | ||
|
|
e3ef4d7c10 | ||
|
|
147a16085a | ||
|
|
72a5a28745 | ||
|
|
d0960d1266 | ||
|
|
6bbeb86da3 | ||
|
|
8d93501d0d | ||
|
|
0c14721b0d | ||
|
|
acd4072872 | ||
|
|
15315bad17 | ||
|
|
e3b89b2201 | ||
|
|
922494caa6 | ||
|
|
f469d4e4d4 | ||
|
|
99321a3f93 | ||
|
|
982b754713 | ||
|
|
2675797136 | ||
|
|
b8d728c781 | ||
|
|
9fc279ef64 | ||
|
|
5c48dcb813 | ||
|
|
dcd31997ed | ||
|
|
b660593d25 | ||
|
|
b3334ac210 | ||
|
|
da22eb1114 | ||
|
|
736746052b | ||
|
|
6f6338bc19 | ||
|
|
ee7736d7b8 | ||
|
|
1ba3d83ec7 | ||
|
|
cdd55ee726 | ||
|
|
5d10a7cbb6 | ||
|
|
8212b02088 | ||
|
|
2462938c3a | ||
|
|
2209b44225 | ||
|
|
9f0b5f1004 | ||
|
|
f8ddc09a70 | ||
|
|
8090387723 | ||
|
|
ebb502b1f5 | ||
|
|
23cf78b5ab | ||
|
|
a3cc7627b8 | ||
|
|
85c84a6a80 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
o/
|
||||
11
.travis.yml
Normal file
11
.travis.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
language: cpp
|
||||
# trusty has gcc 4.8
|
||||
sudo: required
|
||||
dist: trusty
|
||||
compiler:
|
||||
- clang++
|
||||
- g++
|
||||
script: make
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y fuse libfuse-dev
|
||||
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
|
||||
|
||||
56
Buffer.cpp
Normal file
56
Buffer.cpp
Normal file
@@ -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] );
|
||||
}
|
||||
}
|
||||
39
Buffer.h
Normal file
39
Buffer.h
Normal file
@@ -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
|
||||
71
Cache/BlockCache.cpp
Normal file
71
Cache/BlockCache.cpp
Normal file
@@ -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);
|
||||
}
|
||||
62
Cache/BlockCache.h
Normal file
62
Cache/BlockCache.h
Normal file
@@ -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
|
||||
380
Cache/ConcreteBlockCache.cpp
Normal file
380
Cache/ConcreteBlockCache.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
77
Cache/ConcreteBlockCache.h
Normal file
77
Cache/ConcreteBlockCache.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#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);
|
||||
|
||||
|
||||
// public so make_shared can access it.
|
||||
ConcreteBlockCache(BlockDevicePointer device, unsigned size);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
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
|
||||
139
Cache/MappedBlockCache.cpp
Normal file
139
Cache/MappedBlockCache.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
40
Cache/MappedBlockCache.h
Normal file
40
Cache/MappedBlockCache.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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);
|
||||
|
||||
|
||||
// public so make_shared can access it.
|
||||
MappedBlockCache(BlockDevicePointer device, void *data);
|
||||
|
||||
private:
|
||||
|
||||
void sync(unsigned block);
|
||||
|
||||
uint8_t *_data;
|
||||
bool _dirty;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
||||
44
Common/Exception.cpp
Normal file
44
Common/Exception.cpp
Normal file
@@ -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 "";
|
||||
}
|
||||
36
Common/Exception.h
Normal file
36
Common/Exception.h
Normal file
@@ -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
|
||||
27
Common/Lock.cpp
Normal file
27
Common/Lock.cpp
Normal file
@@ -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;
|
||||
}
|
||||
30
Common/Lock.h
Normal file
30
Common/Lock.h
Normal file
@@ -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
|
||||
102
Common/auto.h
Normal file
102
Common/auto.h
Normal file
@@ -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
|
||||
|
||||
35
Common/smart_pointers.h
Normal file
35
Common/smart_pointers.h
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
#ifndef __COMMON_SMART_POINTERS_H__
|
||||
#define __COMMON_SMART_POINTERS_H__
|
||||
|
||||
#if 1
|
||||
//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
|
||||
14
Common/unordered_map.h
Normal file
14
Common/unordered_map.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __COMMON_UNORDERED_MAP_H__
|
||||
#define __COMMON_UNORDERED_MAP_H__
|
||||
|
||||
#if 1
|
||||
//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
|
||||
486
Device/Adaptor.cpp
Normal file
486
Device/Adaptor.cpp
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
278
Device/BlockDevice.cpp
Normal file
278
Device/BlockDevice.cpp
Normal file
@@ -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);
|
||||
}
|
||||
69
Device/BlockDevice.h
Normal file
69
Device/BlockDevice.h
Normal file
@@ -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
|
||||
174
Device/DavexDiskImage.cpp
Normal file
174
Device/DavexDiskImage.cpp
Normal file
@@ -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());
|
||||
|
||||
}
|
||||
41
Device/DavexDiskImage.h
Normal file
41
Device/DavexDiskImage.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#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 *);
|
||||
|
||||
|
||||
DavexDiskImage(MappedFile *);
|
||||
private:
|
||||
DavexDiskImage();
|
||||
|
||||
bool _changed;
|
||||
std::string _volumeName;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
24
Device/Device.h
Normal file
24
Device/Device.h
Normal file
@@ -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
|
||||
240
Device/DiskCopy42Image.cpp
Normal file
240
Device/DiskCopy42Image.cpp
Normal file
@@ -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());
|
||||
}
|
||||
43
Device/DiskCopy42Image.h
Normal file
43
Device/DiskCopy42Image.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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();
|
||||
|
||||
|
||||
DiskCopy42Image();
|
||||
DiskCopy42Image(MappedFile *);
|
||||
|
||||
private:
|
||||
|
||||
bool _changed;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
226
Device/DiskImage.cpp
Normal file
226
Device/DiskImage.cpp
Normal file
@@ -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;
|
||||
}
|
||||
97
Device/DiskImage.h
Normal file
97
Device/DiskImage.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#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 *);
|
||||
|
||||
|
||||
ProDOSOrderDiskImage(MappedFile *);
|
||||
private:
|
||||
ProDOSOrderDiskImage();
|
||||
|
||||
};
|
||||
|
||||
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 *);
|
||||
|
||||
|
||||
DOSOrderDiskImage(MappedFile *);
|
||||
private:
|
||||
DOSOrderDiskImage();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#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 <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);
|
||||
}
|
||||
|
||||
55
Device/RawDevice.h
Normal file
55
Device/RawDevice.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#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();
|
||||
|
||||
|
||||
RawDevice(const char *name, File::FileFlags flags);
|
||||
RawDevice(File& file, File::FileFlags flags);
|
||||
private:
|
||||
|
||||
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
|
||||
220
Device/SDKImage.cpp
Normal file
220
Device/SDKImage.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
30
Device/SDKImage.h
Normal file
30
Device/SDKImage.h
Normal file
@@ -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 &);
|
||||
};
|
||||
}
|
||||
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
|
||||
163
Device/UniversalDiskImage.cpp
Normal file
163
Device/UniversalDiskImage.cpp
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
43
Device/UniversalDiskImage.h
Normal file
43
Device/UniversalDiskImage.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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 *);
|
||||
|
||||
|
||||
UniversalDiskImage(MappedFile *);
|
||||
private:
|
||||
|
||||
UniversalDiskImage();
|
||||
|
||||
uint32_t _format;
|
||||
uint32_t _flags;
|
||||
uint32_t _blocks;
|
||||
uint32_t _dataOffset;
|
||||
uint32_t _dataLength;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
107
Directory.cpp
Normal file
107
Directory.cpp
Normal file
@@ -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
|
||||
|
||||
105
Endian/Endian.cpp
Normal file
105
Endian/Endian.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
159
Endian/Endian.h
Normal file
159
Endian/Endian.h
Normal file
@@ -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
|
||||
103
Endian/IOBuffer.cpp.h
Normal file
103
Endian/IOBuffer.cpp.h
Normal file
@@ -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;
|
||||
|
||||
};
|
||||
15
Endian/IOBuffer.h
Normal file
15
Endian/IOBuffer.h
Normal file
@@ -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
|
||||
7
Endian/Makefile
Normal file
7
Endian/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
CC = g++
|
||||
CPPFLAGS += -g -Wall -I../
|
||||
|
||||
|
||||
all : Endian.o
|
||||
|
||||
Endian.o : Endian.cpp Endian.h
|
||||
173
Entry.cpp
Normal file
173
Entry.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
284
Entry.h
Normal file
284
Entry.h
Normal file
@@ -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
|
||||
124
File/File.cpp
Normal file
124
File/File.cpp
Normal file
@@ -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);
|
||||
}
|
||||
58
File/File.h
Normal file
58
File/File.h
Normal file
@@ -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
|
||||
185
File/MappedFile.cpp
Normal file
185
File/MappedFile.cpp
Normal file
@@ -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);
|
||||
}
|
||||
53
File/MappedFile.h
Normal file
53
File/MappedFile.h
Normal file
@@ -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
|
||||
|
||||
280
Makefile
Normal file
280
Makefile
Normal file
@@ -0,0 +1,280 @@
|
||||
CC = c++
|
||||
CPPFLAGS += -Wall -W -Wno-multichar -Wno-unused-parameter -Wno-unknown-pragmas -Wno-narrowing -I. -O2 -g -std=c++11
|
||||
LDFLAGS += -pthread
|
||||
UNAME = $(shell uname -s)
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
# should use pkg-config but it may not be installed.
|
||||
FUSE_LIBS += -losxfuse -pthread -liconv
|
||||
CPPFLAGS += -I/usr/local/include/osxfuse/fuse -D_FILE_OFFSET_BITS=64 -D_DARWIN_USE_64_BIT_INODE
|
||||
else
|
||||
CPPFLAGS += $(shell pkg-config --cflags fuse)
|
||||
FUSE_LIBS += $(shell pkg-config --libs fuse)
|
||||
endif
|
||||
|
||||
ifdef HAVE_NUFX
|
||||
DEVICE_OBJECTS += Device/SDKImage.o
|
||||
EXCEPTION_OBJECTS += NuFX/Exception.o
|
||||
LDFLAGS += -L/usr/local/lib/
|
||||
LIBS += -lnufx -lz
|
||||
|
||||
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:
|
||||
mkdir $@
|
||||
|
||||
o/xattr: bin/xattr.o | 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} | o
|
||||
$(CC) $(LDFLAGS) $^ $(LIBS) -o $@
|
||||
|
||||
o/apfm: bin/apfm.o \
|
||||
${CACHE_OBJECTS} \
|
||||
${DEVICE_OBJECTS} \
|
||||
${ENDIAN_OBJECTS} \
|
||||
${FILE_OBJECTS} \
|
||||
${COMMON_OBJECTS} \
|
||||
${EXCEPTION_OBJECTS} \
|
||||
${PASCAL_OBJECTS} | o
|
||||
$(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} | o
|
||||
$(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} | o
|
||||
$(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
|
||||
|
||||
|
||||
|
||||
63
NuFX/DirectoryEntry.cpp
Normal file
63
NuFX/DirectoryEntry.cpp
Normal file
@@ -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];
|
||||
}
|
||||
38
NuFX/DirectoryEntry.h
Normal file
38
NuFX/DirectoryEntry.h
Normal file
@@ -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
|
||||
66
NuFX/Entry.cpp
Normal file
66
NuFX/Entry.cpp
Normal file
@@ -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;
|
||||
}
|
||||
69
NuFX/Entry.h
Normal file
69
NuFX/Entry.h
Normal file
@@ -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
|
||||
10
NuFX/Exception.cpp
Normal file
10
NuFX/Exception.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "Exception.h"
|
||||
|
||||
namespace NuFX {
|
||||
|
||||
const char *NuFX::Exception::errorString()
|
||||
{
|
||||
return ::NuStrError((NuError)error());
|
||||
}
|
||||
|
||||
}
|
||||
35
NuFX/Exception.h
Normal file
35
NuFX/Exception.h
Normal file
@@ -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
|
||||
23
NuFX/FileEntry.h
Normal file
23
NuFX/FileEntry.h
Normal file
@@ -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
|
||||
14
NuFX/VolumeEntry.cpp
Normal file
14
NuFX/VolumeEntry.cpp
Normal file
@@ -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;
|
||||
}
|
||||
35
NuFX/VolumeEntry.h
Normal file
35
NuFX/VolumeEntry.h
Normal file
@@ -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
|
||||
13
POSIX/Exception.cpp
Normal file
13
POSIX/Exception.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
#include "Exception.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace POSIX {
|
||||
|
||||
const char *Exception::errorString()
|
||||
{
|
||||
return strerror(error());
|
||||
}
|
||||
|
||||
}
|
||||
34
POSIX/Exception.h
Normal file
34
POSIX/Exception.h
Normal file
@@ -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
|
||||
47
Pascal/Date.cpp
Normal file
47
Pascal/Date.cpp
Normal file
@@ -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);
|
||||
}
|
||||
48
Pascal/Date.h
Normal file
48
Pascal/Date.h
Normal file
@@ -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
|
||||
96
Pascal/Entry.cpp
Normal file
96
Pascal/Entry.cpp
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
89
Pascal/Entry.h
Normal file
89
Pascal/Entry.h
Normal file
@@ -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
|
||||
|
||||
734
Pascal/FileEntry.cpp
Normal file
734
Pascal/FileEntry.cpp
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
111
Pascal/FileEntry.h
Normal file
111
Pascal/FileEntry.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#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);
|
||||
|
||||
|
||||
FileEntry(const char *name, unsigned fileKind);
|
||||
FileEntry(void *vp);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
friend class VolumeEntry;
|
||||
|
||||
|
||||
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
|
||||
|
||||
14
Pascal/Makefile
Normal file
14
Pascal/Makefile
Normal file
@@ -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
|
||||
|
||||
48
Pascal/Makefile.old
Normal file
48
Pascal/Makefile.old
Normal file
@@ -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
|
||||
9
Pascal/Pascal.h
Normal file
9
Pascal/Pascal.h
Normal file
@@ -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
|
||||
92
Pascal/TextFile.cpp
Normal file
92
Pascal/TextFile.cpp
Normal file
@@ -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
Pascal/TextFile.h
Normal file
0
Pascal/TextFile.h
Normal file
104
Pascal/TextWriter.cpp
Normal file
104
Pascal/TextWriter.cpp
Normal file
@@ -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;
|
||||
}
|
||||
37
Pascal/TextWriter.h
Normal file
37
Pascal/TextWriter.h
Normal file
@@ -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
|
||||
1025
Pascal/VolumeEntry.cpp
Normal file
1025
Pascal/VolumeEntry.cpp
Normal file
File diff suppressed because it is too large
Load Diff
117
Pascal/VolumeEntry.h
Normal file
117
Pascal/VolumeEntry.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#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();
|
||||
|
||||
VolumeEntry(Device::BlockDevicePointer, const char *name);
|
||||
VolumeEntry(Device::BlockDevicePointer);
|
||||
|
||||
protected:
|
||||
virtual void writeDirectoryEntry(LittleEndian::IOBuffer *);
|
||||
|
||||
private:
|
||||
|
||||
friend class FileEntry;
|
||||
|
||||
|
||||
VolumeEntry();
|
||||
|
||||
|
||||
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
|
||||
|
||||
219
ProDOS/Bitmap.cpp
Normal file
219
ProDOS/Bitmap.cpp
Normal file
@@ -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;
|
||||
}
|
||||
53
ProDOS/Bitmap.h
Normal file
53
ProDOS/Bitmap.h
Normal file
@@ -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,11 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "File.h"
|
||||
#include <ProDOS/File.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <memory>
|
||||
#include <Common/smart_pointers.h>
|
||||
|
||||
|
||||
enum {
|
||||
@@ -40,6 +44,8 @@ enum {
|
||||
|
||||
};
|
||||
|
||||
class Disk;
|
||||
typedef SHARED_PTR(Disk) DiskPointer;
|
||||
|
||||
class Disk {
|
||||
|
||||
@@ -47,7 +53,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 +71,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
|
||||
|
||||
132
ProDOS/Exception.cpp
Normal file
132
ProDOS/Exception.cpp
Normal file
@@ -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 "";
|
||||
}
|
||||
|
||||
}
|
||||
97
ProDOS/Exception.h
Normal file
97
ProDOS/Exception.h
Normal file
@@ -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>
|
||||
18
ProDOS/common.h
Normal file
18
ProDOS/common.h
Normal file
@@ -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
|
||||
|
||||
1560
ProFUSE.xcodeproj/kelvin.pbxuser
Normal file
1560
ProFUSE.xcodeproj/kelvin.pbxuser
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1481
ProFUSE.xcodeproj/project.pbxproj
Normal file
1481
ProFUSE.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
11
README.md
Normal file
11
README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# profuse
|
||||
ProDOS file system for FUSE
|
||||
|
||||
## End of Life Notice
|
||||
|
||||
This software, while usable, is EOL. No further updates are expected.
|
||||
|
||||
ProFUSE 2.0, featuring read/write support for ProDOS and HFS, is available as part of
|
||||
[Golden Gate](http://golden-gate.ksherlock.com)
|
||||
|
||||
See the Wiki for details about the ProDOS file system.
|
||||
@@ -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
|
||||
|
||||
92
Volume.h
Normal file
92
Volume.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef __VOLUME_H__
|
||||
#define __VOLUME_H__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace ProFUSE {
|
||||
|
||||
class Bitmap;
|
||||
class BlockDevice;
|
||||
|
||||
class Volume;
|
||||
class Directory;
|
||||
class FileEntry;
|
||||
|
||||
|
||||
class Entry {
|
||||
public:
|
||||
Entry() : _address(0), _volume(NULL) { }
|
||||
virtual ~Entry();
|
||||
|
||||
unsigned address() { return _address; }
|
||||
void setAddress(unsigned address) { _address = address; }
|
||||
|
||||
Volume *volume() const { return _volume; }
|
||||
void setVolume(Volume *v) { _volume = v; }
|
||||
|
||||
private:
|
||||
// physical location on disk (block * 512 + offset)
|
||||
unsigned _address;
|
||||
Volume *_volume;
|
||||
};
|
||||
|
||||
class Directory {
|
||||
|
||||
FileEntry *childAtIndex(unsigned index);
|
||||
|
||||
private:
|
||||
unsigned _childCount;
|
||||
|
||||
std::vector<FileEntry *>_children;
|
||||
};
|
||||
|
||||
class FileEntry {
|
||||
virtual ~FileEntry();
|
||||
|
||||
virtual unsigned inode();
|
||||
Directory *directory();
|
||||
Directory *parent();
|
||||
|
||||
private:
|
||||
Directory *_parent;
|
||||
Volume *_volume;
|
||||
}
|
||||
|
||||
class Volume {
|
||||
public:
|
||||
|
||||
~Volume();
|
||||
|
||||
Volume *Create(BlockDevice *);
|
||||
Volume *Open(BlockDevice *);
|
||||
|
||||
|
||||
int allocBlock();
|
||||
|
||||
private:
|
||||
|
||||
Volume(BlockDevice *, int);
|
||||
|
||||
|
||||
Bitmap *_bitmap;
|
||||
BlockDevice *_device;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
class FileEntry : public Entry {
|
||||
public:
|
||||
virtual ~FileEntry();
|
||||
|
||||
unsigned inode();
|
||||
|
||||
private:
|
||||
_unsigned _inode;
|
||||
_unsigned _lookupCount;
|
||||
_unsigned _openCount;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
179
VolumeDirectory.cpp
Normal file
179
VolumeDirectory.cpp
Normal file
@@ -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);
|
||||
}
|
||||
1077
bin/apfm.cpp
Normal file
1077
bin/apfm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user