~kikoodx/kble-c

kble-c/src/level.c -rw-r--r-- 4.0 KiB
779b4e94KikooDX Make the types less confusing. Remove debug printf. 28 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* SPDX-License-Identifier: GPL-3.0-or-later */
/* Copyright (C) 2021 KikooDX */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "level.h"

static const int kble_fmt_version = 0;

static char read_byte(FILE *file);
static tile_t read_byte_group(FILE *file, int size);
static void write_byte(FILE *file, char byte);
static void write_byte_group(FILE *file, tile_t value, int size);

void level_read(struct Level *level, char *path) {
	FILE *file = NULL;
	char byte = 0;
	unsigned int tile_size = 0;
	int level_size = 0;
	int i = 0;
	tile_t tile = 0;

	/* open file in read mode */
	file = fopen(path, "rb");
	if (file == NULL) {
		fprintf(stderr, "ERROR: cannot open input file %s\n", path);
		exit(EXIT_FAILURE);
	}

	/* check KBLE format version */
	byte = read_byte(file);
	if (byte != kble_fmt_version) {
		fprintf(stderr, "ERROR: KBLE format version doesn't match ; "
			"expected %d, got %d\n", kble_fmt_version, byte);
		exit(EXIT_FAILURE);
	}

	/* get tile size (in bytes) */
	tile_size = read_byte(file);
	/* check than tile size is in boundaries */
	if (tile_size > sizeof(tile_t) / sizeof(char)) {
    		fprintf(stderr, "ERROR: tile size is too big ; "
    			"maximum is %ld, found %d", sizeof(tile_t), tile_size);
    		exit(EXIT_FAILURE);
	}
	/* get width */
	level->width = read_byte_group(file, 2);
	/* get height */
	level->height = read_byte_group(file, 2);

	/* allocate memory for data */
	level_size = level->width * level->height;
	if (level->data == NULL) {
		level->data = malloc(level_size * sizeof(tile_t));
	} else {
		level->data = realloc(level->data, level_size * sizeof(tile_t));
	}
	/* check for allocation failure */
	if (level->data == NULL) {
		fprintf(stderr, "ERROR: memory allocation failure\n");
		exit(EXIT_FAILURE);
	}

	/* read file content */
	for (i = 0; i < level_size; i += 1) {
		tile = read_byte_group(file, tile_size);
		level->data[i] = tile;
	}

	/* close file */
	fclose(file);
}

void level_free(struct Level *level) {
	free(level->data);
}

void level_write(struct Level level, char *path) {
	FILE *file = NULL;
	int tile = 0;
	int tile_size = 0;
	int max_tile_size = 1;
	int i = 0;
	const int level_size = level.width * level.height;

	/* open file in write mode */
	file = fopen(path, "wb");
	if (file == NULL) {
		fprintf(stderr, "ERROR: cannot open output file %s\n", path);
		exit(EXIT_FAILURE);
	}

	/* find longest value in data (in bytes) */
	for (i = 0; i < level_size; i += 1) {
		tile = level.data[i];
		tile_size = 1;
		while (tile >>= 8)
			tile_size += 1;
		if (tile_size > max_tile_size)
			max_tile_size = tile_size;
	}

	/* write KBLE format version */
	write_byte(file, kble_fmt_version);
	/* write tile size (in bytes) */
	write_byte(file, max_tile_size);
	/* write width */
	write_byte_group(file, level.width, 2);
	/* write height */
	write_byte_group(file, level.height, 2);

	/* write level content */
	for (i = 0; i < level_size; i += 1) {
		write_byte_group(file, level.data[i], max_tile_size);
	}

	/* close file */
	fclose(file);
}

/* Read a single byte safely (handle EOF). */
static char read_byte(FILE *file) {
	const int byte = fgetc(file);
	if (byte == EOF) {
		fprintf(stderr, "ERROR: unexpected EOF\n");
		exit(EXIT_FAILURE);
	}
	return (char)byte;
}

/* Read multiple bytes and "merge" them into one integer. */
static tile_t read_byte_group(FILE *file, int size) {
	int group = 0;
	char *byte_ptr = (char*)&group;
	int i = 0;
	byte_ptr += size;
	for (i = 0; i < size; i += 1) {
		byte_ptr -= 1,
		*byte_ptr = read_byte(file);
	}
	return group;
}

/* Write a single byte safely (handle EOF). */
static void write_byte(FILE *file, char byte) {
	const int result = fputc(byte, file);
	if (result == EOF) {
		fprintf(stderr, "ERROR: file write error\n");
		exit(EXIT_FAILURE);
	}
}

/* Write an integer as multiple bytes. */
static void write_byte_group(FILE *file, tile_t value, int size) {
	char *value_ptr = (char*)&value;
	value_ptr += size;
	int i = 0;
	for (i = 0; i < size; i += 1) {
		value_ptr -= 1;
		write_byte(file, *value_ptr);
	}
}