1 #include "editor.h"
2 #include "export.h"
3 #include "gfx/gfx.h"
4 #include "gfx/font.h"
5 #include "gui/toolutil.h"
6 #include <stdio.h>
7 #include <math.h>
8 #include <stdlib.h>
9 #include <strings.h>
10
11 #define LIBCONFIG_STATIC
12
13 #include <libconfig.h>
14
15 #define MAGICK_LAYER MAX_LAYERS
16 #define BRUSH_LAYER MAX_LAYERS+1
17 #define CLIPBOARDSIZE 32
18
19 enum
20 {
21 EM_LEVEL,
22 EM_EVENTS
23 };
24
25 Level level;
26 int screen_width = 640, screen_height = 480, screen_scale;
27 int pf_width = 640, pf_height = 480;
28 int saved_layer = 0;
29 int current_layer = 0;
30 int selected_tile = 0;
31 int selected_param = 0;
32 int scroll_x = 0, scroll_y = 0;
33 int drag_x = -1, drag_y = -1, drag_event = -1, selected_event = -1;
34 int edit_mode = EM_LEVEL;
35 int ev_drag_start_x, ev_drag_start_y, drag_start_x, drag_start_y;
36 Uint32 bg_color = 0;
37 GfxDomain *domain = NULL;
38 Font font;
39 GfxSurface *gfx = NULL;
40
41 typedef enum { EVPAR_INT, EVPAR_ENUM } EvParType;
42
43 typedef struct
44 {
45 const char *name;
46 EvParType type;
47 const char **enums;
48 int int_min, int_max;
49 int default_val;
50 } EvParDesc;
51
52 typedef struct
53 {
54 const char *name;
55 Uint32 color;
56 EvParDesc param[EV_PARAMS];
57 } EvDesc;
58
59 static EvDesc *ev_desc = NULL;
60 static int n_ev_desc = 0;
61
62 static const char *tileset = "";
63 TileDescriptor *descriptor;
64 config_t cfg;
65
load_dialog()66 void load_dialog()
67 {
68 FILE *f = open_dialog("rb", "Load level", "lev", domain, gfx, &font, &font, NULL);
69 if (f)
70 {
71
72 level_load(&level, f);
73 fclose(f);
74
75 for (int i = 0 ; i < MAX_LAYERS+2 ; ++i)
76 {
77 level.layer[i].tiles = descriptor;
78 }
79 }
80 }
81
82
load_level(const char * path)83 void load_level(const char *path)
84 {
85 FILE *f = fopen(path, "rb");
86 if (f)
87 {
88
89 level_load(&level, f);
90 fclose(f);
91
92 for (int i = 0 ; i < MAX_LAYERS+2 ; ++i)
93 {
94 level.layer[i].tiles = descriptor;
95 }
96 }
97 }
98
99
load_defs(const char * fn)100 void load_defs(const char *fn)
101 {
102 FILE *f = fn ? fopen(fn, "r") : open_dialog("r", "Load project defs", "cfg", domain, gfx, &font, &font, NULL);
103 if (f)
104 {
105 int r = config_read(&cfg, f);
106
107 fclose(f);
108
109 if (r)
110 {
111 if (!config_lookup_string(&cfg, "tileset", &tileset))
112 warning("No tileset defined");
113 if (!config_lookup_int(&cfg, "bg_color", (Sint32*)&bg_color))
114 warning("No bg_color defined");
115 config_lookup_int(&cfg, "screen.width", &screen_width);
116 config_lookup_int(&cfg, "screen.height", &screen_height);
117 config_lookup_int(&cfg, "screen.scale", &screen_scale);
118 config_lookup_int(&cfg, "playfield.width", &pf_width);
119 config_lookup_int(&cfg, "playfield.height", &pf_height);
120
121 config_setting_t *e = config_lookup(&cfg, "events");
122 if (e)
123 {
124 n_ev_desc = config_setting_length(e);
125 ev_desc = calloc(n_ev_desc, sizeof(ev_desc[0]));
126
127 for (int i = 0 ; i < n_ev_desc ; ++i)
128 {
129 config_setting_t *elem = config_setting_get_elem(e, i);
130 config_setting_lookup_string(elem, "name", &ev_desc[i].name);
131 config_setting_t *params = config_setting_get_member(elem, "params");
132
133 EvDesc *ed = &ev_desc[i];
134
135 ed->color = 0x00ff00;
136
137 config_setting_lookup_int(elem, "color", (Sint32*)&ev_desc[i].color);
138
139 if (params && config_setting_type(params) == CONFIG_TYPE_LIST)
140 {
141 int n_p = config_setting_length(params);
142
143 for (int i = 0 ; i < n_p && i < EV_PARAMS - 3 ; ++i)
144 {
145 EvParDesc *p = &ed->param[i + 1];
146
147 config_setting_t *elem = config_setting_get_elem(params, i);
148 config_setting_lookup_string(elem, "name", &p->name);
149 config_setting_t *enums = config_setting_get_member(elem, "enum");
150
151 if (enums && config_setting_type(enums) == CONFIG_TYPE_ARRAY)
152 {
153 p->type = EVPAR_ENUM;
154 p->int_min = 0;
155 p->int_max = config_setting_length(enums);
156 p->enums = calloc(p->int_max, sizeof(p->enums[0]));
157 for (int e = 0 ; e < p->int_max ; ++e)
158 {
159 p->enums[e] = config_setting_get_string_elem(enums, e);
160 }
161
162 p->int_max--;
163 }
164 else
165 {
166 p->type = EVPAR_INT;
167 config_setting_t *range = config_setting_get_member(elem, "range");
168 if (range && config_setting_type(range) == CONFIG_TYPE_ARRAY)
169 {
170 p->int_min = config_setting_get_int_elem(range, 0);
171 p->int_max = config_setting_get_int_elem(range, 1);
172 }
173 else
174 {
175 p->int_min = 0;
176 p->int_max = 65535;
177 }
178 }
179 }
180 }
181 }
182 }
183 else
184 {
185 warning("No events defined");
186 }
187 }
188 else
189 {
190 warning("Could not read config: (%d) %s", config_error_line(&cfg), config_error_text(&cfg));
191 }
192 }
193 else
194 {
195 warning("cfgfile not found");
196 }
197 }
198
199
save_dialog()200 int save_dialog()
201 {
202 FILE *f = open_dialog("wb", "Save level", "lev", domain, gfx, &font, &font, NULL);
203 if (f)
204 {
205 level.n_layers = MAX_LAYERS;
206
207 level_save(&level, f);
208 fclose(f);
209
210 return 1;
211 }
212 else
213 {
214 return 0;
215 }
216 }
217
draw_rect(GfxDomain * s,int x1,int y1,int x2,int y2,Uint32 color)218 void draw_rect(GfxDomain *s, int x1, int y1, int x2, int y2, Uint32 color)
219 {
220
221 if (x1 > x2) { int temp = x2; x2 = x1; x1 = temp; }
222 if (y1 > y2) { int temp = y2; y2 = y1; y1 = temp; }
223
224 {
225 SDL_Rect rect = {x1, y1, x2-x1, 1};
226 gfx_rect(s, &rect, color);
227 }
228
229 {
230 SDL_Rect rect = {x1, y2, x2-x1, 1};
231 gfx_rect(s, &rect, color);
232 }
233
234 {
235 SDL_Rect rect = {x1, y1, 1, y2-y1};
236 gfx_rect(s, &rect, color);
237 }
238
239 {
240 SDL_Rect rect = {x2, y1, 1, y2-y1};
241 gfx_rect(s, &rect, color);
242 }
243 }
244
245
vector(GfxDomain * screen,int x0,int y0,int x1,int y1,Uint32 color)246 void vector(GfxDomain *screen, int x0, int y0, int x1, int y1, Uint32 color)
247 {
248 gfx_line(screen, x0, y0, x1, y1, color);
249
250 int dx = (x0 - x1);
251 int dy = (y0 - y1);
252 int d = sqrt(dx*dx+dy*dy);
253 if (d == 0) return;
254 dx = dx*16/d;
255 dy = dy*16/d;
256
257 gfx_line(screen, x1, y1, x1 + dy + dx, y1 - dx + dy, color);
258 gfx_line(screen, x1, y1, x1 - dy + dx, y1 + dx + dy, color);
259 }
260
261
draw(GfxDomain * screen,int mouse_x,int mouse_y,int draw_all)262 void draw(GfxDomain *screen, int mouse_x, int mouse_y, int draw_all)
263 {
264 if (draw_all)
265 {
266 for (int i = 0 ; i < MAX_LAYERS ; ++i)
267 {
268 if (level.layer[i].flags & BG_PARALLAX)
269 bg_draw(screen, NULL, &level.layer[i], scroll_x / my_max(1, level.layer[i].prx_mlt_x), scroll_y / my_max(1, level.layer[i].prx_mlt_y));
270 else
271 bg_draw(screen, NULL, &level.layer[i], scroll_x, scroll_y);
272 }
273 }
274 else
275 {
276 int _scroll_x = scroll_x;
277 int _scroll_y = scroll_y;
278 int _drag_x = drag_x;
279 int _drag_y = drag_y;
280
281 if (current_layer >= MAGICK_LAYER)
282 {
283 _scroll_x = my_max(0, my_min(scroll_x, level.layer[current_layer].w * CELLSIZE - screen_width));
284 _scroll_y = my_max(0, my_min(scroll_y, level.layer[current_layer].h * CELLSIZE - screen_height));
285 }
286
287 mouse_x = (mouse_x + ((_scroll_x) & (CELLSIZE-1))) / CELLSIZE;
288 mouse_y = (mouse_y + ((_scroll_y) & (CELLSIZE-1))) / CELLSIZE;
289 _drag_x = (_drag_x + ((_scroll_x) & (CELLSIZE-1))) / CELLSIZE;
290 _drag_y = (_drag_y + ((_scroll_y) & (CELLSIZE-1))) / CELLSIZE;
291
292 bg_draw(screen, NULL, &level.layer[current_layer], _scroll_x, _scroll_y);
293 draw_rect(screen, -_scroll_x-1, -_scroll_y-1, -_scroll_x + level.layer[current_layer].w*CELLSIZE, -_scroll_y + level.layer[current_layer].h*CELLSIZE, 0xffffff);
294
295 if (edit_mode == EM_LEVEL)
296 {
297 if (drag_x != -1)
298 {
299 draw_rect(screen, (_drag_x)*CELLSIZE - ((_scroll_x) & (CELLSIZE-1)), (_drag_y)*CELLSIZE - ((_scroll_y) & (CELLSIZE-1)),
300 (mouse_x+1)*CELLSIZE - ((_scroll_x) & (CELLSIZE-1)), (mouse_y+1)*CELLSIZE - ((_scroll_y) & (CELLSIZE-1)), 0xffffff);
301 }
302 else
303 {
304 draw_rect(screen, (mouse_x)*CELLSIZE - ((_scroll_x) & (CELLSIZE-1)), (mouse_y)*CELLSIZE - ((_scroll_y) & (CELLSIZE-1)),
305 (mouse_x+level.layer[BRUSH_LAYER].w)*CELLSIZE - ((_scroll_x) & (CELLSIZE-1)), (mouse_y+level.layer[BRUSH_LAYER].h)*CELLSIZE - ((_scroll_y) & (CELLSIZE-1)), 0xffffff);
306 }
307 }
308 else
309 {
310 for (int i = 0 ; i < level.n_events ; ++i)
311 {
312 for (int s = 0 ; s < 4 ; s+=2)
313 draw_rect(screen, s + level.event[i].x * CELLSIZE - _scroll_x, s + level.event[i].y * CELLSIZE - _scroll_y,
314 level.event[i].w * CELLSIZE + level.event[i].x * CELLSIZE - _scroll_x - s, level.event[i].h * CELLSIZE + level.event[i].y * CELLSIZE - _scroll_y - s, i == selected_event ? 0xffffff : ev_desc[level.event[i].param[0]].color);
315
316 if (level.event[i].param[EV_NEXT] != -1 && level.event[i].param[EV_NEXT] < level.n_events)
317 {
318 vector(screen, level.event[i].x * CELLSIZE + level.event[i].w*CELLSIZE/2 - _scroll_x, level.event[i].y * CELLSIZE + level.event[i].h*CELLSIZE/2 - _scroll_y,
319 level.event[level.event[i].param[EV_NEXT]].x * CELLSIZE + level.event[level.event[i].param[EV_NEXT]].w*CELLSIZE/2 - _scroll_x, level.event[level.event[i].param[EV_NEXT]].y * CELLSIZE + level.event[level.event[i].param[EV_NEXT]].h*CELLSIZE/2 - _scroll_y, 0xffffff);
320 }
321
322 if (level.event[i].param[EV_TRGPARENT] != -1 && level.event[i].param[EV_TRGPARENT] < level.n_events)
323 {
324 vector(screen, level.event[level.event[i].param[EV_TRGPARENT]].x * CELLSIZE - _scroll_x, level.event[level.event[i].param[EV_TRGPARENT]].y * CELLSIZE - _scroll_y,
325 level.event[i].x * CELLSIZE - _scroll_x, level.event[i].y * CELLSIZE - _scroll_y, 0xff0000);
326 }
327 }
328 }
329 }
330
331 if (current_layer < MAGICK_LAYER)
332 draw_rect(screen, screen->screen_w / 2 - pf_width / 2, screen->screen_h / 2 - pf_height / 2, screen->screen_w / 2 + pf_width / 2, screen->screen_h / 2 + pf_height / 2, 0xa0a040);
333 }
334
335
set_flags(int flip)336 void set_flags(int flip)
337 {
338 if (current_layer < MAGICK_LAYER)
339 level.layer[current_layer].flags ^= flip;
340 }
341
342
resize_layer(int w,int h)343 void resize_layer(int w, int h)
344 {
345 if (current_layer == MAGICK_LAYER) return;
346
347 Background *layer = &level.layer[current_layer];
348
349 if (w <= 0 || h <= 0)
350 {
351 layer->w = my_max(0, layer->w);
352 layer->h = my_max(0, layer->h);
353 if (layer->data) free (layer->data);
354 layer->data = NULL;
355 }
356
357 BgCell * temp = layer->data;
358
359 layer->data = malloc(sizeof(BgCell)*w*h);
360 memset(layer->data, 0, sizeof(BgCell)*w*h);
361
362 if (temp)
363 {
364 for (int y = 0 ; y < h && y < layer->h ; ++y)
365 {
366 for (int x = 0 ; x < w && x < layer->w ; ++x)
367 {
368 memcpy(&layer->data[w*y+x], &temp[x+y*layer->w], sizeof(layer->data[0]));
369 }
370 }
371 free(temp);
372 }
373
374 layer->w = w;
375 layer->h = h;
376
377
378
379 }
380
get_tile(int x,int y)381 void get_tile(int x, int y)
382 {
383 if (current_layer != MAGICK_LAYER)
384 {
385 x += scroll_x;
386 y += scroll_y;
387 }
388
389 x /= CELLSIZE;
390 y /= CELLSIZE;
391
392 if (saved_layer == BRUSH_LAYER || current_layer == BRUSH_LAYER)
393 {
394 if (!(x >= level.layer[current_layer].w || x < 0 || y >= level.layer[current_layer].h || y < 0));
395 selected_tile = level.layer[current_layer].data[x + y*level.layer[current_layer].w].tile;
396
397 return;
398 }
399
400 if (level.layer[current_layer].flags & BG_REPEAT_X)
401 x = (x % level.layer[current_layer].w + level.layer[current_layer].w) % level.layer[current_layer].w;
402
403 if (level.layer[current_layer].flags & BG_REPEAT_Y)
404 y = (y % level.layer[current_layer].h + level.layer[current_layer].h) % level.layer[current_layer].h;
405
406 if (!(x >= level.layer[current_layer].w || x < 0 || y >= level.layer[current_layer].h || y < 0))
407 {
408 int temp = current_layer;
409 current_layer = BRUSH_LAYER;
410 resize_layer(1,1);
411 current_layer = temp;
412 selected_tile = level.layer[BRUSH_LAYER].data[0].tile = level.layer[current_layer].data[x+y*level.layer[current_layer].w].tile;
413 }
414 }
415
416
set_tile(int ax,int ay)417 void set_tile(int ax, int ay)
418 {
419 if (current_layer == MAGICK_LAYER) return;
420
421 if (current_layer == BRUSH_LAYER)
422 {
423 ax /= CELLSIZE;
424 ay /= CELLSIZE;
425
426 if (!(ax >= level.layer[current_layer].w || ax < 0 || ay >= level.layer[current_layer].h || ay < 0))
427 level.layer[current_layer].data[ax + ay*level.layer[current_layer].w].tile = selected_tile;
428
429 return;
430 }
431
432
433 ax += scroll_x;
434 ay += scroll_y;
435
436 int fx = ax < 0;
437 int fy = ay < 0;
438
439
440 ax /= CELLSIZE;
441 ay /= CELLSIZE;
442
443 for (int y = 0 ; y < level.layer[BRUSH_LAYER].h ; ++y)
444 {
445 for (int x = 0 ; x < level.layer[BRUSH_LAYER].w ; ++x)
446 {
447 int _x = x+ax-fx, _y = y+ay-fy;
448
449 if (level.layer[current_layer].flags & BG_REPEAT_X)
450 _x = (_x % level.layer[current_layer].w + level.layer[current_layer].w) % level.layer[current_layer].w;
451
452 if (level.layer[current_layer].flags & BG_REPEAT_Y)
453 _y = (_y % level.layer[current_layer].h + level.layer[current_layer].h) % level.layer[current_layer].h;
454
455 if (!(_x >= level.layer[current_layer].w || _x < 0 || _y >= level.layer[current_layer].h || _y < 0))
456 level.layer[current_layer].data[_x + _y*level.layer[current_layer].w].tile = level.layer[BRUSH_LAYER].data[x+y*level.layer[BRUSH_LAYER].w].tile;
457 }
458 }
459 }
460
461
has_pixels(TileDescriptor * desc)462 int has_pixels(TileDescriptor *desc)
463 {
464 my_lock(desc->surface->surface);
465
466 int result = 0;
467
468 #if SDL_VERSION_ATLEAST(1,3,0)
469 Uint32 key;
470 SDL_GetColorKey(desc->surface->surface, &key);
471 #else
472 const Uint32 key = desc->surface->surface->format->colorkey;
473 #endif
474
475 for (int y = 0 ; y < desc->rect.h ; ++y)
476 {
477 Uint8 *p = (Uint8 *)desc->surface->surface->pixels + ((int)desc->rect.y + y) * desc->surface->surface->pitch + (int)desc->rect.x * desc->surface->surface->format->BytesPerPixel;
478
479 for (int x = 0 ; x < desc->rect.w ; ++x)
480 {
481 //printf("%08x", *(Uint32*)p);
482 if ((*((Uint32*)p)&0xffffff) != key)
483 {
484 ++result;
485 }
486
487 p+=desc->surface->surface->format->BytesPerPixel;
488 }
489 }
490
491 my_unlock(desc->surface->surface);
492
493 return result;
494 }
495
496
init_magic_layer(int w,int h,TileDescriptor * desc,int tiles)497 void init_magic_layer(int w, int h, TileDescriptor *desc, int tiles)
498 {
499 level.layer[BRUSH_LAYER].w = 1;
500 level.layer[BRUSH_LAYER].h = 1;
501 level.layer[BRUSH_LAYER].data = malloc(level.layer[BRUSH_LAYER].w*level.layer[BRUSH_LAYER].h*sizeof(level.layer[BRUSH_LAYER].data[0]));
502 memset(level.layer[BRUSH_LAYER].data, 0, level.layer[BRUSH_LAYER].w*level.layer[BRUSH_LAYER].h*sizeof(level.layer[BRUSH_LAYER].data[0]));
503 level.layer[BRUSH_LAYER].data[0].tile = selected_tile;
504
505 level.layer[MAGICK_LAYER].w = w / CELLSIZE;
506 level.layer[MAGICK_LAYER].h = h / CELLSIZE;
507 level.layer[MAGICK_LAYER].data = malloc(level.layer[MAGICK_LAYER].w*level.layer[MAGICK_LAYER].h*sizeof(level.layer[MAGICK_LAYER].data[0]));
508 memset(level.layer[MAGICK_LAYER].data, 0, level.layer[MAGICK_LAYER].w*level.layer[MAGICK_LAYER].h*sizeof(level.layer[MAGICK_LAYER].data[0]));
509
510 for (int i = 0 ; i < tiles ; ++i)
511 {
512 int x = desc[i].rect.x / CELLSIZE;
513 int y = desc[i].rect.y / CELLSIZE;
514 level.layer[MAGICK_LAYER].data[x+y*level.layer[MAGICK_LAYER].w].tile = has_pixels(&desc[i]) ? i+1 : 0;
515 }
516 }
517
clear_layer()518 void clear_layer()
519 {
520 if (current_layer >= MAGICK_LAYER) return;
521 memset(level.layer[current_layer].data, 0, level.layer[current_layer].w*level.layer[current_layer].h*sizeof(level.layer[current_layer].data[0]));
522 }
523
524
get_brush(int x1,int y1,int x2,int y2)525 void get_brush(int x1, int y1, int x2, int y2)
526 {
527 if (current_layer == BRUSH_LAYER) return;
528
529 if (current_layer < MAGICK_LAYER)
530 {
531 x1 += scroll_x;
532 y1 += scroll_y;
533 x2 += scroll_x;
534 y2 += scroll_y;
535 }
536
537 x1 /= CELLSIZE;
538 y1 /= CELLSIZE;
539 x2 /= CELLSIZE;
540 y2 /= CELLSIZE;
541
542 if (x1 > x2) { int temp = x2; x2 = x1; x1 = temp; }
543 if (y1 > y2) { int temp = y2; y2 = y1; y1 = temp; }
544
545 free(level.layer[BRUSH_LAYER].data);
546 level.layer[BRUSH_LAYER].w = x2 - x1 + 1;
547 level.layer[BRUSH_LAYER].h = y2 - y1 + 1;
548 level.layer[BRUSH_LAYER].data = malloc(level.layer[BRUSH_LAYER].w * level.layer[BRUSH_LAYER].h * sizeof(level.layer[BRUSH_LAYER].data[0]));
549 memset(level.layer[BRUSH_LAYER].data, 0, level.layer[BRUSH_LAYER].w * level.layer[BRUSH_LAYER].h * sizeof(level.layer[BRUSH_LAYER].data[0]));
550
551 for (int y = 0 ; y < level.layer[BRUSH_LAYER].h ; ++y)
552 {
553 for (int x = 0 ; x < level.layer[BRUSH_LAYER].w ; ++x)
554 {
555 int _x = x + x1;
556 int _y = y + y1;
557
558 if (level.layer[current_layer].flags & BG_REPEAT_X)
559 _x = (_x % level.layer[current_layer].w + level.layer[current_layer].w) % level.layer[current_layer].w;
560 else if (_x >= level.layer[current_layer].w) break;
561
562 if (level.layer[current_layer].flags & BG_REPEAT_Y)
563 _y = (_y % level.layer[current_layer].h + level.layer[current_layer].h) % level.layer[current_layer].h;
564 else if (_y >= level.layer[current_layer].h) break;
565
566 level.layer[BRUSH_LAYER].data[x+y*level.layer[BRUSH_LAYER].w].tile =
567 level.layer[current_layer].data[_x+_y*level.layer[current_layer].w].tile;
568 }
569 }
570 }
571
swap(void * a,void * b,size_t size)572 void swap(void *a, void *b, size_t size)
573 {
574 void * ptr = malloc(size);
575 memcpy(ptr, a, size);
576 memcpy(a, b, size);
577 memcpy(b, ptr, size);
578 free(ptr);
579 }
580
581
get_event(int x,int y)582 int get_event(int x, int y)
583 {
584 x += scroll_x;
585 y += scroll_y;
586
587 if (x < 0) x-=CELLSIZE;
588 if (y < 0) y-=CELLSIZE;
589
590 x /= CELLSIZE;
591 y /= CELLSIZE;
592
593 // check for clicks on border
594
595 const int border = 1;
596
597 for (int i = 0 ; i < level.n_events ; ++i)
598 {
599 if (((int)level.event[i].x <= x && (int)level.event[i].y <= y && (int)level.event[i].x+(int)level.event[i].w > x && (int)level.event[i].y+(int)level.event[i].h > y ) &&
600 !((int)level.event[i].x + border <= x && (int)level.event[i].y + border <= y && (int)level.event[i].x+(int)level.event[i].w - border > x && (int)level.event[i].y+(int)level.event[i].h - border > y ))
601 return i;
602 }
603
604 // check for clicks inside the full area
605
606 for (int i = 0 ; i < level.n_events ; ++i)
607 {
608 if ((int)level.event[i].x <= x && (int)level.event[i].y <= y && (int)level.event[i].x+(int)level.event[i].w > x && (int)level.event[i].y+(int)level.event[i].h > y )
609 return i;
610 }
611
612 return -1;
613 }
614
615
move_ev_pos(int x,int y)616 void move_ev_pos(int x, int y)
617 {
618 if (drag_event == -1) return;
619
620 level.event[drag_event].x+=x;
621 level.event[drag_event].y+=y;
622 }
623
624
add_event(int x,int y)625 void add_event(int x, int y)
626 {
627 level.event = realloc(level.event, (level.n_events + 1) * sizeof(*level.event));
628
629 x += scroll_x;
630 y += scroll_y;
631
632 x /= CELLSIZE;
633 y /= CELLSIZE;
634
635 level.event[level.n_events].x = x;
636 level.event[level.n_events].y = y;
637 level.event[level.n_events].w = 1;
638 level.event[level.n_events].h = 1;
639
640
641 if (drag_event != -1)
642 {
643 level.event[level.n_events].w = level.event[drag_event].w;
644 level.event[level.n_events].h = level.event[drag_event].h;
645 memcpy(&level.event[level.n_events].param, &level.event[drag_event].param, sizeof(level.event[drag_event].param));
646 }
647 else
648 {
649 if (n_ev_desc > 0)
650 {
651 for (int i = 0; i < EV_PARAMS ; ++i)
652 level.event[level.n_events].param[i] = ev_desc[0].param[i].default_val;
653 }
654 level.event[level.n_events].param[EV_NEXT] = -1;
655 level.event[level.n_events].param[EV_TRGPARENT] = -1;
656 }
657
658 selected_event = level.n_events;
659
660 ++level.n_events;
661 }
662
663
delete_event()664 void delete_event()
665 {
666 if (selected_event == -1) return;
667
668 memcpy(&level.event[selected_event], &level.event[selected_event+1], (level.n_events - selected_event) * sizeof(*level.event));
669
670 --level.n_events;
671
672 for (int i = 0 ; i < level.n_events ; ++i)
673 {
674 if (level.event[i].param[EV_NEXT] == selected_event)
675 {
676 level.event[i].param[EV_NEXT] = -1;
677 }
678 else if (level.event[i].param[EV_NEXT] > selected_event)
679 {
680 --level.event[i].param[EV_NEXT];
681 }
682
683 if (level.event[i].param[EV_TRGPARENT] == selected_event)
684 {
685 level.event[i].param[EV_TRGPARENT] = -1;
686 }
687 else if (level.event[i].param[EV_TRGPARENT] > selected_event)
688 {
689 --level.event[i].param[EV_TRGPARENT];
690 }
691 }
692
693 if (selected_event >= level.n_events) --selected_event;
694 }
695
696
shift_layer(int dx,int dy)697 void shift_layer(int dx, int dy)
698 {
699 int w = level.layer[current_layer].w;
700 int h = level.layer[current_layer].h;
701
702 BgCell *temp = malloc(sizeof(BgCell)*w*h);
703
704 for (int y = 0 ; y < h ; ++y)
705 for (int x = 0 ; x < w ; ++x)
706 {
707 memcpy(&temp[(x + dx + w) % w + ((y + dy + h) % h) * w], &level.layer[current_layer].data[x+y*w], sizeof(BgCell));
708 }
709
710 memcpy(level.layer[current_layer].data, temp, w * h * sizeof(BgCell));
711
712 free(temp);
713 }
714
715
shift_events(int dx,int dy)716 void shift_events(int dx,int dy)
717 {
718 for (int i = 0 ; i < level.n_events ; ++i)
719 {
720 level.event[i].x += dx;
721 level.event[i].y += dy;
722 }
723 }
724
725
double_layer()726 void double_layer()
727 {
728 int w = level.layer[current_layer].w;
729 int h = level.layer[current_layer].h;
730
731 BgCell *temp = malloc(sizeof(BgCell) * (w * 2) * (h * 2));
732
733 for (int y = 0 ; y < h * 2 ; ++y)
734 for (int x = 0 ; x < w * 2 ; ++x)
735 {
736 memcpy(&temp[x + y * (w * 2)], &level.layer[current_layer].data[(x / 2) + (y / 2) * w], sizeof(BgCell));
737 }
738
739 BgCell *temp2 = level.layer[current_layer].data;
740 level.layer[current_layer].data = temp;
741
742 level.layer[current_layer].w *= 2;
743 level.layer[current_layer].h *= 2;
744
745 free(temp2);
746 }
747
748
insert_rowcol(int sx,int sy,int dx,int dy)749 void insert_rowcol(int sx, int sy, int dx, int dy)
750 {
751 debug("Inserting row/col at %d,%d", sx, sy);
752 resize_layer(level.layer[current_layer].w + dx, level.layer[current_layer].h + dy);
753
754 for (int x = level.layer[current_layer].w - 1 ; x >= sx + dx ; --x)
755 {
756 for (int y = 0 ; y < level.layer[current_layer].h ; ++y)
757 {
758 level.layer[current_layer].data[x + y * level.layer[current_layer].w].tile =
759 level.layer[current_layer].data[x + y * level.layer[current_layer].w - dx].tile;
760 }
761 }
762
763 for (int x = 0 ; x < level.layer[current_layer].w ; ++x)
764 {
765 for (int y = level.layer[current_layer].h - 1 ; y >= sy + dy ; --y)
766 {
767 level.layer[current_layer].data[x + y * level.layer[current_layer].w].tile =
768 level.layer[current_layer].data[x + (y - dy) * level.layer[current_layer].w].tile;
769 }
770 }
771
772 }
773
774
resize_event(int dx,int dy)775 void resize_event(int dx,int dy)
776 {
777 if (selected_event == -1) return;
778
779 if (dx < 0 && level.event[selected_event].w > 1) level.event[selected_event].w += dx;
780 if (dy < 0 && level.event[selected_event].h > 1) level.event[selected_event].h += dy;
781 if (dx > 0 && level.event[selected_event].w < 65535) level.event[selected_event].w += dx;
782 if (dy > 0 && level.event[selected_event].h < 65535) level.event[selected_event].h += dy;
783 }
784
785 #undef main
786
main(int argc,char ** argv)787 int main(int argc, char **argv)
788 {
789 SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE);
790 atexit(SDL_Quit);
791
792 domain = gfx_create_domain("Editor", 0, 640, 480, 1);
793 domain->screen_w = 640;
794 domain->screen_h = 480;
795 domain->fps = 20;
796 domain->scale = 1;
797 gfx_domain_update(domain, true);
798
799 gfx = gfx_load_surface(domain, "bevel.bmp", GFX_KEYED);
800 font_load_file(domain, &font, "8x8.fnt");
801
802 config_init(&cfg);
803
804 load_defs(argc > 1 ? argv[1] : NULL);
805
806 if (strcmp(tileset,"") == 0)
807 {
808 fatal("no tileset specified");
809 return 1;
810 }
811
812 if (argc > 2) load_level(argv[2]);
813
814 domain->screen_w = screen_width;
815 domain->screen_h = screen_height;
816 domain->scale = screen_scale;
817 gfx_domain_update(domain, true);
818
819 GfxSurface *tiles = gfx_load_surface(domain, tileset, GFX_KEYED);
820
821 if (!tiles)
822 {
823 fatal("tileset not found");
824 return 2;
825 }
826
827 descriptor = gfx_build_tiledescriptor(tiles, CELLSIZE, CELLSIZE, NULL);
828
829 for (int i = 0 ; i < MAX_LAYERS+2 ; ++i)
830 {
831 level.layer[i].tiles = descriptor;
832 }
833
834 level.n_layers = MAX_LAYERS;
835
836 init_magic_layer(tiles->surface->w, tiles->surface->h, descriptor, (tiles->surface->w/CELLSIZE) * (tiles->surface->h/CELLSIZE));
837
838 int done = 0;
839
840 while (1)
841 {
842 SDL_Event e;
843
844 int got_event = 0;
845
846 while (SDL_PollEvent(&e))
847 {
848 got_event = 1;
849 switch (e.type)
850 {
851 case SDL_QUIT:
852 done = 1;
853 break;
854
855 case SDL_MOUSEMOTION:
856 {
857 if (e.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
858 {
859 scroll_x -= e.motion.x/domain->scale - drag_start_x;
860 scroll_y -= e.motion.y/domain->scale - drag_start_y;
861 drag_start_x = e.button.x / domain->scale;
862 drag_start_y = e.button.y / domain->scale;
863 }
864
865
866 if (edit_mode == EM_LEVEL && e.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT) && drag_x == -1)
867 {
868 set_tile(e.motion.x / domain->scale, e.motion.y / domain->scale);
869 }
870 else if (edit_mode == EM_EVENTS && e.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT) && drag_event != -1)
871 {
872 move_ev_pos(e.motion.x / CELLSIZE / domain->scale - ev_drag_start_x, e.motion.y / CELLSIZE / domain->scale - ev_drag_start_y);
873 ev_drag_start_x = e.motion.x / CELLSIZE / domain->scale;
874 ev_drag_start_y = e.motion.y / CELLSIZE / domain->scale;
875 }
876 }
877 break;
878
879 case SDL_MOUSEBUTTONUP:
880 {
881 if (edit_mode == EM_LEVEL && drag_x != -1)
882 {
883 get_brush(drag_x, drag_y, e.button.x / domain->scale, e.button.y / domain->scale);
884 drag_x = -1;
885 selected_event = drag_event = -1;
886 }
887 else if (edit_mode == EM_EVENTS && drag_event != -1)
888 {
889 drag_event = -1;
890 }
891 }
892 break;
893
894 case SDL_MOUSEBUTTONDOWN:
895 drag_start_x = e.button.x / domain->scale;
896 drag_start_y = e.button.y / domain->scale;
897 if (edit_mode == EM_LEVEL)
898 {
899 selected_event = drag_event = -1;
900 if (e.button.button == (SDL_BUTTON_LEFT))
901 {
902 const Uint8 * keys = SDL_GetKeyboardState(NULL);
903 if (keys[SDL_SCANCODE_LSHIFT]||keys[SDL_SCANCODE_RSHIFT])
904 {
905 drag_x = e.button.x / domain->scale;
906 drag_y = e.button.y / domain->scale;
907 }
908 else
909 {
910 set_tile(e.button.x / domain->scale, e.button.y / domain->scale);
911 }
912 }
913 if ((e.button.button == (SDL_BUTTON_LEFT) && current_layer == MAGICK_LAYER))
914 {
915 get_tile(e.button.x / domain->scale, e.button.y / domain->scale);
916 }
917 }
918 else
919 {
920 const Uint8 * keys = SDL_GetKeyboardState(NULL);
921 if (selected_event != -1 && (keys[SDL_SCANCODE_LCTRL]||keys[SDL_SCANCODE_RCTRL]))
922 {
923 level.event[selected_event].param[(keys[SDL_SCANCODE_LSHIFT]||keys[SDL_SCANCODE_RSHIFT])?EV_TRGPARENT:EV_NEXT] = get_event(e.button.x / domain->scale, e.button.y / domain->scale);
924 }
925 else
926 {
927 drag_event = selected_event = get_event(e.button.x / domain->scale, e.button.y / domain->scale);
928 ev_drag_start_x = e.button.x / CELLSIZE / domain->scale;
929 ev_drag_start_y = e.button.y / CELLSIZE / domain->scale;
930 }
931 }
932 break;
933
934
935 case SDL_KEYDOWN:
936
937 if ((e.key.keysym.mod & KMOD_SHIFT) && (e.key.keysym.mod & KMOD_CTRL))
938 {
939 if (e.key.keysym.sym == SDLK_i)
940 {
941 int x,y;
942 SDL_GetMouseState(&x, &y);
943 insert_rowcol((x / domain->scale + scroll_x) / CELLSIZE, (y / domain->scale + scroll_y) / CELLSIZE, 1, 0);
944 }
945 else if (edit_mode == EM_EVENTS)
946 {
947 switch (e.key.keysym.sym)
948 {
949 default:break;
950 case SDLK_UP:
951
952 shift_events(0,-1);
953
954 break;
955
956 case SDLK_DOWN:
957
958 shift_events(0,1);
959
960 break;
961
962 case SDLK_LEFT:
963
964 shift_events(-1,0);
965
966 break;
967
968 case SDLK_RIGHT:
969
970 shift_events(1,0);
971
972 break;
973 }
974 }
975 else
976 {
977 switch (e.key.keysym.sym)
978 {
979 default:break;
980 case SDLK_UP:
981
982 shift_layer(0,-1);
983
984 break;
985
986 case SDLK_DOWN:
987
988 shift_layer(0,1);
989
990 break;
991
992 case SDLK_LEFT:
993
994 shift_layer(-1,0);
995
996 break;
997
998 case SDLK_RIGHT:
999
1000 shift_layer(1,0);
1001
1002 break;
1003 }
1004 }
1005 }
1006 else if (e.key.keysym.mod & KMOD_CTRL)
1007 {
1008 if (e.key.keysym.sym == SDLK_F9)
1009 level_export(&level);
1010 else if (e.key.keysym.sym == SDLK_s)
1011 save_dialog();
1012 else if (e.key.keysym.sym == SDLK_o)
1013 load_dialog();
1014 else if (e.key.keysym.sym == SDLK_i)
1015 {
1016 int x,y;
1017 SDL_GetMouseState(&x, &y);
1018 insert_rowcol((x / domain->scale + scroll_x) / CELLSIZE, (y / domain->scale + scroll_y) / CELLSIZE, 0, 1);
1019 }
1020 else if (edit_mode == EM_EVENTS)
1021 {
1022 switch (e.key.keysym.sym)
1023 {
1024 default:break;
1025 case SDLK_UP:
1026
1027 resize_event(0,-1);
1028
1029 break;
1030
1031 case SDLK_DOWN:
1032
1033 resize_event(0,1);
1034
1035 break;
1036
1037 case SDLK_LEFT:
1038
1039 resize_event(-1,0);
1040
1041 break;
1042
1043 case SDLK_RIGHT:
1044
1045 resize_event(1,0);
1046
1047 break;
1048 }
1049 }
1050 else
1051 {
1052 switch (e.key.keysym.sym)
1053 {
1054 case SDLK_UP:
1055
1056 if (level.layer[current_layer].h > 0)
1057 resize_layer(level.layer[current_layer].w, level.layer[current_layer].h-1);
1058
1059 break;
1060
1061 case SDLK_DOWN:
1062
1063 resize_layer(level.layer[current_layer].w, level.layer[current_layer].h+1);
1064
1065 break;
1066
1067 case SDLK_LEFT:
1068
1069 if (level.layer[current_layer].w > 0)
1070 resize_layer(level.layer[current_layer].w-1, level.layer[current_layer].h);
1071
1072 break;
1073
1074 case SDLK_RIGHT:
1075
1076 resize_layer(level.layer[current_layer].w+1, level.layer[current_layer].h);
1077
1078 break;
1079
1080 case SDLK_F10:
1081 clear_layer();
1082 break;
1083
1084 default:
1085 break;
1086 }
1087 }
1088 }
1089 else
1090 {
1091 if (edit_mode == EM_LEVEL)
1092 {
1093 switch (e.key.keysym.sym)
1094 {
1095 case SDLK_h:
1096 double_layer();
1097 break;
1098
1099 case SDLK_PAGEUP:
1100 {
1101 if (current_layer > 0)
1102 {
1103 swap(&level.layer[current_layer], &level.layer[current_layer-1], sizeof(level.layer[current_layer]));
1104 --current_layer;
1105 }
1106 }
1107 break;
1108
1109 case SDLK_PAGEDOWN:
1110 {
1111 if (current_layer < MAX_LAYERS - 1)
1112 {
1113 swap(&level.layer[current_layer], &level.layer[current_layer+1], sizeof(level.layer[current_layer]));
1114 ++current_layer;
1115 }
1116 }
1117 break;
1118
1119 case SDLK_e:
1120 edit_mode = edit_mode == EM_LEVEL ? EM_EVENTS : EM_LEVEL;
1121 break;
1122
1123 case SDLK_1:
1124 case SDLK_2:
1125 case SDLK_3:
1126 case SDLK_4:
1127 case SDLK_5:
1128 case SDLK_6:
1129 case SDLK_7:
1130 case SDLK_8:
1131
1132 current_layer = e.key.keysym.sym-SDLK_1;
1133
1134 break;
1135
1136 case SDLK_b:
1137
1138 current_layer = BRUSH_LAYER;
1139
1140 break;
1141
1142 case SDLK_RALT:
1143 case SDLK_LALT:
1144 if (e.key.repeat)
1145 break;
1146 saved_layer = current_layer;
1147 current_layer = MAGICK_LAYER;
1148 break;
1149
1150 case SDLK_UP:
1151
1152 scroll_y -= 4;
1153
1154 break;
1155
1156 case SDLK_DOWN:
1157
1158 scroll_y += 4;
1159
1160 break;
1161
1162 case SDLK_LEFT:
1163
1164 scroll_x -= 4;
1165
1166 break;
1167
1168 case SDLK_RIGHT:
1169
1170 scroll_x += 4;
1171
1172 break;
1173
1174 case SDLK_m:
1175
1176 level.layer[current_layer].prx_mlt_x = (level.layer[current_layer].prx_mlt_x + 1) % 17;
1177
1178 break;
1179
1180 case SDLK_n:
1181
1182 level.layer[current_layer].prx_mlt_y = (level.layer[current_layer].prx_mlt_y + 1) % 17;
1183
1184 break;
1185
1186 case SDLK_p:
1187
1188 set_flags(BG_PARALLAX);
1189
1190 break;
1191
1192 case SDLK_x:
1193
1194 set_flags(BG_REPEAT_X);
1195
1196 break;
1197
1198 case SDLK_y:
1199
1200 set_flags(BG_REPEAT_Y);
1201
1202 break;
1203
1204 default: break;
1205 }
1206 }
1207 else
1208 {
1209
1210 {
1211 switch (e.key.keysym.sym)
1212 {
1213 case SDLK_e:
1214 edit_mode = edit_mode == EM_LEVEL ? EM_EVENTS : EM_LEVEL;
1215 break;
1216
1217 case SDLK_INSERT:
1218 {
1219 int x,y;
1220 SDL_GetMouseState(&x, &y);
1221 add_event(x / domain->scale, y / domain->scale);
1222 }
1223 break;
1224
1225 case SDLK_DELETE:
1226
1227 delete_event();
1228
1229 break;
1230
1231
1232 case SDLK_PERIOD:
1233 case SDLK_COMMA:
1234 if (selected_event == -1) break;
1235
1236 level.event[selected_event].param[selected_param] += e.key.keysym.sym == SDLK_COMMA ? -1 : 1;
1237
1238 if (selected_param == 0)
1239 {
1240 level.event[selected_event].param[selected_param] = my_max(0, my_min(n_ev_desc - 1,level.event[selected_event].param[selected_param]));
1241
1242 for (int i = 1 ; i < EV_PARAMS - 2 ; ++i)
1243 level.event[selected_event].param[i] = my_max(ev_desc[level.event[selected_event].param[0]].param[i].int_min,
1244 my_min(ev_desc[level.event[selected_event].param[0]].param[i].int_max ,level.event[selected_event].param[i]));
1245 }
1246 else
1247 {
1248 if (selected_param != EV_NEXT && selected_param != EV_TRGPARENT)
1249 level.event[selected_event].param[selected_param] = my_max(ev_desc[level.event[selected_event].param[0]].param[selected_param].int_min,
1250 my_min(ev_desc[level.event[selected_event].param[0]].param[selected_param].int_max ,level.event[selected_event].param[selected_param]));
1251 }
1252 break;
1253
1254 case SDLK_PAGEUP:
1255 selected_param = (selected_param - 1) & (EV_PARAMS-1);
1256 break;
1257
1258 case SDLK_PAGEDOWN:
1259 selected_param = (selected_param + 1) & (EV_PARAMS-1);
1260 break;
1261
1262 case SDLK_UP:
1263
1264 scroll_y -= 4;
1265
1266 break;
1267
1268 case SDLK_DOWN:
1269
1270 scroll_y += 4;
1271
1272 break;
1273
1274 case SDLK_LEFT:
1275
1276 scroll_x -= 4;
1277
1278 break;
1279
1280 case SDLK_RIGHT:
1281
1282 scroll_x += 4;
1283
1284 break;
1285
1286 default: break;
1287 }
1288 }
1289 }
1290 }
1291
1292 break;
1293
1294 case SDL_KEYUP:
1295 if(edit_mode == EM_LEVEL)
1296 {
1297 switch (e.key.keysym.sym)
1298 {
1299
1300 case SDLK_RALT:
1301 case SDLK_LALT:
1302 current_layer = saved_layer;
1303 break;
1304
1305 default: break;
1306 }
1307 }
1308 break;
1309 }
1310 }
1311
1312 if (got_event)
1313 {
1314
1315 const Uint8 * keys = SDL_GetKeyboardState(NULL);
1316 int x,y;
1317 SDL_GetMouseState(&x, &y);
1318
1319 int show_all_layers = keys[SDL_SCANCODE_A];
1320
1321 gfx_rect(domain, NULL, bg_color);
1322 draw(domain, x / domain->scale, y / domain->scale, show_all_layers);
1323
1324 static const char *layer_names[] =
1325 {
1326 "Tiles",
1327 "Brush"
1328 };
1329
1330
1331 char text[100], si[10];
1332 SDL_Rect textpos = {domain->screen_w - 400,0, 1000, 1000};
1333
1334 if (edit_mode == EM_LEVEL)
1335 {
1336 //if (current_layer < MAGICK_LAYER)
1337 {
1338 sprintf(si, "%d", current_layer);
1339 sprintf(text, "[L %s] prx(%s) pos(%d,%d) size(%dx%d,%dx%d)\n", show_all_layers?"All":(current_layer>=MAGICK_LAYER?layer_names[current_layer-MAGICK_LAYER]:si), level.layer[current_layer].flags&BG_PARALLAX?"ON":"OFF", scroll_x/CELLSIZE, scroll_y/CELLSIZE, level.layer[current_layer].w, level.layer[current_layer].prx_mlt_x, level.layer[current_layer].h, level.layer[current_layer].prx_mlt_y);
1340
1341 font_write(&font, domain, &textpos, text);
1342 textpos.y += font.h;
1343 }
1344 }
1345 else
1346 {
1347 if (selected_event == -1)
1348 {
1349 sprintf(text, "[EV] pos(%d,%d)\n", scroll_x/CELLSIZE, scroll_y/CELLSIZE);
1350 font_write(&font, domain, &textpos, text);
1351 textpos.y += font.h;
1352 }
1353 else
1354 {
1355 sprintf(text, "[EV:%02x(%d,%d %d,%d)] pos(%d,%d)\n", selected_event, level.event[selected_event].x, level.event[selected_event].y, level.event[selected_event].w, level.event[selected_event].h, scroll_x/CELLSIZE, scroll_y/CELLSIZE);
1356 font_write(&font, domain, &textpos, text);
1357 textpos.y += font.h;
1358
1359 for (int i = 0 ; i < EV_PARAMS ; ++i)
1360 {
1361 char s = ' ';
1362
1363 if (i == selected_param) s = '�';
1364
1365 if (i == 0)
1366 {
1367 snprintf(text, 100, "%-9s: %s", "Type", ev_desc[level.event[selected_event].param[i]].name);
1368 }
1369 else if (i == EV_TRGPARENT)
1370 {
1371 snprintf(text, 100, "%-9s: %4d", "TrgParent", level.event[selected_event].param[i]);
1372 }
1373 else if (i == EV_NEXT)
1374 {
1375 snprintf(text, 100, "%-9s: %4d", "Next", level.event[selected_event].param[i]);
1376 }
1377 else
1378 {
1379 if (ev_desc[level.event[selected_event].param[0]].param[i].type == EVPAR_ENUM)
1380 snprintf(text, 100, "%-9s: %s", ev_desc[level.event[selected_event].param[0]].param[i].name, ev_desc[level.event[selected_event].param[0]].param[i].enums[level.event[selected_event].param[i]]);
1381 else
1382 snprintf(text, 100, "%-9s: %4d", ev_desc[level.event[selected_event].param[0]].param[i].name, level.event[selected_event].param[i]);
1383 }
1384
1385 font_write_args(&font, domain, &textpos, "%c%s", s, text);
1386 textpos.y += font.h;
1387 }
1388 }
1389
1390
1391 }
1392
1393 gfx_domain_flip(domain);
1394 }
1395 else
1396 {
1397 SDL_Delay(1);
1398 }
1399
1400 if (done)
1401 {
1402 int r = confirm_ync(domain, gfx, &font, "Save level?");
1403
1404 if (r == 0) done = 0;
1405 if (r == -1) goto out;
1406 if (r == 1) { if (!save_dialog()) done = 0; else break; }
1407 }
1408 }
1409
1410 out:
1411
1412 font_destroy(&font);
1413 config_destroy(&cfg);
1414 free(ev_desc);
1415
1416 return 0;
1417 }
1418