From 26757971368ce70ae60caab4b0fb9d87b09828c2 Mon Sep 17 00:00:00 2001 From: ksherlock Date: Thu, 10 Dec 2009 01:41:37 +0000 Subject: [PATCH] git-svn-id: https://profuse.googlecode.com/svn/branches/v2@134 aa027e90-d47c-11dd-86d7-074df07e0730 --- pascal/DateRec.cpp | 29 ++++ pascal/DateRec.h | 44 ++++++ pascal/File.cpp | 359 ++++++++++++++++++++++++++++++++++++++++++++ pascal/File.h | 111 ++++++++++++++ pascal/TextFile.cpp | 92 ++++++++++++ pascal/TextFile.h | 0 6 files changed, 635 insertions(+) create mode 100644 pascal/DateRec.cpp create mode 100644 pascal/DateRec.h create mode 100644 pascal/File.cpp create mode 100644 pascal/File.h create mode 100644 pascal/TextFile.cpp create mode 100644 pascal/TextFile.h diff --git a/pascal/DateRec.cpp b/pascal/DateRec.cpp new file mode 100644 index 0000000..3f42f5f --- /dev/null +++ b/pascal/DateRec.cpp @@ -0,0 +1,29 @@ +#include "DateRec.h" +#include + +using namespace Pascal; + + + +DateRec::DateRec(unsigned val) +{ + // yyyy yyym mmmm dddd + _day = val & 0xf; + _month = (val >> 4) & 0x1f; + _year = (val >> 9) & 0x7f; +} + +DateRec::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; + + return std::mktime(&tm); +} \ No newline at end of file diff --git a/pascal/DateRec.h b/pascal/DateRec.h new file mode 100644 index 0000000..8f9e2e4 --- /dev/null +++ b/pascal/DateRec.h @@ -0,0 +1,44 @@ +#ifndef __DATEREC_H__ +#define __DATEREC_H__ + +#include + +namespace Pascal { + +class DateRec { +public: + + DateRec(); + DateRec(unsigned yy, unsigned mm, unsigned dd); + DateRec(unsigned); + + operator std::time_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 DateRec::DateRec() +{ + _year = 0; + _month = 0; + _day = 0; +} + +inline DateRec::DateRec(unsigned yy, unsigned mm, unsigned dd) +{ + _year = yy; + _month = mm; + _day = dd; +} + +} + +#endif diff --git a/pascal/File.cpp b/pascal/File.cpp new file mode 100644 index 0000000..ae8ff86 --- /dev/null +++ b/pascal/File.cpp @@ -0,0 +1,359 @@ +#include "File.h" +#include "../auto.h" +#include "../Endian.h" +#include "../BlockDevice.h" + +#include +#include + +using namespace LittleEndian; +using namespace Pascal; + +#pragma mark - +#pragma mark DirEntry + +Entry::Entry() +{ + _firstBlock = 0; + _lastBlock = 0; + _fileKind = 0; + _inode = 0; +} + +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; +} + + +#pragma mark - +#pragma mark VolumeEntry + +VolumeEntry::VolumeEntry() +{ + _fileNameLength = 0; + std::memset(_fileName, 0, 8); + _lastVolumeBlock = 0; + _numberOfFiles = 0; + _accessTime = 0; + + setInode(1); + _inodeGenerator = 1; +} + +VolumeEntry::VolumeEntry(ProFUSE::BlockDevice *device) +{ + auto_array buffer(new uint8_t[512]); + unsigned blockCount; + + // read the header block, then load up all the header + // blocks. + + device->read(2, buffer.get()); + + init(tmp); + + // todo -- verify reasonable values. + blockCount = blocks(); + 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... + + try + { + for (unsigned i = 1; i < _numberFiles; ++i) + { + std::auto_ptr child; + + child.reset(new FileEntry(buffer.get() + i * 0x1a)); + + child->setInode(++_inodeGenerator); + _files.push_back(child.release()); + } + } + catch (...) + { + std::vectoriterator iter; + for(iter = _files.begin(); iter != _files.end(); ++iter) + { + if (*iter) delete *iter; + } + + throw; + } + +} + +VolumeEntry::~VolumeEntry() +{ + std::vectoriterator iter; + for(iter = _files.begin(); iter != _files.end(); ++iter) + { + if (*iter) delete *iter; + } +} + + +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); + _numberFile = Read16(vp, 0x10); + _accessTime = Read16(vp, 0x12); + _bootDate = DateRec(Read16(vp, 0x14); + + setInode(1); + _inodeGenerator = 1; +} + + +FileEntry *VolumeEntry::fileAtIndex(unsigned i) const +{ + return i < _files.length() ? _files[i] : NULL; +} + + + +#pragma mark - +#pragma mark FileEntry + +FileEntry::FileEntry(void *vp) : + Entry(vp) +{ + + +} + +unsigned File::fileSize() { + return (_lastBlock - _firstBlock - 1) * 512 + _lastByte; +} + +int File::read(uint8_t *buffer, unsigned size, unsigned offset) +{ + uint8_t tmp[512]; + + unsigned fileSize = fileSize(); + unsigned count = 0; + unsigned block = 0; + + if (offset >= fileSize) return 0; + + if (offset + size > fileSize) size = fileSize - offset; + + block = _startBlock + (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); + + _device->read(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) + { + _device->read(block++, buffer); + + buffer += 512; + count += 512; + size -= 512; + } + + /* + * 3. Read any trailing blocks. + */ + if (size) + { + _device->read(block, tmp); + std::memcpy(buffer, tmp, size); + + count += size; + + } + + + return count; +} + + + +int TextFile::read(uint8_t *buffer, unsigned size, unsigned offset) +{ + unsigned page = 0; + unsigned to = 0; + unsigned l = _pageSize.length(); + unsigned block; + unsigned count = 0; + + auto_array tmp; + unsigned tmpSize = 0; + + if (offset >= _fileSize) return 0; + if (offset + size > _fileSize) size = _fileSize - offset; + + + // find the first page. + for (page = 1; page < l; ++page) + { + if (to + _pageSize[page] > offset) + { + break; + } + + to += _pageSize[i]; + } + + --page; + + block = _startBlock + 2 + (page * 2); + + + // offset not needed anymore, + // convert to offset from *this* page. + offset -= to; + + while (size) + { + unsigned pageSize = _pageSize[page]; + if (pageSize > tmp) + { + tmp.reset(new uint8_t[pageSize]); + tmpSize = pageSize; + } + + bytes = std::min(size, pageSize - offset); + + decodePage(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 TextFile::decodePage(unsigned block, uint8_t *out) +{ + uint8_t buffer[1024]; + unsigned size = 0; + unsigned bytes = readPage(block, buffer); + + 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; + + if (out) for(unsigned i = 0; i < x; ++i) *out++ = ' '; + size += x; + } + else + { + if (out) *out++ = c; + size += 1; + } + } + + return size; +} + +unsigned TextFile::readPage(unsigned block, uint8_t *in) +{ + // reads up to 2 blocks. + // assumes block within _startBlock ... _endBlock - 1 + unsigned size = 0; + + _device->read(block, in); + if (block + 1 == _endBlock) + { + return _lastByte; + } + + _device->read(block + 1, in + 512); + if (block +2 == _endBlock) + { + return 512 + _lastByte; + } + + return 1024; +} + + + +void TextFile::init() +{ + // calculate the file size and page offsets. + + _pageSize.reserve((_endBlock - _startBlock - 2) / 2); + + _fileSize = 0; + for (unsigned block = _startBlock + 2; block < _endBlock; block += 2) + { + unsigned size = decodePage(block, NULL); + _fileSize += size; + _pageSize.push_back(size); + } +} \ No newline at end of file diff --git a/pascal/File.h b/pascal/File.h new file mode 100644 index 0000000..ab2edfc --- /dev/null +++ b/pascal/File.h @@ -0,0 +1,111 @@ +#ifndef __FILE_H__ +#define __FILE_H__ + +#include "DateRec.h" + +#include + +namespace ProFUSE { + class BlockDevice; +}; + +namespace Pascal { + +class FileEntry; + +class Entry { + +public: + + virtual ~Entry(); + + unsigned firstBlock() const { return _firstBlock; } + unsigned lastBlock() const { return _lastBlock; } + unsigned blocks() const { return _firstBlock - lastBlock; } + + unsigned fileKind() const { return _fileKind; } + + unsigned inode() const { return _inode; } + void setInode(unsigned inode) { _inode = inode; } + +protected: + + Entry(); + Entry(void *); + void init(void *); + + unsigned _firstBlock; + unsigned _lastBlock; + unsigned _fileKind; + + unsigned _inode; +} + + +class VolumeEntry : public Entry { + +public: + + VolumeEntry(ProFUSE::BlockDevice *); + virtual ~VolumeEntry(); + + const char *name() const { return _fileName; } + unsigned fileCount() const { return _numberFiles; } + + Pascal::DateRec lastBoot() const { return _lastBoot; } + + FileEntry *fileAtIndex(unsigned i) const; + +private: + + unsigned _fileNameLength; + char _fileName[8]; + unsigned _lastVolumeBlock; + unsigned _numberFiles; + unsigned _accessTime; + Pascal::DateRec _lastBoot; + + std::vector _files; + unsigned _inodeGenerator; + +}; + + +class FileEntry : public Entry { + public: + + FileEntry(void *vp); + virtual ~FileEntry(); + + unsigned fileSize(); + int read(uint8_t *buffer, unsigned size, unsigned offset); + + const char *name() const { return _fileName; } + DateRec modification() const { return _modification; } + + protected: + + unsigned _status; + + unsigned _fileNameLength; + char _fileName[16]; + + unsigned _lastByte; + DateRec _modification; + + // for text files. + + unsigned readTextPage(unsigned block, uint8_t *in); + unsigned decodeTextPage(unsigned block, uint8_t *out); + + + std::vector *_pageLengths; + unsigned _fileSize; + +}; + + + +} + +#endif diff --git a/pascal/TextFile.cpp b/pascal/TextFile.cpp new file mode 100644 index 0000000..727858c --- /dev/null +++ b/pascal/TextFile.cpp @@ -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 _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; + } + + +} + + diff --git a/pascal/TextFile.h b/pascal/TextFile.h new file mode 100644 index 0000000..e69de29