profuse/UniversalDiskImage.cpp

110 lines
2.4 KiB
C++

#include "UniversalDiskImage.h"
#include "MappedFile.h"
#include "Buffer.h"
using namespace ProFUSE;
UniversalDiskImage::UniversalDiskImage(const char *name, bool readOnly) :
DiskImage(name, readOnly)
{
Validate(file());
}
UniversalDiskImage::UniversalDiskImage(MappedFile *file) :
DiskImage(file)
{
}
UniversalDiskImage *UniversalDiskImage::Create(const char *name, size_t blocks)
{
// 64-byte header.
MappedFile *file = new MappedFile(name, blocks * 512 + 64);
Buffer header(64);
// magic + creator
header.pushBytes("2IMGPRFS", 8);
// header size.
header.push16le(64);
// version
header.push16le(1);
//image format -- ProDOS order
header.push32le(1);
// flags
header.push32le(0);
// # blocks. s/b 0 unless prodos-order
header.push32le(blocks);
// offset to disk data
header.push32le(64);
// data length
header.push32le(512 * blocks);
// comment offset, creator, reserved -- 0.
header.resize(64);
std::memcpy(file->fileData(), header.buffer(), 64);
file->setOffset(64);
file->setBlocks(blocks);
return new UniversalDiskImage(file);
}
UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file)
{
Validate(file);
return new UniversalDiskImage(file);
}
/*
* TODO -- support dos-order & nibblized
* TODO -- honor read-only flag.
*
*/
void UniversalDiskImage::Validate(MappedFile *file)
{
#undef __METHOD__
#define __METHOD__ "DavexDiskImage::Validate"
uint8_t *data = (uint8_t *)file->fileData();
size_t size = file->fileSize();
bool ok = false;
unsigned blocks = 0;
unsigned offset = 0;
do {
if (size < 64) break;
if (std::memcmp(data, "2IMG", 4)) break;
// only prodos supported, for now...
if (file->read32(0x0c, LittleEndian) != 1) break;
offset = file->read32(0x20, LittleEndian);
blocks = file->read32(0x14, LittleEndian);
// file size == blocks * 512
if (file->read32(0x1c, LittleEndian) != blocks * 512) break;
if (offset + blocks * 512 > size) break;
ok = true;
} while (false);
if (!ok)
throw Exception(__METHOD__ ": Invalid file format.");
file->reset();
file->setOffset(offset);
file->setBlocks(blocks);
}