GoldFish Engine
Quite simple and lightweight game engine
Loading...
Searching...
No Matches
gf_resource.c
1#define GF_EXPOSE_RESOURCE
2
3#include <gf_pre.h>
4
5/* External library */
6#include <stb_ds.h>
7#include <zlib.h>
8
9/* Interface */
10#include <gf_resource.h>
11
12/* Engine */
13#include <gf_core.h>
14#include <gf_log.h>
15
16/* Standard */
17#include <string.h>
18#include <stdlib.h>
19#include <stdio.h>
20#include <time.h>
21
22#define CHUNK 32767
23
24#pragma pack(1)
25
30struct tar_header {
31 char filename[100];
32 char mode[8];
33 char uid[8];
34 char gid[8];
35 char size[12];
36 char mtime[12];
37 char chksum[8];
38 char typeflag[1];
39 char linkname[100];
40 char ustar[6];
41 char ustarv[2];
42 char username[32];
43 char grpname[32];
44 char devmajor[8];
45 char devminor[8];
46 char prefix[155];
47};
48
49#pragma pack()
50
51unsigned int tar_getsize(const char* input) {
52 int i;
53 unsigned int r = 0;
54 for(i = 0; i < 11; i++) {
55 r = r << 3;
56 r = r | (input[i] - '0');
57 }
58 return r;
59}
60
61void* gf_resource_allocate(gf_resource_t* resource, size_t size) {
62 unsigned int base = resource->size;
63 if(resource->data == NULL) {
64 resource->data = malloc(size);
65 } else {
66 unsigned char* old = resource->data;
67 resource->data = malloc(base + size);
68 memcpy(resource->data, old, base);
69 free(old);
70 }
71 resource->size += size;
72 memset(resource->data + base, 0, size);
73 return resource->data + base;
74}
75
76gf_resource_t* gf_resource_create(gf_engine_t* engine, const char* path) {
77 struct tar_header* th;
78 int i;
79 unsigned int cmpsize = 0;
80 unsigned int cursize = 0;
81 unsigned int curseek = 0;
82 z_stream stream;
83 int ret;
84 FILE* f;
85 gf_resource_t* resource = malloc(sizeof(*resource));
86
87 memset(resource, 0, sizeof(*resource));
88 resource->engine = engine;
89 resource->entries = NULL;
90 resource->data = NULL;
91 resource->size = 0;
92 sh_new_strdup(resource->entries);
93
94 if(path == NULL) {
95 gf_log_function(engine, "Created empty resource", "");
96 return resource;
97 }
98 f = fopen(path, "rb");
99
100 if(f == NULL) {
101 gf_log_function(engine, "Failed to create resource", "");
102 gf_resource_destroy(resource);
103 return NULL;
104 }
105
106 fseek(f, 0, SEEK_END);
107 cmpsize = ftell(f);
108 fseek(f, 0, SEEK_SET);
109
110 stream.zalloc = Z_NULL;
111 stream.zfree = Z_NULL;
112 stream.opaque = Z_NULL;
113 stream.avail_in = 0;
114 stream.next_in = Z_NULL;
115
116 if(inflateInit(&stream) != Z_OK) {
117 gf_log_function(engine, "Failed to initialize zlib", "");
118 gf_resource_destroy(resource);
119 return NULL;
120 }
121
122 gf_log_function(engine, "Created resource", "");
123
124 do {
125 unsigned char in[CHUNK];
126 unsigned char out[CHUNK];
127 stream.avail_in = fread(in, 1, sizeof(in), f);
128 if(stream.avail_in == 0) break;
129 stream.next_in = in;
130
131 do {
132 int have;
133 stream.avail_out = sizeof(out);
134 stream.next_out = out;
135 ret = inflate(&stream, Z_NO_FLUSH);
136 if(ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
137 inflateEnd(&stream);
138 gf_log_function(engine, "Failed to decompress", "");
139 gf_resource_destroy(resource);
140 return NULL;
141 }
142 have = sizeof(out) - stream.avail_out;
143 memcpy(gf_resource_allocate(resource, have), out, have);
144 cursize += have;
145 } while(stream.avail_out == 0);
146 } while(ret != Z_STREAM_END);
147 inflateEnd(&stream);
148
149 fclose(f);
150
151 gf_log_function(engine, "Compression rate: %.2f%%", (double)cursize / cmpsize * 100);
152
153 for(i = 0;; i++) {
154 unsigned int sz;
156 th = (struct tar_header*)(resource->data + curseek);
157 if(th->filename[0] == 0) break;
158
159 curseek += 512;
160
161 if(memcmp(th->ustar, "ustar", 6) != 0) {
162 gf_log_function(engine, "Not USTAR", "");
163 gf_resource_destroy(resource);
164 resource = NULL;
165 break;
166 }
167
168 sz = tar_getsize(&th->size[0]);
169
170 gf_log_function(engine, "%s: %u bytes", th->filename, sz);
171
172 entry.key = th->filename;
173 entry.address = curseek;
174 entry.size = sz;
175 shputs(resource->entries, entry);
176
177 if(sz != 0) curseek += ((sz / 512) + 1) * 512;
178 }
179
180 return resource;
181}
182
183int gf_resource_get(gf_resource_t* resource, const char* name, void** data, size_t* size) {
184 if(resource->entries != NULL && shgeti(resource->entries, name) != -1) {
185 gf_resource_entry_t e = shgets(resource->entries, name);
186 *size = e.size;
187 *data = malloc(e.size);
188 memcpy(*data, resource->data + e.address, e.size);
189 return 0;
190 }
191 return -1;
192}
193
194void gf_resource_add(gf_resource_t* resource, const char* name, void* data, size_t size, int dir) {
196 struct tar_header* th;
197 unsigned char* d;
198 size_t t;
199 int i;
200
201 d = gf_resource_allocate(resource, 512);
202 th = (struct tar_header*)&d[0];
203
204 strcpy(th->filename, name);
205 strcpy(th->ustar, "ustar");
206 memcpy(th->ustarv, "00", 2);
207 strcpy(th->mode, dir ? "0000755" : "0000644");
208 th->typeflag[0] = dir ? '5' : '0';
209
210 t = size;
211 for(i = 10; i >= 0; i--) {
212 th->size[i] = '0' + (t % 8);
213 t /= 8;
214 }
215
216 t = time(NULL);
217 for(i = 10; i >= 0; i--) {
218 th->mtime[i] = '0' + (t % 8);
219 t /= 8;
220 }
221
222 t = ' ' * sizeof(th->chksum);
223 for(i = 0; i < sizeof(struct tar_header); i++) {
224 t += d[i];
225 }
226 for(i = 6; i >= 0; i--) {
227 th->chksum[i] = '0' + (t % 8);
228 t /= 8;
229 }
230
231 e.key = (char*)name;
232 e.address = resource->size;
233 e.size = size;
234 shputs(resource->entries, e);
235
236 if(size != 0) {
237 d = gf_resource_allocate(resource, ((size / 512) + 1) * 512);
238 memcpy(d, data, size);
239 }
240}
241
242void gf_resource_write(gf_resource_t* resource, const char* path, int progress) {
243 unsigned char out[CHUNK];
244 z_stream stream;
245 int i;
246 FILE* f;
247 stream.zalloc = Z_NULL;
248 stream.zfree = Z_NULL;
249 stream.opaque = Z_NULL;
250 stream.avail_in = 0;
251 stream.next_in = Z_NULL;
252
253 if(deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
254 return;
255 }
256
257 f = fopen(path, "wb");
258 if(f != NULL) {
259 unsigned int totalog = 0;
260 unsigned int totalcmp = 0;
261 unsigned char* in;
262 for(i = 0; i < shlen(resource->entries); i++) {
263 unsigned int sz = resource->entries[i].size == 0 ? 0 : (((resource->entries[i].size / 512) + 1) * 512);
264 in = malloc(512 + sz);
265 memcpy(in, resource->data + resource->entries[i].address - 512, 512 + sz);
266 stream.avail_in = 512 + sz;
267 stream.next_in = in;
268
269 totalog += 512 + sz;
270
271 if(progress) {
272 printf("%s", resource->entries[i].key);
273 fflush(stdout);
274 }
275
276 do {
277 unsigned int have;
278 stream.avail_out = CHUNK;
279 stream.next_out = out;
280 deflate(&stream, Z_NO_FLUSH);
281 have = CHUNK - stream.avail_out;
282 fwrite(out, have, 1, f);
283 if(progress) {
284 printf(".");
285 fflush(stdout);
286 }
287 totalcmp += have;
288 } while(stream.avail_out == 0);
289
290 if(progress) {
291 printf("\n");
292 }
293
294 free(in);
295 }
296
297 in = malloc(512);
298 memset(in, 0, 512);
299 stream.avail_in = 512;
300 stream.next_in = in;
301 totalog += 512;
302
303 do {
304 unsigned int have;
305 stream.avail_out = CHUNK;
306 stream.next_out = out;
307 deflate(&stream, Z_FINISH);
308 have = CHUNK - stream.avail_out;
309 fwrite(out, have, 1, f);
310 totalcmp += have;
311 } while(stream.avail_out == 0);
312
313 free(in);
314 fclose(f);
315
316 if(progress) {
317 printf("Compression rate: %.2f%%\n", (double)totalog / totalcmp * 100);
318 }
319 gf_log_function(resource->engine, "Compression rate: %.2f%%\n", (double)totalog / totalcmp * 100);
320 }
321 deflateEnd(&stream);
322}
323
324void gf_resource_destroy(gf_resource_t* resource) {
325 if(resource->entries != NULL) {
326 shfree(resource->entries);
327 }
328 if(resource->data != NULL) {
329 free(resource->data);
330 }
331 gf_log_function(resource->engine, "Destroyed resource", "");
332 free(resource);
333}
Logger.
#define gf_log_function(engine, fmt,...)
Output log with line number and function name.
Definition gf_log.h:33
Required headers before anything.
Resource.
Engine instance.
Definition core.h:46
Resource entry.
Definition resource.h:45
char * key
Entry name.
Definition resource.h:45
unsigned int size
Size.
Definition resource.h:45
unsigned int address
Location in file.
Definition resource.h:45
Resource.
Definition resource.h:69
gf_resource_entry_t * entries
Resource entries.
Definition resource.h:69
unsigned char * data
Data.
Definition resource.h:69
unsigned int size
Data size.
Definition resource.h:69
gf_engine_t * engine
Engine instance.
Definition resource.h:69
tar header
Definition gf_resource.c:30