/*
DiskImageTool - Tool for manipulating disk images - CPC DSK images
Copyright (C) 2019 Ian Cowburn (ianc@noddybox.co.uk)
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 3 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, see .
-------------------------------------------------------------------------
CPC DSK images
*/
#include
#include
#include
#include
#include "handler.h"
#include "util.h"
#include "cpc.h"
/* ---------------------------------------- TYPES
*/
typedef struct
{
int id;
int size;
int reg1;
int reg2;
char data[512];
} Sector;
typedef struct
{
int track_no;
int side_no;
int no_sectors;
int track_size;
int sector_size;
int gap3_length;
int filler_byte;
int formatted;
Sector sector[10];
} Track;
typedef struct
{
int no_tracks;
Track track[80];
} Disk;
typedef enum
{
UnknownFormat,
FormatIBM,
FormatSystem,
FormatData
} DiskFormat;
/* ---------------------------------------- GLOBALS
*/
static char g_last_error[4096];
static int g_is_extended;
static unsigned char *g_mem;
static size_t g_size;
static Disk g_disk;
static DiskFormat g_format;
/* ---------------------------------------- UTILS
*/
static int ImageError(const char *format, ...)
{
va_list va;
va_start(va, format);
vsnprintf(g_last_error, sizeof g_last_error, format, va);
va_end(va);
if (g_mem)
{
free(g_mem);
g_mem = NULL;
g_size = 0;
}
return 0;
}
static int GetWord(int offset)
{
int i;
i = g_mem[offset];
i|= (int)g_mem[offset+1] * 256;
return i;
}
static int LowSectorID(int track)
{
int f;
int id = 256;
for(f = 0; f < g_disk.track[f].no_sectors; f++)
{
if (g_disk.track[track].sector[f].id < id)
{
id = g_disk.track[track].sector[f].id;
}
}
return id;
}
static int SectorSize(int size)
{
return 128 << size;
}
/* ---------------------------------------- HANDLER INTERFACES
*/
static const char *CPC_last_error(void)
{
return g_last_error;
}
static void CPC_list_files(void)
{
}
static void CPC_list_info(void)
{
int f;
printf("Disk format: ");
switch(g_format)
{
case FormatIBM:
printf("IBM\n");
break;
case FormatSystem:
printf("System\n");
break;
case FormatData:
printf("Data\n");
break;
case UnknownFormat:
printf("Unknown\n");
return;
}
printf("No tracks: %d\n", g_disk.no_tracks);
for(f = 0; f < g_disk.no_tracks; f++)
{
printf("Track %2d : ", f);
if (g_disk.track[f].formatted)
{
int n;
printf(" [ ");
for(n = 0; n < g_disk.track[f].no_sectors; n++)
{
printf("%2.2x %3.3x ", (unsigned)g_disk.track[f].sector[n].id,
(unsigned)SectorSize
(g_disk.track[f].sector[n].size));
}
printf("]\n");
}
else
{
printf("unformatted\n");
}
}
}
static int CPC_is_my_file(const void *buff, size_t buffsize)
{
return CompareMem(buff, "MV - CPC") == 0 ||
CompareMem(buff, "EXTENDED") == 0;
}
static int CPC_open_image(void *buff, size_t buffsize)
{
int f,n;
if (CompareMem(buff, "MV - CPC") == 0)
{
g_is_extended = 0;
}
else if (CompareMem(buff, "EXTENDED") == 0)
{
g_is_extended = 1;
}
else
{
return ImageError("Can't find disk file signature");
}
g_mem = buff;
g_size = buffsize;
memset(&g_disk, 0, sizeof g_disk);
if (g_is_extended)
{
int no_sides;
int total_track_length = 0;
no_sides = g_mem[0x31];
if (no_sides > 1)
{
return ImageError("Invalid number of sides %d", no_sides);
}
g_disk.no_tracks = g_mem[0x30];
if (g_disk.no_tracks > 80)
{
return ImageError("Invalid number of tracks %d",
g_disk.no_tracks);
}
for(f = 0; f < g_disk.no_tracks; f++)
{
int offset;
int track_size;
int sector_size;
int data_length;
int total_length = 0;
track_size = g_mem[0x34 + f] * 256;
offset = 0x100 + total_track_length;
total_track_length += track_size;
g_disk.track[f].formatted = track_size != 0;
if (g_disk.track[f].formatted)
{
if (CompareMem(g_mem + offset, "Track-Info"))
{
return ImageError
("Missing 'Track-Info' at offset %d on track %d",
offset, f);
}
g_disk.track[f].track_no = g_mem[offset + 0x10];
g_disk.track[f].side_no = g_mem[offset + 0x11];
g_disk.track[f].sector_size = g_mem[offset + 0x14];
g_disk.track[f].gap3_length = g_mem[offset + 0x16];
g_disk.track[f].filler_byte = g_mem[offset + 0x17];
g_disk.track[f].no_sectors = g_mem[offset + 0x15];
if (g_disk.track[f].no_sectors > 10)
{
return ImageError
("Invalid number of sectors %d on track %d",
g_disk.track[f].no_sectors,
g_disk.track[f].track_no);
}
sector_size = 128 << g_disk.track[f].sector_size;
if (sector_size > 512)
{
return ImageError("Invalid sector size %d", sector_size);
}
for(n = 0; n < g_disk.track[f].no_sectors; n++)
{
int sec_offset;
sec_offset = offset + 0x18 + n * 8;
g_disk.track[f].sector[n].id = g_mem[sec_offset + 0x02];
g_disk.track[f].sector[n].size = g_mem[sec_offset + 0x03];
g_disk.track[f].sector[n].reg1 = g_mem[sec_offset + 0x04];
g_disk.track[f].sector[n].reg2 = g_mem[sec_offset + 0x05];
data_length = GetWord(sec_offset + 0x06);
sector_size = SectorSize(g_disk.track[f].sector[n].size);
if (sector_size > 512 || data_length > 512)
{
return ImageError
("Invalid sector size %d on track %d, sector %d",
sector_size, f, n);
}
memcpy(g_disk.track[f].sector[n].data,
g_mem + offset + 0x100 + total_length,
data_length);
total_length += data_length;
}
}
}
}
else
{
int no_sides;
int track_size;
no_sides = g_mem[0x31];
if (no_sides > 1)
{
return ImageError("Invalid number of sides %d", no_sides);
}
g_disk.no_tracks = g_mem[0x30];
if (g_disk.no_tracks > 80)
{
return ImageError("Invalid number of tracks %d",
g_disk.no_tracks);
}
track_size = GetWord(0x32);
for(f = 0; f < g_disk.no_tracks; f++)
{
int offset;
int sector_size;
offset = 0x100 + f * track_size;
if (CompareMem(g_mem + offset, "Track-Info"))
{
return ImageError
("Missing 'Track-Info' at offset %d on track %d",
offset, f);
}
g_disk.track[f].track_no = g_mem[offset + 0x10];
g_disk.track[f].side_no = g_mem[offset + 0x11];
g_disk.track[f].sector_size = g_mem[offset + 0x14];
g_disk.track[f].gap3_length = g_mem[offset + 0x16];
g_disk.track[f].filler_byte = g_mem[offset + 0x17];
g_disk.track[f].no_sectors = g_mem[offset + 0x15];
g_disk.track[f].formatted = 1;
if (g_disk.track[f].no_sectors > 10)
{
return ImageError("Invalid number of sectors %d on track %d",
g_disk.track[f].no_sectors,
g_disk.track[f].track_no);
}
sector_size = SectorSize(g_disk.track[f].sector_size);
if (sector_size > 512)
{
return ImageError("Invalid sector size %d", sector_size);
}
for(n = 0; n < g_disk.track[f].no_sectors; n++)
{
int sec_offset;
sec_offset = offset + 0x18 + n * 8;
g_disk.track[f].sector[n].id = g_mem[sec_offset + 0x02];
g_disk.track[f].sector[n].size = g_mem[sec_offset + 0x03];
g_disk.track[f].sector[n].reg1 = g_mem[sec_offset + 0x04];
g_disk.track[f].sector[n].reg2 = g_mem[sec_offset + 0x05];
sector_size = SectorSize(g_disk.track[f].sector[n].size);
if (sector_size > 512)
{
return ImageError
("Invalid sector size %d on track %d, sector %d",
SectorSize(g_disk.track[f].sector[n].size));
}
memcpy(g_disk.track[f].sector[n].data,
g_mem + offset + 0x100 + sector_size * n,
sector_size);
}
}
}
switch(LowSectorID(0))
{
case 1:
if (g_disk.track[0].no_sectors == 8)
{
g_format = FormatIBM;
}
break;
case 65:
g_format = FormatSystem;
break;
case 193:
g_format = FormatData;
break;
default:
g_format = UnknownFormat;
break;
}
if (g_format == UnknownFormat)
{
return ImageError("Unknown format");
}
return 1;
}
static int CPC_close_image(const char *name)
{
if (name)
{
}
if (g_mem)
{
free(g_mem);
g_mem = NULL;
g_size = 0;
}
return 0;
}
static int CPC_get_file(const char *name, const char *path)
{
strcpy(g_last_error, "Get file not yet done");
return 0;
}
static int CPC_put_file(const char *name, const char *path)
{
strcpy(g_last_error, "Put file not yet done");
return 0;
}
static int CPC_delete_file(const char *name)
{
strcpy(g_last_error, "Delete not yet done");
return 0;
}
static int CPC_create(const char *path)
{
strcpy(g_last_error, "Create not yet done");
return 0;
}
/* ---------------------------------------- PUBLIC INTERFACES
*/
void CPC_Initialise(void)
{
Handler handler = {0};
handler.id = "cpc";
handler.name = "CPC DSK handler";
handler.last_error = CPC_last_error;
handler.list_files = CPC_list_files;
handler.list_info = CPC_list_info;
handler.is_my_file = CPC_is_my_file;
handler.open_image = CPC_open_image;
handler.close_image = CPC_close_image;
handler.get_file = CPC_get_file;
handler.put_file = CPC_put_file;
handler.delete_file = CPC_delete_file;
handler.create = CPC_create;
DITOOL_Register(&handler);
}
/*
vim: ai sw=4 ts=8 expandtab
*/