summaryrefslogtreecommitdiff
path: root/cpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpc.c')
-rw-r--r--cpc.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/cpc.c b/cpc.c
new file mode 100644
index 0000000..1eb2459
--- /dev/null
+++ b/cpc.c
@@ -0,0 +1,497 @@
+/*
+
+ 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 <http://www.gnu.org/licenses/>.
+
+ -------------------------------------------------------------------------
+
+ CPC DSK images
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#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
+*/