From 8090387723008d29d5abf59b8b5ed8bcddc1ed15 Mon Sep 17 00:00:00 2001 From: ksherlock Date: Sun, 22 Nov 2009 21:56:32 +0000 Subject: [PATCH] git-svn-id: https://profuse.googlecode.com/svn/branches/v2@94 aa027e90-d47c-11dd-86d7-074df07e0730 --- BlockDevice.cpp | 8 + BlockDevice.h | 2 + Buffer.h | 2 + DateTime.cpp | 144 ++++++++++++++++ DateTime.h | 96 +++++++++++ Directory.cpp | 425 ++++++++++++++++++++++++++++++++++++++++++------ Directory.h | 53 +++++- Endian.h | 219 +++++++++++++++++++++++++ 8 files changed, 889 insertions(+), 60 deletions(-) create mode 100644 DateTime.cpp create mode 100644 DateTime.h create mode 100644 Endian.h diff --git a/BlockDevice.cpp b/BlockDevice.cpp index feeae82..99a61f2 100644 --- a/BlockDevice.cpp +++ b/BlockDevice.cpp @@ -18,6 +18,14 @@ BlockDevice::~BlockDevice() { } +BlockDevice::zeroBlock(unsigned block) +{ + uint8_t bp[512]; + std::memset(bp, 0, 512); + + write(block, bp); +} + #pragma mark DiskImage diff --git a/BlockDevice.h b/BlockDevice.h index e024161..4a7ae92 100644 --- a/BlockDevice.h +++ b/BlockDevice.h @@ -19,6 +19,8 @@ public: virtual bool readOnly() = 0; virtual void sync() = 0; + + void zeroBlock(unsigned block); }; diff --git a/Buffer.h b/Buffer.h index 5a0ebf1..4436862 100644 --- a/Buffer.h +++ b/Buffer.h @@ -12,7 +12,9 @@ public: 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); } diff --git a/DateTime.cpp b/DateTime.cpp new file mode 100644 index 0000000..f406ee9 --- /dev/null +++ b/DateTime.cpp @@ -0,0 +1,144 @@ +#include "DateTime.h" + + +#include +#include +#include + +namespace ProDOS { + +/* + * date is a 16 bit value: + * + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * | Year | Month | Day | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + * time is a 16 bit value: + * + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * |0 0 0| Hour | |0 0| Minute | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + */ + + +/* + * ProDOS technote 28 + * + * The following definition allows the same range of years that the Apple IIgs + * Control Panel CDA currently does: + * + * o A seven-bit ProDOS year value is in the range 0 to 99 + * (100 through 127 are invalid) + * o Year values from 40 to 99 represent 1940 through 1999 + * o Year values from 0 to 39 represent 2000 through 2039 + */ + + +DateTime::DateTime() : + _yymmdd(0), _hhmm(0) +{ + init(std::time(NULL)); +} + +DateTime::DateTime(uint32_t dtm) : + _yymmdd((dtm >> 16) & 0xff), _hhmm(dtm & 0xff) +{ +} + +DateTime::DateTime(unsigned yymmdd, unsigned hhmm) : + _yymmdd(yymmdd), _hhmm(hhmm) +{ +} + +DateTime::DateTime(std::time_t time) : + _yymmdd(0), _hhmm(0) +{ + init(time); +} + + +DateTime::DateTime(unsigned year, unsigned month, unsigned day, + unsigned hour, unsigned minute) : + _yymmdd(0), _hhmm(0) +{ + init(year, month, day, hour, minute); +} + +void DateTime::init(std::time_t time) +{ + tm t; + ::localtime_r(&time, &t); + init(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min); +} + +void DateTime::init(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute) +{ + + //printf("%d %d %d %d %d\n", year, month, day, hour, minute); + + // 1940 is the earliest year, so clamp to 1940-01-01 00:00 + if (year < 1940) + { + _yymmdd = (40 << 9) | (1 << 5) | 1; + _hhmm = 0; + return; + } + // 2039 is the latest year, so clamp to 2039-12-31 23:59 + if (year > 2039) + { + _yymmdd = (39 << 9) | (12 << 5) | 31; + _hhmm = (23 << 8) | 59; + return; + } + + if (year >= 2000) year -= 2000; + else year -= 1900; + + _hhmm = minute | (hour << 8); + + _yymmdd = day | (month << 5) | (year << 9); +} + +unsigned DateTime::year() const +{ + unsigned tmp = _yymmdd >> 9; + //if (tmp > 100) return 0; + + if (tmp <= 39) tmp += 100; + + return tmp + 1900; +} + +/* + * A positive or 0 value for tm_isdst causes mktime() to presume initially + * that Daylight Savings Time, respectively, is or is not in effect for + * the specified time. A negative value for tm_isdst causes mktime() to + * attempt to determine whether Daylight Saving Time is in effect for the + * specified time. + */ + +std::time_t DateTime::toUnix() const +{ + tm t; + + if (_yymmdd == 0) return 0; + + std::memset(&t, 0, sizeof(tm)); + + t.tm_min = minute(); + t.tm_hour = hour(); + t.tm_isdst = -1; + + t.tm_mday = day(); + t.tm_mon = month() - 1; + t.tm_year = year() - 1900; + + return std::mktime(&t); + // convert back via locatime & fudge for dst? +} + +} // namespace diff --git a/DateTime.h b/DateTime.h new file mode 100644 index 0000000..9f3aa78 --- /dev/null +++ b/DateTime.h @@ -0,0 +1,96 @@ +#ifndef __PRODOS_DATE_TIME__ +#define __PRODOS_DATE_TIME__ + +#include +#include + +namespace ProDOS { + +class DateTime +{ +public: + DateTime(); + DateTime(std::time_t); + DateTime(uint32_t); + DateTime(unsigned, unsigned); + + DateTime(unsigned year, unsigned month, unsigned day, + unsigned hour, unsigned minute); + + std::time_t toUnix() const; + + + operator std::time_t() const; + operator uint32_t() const; + + unsigned date() const; + unsigned time() const; + + unsigned minute() const; + unsigned hour() const; + unsigned day() const; + unsigned month() const; + unsigned year() const; + +private: + void init(std::time_t); + void init(unsigned, unsigned, unsigned, unsigned, unsigned); + + unsigned _yymmdd; + unsigned _hhmm; +}; + + +inline DateTime::operator std::time_t() const +{ + return toUnix(); +} + +inline DateTime::operator uint32_t() const +{ + return (_yymmdd << 16) | _hhmm; +} + +inline unsigned DateTime::date() const +{ + return _yymmdd; +} + +inline unsigned DateTime::time() const +{ + return _hhmm; +} + +inline unsigned DateTime::minute() const +{ + return _hhmm & 0x3f; +} + +inline unsigned DateTime::hour() const +{ + return (_hhmm >> 8) & 0x1f; +} + +inline unsigned DateTime::day() const +{ + return _yymmdd & 0x1f; +} + +inline unsigned DateTime::month() const +{ + return (_yymmdd >> 5) & 0x0f; +} + +/* +inline unsigned DateTime::year() const +{ + unsigned tmp = _yymmdd >> 9; + if (tmp <= 39) tmp += 100; + return tmp + 1900; +} +*/ + +} // namespace + +#endif + diff --git a/Directory.cpp b/Directory.cpp index 83d7058..da017fa 100644 --- a/Directory.cpp +++ b/Directory.cpp @@ -1,10 +1,16 @@ -#include "Directory" #include +#include + +#include "Directory" +#include "Buffer.h" +#include "Endian.h" #include "Exception.h" + using namespace ProFUSE; +using namespace LittleEndian; static bool isalpha(unsigned c) { @@ -21,29 +27,18 @@ static bool isalnumdot(unsigned c) } -static uint16_t read16(uint8_t *data, unsigned offset) +static bool islower(unsigned c) { - return data[offset + 0] - | (data[offset + 1] << 8) ; -} - -static uint32_t read24(uint8_t *data, unsigned offset) -{ - return data[offset + 0] - | (data[offset + 1] << 8) - | (data[offset + 2] << 16) ; + return c >= 'a' && c <= 'z'; } -static uint32_t read32(uint8_t *data, unsigned offset) -{ - return data[offset + 0] - | (data[offset + 1] << 8) - | (data[offset + 2] << 16) ; - | (data[offset + 3] << 24) ; -} - -unsigned ValidName(const char *name) +/* + * ProDOS names: + * 1-15 letters, digits, or '.' + * first character must be a letter. + */ +unsigned Entry::ValidName(const char *name) { unsigned length; @@ -61,48 +56,148 @@ unsigned ValidName(const char *name) return length; } -Directory::Directory(unsigned type, const char *name) +Entry::Entry(unsigned type, const char *name) +{ + _storageType = type; + _address = 0; + _volume = NULL; + + // 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; + _volume = NULL; + _caseFlag = 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] = _name[i] | 0x20; + } + } +} + +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] = c & ~0x20; // upper + if (islower(c)) + { + caseFlag |= (0x4000 >> i); + } + } + + _length = length; + _caseFlag = caseFlag; +} + + + + + + + +Directory::Directory(unsigned type, const char *name) : + Entry(type, name) { #undef __METHOD__ #define __METHOD__ "Directory::Directory" - _nameLength = ValidName(name); - if (!_length) - throw Exception(__METHOD__ ": Invalid name."); - - _storageType = type; - std::strncpy(_name, name, 16); - - _access = 0xc3; + _access = 0xe3; _entryLength = 0x27; _entriesPerBlock = 13; _fileCount = 0; - - _device = NULL; + _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" - const uint8_t *data = (const uint8_t *)bp; + // input is a block pointer. To simplify, + // create a new pointer past the 4-byte linked list part. + void *dp = 4 + (const uint8_t *)bp; - _storageType = data[0x00] >> 4; - _nameLength = data[0x00] & 0x0f; - std::memcpy(_name, data + 1, _nameLength); - _name[_nameLength] = 0; - _creation = DateTime(read16(data, 0x1c), read16(data, 0x1e)); + _creation = DateTime(Read16(dp, 0x18), Read16(dp, 0x1a)); - _access = data[0x22]; - _entryLength = data[0x23]; - _entriesPerBlock = data[0x24]; + _version = Read8(dp, 0x1c); + _minVersion = Read8(dp, 0x1d); - _fileCount = read16(data, 0x25); + _access = Read8(dp, 0x1e); + _entryLength = Read8(dp, 0x1f); + _entriesPerBlock = Read8(dp, 0x20); - // parse child file entries. + _fileCount = read16(dp, 0x21); + + // parse child file entries ... requires ability to read other blocks. } Directory::~Directory() @@ -114,20 +209,248 @@ Directory::setAccess(unsigned access) #undef __METHOD__ #define __METHOD__ "Directory::setAccess" - if ((access & 0xe3) != access) + if ((access & 0xe7) != access) throw Exception(__METHOD__ ": Illegal access."); _access = access; // todo -- mark dirty? update block? } -Directory::setName(const char *name) -{ - unsigned length = ValidName(name); - if (!length) - throw Exception(__METHOD__ ": Invalid name."); - _nameLength = length; - std::strncpy(_name, name, 16); - // todo -- update or mark dirty. +#pragma mark 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(new Bitmap(_totalBlocks)); + + + // 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(&out); + } + + buffer.rezize(512); + device->write(i, buffer.buffer()); + } + + // TODO -- create/write the volume entry.... + + // allocate blocks for the bitmap itself + unsigned bb = bitmap->bitmapBlocks(); + for (unsigned i = 0; i < bb; ++i) + bitmap->allocBlock(i); + + // now write the bitmap... + const uint8_t *bm = (const uint8_t *)bitmap->bitmap(); + for (unsigned i = 0; i < bb; ++i) + { + device->writeBlock(_bitmapPointer + i, 512 * i + bm); + } + + setDevice(device); + _bitmap = bitmap.release(); } + +VolumeDirectory::~VolumeDirectory() +{ +} + +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); +} + +#pragma mark SubDirectory + +SubDirectory::SubDirectory(unsigned type, const char *name) : + Directory(type, name) +{ + _parentPointer = 0; + _parentEntryNumber = 0; + _parentEntryLength = 0x27; +} + +SubDirectory::SubDirectory(const void *bp) : + Directory(bp) +{ + const void *dp = 4 + (const uint8_t *)bp; + + _parentPointer = Read16(dp, 0x23); + _parentEntryNumber = Read8(dp, 0x25); + _parentEntryLength = Read8(dp, 0x26); +} + +SubDirectory::~SubDirectory +{ +} + +void SubDirectory::write(Buffer *out) +{ + out->push8((VolumeHeader << 4 ) | (nameLength()); + out->pushBytes(namei(), 15); + + // reserved. 0x76 is stored in the first byte. + // no one knows why. + out->push8(0x76); + out->push8(0); + out->push8(0); + out->push8(0); + out->push8(0); + out->push8(0); + out->push8(0); + out->push8(0); + + // 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(_parentPointer); + out->push8(_parentEntryNumber); + out->push8(_parentEntryLength); +} + + + + +FileEntry::FileEntry(unsigned type, const char *name) : + Entry(type, name) +{ + _fileType = type == DirectoryFile ? 0x0f : 0x00; + _keyPointer = 0; + _blocksUsed = 0; + _eof = 0; + _access = 0xe3; + _auxType = 0; + _headerPointer = 0; +} + + +FileEntry::FileEntry(const void *vp) : + Entry(vp), + _creation(0,0), + _modification(0,0) +{ +#undef __METHOD__ +#define __METHOD__ "FileEntry::FileEntry" + + switch(storageType()) + { + case SeedlingFile: + case SaplingFile: + case TreeFile: + case PascalFile: + case ExtendedFile: + case DirectoryFile: + break; + default: + throw Exception(__METHOD__ ": Invalid storage type."); + } + + + _fileType = Read8(vp, 0x10); + _keyPointer = Read16(vp, 0x11); + _blocksUsed = Read16(vp, 0x13); + _eof = Read24(vp, 0x15); + _creation = DateTime(Read16(vp, 0x18), Read16(vp, 0x1a)); + _access = Read8(vp, 0x1e); + _auxType = Read16(vp, 0x1f); + _modification = DateTime(Read16(vp, 0x21), Read16(vp, 0x23)); + _headerPointer = Read16(vp, 0x25); + + +} + +FileEntry::~FileEntry() +{ +} + + +void FileEntry::write(Buffer *out) +{ + out->push8((VolumeHeader << 4 ) | (nameLength()); + out->pushBytes(namei(), 15); + + out->push8(_fileType); + out->push16le(_keyPointer); + out->push16le(_blocksUsed); + out-push24le(_eof); + + // creation + out->push16le(_creation.date()); + out->push16le(_creation.time()); + + // case bits + out->push16le(caseFlag()); + + out->push8(_access); + put->push16le(_auxType); + + // modification + out->push16le(_modification.date()); + out->push16le(_modification.time()); + + out->push16le(_headerPointer); +} \ No newline at end of file diff --git a/Directory.h b/Directory.h index 8fe229c..7b8be06 100644 --- a/Directory.h +++ b/Directory.h @@ -15,25 +15,44 @@ class FileEntry; class Volume; -enum { +enum Access { DestroyEnabled = 0x80, RenameEnabled = 0x40, BackupNeeded = 0x20, + Invisible = 0x04, WriteEnabled = 0x02, ReadEnabled = 0x01 }; +enum StorageType { + 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 caseFlags() const { return _caseFlags; } + + void setName(const char *name); @@ -43,6 +62,20 @@ public: protected: Entry(int storageType, const char *name); + Entry(const void *bp); + + + setStorageType(unsigned type) + { + _storageType = type; + } + + setAddress(unsigned address) + { + _address = address; + } + + Volume *volume() { return _volume; } private: @@ -51,11 +84,13 @@ private: unsigned _storageType; unsigned _nameLength; + char _namei[15+1]; // insensitive, ie, uppercase. char _name[15+1]; - + + unsigned _caseFlag; }; -class Directory public Entry { +class Directory : public Entry { public: virtual ~Directory(); @@ -68,24 +103,24 @@ public: 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(BlockDevice *, unsigned block); + Directory(const void *bp); std::vector _children; std::vector _entryBlocks; - - BlockDevice *_device; - private: DateTime _creation; - // version - // min version + unsigned _version + unsigned _minVersion unsigned _access; usnigned _entryLength; // always 0x27 unsigned _entriesPerBlock; //always 0x0d diff --git a/Endian.h b/Endian.h new file mode 100644 index 0000000..355ceac --- /dev/null +++ b/Endian.h @@ -0,0 +1,219 @@ +#ifndef __ENDIAN_H__ +#define __ENDIAN_H__ + +// utlities to read/write bytes. + +#include + +namespace LittleEndian { + + + uint8_t Read8(const void *vp) + { + const uint8_t *p = (const uint8_t *)vp; + return (p[0]); + } + + 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); + } + + + uint8_t Read8(const void *vp, unsigned offset) + { + return Read8(offset + (const uint8_t *)vp); + } + + uint16_t Read16(const void *vp, unsigned offset) + { + return Read16(offset + (const uint8_t *)vp); + } + + uint32_t Read24(const void *vp, unsigned offset) + { + return Read24(offset + (const uint8_t *)vp); + } + + uint32_t Read32(const void *vp, unsigned offset) + { + return Read32(offset + (const uint8_t *)vp); + } + + + // write + void Write8(void *vp, uint8_t x) + { + uint8_t *p = (uint8_t *)vp; + p[0] = x; + } + + 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; + } + + void Write8(void *vp, unsigned offset, uint8_t x) + { + Write8(offset + (uint8_t *)vp, x); + } + + void Write16(void *vp, unsigned offset, uint16_t x) + { + Write16(offset + (uint8_t *)vp, x); + } + + void Write24(void *vp, unsigned offset, uint32_t x) + { + Write24(offset + (uint8_t *)vp, x); + } + + void Write32(void *vp, unsigned offset, uint32_t x) + { + Write32(offset + (uint8_t *)vp, x); + } + +} + + +namespace BigEndian { + + + uint8_t Read8(const void *vp) + { + const uint8_t *p = (const uint8_t *)vp; + return p[0]; + } + + 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]); + } + + + uint8_t Read8(const void *vp, unsigned offset) + { + return Read8(offset + (const uint8_t *)vp); + } + + uint16_t Read16(const void *vp, unsigned offset) + { + return Read16(offset + (const uint8_t *)vp); + } + + uint32_t Read24(const void *vp, unsigned offset) + { + return Read24(offset + (const uint8_t *)vp); + } + + uint32_t Read32(const void *vp, unsigned offset) + { + return Read32(offset + (const uint8_t *)vp); + } + + + + // write + void Write8(void *vp, uint8_t x) + { + uint8_t *p = (uint8_t *)vp; + p[0] = x; + } + + 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; + } + + void Write8(void *vp, unsigned offset, uint8_t x) + { + Write8(offset + (uint8_t *)vp, x); + } + + void Write16(void *vp, unsigned offset, uint16_t x) + { + Write16(offset + (uint8_t *)vp, x); + } + + void Write24(void *vp, unsigned offset, uint32_t x) + { + Write24(offset + (uint8_t *)vp, x); + } + + void Write32(void *vp, unsigned offset, uint32_t x) + { + Write32(offset + (uint8_t *)vp, x); + } + + +} + + + +#endif \ No newline at end of file