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