2009-12-10 01:41:37 +00:00
|
|
|
#include "File.h"
|
|
|
|
#include "../auto.h"
|
|
|
|
#include "../Endian.h"
|
|
|
|
#include "../BlockDevice.h"
|
2009-12-11 02:04:27 +00:00
|
|
|
#include "../BlockCache.h"
|
2009-12-10 01:41:37 +00:00
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
#include "IOBuffer.h"
|
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
#include <algorithm>
|
2009-12-10 01:41:37 +00:00
|
|
|
#include <cstring>
|
2009-12-11 00:59:53 +00:00
|
|
|
#include <memory>
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
using namespace LittleEndian;
|
|
|
|
using namespace Pascal;
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
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; }
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark DirEntry
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
unsigned Entry::ValidName(const char *cp, unsigned maxLength)
|
|
|
|
{
|
|
|
|
unsigned length;
|
|
|
|
|
|
|
|
if (!cp || !*cp) return 0;
|
|
|
|
|
|
|
|
if (!isalpaha(*cp)) return 0;
|
|
|
|
|
|
|
|
for (length = 1; cp[length]; ++length)
|
|
|
|
{
|
|
|
|
if (length >= maxLength) return 0;
|
|
|
|
|
|
|
|
if (!isalnumdot(cp[length])) return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
Entry::Entry()
|
|
|
|
{
|
|
|
|
_firstBlock = 0;
|
|
|
|
_lastBlock = 0;
|
|
|
|
_fileKind = 0;
|
|
|
|
_inode = 0;
|
2009-12-11 02:04:27 +00:00
|
|
|
_parent = NULL;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
void Entry::writeDirectoryEntry(IOBuffer *b)
|
|
|
|
{
|
|
|
|
b->write16(_firstBlock);
|
|
|
|
b->write16(_lastBlock);
|
|
|
|
b->write8(_fileKind);
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark VolumeEntry
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
unsigned VolumeEntry::ValidName(const char *cp)
|
|
|
|
{
|
|
|
|
return Entry::ValidName(cp, 7);
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
VolumeEntry::VolumeEntry()
|
|
|
|
{
|
|
|
|
_fileNameLength = 0;
|
|
|
|
std::memset(_fileName, 0, 8);
|
|
|
|
_lastVolumeBlock = 0;
|
2009-12-10 02:04:54 +00:00
|
|
|
_fileCount = 0;
|
2009-12-10 01:41:37 +00:00
|
|
|
_accessTime = 0;
|
|
|
|
|
|
|
|
setInode(1);
|
2009-12-11 02:04:27 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
_inodeGenerator = 1;
|
2009-12-11 02:04:27 +00:00
|
|
|
_cache = NULL;
|
|
|
|
_device = NULL;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
VolumeEntry::VolumeEntry(const char *name, ProFUSE::BlockDevice *device)
|
|
|
|
{
|
|
|
|
#undef __METHOD__
|
|
|
|
#define __METHOD__ "VolumeEntry::VolumeEntry"
|
|
|
|
|
|
|
|
unsigned length;
|
|
|
|
length = ValidName(name);
|
|
|
|
|
|
|
|
if (!length)
|
|
|
|
throw Exception(__METHOD__ ": Invalid volume name.");
|
|
|
|
|
|
|
|
_firstBlock = 2;
|
|
|
|
_lastBlock = 6;
|
|
|
|
_fileKind = kUntypedFile;
|
|
|
|
_inode = 1;
|
|
|
|
_inodeGenerator = 1;
|
|
|
|
|
|
|
|
_fileNameLength = length;
|
|
|
|
|
|
|
|
std::memset(_fileName, 0, sizeof(_fileName));
|
|
|
|
for (unsigned i = 0; i < _fileNameLength; ++i)
|
|
|
|
{
|
|
|
|
_fileName[i] = toupper(name[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastVolumeBlock = device->blocks();
|
|
|
|
_fileCount = 0;
|
|
|
|
_accessTime = 0;
|
|
|
|
_lastBoot = Date.Today();
|
|
|
|
|
|
|
|
_cache = device->blockCache();
|
|
|
|
_device = device;
|
|
|
|
|
|
|
|
for (unsigned i = 2; i < 6; ++i)
|
|
|
|
{
|
|
|
|
device->zeroBlock(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *vp = _blockCache->lock(2);
|
|
|
|
IOBuffer b(vp, 0x1a);
|
|
|
|
|
|
|
|
write(&b);
|
|
|
|
|
|
|
|
_blockCache->unlock(2, true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
VolumeEntry::VolumeEntry(ProFUSE::BlockDevice *device)
|
|
|
|
{
|
|
|
|
auto_array<uint8_t> buffer(new uint8_t[512]);
|
|
|
|
unsigned blockCount;
|
|
|
|
|
|
|
|
// read the header block, then load up all the header
|
|
|
|
// blocks.
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
_device = device;
|
|
|
|
_cache = device->blockCache();
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
device->read(2, buffer.get());
|
|
|
|
|
2009-12-10 02:04:54 +00:00
|
|
|
init(buffer.get());
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
// todo -- verify reasonable values.
|
2009-12-11 04:03:21 +00:00
|
|
|
|
|
|
|
//printf("%u %u\n", blocks(), _lastBlock - _firstBlock);
|
|
|
|
|
|
|
|
// why the fuck didn't this work????
|
2009-12-10 01:41:37 +00:00
|
|
|
blockCount = blocks();
|
2009-12-11 04:03:21 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
if (blockCount > 1)
|
|
|
|
{
|
|
|
|
buffer.reset(new uint8_t[512 * blockCount]);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < blockCount; ++i)
|
|
|
|
{
|
|
|
|
device->read(2 + i, buffer.get() + 512 * i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now load up all the children.
|
|
|
|
// if this throws, memory could be lost...
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
try
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
|
2009-12-11 04:03:21 +00:00
|
|
|
for (unsigned i = 1; i <= _fileCount; ++i)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
std::auto_ptr<FileEntry> child;
|
|
|
|
|
2009-12-11 04:03:21 +00:00
|
|
|
//
|
2009-12-10 01:41:37 +00:00
|
|
|
child.reset(new FileEntry(buffer.get() + i * 0x1a));
|
|
|
|
|
|
|
|
child->setInode(++_inodeGenerator);
|
2009-12-11 02:04:27 +00:00
|
|
|
child->_parent = this;
|
2009-12-10 01:41:37 +00:00
|
|
|
_files.push_back(child.release());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2009-12-10 02:04:54 +00:00
|
|
|
std::vector<FileEntry *>::iterator iter;
|
2009-12-10 01:41:37 +00:00
|
|
|
for(iter = _files.begin(); iter != _files.end(); ++iter)
|
|
|
|
{
|
|
|
|
if (*iter) delete *iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VolumeEntry::~VolumeEntry()
|
|
|
|
{
|
2009-12-10 02:04:54 +00:00
|
|
|
std::vector<FileEntry *>::iterator iter;
|
2009-12-10 01:41:37 +00:00
|
|
|
for(iter = _files.begin(); iter != _files.end(); ++iter)
|
|
|
|
{
|
|
|
|
if (*iter) delete *iter;
|
|
|
|
}
|
2009-12-11 02:04:27 +00:00
|
|
|
|
|
|
|
// _blockCache does not need deleting.
|
|
|
|
delete _device;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void VolumeEntry::init(void *vp)
|
|
|
|
{
|
|
|
|
Entry::init(vp);
|
|
|
|
_fileNameLength = Read8(vp, 6);
|
|
|
|
|
|
|
|
// verify filenamelength <= 7
|
|
|
|
// verify fileKind == 0
|
|
|
|
|
|
|
|
std::memcpy(_fileName, 7 + (uint8_t *)vp, _fileNameLength);
|
|
|
|
|
|
|
|
_lastVolumeBlock = Read16(vp, 0x0e);
|
2009-12-10 02:04:54 +00:00
|
|
|
_fileCount = Read16(vp, 0x10);
|
2009-12-10 01:41:37 +00:00
|
|
|
_accessTime = Read16(vp, 0x12);
|
2009-12-12 04:51:53 +00:00
|
|
|
_lastBoot = Date(Read16(vp, 0x14));
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
setInode(1);
|
|
|
|
_inodeGenerator = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FileEntry *VolumeEntry::fileAtIndex(unsigned i) const
|
|
|
|
{
|
2009-12-10 02:04:54 +00:00
|
|
|
return i < _files.size() ? _files[i] : NULL;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 02:18:05 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
void *VolumeEntry::loadBlock(unsigned block)
|
|
|
|
{
|
|
|
|
return _cache->load(block);
|
|
|
|
}
|
|
|
|
void VolumeEntry::unloadBlock(unsigned block, bool dirty)
|
|
|
|
{
|
|
|
|
return _cache->unload(block, dirty);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VolumeEntry::readBlock(unsigned block, void *buffer)
|
|
|
|
{
|
|
|
|
_device->read(block, buffer);
|
|
|
|
}
|
|
|
|
void VolumeEntry::writeBlock(unsigned block, void *buffer)
|
|
|
|
{
|
|
|
|
_device->write(block, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
|
|
|
|
void VolumeEntry::writeDirectoryEntry(IOBuffer *b)
|
|
|
|
{
|
|
|
|
Entry::writeDirectoryEntry(b);
|
|
|
|
|
|
|
|
b->write8(0); // reserved
|
|
|
|
b->write8(_fileNameLength);
|
|
|
|
b->writeBytes(_fileName, 7);
|
|
|
|
b->write16(_fileCount);
|
|
|
|
b->write16(_accessTime);
|
|
|
|
b->write16((unsigned)_lastBoot);
|
|
|
|
|
|
|
|
// rest is reserved.
|
|
|
|
b->writeZero(4);
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
#pragma mark -
|
|
|
|
#pragma mark FileEntry
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
unsigned FileEntry::ValidName(const char *cp)
|
|
|
|
{
|
|
|
|
return Entry::ValidName(cp, 15);
|
|
|
|
}
|
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
FileEntry::FileEntry(void *vp) :
|
|
|
|
Entry(vp)
|
|
|
|
{
|
2009-12-10 02:18:05 +00:00
|
|
|
_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);
|
2009-12-12 04:51:53 +00:00
|
|
|
_modification = Date(Read16(vp, 0x18));
|
2009-12-11 00:59:53 +00:00
|
|
|
|
|
|
|
_fileSize = 0;
|
2009-12-11 02:04:27 +00:00
|
|
|
_pageSize = NULL;
|
2009-12-10 02:18:05 +00:00
|
|
|
}
|
2009-12-10 01:41:37 +00:00
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
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 = kind;
|
|
|
|
_status = 0;
|
|
|
|
|
|
|
|
_fileNameLength = length;
|
|
|
|
std::memset(_fileName, 0, sizeof(_fileName));
|
|
|
|
for (unsigned i = 0; i < length; ++i)
|
|
|
|
_fileName[i] = toupper(name[i]);
|
|
|
|
|
|
|
|
_modification = Date::Today();
|
|
|
|
_lastByte = 0;
|
|
|
|
|
|
|
|
_fileSize = 0;
|
|
|
|
_pageSize = NULL;
|
|
|
|
}
|
|
|
|
|
2009-12-10 02:18:05 +00:00
|
|
|
FileEntry::~FileEntry()
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
delete _pageSize;
|
2009-12-10 02:18:05 +00:00
|
|
|
}
|
2009-12-10 01:41:37 +00:00
|
|
|
|
2009-12-10 02:18:05 +00:00
|
|
|
|
|
|
|
unsigned FileEntry::fileSize()
|
|
|
|
{
|
|
|
|
switch(fileKind())
|
|
|
|
{
|
|
|
|
case kTextFile:
|
|
|
|
return textFileSize();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return dataFileSize();
|
|
|
|
break;
|
|
|
|
}
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
2009-12-12 15:26:51 +00:00
|
|
|
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);
|
|
|
|
}
|
2009-12-10 02:18:05 +00:00
|
|
|
|
|
|
|
int FileEntry::read(uint8_t *buffer, unsigned size, unsigned offset)
|
|
|
|
{
|
2009-12-11 00:59:53 +00:00
|
|
|
unsigned fsize = fileSize();
|
|
|
|
|
|
|
|
if (offset + size > fsize) size = fsize - offset;
|
|
|
|
if (offset >= fsize) return 0;
|
|
|
|
|
2009-12-10 02:18:05 +00:00
|
|
|
switch(fileKind())
|
|
|
|
{
|
|
|
|
case kTextFile:
|
|
|
|
return textRead(buffer, size, offset);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return dataRead(buffer, size, offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-10 02:40:41 +00:00
|
|
|
unsigned FileEntry::dataFileSize()
|
|
|
|
{
|
|
|
|
return blocks() * 512 - 512 + _lastByte;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned FileEntry::textFileSize()
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
if (!_pageSize) textInit();
|
2009-12-10 02:40:41 +00:00
|
|
|
return _fileSize;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 02:40:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int FileEntry::dataRead(uint8_t *buffer, unsigned size, unsigned offset)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
uint8_t tmp[512];
|
|
|
|
|
|
|
|
unsigned count = 0;
|
|
|
|
unsigned block = 0;
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
block = _firstBlock + (offset / 512);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
parent()->readBlock(block++, tmp);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
std::memcpy(buffer, tmp + 512 - bytes, bytes);
|
|
|
|
|
|
|
|
buffer += bytes;
|
|
|
|
count += bytes;
|
|
|
|
size -= bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 2. read full blocks into the buffer.
|
|
|
|
*/
|
|
|
|
|
|
|
|
while (size >= 512)
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
parent()->readBlock(block++, buffer);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
buffer += 512;
|
|
|
|
count += 512;
|
|
|
|
size -= 512;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3. Read any trailing blocks.
|
|
|
|
*/
|
|
|
|
if (size)
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
parent()->readBlock(block, tmp);
|
2009-12-10 01:41:37 +00:00
|
|
|
std::memcpy(buffer, tmp, size);
|
|
|
|
|
|
|
|
count += size;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
int FileEntry::textRead(uint8_t *buffer, unsigned size, unsigned offset)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
unsigned page = 0;
|
|
|
|
unsigned to = 0;
|
|
|
|
unsigned block;
|
2009-12-11 02:04:27 +00:00
|
|
|
unsigned l;
|
2009-12-10 01:41:37 +00:00
|
|
|
unsigned count = 0;
|
|
|
|
|
|
|
|
auto_array<uint8_t> tmp;
|
|
|
|
unsigned tmpSize = 0;
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
if (!_pageSize) textInit();
|
|
|
|
|
|
|
|
l = _pageSize->size();
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
// find the first page.
|
|
|
|
for (page = 1; page < l; ++page)
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
unsigned pageSize = (*_pageSize)[page];
|
|
|
|
if (to + pageSize > offset)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
to += pageSize;
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
--page;
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
block = _firstBlock + 2 + (page * 2);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
// offset not needed anymore,
|
|
|
|
// convert to offset from *this* page.
|
|
|
|
offset -= to;
|
|
|
|
|
|
|
|
while (size)
|
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
unsigned pageSize = (*_pageSize)[page];
|
|
|
|
unsigned bytes = std::min(size, pageSize - offset);
|
|
|
|
|
|
|
|
if (pageSize > tmpSize)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
tmp.reset(new uint8_t[pageSize]);
|
|
|
|
tmpSize = pageSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
// can decode straight to buffer if size >= bytes && offset = 0.
|
|
|
|
textDecodePage(block, tmp.get());
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
std::memcpy(buffer, tmp.get() + offset, bytes);
|
|
|
|
|
|
|
|
|
|
|
|
block += 2;
|
|
|
|
page += 1;
|
|
|
|
|
|
|
|
size -= bytes;
|
|
|
|
buffer += bytes;
|
|
|
|
count += bytes;
|
|
|
|
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
unsigned FileEntry::textDecodePage(unsigned block, uint8_t *out)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
uint8_t buffer[1024];
|
|
|
|
unsigned size = 0;
|
2009-12-11 02:04:27 +00:00
|
|
|
unsigned bytes = textReadPage(block, buffer);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < bytes; ++i)
|
|
|
|
{
|
|
|
|
uint8_t c = buffer[i];
|
|
|
|
|
|
|
|
if (!c) break;
|
|
|
|
if (c == 16 && i != bytes - 1)
|
|
|
|
{
|
|
|
|
// DLE
|
|
|
|
// 16, n -> n-32 spaces.
|
|
|
|
unsigned x = buffer[++i] - 32;
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
if (out) for (unsigned i = 0; i < x; ++i) *out++ = ' ';
|
2009-12-10 01:41:37 +00:00
|
|
|
size += x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (out) *out++ = c;
|
|
|
|
size += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
unsigned FileEntry::textReadPage(unsigned block, uint8_t *in)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
// reads up to 2 blocks.
|
2009-12-11 02:04:27 +00:00
|
|
|
// assumes block within _startBlock ... _lastBlock - 1
|
2009-12-10 01:41:37 +00:00
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
parent()->readBlock(block, in);
|
|
|
|
if (block + 1 == _lastBlock)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
return _lastByte;
|
|
|
|
}
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
parent()->readBlock(block + 1, in + 512);
|
|
|
|
if (block +2 == _lastBlock)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
return 512 + _lastByte;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1024;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
void FileEntry::textInit()
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
|
|
|
// calculate the file size and page offsets.
|
|
|
|
|
2009-12-11 02:04:27 +00:00
|
|
|
_pageSize->reserve((_lastBlock - _firstBlock + 1 - 2) / 2);
|
2009-12-10 01:41:37 +00:00
|
|
|
|
|
|
|
_fileSize = 0;
|
2009-12-11 02:04:27 +00:00
|
|
|
for (unsigned block = _firstBlock + 2; block < _lastBlock; block += 2)
|
2009-12-10 01:41:37 +00:00
|
|
|
{
|
2009-12-11 02:04:27 +00:00
|
|
|
unsigned size = textDecodePage(block, NULL);
|
2009-12-10 01:41:37 +00:00
|
|
|
_fileSize += size;
|
2009-12-11 02:04:27 +00:00
|
|
|
_pageSize->push_back(size);
|
2009-12-10 01:41:37 +00:00
|
|
|
}
|
2009-12-10 02:04:54 +00:00
|
|
|
}
|
|
|
|
|