1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-28 14:20:54 +03:00
Files
fuse/peripherals/disk/disk.c
Philip Kendall 224fe9332d Merge in trunk r4549.
Legacy-ID: 4550
2011-11-06 22:10:10 +00:00

2631 lines
77 KiB
C

/* disk.c: Routines for handling disk images
Copyright (c) 2007-2010 Gergely Szasz
$Id$
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Author contact information:
Philip: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libspectrum.h>
#include "bitmap.h"
#include "crc.h"
#include "disk.h"
#include "settings.h"
#include "ui/ui.h"
#include "utils.h"
/* The ordering of these strings must match the order of the
* disk_error_t enumeration in disk.h */
static const char *disk_error[] = {
"OK", /* DISK_OK */
"Feature not implemented", /* DISK_IMPL */
"Out of memory", /* DISK_MEM */
"Invalid disk geometry", /* DISK_GEOM */
"Cannot open disk image", /* DISK_OPEN */
"Unsupported file feature", /* DISK_UNSUP */
"Read only disk", /* DISK_RDONLY */
"Cannot close file", /* DISK_CLOSE */
"Cannot write disk image", /* DISK_WRFILE */
"Partially written file", /* DISK_WRPART */
"Unknown error code" /* DISK_LAST_ERROR */
};
static const int disk_bpt[] = {
6250, /* AUTO assumes DD */
5208, /* 8" SD */
10416, /* 8" DD */
3125, /* SD */
6250, /* DD */
6500, /* DD+ e.g. Coin Op Hits */
12500, /* HD */
};
static unsigned char head[256];
typedef struct disk_gap_t {
int gap; /* gap byte */
int sync; /* sync byte */
int sync_len;
int mark; /* mark byte 0xa1 for MFM -1 for MF */
int len[4];
} disk_gap_t;
disk_gap_t gaps[] = {
{ 0x4e, 0x00, 12, 0xa1, { 0, 60, 22, 24 } }, /* MGT MFM */
{ 0x4e, 0x00, 12, 0xa1, { 0, 10, 22, 60 } }, /* TRD MFM */
{ 0xff, 0x00, 6, -1, { 40, 26, 11, 27 } }, /* IBM3740 FM */
{ 0x4e, 0x00, 12, 0xa1, { 80, 50, 22, 54 } }, /* IBM34 MFM */
{ 0xff, 0x00, 6, -1, { 0, 16, 11, 10 } }, /* MINIMAL FM */
{ 0x4e, 0x00, 12, 0xa1, { 0, 32, 22, 24 } }, /* MINIMAL MFM */
};
#define GAP_MGT_PLUSD 0
#define GAP_TRDOS 1
#define GAP_IBM3740 2
#define GAP_IBM34 3
#define GAP_MINIMAL_FM 4
#define GAP_MINIMAL_MFM 5
#define buffavail(buffer) ( buffer->file.length - buffer->index )
/* data buffer */
#define buff ( buffer->file.buffer + buffer->index )
typedef struct buffer_t { /* to store buffer data */
utils_file file; /* buffer, length */
size_t index;
} buffer_t;
void disk_update_tlens( disk_t *d );
const char *
disk_strerror( int error )
{
if( error < 0 || error > DISK_LAST_ERROR )
error = DISK_LAST_ERROR;
return disk_error[ error ];
}
static int
buffread( void *data, size_t len, buffer_t *buffer )
{
if( len > buffer->file.length - buffer->index )
return 0;
memcpy( data, buffer->file.buffer + buffer->index, len );
buffer->index += len;
return 1;
}
static int
buffseek( buffer_t *buffer, long offset, int whence )
{
if( whence == SEEK_CUR )
offset += buffer->index;
if( offset >= buffer->file.length )
return -1;
buffer->index = offset;
return 0;
}
static int
id_read( disk_t *d, int *head, int *track, int *sector, int *length )
{
int a1mark = 0;
while( d->i < d->bpt ) {
if( d->track[ d->i ] == 0xa1 &&
bitmap_test( d->clocks, d->i ) ) { /* 0xa1 with clock */
a1mark = 1;
} else if( d->track[ d->i ] == 0xfe &&
( bitmap_test( d->clocks, d->i ) || /* 0xfe with clock */
a1mark ) ) { /* or 0xfe with 0xa1 */
d->i++;
*track = d->track[ d->i++ ];
*head = d->track[ d->i++ ];
*sector = d->track[ d->i++ ];
*length = d->track[ d->i++ ];
d->i += 2; /* skip CRC */
return 1;
} else {
a1mark = 0;
}
d->i++;
}
return 0;
}
static int
datamark_read( disk_t *d, int *deleted )
{
int a1mark = 0;
while( d->i < d->bpt ) {
if( d->track[ d->i ] == 0xa1 &&
bitmap_test( d->clocks, d->i ) ) { /* 0xa1 with clock */
a1mark = 1;
} else if( d->track[ d->i ] >= 0xf8 && d->track[ d->i ] <= 0xfe &&
( bitmap_test( d->clocks, d->i ) || a1mark ) ) {
/* 0xfe with clock or 0xfe after 0xa1 mark */
*deleted = d->track[ d->i ] == 0xf8 ? 1 : 0;
d->i++;
return 1;
} else {
a1mark = 0;
}
d->i++;
}
return 0;
}
static int
id_seek( disk_t *d, int sector )
{
int h, t, s, b;
d->i = 0; /* start of the track */
while( id_read( d, &h, &t, &s, &b ) ) {
if( s == sector )
return 1;
}
return 0;
}
/* seclen 00 -> 128, 01 -> 256 ... byte */
static int
data_write_file( disk_t *d, FILE *file, int seclen )
{
int len = 0x80 << seclen;
if( fwrite( &d->track[ d->i ], len, 1, file ) != 1 )
return 1;
return 0;
}
static int
savetrack( disk_t *d, FILE *file, int head, int track,
int sector_base, int sectors, int seclen )
{
int s;
int del;
DISK_SET_TRACK( d, head, track );
d->i = 0;
for( s = sector_base; s < sector_base + sectors; s++ ) {
if( id_seek( d, s ) ) {
if( datamark_read( d, &del ) ) { /* write data if we have data */
if( data_write_file( d, file, seclen ) )
return 1;
}
} else {
return 1;
}
}
return 0;
}
static int
saverawtrack( disk_t *d, FILE *file, int head, int track )
{
int h, t, s, seclen;
int del;
DISK_SET_TRACK( d, head, track );
d->i = 0;
while( id_read( d, &h, &t, &s, &seclen ) ) {
if( datamark_read( d, &del ) ) { /* write data if we have data */
if( data_write_file( d, file, seclen ) )
return 1;
}
}
return 0;
}
#define DISK_ID_NOTMATCH 1
#define DISK_SECLEN_VARI 2
#define DISK_SPT_VARI 4
#define DISK_SBASE_VARI 8
#define DISK_MFM_VARI 16
#define DISK_DDAM 32
#define DISK_CORRUPT_SECTOR 64
#define DISK_UNFORMATTED_TRACK 128
#define DISK_FM_DATA 256
#define DISK_WEAK_DATA 512
static int
guess_track_geom( disk_t *d, int head, int track, int *sector_base,
int *sectors, int *seclen, int *mfm )
{
int r = 0;
int h, t, s, sl;
int del;
*sector_base = -1;
*sectors = 0;
*seclen = -1;
*mfm = -1;
DISK_SET_TRACK( d, head, track );
d->i = 0;
while( id_read( d, &h, &t, &s, &sl ) ) {
if( *sector_base == -1 )
*sector_base = s;
if( *seclen == -1 )
*seclen = sl;
if( *mfm == -1 )
*mfm = d->track[ d->i ] == 0x4e ? 1 : 0; /* not so robust */
if( !datamark_read( d, &del ) )
r |= DISK_CORRUPT_SECTOR;
if( t != track )
r |= DISK_ID_NOTMATCH;
if( s < *sector_base )
*sector_base = s;
if( sl != *seclen ) {
r |= DISK_SECLEN_VARI;
if( sl > *seclen )
*seclen = sl;
}
if( del )
r |= DISK_DDAM;
*sectors += 1;
}
return r;
}
static void
update_tracks_mode( disk_t *d )
{
int i, j, bpt;
int mfm, fm, weak;
for( i = 0; i < d->cylinders * d->sides; i++ ) {
DISK_SET_TRACK_IDX( d, i );
mfm = 0, fm = 0, weak = 0;
bpt = d->track[-3] + 256 * d->track[-2];
for( j = DISK_CLEN( bpt ) - 1; j >= 0; j-- ) {
mfm |= ~d->fm[j];
fm |= d->fm[j];
weak |= d->weak[j];
}
if( mfm && !fm ) d->track[-1] = 0x00;
if( !mfm && fm ) d->track[-1] = 0x01;
if( mfm && fm ) d->track[-1] = 0x02;
if( weak ) {
d->track[-1] |= 0x80;
d->have_weak = 1;
}
}
}
static int
check_disk_geom( disk_t *d, int *sector_base, int *sectors,
int *seclen, int *mfm, int *unf )
{
int h, t, s, slen, sbase, m;
int r = 0;
DISK_SET_TRACK_IDX( d, 0 );
d->i = 0;
*sector_base = -1;
*sectors = -1;
*seclen = -1;
*mfm = -1;
*unf = -1;
for( t = 0; t < d->cylinders; t++ ) {
for( h = 0; h < d->sides; h++ ) {
r |= ( d->track[-1] & 0x80 ) ? DISK_WEAK_DATA : 0;
r |= ( d->track[-1] & 0x03 ) == 0x02 ? DISK_MFM_VARI : 0;
r |= ( d->track[-1] & 0x03 ) == 0x01 ? DISK_FM_DATA : 0;
r |= guess_track_geom( d, h, t, &sbase, &s, &slen, &m );
if( *sector_base == -1 )
*sector_base = sbase;
if( *sectors == -1 )
*sectors = s;
if( *seclen == -1 )
*seclen = slen;
if( *mfm == -1 )
*mfm = m;
if( sbase == -1 ) { /* unformatted */
if( *unf == -1 && h > 0 ) *unf = -2;
if( *unf == -1 ) *unf = t;
continue;
}
if( *unf > -1 ) *unf = -2;
if( sbase != *sector_base ) {
r |= DISK_SBASE_VARI;
if( sbase < *sector_base )
*sector_base = sbase;
}
if( s != *sectors ) {
r |= DISK_SPT_VARI;
if( s > *sectors )
*sectors = s;
}
if( slen != *seclen ) {
r |= DISK_SECLEN_VARI;
if( slen > *seclen )
*seclen = slen;
}
if( m != *mfm ) {
r |= DISK_MFM_VARI;
*mfm = 1;
}
}
}
if( *unf == -2 ) {
r |= DISK_UNFORMATTED_TRACK;
*unf = -1;
}
return r;
}
static int
gap_add( disk_t *d, int gap, int gaptype )
{
disk_gap_t *g = &gaps[ gaptype ];
if( d->i + g->len[gap] >= d->bpt ) /* too many data bytes */
return 1;
/*-------------------------------- given gap --------------------------------*/
memset( d->track + d->i, g->gap, g->len[gap] ); d->i += g->len[gap];
return 0;
}
static int
preindex_len( disk_t *d, int gaptype ) /* preindex gap and index mark */
{
disk_gap_t *g = &gaps[ gaptype ];
return g->len[0] + g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 1;
}
/*
[ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ]
|------------------------------------------->
Preindex
*/
static int
preindex_add( disk_t *d, int gaptype ) /* preindex gap and index mark */
{
disk_gap_t *g = &gaps[ gaptype ];
if( d->i + preindex_len( d, gaptype ) >= d->bpt )
return 1;
/*------------------------------ pre-index gap -------------------------------*/
if( gap_add( d, 0, gaptype ) )
return 1;
/*------------------------------ sync ---------------------------*/
memset( d->track + d->i, g->sync, g->sync_len ); d->i += g->sync_len;
if( g->mark >= 0 ) {
memset( d->track + d->i , g->mark, 3 );
bitmap_set( d->clocks, d->i ); d->i++;
bitmap_set( d->clocks, d->i ); d->i++;
bitmap_set( d->clocks, d->i ); d->i++;
}
/*------------------------------ mark ------------------------------*/
if( g->mark < 0 ) /* FM */
bitmap_set( d->clocks, d->i ); /* set clock mark */
d->track[ d->i++ ] = 0xfc; /* index mark */
return 0;
}
static int
postindex_len( disk_t *d, int gaptype ) /* preindex gap and index mark */
{
disk_gap_t *g = &gaps[ gaptype ];
return g->len[1];
}
static int
postindex_add( disk_t *d, int gaptype ) /* postindex gap */
{
return gap_add( d, 1, gaptype );
}
static int
gap4_add( disk_t *d, int gaptype )
{
int len = d->bpt - d->i;
disk_gap_t *g = &gaps[ gaptype ];
if( len < 0 ) {
return 1;
}
/*------------------------------ GAP IV ------------------------------*/
memset( d->track + d->i, g->gap, len ); /* GAP IV fill until end of track */
d->i = d->bpt;
return 0;
}
#define SECLEN_128 0x00
#define SECLEN_256 0x01
#define SECLEN_512 0x02
#define SECLEN_1024 0x03
#define CRC_OK 0
#define CRC_ERROR 1
/*
[ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ] [ .. DATA .. ] [ . CRC . ]
|------------------------------------------------------->
ID
*/
static int
id_add( disk_t *d, int h, int t, int s, int l, int gaptype, int crc_error )
{
libspectrum_word crc = 0xffff;
disk_gap_t *g = &gaps[ gaptype ];
if( d->i + g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 7 >= d->bpt )
return 1;
/*------------------------------ sync ---------------------------*/
memset( d->track + d->i, g->sync, g->sync_len ); d->i += g->sync_len;
if( g->mark >= 0 ) {
memset( d->track + d->i , g->mark, 3 );
bitmap_set( d->clocks, d->i ); d->i++;
crc = crc_fdc( crc, g->mark );
bitmap_set( d->clocks, d->i ); d->i++;
crc = crc_fdc( crc, g->mark );
bitmap_set( d->clocks, d->i ); d->i++;
crc = crc_fdc( crc, g->mark );
}
/*------------------------------ mark ------------------------------*/
if( g->mark < 0 ) /* FM */
bitmap_set( d->clocks, d->i ); /* set clock mark */
d->track[ d->i++ ] = 0xfe; /* ID mark */
crc = crc_fdc( crc, 0xfe );
/*------------------------------ header ------------------------------*/
d->track[ d->i++ ] = t; crc = crc_fdc( crc, t );
d->track[ d->i++ ] = h; crc = crc_fdc( crc, h );
d->track[ d->i++ ] = s; crc = crc_fdc( crc, s );
d->track[ d->i++ ] = l; crc = crc_fdc( crc, l );
d->track[ d->i++ ] = crc >> 8;
if( crc_error ) {
d->track[ d->i++ ] = (~crc) & 0xff; /* record a CRC error */
} else {
d->track[ d->i++ ] = crc & 0xff; /* CRC */
}
/*------------------------------ GAP II ------------------------------*/
return gap_add( d, 2, gaptype );
}
/*
[ ....GAP.... ] [ ... SYNC ... ] [ . MARK . ] [ .. DATA .. ] [ . CRC . ]
|-------------------------------------------->
datamark
*/
static int
datamark_add( disk_t *d, int ddam, int gaptype )
{
disk_gap_t *g = &gaps[ gaptype ];
if( d->i + g->len[2] + g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 1 >= d->bpt )
return 1;
/*------------------------------ sync ---------------------------*/
memset( d->track + d->i, g->sync, g->sync_len ); d->i += g->sync_len;
if( g->mark >= 0 ) {
memset( d->track + d->i , g->mark, 3 );
bitmap_set( d->clocks, d->i ); d->i++;
bitmap_set( d->clocks, d->i ); d->i++;
bitmap_set( d->clocks, d->i ); d->i++;
}
/*------------------------------ mark ------------------------------*/
if( g->mark < 0 ) /* FM */
bitmap_set( d->clocks, d->i ); /* set clock mark */
d->track[ d->i++ ] = ddam ? 0xf8 : 0xfb; /* DATA mark 0xf8 -> deleted data */
return 0;
}
#define NO_DDAM 0
#define DDAM 1
#define NO_AUTOFILL -1
/* copy data from *buffer and update *buffer->index */
/* if 'buffer' == NULL, then copy data bytes from 'data' */
static int
data_add( disk_t *d, buffer_t *buffer, unsigned char *data, int len, int ddam,
int gaptype, int crc_error, int autofill, int *start_data )
{
int length;
libspectrum_word crc = 0xffff;
disk_gap_t *g = &gaps[ gaptype ];
if( datamark_add( d, ddam, gaptype ) )
return 1;
if( g->mark >= 0 ) {
crc = crc_fdc( crc, g->mark );
crc = crc_fdc( crc, g->mark );
crc = crc_fdc( crc, g->mark );
}
crc = crc_fdc( crc, ddam ? 0xf8 : 0xfb ); /* deleted or normal */
if( len < 0 )
goto header_crc_error; /* CRC error */
if( d->i + len + 2 >= d->bpt ) /* too many data bytes */
return 1;
/*------------------------------ data ------------------------------*/
if( start_data != NULL ) *start_data = d->i; /* record data start position */
if( buffer == NULL ) {
memcpy( d->track + d->i, data, len );
length = len;
} else {
length = buffavail( buffer );
if( length > len ) length = len;
buffread( d->track + d->i, length, buffer );
}
if( length < len ) { /* autofill with 'autofill' */
if( autofill < 0 )
return 1;
while( length < len ) {
d->track[ d->i + length ] = autofill;
length++;
}
}
length = 0;
while( length < len ) { /* calculate CRC */
crc = crc_fdc( crc, d->track[ d->i ] );
d->i++;
length++;
}
if( crc_error ) crc ^= 1; /* mess up CRC */
d->track[ d->i++ ] = crc >> 8; d->track[ d->i++ ] = crc & 0xff; /* CRC */
/*------------------------------ GAP III ------------------------------*/
header_crc_error:
return ( gap_add( d, 3, gaptype ) );
}
static int
calc_sectorlen( int mfm, int sector_length, int gaptype )
{
int len = 0;
disk_gap_t *g = &gaps[ gaptype ];
/*------------------------------ ID ------------------------------*/
len += g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 7;
/*------------------------------ GAP II ------------------------------*/
len += g->len[2];
/*--------------------------------- data ---------------------------------*/
len += g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 1; /* DAM */
len += sector_length;
len += 2; /* CRC */
/*------------------------------ GAP III ------------------------------*/
len += g->len[3];
return len;
}
static int
calc_lenid( int sector_length )
{
int id = 0;
while( sector_length > 0x80 ) {
id++;
sector_length >>= 1;
}
return id;
}
#define NO_INTERLEAVE 1
#define INTERLEAVE_2 2
#define INTERLEAVE_OPUS 13
#define NO_PREINDEX 0
#define PREINDEX 1
static int
trackgen( disk_t *d, buffer_t *buffer, int head, int track,
int sector_base, int sectors, int sector_length, int preindex,
int gap, int interleave, int autofill )
{
int i, s, pos;
int slen = calc_sectorlen( ( d->density != DISK_SD && d->density != DISK_8_SD ),
sector_length, gap );
int idx;
d->i = 0;
DISK_SET_TRACK( d, head, track );
if( preindex && preindex_add( d, gap ) )
return 1;
if( postindex_add( d, gap ) )
return 1;
idx = d->i;
pos = i = 0;
for( s = sector_base; s < sector_base + sectors; s++ ) {
d->i = idx + pos * slen;
if( id_add( d, head, track, s, calc_lenid( sector_length ), gap, CRC_OK ) )
return 1;
if( data_add( d, buffer, NULL, sector_length, NO_DDAM, gap, CRC_OK, autofill, NULL ) )
return 1;
pos += interleave;
if( pos >= sectors ) { /* wrap around */
pos -= sectors;
if( pos <= i ) { /* we fill this pos already */
pos++; /* skip one more pos */
i++;
}
}
}
d->i = idx + sectors * slen;
return gap4_add( d, gap );
}
/* close and destroy a disk structure and data */
void
disk_close( disk_t *d )
{
if( d->data != NULL ) {
free( d->data );
d->data = NULL;
}
if( d->filename != NULL ) {
free( d->filename );
d->filename = NULL;
}
d->type = DISK_TYPE_NONE;
}
/*
* if d->density == DISK_DENS_AUTO =>
* use d->tlen if d->bpt == 0
* or use d->bpt to determine d->density
* or use d->density
*/
static int
disk_alloc( disk_t *d )
{
size_t dlen;
if( d->density != DISK_DENS_AUTO ) {
d->bpt = disk_bpt[ d->density ];
} else if( d->bpt > 12500 ) {
return d->status = DISK_UNSUP;
} else if( d->bpt > 10416 ) {
d->density = DISK_HD;
d->bpt = disk_bpt[ DISK_HD ];
} else if( d->bpt > 6500 ) {
d->density = DISK_8_DD;
d->bpt = disk_bpt[ DISK_8_DD ];
} else if( d->bpt > 6250 ) {
d->density = DISK_DD_PLUS;
d->bpt = disk_bpt[ DISK_DD_PLUS ];
} else if( d->bpt > 5208 ) {
d->density = DISK_DD;
d->bpt = disk_bpt[ DISK_DD ];
} else if( d->bpt > 3125 ) {
d->density = DISK_8_SD;
d->bpt = disk_bpt[ DISK_8_SD ];
} else if( d->bpt > 0 ) {
d->density = DISK_SD;
d->bpt = disk_bpt[ DISK_SD ];
}
if( d->bpt > 0 )
d->tlen = 4 + d->bpt + 3 * DISK_CLEN( d->bpt );
dlen = d->sides * d->cylinders * d->tlen; /* track len with clock and other marks */
if( ( d->data = calloc( 1, dlen ) ) == NULL )
return d->status = DISK_MEM;
return d->status = DISK_OK;
}
/* create a new unformatted disk */
int
disk_new( disk_t *d, int sides, int cylinders,
disk_dens_t density, disk_type_t type )
{
d->filename = NULL;
if( density < DISK_DENS_AUTO || density > DISK_HD || /* unknown density */
type <= DISK_TYPE_NONE || type >= DISK_TYPE_LAST || /* unknown type */
sides < 1 || sides > 2 || /* 1 or 2 side */
cylinders < 35 || cylinders > 83 ) /* 35 .. 83 cylinder */
return d->status = DISK_GEOM;
d->type = type;
d->density = density == DISK_DENS_AUTO ? DISK_DD : density;
d->sides = sides;
d->cylinders = cylinders;
if( disk_alloc( d ) != DISK_OK )
return d->status;
d->wrprot = 0;
d->dirty = 0;
disk_update_tlens( d );
return d->status = DISK_OK;
}
static int
alloc_uncompress_buffer( unsigned char **buffer, int length )
{
unsigned char *b;
if( *buffer != NULL ) /* return if allocated */
return 0;
b = calloc( length, 1 );
if( b == NULL )
return 1;
*buffer = b;
return 0;
}
int disk_preformat( disk_t *d )
{
buffer_t buffer;
buffer.file.length = 0;
buffer.index = 0;
if( trackgen( d, &buffer, 0, 0, 0xff, 1, 128,
NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff ) )
return DISK_GEOM;
if( trackgen( d, &buffer, 0, 2, 0xfe, 1, 128,
NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff ) )
return DISK_GEOM;
return DISK_OK;
}
/* open a disk image */
#define GEOM_CHECK \
if( d->sides < 1 || d->sides > 2 || \
d->cylinders < 1 || d->cylinders > 85 ) return d->status = DISK_GEOM
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
static int
udi_read_compressed( const libspectrum_byte *buffer,
size_t compr_size, size_t uncompr_size,
libspectrum_byte **data, size_t *data_size )
{
libspectrum_error error;
libspectrum_byte *tmp;
size_t olength = uncompr_size;
tmp = NULL;
error = libspectrum_zlib_inflate( buffer, compr_size, &tmp, &olength );
if( error ) {
if( *data ) free( *data );
*data_size = 0;
return error;
}
if( *data_size < uncompr_size ) {
*data = libspectrum_realloc( *data, uncompr_size );
*data_size = uncompr_size;
}
memcpy( *data, tmp, uncompr_size );
libspectrum_free( tmp );
return 0;
}
static int
udi_write_compressed( const libspectrum_byte *buffer,
size_t uncompr_size, size_t *compr_size,
libspectrum_byte **data, size_t *data_size )
{
libspectrum_error error;
libspectrum_byte *tmp;
tmp = NULL;
error = libspectrum_zlib_compress( buffer, uncompr_size,
&tmp, compr_size );
if( error ) return error;
if( *data_size < *compr_size ) {
*data = libspectrum_realloc( *data, *compr_size );
*data_size = *compr_size;
}
memcpy( *data, tmp, *compr_size );
libspectrum_free( tmp );
return LIBSPECTRUM_ERROR_NONE;
}
#endif /* #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
static void
udi_pack_tracks( disk_t *d )
{
int i, tlen, clen, ttyp;
libspectrum_byte *tmp;
for( i = 0; i < d->sides * d->cylinders; i++ ) {
DISK_SET_TRACK_IDX( d, i );
tmp = d->track;
ttyp = tmp[-1];
tlen = tmp[-3] + 256 * tmp[-2];
clen = DISK_CLEN( tlen );
tmp += tlen;
/* copy clock if needed */
if( tmp != d->clocks )
memcpy( tmp, d->clocks, clen );
if( ttyp == 0x00 || ttyp == 0x01 ) continue;
tmp += clen;
if( ttyp & 0x02 ) { /* copy FM marks */
if( tmp != d->fm )
memcpy( tmp, d->fm, clen );
tmp += clen;
}
if( ! ( ttyp & 0x80 ) ) continue;
if( tmp != d->weak ) /* copy WEAK marks*/
memcpy( tmp, d->weak, clen );
}
}
static void
udi_unpack_tracks( disk_t *d )
{
int i, tlen, clen, ttyp;
libspectrum_byte *tmp;
libspectrum_byte mask[] = { 0xff, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
for( i = 0; i < d->sides * d->cylinders; i++ ) {
DISK_SET_TRACK_IDX( d, i );
tmp = d->track;
ttyp = tmp[-1];
tlen = tmp[-3] + 256 * tmp[-2];
clen = DISK_CLEN( tlen );
tmp += tlen;
if( ttyp & 0x80 ) tmp += clen;
if( ttyp & 0x02 ) tmp += clen;
if( ( ttyp & 0x80 ) ) { /* copy WEAK marks*/
if( tmp != d->weak )
memcpy( d->weak, tmp, clen );
tmp -= clen;
} else { /* clear WEAK marks*/
memset( d->weak, 0, clen );
}
if( ttyp & 0x02 ) { /* copy FM marks */
if( tmp != d->fm )
memcpy( d->fm, tmp, clen );
tmp -= clen;
} else { /* set/clear FM marks*/
memset( d->fm, ttyp & 0x01 ? 0xff : 0, clen );
if( tlen % 8 ) { /* adjust last byte */
d->fm[clen - 1] &= mask[ tlen % 8 ];
}
}
/* copy clock if needed */
if( tmp != d->clocks )
memcpy( d->clocks, tmp, clen );
}
}
/* calculate track len from type, if type eq. 0x00/0x01/0x02/0x80/0x81/0x82
!!! not for 0x83 nor 0xf0 !!!
*/
#define UDI_TLEN( type, bpt ) ( ( bpt ) + DISK_CLEN( bpt ) * ( 1 + \
( type & 0x02 ? 1 : 0 ) + \
( type & 0x80 ? 1 : 0 ) ) )
static int
udi_uncompress_tracks( disk_t *d )
{
int i;
libspectrum_byte *data = NULL;
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
size_t data_size = 0;
int bpt, tlen, clen, ttyp;
#endif /* #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
for( i = 0; i < d->sides * d->cylinders; i++ ) {
DISK_SET_TRACK_IDX( d, i );
if( d->track[-1] != 0xf0 ) continue; /* if not compressed */
#ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
/* if libspectrum cannot support */
return d->status = DISK_UNSUP;
#else /* #ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
clen = d->track[-3] + 256 * d->track[-2] + 1;
ttyp = d->track[0]; /* compressed track type */
bpt = d->track[1] + 256 * d->track[2]; /* compressed track len... */
tlen = UDI_TLEN( ttyp, bpt );
d->track[-1] = ttyp;
d->track[-3] = d->track[1];
d->track[-2] = d->track[2];
if( udi_read_compressed( d->track + 3, clen, tlen, &data, &data_size ) ) {
if( data ) libspectrum_free( data );
return d->status = DISK_UNSUP;
}
memcpy( d->track, data, tlen ); /* read track */
#endif /* #ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
}
if( data ) libspectrum_free( data );
return DISK_OK;
}
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
static int
udi_compress_tracks( disk_t *d )
{
int i, tlen;
libspectrum_byte *data = NULL;
size_t clen, data_size = 0;
for( i = 0; i < d->sides * d->cylinders; i++ ) {
DISK_SET_TRACK_IDX( d, i );
if( d->track[-1] == 0xf0 ) continue; /* already compressed??? */
tlen = UDI_TLEN( d->track[-1], d->track[-3] + 256 * d->track[-2] );
/* if fail to compress, skip ... */
if( udi_write_compressed( d->track, tlen, &clen, &data, &data_size ) ||
clen < 1 ) continue;
/* if compression too large, skip... */
if( clen > 65535 || clen >= tlen ) continue;
d->track[0] = d->track[-1]; /* track type... */
d->track[1] = d->track[-3]; /* compressed track len... */
d->track[2] = d->track[-2]; /* compressed track len... */
memcpy( d->track + 3, data, clen ); /* read track */
clen--;
d->track[-1] = 0xf0;
d->track[-3] = clen & 0xff;
d->track[-2] = ( clen >> 8 ) & 0xff;
}
if( data ) libspectrum_free( data );
return DISK_OK;
}
#endif /* #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
static int
open_udi( buffer_t *buffer, disk_t *d )
{
int i, bpt, ttyp, tlen, error;
size_t eof;
libspectrum_dword crc;
crc = ~(libspectrum_dword) 0;
/* check file length */
eof = buff[4] + 256 * buff[5] + 65536 * buff[6] + 16777216 * buff[7];
if( eof != buffer->file.length - 4 )
return d->status = DISK_OPEN;
/* check CRC32 */
for( i = 0; i < eof; i++ )
crc = crc_udi( crc, buff[i] );
if( crc != buff[eof] + 256 * buff[eof + 1] + 65536 * buff[eof + 2] +
16777216 * buff[eof + 3] )
return d->status = DISK_OPEN;
d->sides = buff[10] + 1;
d->cylinders = buff[9] + 1;
GEOM_CHECK;
d->density = DISK_DENS_AUTO;
buffer->index = 16;
d->bpt = 0;
/* scan file for the longest track */
for( i = 0; buffer->index < eof; i++ ) {
if( buffavail( buffer ) < 3 )
return d->status = DISK_OPEN;
ttyp = buff[0];
if( ttyp != 0x00 && ttyp != 0x01 && ttyp != 0x02 && ttyp != 0x80 &&
ttyp != 0x81 && ttyp != 0x82 && ttyp != 0x83 && ttyp != 0xf0 )
return d->status = DISK_UNSUP;
/* if libspectrum cannot suppot*/
#ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
if( ttyp == 0xf0 ) d->status = DISK_UNSUP;
#endif /* #ifndef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
if( ttyp == 0x83 ) { /* multiple read */
if( i == 0 ) return d->status = DISK_GEOM; /* cannot be first track */
i--; bpt = 0; /* not a real track */
tlen = buff[1] + 256 * buff[2]; /* current track len... */
tlen = ( tlen & 0xfff8 ) * ( tlen & 0x07 );
} else if( ttyp == 0xf0 ) { /* compressed track */
if( buffavail( buffer ) < 7 )
return d->status = DISK_OPEN;
bpt = buff[4] + 256 * buff[5];
tlen = 7 + buff[1] + 256 * buff[2];
} else {
bpt = buff[1] + 256 * buff[2]; /* current track len... */
tlen = 3 + UDI_TLEN( ttyp, bpt );
}
if( bpt > d->bpt )
d->bpt = bpt;
if( buffseek( buffer, tlen, SEEK_CUR ) == -1 )
return d->status = DISK_OPEN;
}
if( d->bpt == 0 )
return d->status = DISK_GEOM;
bpt = d->bpt; /* save the maximal value */
d->tlen = 3 + bpt + 3 * DISK_CLEN( bpt );
d->bpt = 0; /* we know exactly the track len... */
if( disk_alloc( d ) != DISK_OK )
return d->status;
d->bpt = bpt; /* restore the maximal byte per track */
buffer->index = 16;
for( i = 0; buffer->index < eof; i++ ) {
DISK_SET_TRACK_IDX( d, i );
ttyp = buff[0];
bpt = buff[1] + 256 * buff[2]; /* current track len... */
memset( d->track, 0x4e, d->bpt ); /* fillup */
/* read track + clocks */
if( ttyp == 0x83 ) { /* multiple read */
i--; /* not a real track */
DISK_SET_TRACK_IDX( d, i ); /* back to previouse track */
d->weak += buff[3] + 256 * buff[4]; /* add offset to weak */
tlen = ( buff[1] + 256 * buff[2] ) >> 3; /* weak len in bytes */
for( tlen--; tlen >= 0; tlen-- )
d->weak[tlen] = 0xff;
tlen = buff[1] + 256 * buff[2]; /* current track len... */
tlen = ( tlen & 0xfff8 ) * ( tlen & 0x07 );
buffseek( buffer, tlen, SEEK_CUR );
} else {
if( ttyp == 0xf0 ) /* compressed */
tlen = bpt + 4;
else
tlen = UDI_TLEN( ttyp, bpt );
d->track[-1] = ttyp;
d->track[-3] = buff[1];
d->track[-2] = buff[2];
buffer->index += 3;
buffread( d->track, tlen, buffer ); /* first read data */
}
}
error = udi_uncompress_tracks( d );
if( error ) return error;
udi_unpack_tracks( d );
return d->status = DISK_OK;
}
static int
open_img_mgt_opd( buffer_t *buffer, disk_t *d )
{
int i, j, sectors, seclen;
buffer->index = 0;
/* guess geometry of disk:
* 2*80*10*512, 1*80*10*512, 1*40*10*512, 1*40*18*256, 1*80*18*256,
* 2*80*18*256
*/
if( buffer->file.length == 2*80*10*512 ) {
d->sides = 2; d->cylinders = 80; sectors = 10; seclen = 512;
} else if( buffer->file.length == 1*80*10*512 ) {
/* we cannot distinguish between a single sided 80 track image
* and a double sided 40 track image (2*40*10*512) */
d->sides = 1; d->cylinders = 80; sectors = 10; seclen = 512;
} else if( buffer->file.length == 1*40*10*512 ) {
d->sides = 1; d->cylinders = 40; sectors = 10; seclen = 512;
} else if( buffer->file.length == 1*40*18*256 ) {
d->sides = 1; d->cylinders = 40; sectors = 18; seclen = 256;
} else if( buffer->file.length == 1*80*18*256 ) {
/* we cannot distinguish between a single sided 80 track image
* and a double sided 40 track image (2*40*18*256) */
d->sides = 1; d->cylinders = 80; sectors = 18; seclen = 256;
} else if( buffer->file.length == 2*80*18*256 ) {
d->sides = 2; d->cylinders = 80; sectors = 18; seclen = 256;
} else {
return d->status = DISK_GEOM;
}
/* create a DD disk */
d->density = DISK_DD;
if( disk_alloc( d ) != DISK_OK )
return d->status;
if( d->type == DISK_IMG ) { /* IMG out-out */
for( j = 0; j < d->sides; j++ ) {
for( i = 0; i < d->cylinders; i++ ) {
if( trackgen( d, buffer, j, i, 1, sectors, seclen,
NO_PREINDEX, GAP_MGT_PLUSD, NO_INTERLEAVE, NO_AUTOFILL ) )
return d->status = DISK_GEOM;
}
}
} else { /* MGT / OPD alt */
for( i = 0; i < d->cylinders; i++ ) {
for( j = 0; j < d->sides; j++ ) {
if( trackgen( d, buffer, j, i, d->type == DISK_MGT ? 1 : 0, sectors, seclen,
NO_PREINDEX, GAP_MGT_PLUSD,
d->type == DISK_MGT ? NO_INTERLEAVE : INTERLEAVE_OPUS, NO_AUTOFILL ) )
return d->status = DISK_GEOM;
}
}
}
return d->status = DISK_OK;
}
static int
open_sad( buffer_t *buffer, disk_t *d, int preindex )
{
int i, j, sectors, seclen;
d->sides = buff[18];
d->cylinders = buff[19];
GEOM_CHECK;
sectors = buff[20];
seclen = buff[21] * 64;
buffer->index = 22;
/* create a DD disk */
d->density = DISK_DD;
if( disk_alloc( d ) != DISK_OK )
return d->status;
for( j = 0; j < d->sides; j++ ) {
for( i = 0; i < d->cylinders; i++ ) {
if( trackgen( d, buffer, j, i, 1, sectors, seclen, preindex,
GAP_MGT_PLUSD, NO_INTERLEAVE, NO_AUTOFILL ) )
return d->status = DISK_GEOM;
}
}
return d->status = DISK_OK;
}
static int
open_trd( buffer_t *buffer, disk_t *d )
{
int i, j, sectors, seclen;
if( buffseek( buffer, 8*256, SEEK_CUR ) == -1 )
return d->status = DISK_OPEN;
if( buffavail( buffer ) < 256 )
return d->status = DISK_OPEN;
if( buff[231] != 0x10 || buff[227] < 0x16 || buff[227] > 0x19 )
return d->status = DISK_OPEN; /*?*/
/* guess geometry of disk */
d->sides = buff[227] & 0x08 ? 1 : 2;
d->cylinders = buff[227] & 0x01 ? 40 : 80;
/* we have more tracks then on a standard disk... */
if( buffer->file.length > d->sides * d->cylinders * 16 * 256 ) {
for( i = d->cylinders + 1; i < 83; i++ ) {
if( d->sides * i * 16 * 256 >= buffer->file.length ) break;
}
d->cylinders = i;
}
sectors = 16; seclen = 256;
/* create a DD disk */
d->density = DISK_DD;
if( disk_alloc( d ) != DISK_OK )
return d->status;
buffer->index = 0;
for( i = 0; i < d->cylinders; i++ ) {
for( j = 0; j < d->sides; j++ ) {
if( trackgen( d, buffer, j, i, 1, sectors, seclen,
NO_PREINDEX, GAP_TRDOS, INTERLEAVE_2, 0x00 ) )
return d->status = DISK_GEOM;
}
}
return d->status = DISK_OK;
}
static int
open_fdi( buffer_t *buffer, disk_t *d, int preindex )
{
int i, j, h, gap;
int bpt, bpt_fm, max_bpt = 0, max_bpt_fm = 0;
int data_offset, track_offset, head_offset, sector_offset;
d->wrprot = buff[0x03] == 1 ? 1 : 0;
d->sides = buff[0x06] + 256 * buff[0x07];
d->cylinders = buff[0x04] + 256 * buff[0x05];
GEOM_CHECK;
data_offset = buff[0x0a] + 256 * buff[0x0b];
h = 0x0e + buff[0x0c] + 256 * buff[0x0d]; /* save head start */
head_offset = h;
/* first determine the longest track */
d->bpt = 0;
for( i = 0; i < d->cylinders * d->sides; i++ ) { /* ALT */
buffer->index = head_offset;
if( buffread( head, 7, buffer ) != 1 ) /* 7 := track head */
return d->status = DISK_OPEN;
bpt = postindex_len( d, GAP_MINIMAL_MFM ) +
( preindex ? preindex_len( d, GAP_MINIMAL_MFM ) : 0 ) + 6; /* +gap4 */
bpt_fm = postindex_len( d, GAP_MINIMAL_FM ) +
( preindex ? preindex_len( d, GAP_MINIMAL_FM ) : 0 ) + 3; /* +gap4 */
for( j = 0; j < head[0x06]; j++ ) { /* calculate track len */
if( j % 35 == 0 ) { /* 35-sector header */
if( buffread( head + 7, 245, buffer ) != 1 ) /* 7*35 := max 35 sector head */
return d->status = DISK_OPEN;
}
if( ( head[ 0x0b + 7 * ( j % 35 ) ] & 0x3f ) != 0 ) {
bpt += calc_sectorlen( 1, 0x80 << head[ 0x0a + 7 * ( j % 35 ) ], GAP_MINIMAL_MFM );
bpt_fm += calc_sectorlen( 0, 0x80 << head[ 0x0a + 7 * ( j % 35 ) ], GAP_MINIMAL_FM );
}
}
if( bpt > max_bpt )
max_bpt = bpt;
if( bpt_fm > max_bpt_fm )
max_bpt_fm = bpt_fm;
head_offset += 7 + 7 * head[ 0x06 ];
}
if( max_bpt == 0 || max_bpt_fm == 0 )
return d->status = DISK_GEOM;
d->density = DISK_DENS_AUTO; /* disk_alloc use d->bpt */
if( max_bpt_fm < 3000 ) { /* we choose an SD disk with FM */
d->bpt = max_bpt_fm;
gap = GAP_MINIMAL_FM;
} else {
d->bpt = max_bpt;
gap = GAP_MINIMAL_MFM;
}
if( disk_alloc( d ) != DISK_OK )
return d->status;
/* start reading the tracks */
head_offset = h; /* restore head start */
for( i = 0; i < d->cylinders * d->sides; i++ ) { /* ALT */
buffer->index = head_offset;
buffread( head, 7, buffer ); /* 7 = track head */
track_offset = head[0x00] + 256 * head[0x01] +
65536 * head[0x02] + 16777216 * head[0x03];
DISK_SET_TRACK_IDX( d, i );
d->i = 0;
if( preindex )
preindex_add( d, gap );
postindex_add( d, gap );
for( j = 0; j < head[0x06]; j++ ) {
if( j % 35 == 0 ) { /* if we have more than 35 sector in a track,
we have to seek back to the next sector
headers and read it ( max 35 sector header */
buffer->index = head_offset + 7 *( j + 1 );
buffread( head + 7, 245, buffer ); /* 7*35 := max 35 sector head */
}
id_add( d, head[ 0x08 + 7 * ( j % 35 ) ], head[ 0x07 + 7*( j % 35 ) ],
head[ 0x09 + 7*( j % 35 ) ], head[ 0x0a + 7*( j % 35 ) ], gap,
( head[ 0x0b + 7*( j % 35 ) ] & 0x3f ) ? CRC_OK : CRC_ERROR );
sector_offset = head[ 0x0c + 7 * ( j % 35 ) ] +
256 * head[ 0x0d + 7 * ( j % 35 ) ];
buffer->index = data_offset + track_offset + sector_offset;
data_add( d, buffer, NULL, ( head[ 0x0b + 7 * ( j % 35 ) ] & 0x3f ) == 0 ?
-1 : 0x80 << head[ 0x0a + 7 * ( j % 35 ) ],
head[ 0x0b + 7 * ( j % 35 ) ] & 0x80 ? DDAM : NO_DDAM,
gap, CRC_OK, NO_AUTOFILL, NULL );
}
head_offset += 7 + 7 * head[0x06];
gap4_add( d, gap );
}
return d->status = DISK_OK;
}
static void
cpc_set_weak_range( disk_t *d, int idx, buffer_t *buffer, int n, int len )
{
int i, j, first = -1, last = -1;
libspectrum_byte *t, *w;
t = d->track + idx;
w = buffer->file.buffer + buffer->index;
for( i = 0; i < len; i++, t++, w++ ) {
for( j = 0; j < n - 1; j ++ ) {
if( *t != w[j * len] ) {
if( first == -1 ) first = idx + i;
last = idx + i;
}
}
}
for( ; first <= last; first++ ) {
bitmap_set( d->weak, first );
}
}
#define CPC_ISSUE_NONE 0
#define CPC_ISSUE_1 1
#define CPC_ISSUE_2 2
#define CPC_ISSUE_3 3
#define CPC_ISSUE_4 4
#define CPC_ISSUE_5 5
static int
open_cpc( buffer_t *buffer, disk_t *d, int preindex )
{
int i, j, seclen, idlen, gap, sector_pad, idx;
int bpt, max_bpt = 0, trlen;
int fix[84], plus3_fix;
unsigned char *hdrb;
d->sides = buff[0x31];
d->cylinders = buff[0x30]; /* maximum number of tracks */
GEOM_CHECK;
buffer->index = 256;
/* first scan for the longest track */
for( i = 0; i < d->sides*d->cylinders; i++ ) {
/* sometimes in the header there are more track than in the file */
if( buffavail( buffer ) == 0 ) { /* no more data */
d->cylinders = i / d->sides + i % d->sides; /* the real cylinder number */
break;
}
if( buffavail( buffer ) < 256 ||
memcmp( buff, "Track-Info", 10 ) ) /* check track header */
return d->status = DISK_OPEN;
gap = (unsigned char)buff[0x16] == 0xff ? GAP_MINIMAL_FM :
GAP_MINIMAL_MFM;
plus3_fix = trlen = 0;
while( i < buff[0x10] * d->sides + buff[0x11] ) {
if( i < 84 ) fix[i] = 0;
i++;
}
if( i >= d->sides*d->cylinders ||
i != buff[0x10] * d->sides + buff[0x11] ) /* problem with track idx. */
return d->status = DISK_OPEN;
bpt = postindex_len( d, gap ) +
( preindex ? preindex_len( d, gap ) : 0 ) +
( gap == GAP_MINIMAL_MFM ? 6 : 3 ); /* gap4 */
sector_pad = 0;
for( j = 0; j < buff[0x15]; j++ ) { /* each sector */
seclen = d->type == DISK_ECPC ? buff[ 0x1e + 8 * j ] +
256 * buff[ 0x1f + 8 * j ]
: 0x80 << buff[ 0x1b + 8 * j ];
idlen = 0x80 << buff[ 0x1b + 8 * j ]; /* sector length from ID */
if( idlen != 0 && idlen <= ( 0x80 << 0x08 ) && /* idlen is o.k. */
seclen > idlen && seclen % idlen ) /* seclen != N * len */
return d->status = DISK_OPEN;
bpt += calc_sectorlen( gap == GAP_MINIMAL_MFM ? 1 : 0, seclen > idlen ? idlen : seclen, gap );
if( i < 84 && d->flag & DISK_FLAG_PLUS3_CPC ) {
if( j == 0 && buff[ 0x1b + 8 * j ] == 6 && seclen > 6144 )
plus3_fix = CPC_ISSUE_4;
else if( j == 0 && buff[ 0x1b + 8 * j ] == 6 )
plus3_fix = CPC_ISSUE_1;
else if( j == 0 &&
buff[ 0x18 + 8 * j ] == j && buff[ 0x19 + 8 * j ] == j &&
buff[ 0x1a + 8 * j ] == j && buff[ 0x1b + 8 * j ] == j )
plus3_fix = CPC_ISSUE_3;
else if( j == 1 && plus3_fix == CPC_ISSUE_1 &&
buff[ 0x1b + 8 * j ] == 2 )
plus3_fix = CPC_ISSUE_2;
else if( i == 38 && j == 0 && buff[ 0x1b + 8 * j ] == 2 )
plus3_fix = CPC_ISSUE_5;
else if( j > 1 && plus3_fix == CPC_ISSUE_2 && buff[ 0x1b + 8 * j ] != 2 )
plus3_fix = CPC_ISSUE_NONE;
else if( j > 0 && plus3_fix == CPC_ISSUE_3 &&
( buff[ 0x18 + 8 * j ] != j || buff[ 0x19 + 8 * j ] != j ||
buff[ 0x1a + 8 * j ] != j || buff[ 0x1b + 8 * j ] != j ) )
plus3_fix = CPC_ISSUE_NONE;
else if( j > 10 && plus3_fix == CPC_ISSUE_2 )
plus3_fix = CPC_ISSUE_NONE;
else if( i == 38 && j > 0 && plus3_fix == CPC_ISSUE_5 &&
buff[ 0x1b + 8 * j ] != 2 - ( j & 1 ) )
plus3_fix = CPC_ISSUE_NONE;
}
trlen += seclen;
if( seclen % 0x100 ) /* every? 128/384/...byte length sector padded */
sector_pad++;
}
if( i < 84 ) {
fix[i] = plus3_fix;
if( fix[i] == CPC_ISSUE_4 ) bpt = 6500;/* Type 1 variant DD+ (e.g. Coin Op Hits) */
else if( fix[i] != CPC_ISSUE_NONE ) bpt = 6250;/* we assume a standard DD track */
}
buffer->index += trlen + sector_pad * 128 + 256;
if( bpt > max_bpt )
max_bpt = bpt;
}
if( max_bpt == 0 )
return d->status = DISK_GEOM;
d->density = DISK_DENS_AUTO; /* disk_alloc use d->bpt */
d->bpt = max_bpt;
if( disk_alloc( d ) != DISK_OK )
return d->status;
DISK_SET_TRACK_IDX( d, 0 );
buffer->index = 256; /* rewind to first track */
for( i = 0; i < d->sides*d->cylinders; i++ ) {
hdrb = buff;
buffer->index += 256; /* skip to data */
gap = (unsigned char)hdrb[0x16] == 0xff ? GAP_MINIMAL_FM : GAP_MINIMAL_MFM;
i = hdrb[0x10] * d->sides + hdrb[0x11]; /* adjust track No. */
DISK_SET_TRACK_IDX( d, i );
d->i = 0;
if( preindex)
preindex_add( d, gap );
postindex_add( d, gap );
sector_pad = 0;
for( j = 0; j < hdrb[0x15]; j++ ) { /* each sector */
seclen = d->type == DISK_ECPC ? hdrb[ 0x1e + 8 * j ] + /* data length in sector */
256 * hdrb[ 0x1f + 8 * j ]
: 0x80 << hdrb[ 0x1b + 8 * j ];
idlen = 0x80 << hdrb[ 0x1b + 8 * j ]; /* sector length from ID */
if( idlen == 0 || idlen > ( 0x80 << 0x08 ) ) /* error in sector length code -> ignore */
idlen = seclen;
if( i < 84 && fix[i] == 2 && j == 0 ) { /* repositionate the dummy track */
d->i = 8;
}
id_add( d, hdrb[ 0x19 + 8 * j ], hdrb[ 0x18 + 8 * j ],
hdrb[ 0x1a + 8 * j ], hdrb[ 0x1b + 8 * j ], gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && !( hdrb[ 0x1d + 8 * j ] & 0x20 ) ?
CRC_ERROR : CRC_OK );
if( i < 84 && fix[i] == CPC_ISSUE_1 && j == 0 ) { /* 6144 */
data_add( d, buffer, NULL, seclen,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, NULL );
} else if( i < 84 && fix[i] == CPC_ISSUE_2 && j == 0 ) { /* 6144, 10x512 */
datamark_add( d, hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap );
gap_add( d, 2, gap );
buffer->index += seclen;
} else if( i < 84 && fix[i] == CPC_ISSUE_3 ) { /* 128, 256, 512, ... 4096k */
data_add( d, buffer, NULL, 128,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, NULL );
buffer->index += seclen - 128;
} else if( i < 84 && fix[i] == CPC_ISSUE_4 ) { /* Nx8192 (max 6384 byte ) */
data_add( d, buffer, NULL, 6384,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, NULL );
buffer->index += seclen - 6384;
} else if( i < 84 && fix[i] == CPC_ISSUE_5 ) { /* 9x512 */
/* 512 256 512 256 512 256 512 256 512 */
if( idlen == 256 ) {
data_add( d, NULL, buff, 512,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, NULL );
buffer->index += idlen;
} else {
data_add( d, buffer, NULL, idlen,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, NULL );
}
} else {
data_add( d, buffer, NULL, seclen > idlen ? idlen : seclen,
hdrb[ 0x1d + 8 * j ] & 0x40 ? DDAM : NO_DDAM, gap,
hdrb[ 0x1c + 8 * j ] & 0x20 && hdrb[ 0x1d + 8 * j ] & 0x20 ?
CRC_ERROR : CRC_OK, 0x00, &idx );
if( seclen > idlen ) { /* weak sector with multiple copy */
cpc_set_weak_range( d, idx, buffer, seclen / idlen, idlen );
buffer->index += ( seclen / idlen - 1 ) * idlen;
/* ( ( N * len ) / len - 1 ) * len */
}
}
if( seclen % 0x100 ) /* every? 128/384/...byte length sector padded */
sector_pad++;
}
gap4_add( d, gap );
buffer->index += sector_pad * 0x80;
}
return d->status = DISK_OK;
}
static int
open_scl( buffer_t *buffer, disk_t *d )
{
int i, j, s, sectors, seclen;
int scl_deleted, scl_files, scl_i;
d->sides = 2;
d->cylinders = 80;
d->density = DISK_DD;
if( disk_alloc( d ) != DISK_OK )
return d->status;
/*
TR-DOS:
- Root directory is 8 sectors long starting from track 0, sector 1
- Max root entries are 128
- Root entry dimension is 16 bytes (16*128 = 2048 => 8 sector
- Logical sector(start from 0) 8th (9th physical) holds disc info
- Logical sectors from 9 to 15 are unused
- Files are *NOT* fragmented, and start on track 1, sector 1...
*/
if( ( scl_files = buff[8] ) > 128 || scl_files < 1 ) /* number of files */
return d->status = DISK_GEOM; /* too many file */
buffer->index = 9; /* read SCL entries */
DISK_SET_TRACK_IDX( d, 0 );
d->i = 0;
postindex_add( d, GAP_TRDOS );
scl_i = d->i; /* the position of first sector */
s = 1; /* start sector number */
scl_deleted = 0; /* deleted files */
sectors = 0; /* allocated sectors */
/* we use 'head[]' to build
TR-DOS directory */
j = 0; /* index for head[] */
memset( head, 0, 256 );
seclen = calc_sectorlen( 1, 256, GAP_TRDOS ); /* one sector raw length */
for( i = 0; i < scl_files; i++ ) { /* read all entry and build TR-DOS dir */
if( buffread( head + j, 14, buffer ) != 1 )
return d->status = DISK_OPEN;
head[ j + 14 ] = sectors % 16; /* ( sectors + 16 ) % 16 := sectors % 16
starting sector */
head[ j + 15 ] = sectors / 16 + 1; /* ( sectors + 16 ) / 16 := sectors / 16 + 1
starting track */
sectors += head[ j + 13 ];
if( d->data[j] == 0x01 ) /* deleted file */
scl_deleted++;
if( sectors > 16 * 159 ) /* too many sectors needed */
return d->status = DISK_MEM; /* or DISK_GEOM??? */
j += 16;
if( j == 256 ) { /* one sector ready */
d->i = scl_i + ( ( s - 1 ) % 8 * 2 + ( s - 1 ) / 8 ) * seclen; /* 1 9 2 10 3 ... */
id_add( d, 0, 0, s, SECLEN_256, GAP_TRDOS, CRC_OK );
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL, NULL );
memset( head, 0, 256 );
s++;
j = 0;
}
}
if( j != 0 ) { /* we have to add this sector */
d->i = scl_i + ( ( s - 1 ) % 8 * 2 + ( s - 1 ) / 8 ) * seclen; /* 1 9 2 10 3 ... */
id_add( d, 0, 0, s, SECLEN_256, GAP_TRDOS, CRC_OK );
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL, NULL );
s++;
}
/* and add empty sectors up to No. 16 */
memset( head, 0, 256 );
for( ; s <= 16; s++ ) { /* finish the first track */
d->i = scl_i + ( ( s - 1 ) % 8 * 2 + ( s - 1 ) / 8 ) * seclen; /* 1 9 2 10 3 ... */
id_add( d, 0, 0, s, SECLEN_256, GAP_TRDOS, CRC_OK );
if( s == 9 ) { /* TR-DOS descriptor */
head[225] = sectors % 16; /* first free sector */
head[226] = sectors / 16 + 1; /* first free track */
head[227] = 0x16; /* 80 track 2 side disk */
head[228] = scl_files; /* number of files */
head[229] = ( 2544 - sectors ) % 256; /* free sectors */
head[230] = ( 2544 - sectors ) / 256;
head[231] = 0x10; /* TR-DOS ID byte */
memset( head + 234, 32, 9 );
head[244] = scl_deleted; /* number of deleted files */
memcpy( head + 245, "FUSE-SCL", 8 );
}
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL, NULL );
if( s == 9 )
memset( head, 0, 256 ); /* clear sector data... */
}
gap4_add( d, GAP_TRDOS );
/* now we continue with the data */
for( i = 1; i < d->sides * d->cylinders; i++ ) {
if( trackgen( d, buffer, i % 2, i / 2, 1, 16, 256,
NO_PREINDEX, GAP_TRDOS, INTERLEAVE_2, 0x00 ) )
return d->status = DISK_OPEN;
}
return d->status = DISK_OK;
}
static int
open_td0( buffer_t *buffer, disk_t *d, int preindex )
{
int i, j, s, sectors, seclen, bpt, gap, mfm, mfm_old;
int data_offset, track_offset, sector_offset;
unsigned char *uncomp_buff, *hdrb;
if( buff[0] == 't' ) /* signature "td" -> advanced compression */
return d->status = DISK_IMPL; /* not implemented */
uncomp_buff = NULL; /* we may use this buffer */
mfm_old = buff[5] & 0x80 ? 0 : 1; /* td0notes say: may older teledisk
indicate the SD on high bit of
data rate */
d->sides = buff[9]; /* 1 or 2 */
if( d->sides < 1 || d->sides > 2 )
return d->status = DISK_GEOM;
/* skip comment block if any */
data_offset = track_offset = 12 + ( buff[7] & 0x80 ?
10 + buff[14] + 256 * buff[15] : 0 );
/* determine the greatest track length */
d->bpt = 0;
d->cylinders = 0;
seclen = 0;
while( 1 ) {
buffer->index = track_offset;
if( buffavail( buffer ) < 1 )
return d->status = DISK_OPEN;
if( ( sectors = buff[0] ) == 255 ) /* sector number 255 => end of tracks */
break;
if( buffavail( buffer ) < 4 ) /* check track header is avail. */
return d->status = DISK_OPEN;
if( buff[1] + 1 > d->cylinders ) /* find the biggest cylinder number */
d->cylinders = buff[1] + 1;
sector_offset = track_offset + 4;
mfm = buff[2] & 0x80 ? 0 : 1; /* 0x80 == 1 => SD track */
bpt = postindex_len( d, mfm_old || mfm ? GAP_MINIMAL_FM : GAP_MINIMAL_MFM ) +
( preindex ?
preindex_len( d, mfm_old || mfm ? GAP_MINIMAL_FM : GAP_MINIMAL_MFM ) :
0 ) +
mfm_old || mfm ? 6 : 3;
for( s = 0; s < sectors; s++ ) {
buffer->index = sector_offset;
if( buffavail( buffer ) < 6 ) /* check sector header is avail. */
return d->status = DISK_OPEN;
if( !( buff[4] & 0x30 ) ) { /* only if we have data */
if( buffavail( buffer ) < 9 ) /* check data header is avail. */
return d->status = DISK_OPEN;
bpt += calc_sectorlen( mfm_old || mfm, 0x80 << buff[3],
mfm_old || mfm ? GAP_MINIMAL_FM : GAP_MINIMAL_MFM );
if( buff[3] > seclen )
seclen = buff[3]; /* biggest sector */
sector_offset += buff[6] + 256 * buff[7] - 1;
}
sector_offset += 9;
}
if( bpt > d->bpt )
d->bpt = bpt;
track_offset = sector_offset;
}
if( d->bpt == 0 )
return d->status = DISK_GEOM;
d->density = DISK_DENS_AUTO;
if( disk_alloc( d ) != DISK_OK )
return d->status;
DISK_SET_TRACK_IDX( d, 0 );
buffer->index = data_offset; /* first track header */
while( 1 ) {
if( ( sectors = buff[0] ) == 255 ) /* sector number 255 => end of tracks */
break;
DISK_SET_TRACK( d, ( buff[2] & 0x01 ), buff[1] );
d->i = 0;
/* later teledisk -> if buff[2] & 0x80 -> FM track */
gap = mfm_old || buff[2] & 0x80 ? GAP_MINIMAL_FM : GAP_MINIMAL_MFM;
postindex_add( d, gap );
buffer->index += 4; /* sector header*/
for( s = 0; s < sectors; s++ ) {
hdrb = buff;
buffer->index += 9; /* skip to data */
if( !( hdrb[4] & 0x40 ) ) /* if we have id we add */
id_add( d, hdrb[1], hdrb[0], hdrb[2], hdrb[3], gap,
hdrb[4] & 0x02 ? CRC_ERROR : CRC_OK );
if( hdrb[4] & 0x40 ) { /* if we have _no_ id we drop data... */
buffer->index += hdrb[6] + 256 * hdrb[7] - 1;
continue; /* next sector */
}
if( !( hdrb[4] & 0x30 ) ) { /* only if we have data */
seclen = 0x80 << hdrb[3];
switch( hdrb[8] ) {
case 0: /* raw sector data */
if( hdrb[6] + 256 * hdrb[7] - 1 != seclen ) {
if( uncomp_buff )
free( uncomp_buff );
return d->status = DISK_OPEN;
}
if( data_add( d, buffer, NULL, hdrb[6] + 256 * hdrb[7] - 1,
hdrb[4] & 0x04 ? DDAM : NO_DDAM, gap, CRC_OK, NO_AUTOFILL, NULL ) ) {
if( uncomp_buff )
free( uncomp_buff );
return d->status = DISK_OPEN;
}
break;
case 1: /* Repeated 2-byte pattern */
if( uncomp_buff == NULL && alloc_uncompress_buffer( &uncomp_buff, 8192 ) )
return d->status = DISK_MEM;
for( i = 0; i < seclen; ) { /* fill buffer */
if( buffavail( buffer ) < 13 ) { /* check block header is avail. */
free( uncomp_buff );
return d->status = DISK_OPEN;
}
if( i + 2 * ( hdrb[9] + 256*hdrb[10] ) > seclen ) {
/* too many data bytes */
free( uncomp_buff );
return d->status = DISK_OPEN;
}
/* ab ab ab ab ab ab ab ab ab ab ab ... */
for( j = 1; j < hdrb[9] + 256 * hdrb[10]; j++ )
memcpy( uncomp_buff + i + j * 2, &hdrb[11], 2 );
i += 2 * ( hdrb[9] + 256 * hdrb[10] );
}
if( data_add( d, NULL, uncomp_buff, hdrb[6] + 256 * hdrb[7] - 1,
hdrb[4] & 0x04 ? DDAM : NO_DDAM, gap, CRC_OK, NO_AUTOFILL, NULL ) ) {
free( uncomp_buff );
return d->status = DISK_OPEN;
}
break;
case 2: /* Run Length Encoded data */
if( uncomp_buff == NULL && alloc_uncompress_buffer( &uncomp_buff, 8192 ) )
return d->status = DISK_MEM;
for( i = 0; i < seclen; ) { /* fill buffer */
if( buffavail( buffer ) < 11 ) { /* check block header is avail */
free( uncomp_buff );
return d->status = DISK_OPEN;
}
if( hdrb[9] == 0 ) { /* raw bytes */
if( i + hdrb[10] > seclen || /* too many data bytes */
buffread( uncomp_buff + i, hdrb[10], buffer ) != 1 ) {
free( uncomp_buff );
return d->status = DISK_OPEN;
}
i += hdrb[10];
} else { /* repeated samples */
if( i + 2 * hdrb[9] * hdrb[10] > seclen || /* too many data bytes */
buffread( uncomp_buff + i, 2 * hdrb[9], buffer ) != 1 ) {
free( uncomp_buff );
return d->status = DISK_OPEN;
}
/*
abcdefgh abcdefg abcdefg abcdefg ...
\--v---/
2*hdrb[9]
| | | | |
+- 0 +- 1 +- 2 +- 3 ... +- hdrb[10]-1
*/
for( j = 1; j < hdrb[10]; j++ ) /* repeat 'n' times */
memcpy( uncomp_buff + i + j * 2 * hdrb[9], uncomp_buff + i, 2 * hdrb[9] );
i += 2 * hdrb[9] * hdrb[10];
}
}
if( data_add( d, NULL, uncomp_buff, hdrb[6] + 256 * hdrb[7] - 1,
hdrb[4] & 0x04 ? DDAM : NO_DDAM, gap, CRC_OK, NO_AUTOFILL, NULL ) ) {
free( uncomp_buff );
return d->status = DISK_OPEN;
}
break;
default:
if( uncomp_buff )
free( uncomp_buff );
return d->status = DISK_OPEN;
break;
}
}
}
gap4_add( d, gap );
}
if( uncomp_buff )
free( uncomp_buff );
return d->status = DISK_OK;
}
/* update tracks TLEN */
void
disk_update_tlens( disk_t *d )
{
int i;
for( i = 0; i < d->sides * d->cylinders; i++ ) { /* check tracks */
DISK_SET_TRACK_IDX( d, i );
if( d->track[-3] + 256 * d->track[-2] == 0 ) {
d->track[-3] = d->bpt & 0xff;
d->track[-2] = ( d->bpt >> 8 ) & 0xff;
}
}
}
/* open a disk image file, read and convert to our format
* if preindex != 0 we generate preindex gap if needed
*/
static int
disk_open2( disk_t *d, const char *filename, int preindex )
{
buffer_t buffer;
libspectrum_id_t type;
int error;
#ifdef GEKKO /* Wii doesn't have access() */
d->wrprot = 0;
#else /* #ifdef GEKKO */
if( access( filename, W_OK ) == -1 ) /* file read only */
d->wrprot = 1;
else
d->wrprot = 0;
#endif /* #ifdef GEKKO */
if( utils_read_file( filename, &buffer.file ) )
return d->status = DISK_OPEN;
buffer.index = 0;
error = libspectrum_identify_file_raw( &type, filename,
buffer.file.buffer, buffer.file.length );
if( error ) return d->status = DISK_OPEN;
d->type = DISK_TYPE_NONE;
switch ( type ) {
case LIBSPECTRUM_ID_DISK_UDI:
d->type = DISK_UDI;
open_udi( &buffer, d );
break;
case LIBSPECTRUM_ID_DISK_OPD:
d->type = DISK_OPD;
case LIBSPECTRUM_ID_DISK_MGT:
if( d->type == DISK_TYPE_NONE) d->type = DISK_MGT;
case LIBSPECTRUM_ID_DISK_IMG:
if( d->type == DISK_TYPE_NONE) d->type = DISK_IMG;
open_img_mgt_opd( &buffer, d );
break;
case LIBSPECTRUM_ID_DISK_SAD:
d->type = DISK_SAD;
open_sad( &buffer, d, preindex );
break;
case LIBSPECTRUM_ID_DISK_TRD:
d->type = DISK_TRD;
open_trd( &buffer, d );
break;
case LIBSPECTRUM_ID_DISK_FDI:
d->type = DISK_FDI;
open_fdi( &buffer, d, preindex );
break;
case LIBSPECTRUM_ID_DISK_CPC:
d->type = DISK_CPC;
case LIBSPECTRUM_ID_DISK_ECPC:
if( d->type == DISK_TYPE_NONE) d->type = DISK_ECPC;
open_cpc( &buffer, d, preindex );
break;
case LIBSPECTRUM_ID_DISK_SCL:
d->type = DISK_SCL;
open_scl( &buffer, d );
break;
case LIBSPECTRUM_ID_DISK_TD0:
d->type = DISK_TD0;
open_td0( &buffer, d, preindex );
break;
default:
utils_close_file( &buffer.file );
return d->status = DISK_OPEN;
}
if( d->status != DISK_OK ) {
if( d->data != NULL )
free( d->data );
utils_close_file( &buffer.file );
return d->status;
}
utils_close_file( &buffer.file );
d->dirty = 0;
disk_update_tlens( d );
update_tracks_mode( d );
d->filename = strdup( filename );
return d->status = DISK_OK;
}
/*--------------------- other fuctions -----------------------*/
/* create a two sided disk (d) from two one sided (d1 and d2) */
int
disk_merge_sides( disk_t *d, disk_t *d1, disk_t *d2, int autofill )
{
int i;
int clen;
if( d1->sides != 1 || d2->sides != 1 ||
d1->bpt != d2->bpt ||
( autofill < 0 && d1->cylinders != d2->cylinders ) )
return DISK_GEOM;
d->wrprot = 0;
d->dirty = 0;
d->sides = 2;
d->type = d1->type;
d->cylinders = d2->cylinders > d1->cylinders ? d2->cylinders : d1->cylinders;
d->bpt = d1->bpt;
d->density = DISK_DENS_AUTO;
if( disk_alloc( d ) != DISK_OK )
return d->status;
clen = DISK_CLEN( d->bpt );
d->track = d->data;
d1->track = d1->data;
d2->track = d2->data;
for( i = 0; i < d->cylinders; i++ ) {
if( i < d1->cylinders )
memcpy( d->track, d1->track, d->tlen );
else {
d->track[0] = d->bpt & 0xff;
d->track[1] = ( d->bpt >> 8 ) & 0xff;
d->track[2] = 0x00;
memset( d->track + 3, autofill & 0xff, d->bpt ); /* fill data */
memset( d->track + 3 + d->bpt, 0x00, 3 * clen ); /* no clock and other marks */
}
d->track += d->tlen;
d1->track += d1->tlen;
if( i < d2->cylinders )
memcpy( d->track, d2->track, d->tlen );
else {
d->track[0] = d->bpt & 0xff;
d->track[1] = ( d->bpt >> 8 ) & 0xff;
d->track[2] = 0x00;
memset( d->track + 1, autofill & 0xff, d->bpt ); /* fill data */
memset( d->track + 1 + d->bpt, 0x00, 3 * clen ); /* no clock and other marks */
}
d->track += d->tlen;
d2->track += d2->tlen;
}
disk_close( d1 );
disk_close( d2 );
return d->status = DISK_OK;
}
int
disk_open( disk_t *d, const char *filename, int preindex, int merge_disks )
{
char *filename2;
char c = ' ';
int l, g = 0, pos = 0;
disk_t d1, d2;
d->filename = NULL;
if( filename == NULL || *filename == '\0' )
return d->status = DISK_OPEN;
l = strlen( filename );
if( !merge_disks || l < 7 ) /* if we do not want to open two separated disk image as one double sided disk */
return disk_open2( d, filename, preindex );
filename2 = (char *)filename + ( l - 1 );
while( l ) { /* [Ss]ide[ _][abAB12][ _.] */
if( g == 0 && ( *filename2 == '.' || *filename2 == '_' ||
*filename2 == ' ' ) ) {
g++;
} else if( g == 1 && ( *filename2 == '1' || *filename2 == 'a' ||
*filename2 == 'A' ) ) {
g++;
pos = filename2 - filename;
c = *filename2 + 1; /* 1->2, a->b, A->B */
} else if( g == 1 && ( *filename2 == '2' || *filename2 == 'b' ||
*filename2 == 'B' ) ) {
g++;
pos = filename2 - filename;
c = *filename2 - 1; /* 2->1, b->a, B->A */
} else if( g == 2 && ( *filename2 == '_' || *filename2 == ' ' ) ) {
g++;
} else if( g == 3 && l >= 5 && ( !memcmp( filename2 - 3, "Side", 4 ) ||
!memcmp( filename2 - 3, "side", 4 ) ) ) {
g++;
break;
} else {
g = 0;
}
l--;
filename2--;
}
if( g != 4 )
return d->status = disk_open2( d, filename, preindex );
d1.data = NULL; d1.flag = d->flag;
d2.data = NULL; d2.flag = d->flag;
filename2 = strdup( filename );
*(filename2 + pos) = c;
if( filename2 == NULL ) {
fprintf( stderr, "out of memory in merge disk files\n" );
return d->status = DISK_OPEN;
}
if( settings_current.disk_ask_merge &&
!ui_query( "Try to merge 'B' side of this disk?" ) ) {
free( filename2 );
return d->status = disk_open2( d, filename, preindex );
}
if( disk_open2( &d2, filename2, preindex ) ) {
return d->status = disk_open2( d, filename, preindex );
}
if( disk_open2( &d1, filename, preindex ) )
return d->status = d1.status;
if( disk_merge_sides( d, &d1, &d2, 0x00 ) ) {
disk_close( &d2 );
*d = d1;
}
/* fprintf( stderr, "`%s' and `%s' merged\n", filename, filename2 ); */
free( filename2 );
return d->status;
}
/*--------------------- start of write section ----------------*/
static int
write_udi( FILE *file, disk_t *d )
{
int i, j;
size_t len;
libspectrum_dword crc;
udi_pack_tracks( d );
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
udi_compress_tracks( d );
#endif /* #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
crc = ~( libspectrum_dword ) 0;
len = 16;
for( i = 0; i < d->sides * d->cylinders; i++ ) { /* check tracks */
DISK_SET_TRACK_IDX( d, i );
if( d->track[-1] == 0xf0 )
len += 7 + d->track[-3] + 256 * d->track[-2];
else
len += 3 + UDI_TLEN( d->track[-1], d->track[-3] + 256 * d->track[-2] );
}
head[0] = 'U';
head[1] = 'D';
head[2] = 'I';
head[3] = '!';
head[4] = len & 0xff;
head[5] = ( len >> 8 ) & 0xff;
head[6] = ( len >> 16 ) & 0xff;
head[7] = ( len >> 24 ) & 0xff;
head[8] = 0x00;
head[9] = d->cylinders - 1;
head[10] = d->sides - 1;
head[11] = head[12] = head[13] = head[14] = head[15] = 0;
if( fwrite( head, 16, 1, file ) != 1 )
return d->status = DISK_WRPART;
for( j = 0; j < 16; j++ )
crc = crc_udi( crc, head[j] );
for( i = 0; i < d->sides * d->cylinders; i++ ) { /* write tracks */
DISK_SET_TRACK_IDX( d, i );
head[0] = d->track[-1]; /* track type */
head[1] = d->track[-3]; /* track len */
head[2] = d->track[-2]; /* track len2 */
if( fwrite( head, 3, 1, file ) != 1 )
return d->status = DISK_WRPART;
for( j = 0; j < 3; j++ )
crc = crc_udi( crc, head[j] );
if( d->track[-1] == 0xf0 )
len = 4 + d->track[-3] + 256 * d->track[-2];
else
len = UDI_TLEN( d->track[-1], d->track[-3] + 256 * d->track[-2] );
if( fwrite( d->track, len, 1, file ) != 1 )
return d->status = DISK_WRPART;
for( j = len; j > 0; j-- ) {
crc = crc_udi( crc, *d->track );
d->track++;
}
}
head[0] = crc & 0xff;
head[1] = ( crc >> 8 ) & 0xff;
head[2] = ( crc >> 16 ) & 0xff;
head[3] = ( crc >> 24 ) & 0xff;
if( fwrite( head, 4, 1, file ) != 1 ) /* CRC */
fclose( file );
udi_unpack_tracks( d );
return d->status = DISK_OK;
}
static int
write_img_mgt_opd( FILE *file, disk_t *d )
{
int i, j, sbase, sectors, seclen, mfm, cyl;
if( check_disk_geom( d, &sbase, &sectors, &seclen, &mfm, &cyl ) ||
( d->type != DISK_OPD && ( sbase != 1 || seclen != 2 || sectors != 10 ) ) ||
( d->type == DISK_OPD && ( sbase != 0 || seclen != 1 || sectors != 18 ) ) )
return d->status = DISK_GEOM;
if( cyl == -1 ) cyl = d->cylinders;
if( cyl != 40 && cyl != 80 )
return d->status = DISK_GEOM;
if( d->type == DISK_IMG ) { /* out-out */
for( j = 0; j < d->sides; j++ ) {
for( i = 0; i < cyl; i++ ) {
if( savetrack( d, file, j, i, 1, sectors, seclen ) )
return d->status = DISK_GEOM;
}
}
} else { /* alt */
for( i = 0; i < cyl; i++ ) { /* MGT */
for( j = 0; j < d->sides; j++ ) {
if( savetrack( d, file, j, i, d->type == DISK_MGT ? 1 : 0,
sectors, seclen ) )
return d->status = DISK_GEOM;
}
}
}
return d->status = DISK_OK;
}
static int
write_trd( FILE *file, disk_t *d )
{
int i, j, sbase, sectors, seclen, mfm, cyl;
if( check_disk_geom( d, &sbase, &sectors, &seclen, &mfm, &cyl ) ||
sbase != 1 || seclen != 1 || sectors != 16 )
return d->status = DISK_GEOM;
if( cyl == -1 ) cyl = d->cylinders;
for( i = 0; i < cyl; i++ ) {
for( j = 0; j < d->sides; j++ ) {
if( savetrack( d, file, j, i, 1, sectors, seclen ) )
return d->status = DISK_GEOM;
}
}
return d->status = DISK_OK;
}
static int
write_sad( FILE *file, disk_t *d )
{
int i, j, sbase, sectors, seclen, mfm, cyl;
if( check_disk_geom( d, &sbase, &sectors, &seclen, &mfm, &cyl ) || sbase != 1 )
return d->status = DISK_GEOM;
if( cyl == -1 ) cyl = d->cylinders;
memcpy( head, "Aley's disk backup", 18 );
head[18] = d->sides;
head[19] = cyl;
head[20] = sectors;
head[21] = seclen * 4;
if( fwrite( head, 22, 1, file ) != 1 ) /* SAD head */
return d->status = DISK_WRPART;
for( j = 0; j < d->sides; j++ ) { /* OUT-OUT */
for( i = 0; i < cyl; i++ ) {
if( savetrack( d, file, j, i, 1, sectors, seclen ) )
return d->status = DISK_GEOM;
}
}
return d->status = DISK_OK;
}
static int
write_fdi( FILE *file, disk_t *d )
{
int i, j, k, sbase, sectors, seclen, mfm, del;
int h, t, s, b;
int toff, soff;
memset( head, 0, 14 );
memcpy( head, "FDI", 3 );
head[0x03] = d->wrprot = 1;
head[0x04] = d->cylinders & 0xff;
head[0x05] = d->cylinders >> 8;
head[0x06] = d->sides & 0xff;
head[0x07] = d->sides >> 8;
sectors = 0;
for( j = 0; j < d->cylinders; j++ ) { /* count sectors */
for( i = 0; i < d->sides; i++ ) {
guess_track_geom( d, i, j, &sbase, &s, &seclen, &mfm );
sectors += s;
}
}
h = ( sectors + d->cylinders * d->sides ) * 7; /* track header len */
head[0x08] = ( h + 0x0e ) & 0xff; /* description offset */
head[0x09] = ( h + 0x0e ) >> 8; /* "http://fuse-emulator.sourceforge.net" */
head[0x0a] = ( h + 0x33 ) & 0xff; /* data offset */
head[0x0b] = ( h + 0x33 ) >> 8;
if( fwrite( head, 14, 1, file ) != 1 ) /* FDI head */
return d->status = DISK_WRPART;
/* write track headers */
toff = 0; /* offset of track data */
for( i = 0; i < d->cylinders; i++ ) {
for( j = 0; j < d->sides; j++ ) {
DISK_SET_TRACK( d, j, i );
d->i = 0;
head[0x00] = toff & 0xff;
head[0x01] = ( toff >> 8 ) & 0xff; /* track offset */
head[0x02] = ( toff >> 16 ) & 0xff;
head[0x03] = ( toff >> 24 ) & 0xff;
head[0x04] = 0;
head[0x05] = 0;
guess_track_geom( d, j, i, &sbase, &sectors, &seclen, &mfm );
head[0x06] = sectors;
if( fwrite( head, 7, 1, file ) != 1 ) /* track header */
return d->status = DISK_WRPART;
DISK_SET_TRACK( d, j, i );
d->i = 0;
k = 0;
soff = 0;
while( sectors > 0 ) {
while( k < 35 && id_read( d, &h, &t, &s, &b ) ) {
head[ 0x00 + k * 7 ] = t;
head[ 0x01 + k * 7 ] = h;
head[ 0x02 + k * 7 ] = s;
head[ 0x03 + k * 7 ] = b;
head[ 0x05 + k * 7 ] = soff & 0xff;
head[ 0x06 + k * 7 ] = ( soff >> 8 ) & 0xff;
if( !datamark_read( d, &del ) ) {
head[ 0x04 + k * 7 ] = 0; /* corrupt sector data */
} else {
head[ 0x04 + k * 7 ] = 1 << b | ( del ? 0x80 : 0x00 );
soff += 0x80 << b;
}
k++;
} /* Sector header */
if( fwrite( head, 7 * k, 1, file ) != 1 )
return d->status = DISK_WRPART;
sectors -= k;
k = 0;
}
toff += soff;
}
}
if( fwrite( "http://fuse-emulator.sourceforge.net", 37, 1, file ) != 1 )
return d->status = DISK_WRPART;
/* write data */
for( i = 0; i < d->cylinders; i++ ) {
for( j = 0; j < d->sides; j++ ) {
if( saverawtrack( d, file, j, i ) )
return d->status = DISK_WRPART;
}
}
return d->status = DISK_OK;
}
static int
write_cpc( FILE *file, disk_t *d )
{
int i, j, k, sbase, sectors, seclen, mfm, cyl;
int h, t, s, b;
size_t len;
i = check_disk_geom( d, &sbase, &sectors, &seclen, &mfm, &cyl );
if( i & DISK_SECLEN_VARI || i & DISK_SPT_VARI || i & DISK_WEAK_DATA )
return d->status = DISK_GEOM;
if( i & DISK_MFM_VARI )
mfm = -1;
if( cyl == -1 ) cyl = d->cylinders;
memset( head, 0, 256 );
memcpy( head, "MV - CPCEMU Disk-File\r\nDisk-Info\r\n", 34 );
head[0x30] = cyl;
head[0x31] = d->sides;
len = sectors * ( 0x80 << seclen ) + 256;
head[0x32] = len & 0xff;
head[0x33] = len >> 8;
if( fwrite( head, 256, 1, file ) != 1 ) /* CPC head */
return d->status = DISK_WRPART;
memset( head, 0, 256 );
memcpy( head, "Track-Info\r\n", 12 );
for( i = 0; i < cyl; i++ ) {
for( j = 0; j < d->sides; j++ ) {
DISK_SET_TRACK( d, j, i );
d->i = 0;
head[0x10] = i;
head[0x11] = j;
head[0x14] = seclen;
head[0x15] = sectors;
if( mfm != -1 )
head[0x16] = mfm ? 0x4e : 0xff;
head[0x17] = 0xe5;
k = 0;
while( id_read( d, &h, &t, &s, &b ) ) {
head[ 0x18 + k * 8 ] = t;
head[ 0x19 + k * 8 ] = h;
head[ 0x1a + k * 8 ] = s;
head[ 0x1b + k * 8 ] = b;
if( k == 0 && mfm == -1 ) { /* if mixed MFM/FM tracks */
head[0x16] = d->track[ d->i ] == 0x4e ? 0x4e : 0xff;
}
k++;
}
if( fwrite( head, 256, 1, file ) != 1 ) /* Track head */
return d->status = DISK_WRPART;
if( saverawtrack( d, file, j, i ) )
return d->status = DISK_WRPART;
}
}
return d->status = DISK_OK;
}
static int
write_scl( FILE *file, disk_t *d )
{
int i, j, k, l, t, s, sbase, sectors, seclen, mfm, del, cyl;
int entries;
libspectrum_dword sum = 597; /* sum of "SINCLAIR" */
if( check_disk_geom( d, &sbase, &sectors, &seclen, &mfm, &cyl ) ||
sbase != 1 || seclen != 1 || sectors != 16 )
return d->status = DISK_GEOM;
DISK_SET_TRACK_IDX( d, 0 );
/* TR-DOS descriptor */
if( !id_seek( d, 9 ) || !datamark_read( d, &del ) )
return d->status = DISK_GEOM;
entries = head[8] = d->track[ d->i + 228 ]; /* number of files */
if( entries > 128 || d->track[ d->i + 231 ] != 0x10 ||
( d->track[ d->i + 227 ] != 0x16 && d->track[ d->i + 227 ] != 0x17 &&
d->track[ d->i + 227 ] != 0x18 && d->track[ d->i + 227 ] != 0x19 ) ||
d->track[ d->i ] != 0 )
return d->status = DISK_GEOM;
memcpy( head, "SINCLAIR", 8 );
sum += entries;
if( fwrite( head, 9, 1, file ) != 1 )
return d->status = DISK_WRPART;
/* save SCL entries */
j = 1; /* sector number */
k = 0;
sectors = 0;
for( i = 0; i < entries; i++ ) { /* read TR-DOS dir */
if( j > 8 ) /* TR-DOS dir max 8 sector len */
return d->status = DISK_GEOM;
if( k == 0 && ( !id_seek( d, j ) || !datamark_read( d, &del ) ) )
return d->status = DISK_GEOM;
if( fwrite( d->track + d->i + k, 14, 1, file ) != 1 )
return d->status = DISK_WRPART;
sectors += d->track[ d->i + k + 13 ]; /* file length in sectors */
for( s = 0; s < 14; s++ ) {
sum += d->track[ d->i + k ];
k++;
}
k += 2;
if( k >= 256 ) { /* end of sector */
j++;
k = 0;
}
}
/* save data */
/* we have to 'defragment' the disk :) */
j = 1; /* sector number */
k = 0; /* byte offset */
for( i = 0; i < entries; i++ ) { /* read TR-DOS dir */
DISK_SET_TRACK_IDX( d, 0 );
if( k == 0 ) {
if ( !id_seek( d, j ) || !datamark_read( d, &del ) )
return d->status = DISK_GEOM;
memcpy( head, d->track + d->i, 256 );
}
s = head[ k + 14 ]; /* starting sector */
t = head[ k + 15 ]; /* starting track */
sectors = head[ k + 13 ] + s; /* last sector */
k += 16;
if( k == 256 ) {
k = 0;
j++;
}
if( t >= d->sides * d->cylinders )
return d->status = DISK_GEOM;
if( s % 16 == 0 )
t--;
DISK_SET_TRACK_IDX( d, t );
for( ; s < sectors; s++ ) { /* save all sectors */
if( s % 16 == 0 ) {
t++;
if( t >= d->sides * d->cylinders )
return d->status = DISK_GEOM;
DISK_SET_TRACK_IDX( d, t );
}
if( id_seek( d, s % 16 + 1 ) ) {
if( datamark_read( d, &del ) ) { /* write data if we have data */
if( data_write_file( d, file, 1 ) ) {
return d->status = DISK_GEOM;
} else {
for( l = 0; l < 256; l++ )
sum += d->track[ d->i + l ];
}
}
} else {
return DISK_GEOM;
}
}
}
head[0] = sum & 0xff;
head[1] = ( sum >> 8 ) & 0xff;
head[2] = ( sum >> 16 ) & 0xff;
head[3] = ( sum >> 24 ) & 0xff;
if( fwrite( head, 4, 1, file ) != 1 )
return d->status = DISK_WRPART;
return d->status = DISK_OK;
}
static int
write_log( FILE *file, disk_t *d )
{
int i, j, k, del, rev;
int h, t, s, b;
char str[17];
str[16] = '\0';
fprintf( file, "DISK tracks log!\n" );
fprintf( file, "Sides: %d, cylinders: %d\n", d->sides, d->cylinders );
for( j = 0; j < d->cylinders; j++ ) { /* ALT :) */
for( i = 0; i < d->sides; i++ ) {
DISK_SET_TRACK( d, i, j );
d->i = 0;
fprintf( file, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
i, j, d->track[-1], d->track[-3] + 256 * d->track[-2] );
while( id_read( d, &h, &t, &s, &b ) ) {
fprintf( file, " h:%d t:%d s:%d l:%d(%d)", h, t, s, b, 0x80 << b );
if( datamark_read( d, &del ) )
fprintf( file, " %s\n", del ? "deleted" : "normal" );
else
fprintf( file, " noDAM\n" );
}
}
}
fprintf( file, "\n***************************\nSector Data Dump:\n" );
for( j = 0; j < d->cylinders; j++ ) { /* ALT :) */
for( i = 0; i < d->sides; i++ ) {
DISK_SET_TRACK( d, i, j );
d->i = 0;
fprintf( file, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
i, j, d->track[-1], d->track[-3] + 256 * d->track[-2] );
rev = k = 0;
while( id_read( d, &h, &t, &s, &b ) ) {
b = 0x80 << b;
if( datamark_read( d, &del ) )
fprintf( file, " h:%d t:%d s:%d l:%d (%s)\n", h, t, s, b,
del ? "deleted" : "normal" );
else
fprintf( file, " h:%d t:%d s:%d l:%d (missing data)\n", h,
t, s, b );
k = 0;
while( k < b ) {
if( !( k % 16 ) )
fprintf( file, "0x%08x:", k );
fprintf( file, " 0x%02x", d->track[ d->i ] );
str[ k & 0x0f ] = d->track[ d->i ] >= 32 &&
d->track[ d->i ] < 127 ? d->track[ d->i ] : '.';
k++;
if( !( k % 16 ) )
fprintf( file, " | %s\n", str );
d->i++;
if( d->i >= d->bpt ) {
d->i = 0;
rev++;
if( rev == 6 )
break;
}
}
}
}
}
fprintf( file, "\n***************************\n**Full Dump:\n" );
for( j = 0; j < d->cylinders; j++ ) { /* ALT :) */
for( i = 0; i < d->sides; i++ ) {
DISK_SET_TRACK( d, i, j );
d->i = 0;
fprintf( file, "\n*********\nSide: %d, cylinder: %d type: 0x%02x tlen: %5u\n",
i, j, d->track[-1], d->track[-3] + 256 * d->track[-2] );
k = 0;
while( d->i < d->bpt ) {
if( !( k % 8 ) )
fprintf( file, "0x%08x:", d->i );
fprintf( file, " 0x%04x", d->track[ d->i ] |
( bitmap_test( d->clocks, d->i ) ? 0x0c00 : 0x0000 ) |
( bitmap_test( d->fm, d->i ) ? 0x1000 : 0x0000 ) |
( bitmap_test( d->weak, d->i ) ? 0x8000 : 0x0000 ) );
k++;
if( !( k % 8 ) )
fprintf( file, "\n" );
d->i++;
}
}
}
return d->status = DISK_OK;
}
int
disk_write( disk_t *d, const char *filename )
{
FILE *file;
const char *ext;
size_t namelen;
if( ( file = fopen( filename, "wb" ) ) == NULL )
return d->status = DISK_WRFILE;
namelen = strlen( filename );
if( namelen < 4 )
ext = NULL;
else
ext = filename + namelen - 4;
if( d->type == DISK_TYPE_NONE ) {
if( !strcasecmp( ext, ".udi" ) )
d->type = DISK_UDI; /* ALT side */
else if( !strcasecmp( ext, ".dsk" ) )
d->type = DISK_CPC; /* ALT side */
else if( !strcasecmp( ext, ".mgt" ) )
d->type = DISK_MGT; /* ALT side */
else if( !strcasecmp( ext, ".opd" ) || !strcasecmp( ext, ".opu" ) )
d->type = DISK_OPD; /* ALT side */
else if( !strcasecmp( ext, ".img" ) ) /* out-out */
d->type = DISK_IMG;
else if( !strcasecmp( ext, ".trd" ) ) /* ALT */
d->type = DISK_TRD;
else if( !strcasecmp( ext, ".sad" ) ) /* ALT */
d->type = DISK_SAD;
else if( !strcasecmp( ext, ".fdi" ) ) /* ALT */
d->type = DISK_FDI;
else if( !strcasecmp( ext, ".scl" ) ) /* not really a disk image */
d->type = DISK_SCL;
else if( !strcasecmp( ext, ".log" ) ) /* ALT */
d->type = DISK_LOG;
else
d->type = DISK_UDI; /* ALT side */
}
update_tracks_mode( d );
switch( d->type ) {
case DISK_UDI:
write_udi( file, d );
break;
case DISK_IMG:
case DISK_MGT:
case DISK_OPD:
write_img_mgt_opd( file, d );
break;
case DISK_TRD:
write_trd( file, d );
break;
case DISK_SAD:
write_sad( file, d );
break;
case DISK_FDI:
write_fdi( file, d );
break;
case DISK_SCL:
write_scl( file, d );
break;
case DISK_CPC:
write_cpc( file, d );
break;
case DISK_LOG:
write_log( file, d );
break;
default:
return d->status = DISK_WRFILE;
break;
}
if( d->status != DISK_OK ) {
fclose( file );
return d->status;
}
if( fclose( file ) == -1 )
return d->status = DISK_WRFILE;
return d->status = DISK_OK;
}