#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

// Program to examine YAZE disk images and
// generate a diskdef for use with cpmtools.
//
// Command syntax: getydskdef <image-file>

int mediaSize_m;
int secSize_m;
int numSectors_m;
int numSides_m;
int numTracks_m;
int doubleDensity_m;
int interlaced_m;
int mediaLat_m;
int mediaCyl;
int mediaHead;
int mediaSsz;
int mediaSpt;
int mediaSec_m = 0;
int result;

struct dpb {
	unsigned short spt;
	unsigned char bsh, bsm, exm;
	unsigned short dsm, drm;
	unsigned char alv0, alv1;
	unsigned short cks;
	unsigned short restrk;
} __attribute__ ((packed));

float check_os(int fd, struct dpb *dpb, int offset) {
	float os = 2.2;
	unsigned char buf[128];
	off_t off = offset * 128L;
	off += dpb->restrk * dpb->spt * 128;
	lseek(fd, off, SEEK_SET);
	size_t rc = read(fd, buf, sizeof(buf));
	if (rc == sizeof(buf) && buf[96] == 0x21) {
		os = 3;
	}
	return os;
}

static int count_ones(unsigned char alv0, unsigned char alv1) {
	unsigned int alv = (alv0 << 8) | alv1;
	int x;
	int n = 0;
	for (x = 0; x < 16; ++x) {
		if (alv & 1) ++n;
		alv >>= 1;
	}
	return n;
}

#define DPB_OFF 32

void get_ydsk_diskdef(int fd, unsigned char *buf, int off) {
	struct dpb dpb;
	dpb.spt = buf[DPB_OFF] + (buf[DPB_OFF + 1] << 8);
	dpb.bsh = buf[DPB_OFF + 2];
	dpb.bsm = buf[DPB_OFF + 3];
	dpb.exm = buf[DPB_OFF + 4];
	dpb.dsm = buf[DPB_OFF + 5] + (buf[DPB_OFF + 6] << 8);
	dpb.drm = buf[DPB_OFF + 7] + (buf[DPB_OFF + 8] << 8);
	dpb.alv0 = buf[DPB_OFF + 9];
	dpb.alv1 = buf[DPB_OFF + 10];
	dpb.cks = buf[DPB_OFF + 11] + (buf[DPB_OFF + 12] << 8);
	dpb.restrk = buf[DPB_OFF + 13] + (buf[DPB_OFF + 14] << 8);

	int blksize = (0x80 << dpb.bsh);
	int trksize = (dpb.spt * 128);
	int tracks = ((dpb.dsm + 1) * blksize) / trksize;

	off += dpb.restrk * trksize; // typically "+= 0"

	float os = check_os(fd, &dpb, off);
	int dirblks = count_ones(dpb.alv0, dpb.alv1);
	int dnb = ((dpb.drm + 1) * 32 + blksize - 1) / blksize;
	printf("diskdef\n");
	printf("  seclen %d\n", 128);
	printf("  tracks %d\n", tracks);
	printf("  sectrk %d\n", dpb.spt);
	printf("  blocksize %d\n", blksize);
	printf("  maxdir %d\n", dpb.drm + 1);
	// assert(dnb <= dirblks);
	if (dnb < dirblks) {
		printf("  dirblks %d\n", dirblks);
	}
	printf("  skew %d\n", 1);
	printf("  offset %d\n", off);
	printf("  boottrk %d\n", dpb.restrk);
	printf("  os %g\n", os);
	printf("end\n");
}

int main(int argc, char **argv) {
	unsigned char buf[128];
	int fd;
	struct stat stb;

	if (argc != 2) {
		fprintf(stderr, "Syntax: %s <image-file>\n", argv[0]);
		exit(1);
	}

	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror(argv[1]);
		exit(1);
	}
	memset(buf, 0, sizeof(buf));
	read(fd, buf, sizeof(buf));
	fstat(fd, &stb);
	long end = stb.st_size - 128;
	if (strcmp(buf, "<CPM_Disk>") != 0) {
		// silently fail if not recognized?
		fprintf(stderr, "Failed to recognize image %s\n", argv[1]);
		exit(1);
	}
	get_ydsk_diskdef(fd, buf, sizeof(buf));
	close(fd);
	return 0;
}
