#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>

const size_t block_size = 4096;
const unsigned char zeros[block_size] = {};

static void die(const char *msg) {
	perror(msg);
	exit(EXIT_FAILURE);
}

static void writeBuffer(const void *data, size_t size) {
	if (fwrite(data, 1, size, stdout) != size)
		die("fwrite");
}

int main(int argc, char *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "usage: %s <list of flat binaries>\n", argv[0]);
		return EXIT_FAILURE;
	} else if (argc > 1000) {
		fprintf(stderr, "Only a maximum of 1000 apps are supported\n");
		return EXIT_FAILURE;
	}

	// HEADER: Number of apps + size of each app
	uint32_t size[argc];  // NOLINT
	size[0] = static_cast<uint32_t>(argc - 1);

	for (int i = 1; i < argc; ++i) {
		struct stat sb;
		if (stat(argv[i], &sb) != 0)
			die("stat");

		// 4 GB Limit
		if (sb.st_size >= UINT32_MAX) {
			errno = EFBIG;
			die("stat");
		}

		size[i] = sb.st_size;
	}

	writeBuffer(&size, sizeof(size));

	// Fill block
	writeBuffer(zeros, block_size - sizeof(size));

	// DATA: Each App
	for (int i = 1; i < argc; ++i) {
		unsigned char *buf = reinterpret_cast<unsigned char *>(malloc(size[i]));
		if (buf == nullptr)
			die("malloc");

		FILE *fp = fopen(argv[i], "r");
		if (fp == nullptr)
			die("fopen");

		if (fread(buf, sizeof(unsigned char), size[i], fp) != size[i])
			die("fread");

		if (fclose(fp) != 0)
			die("fclose");

		writeBuffer(buf, size[i]);

		// Fill to block size, if required
		if (size[i] % block_size != 0)
			writeBuffer(zeros, block_size - (size[i] % block_size));

		free(buf);
	}

	fflush(stdout);
	fclose(stdout);
	return EXIT_SUCCESS;
}
