mame/src/lib/formats/td0_dsk.cpp

1037 lines
24 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic
/*********************************************************************
formats/td0_dsk.c
TD0 disk images
*********************************************************************/
/*
* Based on Japanese version 29-NOV-1988
* LZSS coded by Haruhiko OKUMURA
* Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
* Edited and translated to English by Kenji RIKITAKE
*/
#include <string.h>
#include <assert.h>
#include "pool.h"
#include "flopimg.h"
#define BUFSZ 512 // new input buffer
/* LZSS Parameters */
#define N 4096 /* Size of string buffer */
#define F 60 /* Size of look-ahead buffer */
#define THRESHOLD 2
#define NIL N /* End of tree's node */
/* Huffman coding parameters */
#define N_CHAR (256 - THRESHOLD + F)
/* character code (= 0..N_CHAR-1) */
#define T (N_CHAR * 2 - 1) /* Size of table */
#define R (T - 1) /* root position */
#define MAX_FREQ 0x8000
/* update when cumulative frequency */
/* reaches to this value */
struct td0dsk_tag
{
int heads;
int tracks;
int sector_size;
uint64_t track_offsets[84*2]; /* offset within data for each track */
uint8_t *data;
};
struct tdlzhuf {
uint16_t r,
bufcnt,bufndx,bufpos, // string buffer
// the following to allow block reads from input in next_word()
ibufcnt,ibufndx; // input buffer counters
uint8_t inbuf[BUFSZ]; // input buffer
};
struct td0dsk_t
{
io_generic *floppy_file;
uint64_t floppy_file_offset;
struct tdlzhuf tdctl;
uint8_t text_buf[N + F - 1];
uint16_t freq[T + 1]; /* cumulative freq table */
/*
* pointing parent nodes.
* area [T..(T + N_CHAR - 1)] are pointers for leaves
*/
int16_t prnt[T + N_CHAR];
/* pointing children nodes (son[], son[] + 1)*/
int16_t son[T];
uint16_t getbuf;
uint8_t getlen;
int data_read(uint8_t *buf, uint16_t size);
int next_word();
int GetBit();
int GetByte();
void StartHuff();
void reconst();
void update(int c);
int16_t DecodeChar();
int16_t DecodePosition();
void init_Decode();
int Decode(uint8_t *buf, int len);
};
//static td0dsk_t td0dsk;
struct floppy_image_legacy
{
struct io_generic io;
const struct FloppyFormat *floppy_option;
struct FloppyCallbacks format;
/* loaded track stuff */
int loaded_track_head;
int loaded_track_index;
uint32_t loaded_track_size;
void *loaded_track_data;
uint8_t loaded_track_status;
uint8_t flags;
/* tagging system */
object_pool *tags;
void *tag_data;
};
static struct td0dsk_tag *get_tag(floppy_image_legacy *floppy)
{
struct td0dsk_tag *tag;
tag = (td0dsk_tag *)floppy_tag((floppy_image_legacy *)floppy);
return tag;
}
FLOPPY_IDENTIFY( td0_dsk_identify )
{
uint8_t header[2];
floppy_image_read(floppy, header, 0, 2);
if (header[0]=='T' && header[1]=='D') {
*vote = 100;
} else if (header[0]=='t' && header[1]=='d') {
*vote = 100;
} else {
*vote = 0;
}
return FLOPPY_ERROR_SUCCESS;
}
static int td0_get_heads_per_disk(floppy_image_legacy *floppy)
{
return get_tag(floppy)->heads;
}
static int td0_get_tracks_per_disk(floppy_image_legacy *floppy)
{
return get_tag(floppy)->tracks;
}
static uint64_t td0_get_track_offset(floppy_image_legacy *floppy, int head, int track)
{
return get_tag(floppy)->track_offsets[(track<<1) + head];
}
static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
{
uint64_t offs;
uint8_t *header;
uint8_t sectors_per_track;
int i;
if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks)
|| (sector < 0) )
return FLOPPY_ERROR_SEEKERROR;
// position on beginning of track data
offs = td0_get_track_offset(floppy, head, track);
// read track header
header = get_tag(floppy)->data + offs - 4;
// take number of sectors per track
sectors_per_track = header[0];
if (!sector_is_index) {
// when taking ID's return seek error if number is over counter
if (sector > sectors_per_track) {
return FLOPPY_ERROR_SEEKERROR;
}
}
// move trought sectors
for(i=0;i < sector-1;i++) {
header = get_tag(floppy)->data + offs;
offs+= 6;
if ((header[4] & 0x30)==0) {
offs+= 2;
offs+= header[6] + (header[7]<<8);
}
}
// read size of sector
header = get_tag(floppy)->data + offs;
get_tag(floppy)->sector_size = 1 << (header[3] + 7);
if (offset)
*offset = offs;
return FLOPPY_ERROR_SUCCESS;
}
static floperr_t internal_td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
{
uint64_t offset;
floperr_t err;
uint8_t *header;
int size,realsize,i;
int buff_pos;
int data_pos;
uint8_t *data;
uint8_t *buf;
buf = (uint8_t*)buffer;
// take sector offset
err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
if (err)
return err;
// read sector header
header = get_tag(floppy)->data + offset;
offset+=6;
// if there is no date just jump out
if ((header[4] & 0x30)!=0) return FLOPPY_ERROR_SUCCESS;
offset+=3;
// take data size
size = header[6] + (header[7]<<8)-1;
// take real sector size
realsize = 1 << (header[3] + 7);
// read sector data
data = get_tag(floppy)->data + offset;
buff_pos = 0;
data_pos = 0;
switch(header[8]) {
case 0:
// encoding type 0
// - plain data
memcpy(buffer,data,size);
break;
case 1:
// encoding type 1
// - 2 bytes size
// - 2 bytes of data
// data is reapeted specified number of times
while(buff_pos<realsize) {
for (i=0;i<data[data_pos]+(data[data_pos+1] << 8);i++) {
buf[buff_pos] = data[data_pos+2];buff_pos++;
buf[buff_pos] = data[data_pos+3];buff_pos++;
}
data_pos+=4;
}
break;
case 2:
// encoding type 2
// - if first byte is zero next byte represent size of
// plain data after it
// - if different then zero when multiply by 2 represent
// size of data that should be reapeted next byte times
while(buff_pos<realsize) {
if (data[data_pos]==0x00) {
int size_ = data[data_pos+1];
memcpy(buf+buff_pos,data + data_pos + 2,size_);
data_pos += 2 + size_;
buff_pos += size_;
} else {
int size_ = 2*data[data_pos];
int repeat = data[data_pos+1];
data_pos+=2;
for (i=0;i<repeat;i++) {
memcpy(buf + buff_pos,data + data_pos,size_);
buff_pos += size_;
}
data_pos += size_;
}
}
break;
default:
return FLOPPY_ERROR_INTERNAL;
}
return FLOPPY_ERROR_SUCCESS;
}
static floperr_t td0_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
{
return internal_td0_read_sector(floppy, head, track, sector, false, buffer, buflen);
}
static floperr_t td0_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
{
return internal_td0_read_sector(floppy, head, track, sector, true, buffer, buflen);
}
static floperr_t td0_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
{
floperr_t err;
err = get_offset(floppy, head, track, sector, false, nullptr);
if (err)
return err;
if (sector_length) {
*sector_length = get_tag(floppy)->sector_size;
}
return FLOPPY_ERROR_SUCCESS;
}
static floperr_t td0_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
{
floperr_t retVal;
uint64_t offset = 0;
uint8_t *sector_info;
retVal = get_offset(floppy, head, track, sector_index, false, &offset);
sector_info = get_tag(floppy)->data + offset;
if (cylinder)
*cylinder = sector_info[0];
if (side)
*side = sector_info[1];
if (sector)
*sector = sector_info[2];
if (sector_length) {
*sector_length = 1 << (sector_info[3] + 7);
}
if (flags) {
*flags = 0;
if (sector_info[4] & 0x02) *flags |= ID_FLAG_CRC_ERROR_IN_DATA_FIELD;
if (sector_info[4] & 0x04) *flags |= ID_FLAG_DELETED_DATA;
}
return retVal;
}
int td0dsk_t::data_read(uint8_t *buf, uint16_t size)
{
uint64_t image_size = io_generic_size(floppy_file);
if (size > image_size - floppy_file_offset) {
size = image_size - floppy_file_offset;
}
io_generic_read(floppy_file,buf,floppy_file_offset,size);
floppy_file_offset += size;
return size;
}
/*
* Tables for encoding/decoding upper 6 bits of
* sliding dictionary pointer
*/
/* decoder table */
static const uint8_t d_code[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
};
static const uint8_t d_len[256] = {
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};
int td0dsk_t::next_word()
{
if(tdctl.ibufndx >= tdctl.ibufcnt)
{
tdctl.ibufndx = 0;
tdctl.ibufcnt = data_read(tdctl.inbuf,BUFSZ);
if(tdctl.ibufcnt <= 0)
return(-1);
}
while (getlen <= 8) { // typically reads a word at a time
getbuf |= tdctl.inbuf[tdctl.ibufndx++] << (8 - getlen);
getlen += 8;
}
return(0);
}
int td0dsk_t::GetBit() /* get one bit */
{
int16_t i;
if(next_word() < 0)
return(-1);
i = getbuf;
getbuf <<= 1;
getlen--;
if(i < 0)
return(1);
else
return(0);
}
int td0dsk_t::GetByte() /* get a byte */
{
uint16_t i;
if(next_word() != 0)
return(-1);
i = getbuf;
getbuf <<= 8;
getlen -= 8;
i = i >> 8;
return((int) i);
}
/* initialize freq tree */
void td0dsk_t::StartHuff()
{
int i, j;
for (i = 0; i < N_CHAR; i++) {
freq[i] = 1;
son[i] = i + T;
prnt[i + T] = i;
}
i = 0; j = N_CHAR;
while (j <= R) {
freq[j] = freq[i] + freq[i + 1];
son[j] = i;
prnt[i] = prnt[i + 1] = j;
i += 2; j++;
}
freq[T] = 0xffff;
prnt[R] = 0;
}
/* reconstruct freq tree */
void td0dsk_t::reconst()
{
int16_t i, j, k;
uint16_t f, l;
/* halven cumulative freq for leaf nodes */
j = 0;
for (i = 0; i < T; i++) {
if (son[i] >= T) {
freq[j] = (freq[i] + 1) / 2;
son[j] = son[i];
j++;
}
}
/* make a tree : first, connect children nodes */
for (i = 0, j = N_CHAR; j < T; i += 2, j++) {
k = i + 1;
f = freq[j] = freq[i] + freq[k];
for (k = j - 1; f < freq[k]; k--) {};
k++;
l = (j - k) * 2;
/* movmem() is Turbo-C dependent
rewritten to memmove() by Kenji */
/* movmem(&freq[k], &freq[k + 1], l); */
(void)memmove(&freq[k + 1], &freq[k], l);
freq[k] = f;
/* movmem(&son[k], &son[k + 1], l); */
(void)memmove(&son[k + 1], &son[k], l);
son[k] = i;
}
/* connect parent nodes */
for (i = 0; i < T; i++) {
if ((k = son[i]) >= T) {
prnt[k] = i;
} else {
prnt[k] = prnt[k + 1] = i;
}
}
}
/* update freq tree */
void td0dsk_t::update(int c)
{
int i, j, k, l;
if (freq[R] == MAX_FREQ) {
reconst();
}
c = prnt[c + T];
do {
k = ++freq[c];
/* swap nodes to keep the tree freq-ordered */
if (k > freq[l = c + 1]) {
while (k > freq[++l]) {};
l--;
freq[c] = freq[l];
freq[l] = k;
i = son[c];
prnt[i] = l;
if (i < T) prnt[i + 1] = l;
j = son[l];
son[l] = i;
prnt[j] = c;
if (j < T) prnt[j + 1] = c;
son[c] = j;
c = l;
}
} while ((c = prnt[c]) != 0); /* do it until reaching the root */
}
int16_t td0dsk_t::DecodeChar()
{
int ret;
uint16_t c;
c = son[R];
/*
* start searching tree from the root to leaves.
* choose node #(son[]) if input bit == 0
* else choose #(son[]+1) (input bit == 1)
*/
while (c < T) {
if((ret = GetBit()) < 0)
return(-1);
c += (unsigned) ret;
c = son[c];
}
c -= T;
update(c);
return c;
}
int16_t td0dsk_t::DecodePosition()
{
int16_t bit;
uint16_t i, j, c;
/* decode upper 6 bits from given table */
if((bit=GetByte()) < 0)
return(-1);
i = (uint16_t) bit;
c = (uint16_t)d_code[i] << 6;
j = d_len[i];
/* input lower 6 bits directly */
j -= 2;
while (j--) {
if((bit = GetBit()) < 0)
return(-1);
i = (i << 1) + bit;
}
return(c | (i & 0x3f));
}
/* DeCompression
split out initialization code to init_Decode()
*/
void td0dsk_t::init_Decode()
{
int i;
getbuf = 0;
getlen = 0;
tdctl.ibufcnt= tdctl.ibufndx = 0; // input buffer is empty
tdctl.bufcnt = 0;
StartHuff();
for (i = 0; i < N - F; i++)
text_buf[i] = ' ';
tdctl.r = N - F;
}
int td0dsk_t::Decode(uint8_t *buf, int len) /* Decoding/Uncompressing */
{
int16_t c,pos;
int count; // was an unsigned long, seems unnecessary
for (count = 0; count < len; ) {
if(tdctl.bufcnt == 0) {
if((c = DecodeChar()) < 0)
return(count); // fatal error
if (c < 256) {
*(buf++) = c;
text_buf[tdctl.r++] = c;
tdctl.r &= (N - 1);
count++;
}
else {
if((pos = DecodePosition()) < 0)
return(count); // fatal error
tdctl.bufpos = (tdctl.r - pos - 1) & (N - 1);
tdctl.bufcnt = c - 255 + THRESHOLD;
tdctl.bufndx = 0;
}
}
else { // still chars from last string
while( tdctl.bufndx < tdctl.bufcnt && count < len ) {
c = text_buf[(tdctl.bufpos + tdctl.bufndx) & (N - 1)];
*(buf++) = c;
tdctl.bufndx++;
text_buf[tdctl.r++] = c;
tdctl.r &= (N - 1);
count++;
}
// reset bufcnt after copy string from text_buf[]
if(tdctl.bufndx >= tdctl.bufcnt)
tdctl.bufndx = tdctl.bufcnt = 0;
}
}
return(count); // count == len, success
}
FLOPPY_CONSTRUCT( td0_dsk_construct )
{
td0dsk_t state;
struct FloppyCallbacks *callbacks;
struct td0dsk_tag *tag;
uint8_t *header;
int number_of_sectors;
int position;
int i;
int track;
if(params)
{
// create
return FLOPPY_ERROR_UNSUPPORTED;
}
tag = (struct td0dsk_tag *) floppy_create_tag(floppy, sizeof(struct td0dsk_tag));
if (!tag)
return FLOPPY_ERROR_OUTOFMEMORY;
tag->data = (uint8_t*)malloc(floppy_image_size(floppy));
if (tag->data==nullptr) {
return FLOPPY_ERROR_OUTOFMEMORY;
}
floppy_image_read(floppy, tag->data, 0, floppy_image_size(floppy));
header = tag->data;
if (header[0]=='t') {
uint8_t obuf[BUFSZ];
int rd;
int off = 12;
int size = 0;
state.floppy_file = &(floppy->io);
state.init_Decode();
state.floppy_file_offset = 12;
do
{
if((rd = state.Decode(obuf, BUFSZ)) > 0) size += rd;
} while(rd == BUFSZ);
memcpy(obuf,tag->data,12);
free(tag->data);
tag->data = (uint8_t*)malloc(size+12);
if (tag->data==nullptr) {
return FLOPPY_ERROR_OUTOFMEMORY;
}
memcpy(tag->data,obuf,12);
state.floppy_file_offset = 12;
state.init_Decode();
do
{
if((rd = state.Decode(obuf, BUFSZ)) > 0) {
memcpy(tag->data+off,obuf,rd);
off += rd;
}
} while(rd == BUFSZ);
}
header = tag->data;
tag->heads = header[9];
if (tag->heads > 1) {
tag->heads = 2;
}
// header len + comment header + comment len
position = 12;
if (header[7] & 0x80) {
position += 10 + header[14] + (header[15]<<8);
}
tag->tracks = 0;
do {
// read track header
header = tag->data + position;
track = header[1];
number_of_sectors = header[0];
if (number_of_sectors!=0xff){
position+=4;
tag->track_offsets[(track<<1) + (header[2] & 1)] = position;
for(i=0;i<number_of_sectors;i++) {
// read sector header
header = tag->data + position;
position+=6;
// read sector size
if ((header[4] & 0x30)==0) {
// if there is sector data
header = tag->data + position;
position+=2;
// skip sector data
position+= header[0] + (header[1]<<8);
}
}
tag->tracks++;
}
} while(number_of_sectors!=0xff);
tag->tracks++;
callbacks = floppy_callbacks(floppy);
callbacks->read_sector = td0_read_sector;
callbacks->read_indexed_sector = td0_read_indexed_sector;
callbacks->get_sector_length = td0_get_sector_length;
callbacks->get_heads_per_disk = td0_get_heads_per_disk;
callbacks->get_tracks_per_disk = td0_get_tracks_per_disk;
callbacks->get_indexed_sector_info = td0_get_indexed_sector_info;
return FLOPPY_ERROR_SUCCESS;
}
FLOPPY_DESTRUCT( td0_dsk_destruct )
{
struct td0dsk_tag *tag = get_tag(floppy);
free(tag->data);
tag->data = nullptr;
return FLOPPY_ERROR_SUCCESS;
}
/*********************************************************************
formats/td0_dsk.h
Teledisk disk images
*********************************************************************/
#include "td0_dsk.h"
td0_format::td0_format()
{
}
const char *td0_format::name() const
{
return "td0";
}
const char *td0_format::description() const
{
return "Teledisk disk image";
}
const char *td0_format::extensions() const
{
return "td0";
}
int td0_format::identify(io_generic *io, uint32_t form_factor)
{
uint8_t h[7];
io_generic_read(io, h, 0, 7);
if(((h[0] == 'T') && (h[1] == 'D')) || ((h[0] == 't') && (h[1] == 'd')))
{
return 100;
}
return 0;
}
bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
int track_count = 0;
int head_count = 0;
int track_spt;
int offset = 0;
const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy
std::vector<uint8_t> imagebuf(max_size);
uint8_t header[12];
io_generic_read(io, header, 0, 12);
head_count = header[9];
if(header[0] == 't')
{
td0dsk_t disk_decode;
disk_decode.floppy_file = io;
disk_decode.init_Decode();
disk_decode.floppy_file_offset = 12;
disk_decode.Decode(&imagebuf[0], max_size);
}
else
io_generic_read(io, &imagebuf[0], 12, io_generic_size(io));
if(header[7] & 0x80)
offset = 10 + imagebuf[2] + (imagebuf[3] << 8);
track_spt = imagebuf[offset];
if(track_spt == 255) // Empty file?
return false;
switch(header[6])
{
case 2:
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
{
if(head_count == 2)
image->set_variant(floppy_image::DSHD);
else
return false; // single side hd?
break;
}
/* no break; could be qd, won't know until tracks are counted */
case 1:
if(head_count == 2)
image->set_variant(floppy_image::DSDD);
else
image->set_variant(floppy_image::SSDD);
break;
case 4:
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
{
if(head_count == 2)
image->set_variant(floppy_image::DSHD);
else
return false; // single side 3.5?
break;
} else
image->set_variant(floppy_image::SSDD);
break;
/* no break */
case 3:
if(head_count == 2)
{
if(form_factor == floppy_image::FF_525)
image->set_variant(floppy_image::DSQD);
else
image->set_variant(floppy_image::DSDD);
}
else
{
if(form_factor == floppy_image::FF_525)
image->set_variant(floppy_image::SSQD);
else
return false; // single side 3.5?
}
break;
}
static const int rates[3] = { 250000, 300000, 500000 };
int rate = (header[5] & 0x7f) >= 3 ? 500000 : rates[header[5] & 0x7f];
int rpm = form_factor == floppy_image::FF_8 || (form_factor == floppy_image::FF_525 && rate >= 300000) ? 360 : 300;
int base_cell_count = rate*60/rpm;
while(track_spt != 255)
{
desc_pc_sector sects[256];
uint8_t sect_data[65536];
int sdatapos = 0;
int track = imagebuf[offset + 1];
int head = imagebuf[offset + 2] & 1;
bool fm = (header[5] & 0x80) || (imagebuf[offset + 2] & 0x80); // ?
offset += 4;
for(int i = 0; i < track_spt; i++)
{
uint8_t *hs = &imagebuf[offset];
uint16_t size;
offset += 6;
sects[i].track = hs[0];
sects[i].head = hs[1];
sects[i].sector = hs[2];
sects[i].size = hs[3];
sects[i].deleted = (hs[4] & 4) == 4;
sects[i].bad_crc = (hs[4] & 2) == 2;
if(hs[4] & 0x30)
size = 0;
else
{
offset += 3;
size = 128 << hs[3];
int j, k;
switch(hs[8])
{
default:
return false;
case 0:
memcpy(&sect_data[sdatapos], &imagebuf[offset], size);
offset += size;
break;
case 1:
offset += 4;
k = (hs[9] + (hs[10] << 8)) * 2;
k = (k <= size) ? k : size;
for(j = 0; j < k; j += 2)
{
sect_data[sdatapos + j] = hs[11];
sect_data[sdatapos + j + 1] = hs[12];
}
if(k < size)
memset(&sect_data[sdatapos + k], '\0', size - k);
break;
case 2:
k = 0;
while(k < size)
{
uint16_t len = imagebuf[offset];
uint16_t rep = imagebuf[offset + 1];
offset += 2;
if(!len)
{
memcpy(&sect_data[sdatapos + k], &imagebuf[offset], rep);
offset += rep;
k += rep;
}
else
{
len = (1 << len);
rep = len * rep;
rep = ((rep + k) <= size) ? rep : (size - k);
for(j = 0; j < rep; j += len)
memcpy(&sect_data[sdatapos + j + k], &imagebuf[offset], len);
k += rep;
offset += len;
}
}
break;
}
}
sects[i].actual_size = size;
if(size)
{
sects[i].data = &sect_data[sdatapos];
sdatapos += size;
}
else
sects[i].data = nullptr;
}
track_count = track;
if(fm)
build_pc_track_fm(track, head, image, base_cell_count, track_spt, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
else
build_pc_track_mfm(track, head, image, base_cell_count*2, track_spt, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
track_spt = imagebuf[offset];
}
if((track_count > 50) && (form_factor == floppy_image::FF_525)) // ?
{
if(image->get_variant() == floppy_image::DSDD)
image->set_variant(floppy_image::DSQD);
else if(image->get_variant() == floppy_image::SSDD)
image->set_variant(floppy_image::SSQD);
}
return true;
}
bool td0_format::save(io_generic *io, floppy_image *image)
{
return false;
}
bool td0_format::supports_save() const
{
return false;
}
const floppy_format_type FLOPPY_TD0_FORMAT = &floppy_image_format_creator<td0_format>;