/* 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 */