/*
 * DF-3128 bisect tool: FAT32 image crafter with per-field overrides.
 * usage: ./craftvar <variant> <out.img>
 *   b   = baseline crafted (BytesPerSec=512 SecPerClust=1 Res=1 FATs=1
 *         RootDirEnts=0 Sectors=0 Media=0xF8 FATsecs16=0
 *         Huge=0xC0000000 BigFAT=0x100 Ext=0x0080 FSVers=0 RootClust=2
 *         FSInfo=0 Backup=0), 512 MiB sparse device
 *   f2  = b + FATs=2
 *   r32 = b + ResSectors=32
 *   fi  = b + FSInfo=1 Backup=2
 *   md  = b + Media=0xF0 (FAT[0]=0x0FFFFFF0)
 *   all = all of the above
 *   ex  = all + ExtFlags=0
 *   st  = all + SecPerTrack=32 Heads=64
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>

static uint8_t bs[512];

static void w16(uint8_t *p, uint16_t v) { p[0] = v; p[1] = v >> 8; }
static void w32(uint8_t *p, uint32_t v) { w16(p, v); w16(p + 2, v >> 16); }

int
main(int argc, char **argv)
{
	const char *v;
	uint8_t fat[8];
	uint32_t res = 1, fat0;
	uint8_t fats = 1, media = 0xF8;
	uint16_t fsinfo = 0, backup = 0, ext = 0x0080, spt = 63, heads = 255;
	uint64_t devsz = 512LL * 1024 * 1024;
	int fd;

	if (argc != 3) { fprintf(stderr, "usage: %s variant out.img\n", argv[0]); return 1; }
	v = argv[1];
	if (strchr(v, '2')) fats = 2;
	if (strchr(v, 'r')) res = 32;
	if (strchr(v, 'i')) { fsinfo = 1; backup = 2; }
	if (strchr(v, 'm')) media = 0xF0;
	if (strchr(v, 'e')) ext = 0;
	if (strchr(v, 't')) { spt = 32; heads = 64; }

	memset(bs, 0, sizeof(bs));
	bs[0] = 0xEB; bs[1] = 0x3C; bs[2] = 0x90;
	memcpy(bs + 3, "DF3128  ", 8);
	w16(bs + 11, 512);
	bs[13] = 1;
	w16(bs + 14, res);
	bs[16] = fats;
	w16(bs + 17, 0);			/* FAT32 */
	w16(bs + 19, 0);
	bs[21] = media;
	w16(bs + 22, 0);
	w16(bs + 24, spt);
	w16(bs + 26, heads);
	w32(bs + 28, 0);
	w32(bs + 32, 0xC0000000u);		/* claim 1.5 TiB */
	w32(bs + 36, 0x00000100u);		/* 128 KiB FAT */
	w16(bs + 40, ext);
	w16(bs + 42, 0);
	w32(bs + 44, 2);
	w16(bs + 48, fsinfo);
	w16(bs + 50, backup);
	bs[510] = 0x55; bs[511] = 0xAA;

	fat0 = 0x0FFFFFFFu & 0xFFFFFF00u;
	fat0 |= media;				/* media-ID word */
	w32(fat + 0, fat0);
	w32(fat + 4, 0x0FFFFFFFu);

	fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (fd < 0) { perror("open"); return 1; }
	if (ftruncate(fd, devsz)) { perror("ftruncate"); return 1; }
	if (pwrite(fd, bs, 512, 0) != 512) { perror("pwrite bs"); return 1; }
	if (pwrite(fd, fat, 8, (uint64_t)res * 512) != 8) { perror("pwrite fat"); return 1; }
	close(fd);
	printf("%s: res=%u fats=%u media=%#x fat0=%#010x fsinfo=%u backup=%u ext=%#x spt=%u heads=%u\n",
	    v, (unsigned)res, fats, media, fat0, fsinfo, backup, ext, spt, heads);
	return 0;
}
