mirror of
https://github.com/holub/mame
synced 2025-04-26 18:23:08 +03:00
929 lines
26 KiB
C++
929 lines
26 KiB
C++
// license:GPL-2.0+
|
|
// copyright-holders:Jonathan Edwards
|
|
/****************************************************************************
|
|
|
|
bml3.c
|
|
|
|
Hitachi bml3 disk images
|
|
|
|
By Jonathan Edwards, based on rsdos.c (both use Microsoft BASIC)
|
|
|
|
****************************************************************************/
|
|
|
|
/* Supported Hitachi floppy formats are:
|
|
- 3" or 5"1/4 single density, single-sided: 40 tracks, 16 sectors/track, 128 bytes/sector
|
|
- (used with MP-1805 floppy disk controller card)
|
|
- 5"1/4 double density, double-sided: 40 tracks, 16 sectors/track, 256 bytes/sector
|
|
- (first track on first head may be single density)
|
|
- (used with MP-1802 floppy disk controller card)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "imgtool.h"
|
|
#include "iflopimg.h"
|
|
|
|
#define MAX_SECTOR_SIZE 256
|
|
|
|
struct bml3_diskinfo
|
|
{
|
|
UINT16 sector_size; /* 128 or 256 */
|
|
UINT8 heads; /* 1 or 2 */
|
|
UINT8 fat_start_sector; /* in cylinder 20, start sector of FAT */
|
|
UINT8 fat_start_offset; /* start byte of FAT in sector */
|
|
UINT8 fat_sectors; /* the number of sectors in the FAT */
|
|
UINT8 dirent_start_sector; /* in cylinder 20, start sector of directory entries */
|
|
UINT8 granule_sectors; /* how many sectors per granule */
|
|
UINT8 first_granule_cylinder; /* the number of the first cylinder with granule numbers assigned */
|
|
UINT8 variant; /* 0 - older version, uses EOF to terminate files, 1 - newer version, stores file length */
|
|
};
|
|
|
|
/* this structure mirrors the structure of a directory entry on disk */
|
|
struct bml3_dirent
|
|
{
|
|
char fname[8];
|
|
char fext[3];
|
|
UINT8 ftype;
|
|
UINT8 asciiflag;
|
|
UINT8 first_granule;
|
|
UINT16 lastsectorbytes;
|
|
// TODO there are some 'unused' bytes here that are sometimes used to store a timestamp, maybe support this?
|
|
};
|
|
|
|
struct bml3_direnum
|
|
{
|
|
int index;
|
|
int eof;
|
|
};
|
|
|
|
#define MAX_GRANULEMAP_SIZE 256
|
|
|
|
struct granule_list_t {
|
|
UINT8 granules[MAX_GRANULEMAP_SIZE];
|
|
UINT8 granule_count;
|
|
UINT8 last_granule_sectors;
|
|
};
|
|
|
|
#define BML3_OPTIONS_FTYPE 'T'
|
|
#define BML3_OPTIONS_ASCII 'M'
|
|
|
|
static imgtoolerr_t bml3_diskimage_deletefile(imgtool_partition *partition, const char *fname);
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
Imgtool module code
|
|
*********************************************************************/
|
|
|
|
static bml3_diskinfo *bml3_get_diskinfo(imgtool::image *image)
|
|
{
|
|
return (bml3_diskinfo *) imgtool_floppy_extrabytes(image);
|
|
}
|
|
|
|
|
|
|
|
static int max_dirents(imgtool::image *image)
|
|
{
|
|
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
|
return (16 * info->heads + 1 - info->dirent_start_sector)*(info->sector_size/32);
|
|
}
|
|
|
|
|
|
|
|
static void dirent_location(imgtool::image *image, int index_loc, UINT8 *head, UINT8 *track, UINT8 *sector, UINT8 *offset)
|
|
{
|
|
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
|
*track = 20;
|
|
*sector = info->dirent_start_sector + index_loc / (info->sector_size / 32);
|
|
*head = 0;
|
|
if (*sector > 16) {
|
|
// wrap to second head
|
|
*sector -= 16;
|
|
(*head)++;
|
|
}
|
|
*offset = index_loc % (info->sector_size/32) * 32;
|
|
}
|
|
|
|
|
|
|
|
static floperr_t get_bml3_dirent(imgtool::image *f, int index_loc, struct bml3_dirent *ent)
|
|
{
|
|
floperr_t err;
|
|
UINT8 head, track, sector, offset;
|
|
UINT8 buf[32];
|
|
bml3_diskinfo *info = bml3_get_diskinfo(f);
|
|
dirent_location(f, index_loc, &head, &track, §or, &offset);
|
|
err = floppy_read_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf));
|
|
memset(ent, 0, sizeof(*ent));
|
|
switch (info->variant) {
|
|
case 0:
|
|
memcpy(&ent->fname, &buf[0], 8);
|
|
ent->ftype = buf[11];
|
|
ent->asciiflag = buf[12];
|
|
ent->first_granule = buf[14];
|
|
break;
|
|
case 1:
|
|
memcpy(&ent->fname, &buf[0], 8);
|
|
memcpy(&ent->fext, &buf[8], 3);
|
|
ent->ftype = buf[11];
|
|
ent->asciiflag = buf[12];
|
|
ent->first_granule = buf[13];
|
|
ent->lastsectorbytes = (buf[14] << 8) | buf[15];
|
|
break;
|
|
default:
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
static floperr_t put_bml3_dirent(imgtool::image *f, int index_loc, const struct bml3_dirent *ent)
|
|
{
|
|
floperr_t err;
|
|
UINT8 head, track, sector, offset;
|
|
UINT8 buf[32];
|
|
bml3_diskinfo *info = bml3_get_diskinfo(f);
|
|
if (index_loc >= max_dirents(f))
|
|
return (floperr_t)IMGTOOLERR_FILENOTFOUND;
|
|
dirent_location(f, index_loc, &head, &track, §or, &offset);
|
|
memset(buf, 0, sizeof(buf));
|
|
switch (info->variant) {
|
|
case 0:
|
|
memcpy(&buf[0], &ent->fname, 8);
|
|
buf[11] = ent->ftype;
|
|
buf[12] = ent->asciiflag;
|
|
buf[14] = ent->first_granule;
|
|
break;
|
|
case 1:
|
|
memcpy(&buf[0], &ent->fname, 8);
|
|
memcpy(&buf[8], &ent->fext, 3);
|
|
buf[11] = ent->ftype;
|
|
buf[12] = ent->asciiflag;
|
|
buf[13] = ent->first_granule;
|
|
buf[14] = ent->lastsectorbytes >> 8;
|
|
buf[15] = ent->lastsectorbytes & 0xff;
|
|
break;
|
|
default:
|
|
return FLOPPY_ERROR_INVALIDIMAGE;
|
|
}
|
|
err = floppy_write_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf), 0); /* TODO: pass ddam argument from imgtool */
|
|
return err;
|
|
}
|
|
|
|
|
|
|
|
/* fnamebuf must have at least 13 bytes */
|
|
static void get_dirent_fname(char *fnamebuf, const struct bml3_dirent *ent)
|
|
{
|
|
char *s;
|
|
|
|
memset(fnamebuf, 0, 13);
|
|
memcpy(fnamebuf, ent->fname, sizeof(ent->fname));
|
|
rtrim(fnamebuf);
|
|
s = fnamebuf + strlen(fnamebuf);
|
|
*(s++) = '.';
|
|
memcpy(s, ent->fext, sizeof(ent->fext));
|
|
rtrim(s);
|
|
|
|
/* If no extension, remove period */
|
|
if (*s == '\0')
|
|
s[-1] = '\0';
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t lookup_bml3_file(imgtool::image *f, const char *fname, struct bml3_dirent *ent, int *position)
|
|
{
|
|
int i;
|
|
floperr_t ferr;
|
|
char fnamebuf[13];
|
|
|
|
i = 0;
|
|
fnamebuf[0] = '\0';
|
|
|
|
do
|
|
{
|
|
do
|
|
{
|
|
ferr = get_bml3_dirent(f, i++, ent);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
}
|
|
while(ent->fname[0] == '\0');
|
|
|
|
|
|
if (ent->fname[0] != -1)
|
|
get_dirent_fname(fnamebuf, ent);
|
|
}
|
|
while((ent->fname[0] != -1) && core_stricmp(fnamebuf, fname));
|
|
|
|
if (ent->fname[0] == -1)
|
|
return IMGTOOLERR_FILENOTFOUND;
|
|
|
|
if (position)
|
|
*position = i - 1;
|
|
return (imgtoolerr_t)0;
|
|
}
|
|
|
|
|
|
|
|
static UINT8 get_granule_count(imgtool::image *img)
|
|
{
|
|
// UINT16 tracks;
|
|
UINT16 disk_granules;
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
|
|
// This always returns 82 for D88, so not quite right
|
|
// tracks = floppy_get_tracks_per_disk(imgtool_floppy(img));
|
|
|
|
// The number of granules is primarily constrained by the disk capacity.
|
|
disk_granules = (40 - 1 - info->first_granule_cylinder) * info->heads * (16 / info->granule_sectors);
|
|
// Also, granule numbers from 0xC0 upwards are reserved for terminating a granule chain
|
|
return (UINT8)((disk_granules < 0xC0) ? disk_granules : 0xC0);
|
|
}
|
|
|
|
/* granule_map must be an array of MAX_GRANULEMAP_SIZE bytes */
|
|
static floperr_t get_granule_map(imgtool::image *img, UINT8 *granule_map, UINT8 *granule_count)
|
|
{
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
UINT8 count;
|
|
|
|
count = get_granule_count(img);
|
|
if (granule_count)
|
|
*granule_count = count;
|
|
|
|
// The first byte of the granule map sector is ignored (and expected to be 0)
|
|
return floppy_read_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, count);
|
|
}
|
|
|
|
|
|
|
|
static floperr_t put_granule_map(imgtool::image *img, const UINT8 *granule_map, UINT8 granule_count)
|
|
{
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
return floppy_write_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */
|
|
}
|
|
|
|
|
|
|
|
|
|
static void granule_location(imgtool::image *image, UINT8 granule, UINT8 *head, UINT8 *track, UINT8 *sector)
|
|
{
|
|
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
|
UINT16 abs_track = granule * info->granule_sectors / 16;
|
|
*head = abs_track % info->heads;
|
|
*track = abs_track / info->heads + info->first_granule_cylinder;
|
|
// skip filesystem cylinder
|
|
if (*track >= 20)
|
|
(*track)++;
|
|
*sector = granule * info->granule_sectors % 16 + 1;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t transfer_granule(imgtool::image *img, UINT8 granule, int length, imgtool_stream *f, imgtoolerr_t (*proc)(imgtool::image *, int, int, int, int, size_t, imgtool_stream *))
|
|
{
|
|
imgtoolerr_t err = IMGTOOLERR_SUCCESS;
|
|
UINT8 head, track, sector;
|
|
granule_location(img, granule, &head, &track, §or);
|
|
if (length > 0)
|
|
err = proc(img, head, track, sector, 0, length, f);
|
|
return err;
|
|
}
|
|
|
|
|
|
static imgtoolerr_t transfer_from_granule(imgtool::image *img, UINT8 granule, int length, imgtool_stream *destf)
|
|
{
|
|
return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream);
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t transfer_to_granule(imgtool::image *img, UINT8 granule, int length, imgtool_stream *sourcef)
|
|
{
|
|
return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream);
|
|
}
|
|
|
|
|
|
|
|
static floperr_t read_granule(imgtool::image *img, UINT8 granule, int offset, int length, UINT8 *buf)
|
|
{
|
|
UINT8 head, track, sector;
|
|
granule_location(img, granule, &head, &track, §or);
|
|
return floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buf, length);
|
|
}
|
|
|
|
|
|
|
|
static floperr_t write_granule(imgtool::image *img, UINT8 granule, int offset, int length, const UINT8 *buf)
|
|
{
|
|
UINT8 head, track, sector;
|
|
granule_location(img, granule, &head, &track, §or);
|
|
return floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buf, length, 0); /* TODO: pass ddam argument from imgtool */
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t list_granules(struct bml3_dirent *ent, imgtool::image *img, struct granule_list_t *granule_list)
|
|
{
|
|
floperr_t ferr;
|
|
UINT8 max_granules;
|
|
UINT8 granule;
|
|
UINT8 usedmap[MAX_GRANULEMAP_SIZE]; /* Used to detect infinite loops */
|
|
UINT8 granule_map[MAX_GRANULEMAP_SIZE];
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
|
|
ferr = get_granule_map(img, granule_map, &max_granules);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
memset(usedmap, 0, max_granules);
|
|
|
|
granule = ent->first_granule;
|
|
granule_list->granule_count = 0;
|
|
|
|
while(!usedmap[granule] && granule < max_granules)
|
|
{
|
|
usedmap[granule] = 1;
|
|
granule_list->granules[granule_list->granule_count++] = granule;
|
|
granule = granule_map[granule];
|
|
}
|
|
|
|
granule_list->last_granule_sectors = granule - 0xc0;
|
|
if (info->variant == 0) {
|
|
// add final incomplete sector
|
|
granule_list->last_granule_sectors++;
|
|
}
|
|
|
|
// A value of zero (variant 1) and max (variant 0) seem to indicate a file open for writing.
|
|
// Strictly speaking this means the image is corrupt, although a real system will happily read
|
|
// garbage from the file.
|
|
if (granule_list->last_granule_sectors > info->granule_sectors)
|
|
return IMGTOOLERR_CORRUPTIMAGE;
|
|
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t get_file_size(struct bml3_dirent *ent, imgtool::image *img, const struct granule_list_t *granule_list, size_t *size)
|
|
{
|
|
floperr_t ferr;
|
|
size_t last_sector_bytes = 0;
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
|
|
// TODO are these special cases valid, or maybe indicate a corrupt image?
|
|
if (granule_list->granule_count == 0) {
|
|
*size = 0;
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
else if (granule_list->last_granule_sectors == 0) {
|
|
*size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors);
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
// determine size excluding final sector
|
|
*size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors + granule_list->last_granule_sectors - 1);
|
|
|
|
// determine bytes used in final sector
|
|
switch (info->variant) {
|
|
case 0:
|
|
// look for EOF (ASCII SUB) and trailing NULs in final sector
|
|
{
|
|
UINT8 buf[MAX_SECTOR_SIZE];
|
|
ferr = read_granule(img, granule_list->granules[granule_list->granule_count-1], info->sector_size * (granule_list->last_granule_sectors - 1), info->sector_size, buf);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
for (last_sector_bytes = info->sector_size - 1; ; last_sector_bytes--) {
|
|
if (buf[last_sector_bytes] != 0)
|
|
break;
|
|
if (last_sector_bytes == 0)
|
|
break;
|
|
}
|
|
if (buf[last_sector_bytes] != 0x1a) {
|
|
last_sector_bytes++;
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
last_sector_bytes = ent->lastsectorbytes;
|
|
break;
|
|
}
|
|
|
|
// TODO is it valid for last_sector_bytes == 0?
|
|
if (last_sector_bytes > info->sector_size) {
|
|
return IMGTOOLERR_CORRUPTIMAGE;
|
|
}
|
|
*size += last_sector_bytes;
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t process_bml3_file(struct bml3_dirent *ent, imgtool::image *img, imgtool_stream *destf, size_t *size)
|
|
{
|
|
imgtoolerr_t err;
|
|
size_t remaining_size, granule_size;
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
struct granule_list_t granule_list;
|
|
granule_list.granule_count = 0;
|
|
|
|
err = list_granules(ent, img, &granule_list);
|
|
if (err)
|
|
return err;
|
|
err = get_file_size(ent, img, &granule_list, size);
|
|
if (err)
|
|
return err;
|
|
|
|
if (destf) {
|
|
remaining_size = *size;
|
|
granule_size = info->granule_sectors * info->sector_size;
|
|
|
|
for (int c = 0; c < granule_list.granule_count; c++) {
|
|
if (granule_size >= remaining_size)
|
|
granule_size = remaining_size;
|
|
transfer_from_granule(img, granule_list.granules[c], granule_size, destf);
|
|
remaining_size -= granule_size;
|
|
}
|
|
}
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/* create a new directory entry with a specified name */
|
|
static imgtoolerr_t prepare_dirent(UINT8 variant, struct bml3_dirent *ent, const char *fname)
|
|
{
|
|
const char *fname_end;
|
|
const char *fname_ext;
|
|
int fname_ext_len;
|
|
|
|
memset(ent, '\0', sizeof(*ent));
|
|
memset(ent->fname, ' ', sizeof(ent->fname));
|
|
memset(ent->fext, ' ', sizeof(ent->fext));
|
|
|
|
fname_end = strchr(fname, '.');
|
|
if (fname_end)
|
|
fname_ext = fname_end + 1;
|
|
else
|
|
fname_end = fname_ext = fname + strlen(fname);
|
|
|
|
fname_ext_len = strlen(fname_ext);
|
|
|
|
switch (variant) {
|
|
case 0:
|
|
/* 8-character max filename */
|
|
if (((fname_end - fname) > 8) || (fname_ext_len > 0))
|
|
return IMGTOOLERR_BADFILENAME;
|
|
break;
|
|
case 1:
|
|
/*8.3 filename */
|
|
if (((fname_end - fname) > 8) || (fname_ext_len > 3))
|
|
return IMGTOOLERR_BADFILENAME;
|
|
break;
|
|
default:
|
|
return IMGTOOLERR_CORRUPTIMAGE;
|
|
}
|
|
|
|
memcpy(ent->fname, fname, fname_end - fname);
|
|
memcpy(ent->fext, fname_ext, fname_ext_len);
|
|
|
|
/* By default, set as a type 2 binary file */
|
|
ent->ftype = 2;
|
|
ent->asciiflag = 0;
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_open(imgtool::image *image, imgtool_stream *stream)
|
|
{
|
|
// imgtoolerr_t err;
|
|
floperr_t ferr;
|
|
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
|
floppy_image_legacy *floppy = imgtool_floppy(image);
|
|
const struct FloppyCallbacks *callbacks = floppy_callbacks(floppy);
|
|
|
|
// probe disk geometry to guess format
|
|
int heads_per_disk = callbacks->get_heads_per_disk(floppy);
|
|
UINT32 sector_length;
|
|
ferr = callbacks->get_sector_length(floppy, 0, 20, 1, §or_length);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
int sectors_per_track = callbacks->get_sectors_per_track(floppy, 0, 20);
|
|
|
|
if (heads_per_disk == 2 && sector_length == 128 && sectors_per_track == 16) {
|
|
// single-sided, single-density
|
|
info->sector_size = 128;
|
|
info->heads = 1;
|
|
info->fat_start_sector = 1;
|
|
info->fat_start_offset = 5;
|
|
info->fat_sectors = 2;
|
|
info->dirent_start_sector = 7;
|
|
info->granule_sectors = 4;
|
|
info->first_granule_cylinder = 0;
|
|
info->variant = 0;
|
|
}
|
|
else if (heads_per_disk == 2 && sector_length == 256 && sectors_per_track == 16) {
|
|
// double-sided, double-density
|
|
info->sector_size = 256;
|
|
info->heads = 2;
|
|
info->fat_start_sector = 2;
|
|
info->fat_start_offset = 1;
|
|
info->fat_sectors = 1;
|
|
info->dirent_start_sector = 5;
|
|
info->granule_sectors = 8;
|
|
info->first_granule_cylinder = 1;
|
|
info->variant = 1;
|
|
}
|
|
else {
|
|
// invalid or unsupported format
|
|
return IMGTOOLERR_CORRUPTIMAGE;
|
|
}
|
|
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent)
|
|
{
|
|
floperr_t ferr;
|
|
imgtoolerr_t err;
|
|
size_t filesize;
|
|
struct bml3_direnum *rsenum;
|
|
struct bml3_dirent rsent;
|
|
char fname[13];
|
|
imgtool::image *image;
|
|
|
|
image = imgtool_directory_image(enumeration);
|
|
rsenum = (struct bml3_direnum *) imgtool_directory_extrabytes(enumeration);
|
|
|
|
/* Did we hit the end of file before? */
|
|
if (rsenum->eof)
|
|
goto eof;
|
|
|
|
do
|
|
{
|
|
if (rsenum->index >= max_dirents(image))
|
|
goto eof;
|
|
|
|
ferr = get_bml3_dirent(image, rsenum->index++, &rsent);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
}
|
|
while(rsent.fname[0] == '\0');
|
|
|
|
/* Now are we at the eof point? */
|
|
if (rsent.fname[0] == -1)
|
|
{
|
|
rsenum->eof = 1;
|
|
eof:
|
|
ent->eof = 1;
|
|
}
|
|
else
|
|
{
|
|
/* Not the end of file */
|
|
err = process_bml3_file(&rsent, image, NULL, &filesize);
|
|
if (err)
|
|
return err;
|
|
|
|
if (filesize == ((size_t) -1))
|
|
{
|
|
/* corrupt! */
|
|
ent->filesize = 0;
|
|
ent->corrupt = 1;
|
|
}
|
|
else
|
|
{
|
|
ent->filesize = filesize;
|
|
ent->corrupt = 0;
|
|
}
|
|
ent->eof = 0;
|
|
|
|
get_dirent_fname(fname, &rsent);
|
|
|
|
snprintf(ent->filename, ARRAY_LENGTH(ent->filename), "%s", fname);
|
|
snprintf(ent->attr, ARRAY_LENGTH(ent->attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B'));
|
|
}
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_freespace(imgtool_partition *partition, UINT64 *size)
|
|
{
|
|
floperr_t ferr;
|
|
UINT8 i;
|
|
size_t s = 0;
|
|
UINT8 granule_count;
|
|
UINT8 granule_map[MAX_GRANULEMAP_SIZE];
|
|
imgtool::image *image = imgtool_partition_image(partition);
|
|
bml3_diskinfo *info = bml3_get_diskinfo(image);
|
|
|
|
ferr = get_granule_map(image, granule_map, &granule_count);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
for (i = 0; i < granule_count; i++)
|
|
{
|
|
if (granule_map[i] == 0xff)
|
|
s += (info->granule_sectors * info->sector_size);
|
|
}
|
|
*size = s;
|
|
return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t delete_entry(imgtool::image *img, struct bml3_dirent *ent, int pos)
|
|
{
|
|
floperr_t ferr;
|
|
unsigned char g, i;
|
|
UINT8 granule_count;
|
|
UINT8 granule_map[MAX_GRANULEMAP_SIZE];
|
|
|
|
/* Write a NUL in the filename, marking it deleted */
|
|
ent->fname[0] = 0;
|
|
ferr = put_bml3_dirent(img, pos, ent);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
ferr = get_granule_map(img, granule_map, &granule_count);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
/* Now free up the granules */
|
|
g = ent->first_granule;
|
|
while (g < granule_count)
|
|
{
|
|
i = granule_map[g];
|
|
granule_map[g] = 0xff;
|
|
g = i;
|
|
}
|
|
|
|
ferr = put_granule_map(img, granule_map, granule_count);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_readfile(imgtool_partition *partition, const char *fname, const char *fork, imgtool_stream *destf)
|
|
{
|
|
imgtoolerr_t err;
|
|
struct bml3_dirent ent;
|
|
size_t size;
|
|
imgtool::image *img = imgtool_partition_image(partition);
|
|
|
|
err = lookup_bml3_file(img, fname, &ent, NULL);
|
|
if (err)
|
|
return err;
|
|
|
|
err = process_bml3_file(&ent, img, destf, &size);
|
|
if (err)
|
|
return err;
|
|
|
|
if (size == (size_t) -1)
|
|
return IMGTOOLERR_CORRUPTIMAGE;
|
|
|
|
return (imgtoolerr_t)0;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_writefile(imgtool_partition *partition, const char *fname, const char *fork, imgtool_stream *sourcef, util::option_resolution *writeoptions)
|
|
{
|
|
floperr_t ferr;
|
|
imgtoolerr_t err;
|
|
imgtool::image *img = imgtool_partition_image(partition);
|
|
bml3_diskinfo *info = bml3_get_diskinfo(img);
|
|
struct bml3_dirent ent, ent2;
|
|
size_t i;
|
|
UINT64 sz, read_sz;
|
|
UINT64 freespace = 0;
|
|
unsigned char *gptr;
|
|
UINT8 granule_count;
|
|
UINT8 granule_map[MAX_GRANULEMAP_SIZE];
|
|
UINT8 eof_buf[MAX_SECTOR_SIZE];
|
|
|
|
// one-time setup of eof_buf
|
|
memset(eof_buf, 0, sizeof(eof_buf));
|
|
eof_buf[0] = 0x1A;
|
|
|
|
/* can we write to this image? */
|
|
if (floppy_is_read_only(imgtool_floppy(img)))
|
|
return IMGTOOLERR_READONLY;
|
|
|
|
err = bml3_diskimage_freespace(partition, &freespace);
|
|
if (err)
|
|
return err;
|
|
|
|
/* is there enough space? */
|
|
sz = read_sz = stream_size(sourcef);
|
|
if (info->variant == 0) {
|
|
// also need to write EOF
|
|
sz++;
|
|
}
|
|
if (sz > freespace)
|
|
return IMGTOOLERR_NOSPACE;
|
|
|
|
/* setup our directory entry */
|
|
err = prepare_dirent(info->variant, &ent, fname);
|
|
if (err)
|
|
return err;
|
|
|
|
ent.ftype = writeoptions->lookup_int(BML3_OPTIONS_FTYPE);
|
|
ent.asciiflag = UINT8(writeoptions->lookup_int(BML3_OPTIONS_ASCII)) - 1;
|
|
gptr = &ent.first_granule;
|
|
|
|
ferr = get_granule_map(img, granule_map, &granule_count);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
unsigned char g = 0x00;
|
|
UINT32 granule_bytes = info->granule_sectors * info->sector_size;
|
|
|
|
do
|
|
{
|
|
while (granule_map[g] != 0xff)
|
|
{
|
|
g++;
|
|
if ((g >= granule_count) || (g == 0))
|
|
return IMGTOOLERR_UNEXPECTED; /* We should have already verified that there is enough space */
|
|
}
|
|
*gptr = g;
|
|
gptr = &granule_map[g];
|
|
|
|
|
|
i = std::min(read_sz, UINT64(granule_bytes));
|
|
if (i > 0) {
|
|
err = transfer_to_granule(img, g, i, sourcef);
|
|
if (err)
|
|
return err;
|
|
read_sz -= i;
|
|
sz -= i;
|
|
}
|
|
if (i < info->granule_sectors * info->sector_size && sz > 0) {
|
|
// write EOF and trailing NULs in the final sector
|
|
ferr = write_granule(img, g, i, (info->granule_sectors * info->sector_size - i - 1) % info->sector_size + 1, eof_buf);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
sz--;
|
|
i++;
|
|
}
|
|
|
|
/* Go to next granule */
|
|
g++;
|
|
}
|
|
while(sz > 0);
|
|
|
|
/* Now that we are done with the file, we need to specify the final entry
|
|
* in the file allocation table
|
|
*/
|
|
*gptr = 0xc0 + ((i + info->sector_size-1) / info->sector_size) - (info->variant == 0 ? 1 : 0);
|
|
ent.lastsectorbytes = (i - 1) % info->sector_size + 1;
|
|
|
|
/* delete file if it already exists */
|
|
err = bml3_diskimage_deletefile(partition, fname);
|
|
if (err && err != IMGTOOLERR_FILENOTFOUND)
|
|
return err;
|
|
|
|
/* Now we need to find an empty directory entry */
|
|
i = -1;
|
|
do
|
|
{
|
|
ferr = get_bml3_dirent(img, ++i, &ent2);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
}
|
|
while(ent2.fname[0] != '\0' && ent2.fname[0] != -1);
|
|
|
|
ferr = put_bml3_dirent(img, i, &ent);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
/* write the granule map back out */
|
|
ferr = put_granule_map(img, granule_map, granule_count);
|
|
if (ferr)
|
|
return imgtool_floppy_error(ferr);
|
|
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_deletefile(imgtool_partition *partition, const char *fname)
|
|
{
|
|
imgtoolerr_t err;
|
|
imgtool::image *image = imgtool_partition_image(partition);
|
|
int pos = 0;
|
|
struct bml3_dirent ent;
|
|
|
|
err = lookup_bml3_file(image, fname, &ent, &pos);
|
|
if (err)
|
|
return err;
|
|
|
|
return delete_entry(image, &ent, pos);
|
|
}
|
|
|
|
|
|
|
|
static imgtoolerr_t bml3_diskimage_suggesttransfer(imgtool_partition *partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length)
|
|
{
|
|
imgtoolerr_t err;
|
|
imgtool::image *image = imgtool_partition_image(partition);
|
|
struct bml3_dirent ent;
|
|
int pos;
|
|
|
|
if (fname)
|
|
{
|
|
err = lookup_bml3_file(image, fname, &ent, &pos);
|
|
if (err)
|
|
return err;
|
|
|
|
if (ent.asciiflag == 0xFF)
|
|
{
|
|
/* ASCII file */
|
|
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
|
suggestions[0].filter = filter_eoln_getinfo;
|
|
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
|
suggestions[1].filter = NULL;
|
|
}
|
|
else if (ent.ftype == 0)
|
|
{
|
|
/* tokenized BASIC file */
|
|
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
|
suggestions[0].filter = NULL;
|
|
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
|
suggestions[1].filter = filter_bml3bas_getinfo;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
suggestions[0].viability = SUGGESTION_RECOMMENDED;
|
|
suggestions[0].filter = NULL;
|
|
suggestions[1].viability = SUGGESTION_POSSIBLE;
|
|
suggestions[1].filter = filter_eoln_getinfo;
|
|
suggestions[2].viability = SUGGESTION_POSSIBLE;
|
|
suggestions[2].filter = filter_bml3bas_getinfo;
|
|
}
|
|
|
|
return IMGTOOLERR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
Imgtool module declaration
|
|
*********************************************************************/
|
|
|
|
OPTION_GUIDE_START( bml3_writefile_optionguide )
|
|
OPTION_ENUM_START( BML3_OPTIONS_FTYPE, "ftype", "File type" )
|
|
OPTION_ENUM( 0, "basic", "Basic" )
|
|
OPTION_ENUM( 1, "data", "Data" )
|
|
OPTION_ENUM( 2, "binary", "Binary" )
|
|
OPTION_ENUM( 3, "assembler", "Assembler Source" )
|
|
OPTION_ENUM_END
|
|
OPTION_ENUM_START( BML3_OPTIONS_ASCII, "ascii", "Ascii flag" )
|
|
OPTION_ENUM( 0, "ascii", "Ascii" )
|
|
OPTION_ENUM( 1, "binary", "Binary" )
|
|
OPTION_ENUM_END
|
|
OPTION_GUIDE_END
|
|
|
|
|
|
|
|
void bml3_get_info(const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
|
|
{
|
|
switch(state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break;
|
|
case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(bml3_diskinfo); break;
|
|
case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct bml3_direnum); break;
|
|
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "bml3"); break;
|
|
case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Basic Master Level 3 format"); break;
|
|
case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
|
|
case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
|
|
case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
|
|
case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = bml3_diskimage_open; break;
|
|
case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = bml3_diskimage_nextenum; break;
|
|
case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = bml3_diskimage_freespace; break;
|
|
case IMGTOOLINFO_PTR_READ_FILE: info->read_file = bml3_diskimage_readfile; break;
|
|
case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = bml3_diskimage_writefile; break;
|
|
case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = bml3_diskimage_deletefile; break;
|
|
case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = bml3_diskimage_suggesttransfer; break;
|
|
case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &bml3_writefile_optionguide; break;
|
|
case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break;
|
|
}
|
|
}
|