OXYGENHUB
# 8e8d9cad1884 x16-emulator / src / files.c
6.4 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
#ifndef __APPLE__
2
#define _XOPEN_SOURCE   600
3
#define _POSIX_C_SOURCE 1
4
#endif
5
6
#include "files.h"
7
8
#include <limits.h>
9
#include <string.h>
10
#include <stdlib.h>
11
#include <unistd.h>
12
#include <stdio.h>
13
#include <SDL.h>
14
#include <zlib.h>
15
#include <inttypes.h>
16
17
struct x16file
18
{
19
	char path[PATH_MAX];
20
21
	SDL_RWops *file;
22
	int64_t size;
23
	int64_t pos;
24
	bool modified;
25
26
	struct x16file *next;
27
};
28
29
struct x16file *open_files = NULL;
30
31
static bool 
32
get_tmp_name(char *path_buffer, const char *original_path, char const *extension)
33
{
34
	if(strlen(original_path) > PATH_MAX - strlen(extension)) {
35
		printf("Path too long, cannot create temp file: %s\n", original_path);
36
		return false;
37
	}
38
39
	strcpy(path_buffer, original_path);
40
	strcat(path_buffer, extension);
41
42
	return true;
43
}
44
45
bool 
46
file_is_compressed_type(char const *path)
47
{
48
	int len = (int)strlen(path);
49
50
	if (strcmp(path + len - 3, ".gz") == 0 || strcmp(path + len - 3, "-gz") == 0) {
51
		return true;
52
	} else if (strcmp(path + len - 2, ".z") == 0 || strcmp(path + len - 2, "-z") == 0 || strcmp(path + len - 2, "_z") == 0 || strcmp(path + len - 2, ".Z") == 0) {
53
		return true;
54
	}
55
56
	return false;
57
}
58
59
const char *
60
file_find_extension(const char *path, const char *mark)
61
{
62
	if(path == NULL) {
63
		return NULL;
64
	}
65
66
	if(mark == NULL) {
67
		mark = path + strlen(path);
68
		if(file_is_compressed_type(path)) {
69
			mark -= 3;
70
		}
71
	}
72
73
	while(mark > path) {
74
		if(*mark == '.') {
75
			return mark;
76
		}
77
		--mark;
78
	}
79
80
	return NULL;
81
}
82
83
84
void
85
files_shutdown()
86
{
87
	struct x16file *f = open_files;
88
	struct x16file *next_f = NULL;
89
	for(; f != NULL; f = next_f) {
90
		next_f = f->next;
91
		x16close(f);
92
	}
93
}
94
95
struct x16file *
96
x16open(const char *path, const char *attribs)
97
{
98
	struct x16file *f = malloc(sizeof(struct x16file));
99
	strcpy(f->path, path);
100
101
	if(file_is_compressed_type(path)) {
102
		char tmp_path[PATH_MAX];
103
		if(!get_tmp_name(tmp_path, path, ".tmp")) {
104
			printf("Path too long, cannot create temp file: %s\n", path);
105
			goto error;
106
		}
107
108
		gzFile zfile = gzopen(path, "rb");
109
		if(zfile == Z_NULL) {
110
			printf("Could not open file for decompression: %s\n", path);
111
			goto error;
112
		}
113
114
		SDL_RWops *tfile = SDL_RWFromFile(tmp_path, "wb");
115
		if(tfile == NULL) {
116
			gzclose(zfile);
117
			printf("Could not open file for write: %s\n", tmp_path);
118
			goto error;
119
		}
120
121
		printf("Decompressing %s\n", path);
122
123
		const int buffer_size = 16 * 1024 * 1024;
124
		char *buffer = malloc(buffer_size);
125
126
		int read = gzread(zfile, buffer, buffer_size);
127
		int64_t total_read = read;
128
		const int64_t progress_increment = 128 * 1024 * 1024;
129
		int64_t progress_threshold = progress_increment;
130
		while(read > 0) {
131
			if(total_read > progress_threshold) {
132
				printf("%" PRId64 " MB\n", total_read / (1024 * 1024));
133
				progress_threshold += progress_increment;
134
			}
135
			SDL_RWwrite(tfile, buffer, read, 1);
136
			read = gzread(zfile, buffer, buffer_size);
137
			total_read += read;
138
		}
139
		printf("%" PRId64 " MB\n", total_read / (1024 * 1024));
140
141
		SDL_RWclose(tfile);
142
		gzclose(zfile);
143
		free(buffer);
144
145
		f->file = SDL_RWFromFile(tmp_path, attribs);
146
		if(f->file == NULL) {
147
			unlink(tmp_path);
148
			goto error;
149
		}
150
		f->size = total_read;
151
	} else {
152
		f->file = SDL_RWFromFile(path, attribs);
153
		if(f->file == NULL) {
154
			goto error;
155
		}
156
		f->size = SDL_RWsize(f->file);
157
	}
158
	f->pos = 0;
159
	f->modified = false;
160
	f->next = open_files ? open_files : NULL;
161
	open_files = f;
162
163
	return f;
164
165
error:
166
	free(f);
167
	return NULL;
168
}
169
170
void 
171
x16close(struct x16file *f)
172
{
173
	if(f == NULL) {
174
		return;
175
	}
176
177
	SDL_RWclose(f->file);
178
179
	if(file_is_compressed_type(f->path)) {
180
		char tmp_path[PATH_MAX];
181
		if(!get_tmp_name(tmp_path, f->path, ".tmp")) {
182
			printf("Path too long, cannot create temp file: %s\n", f->path);
183
			goto tmp_path_error;
184
		}
185
186
		if(f->modified == false) {
187
			goto zfile_clean;
188
		}
189
190
		gzFile zfile = gzopen(f->path, "wb6");
191
		if(zfile == Z_NULL) {
192
			printf("Could not open file for compression: %s\n", f->path);
193
			goto zfile_error;
194
		}
195
196
		SDL_RWops *tfile = SDL_RWFromFile(tmp_path, "rb");
197
		if(tfile == NULL) {
198
			gzclose(zfile);
199
			printf("Could not open file for read: %s\n", tmp_path);
200
			goto tfile_error;
201
		}
202
203
		printf("Recompressing %s\n", f->path);
204
205
		const int buffer_size = 16 * 1024 * 1024;
206
		char *buffer = malloc(buffer_size);
207
208
		const int64_t progress_increment = 128 * 1024 * 1024;
209
		int64_t progress_threshold = progress_increment;
210
		int read = SDL_RWread(tfile, buffer, 1, buffer_size);
211
		int64_t total_read = read;
212
		while(read > 0) {
213
			if(total_read > progress_threshold) {
214
				printf("%d%%\n", (int)(total_read * 100 / f->size));
215
				progress_threshold += progress_increment;
216
			}
217
			gzwrite(zfile, buffer, read);
218
			read = SDL_RWread(tfile, buffer, 1, buffer_size);
219
			total_read += read;
220
		}
221
222
		free(buffer);
223
224
		if(tfile != NULL) {
225
			SDL_RWclose(tfile);
226
		}
227
228
	tfile_error:
229
		if(zfile != Z_NULL) {
230
			gzclose(zfile);
231
		}
232
233
	zfile_error: // fall-through
234
	zfile_clean:
235
		unlink(tmp_path);
236
	}
237
tmp_path_error:
238
	if(f == open_files) {
239
		open_files = f->next;
240
	} else {
241
		for(struct x16file *fi = open_files; fi != NULL; fi = fi->next) {
242
			if(fi->next == f) {
243
				fi->next = f->next;
244
				break;
245
			}
246
		}
247
	}
248
	free(f);
249
}
250
251
int64_t 
252
x16size(struct x16file *f)
253
{
254
	if(f == NULL) {
255
		return 0;
256
	}
257
258
	return f->size;
259
}
260
261
int 
262
x16seek(struct x16file *f, int64_t pos, int origin)
263
{
264
	if(f == NULL) {
265
		return 0;
266
	}
267
	switch(origin) {
268
		case SEEK_SET:
269
			f->pos = (pos > f->size) ? f->size : pos;
270
			break;
271
		case SEEK_CUR:
272
			f->pos += pos;
273
			if(f->pos > f->size || f->pos < 0) {
274
				f->pos = f->size;
275
			}
276
			break;
277
		case SEEK_END:
278
			f->pos = f->size - pos;
279
			if(f->pos < 0) {
280
				f->pos = f->size;
281
			}
282
	}
283
	return SDL_RWseek(f->file, f->pos, SEEK_SET);
284
}
285
286
int64_t 
287
x16tell(struct x16file *f)
288
{
289
	if(f == NULL) {
290
		return 0;
291
	}
292
	return f->pos;
293
}
294
295
int 
296
x16write8(struct x16file *f, uint8_t val)
297
{
298
	if(f == NULL) {
299
		return 0;
300
	}
301
	int written = SDL_RWwrite(f->file, &val, 1, 1);
302
	f->pos += written;
303
	return written;
304
}
305
306
uint8_t 
307
x16read8(struct x16file *f)
308
{
309
	if(f == NULL) {
310
		return 0;
311
	}
312
	uint8_t val;
313
	int read = SDL_RWread(f->file, &val, 1, 1);
314
	f->pos += read;
315
	return read;
316
}
317
318
uint64_t 
319
x16write(struct x16file *f, const uint8_t *data, uint64_t data_size, uint64_t data_count)
320
{
321
	if(f == NULL) {
322
		return 0;
323
	}
324
	int64_t written = SDL_RWwrite(f->file, data, data_size, data_count);
325
	if(written) {
326
		f->modified = true;
327
	}
328
	f->pos += written * data_size;
329
	return written;
330
}
331
332
uint64_t 
333
x16read(struct x16file *f, uint8_t *data, uint64_t data_size, uint64_t data_count)
334
{
335
	if(f == NULL) {
336
		return 0;
337
	}
338
	int64_t read = SDL_RWread(f->file, data, data_size, data_count);
339
	f->pos += read * data_size;
340
	return read;
341
}