mirror of
https://git.code.sf.net/p/fuse-emulator/fuse
synced 2026-01-27 01:41:34 +03:00
2946 lines
86 KiB
C
2946 lines
86 KiB
C
/* disk.c: Routines for handling disk images
|
|
Copyright (c) 2007-2017 Gergely Szasz
|
|
|
|
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>
|
|
#ifdef HAVE_STRINGS_STRCASECMP
|
|
#include <strings.h>
|
|
#endif /* #ifdef HAVE_STRINGS_STRCASECMP */
|
|
#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 "trdos.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 * const 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 void
|
|
position_context_save( const disk_t *d, disk_position_context_t *c )
|
|
{
|
|
c->track = d->track;
|
|
c->clocks = d->clocks;
|
|
c->fm = d->fm;
|
|
c->weak = d->weak;
|
|
c->i = d->i;
|
|
}
|
|
|
|
static void
|
|
position_context_restore( disk_t *d, const disk_position_context_t *c )
|
|
{
|
|
d->track = c->track;
|
|
d->clocks = c->clocks;
|
|
d->fm = c->fm;
|
|
d->weak = c->weak;
|
|
d->i = c->i;
|
|
}
|
|
|
|
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 = 0;
|
|
*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 ) {
|
|
libspectrum_free( d->data );
|
|
d->data = NULL;
|
|
}
|
|
if( d->filename != NULL ) {
|
|
libspectrum_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( dlen == 0 ) return d->status = DISK_GEOM;
|
|
|
|
d->data = libspectrum_new0( libspectrum_byte, dlen );
|
|
|
|
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 = 1;
|
|
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 = libspectrum_new0( unsigned char, length );
|
|
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( d->sides == 2 ) {
|
|
if( trackgen( d, &buffer, 1, 0, 0xff, 1, 128,
|
|
NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff ) )
|
|
return DISK_GEOM;
|
|
|
|
if( trackgen( d, &buffer, 1, 2, 0xfe, 1, 128,
|
|
NO_PREINDEX, GAP_MINIMAL_MFM, NO_INTERLEAVE, 0xff ) )
|
|
return DISK_GEOM;
|
|
}
|
|
|
|
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 ) return error;
|
|
|
|
if( *data_size < uncompr_size ) {
|
|
*data = libspectrum_renew( libspectrum_byte, *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_renew( libspectrum_byte, *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_d40_d80( buffer_t *buffer, disk_t *d )
|
|
{
|
|
int i, j, sectors, seclen;
|
|
|
|
if( buffavail( buffer ) < 180 )
|
|
return d->status = DISK_OPEN;
|
|
|
|
/* guess geometry of disk */
|
|
d->sides = buff[0xb1] & 0x10 ? 2 : 1;
|
|
d->cylinders = buff[0xb2];
|
|
sectors = buff[0xb3];
|
|
|
|
if( d->sides < 1 || d->sides > 2 || d->cylinders > 83 || sectors > 127 )
|
|
return d->status = DISK_GEOM;
|
|
|
|
seclen = 512;
|
|
|
|
buffer->index = 0;
|
|
|
|
/* create a DD disk */
|
|
d->density = DISK_DD;
|
|
if( disk_alloc( d ) != DISK_OK )
|
|
return d->status;
|
|
|
|
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_MGT_PLUSD, NO_INTERLEAVE, 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;
|
|
}
|
|
|
|
/* 1 RANDOMIZE USR 15619: REM : RUN " " */
|
|
static libspectrum_byte beta128_boot_loader[] = {
|
|
0x00, 0x01, 0x1c, 0x00, 0xf9, 0xc0, 0x31, 0x35, 0x36, 0x31, 0x39, 0x0e,
|
|
0x00, 0x00, 0x03, 0x3d, 0x00, 0x3a, 0xea, 0x3a, 0xf7, 0x22, 0x20, 0x20,
|
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x0d,
|
|
};
|
|
|
|
static int
|
|
trdos_insert_basic_file( disk_t *d, trdos_spec_t *spec,
|
|
const libspectrum_byte *data, unsigned int size )
|
|
{
|
|
unsigned int fat_sector, fat_entry, n_sec, n_bytes, n_copied;
|
|
int i, t, s, slen, len_pre_dam, len_pre_data;
|
|
disk_gap_t *g = &gaps[ GAP_TRDOS ];
|
|
trdos_dirent_t entry;
|
|
libspectrum_byte trailing_data[] = { 0x80, 0xaa, 0x01, 0x00 }; /* line 1 */
|
|
|
|
/* Check free FAT entries (we don't purge deleted files) */
|
|
if( spec->file_count >= 128 )
|
|
return DISK_UNSUP;
|
|
|
|
/* Check free sectors */
|
|
n_sec = ( size + ARRAY_SIZE( trailing_data ) + 255 ) / 256;
|
|
if( spec->free_sectors < n_sec )
|
|
return DISK_UNSUP;
|
|
|
|
/* Calculate sector raw length */
|
|
slen = calc_sectorlen( ( d->density != DISK_SD && d->density != DISK_8_SD ),
|
|
256, GAP_TRDOS );
|
|
|
|
/* Calculate initial gap before data in a sector */
|
|
len_pre_dam = 0;
|
|
len_pre_dam += g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 7; /* ID */
|
|
len_pre_dam += g->len[2]; /* GAP II */
|
|
|
|
len_pre_data = len_pre_dam;
|
|
len_pre_data += g->sync_len + ( g->mark >= 0 ? 3 : 0 ) + 1; /* DAM */
|
|
|
|
/* Write file data */
|
|
n_copied = 0;
|
|
s = spec->first_free_sector;
|
|
t = spec->first_free_track;
|
|
DISK_SET_TRACK_IDX( d, t );
|
|
|
|
for( i = 0; i < n_sec; i++ ) {
|
|
memset( head, 0, 256 );
|
|
n_bytes = 0;
|
|
|
|
/* Copy chunk of file body */
|
|
if( n_copied < size ) {
|
|
n_bytes = ( size - n_copied > 256 )? 256 : size - n_copied;
|
|
memcpy( head, data + n_copied, n_bytes );
|
|
n_copied += n_bytes;
|
|
}
|
|
|
|
/* Copy trailing parameters */
|
|
if( n_copied >= size ) {
|
|
while( n_copied - size < ARRAY_SIZE( trailing_data ) && n_bytes < 256 ) {
|
|
head[ n_bytes ] = trailing_data[ n_copied - size ];
|
|
n_copied++;
|
|
n_bytes++;
|
|
}
|
|
}
|
|
|
|
/* Write buffer to disk */
|
|
d->i = g->len[1] + ( s % 8 * 2 + s / 8 ) * slen; /* 1 9 2 10 3 ... */
|
|
d->i += len_pre_dam;
|
|
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL,
|
|
NULL );
|
|
|
|
/* Next sector */
|
|
s = ( s + 1 ) % 16;
|
|
|
|
/* Next track */
|
|
if( s == 0 ) {
|
|
t = t + 1;
|
|
if( t >= d->cylinders ) return DISK_UNSUP;
|
|
DISK_SET_TRACK_IDX( d, t );
|
|
}
|
|
}
|
|
|
|
/* Write FAT entry */
|
|
memcpy( entry.filename, "boot ", 8 );
|
|
entry.file_extension = 'B';
|
|
entry.param1 = size; /* assumes variables = 0 */
|
|
entry.param2 = size;
|
|
entry.file_length = n_sec;
|
|
entry.start_sector = spec->first_free_sector;
|
|
entry.start_track = spec->first_free_track;
|
|
|
|
/* Copy sector to buffer, modify and write back to disk recalculating CRCs */
|
|
DISK_SET_TRACK_IDX( d, 0 );
|
|
fat_sector = spec->file_count / 16;
|
|
d->i = g->len[1] + ( ( fat_sector ) % 8 * 2 + ( fat_sector ) / 8 ) * slen;
|
|
memcpy( head, d->track + d->i + len_pre_data, 256 );
|
|
|
|
fat_entry = spec->file_count % 16;
|
|
trdos_write_dirent( head + fat_entry * 16, &entry );
|
|
|
|
d->i += len_pre_dam;
|
|
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL, NULL );
|
|
|
|
/* Write specification sector */
|
|
spec->file_count += 1;
|
|
spec->free_sectors -= n_sec;
|
|
spec->first_free_sector = s;
|
|
spec->first_free_track = t;
|
|
trdos_write_spec( head, spec );
|
|
|
|
d->i = g->len[1] + slen + len_pre_dam; /* sector-9: 1 9 2 10 3 ... */
|
|
data_add( d, NULL, head, 256, NO_DDAM, GAP_TRDOS, CRC_OK, NO_AUTOFILL, NULL );
|
|
|
|
return DISK_OK;
|
|
}
|
|
|
|
static void
|
|
trdos_insert_boot_loader( disk_t *d )
|
|
{
|
|
trdos_spec_t spec;
|
|
trdos_boot_info_t info;
|
|
int slen, del;
|
|
|
|
/* TR-DOS specification sector */
|
|
DISK_SET_TRACK_IDX( d, 0 );
|
|
if( !id_seek( d, 9 ) || !datamark_read( d, &del ) )
|
|
return;
|
|
|
|
if( trdos_read_spec( &spec, d->track + d->i ) )
|
|
return;
|
|
|
|
/* Check free FAT entries (we don't purge deleted files) */
|
|
if( spec.file_count >= 128 )
|
|
return;
|
|
|
|
/* Check there is at least one free sector */
|
|
if( spec.free_sectors == 0 )
|
|
return;
|
|
/* TODO: stealth mode? some boot loaders hide between sectors 10-16 */
|
|
|
|
/* Calculate sector raw length */
|
|
slen = calc_sectorlen( ( d->density != DISK_SD && d->density != DISK_8_SD ),
|
|
256, GAP_TRDOS );
|
|
|
|
/* Read FAT entries */
|
|
if( !id_seek( d, 1 ) || !datamark_read( d, &del ) )
|
|
return;
|
|
|
|
if( trdos_read_fat( &info, d->track + d->i, slen ) )
|
|
return;
|
|
|
|
/* Check actual boot file (nothing to do) */
|
|
if( info.have_boot_file )
|
|
return;
|
|
|
|
/* Insert a simple boot loader that runs the first program */
|
|
if( info.basic_files_count >= 1 ) {
|
|
memcpy( beta128_boot_loader + 22, info.first_basic_file, 8 );
|
|
|
|
trdos_insert_basic_file( d, &spec, beta128_boot_loader,
|
|
ARRAY_SIZE( beta128_boot_loader ) );
|
|
}
|
|
|
|
/* TODO: use also a boot loader that can handle multiple basic pograms */
|
|
}
|
|
|
|
static int
|
|
open_trd( buffer_t *buffer, disk_t *d )
|
|
{
|
|
int i, j, sectors, seclen;
|
|
disk_position_context_t context;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if( settings_current.auto_load ) {
|
|
position_context_save( d, &context );
|
|
trdos_insert_boot_loader( d );
|
|
position_context_restore( d, &context );
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
if( first == -1 || last == -1 ) {
|
|
return;
|
|
}
|
|
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++ ) {
|
|
/* ignore Sector Offset block */
|
|
if( buffavail( buffer ) >= 13 && !memcmp( buff, "Offset-Info\r\n", 13 ) ) {
|
|
buffer->index = buffer->file.length;
|
|
}
|
|
|
|
/* 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;
|
|
disk_position_context_t context;
|
|
|
|
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;
|
|
}
|
|
|
|
if( settings_current.auto_load ) {
|
|
position_context_save( d, &context );
|
|
trdos_insert_boot_loader( d );
|
|
position_context_restore( d, &context );
|
|
}
|
|
|
|
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 )
|
|
libspectrum_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 )
|
|
libspectrum_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. */
|
|
libspectrum_free( uncomp_buff );
|
|
return d->status = DISK_OPEN;
|
|
}
|
|
if( i + 2 * ( hdrb[9] + 256*hdrb[10] ) > seclen ) {
|
|
/* too many data bytes */
|
|
libspectrum_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 ) ) {
|
|
libspectrum_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 */
|
|
libspectrum_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 ) {
|
|
libspectrum_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 ) {
|
|
libspectrum_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 ) ) {
|
|
libspectrum_free( uncomp_buff );
|
|
return d->status = DISK_OPEN;
|
|
}
|
|
break;
|
|
default:
|
|
if( uncomp_buff )
|
|
libspectrum_free( uncomp_buff );
|
|
return d->status = DISK_OPEN;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
gap4_add( d, gap );
|
|
}
|
|
|
|
if( uncomp_buff )
|
|
libspectrum_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;
|
|
case LIBSPECTRUM_ID_DISK_D80:
|
|
d->type = DISK_D80;
|
|
open_d40_d80( &buffer, d );
|
|
break;
|
|
default:
|
|
utils_close_file( &buffer.file );
|
|
return d->status = DISK_OPEN;
|
|
}
|
|
if( d->status != DISK_OK ) {
|
|
if( d->data != NULL )
|
|
libspectrum_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 = utils_safe_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 = utils_safe_strdup( filename );
|
|
*(filename2 + pos) = c;
|
|
|
|
if( settings_current.disk_ask_merge &&
|
|
!ui_query( "Try to merge 'B' side of this disk?" ) ) {
|
|
libspectrum_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 ); */
|
|
libspectrum_free( filename2 );
|
|
return d->status;
|
|
}
|
|
|
|
/*--------------------- start of write section ----------------*/
|
|
|
|
static int
|
|
write_udi( FILE *file, disk_t *d )
|
|
{
|
|
int i, j, error;
|
|
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 );
|
|
|
|
#ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION
|
|
/* Keep tracks uncompressed in memory */
|
|
error = udi_uncompress_tracks( d );
|
|
if( error ) return error;
|
|
#endif /* #ifdef LIBSPECTRUM_SUPPORTS_ZLIB_COMPRESSION */
|
|
|
|
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, §ors, &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_d40_d80( FILE *file, disk_t *d )
|
|
{
|
|
int i, j, sbase, sectors, seclen, mfm, cyl;
|
|
|
|
if( check_disk_geom( d, &sbase, §ors, &seclen, &mfm, &cyl ) ||
|
|
( sbase != 1 ) )
|
|
return d->status = DISK_GEOM;
|
|
|
|
if( cyl == -1 ) cyl = d->cylinders;
|
|
if( ( d->type == DISK_D40 && cyl > 43 ) ||
|
|
( d->type == DISK_D80 && cyl > 83 ) )
|
|
return d->status = DISK_GEOM;
|
|
|
|
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_trd( FILE *file, disk_t *d )
|
|
{
|
|
int i, j, sbase, sectors, seclen, mfm, cyl;
|
|
|
|
if( check_disk_geom( d, &sbase, §ors, &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, §ors, &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, §ors, &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, §ors, &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, §ors, &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;
|
|
libspectrum_byte *t, *c, *f, *w;
|
|
int idx;
|
|
|
|
if( ( file = fopen( filename, "wb" ) ) == NULL )
|
|
return d->status = DISK_WRFILE;
|
|
|
|
namelen = strlen( filename );
|
|
if( namelen < 4 )
|
|
ext = "";
|
|
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, ".d40" ) ) /* ALT side */
|
|
d->type = DISK_D40;
|
|
else if( !strcasecmp( ext, ".d80" ) ) /* ALT side */
|
|
d->type = DISK_D80;
|
|
else if( !strcasecmp( ext, ".scl" ) ) /* not really a disk image */
|
|
d->type = DISK_SCL;
|
|
else if( !strcasecmp( ext, ".td0" ) ) /* not supported */
|
|
d->type = DISK_TD0;
|
|
else if( !strcasecmp( ext, ".log" ) ) /* ALT */
|
|
d->type = DISK_LOG;
|
|
else
|
|
d->type = DISK_UDI; /* ALT side */
|
|
}
|
|
|
|
/* Save position of current data */
|
|
t = d->track;
|
|
c = d->clocks;
|
|
f = d->fm;
|
|
w = d->weak;
|
|
idx = d->i;
|
|
|
|
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_D40:
|
|
case DISK_D80:
|
|
write_d40_d80( 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:
|
|
d->status = DISK_WRFILE;
|
|
break;
|
|
}
|
|
|
|
/* Restore position of previous data.
|
|
FIXME: This is a workaround. Revisit bug #279 and rethink a proper fix */
|
|
d->track = t;
|
|
d->clocks = c;
|
|
d->fm = f;
|
|
d->weak = w;
|
|
d->i = idx;
|
|
|
|
if( d->status != DISK_OK ) {
|
|
fclose( file );
|
|
return d->status;
|
|
}
|
|
|
|
if( fclose( file ) == -1 )
|
|
return d->status = DISK_WRFILE;
|
|
|
|
return d->status = DISK_OK;
|
|
}
|