2017-06-18 04:09:25 +00:00
|
|
|
#ifndef GUARD_COMMON_H
|
|
|
|
#define GUARD_COMMON_H
|
|
|
|
|
2021-09-02 04:21:10 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
2022-03-15 21:29:15 +00:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdnoreturn.h>
|
2021-09-02 04:21:10 +00:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <getopt.h>
|
2017-06-18 04:09:25 +00:00
|
|
|
|
2021-09-21 21:37:43 +00:00
|
|
|
#ifndef PROGRAM_NAME
|
|
|
|
#error Define PROGRAM_NAME before including common.h!
|
|
|
|
#endif
|
|
|
|
#ifndef USAGE_OPTS
|
|
|
|
#error Define USAGE_OPTS before including common.h!
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define error_exit(...) exit((fprintf(stderr, PROGRAM_NAME ": " __VA_ARGS__), 1))
|
|
|
|
|
2022-03-15 21:35:35 +00:00
|
|
|
noreturn void usage_exit(int status) {
|
2021-09-21 21:37:43 +00:00
|
|
|
fprintf(stderr, "Usage: " PROGRAM_NAME " " USAGE_OPTS "\n");
|
|
|
|
exit(status);
|
|
|
|
}
|
2021-09-02 07:04:40 +00:00
|
|
|
|
2021-09-02 04:21:10 +00:00
|
|
|
int getopt_long_index;
|
2021-09-02 05:21:15 +00:00
|
|
|
#define getopt_long(argc, argv, optstring, longopts) getopt_long(argc, argv, optstring, longopts, &getopt_long_index)
|
2021-09-02 04:21:10 +00:00
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
void *xmalloc(size_t size) {
|
2021-09-02 05:21:15 +00:00
|
|
|
errno = 0;
|
2021-09-02 04:21:10 +00:00
|
|
|
void *m = malloc(size);
|
|
|
|
if (!m) {
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Could not allocate %zu bytes: %s\n", size, strerror(errno));
|
2021-09-02 04:21:10 +00:00
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
void *xcalloc(size_t size) {
|
2021-09-02 22:37:36 +00:00
|
|
|
errno = 0;
|
|
|
|
void *m = calloc(size, 1);
|
|
|
|
if (!m) {
|
|
|
|
error_exit("Could not allocate %zu bytes: %s\n", size, strerror(errno));
|
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
void *xrealloc(void *m, size_t size) {
|
|
|
|
errno = 0;
|
|
|
|
m = realloc(m, size);
|
|
|
|
if (!m) {
|
|
|
|
error_exit("Could not allocate %zu bytes: %s\n", size, strerror(errno));
|
|
|
|
}
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *xfopen(const char *filename, char rw) {
|
2021-09-02 04:21:10 +00:00
|
|
|
char mode[3] = {rw, 'b', '\0'};
|
2021-09-02 05:21:15 +00:00
|
|
|
errno = 0;
|
2017-06-18 04:09:25 +00:00
|
|
|
FILE *f = fopen(filename, mode);
|
|
|
|
if (!f) {
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Could not open file \"%s\": %s\n", filename, strerror(errno));
|
2017-06-18 04:09:25 +00:00
|
|
|
}
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
void xfread(uint8_t *data, size_t size, const char *filename, FILE *f) {
|
2021-09-02 05:21:15 +00:00
|
|
|
errno = 0;
|
2021-09-02 04:21:10 +00:00
|
|
|
if (fread(data, 1, size, f) != size) {
|
|
|
|
fclose(f);
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Could not read from file \"%s\": %s\n", filename, strerror(errno));
|
2017-06-18 04:09:25 +00:00
|
|
|
}
|
2021-09-02 04:21:10 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
void xfwrite(const uint8_t *data, size_t size, const char *filename, FILE *f) {
|
2021-09-02 05:21:15 +00:00
|
|
|
errno = 0;
|
2021-09-02 04:21:10 +00:00
|
|
|
if (fwrite(data, 1, size, f) != size) {
|
|
|
|
fclose(f);
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Could not write to file \"%s\": %s\n", filename, strerror(errno));
|
2017-12-28 06:25:25 +00:00
|
|
|
}
|
2021-09-02 04:21:10 +00:00
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
long xfsize(const char *filename, FILE *f) {
|
2021-09-02 05:21:15 +00:00
|
|
|
long size = -1;
|
|
|
|
errno = 0;
|
2021-09-02 04:21:10 +00:00
|
|
|
if (!fseek(f, 0, SEEK_END)) {
|
|
|
|
size = ftell(f);
|
|
|
|
if (size != -1) {
|
|
|
|
rewind(f);
|
|
|
|
}
|
|
|
|
}
|
2021-09-02 05:21:15 +00:00
|
|
|
if (size == -1) {
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Could not measure file \"%s\": %s\n", filename, strerror(errno));
|
2021-09-02 04:21:10 +00:00
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *read_u8(const char *filename, long *size) {
|
2022-03-03 02:39:13 +00:00
|
|
|
FILE *f = xfopen(filename, 'r');
|
|
|
|
*size = xfsize(filename, f);
|
|
|
|
uint8_t *data = xmalloc(*size);
|
|
|
|
xfread(data, *size, filename, f);
|
2017-06-18 04:09:25 +00:00
|
|
|
fclose(f);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2021-09-02 04:21:10 +00:00
|
|
|
void write_u8(const char *filename, uint8_t *data, size_t size) {
|
2022-03-03 02:39:13 +00:00
|
|
|
FILE *f = xfopen(filename, 'w');
|
|
|
|
xfwrite(data, size, filename, f);
|
2021-09-02 04:21:10 +00:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2022-03-03 02:39:13 +00:00
|
|
|
uint32_t read_png_width(const char *filename) {
|
|
|
|
FILE *f = xfopen(filename, 'r');
|
2021-09-02 04:21:10 +00:00
|
|
|
uint8_t header[16] = {0};
|
2022-03-03 02:39:13 +00:00
|
|
|
xfread(header, sizeof(header), filename, f);
|
2021-09-02 04:21:10 +00:00
|
|
|
static uint8_t expected_header[16] = {
|
|
|
|
0x89, 'P', 'N', 'G', '\r', '\n', 0x1A, '\n', // signature
|
|
|
|
0, 0, 0, 13, // IHDR chunk length
|
|
|
|
'I', 'H', 'D', 'R', // IHDR chunk type
|
|
|
|
};
|
|
|
|
if (memcmp(header, expected_header, sizeof(header))) {
|
2017-06-18 04:09:25 +00:00
|
|
|
fclose(f);
|
2021-09-02 07:04:40 +00:00
|
|
|
error_exit("Not a valid PNG file: \"%s\"\n", filename);
|
2017-06-18 04:09:25 +00:00
|
|
|
}
|
2021-09-02 04:21:10 +00:00
|
|
|
uint8_t bytes[4] = {0};
|
2022-03-03 02:39:13 +00:00
|
|
|
xfread(bytes, sizeof(bytes), filename, f);
|
2021-09-02 04:21:10 +00:00
|
|
|
fclose(f);
|
|
|
|
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
2017-06-18 04:09:25 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 22:37:36 +00:00
|
|
|
void read_dimensions(const char *filename, int *width) {
|
|
|
|
long filesize;
|
|
|
|
uint8_t *bytes = read_u8(filename, &filesize);
|
|
|
|
if (filesize != 1) {
|
|
|
|
error_exit("%s: invalid dimensions file\n", filename);
|
|
|
|
}
|
|
|
|
uint8_t dimensions = bytes[0];
|
|
|
|
free(bytes);
|
|
|
|
*width = dimensions & 0xF;
|
|
|
|
int height = dimensions >> 4;
|
|
|
|
if (*width != height || (*width != 5 && *width != 6 && *width != 7)) {
|
|
|
|
error_exit("%s: invalid dimensions: %dx%d tiles\n", filename, *width, height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-18 04:09:25 +00:00
|
|
|
#endif // GUARD_COMMON_H
|