diff --git a/DavexDiskImage.cpp b/DavexDiskImage.cpp index e16ed13..fafb8a9 100644 --- a/DavexDiskImage.cpp +++ b/DavexDiskImage.cpp @@ -9,6 +9,7 @@ #include #include #include +#include using namespace ProFUSE; using namespace LittleEndian; @@ -89,6 +90,10 @@ DavexDiskImage *DavexDiskImage::Open(MappedFile *file) } DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks) +{ + return Create(name, blocks, "Untitled"); +} +DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks, const char *vname) { #undef __METHOD__ #define __METHOD__ "DavexDiskImage::Create" @@ -122,7 +127,10 @@ DavexDiskImage *DavexDiskImage::Create(const char *name, size_t blocks) header.push32le(0); // volume Name - header.pushBytes("\x08Untitled", 9); + if (!vname || !*vname) vname = "Untitled"; + unsigned l = std::strlen(vname); + header.push8(std::min(l, 15u)); + header.pushBytes(vname, std::min(l, 15u)); // name + reserved. header.resize(64); diff --git a/DavexDiskImage.h b/DavexDiskImage.h index 0541428..5f75934 100644 --- a/DavexDiskImage.h +++ b/DavexDiskImage.h @@ -14,6 +14,7 @@ public: virtual ~DavexDiskImage(); static DavexDiskImage *Create(const char *name, size_t blocks); + static DavexDiskImage *Create(const char *name, size_t blocks, const char *vname); static DavexDiskImage *Open(MappedFile *); diff --git a/DiskCopy42Image.cpp b/DiskCopy42Image.cpp index 7f1169c..5502130 100644 --- a/DiskCopy42Image.cpp +++ b/DiskCopy42Image.cpp @@ -4,6 +4,8 @@ #include "Endian.h" #include +#include +#include using namespace ProFUSE; using namespace BigEndian; @@ -85,6 +87,11 @@ static uint8_t FormatByte(size_t blocks) } } DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks) +{ + return Create(name, blocks, "Untitled"); +} + +DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks, const char *vname) { MappedFile *file = new MappedFile(name, blocks * 512 + 84); file->setOffset(84); @@ -92,8 +99,13 @@ DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks) Buffer header(84); - // name -- 64byte pstring. - header.pushBytes("\x08Untitled", 9); + // name -- 64byte pstring. + + if (vname == NULL) vname = "Untitled"; + unsigned l = std::strlen(vname); + header.push8(std::min(l, 63u)); + header.pushBytes(vname, std::min(l, 63u)); + header.resize(64); // data size -- number of bytes @@ -139,11 +151,14 @@ DiskCopy42Image *DiskCopy42Image::Create(const char *name, size_t blocks) void DiskCopy42Image::Validate(MappedFile *file) { +#undef __METHOD__ +#define __METHOD__ "DiskCopy42Image::Validate" + size_t bytes = 0; size_t size = file->fileSize(); const void *data = file->fileData(); bool ok = false; - uint32_t checksum; + uint32_t checksum = 0; do { if (size < 84) break; @@ -167,6 +182,8 @@ void DiskCopy42Image::Validate(MappedFile *file) ok = true; } while (false); + if (!ok) + throw Exception(__METHOD__ ": Invalid file format."); uint32_t cs = Checksum(64 + (uint8_t *)data, bytes); diff --git a/DiskCopy42Image.h b/DiskCopy42Image.h index 689deca..7a80f87 100644 --- a/DiskCopy42Image.h +++ b/DiskCopy42Image.h @@ -13,6 +13,8 @@ public: virtual ~DiskCopy42Image(); static DiskCopy42Image *Create(const char *name, size_t blocks); + static DiskCopy42Image *Create(const char *name, size_t blocks, const char *vname); + static DiskCopy42Image *Open(MappedFile *); static uint32_t Checksum(void *data, size_t size); diff --git a/Endian.h b/Endian.h index 355ceac..7241f7e 100644 --- a/Endian.h +++ b/Endian.h @@ -8,68 +8,68 @@ namespace LittleEndian { - uint8_t Read8(const void *vp) + inline uint8_t Read8(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (p[0]); } - uint16_t Read16(const void *vp) + inline 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) + inline 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) + inline 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) + inline uint8_t Read8(const void *vp, unsigned offset) { return Read8(offset + (const uint8_t *)vp); } - uint16_t Read16(const void *vp, unsigned offset) + inline uint16_t Read16(const void *vp, unsigned offset) { return Read16(offset + (const uint8_t *)vp); } - uint32_t Read24(const void *vp, unsigned offset) + inline uint32_t Read24(const void *vp, unsigned offset) { return Read24(offset + (const uint8_t *)vp); } - uint32_t Read32(const void *vp, unsigned offset) + inline uint32_t Read32(const void *vp, unsigned offset) { return Read32(offset + (const uint8_t *)vp); } // write - void Write8(void *vp, uint8_t x) + inline void Write8(void *vp, uint8_t x) { uint8_t *p = (uint8_t *)vp; p[0] = x; } - void Write16(void *vp, uint16_t x) + inline 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) + inline void Write24(void *vp, uint32_t x) { uint8_t *p = (uint8_t *)vp; p[0] = (x) & 0xff; @@ -77,7 +77,7 @@ namespace LittleEndian { p[2] = (x >> 16) & 0xff; } - void Write32(void *vp, uint32_t x) + inline void Write32(void *vp, uint32_t x) { uint8_t *p = (uint8_t *)vp; p[0] = (x) & 0xff; @@ -86,22 +86,22 @@ namespace LittleEndian { p[3] = (x >> 24) & 0xff; } - void Write8(void *vp, unsigned offset, uint8_t x) + inline void Write8(void *vp, unsigned offset, uint8_t x) { Write8(offset + (uint8_t *)vp, x); } - void Write16(void *vp, unsigned offset, uint16_t x) + inline void Write16(void *vp, unsigned offset, uint16_t x) { Write16(offset + (uint8_t *)vp, x); } - void Write24(void *vp, unsigned offset, uint32_t x) + inline void Write24(void *vp, unsigned offset, uint32_t x) { Write24(offset + (uint8_t *)vp, x); } - void Write32(void *vp, unsigned offset, uint32_t x) + inline void Write32(void *vp, unsigned offset, uint32_t x) { Write32(offset + (uint8_t *)vp, x); } @@ -112,48 +112,48 @@ namespace LittleEndian { namespace BigEndian { - uint8_t Read8(const void *vp) + inline uint8_t Read8(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return p[0]; } - uint16_t Read16(const void *vp) + inline 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) + inline 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) + inline 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) + inline uint8_t Read8(const void *vp, unsigned offset) { return Read8(offset + (const uint8_t *)vp); } - uint16_t Read16(const void *vp, unsigned offset) + inline uint16_t Read16(const void *vp, unsigned offset) { return Read16(offset + (const uint8_t *)vp); } - uint32_t Read24(const void *vp, unsigned offset) + inline uint32_t Read24(const void *vp, unsigned offset) { return Read24(offset + (const uint8_t *)vp); } - uint32_t Read32(const void *vp, unsigned offset) + inline uint32_t Read32(const void *vp, unsigned offset) { return Read32(offset + (const uint8_t *)vp); } @@ -161,20 +161,20 @@ namespace BigEndian { // write - void Write8(void *vp, uint8_t x) + inline void Write8(void *vp, uint8_t x) { uint8_t *p = (uint8_t *)vp; p[0] = x; } - void Write16(void *vp, uint16_t x) + inline 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) + inline void Write24(void *vp, uint32_t x) { uint8_t *p = (uint8_t *)vp; p[0] = (x >> 16) & 0xff; @@ -182,7 +182,7 @@ namespace BigEndian { p[2] = (x) & 0xff; } - void Write32(void *vp, uint32_t x) + inline void Write32(void *vp, uint32_t x) { uint8_t *p = (uint8_t *)vp; p[0] = (x >> 24) & 0xff; @@ -191,22 +191,22 @@ namespace BigEndian { p[3] = (x) & 0xff; } - void Write8(void *vp, unsigned offset, uint8_t x) + inline void Write8(void *vp, unsigned offset, uint8_t x) { Write8(offset + (uint8_t *)vp, x); } - void Write16(void *vp, unsigned offset, uint16_t x) + inline void Write16(void *vp, unsigned offset, uint16_t x) { Write16(offset + (uint8_t *)vp, x); } - void Write24(void *vp, unsigned offset, uint32_t x) + inline void Write24(void *vp, unsigned offset, uint32_t x) { Write24(offset + (uint8_t *)vp, x); } - void Write32(void *vp, unsigned offset, uint32_t x) + inline void Write32(void *vp, unsigned offset, uint32_t x) { Write32(offset + (uint8_t *)vp, x); } diff --git a/UniversalDiskImage.cpp b/UniversalDiskImage.cpp index 87c6055..0e077c7 100644 --- a/UniversalDiskImage.cpp +++ b/UniversalDiskImage.cpp @@ -80,7 +80,7 @@ UniversalDiskImage *UniversalDiskImage::Open(MappedFile *file) void UniversalDiskImage::Validate(MappedFile *file) { #undef __METHOD__ -#define __METHOD__ "DavexDiskImage::Validate" +#define __METHOD__ "UniversalDiskImage::Validate" const void *data = file->fileData(); size_t size = file->fileSize(); diff --git a/newfs_prodos.cpp b/newfs_prodos.cpp new file mode 100644 index 0000000..e4d1728 --- /dev/null +++ b/newfs_prodos.cpp @@ -0,0 +1,288 @@ +#include "BlockDevice.h" +#include "UniversalDiskImage.h" +#include "DiskCopy42Image.h" +#include "DavexDiskImage.h" +#include "RawDevice.h" +#include "Exception.h" + +#include +#include +#include +#include +#include + +#include + +#include + +#define NEWFS_VERSION "0.1" + +using namespace ProFUSE; + +void usage() +{ + std::printf("newfs_prodos %s\n", NEWFS_VERSION); + std::printf("\n"); + + std::printf("newfs_prodos [-v volume_name] [-s size] [-f format] file\n"); + std::printf("\n"); + std::printf(" -v volume_name specify the volume name.\n" + " Default is Untitled.\n" + " -s size specify size in blocks.\n" + " Default is 1600 blocks (800K)\n" + " -f format specify the disk image format. Valid values are:\n" + " 2mg Universal Disk Image (default)\n" + " dc42 DiskCopy 4.2 Image\n" + " po ProDOS Order Disk Image\n" + " do DOS Order Disk Image\n" + " davex Davex Disk Image\n" + ); + +} + +/* + * \d+ by block + * \d+[Kk] by kilobyte + * \d+[Mm] by megabyte + */ +unsigned parseBlocks(const char *cp) +{ + unsigned long blocks = 0; + char *mod; + + errno = 0; + blocks = std::strtoul(cp, &mod, 10); + if (errno) return -1; + + if (mod == cp) return -1; + if (blocks > 0xffff) return -1; + + if (mod) + { + switch(*mod) + { + case 0: + break; + + case 'm': // 1m = 1024*1024b = 2048 blocks + case 'M': + blocks *= 2048; + break; + + case 'k': // 1k = 1024b = 2 blocks + case 'K': + blocks *= 2; + break; + } + if (blocks > 0xffff) return -1; + } + + + return (unsigned)blocks; +} + +unsigned parseFormat(const char *type, unsigned defv = 0) +{ + if (type == 0 || *type == 0) return defv; + + if (::strcasecmp(type, "2mg") == 0) + return '2IMG'; + if (::strcasecmp(type, "2img") == 0) + return '2IMG'; + if (::strcasecmp(type, "dc42") == 0) + return 'DC42'; + if (::strcasecmp(type, "po") == 0) + return 'PO__'; + if (::strcasecmp(type, "do") == 0) + return 'DO__'; + if (::strcasecmp(type, "davex") == 0) + return 'DVX_'; + + return defv; +} + +// return the filename extension, NULL if none. +const char *extname(const char *src) +{ + if (!src) return NULL; + unsigned l = std::strlen(src); + + for (unsigned i = 0; i < l; ++i) + { + char c = src[l - 1 - i]; + if (c == '/') return NULL; + if (c == '.') return src + l - i; + } + + return NULL; +} + +// return the basename, without an extension. +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); +} + + +bool ValidName(const char *cp) +{ + + unsigned i = 0; + if (!cp || !*cp) return false; + + if (!isalpha(*cp)) return false; + + for (i = 1; i <17; ++i) + { + unsigned char c = cp[i]; + if (c == 0) break; + + if (c == '.') continue; + if (isalnum(c)) continue; + + return false; + } + + return i < 16; +} + +int main(int argc, char **argv) +{ + unsigned blocks = 1600; + std::string volumeName; + std::string fileName; + int format = 0; + const char *fname; + int c; + + // ctype uses ascii only. + ::setlocale(LC_ALL, "C"); + + while ( (c = ::getopt(argc, argv, "hf:s:v:")) != -1) + { + switch(c) + { + case '?': + case 'h': + usage(); + return 0; + break; + + case 'v': + volumeName = optarg; + // make sure it's legal. + if (!ValidName(optarg)) + { + std::fprintf(stderr, "Error: `%s' is not a valid ProDOS volume name.\n", optarg); + return 0x40; + } + break; + + case 's': + blocks = parseBlocks(optarg); + if (blocks > 0xffff) + { + std::fprintf(stderr, "Error: `%s' is not a valid disk size.\n", optarg); + return 0x5a; + } + break; + + case 'f': + { + format = parseFormat(optarg); + if (format == 0) + { + std::fprintf(stderr, "Error: `%s' is not a supposed disk image format.\n", optarg); + return -1; + } + } + + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + { + usage(); + return -1; + } + + fname = argv[0]; + fileName = argv[0]; + + // generate a filename. + if (volumeName.empty()) + { + volumeName = filename(fileName); + if (volumeName.empty() || !ValidName(volumeName.c_str())) + volumeName = "Untitled"; + } + + if (format == 0) format = parseFormat(extname(fname)); + + + try + { + std::auto_ptr device; + //auto_ptr volume; + + // todo -- check if path matches /dev/xxx; if so, use RawDevice. + // todo -- check if file exists at path? + + switch(format) + { + case 'DC42': + // todo -- pass in volume name + device.reset(DiskCopy42Image::Create(fname, blocks, volumeName.c_str())); + break; + + case 'PO__': + device.reset(ProDOSOrderDiskImage::Create(fname, blocks)); + break; + + case 'DO__': + device.reset(DOSOrderDiskImage::Create(fname, blocks)); + break; + + case 'DVX_': + device.reset(DavexDiskImage::Create(fname, blocks, volumeName.c_str())); + break; + + case '2IMG': + default: + device.reset(UniversalDiskImage::Create(fname, blocks)); + } + + // VolumeDirectory assumes ownership of device, + // but doesn't release it on exception. + //volume.reset(new VolumeDirectory(name, device)); + device.release(); + + } + catch (Exception e) + { + std::fprintf(stderr, "Error: %s\n", e.what()); + return -1; + } + return 0; +}