maug
Quick and dirty C mini-augmentation library.
retrotil.h
Go to the documentation of this file.
1
2#ifndef RETROTIL_H
3#define RETROTIL_H
4
5#include <mjson.h>
6#include <mfile.h>
7
17#define RETROTILE_PRESENT
18
19#ifndef RETROTILE_TRACE_CHARS
20# define RETROTILE_TRACE_CHARS 0
21#endif /* !RETROTILE_TRACE_TOKENS */
22
23#ifndef RETROTILE_NAME_SZ_MAX
25# define RETROTILE_NAME_SZ_MAX 10
26#endif /* !RETROTILE_NAME_SZ_MAX */
27
28#ifndef RETROTILE_PROP_NAME_SZ_MAX
30# define RETROTILE_PROP_NAME_SZ_MAX 10
31#endif /* !RETROTILE_PROP_NAME_SZ_MAX */
32
33#ifndef RETROTILE_TILE_SCALE_DEFAULT
35# define RETROTILE_TILE_SCALE_DEFAULT 1.0f
36#endif /* !RETROTILE_TILE_SCALE_DEFAULT */
37
38#ifndef RETROTILE_TRACE_LVL
40# define RETROTILE_TRACE_LVL 0
41#endif /* !RETROTILE_TRACE_LVL */
42
43#ifndef RETROTILE_VORONOI_DEFAULT_SPB
44# define RETROTILE_VORONOI_DEFAULT_SPB 8
45#endif /* !RETROTILE_VORONOI_DEFAULT_SPB */
46
47#ifndef RETROTILE_VORONOI_DEFAULT_DRIFT
48# define RETROTILE_VORONOI_DEFAULT_DRIFT 4
49#endif /* !RETROTILE_VORONOI_DEFAULT_DRIFT */
50
51#ifdef MPARSER_TRACE_NAMES
52# define retrotile_mstate_name( state ) gc_retrotile_mstate_names[state]
53#else
54# define retrotile_mstate_name( state ) state
55#endif /* MPARSER_TRACE_NAMES */
56
57#ifndef RETROTILE_PARSER_FLAG_LITERAL_PATHS
63# define RETROTILE_PARSER_FLAG_LITERAL_PATHS 0x02
64#endif /* !RETROTILE_PARSER_FLAG_LITERAL_PATHS */
65
71#define RETROTILE_PARSER_MODE_MAP 0
72
78#define RETROTILE_PARSER_MODE_DEFS 1
79
80#define RETROTILE_CLASS_TABLE( f ) \
81 f( TILE, tile, 0 ) \
82 f( MOBILE, mobile, 1 ) \
83 f( WARP, warp, 2 ) \
84 f( ITEM, item, 3 ) \
85 f( CROP, crop, 4 )
86
101#define RETROTILE_TILE_FLAG_BLOCK 0x01
102
107#define RETROTILE_TILE_FLAG_RESERVED1 0x02
108
113#define RETROTILE_TILE_FLAG_RESERVED2 0x04
114
119#define RETROTILE_TILE_FLAG_RESERVED3 0x08
120 /* retrotile_defs_types */
122
127#define RETROTILE_PROP_TYPE_OTHER 0
128
133#define RETROTILE_PROP_TYPE_STRING 1
134
139#define RETROTILE_PROP_TYPE_FILE 2
140
145#define RETROTILE_PROP_TYPE_INT 3
146
157#define RETROTILE_DS_FLAG_INIT_DATA 0x02
158
162#define RETROTILE_IDX_FMT "%u"
163
166 size_t sz;
167 uint8_t flags;
168 maug_path image_path;
169 size_t x;
170 size_t y;
171 uint16_t tile_class;
172 char warp_dest[RETROTILE_NAME_SZ_MAX + 1];
179 retrotile_coord_t warp_x;
180 retrotile_coord_t warp_y;
182 int8_t no_serial;
183 RETROFLAT_COLOR color;
184#ifdef RETROFLAT_3D
185 /* TODO: Work this into retrogxc? */
186 struct RETRO3DP_MODEL model;
187#endif /* RETROFLAT_3D */
188#ifdef RETROGXC_PRESENT
189 ssize_t image_cache_id;
190#else
191 /* TODO: Don't exclude this if the cache is disabled; see retrogui image ctl. */
192 struct RETROFLAT_BITMAP image;
193#endif /* RETROGXC_PRESENT */
194};
195 /* retrotile_defs */
197
200 size_t sz;
202 size_t total_sz;
203 uint16_t layer_class;
204};
205
211 /* \brief X position in tilemap tiles. */
212 retrotile_coord_t x;
213 /* \brief Y position in tilemap tiles. */
214 retrotile_coord_t y;
215};
216
222struct RETROTILE {
224 size_t sz;
225 char name[RETROTILE_NAME_SZ_MAX + 1];
226 char tileset[RETROTILE_NAME_SZ_MAX + 1];
228 uint32_t total_sz;
230 uint32_t layers_count;
234 size_t tiles_h;
236 size_t tiles_w;
242};
243
250 int16_t sect_x;
252 int16_t sect_y;
254 int16_t sect_w;
256 int16_t sect_h;
258 int16_t sect_w_half;
260 int16_t sect_h_half;
261 retroflat_tile_t highest_generated;
262 retroflat_tile_t lowest_generated;
263};
264
266 int16_t tiles_changed;
267 retroflat_tile_t center;
268 retroflat_tile_t outside;
271};
272
273#define retrotile_get_tile( tilemap, layer, x, y ) \
274 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
275
276#define retrotile_set_tile( tilemap, layer, x, y, new_val ) \
277 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
278
279#define retrotile_get_tiles_p( layer ) \
280 ((retroflat_tile_t*)(((uint8_t*)(layer)) + \
281 sizeof( struct RETROTILE_LAYER )))
282
283#ifdef MAUG_NO_STDLIB
284/* Loop is slower, but doesn't use memset. */
285/* TODO: This kinda assumes retroflat_tile_t is 8 bits... Fix that! */
286# define retrotile_clear_tiles( t, layer, i ) \
287 assert( 1 == sizeof( retroflat_tile_t ) ); \
288 for( \
289 i = 0 ; \
290 (t)->tiles_w * (t)->tiles_h * sizeof( retroflat_tile_t ) > i ; \
291 i++ \
292 ) { \
293 retrotile_get_tiles_p( layer )[i] = -1; \
294 }
295#else
296# define retrotile_clear_tiles( t, layer, i ) \
297 memset( retrotile_get_tiles_p( layer ), -1, \
298 (t)->tiles_w * (t)->tiles_h * sizeof( retroflat_tile_t ) )
299#endif /* MAUG_NO_STDLIB */
300
306typedef MERROR_RETVAL (*retrotile_tj_parse_cb)(
307 const char* dirname, const char* filename, MAUG_MHANDLE* p_tm_h,
308 struct MDATA_VECTOR* p_td, mparser_wait_cb_t wait_cb, void* wait_data,
309 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
310 uint8_t flags );
311
313 uint8_t mstate;
314 uint16_t flags;
315 /* TODO: Use flags and combine these. */
316 uint8_t pass;
317 uint8_t passes_max;
319 uint8_t mode;
320 mparser_wait_cb_t wait_cb;
321 void* wait_data;
322 retroflat_ms_t wait_last;
323 size_t layer_tile_iter;
326 int last_prop_type;
327 size_t last_prop_name_sz;
330 char tileset_name[RETROTILE_NAME_SZ_MAX + 1];
331 size_t pass_layer_iter;
337 size_t tiles_w;
338 size_t tiles_h;
339 struct RETROTILE* t;
340 retrotile_tj_parse_cb tj_parse_cb;
346 mparser_parse_token_cb custom_token_cb;
347 void* custom_token_cb_data;
348 struct MJSON_PARSER jparser;
349 struct MDATA_VECTOR* p_tile_defs;
350 maug_path dirname;
351 uint16_t layer_class;
352};
353
354/* State Idx JSONKeyWord Parent ParseMode */
355#define RETROTILE_PARSER_MSTATE_TABLE( f ) \
356 f( MTILESTATE_NONE, 0, "", 0, 0 ) \
357 f( MTILESTATE_HEIGHT, 1, "height", 0 , 0 ) \
358 f( MTILESTATE_WIDTH, 2, "width", 0 , 0 ) \
359 f( MTILESTATE_LAYERS, 3, "layers", 0 , 0 ) \
360 f( MTILESTATE_LAYER_DATA, 4, "data", 15 /* LAYER */ , 0 ) \
361 f( MTILESTATE_LAYER_NAME, 5, "name", 15 /* LAYER */ , 0 ) \
362 f( MTILESTATE_TILES, 6, "tiles", 0 , 1 ) \
363 f( MTILESTATE_TILES_ID, 7, "id", 6 /* TILES */ , 1 ) \
364 f( MTILESTATE_TILES_IMAGE, 8, "image", 6 /* TILES */ , 1 ) \
365 f( MTILESTATE_TILESETS, 9, "tilesets", 0 , 0 ) \
366 f( MTILESTATE_TILESETS_SRC, 10, "source", 9 /* TILESETS */ , 0 ) \
367 f( MTILESTATE_TILESETS_FGID, 11, "firstgid", 9 /* TILESETS */ , 0 ) \
368 f( MTILESTATE_TILESETS_PROP, 12, "firstgid", 9 /* TILESETS */ , 0 ) \
369 f( MTILESTATE_GRID, 13, "grid", 0 , 1 ) \
370 f( MTILESTATE_TILES_PROP, 14, "properties", 6 /* TILES */ , 1 ) \
371 f( MTILESTATE_LAYER, 15, "layers", /* [sic] */ 3 , 0 ) \
372 f( MTILESTATE_TILES_PROP_NAME, 16, "name", 14 /* TIL_PROP */ , 1 ) \
373 f( MTILESTATE_TILES_PROP_TYPE, 17, "type", 14 /* TIL_PROP */ , 1 ) \
374 f( MTILESTATE_TILES_PROP_VAL, 18, "value", 14 /* TIL_PROP */ , 1 ) \
375 f( MTILESTATE_PROP, 19, "properties", 0 /* NONE */ , 0 ) \
376 f( MTILESTATE_PROP_NAME, 20, "name", 19 /* PROP */ , 0 ) \
377 f( MTILESTATE_PROP_TYPE, 21, "type", 19 /* PROP */ , 0 ) \
378 f( MTILESTATE_PROP_VAL, 22, "value", 19 /* PROP */ , 0 ) \
379 f( MTILESTATE_LAYER_CLASS, 23, "class", 15 /* LAYER */ , 0 ) \
380 f( MTILESTATE_TILES_CLASS, 24, "type", 6 /* TILES */ , 1 ) \
381 f( MTILESTATE_NAME, 25, "name", 0 , 1 ) \
382 f( MTILESTATE_WANGSETS, 26, "wangsets", 0 , 1 ) \
383 f( MTILESTATE_TPROP, 27, "properties", 0 /* NONE */ , 1 ) \
384 f( MTILESTATE_TPROP_NAME, 28, "name", 27 /* PROP */ , 1 ) \
385 f( MTILESTATE_TPROP_TYPE, 29, "type", 27 /* PROP */ , 1 ) \
386 f( MTILESTATE_TPROP_VAL, 30, "value", 27 /* PROP */ , 1 )
387
388/* TODO: Mine wangsets for slowdown values, etc. */
389
391retrotile_parse_json_c( struct RETROTILE_PARSER* parser, char c );
392
397int retrotile_parse_prop_type( const char* token, size_t token_sz );
398
406mfix_t retrotile_static_rotation_from_dir( const char* dir );
407
433 const maug_path dirname, const char* filename, MAUG_MHANDLE* p_tilemap_h,
434 struct MDATA_VECTOR* p_tile_defs,
435 mparser_wait_cb_t wait_cb, void* wait_data,
436 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
437 uint8_t flags );
438 /* retrotile_parser */
440
447typedef MERROR_RETVAL (*retrotile_ani_cb)(
448 void* animation_cb_data, int16_t iter );
449
450typedef MERROR_RETVAL (*retrotile_gen_cb)(
451 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
452 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
453 retrotile_ani_cb animation_cb, void* animation_cb_data );
454
465 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
466 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
467 retrotile_ani_cb animation_cb, void* animation_cb_data );
468
480 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
481 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
482 retrotile_ani_cb animation_cb, void* animation_cb_data );
483
492 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
493 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
494 retrotile_ani_cb animation_cb, void* animation_cb_data );
495
503 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
504 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
505 retrotile_ani_cb animation_cb, void* animation_cb_data );
506 /* retrotile_gen */
508
509struct RETROTILE_LAYER* retrotile_get_layer_p(
510 struct RETROTILE* tilemap, uint32_t layer_idx );
511
512MERROR_RETVAL retrotile_alloc(
513 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count,
514 const char* tilemap_name, const char* tileset_name );
515
516void retrotile_format_asset_path(
517 maug_path path_out, const char* afile,
518 struct RETROTILE_PARSER* parser );
519
520#ifdef RETROTIL_C
521
522# include <mparser.h>
523
524/* TODO: Function names should be verb_noun! */
525
526#if RETROTILE_TRACE_LVL > 0
527# define retrotile_parser_mstate( parser, new_mstate ) \
528 parser->mstate = new_mstate; \
529 debug_printf( \
530 RETROTILE_TRACE_LVL, "parser mstate: %d", parser->mstate );
531#else
532# define retrotile_parser_mstate( parser, new_mstate ) \
533 parser->mstate = new_mstate;
534#endif /* RETROTILE_TRACE_LVL */
535
536# define RETROTILE_PARSER_MSTATE_TABLE_CONST( name, idx, tokn, parent, m ) \
537 MAUG_CONST uint8_t SEG_MCONST name = idx;
538
539RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_CONST )
540
541#ifdef MPARSER_TRACE_NAMES
542# define RETROTILE_PARSER_MSTATE_TABLE_NAME( name, idx, tokn, parent, m ) \
543 #name,
544
545static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_names[] = {
546 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_NAME )
547 ""
548};
549#endif /* MPARSER_TRACE_NAMES */
550
551# define RETROTILE_PARSER_MSTATE_TABLE_TOKEN( name, idx, tokn, parent, m ) \
552 tokn,
553
554static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_tokens[] = {
555 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_TOKEN )
556 ""
557};
558
559# define RETROTILE_PARSER_MSTATE_TABLE_PARNT( name, idx, tokn, parent, m ) \
560 parent,
561
562static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_parents[] = {
563 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_PARNT )
564 0
565};
566
567# define RETROTILE_PARSER_MSTATE_TABLE_MODE( name, idx, tokn, parent, m ) \
568 m,
569
570static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_modes[] = {
571 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_MODE )
572 0
573};
574
575# define RETROTILE_CLASS_TABLE_CONSTS( A, a, i ) \
576 MAUG_CONST uint8_t SEG_MCONST RETROTILE_CLASS_ ## A = i;
577
578RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_CONSTS )
579
580/* === */
581
582static void retrotile_parser_match_token(
583 const char* token, size_t token_sz, struct RETROTILE_PARSER* parser
584) {
585 size_t j = 1;
586
587 /* Figure out what the key is for. */
588 while( '\0' != gc_retrotile_mstate_tokens[j][0] ) {
589 if(
590 /* Make sure tokens match. */
591 maug_strlen( gc_retrotile_mstate_tokens[j] ) != token_sz ||
592 0 != maug_strncmp(
593 token, gc_retrotile_mstate_tokens[j], token_sz + 1
594 )
595 ) {
596 j++;
597 continue;
598
599 } else if(
600 /* This state can only be
601 * reached THROUGH that parent state. This allows us to have
602 * keys with the same name but different parents!
603 */
604 parser->mstate != gc_retrotile_mstate_parents[j]
605 ) {
606#ifdef RETROTILE_TRACE_TOKENS
607 debug_printf(
609 "found token \"%s\" "
610#ifdef MPARSER_TRACE_NAMES
611 "but incorrect parent %s (%d) (needs %s (%d))!",
612#else
613 "but incorrect parent %d (needs %d)!",
614#endif /* MPARSER_TRACE_NAMES */
615 token,
616#ifdef MPARSER_TRACE_NAMES
617 retrotile_mstate_name( parser->mstate ),
618 parser->mstate,
619 retrotile_mstate_name( gc_retrotile_mstate_parents[j] ),
620 gc_retrotile_mstate_parents[j]
621#else
622 parser->mstate,
623 gc_retrotile_mstate_parents[j]
624#endif /* MPARSER_TRACE_NAMES */
625 );
626#endif /* !RETROTILE_TRACE_TOKENS */
627 j++;
628 continue;
629
630 } else if( parser->mode != gc_retrotile_mstate_modes[j] ) {
631#ifdef RETROTILE_TRACE_TOKENS
632 debug_printf(
633 RETROTILE_TRACE_LVL, "found token %s but incorrect mode %u!",
634 token,
635 gc_retrotile_mstate_modes[j] );
636#endif /* !RETROTILE_TRACE_TOKENS */
637 j++;
638 continue;
639
640 } else {
641 /* Found it! */
642#ifdef RETROTILE_TRACE_TOKENS
643 debug_printf(
645 "found token \"%s\" "
646#ifdef MPARSER_TRACE_NAMES
647 "under correct parent %s (%d)!",
648#else
649 "under correct parent %d!",
650#endif /* MPARSER_TRACE_NAMES */
651 token,
652#ifdef MPARSER_TRACE_NAMES
653 retrotile_mstate_name( parser->mstate ),
654 parser->mstate
655#else
656 parser->mstate
657#endif /* MPARSER_TRACE_NAMES */
658 );
659#endif /* !RETROTILE_TRACE_TOKENS */
660
661 retrotile_parser_mstate( parser, j );
662 return;
663 }
664 }
665}
666
667/* === */
668
669MERROR_RETVAL retrotile_parser_parse_tiledef_token(
670 void* jparser_void, const char* token, size_t token_sz, void* parser_arg
671) {
672 MERROR_RETVAL retval = MERROR_OK;
673 struct RETROTILE_TILE_DEF* tile_def = NULL;
674 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
675 size_t tileset_id_parsed = 0;
676
677 /* We don't lock the vector right away, since we might reallocate tile defs
678 * later on, below.
679 */
680
681 /* Try the custom parser. */
682 if(
683 NULL != parser->custom_token_cb &&
684 /* MERROR_PREEMPT is dealt with in cleanup. */
685 MERROR_OK != (retval = parser->custom_token_cb(
686 parser, token, token_sz, parser_arg ))
687 ) {
688 goto cleanup;
689 }
690
691 if( MJSON_PSTATE_OBJECT_VAL != mjson_parser_pstate( &(parser->jparser) ) ) {
692 /* Not a token we have to worry about! */
693 retrotile_parser_match_token( token, token_sz, parser );
694 goto cleanup;
695 }
696
697 if( MTILESTATE_TILES_ID == parser->mstate ) {
698 retrotile_parser_mstate( parser, MTILESTATE_TILES );
699 if( 0 == parser->pass ) {
700 /* Parse tile ID. */
701 tileset_id_parsed = maug_atou32( token, token_sz, 10 );
702 if( tileset_id_parsed > parser->tileset_id_cur ) {
703 parser->tileset_id_cur = tileset_id_parsed;
704#if RETROTILE_TRACE_LVL > 0
705 debug_printf(
707 "new highest tile ID: " SIZE_T_FMT, parser->tileset_id_cur );
708#endif /* RETROTILE_TRACE_LVL */
709 }
710 } else {
711 assert( 0 < mdata_vector_ct( parser->p_tile_defs ) );
712 parser->tileset_id_cur = maug_atou32( token, token_sz, 10 );
713#if RETROTILE_TRACE_LVL > 0
714 debug_printf(
716 "next tile ID: " SIZE_T_FMT, parser->tileset_id_cur );
717#endif /* RETROTILE_TRACE_LVL */
718 }
719 retrotile_parser_mstate( parser, MTILESTATE_TILES );
720
721 } else if( MTILESTATE_TILES_IMAGE == parser->mstate ) {
722 if( 1 == parser->pass ) {
723 /* Need this pass 1 so tile_defs vector is allocated. */
724 mdata_vector_lock( parser->p_tile_defs );
725 tile_def = mdata_vector_get(
726 parser->p_tile_defs, parser->tileset_id_cur,
727 struct RETROTILE_TILE_DEF );
728 assert( NULL != tile_def );
729
730 /* Parse tile image. */
731 if(
734 ) {
735 mfile_assign_path( tile_def->image_path, token, 0 );
736 } else {
738 tile_def->image_path, token, MFILE_ASSIGN_FLAG_TRIM_EXT );
739 }
740
741 /* Setup tile_def sz for serializing. */
742 tile_def->sz = sizeof( struct RETROTILE_TILE_DEF );
743
744#if RETROTILE_TRACE_LVL > 0
745 debug_printf(
746 RETROTILE_TRACE_LVL, "set tile ID " SIZE_T_FMT " to: %s",
747 parser->tileset_id_cur, tile_def->image_path );
748#endif /* RETROTILE_TRACE_LVL */
749 }
750 retrotile_parser_mstate( parser, MTILESTATE_TILES );
751
752 } else if( MTILESTATE_TILES_CLASS == parser->mstate ) {
753 if( 1 == parser->pass ) {
754 /* Set the class from the pass 1 so the custom function can use
755 * it on pass 2 even if it's alphabetically out of order.
756 * (Tiled calls it "type" and puts it near the bottom!)
757 *
758 * We can't do this on pass 0 since tiles aren't allocated yet!
759 */
760 mdata_vector_lock( parser->p_tile_defs );
761 tile_def = mdata_vector_get(
762 parser->p_tile_defs, parser->tileset_id_cur,
763 struct RETROTILE_TILE_DEF );
764 assert( NULL != tile_def );
765 assert( 0 == tile_def->tile_class );
766
767#if RETROTILE_TRACE_LVL > 0
768 #define RETROTILE_CLASS_TABLE_SET( A, a, i ) \
769 } else if( 0 == maug_strncmp( #a, token, maug_strlen( #a ) + 1 ) ) { \
770 tile_def->tile_class = RETROTILE_CLASS_ ## A; \
771 debug_printf( RETROTILE_TRACE_LVL, \
772 "set tile " SIZE_T_FMT " type: " #a " (%u)", \
773 parser->tileset_id_cur, tile_def->tile_class );
774#else
775 #define RETROTILE_CLASS_TABLE_SET( A, a, i ) \
776 } else if( 0 == maug_strncmp( #a, token, maug_strlen( #a ) + 1 ) ) { \
777 tile_def->tile_class = RETROTILE_CLASS_ ## A;
778#endif /* RETROTILE_TRACE_LVL */
779
780 if( 0 ) {
781 RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_SET )
782 } else {
783 tile_def->tile_class = RETROTILE_CLASS_TILE;
784#if RETROTILE_TRACE_LVL > 0
785 debug_printf( RETROTILE_TRACE_LVL,
786 "set tile " SIZE_T_FMT " type: tile (%u)",
787 parser->tileset_id_cur, tile_def->tile_class );
788#endif /* RETROTILE_TRACE_LVL */
789 }
790 }
791 retrotile_parser_mstate( parser, MTILESTATE_TILES );
792
793 } else if( MTILESTATE_TILES_PROP_NAME == parser->mstate ) {
794#if RETROTILE_TRACE_LVL > 0
795 debug_printf( RETROTILE_TRACE_LVL, "parsing property: %s", token );
796#endif /* RETROTILE_TRACE_LVL */
797 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
798 maug_strncpy( parser->last_prop_name, token, RETROTILE_PROP_NAME_SZ_MAX );
799 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
800
801 } else if( MTILESTATE_TILES_PROP_TYPE == parser->mstate ) {
802#if RETROTILE_TRACE_LVL > 0
803 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
804 parser->last_prop_name, token );
805#endif /* RETROTILE_TRACE_LVL */
806 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
807 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
808
809 } else if( MTILESTATE_TILES_PROP_VAL == parser->mstate ) {
810 /* This would be ideal to be handled in the custom token parser callback.
811 */
812
813 if( 1 == parser->pass ) {
814 mdata_vector_lock( parser->p_tile_defs );
815 tile_def = mdata_vector_get(
816 parser->p_tile_defs, parser->tileset_id_cur,
817 struct RETROTILE_TILE_DEF );
818 assert( NULL != tile_def );
819
820 if( 0 == maug_strncmp( "warp_dest", parser->last_prop_name, 10 ) ) {
821 maug_mzero( tile_def->warp_dest, RETROTILE_NAME_SZ_MAX );
822 maug_strncpy( tile_def->warp_dest, token, RETROTILE_NAME_SZ_MAX );
823#if RETROTILE_TRACE_LVL > 0
824 debug_printf(
825 RETROTILE_TRACE_LVL, "set tile " SIZE_T_FMT " warp_dest: %s",
826 parser->tileset_id_cur, tile_def->warp_dest );
827#endif /* RETROTILE_TRACE_LVL */
828
829 } else if( 0 == maug_strncmp( "warp_x", parser->last_prop_name, 7 ) ) {
830 tile_def->warp_x = maug_atos32( token, token_sz );
831#if RETROTILE_TRACE_LVL > 0
832 debug_printf(
833 RETROTILE_TRACE_LVL, "set tile " SIZE_T_FMT " warp_x: %d",
834 parser->tileset_id_cur, tile_def->warp_x );
835#endif /* RETROTILE_TRACE_LVL */
836
837 } else if( 0 == maug_strncmp( "warp_y", parser->last_prop_name, 7 ) ) {
838 tile_def->warp_y = maug_atos32( token, token_sz );
839#if RETROTILE_TRACE_LVL > 0
840 debug_printf(
841 RETROTILE_TRACE_LVL, "set tile " SIZE_T_FMT " warp_y: %d",
842 parser->tileset_id_cur, tile_def->warp_y );
843#endif /* RETROTILE_TRACE_LVL */
844
845 } else if( 0 == maug_strncmp( "color", parser->last_prop_name, 7 ) ) {
846
847#define RETROFLAT_COLOR_TABLE_TILPRP( idx, name_l, name_u, r, g, b, cgc, cgd ) \
848 } else if( 0 == maug_strncmp( token, #name_l, token_sz ) ) { \
849 tile_def->color = RETROFLAT_COLOR_ ## name_u;
850
851 if( 0 ) {
852 RETROFLAT_COLOR_TABLE( RETROFLAT_COLOR_TABLE_TILPRP );
853 }
854
855#if RETROTILE_TRACE_LVL > 0
856 debug_printf(
857 RETROTILE_TRACE_LVL, "set tile " SIZE_T_FMT " color: %d",
858 parser->tileset_id_cur, tile_def->color );
859#endif /* RETROTILE_TRACE_LVL */
860
861 }
862 }
863
864 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
865 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
866
867 } else if( MTILESTATE_NAME == parser->mstate ) {
868 maug_strncpy( parser->tileset_name, token, RETROTILE_NAME_SZ_MAX );
869#if RETROTILE_TRACE_LVL > 0
870 debug_printf(
871 RETROTILE_TRACE_LVL, "tileset name: %s", parser->tileset_name );
872#endif /* RETROTILE_TRACE_LVL */
873
874 retrotile_parser_mstate( parser, 0 );
875
876 } else if( MTILESTATE_TPROP_NAME == parser->mstate ) {
877 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
878 maug_strncpy(
880 parser->last_prop_name_sz = token_sz;
881 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
882
883 } else if( MTILESTATE_TPROP_TYPE == parser->mstate ) {
884#if RETROTILE_TRACE_LVL > 0
885 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
886 parser->last_prop_name, token );
887#endif /* RETROTILE_TRACE_LVL */
888 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
889 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
890
891 } else if( MTILESTATE_TPROP_VAL == parser->mstate ) {
892 /* This should be handled in the custom_cb above! */
893 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
894 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
895 }
896
897cleanup:
898
899 mdata_vector_unlock( parser->p_tile_defs );
900
901 if( MERROR_PREEMPT == retval ) {
902 /* Reset custom callback retval. */
903 retval = MERROR_OK;
904 }
905
906 return retval;
907}
908
909/* === */
910
911MERROR_RETVAL retrotile_parser_parse_token(
912 void* jparser_void, const char* token, size_t token_sz, void* parser_arg
913) {
914 MERROR_RETVAL retval = MERROR_OK;
915 struct RETROTILE_LAYER* tiles_layer = NULL;
916 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
917 retroflat_tile_t* tiles = NULL;
918
919 /* Try the custom parser. */
920 if(
921 NULL != parser->custom_token_cb &&
922 /* MERROR_PREEMPT is dealt with in cleanup. */
923 MERROR_OK != (retval = parser->custom_token_cb(
924 parser, token, token_sz, parser_arg ))
925 ) {
926 goto cleanup;
927 }
928
929 if( MJSON_PSTATE_LIST == mjson_parser_pstate( &(parser->jparser) ) ) {
930 /* We're parsing a list. What lists do we care about? */
931
932 /* Please note that this is for new tokens encountered inside of a list.
933 * For dealing with the closing of a list, see
934 * retrotile_json_close_list()!
935 */
936
937 if(
938 1 == parser->pass &&
939 MTILESTATE_LAYER_DATA == parser->mstate
940 ) {
941 /* This is a list of layer data... AKA tiles! */
942
943 /*
944 assert( NULL != *(parser->p_tilemap_h) );
945 maug_mlock( *(parser->p_tilemap_h), tilemap );
946 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
947 */
948
949 /*
950 debug_printf( RETROTILE_TRACE_LVL,
951 "selecting layer " SIZE_T_FMT "...",
952 parser->pass_layer_iter );
953 */
954 assert( NULL != parser->t );
955 tiles_layer = retrotile_get_layer_p(
956 parser->t, parser->pass_layer_iter );
957 assert( NULL != tiles_layer );
958
959 /* Apply the class parsed previously. */
960 tiles_layer->layer_class = parser->layer_class;
961
962 tiles = retrotile_get_tiles_p( tiles_layer );
963
964 /* Parse tilemap tile. */
965 /*
966 debug_printf( RETROTILE_TRACE_LVL,
967 "layer " SIZE_T_FMT " tile: " SIZE_T_FMT " (tiles: %p)",
968 parser->pass_layer_iter, parser->layer_tile_iter,
969 tiles );
970 */
971 assert( NULL != token );
972 assert( NULL != parser );
973 assert( NULL != tiles );
974
975 if(
976 parser->layer_tile_iter >=
977 parser->t->tiles_w * parser->t->tiles_h
978 ) {
979 /* Tile is outside of layer! */
980 error_printf(
981 "tile " SIZE_T_FMT " outside of layer tile buffer size "
982 SIZE_T_FMT "!",
983 parser->layer_tile_iter,
984 parser->t->tiles_w * parser->t->tiles_h );
985 retval = MERROR_OVERFLOW;
986 goto cleanup;
987 }
988
989 assert( 0 == tiles[parser->layer_tile_iter] );
990
991 tiles[parser->layer_tile_iter] = maug_atou32(
992 token, token_sz, 10 );
993
994 }
995 parser->layer_tile_iter++;
996 goto cleanup;
997
998 } else if(
999 MJSON_PSTATE_OBJECT_VAL ==
1000 mjson_parser_pstate( &(parser->jparser) )
1001 ) {
1002
1003 /* Pay attention to the retrotile_parser_mstate() calls here. We don't
1004 * have a stack of states, and we really only track a few we're
1005 * interested in. Even if we don't process their data here, we still
1006 * use retrotile_parser_mstate() to move the parser mstate back up
1007 * to the parent mstate when we're done with it. This kind of "fakes" a
1008 * stack (in a well-formed file, of course).
1009 */
1010
1011 if( MTILESTATE_TILESETS_FGID == parser->mstate ) {
1012 if( 1 == parser->pass ) {
1013 parser->t->tileset_fgid = maug_atou32( token, token_sz, 10 );
1014#if RETROTILE_TRACE_LVL > 0
1015 debug_printf(
1016 RETROTILE_TRACE_LVL, "tileset FGID set to: " SIZE_T_FMT,
1017 parser->t->tileset_fgid );
1018#endif /* RETROTILE_TRACE_LVL */
1019 }
1020 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
1021
1022 } else if( MTILESTATE_TILESETS_SRC == parser->mstate ) {
1023 if( 1 == parser->pass ) {
1024#if RETROTILE_TRACE_LVL > 0
1025 debug_printf( RETROTILE_TRACE_LVL, "parsing %s...", token );
1026#endif /* RETROTILE_TRACE_LVL */
1027 parser->tj_parse_cb(
1028 parser->dirname, token, NULL, parser->p_tile_defs,
1029 parser->wait_cb, parser->wait_data,
1030 parser->custom_token_cb, parser->custom_token_cb_data,
1031 parser->passes_max, parser->flags );
1032 }
1033 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
1034
1035 } else if( MTILESTATE_HEIGHT == parser->mstate ) {
1036 if( 0 == parser->pass ) {
1037 /* Need this to allocate on pass 1. */
1038 parser->tiles_h = maug_atou32( token, token_sz, 10 );
1039#if RETROTILE_TRACE_LVL > 0
1040 debug_printf(
1041 RETROTILE_TRACE_LVL, "tilemap height: " SIZE_T_FMT,
1042 parser->tiles_h );
1043#endif /* RETROTILE_TRACE_LVL */
1044 }
1045 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1046
1047 } else if( MTILESTATE_WIDTH == parser->mstate ) {
1048 if( 0 == parser->pass ) {
1049 /* Need this to allocate on pass 1. */
1050 parser->tiles_w = maug_atou32( token, token_sz, 10 );
1051#if RETROTILE_TRACE_LVL > 0
1052 debug_printf(
1053 RETROTILE_TRACE_LVL, "tilemap width: " SIZE_T_FMT,
1054 parser->tiles_w );
1055#endif /* RETROTILE_TRACE_LVL */
1056 }
1057 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1058
1059 } else if( MTILESTATE_LAYER_NAME == parser->mstate ) {
1060 /* TODO: Store */
1061 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1062
1063 } else if( MTILESTATE_LAYER_CLASS == parser->mstate ) {
1064 /* TODO: Use the class table to create layers for e.g. crops, items. */
1065 if( 0 == maug_strncmp( "mobile", token, 7 ) ) {
1066#if RETROTILE_TRACE_LVL > 0
1067 debug_printf( RETROTILE_TRACE_LVL,
1068 "layer " SIZE_T_FMT " type: mobile",
1069 parser->pass_layer_iter );
1070#endif /* RETROTILE_TRACE_LVL */
1071 parser->layer_class = RETROTILE_CLASS_MOBILE;
1072 } else {
1073#if RETROTILE_TRACE_LVL > 0
1074 debug_printf( RETROTILE_TRACE_LVL,
1075 "layer " SIZE_T_FMT " type: tile",
1076 parser->pass_layer_iter );
1077#endif /* RETROTILE_TRACE_LVL */
1078 parser->layer_class = RETROTILE_CLASS_TILE;
1079 }
1080 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1081
1082 } else if( MTILESTATE_PROP_NAME == parser->mstate ) {
1083 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
1084 maug_strncpy(
1086 parser->last_prop_name_sz = token_sz;
1087 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1088
1089 } else if( MTILESTATE_PROP_TYPE == parser->mstate ) {
1090#if RETROTILE_TRACE_LVL > 0
1091 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
1092 parser->last_prop_name, token );
1093#endif /* RETROTILE_TRACE_LVL */
1094 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
1095 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1096
1097 } else if( MTILESTATE_PROP_VAL == parser->mstate ) {
1098 /* We're dealing with properties of the tilemap. */
1099 if( 0 == maug_strncmp( parser->last_prop_name, "name", 5 ) ) {
1100#if RETROTILE_TRACE_LVL > 0
1101 debug_printf( RETROTILE_TRACE_LVL, "tilemap name: %s", token );
1102#endif /* RETROTILE_TRACE_LVL */
1103 maug_strncpy( parser->tilemap_name, token, RETROTILE_NAME_SZ_MAX );
1104 }
1105
1106 maug_mzero( parser->last_prop_name, RETROTILE_PROP_NAME_SZ_MAX + 1 );
1107 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1108 }
1109 goto cleanup;
1110 }
1111
1112 retrotile_parser_match_token( token, token_sz, parser );
1113
1114cleanup:
1115
1116 return retval;
1117}
1118
1119/* === */
1120
1121MERROR_RETVAL retrotile_json_close_list(
1122 struct MJSON_PARSER* jparser, void* parg
1123) {
1124 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1125
1126 if( MTILESTATE_LAYER_DATA == parser->mstate ) {
1127 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1128#if RETROTILE_TRACE_LVL > 0
1129 debug_printf( RETROTILE_TRACE_LVL, "parsed " SIZE_T_FMT " tiles!",
1130 parser->layer_tile_iter );
1131#endif /* RETROTILE_TRACE_LVL */
1132 assert( parser->layer_tile_iter > 0 );
1133 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1134
1135 } else if( MTILESTATE_LAYERS == parser->mstate ) {
1136 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1137 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1138
1139 } else if( MTILESTATE_TILESETS == parser->mstate ) {
1140 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1141 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1142
1143 } else if( MTILESTATE_TILES_PROP == parser->mstate ) {
1144 assert( RETROTILE_PARSER_MODE_DEFS == parser->mode );
1145 retrotile_parser_mstate( parser, MTILESTATE_TILES );
1146
1147 } else if( MTILESTATE_TILES == parser->mstate ) {
1148 assert( RETROTILE_PARSER_MODE_DEFS == parser->mode );
1149 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1150
1151 } else if( MTILESTATE_PROP == parser->mstate ) {
1152 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1153
1154 } else if( MTILESTATE_TPROP == parser->mstate ) {
1155 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1156 }
1157
1158 return MERROR_OK;
1159}
1160
1161/* === */
1162
1163MERROR_RETVAL retrotile_json_open_obj(
1164 struct MJSON_PARSER* jparser, void* parg
1165) {
1166 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1167
1168 if( MTILESTATE_LAYERS == parser->mstate ) {
1169 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1170 /* Reset on open so count is retained for allocating after first
1171 * pass. */
1172 parser->layer_tile_iter = 0;
1173 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1174 }
1175
1176 return MERROR_OK;
1177}
1178
1179/* === */
1180
1181MERROR_RETVAL retrotile_json_close_obj(
1182 struct MJSON_PARSER* jparser, void* parg
1183) {
1184 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1185
1186 if( MTILESTATE_LAYER == parser->mstate ) {
1187 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1188#if RETROTILE_TRACE_LVL > 0
1189 debug_printf( RETROTILE_TRACE_LVL,
1190 "incrementing pass layer to " SIZE_T_FMT " after " SIZE_T_FMT
1191 " tiles...",
1192 parser->pass_layer_iter + 1, parser->layer_tile_iter );
1193#endif /* RETROTILE_TRACE_LVL */
1194 parser->pass_layer_iter++;
1195 retrotile_parser_mstate( parser, MTILESTATE_LAYERS );
1196
1197 } else if( MTILESTATE_GRID == parser->mstate ) {
1198 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1199 }
1200
1201 return MERROR_OK;
1202}
1203
1204/* === */
1205
1206int retrotile_parse_prop_type( const char* token, size_t token_sz ) {
1207 int out = RETROTILE_PROP_TYPE_OTHER;
1208
1209 if( 0 == maug_strncmp( "string", token, 7 ) ) {
1211 } else if( 0 == maug_strncmp( "file", token, 5 ) ) {
1213 } else if( 0 == maug_strncmp( "int", token, 4 ) ) {
1215 }
1216
1217 return out;
1218}
1219
1220/* === */
1221
1222mfix_t retrotile_static_rotation_from_dir( const char* dir ) {
1223 mfix_t static_rotate_out = 0;
1224
1225 if( NULL == dir ) {
1226 return 0;
1227 }
1228
1229 /* Translate dir into rotation value. */
1230 if( 0 == maug_strncmp( dir, "NW", 3 ) ) {
1231 static_rotate_out = mfix_from_f( 90.0f );
1232 } else if( 0 == maug_strncmp( dir, "SW", 3 ) ) {
1233 static_rotate_out = mfix_from_f( 180.0f );
1234 } else if( 0 == maug_strncmp( dir, "SE", 3 ) ) {
1235 static_rotate_out = mfix_from_f( 270.0f );
1236
1237 } else if( 0 == maug_strncmp( dir, "W", 2 ) ) {
1238 static_rotate_out = mfix_from_f( 90.0f );
1239 } else if( 0 == maug_strncmp( dir, "S", 2 ) ) {
1240 static_rotate_out = mfix_from_f( 180.0f );
1241 } else if( 0 == maug_strncmp( dir, "E", 2 ) ) {
1242 static_rotate_out = mfix_from_f( 270.0f );
1243
1244 } else {
1245 static_rotate_out = 0;
1246 }
1247
1248 return static_rotate_out;
1249}
1250
1251/* === */
1252
1254 const maug_path dirname, const char* filename, MAUG_MHANDLE* p_tilemap_h,
1255 struct MDATA_VECTOR* p_tile_defs, mparser_wait_cb_t wait_cb, void* wait_data,
1256 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
1257 uint8_t flags
1258) {
1259 MERROR_RETVAL retval = MERROR_OK;
1260 MAUG_MHANDLE parser_h = (MAUG_MHANDLE)NULL;
1261 struct RETROTILE_PARSER* parser = NULL;
1262 maug_path filename_path;
1263 mfile_t tile_file;
1264 char c;
1265 char* filename_ext = NULL;
1266
1267 /* Initialize parser. */
1268 maug_malloc_test( parser_h, 1, sizeof( struct RETROTILE_PARSER ) );
1269
1270 maug_mlock( parser_h, parser );
1271 maug_cleanup_if_null_lock( struct RETROTILE_PARSER*, parser );
1272 maug_mzero( parser, sizeof( struct RETROTILE_PARSER ) );
1273
1274 parser->flags = flags;
1275 parser->tj_parse_cb = retrotile_parse_json_file;
1276 parser->custom_token_cb = token_cb;
1277 parser->custom_token_cb_data = token_cb_data;
1278
1279 retval = mfile_assign_path( parser->dirname, dirname, 0 );
1280 maug_cleanup_if_not_ok();
1281
1282 if( 2 > passes ) {
1283#if RETROTILE_TRACE_LVL > 0
1284 debug_printf( RETROTILE_TRACE_LVL,
1285 "increasing parse passes to minimum, 2!" );
1286#endif /* RETROTILE_TRACE_LVL */
1287 passes = 2;
1288 }
1289
1290 parser->passes_max = passes;
1291
1292 /* Setup filename path. */
1293 retrotile_format_asset_path( filename_path, filename, parser );
1294
1295#if RETROTILE_TRACE_LVL > 0
1296 debug_printf( RETROTILE_TRACE_LVL, "opening %s...", filename_path );
1297#endif /* RETROTILE_TRACE_LVL */
1298
1299 retval = mfile_open_read( filename_path, &tile_file );
1300 maug_cleanup_if_not_ok();
1301
1302 /* Parse JSON and react to state. */
1303 for( parser->pass = 0 ; passes > parser->pass ; parser->pass++ ) {
1304#if RETROTILE_TRACE_LVL > 0
1305 debug_printf( RETROTILE_TRACE_LVL, "beginning pass #%u...",
1306 parser->pass );
1307#endif /* RETROTILE_TRACE_LVL */
1308
1309 /* Reset tilemap parser. */
1310 parser->mstate = 0;
1311
1312 /* Reset JSON parser. */
1313 maug_mzero( &(parser->jparser.base), sizeof( struct MJSON_PARSER ) );
1314
1315 parser->wait_cb = wait_cb;
1316 parser->wait_data = wait_data;
1317 parser->jparser.base.wait_cb = wait_cb;
1318 parser->jparser.base.wait_data = wait_data;
1319
1320 /* Figure out if we're parsing a .tmj or .tsj. */
1321 filename_ext = maug_strrchr( filename, '.' );
1322 if( NULL == filename_ext ) {
1323 error_printf( "could not parse filename extension!" );
1324 retval = MERROR_FILE;
1325 goto cleanup;
1326 }
1327 if( 's' == filename_ext[2] ) {
1328#if RETROTILE_TRACE_LVL > 0
1329 debug_printf( RETROTILE_TRACE_LVL,
1330 "(tile_defs pass %u)", parser->pass );
1331#endif /* RETROTILE_TRACE_LVL */
1333 parser->jparser.token_parser = retrotile_parser_parse_tiledef_token;
1334 parser->jparser.token_parser_arg = parser;
1335 parser->jparser.close_list = retrotile_json_close_list;
1336 parser->jparser.close_list_arg = parser;
1337 parser->jparser.close_obj = retrotile_json_close_obj;
1338 parser->jparser.close_obj_arg = parser;
1339 /*
1340 parser->jparser.base.close_val = retrotile_json_close_val;
1341 parser->jparser.base.close_val_arg = parser;
1342 */
1343 parser->p_tile_defs = p_tile_defs;
1344
1345 assert( NULL != p_tile_defs );
1346 if( 1 == parser->pass ) {
1347 /* Allocate tile defs based on highest tile ID found on
1348 * first pass.
1349 */
1350 assert( 0 < parser->tileset_id_cur );
1351#if RETROTILE_TRACE_LVL > 0
1352 debug_printf(
1353 RETROTILE_TRACE_LVL, "allocating " SIZE_T_FMT " tile defs...",
1354 parser->tileset_id_cur + 1 );
1355#endif /* RETROTILE_TRACE_LVL */
1357 parser->p_tile_defs, parser->tileset_id_cur + 1,
1358 sizeof( struct RETROTILE_TILE_DEF ) );
1359 }
1360 } else {
1361#if RETROTILE_TRACE_LVL > 0
1362 debug_printf( RETROTILE_TRACE_LVL, "(tilemap pass %u)", parser->pass );
1363#endif /* RETROTILE_TRACE_LVL */
1365
1366 parser->jparser.close_list = retrotile_json_close_list;
1367 parser->jparser.close_list_arg = parser;
1368 parser->jparser.open_obj = retrotile_json_open_obj;
1369 parser->jparser.open_obj_arg = parser;
1370 parser->jparser.close_obj = retrotile_json_close_obj;
1371 parser->jparser.close_obj_arg = parser;
1372 parser->jparser.token_parser = retrotile_parser_parse_token;
1373 parser->jparser.token_parser_arg = parser;
1374 parser->p_tile_defs = p_tile_defs;
1375
1376 assert( NULL != p_tilemap_h );
1377 assert( NULL != p_tile_defs );
1378 if( 1 == parser->pass ) {
1379 /* Allocate tiles for the new layers. */
1380 retval = retrotile_alloc(
1381 p_tilemap_h, parser->tiles_w, parser->tiles_h,
1382 parser->pass_layer_iter, parser->tilemap_name,
1383 parser->tileset_name );
1384 maug_cleanup_if_not_ok();
1385 maug_mlock( *p_tilemap_h, parser->t );
1386 }
1387 parser->pass_layer_iter = 0;
1388 }
1389
1390 while( mfile_has_bytes( &tile_file ) ) {
1391 mfile_read_int( &tile_file, (uint8_t*)&c, 1, 0 );
1392#if RETROTILE_TRACE_CHARS > 0
1393 debug_printf( RETROTILE_TRACE_CHARS, "%c", c );
1394#endif /* RETROTILE_TRACE_CHARS */
1395 retval = mjson_parse_c( &(parser->jparser), c );
1396 if( MERROR_OK != retval ) {
1397 error_printf( "error parsing JSON!" );
1398 goto cleanup;
1399 }
1400 }
1401
1402 mfile_seek( &tile_file, 0 );
1403
1404 filename_ext = maug_strrchr( filename, '.' );
1405 if( NULL == filename_ext ) {
1406 error_printf( "could not parse filename extension!" );
1407 retval = MERROR_FILE;
1408 goto cleanup;
1409 }
1410 if( 's' != filename_ext[2] ) {
1411#if RETROTILE_TRACE_LVL > 0
1412 debug_printf( RETROTILE_TRACE_LVL,
1413 "pass %u found " SIZE_T_FMT " layers",
1414 parser->pass, parser->pass_layer_iter );
1415#endif /* RETROTILE_TRACE_LVL */
1416 }
1417 }
1418
1419#if RETROTILE_TRACE_LVL > 0
1420 debug_printf(
1421 RETROTILE_TRACE_LVL, "finished parsing %s, retval: %d",
1422 filename_path, retval );
1423#endif /* RETROTILE_TRACE_LVL */
1424
1425cleanup:
1426
1427 if( NULL != parser ) {
1428 if( NULL != parser->t ) {
1429 maug_munlock( *p_tilemap_h, parser->t );
1430 }
1431 maug_munlock( parser_h, parser );
1432 }
1433
1434 if( (MAUG_MHANDLE)NULL != parser_h ) {
1435 maug_mfree( parser_h );
1436 }
1437
1438 return retval;
1439}
1440
1441/* === */
1442
1443static retroflat_tile_t retrotile_gen_diamond_square_rand(
1444 retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning,
1445 retroflat_tile_t top_left_z
1446) {
1447 retroflat_tile_t avg = top_left_z;
1448
1449 if( 8 > rand() % 10 ) {
1450 /* avg = min_z + (rand() % (max_z - min_z)); */
1451 avg -= (min_z / tuning) + (rand() % (max_z / tuning));
1452 /* } else {
1453 avg += (min_z / 10) + (rand() % (max_z / 10)); */
1454 }
1455
1456 /* Clamp the result. */
1457
1458 if( min_z > avg ) {
1459 avg = min_z;
1460 }
1461
1462 if( max_z < avg ) {
1463 avg = max_z;
1464 }
1465
1466 return avg;
1467}
1468
1469/* === */
1470
1471static void retrotile_gen_diamond_square_corners(
1472 int16_t corners_x[2][2], int16_t corners_y[2][2],
1474 uint32_t tuning, struct RETROTILE_DATA_DS* data_ds,
1475 struct RETROTILE_LAYER* layer, struct RETROTILE* t
1476) {
1477 int16_t iter_x = 0,
1478 iter_y = 0;
1479 retroflat_tile_t* tile_iter = NULL;
1480 retroflat_tile_t top_left_z = 0;
1481
1482 /* Generate missing corner data. Loop through X/Y coords stored in
1483 * corners_x/corners_y convenience arrays.
1484 */
1485 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
1486 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
1487
1488 /* Make sure corner X is in bounds. */
1489 corners_x[iter_x][iter_y] =
1490 (data_ds->sect_x - 1) + (iter_x * data_ds->sect_w);
1491 if( 0 > corners_x[iter_x][iter_y] ) {
1492 corners_x[iter_x][iter_y] += 1;
1493 }
1494
1495 /* Make sure corner Y is in bounds. */
1496 corners_y[iter_x][iter_y] =
1497 (data_ds->sect_y - 1) + (iter_y * data_ds->sect_h);
1498 if( 0 > corners_y[iter_x][iter_y] ) {
1499 corners_y[iter_x][iter_y] += 1;
1500 }
1501 }
1502 }
1503
1504 /* Should be handled by the check above. */
1505 assert( 0 <= corners_x[0][0] );
1506 assert( 0 <= corners_y[0][0] );
1507 assert( t->tiles_w > corners_x[0][0] );
1508 assert( t->tiles_h > corners_y[0][0] );
1509
1510 /* Grab the top-left Z-value to anchor generated corners to. */
1511 top_left_z = retrotile_get_tile(
1512 t, layer,
1513 corners_x[0][0],
1514 corners_y[0][0] );
1515
1516 if( 0 > top_left_z ) {
1517 retrotile_get_tile(
1518 t, layer,
1519 corners_x[0][0] >= 0 ? corners_x[0][0] : 0,
1520 corners_y[0][0] >= 0 ? corners_y[0][0] : 0 ) = max_z;
1521 top_left_z = max_z;
1522 }
1523
1524 /* Fill in missing corners. */
1525 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
1526 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
1527 /* Grab a pointer to the corner so we can modify it easily. */
1528 tile_iter = &(retrotile_get_tile(
1529 t, layer,
1530 corners_x[iter_x][iter_y],
1531 corners_y[iter_x][iter_y] ));
1532
1533 /* Check if corner is already filled in. */
1534 if( -1 != *tile_iter ) {
1535#if RETROTILE_TRACE_LVL > 0
1536 debug_printf(
1537 RETROTILE_TRACE_LVL, "corner coord %d x %d present: %d",
1538 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1539 retrotile_get_tile(
1540 t, layer,
1541 corners_x[iter_x][iter_y],
1542 corners_y[iter_x][iter_y] ) );
1543#endif /* RETROTILE_TRACE_LVL */
1544 continue;
1545 }
1546
1547 /* Generate a new value for this corner. */
1548 *tile_iter = retrotile_gen_diamond_square_rand(
1549 min_z, max_z, tuning, top_left_z );
1550
1551#if RETROTILE_TRACE_LVL > 0
1552 debug_printf( RETROTILE_TRACE_LVL,
1553 "missing corner coord %d x %d: %d",
1554 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1555 *tile_iter );
1556#endif /* RETROTILE_TRACE_LVL */
1557 }
1558 }
1559}
1560
1561/* === */
1562
1563static retroflat_tile_t retrotile_gen_diamond_square_avg(
1564 int16_t corners_x[2][2], int16_t corners_y[2][2],
1565 struct RETROTILE* t, struct RETROTILE_LAYER* layer
1566) {
1567 retroflat_tile_t* tile_iter = NULL;
1568 int16_t iter_x = 0,
1569 iter_y = 0;
1570 retroflat_tile_t avg = 0;
1571
1572 /* Average corner data. */
1573 for( iter_y = 0 ; 2 > iter_y ; iter_y++ ) {
1574 for( iter_x = 0 ; 2 > iter_x ; iter_x++ ) {
1575 tile_iter = &(retrotile_get_tile(
1576 t, layer,
1577 corners_x[iter_x][iter_y],
1578 corners_y[iter_x][iter_y] ));
1579 assert( -1 != *tile_iter );
1580 /*
1581 debug_printf(
1582 RETROTILE_TRACE_LVL, "%d: adding from coords %d x %d: %d",
1583 iter_depth,
1584 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1585 *tile_iter ); */
1586 avg += *tile_iter;
1587 }
1588 }
1589
1590 /* TODO: Use right shift? */
1591 avg /= 4;
1592
1593 return avg;
1594}
1595
1596/* === */
1597
1599 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1600 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1601 retrotile_ani_cb animation_cb, void* animation_cb_data
1602) {
1603 int16_t iter_x = 0,
1604 iter_y = 0
1605#if RETROTILE_TRACE_LVL > 0
1606 , iter_depth = 0;
1607#else
1608 ;
1609#endif /* RETROTILE_TRACE_LVL */
1610 int16_t corners_x[2][2];
1611 int16_t corners_y[2][2];
1612 int32_t avg = 0;
1613 /* size_t tile_idx = 0; */
1614 struct RETROTILE_DATA_DS data_ds_sub;
1615 MAUG_MHANDLE data_ds_h = (MAUG_MHANDLE)NULL;
1616 struct RETROTILE_DATA_DS* data_ds = NULL;
1617 /* retroflat_tile_t* tiles = NULL; */
1618 MERROR_RETVAL retval = MERROR_OK;
1619 struct RETROTILE_LAYER* layer = NULL;
1620 retroflat_tile_t* tile_iter = NULL;
1621 uint8_t free_ds_data = 0;
1622#ifdef MAUG_NO_STDLIB
1623 size_t i = 0;
1624#endif /* MAUG_NO_STDLIB */
1625
1626 /*
1627 maug_mlock( t->tiles, tiles );
1628 maug_cleanup_if_null_alloc( struct GRIDCITY_TILE*, tiles );
1629 */
1630
1631 #define _retrotile_ds_update_statistics( data, tile ) \
1632 /* Gather statistics. */ \
1633 if( (data)->highest_generated < (tile) && 32767 > (tile) ) { \
1634 (data)->highest_generated = (tile); \
1635 } \
1636 if( (data)->lowest_generated > (tile) && 0 < (tile) ) { \
1637 (data)->lowest_generated = (tile); \
1638 }
1639
1640 layer = retrotile_get_layer_p( t, layer_idx );
1641
1642 if(
1643 NULL == data ||
1645 ) {
1646 /* This must be the first call, so initialize or allocate a new struct.
1647 */
1648 if( NULL == data ) {
1649 /* An internal struct needs to be allocated before initialization. */
1650 maug_malloc_test( data_ds_h, 1, sizeof( struct RETROTILE_DATA_DS ) );
1651 free_ds_data = 1;
1652 maug_mlock( data_ds_h, data_ds );
1653 maug_cleanup_if_null_alloc( struct RETROTILE_DATA_DS*, data_ds );
1654 } else {
1655 data_ds = (struct RETROTILE_DATA_DS*)data;
1656 }
1657
1658 /* Initialize passed tilemap while we're handling first call stuff. */
1659 retrotile_clear_tiles( t, layer, i );
1660
1661 /* Initialize DS struct from tilemap properties. */
1662 maug_mzero( data_ds, sizeof( struct RETROTILE_DATA_DS ) );
1663 data_ds->sect_w = t->tiles_w;
1664 data_ds->sect_h = t->tiles_h;
1665 data_ds->sect_w_half = data_ds->sect_w >> 1;
1666 data_ds->sect_h_half = data_ds->sect_h >> 1;
1667 data_ds->lowest_generated = 32767;
1668
1669 /* Disable this flag for subsequent calls. */
1670 flags &= ~RETROTILE_DS_FLAG_INIT_DATA;
1671 } else {
1672 data_ds = (struct RETROTILE_DATA_DS*)data;
1673 }
1674 assert( NULL != data_ds );
1675
1676 /* Trivial case; end recursion. */
1677 if( 0 == data_ds->sect_w ) {
1678#if RETROTILE_TRACE_LVL > 0
1679 debug_printf(
1680 RETROTILE_TRACE_LVL, "%d return: null sector", iter_depth );
1681#endif /* RETROTILE_TRACE_LVL */
1682 goto cleanup;
1683 }
1684
1685 if(
1686 data_ds->sect_x + data_ds->sect_w > t->tiles_w ||
1687 data_ds->sect_y + data_ds->sect_h > t->tiles_h
1688 ) {
1689#if RETROTILE_TRACE_LVL > 0
1690 debug_printf(
1691 RETROTILE_TRACE_LVL, "%d return: overflow sector", iter_depth );
1692#endif /* RETROTILE_TRACE_LVL */
1693 goto cleanup;
1694 }
1695
1696#if RETROTILE_TRACE_LVL > 0
1697 iter_depth = t->tiles_w / data_ds->sect_w;
1698#endif /* RETROTILE_TRACE_LVL */
1699
1700 /* Generate/grab corners before averaging them! */
1701 retrotile_gen_diamond_square_corners(
1702 corners_x, corners_y, min_z, max_z, tuning, data_ds, layer, t );
1703
1704 if( 2 == data_ds->sect_w || 2 == data_ds->sect_h ) {
1705 /* Nothing to average, this sector is just corners! */
1706#if RETROTILE_TRACE_LVL > 0
1707 debug_printf(
1709 "%d return: reached innermost point", iter_depth );
1710#endif /* RETROTILE_TRACE_LVL */
1711 goto cleanup; /* Skip further descent regardless. */
1712 }
1713
1714 avg =
1715 retrotile_gen_diamond_square_avg( corners_x, corners_y, t, layer );
1716
1717#if RETROTILE_TRACE_LVL > 0
1718 debug_printf( RETROTILE_TRACE_LVL, "avg: " S32_FMT, avg );
1719#endif /* RETROTILE_TRACE_LVL */
1720
1721 tile_iter = &(retrotile_get_tile(
1722 t, layer,
1723 data_ds->sect_x + data_ds->sect_w_half,
1724 data_ds->sect_y + data_ds->sect_h_half ));
1725 if( -1 != *tile_iter ) {
1726#if RETROTILE_TRACE_LVL > 0
1727 debug_printf( RETROTILE_TRACE_LVL, "avg already present at %d x %d!",
1728 data_ds->sect_x + data_ds->sect_w_half,
1729 data_ds->sect_y + data_ds->sect_h_half );
1730#endif /* RETROTILE_TRACE_LVL */
1731 goto cleanup;
1732 }
1733 *tile_iter = avg;
1734 _retrotile_ds_update_statistics( data_ds, avg );
1735
1736 /* assert( 0 <= tiles[tile_idx].terrain );
1737
1738 maug_munlock( city->tiles, tiles );
1739 tiles = NULL; */
1740
1741 /* Recurse into subsectors. */
1742 for(
1743 iter_y = data_ds->sect_y ;
1744 iter_y < (data_ds->sect_y + data_ds->sect_h) ;
1745 iter_y++
1746 ) {
1747 for(
1748 iter_x = data_ds->sect_x ;
1749 iter_x < (data_ds->sect_x + data_ds->sect_w) ;
1750 iter_x++
1751 ) {
1752 data_ds_sub.sect_x = data_ds->sect_x + iter_x;
1753
1754 data_ds_sub.sect_y = data_ds->sect_y + iter_y;
1755
1756 data_ds_sub.sect_w = data_ds->sect_w_half;
1757 data_ds_sub.sect_h = data_ds->sect_h_half;
1758 data_ds_sub.sect_w_half = data_ds_sub.sect_w >> 1;
1759 data_ds_sub.sect_h_half = data_ds_sub.sect_h >> 1;
1760 data_ds_sub.lowest_generated = 32767;
1761 data_ds_sub.highest_generated = 0;
1762
1763#if RETROTILE_TRACE_LVL > 0
1764 debug_printf(
1765 RETROTILE_TRACE_LVL, "%d: child sector at %d x %d, %d wide",
1766 iter_depth,
1767 data_ds_sub.sect_x, data_ds_sub.sect_y, data_ds_sub.sect_w );
1768#endif /* RETROTILE_TRACE_LVL */
1769
1771 t, min_z, max_z, tuning, layer_idx, flags, &data_ds_sub,
1772 animation_cb, animation_cb_data );
1773 maug_cleanup_if_not_ok();
1774
1775 _retrotile_ds_update_statistics(
1776 data_ds, data_ds_sub.highest_generated );
1777 _retrotile_ds_update_statistics(
1778 data_ds, data_ds_sub.lowest_generated );
1779 }
1780 }
1781
1782 if(
1783 data_ds->sect_w == (t->tiles_w >> 1) &&
1784 NULL != animation_cb
1785 ) {
1786 retval = animation_cb( animation_cb_data, iter_y );
1787 maug_cleanup_if_not_ok();
1788 }
1789
1790#if RETROTILE_TRACE_LVL > 0
1791 debug_printf(
1792 RETROTILE_TRACE_LVL, "%d return: all sectors complete", iter_depth );
1793#endif /* RETROTILE_TRACE_LVL */
1794
1795cleanup:
1796
1797 if( free_ds_data && NULL != data_ds ) {
1798 maug_munlock( data_ds_h, data_ds );
1799 }
1800
1801 if( free_ds_data && (MAUG_MHANDLE)NULL != data_ds_h ) {
1802 maug_mfree( data_ds_h );
1803 }
1804
1805 return retval;
1806}
1807
1808/* === */
1809
1811 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1812 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1813 retrotile_ani_cb animation_cb, void* animation_cb_data
1814) {
1815 size_t x = 0,
1816 y = 0;
1817 int16_t offset_x = 0,
1818 offset_y = 0,
1819 finished = 0;
1820 MERROR_RETVAL retval = MERROR_OK;
1821 struct RETROTILE_LAYER* layer = NULL;
1822 int16_t spb = RETROTILE_VORONOI_DEFAULT_SPB;
1823 int16_t drift = RETROTILE_VORONOI_DEFAULT_DRIFT;
1824 MAUG_MHANDLE temp_grid_h = (MAUG_MHANDLE)NULL;
1825 retroflat_tile_t* temp_grid = NULL;
1826 retroflat_tile_t* tiles = NULL;
1827 /* Only use 4 cardinal directions. */
1828 int8_t side_iter = 0;
1829#ifdef MAUG_NO_STDLIB
1830 size_t i = 0;
1831#endif /* MAUG_NO_STDLIB */
1832
1833 layer = retrotile_get_layer_p( t, 0 );
1834
1835 tiles = retrotile_get_tiles_p( layer );
1836
1837 /* Initialize grid to empty. */
1838 retrotile_clear_tiles( t, layer, i );
1839
1840 /* Generate the initial sector starting points. */
1841 for( y = 0 ; t->tiles_w > y ; y += spb ) {
1842 for( x = 0 ; t->tiles_w > x ; x += spb ) {
1843 offset_x = x + ((drift * -1) + (rand() % drift));
1844 offset_y = y + ((drift * -1) + (rand() % drift));
1845
1846 /* Clamp sector offsets onto map borders. */
1847 if( 0 > offset_x ) {
1848 offset_x = 0;
1849 }
1850 if( offset_x >= t->tiles_w ) {
1851 offset_x = t->tiles_w - 1;
1852 }
1853 if( 0 > offset_y ) {
1854 offset_y = 0;
1855 }
1856 if( offset_y >= t->tiles_h ) {
1857 offset_y = t->tiles_h - 1;
1858 }
1859
1860 retrotile_get_tile( t, layer, offset_x, offset_y ) =
1861 min_z + (rand() % max_z);
1862 }
1863 }
1864
1865 maug_malloc_test(
1866 temp_grid_h, sizeof( retroflat_tile_t ), t->tiles_w * t->tiles_h );
1867
1868 maug_mlock( temp_grid_h, temp_grid );
1869 maug_cleanup_if_null_alloc( retroflat_tile_t*, temp_grid );
1870
1871 /* Grow the sector starting points. */
1872 while( !finished ) {
1873 if( NULL != animation_cb ) {
1874 retval = animation_cb( animation_cb_data, -1 );
1875 maug_cleanup_if_not_ok();
1876 }
1877
1878 /* Prepare sampling grid so we're working from unexpanded sections
1879 * below.
1880 */
1881 memcpy(
1882 temp_grid, tiles,
1883 sizeof( retroflat_tile_t ) * t->tiles_w * t->tiles_h );
1884
1885 /* Starting another pass, assume finished until proven otherwise. */
1886 finished = 1;
1887 for( y = 0 ; t->tiles_h > y ; y++ ) {
1888 for( x = 0 ; t->tiles_w > x ; x++ ) {
1889 if( -1 == retrotile_get_tile( t, layer, x, y ) ) {
1890 /* If there are still unfilled tiles, we're not finished
1891 * yet!
1892 */
1893 finished = 0;
1894
1895 /* Skip filled tile. */
1896 continue;
1897 }
1898
1899
1900 for( side_iter = 0 ; 4 > side_iter ; side_iter++ ) {
1901#if RETROTILE_TRACE_LVL > 0
1902 debug_printf( RETROTILE_TRACE_LVL,
1903 SIZE_T_FMT " (%d), " SIZE_T_FMT " (%d) ("
1904 SIZE_T_FMT ", " SIZE_T_FMT ")",
1905 x,
1906 gc_retroflat_offsets4_x[side_iter],
1907 y,
1908 gc_retroflat_offsets4_y[side_iter],
1909 t->tiles_w, t->tiles_h );
1910#endif /* RETROTILE_TRACE_LVL */
1911
1912 /* Iterate through directions to expand. */
1913 /* TODO: Add tuning to select directional probability. */
1914 if(
1915 t->tiles_w > x + gc_retroflat_offsets4_x[side_iter] &&
1916 t->tiles_h > y + gc_retroflat_offsets4_y[side_iter] &&
1917 -1 == temp_grid[
1918 ((y + gc_retroflat_offsets4_y[side_iter]) *
1919 t->tiles_w) +
1920 (x + gc_retroflat_offsets4_x[side_iter])]
1921 ) {
1922 /* Copy center tile to this direction. */
1923 retrotile_get_tile( t, layer,
1924 x + gc_retroflat_offsets4_x[side_iter],
1925 y + gc_retroflat_offsets4_y[side_iter] ) =
1926 retrotile_get_tile( t, layer, x, y );
1927 break;
1928 }
1929 }
1930 }
1931 }
1932 }
1933
1934cleanup:
1935
1936 if( NULL != temp_grid ) {
1937 maug_munlock( temp_grid_h, temp_grid );
1938 }
1939
1940 if( (MAUG_MHANDLE)NULL != temp_grid_h ) {
1941 maug_mfree( temp_grid_h );
1942 }
1943
1944 return retval;
1945}
1946
1947/* === */
1948
1950 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1951 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1952 retrotile_ani_cb animation_cb, void* animation_cb_data
1953) {
1954 MERROR_RETVAL retval = MERROR_OK;
1955 size_t x = 0,
1956 y = 0;
1957 int16_t side_iter = 0,
1958 sides_avail = 0,
1959 sides_sum = 0;
1960 /* Sides start from 12 on the clock (up). */
1961 struct RETROTILE_LAYER* layer = NULL;
1962
1963 assert( NULL != t );
1964 layer = retrotile_get_layer_p( t, layer_idx );
1965 assert( NULL != layer );
1966
1967 for( y = 0 ; t->tiles_h > y ; y++ ) {
1968 if( NULL != animation_cb ) {
1969 retval = animation_cb( animation_cb_data, y );
1970 maug_cleanup_if_not_ok();
1971 }
1972 for( x = 0 ; t->tiles_w > x ; x++ ) {
1973 /* Reset average. */
1974 sides_avail = 0;
1975 sides_sum = 0;
1976
1977 /* Grab values for available sides. */
1978 for( side_iter = 0 ; 8 > side_iter ; side_iter++ ) {
1979 if(
1980 t->tiles_w <= x + gc_retroflat_offsets8_x[side_iter] ||
1981 t->tiles_h <= y + gc_retroflat_offsets8_y[side_iter]
1982 ) {
1983 continue;
1984 }
1985
1986 sides_avail++;
1987#if RETROTILE_TRACE_LVL > 0
1988 debug_printf(
1990 "si %d: x, y: " SIZE_T_FMT " (+%d), " SIZE_T_FMT
1991 " (+%d) idx: " SIZE_T_FMT,
1992 side_iter,
1993 x + gc_retroflat_offsets8_x[side_iter],
1994 gc_retroflat_offsets8_x[side_iter],
1995 y + gc_retroflat_offsets8_y[side_iter],
1996 gc_retroflat_offsets8_y[side_iter],
1997 ((y + gc_retroflat_offsets8_y[side_iter]) * t->tiles_w) +
1998 x + gc_retroflat_offsets8_x[side_iter] );
1999#endif /* RETROTILE_TRACE_LVL */
2000 sides_sum += retrotile_get_tile(
2001 t, layer,
2002 x + gc_retroflat_offsets8_x[side_iter],
2003 y + gc_retroflat_offsets8_y[side_iter] );
2004 }
2005
2006 retrotile_get_tile( t, layer, x, y ) = sides_sum / sides_avail;
2007 }
2008 }
2009
2010cleanup:
2011
2012 return retval;
2013}
2014
2015/* === */
2016
2018 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
2019 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
2020 retrotile_ani_cb animation_cb, void* animation_cb_data
2021) {
2022 MERROR_RETVAL retval = MERROR_OK;
2023 struct RETROTILE_DATA_BORDER* borders =
2024 (struct RETROTILE_DATA_BORDER*)data;
2025 size_t i = 0,
2026 x = 0,
2027 y = 0,
2028 x_plus_1 = 0,
2029 y_plus_1 = 0,
2030 side = 0;
2031 int16_t ctr_iter = 0,
2032 outside_iter = 0;
2033 struct RETROTILE_LAYER* layer = NULL;
2034
2035 assert( NULL != t );
2036 layer = retrotile_get_layer_p( t, layer_idx );
2037 assert( NULL != layer );
2038
2039 /* Reset tile counter for all defined borders. */
2040 for( i = 0 ; 0 <= borders[i].center ; i++ ) {
2041 borders[i].tiles_changed = 0;
2042 }
2043
2044#if RETROTILE_TRACE_LVL > 0
2045 debug_printf( RETROTILE_TRACE_LVL, "adding borders..." );
2046#endif /* RETROTILE_TRACE_LVL */
2047
2048 for( y = 0 ; t->tiles_h > y ; y++ ) {
2049 for( x = 0 ; t->tiles_w > x ; x++ ) {
2050 i = 0;
2051 while( 0 <= borders[i].center ) {
2052 /* Compare/grab current center tile. */
2053 ctr_iter = retrotile_get_tile( t, layer, x, y );
2054#if RETROTILE_TRACE_LVL > 0
2055 debug_printf( RETROTILE_TRACE_LVL,
2056 "x: " SIZE_T_FMT ", y: " SIZE_T_FMT ", 0x%04x vs 0x%04x",
2057 x, y, ctr_iter, borders[i].center );
2058#endif /* RETROTILE_TRACE_LVL */
2059 if( ctr_iter != borders[i].center ) {
2060 i++;
2061 continue;
2062 }
2063
2064#if RETROTILE_TRACE_LVL > 0
2065 debug_printf( RETROTILE_TRACE_LVL, "comparing sides..." );
2066#endif /* RETROTILE_TRACE_LVL */
2067
2068 /* Zeroth pass: look for stick-outs. */
2069 for( side = 0 ; 8 > side ; side += 2 ) {
2070 if(
2071 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
2072 y + gc_retroflat_offsets8_y[side] > t->tiles_h
2073 ) {
2074 /* Skip out-of-bounds. */
2075 continue;
2076 }
2077 /* Get the outside tile on this side. */
2078 outside_iter = retrotile_get_tile( t, layer,
2079 x + gc_retroflat_offsets8_x[side],
2080 y + gc_retroflat_offsets8_y[side] );
2081
2082 /* Get the outside tile next two clock-steps from this one.
2083 */
2084 if( side + 4 < 8 ) {
2085 x_plus_1 = x + gc_retroflat_offsets8_x[side + 4];
2086 y_plus_1 = y + gc_retroflat_offsets8_y[side + 4];
2087 } else {
2088 x_plus_1 = x + gc_retroflat_offsets8_x[side - 4];
2089 y_plus_1 = y + gc_retroflat_offsets8_y[side - 4];
2090 }
2091
2092 if(
2093 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
2094 outside_iter == borders[i].outside &&
2095 outside_iter == retrotile_get_tile( t, layer,
2096 x_plus_1, y_plus_1 )
2097 ) {
2098 /* This has the outside on two opposing sides, so just
2099 * erase it and use the outside. */
2100 retrotile_get_tile( t, layer, x, y ) =
2101 borders[i].outside;
2102 borders[i].tiles_changed++;
2103 goto tile_done;
2104 }
2105 }
2106
2107
2108 /* First pass: look for corners. */
2109 for( side = 0 ; 8 > side ; side += 2 ) {
2110 if(
2111 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
2112 y + gc_retroflat_offsets8_y[side] > t->tiles_h
2113 ) {
2114 /* Skip out-of-bounds. */
2115 continue;
2116 }
2117 /* Get the outside tile on this side. */
2118 outside_iter = retrotile_get_tile( t, layer,
2119 x + gc_retroflat_offsets8_x[side],
2120 y + gc_retroflat_offsets8_y[side] );
2121
2122 /* Get the outside tile next two clock-steps from this one.
2123 */
2124 if( side + 2 < 8 ) {
2125 x_plus_1 = x + gc_retroflat_offsets8_x[side + 2];
2126 y_plus_1 = y + gc_retroflat_offsets8_y[side + 2];
2127 } else {
2128 x_plus_1 = x + gc_retroflat_offsets8_x[0];
2129 y_plus_1 = y + gc_retroflat_offsets8_y[0];
2130 }
2131
2132 if(
2133 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
2134 outside_iter == borders[i].outside &&
2135 outside_iter == retrotile_get_tile( t, layer,
2136 x_plus_1, y_plus_1 )
2137 ) {
2138 /* This has the outside on two sides, so use a corner. */
2139 retrotile_get_tile( t, layer, x, y ) =
2140 borders[i].mod_to[side + 1 < 8 ? side + 1 : 0];
2141 borders[i].tiles_changed++;
2142 goto tile_done;
2143 }
2144 }
2145
2146 /* Second pass (if first pass fails): look for edges. */
2147 for( side = 0 ; 8 > side ; side += 2 ) {
2148 if(
2149 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
2150 y + gc_retroflat_offsets8_y[side] > t->tiles_h
2151 ) {
2152 /* Skip out-of-bounds. */
2153 continue;
2154 }
2155 /* Get the outside tile on this side. */
2156 outside_iter = retrotile_get_tile( t, layer,
2157 x + gc_retroflat_offsets8_x[side],
2158 y + gc_retroflat_offsets8_y[side] );
2159
2160 if( outside_iter == borders[i].outside ) {
2161 /* It only matches on this side. */
2162#if RETROTILE_TRACE_LVL > 0
2163 debug_printf( RETROTILE_TRACE_LVL, "replacing..." );
2164#endif /* RETROTILE_TRACE_LVL */
2165 retrotile_get_tile( t, layer, x, y ) =
2166 borders[i].mod_to[side];
2167 borders[i].tiles_changed++;
2168 goto tile_done;
2169 }
2170 }
2171
2172tile_done:
2173 /* Tile replaced or not replaceable. */
2174 break;
2175 }
2176 }
2177 }
2178
2179 return retval;
2180}
2181
2182/* === */
2183
2184struct RETROTILE_LAYER* retrotile_get_layer_p(
2185 struct RETROTILE* tilemap, uint32_t layer_idx
2186) {
2187 struct RETROTILE_LAYER* layer_iter = NULL;
2188 uint8_t* tilemap_buf = (uint8_t*)tilemap;
2189
2190 if( 0 == tilemap->layers_count || layer_idx >= tilemap->layers_count ) {
2191 error_printf( "invalid layer " U32_FMT
2192 " requested (of " U32_FMT ")!",
2193 layer_idx, tilemap->layers_count );
2194 return NULL;
2195 }
2196
2197 /* Advance to first grid. */
2198 tilemap_buf += sizeof( struct RETROTILE );
2199 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
2200 while( layer_idx > 0 ) {
2201 tilemap_buf += layer_iter->total_sz;
2202 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
2203 layer_idx--;
2204 }
2205
2206 return layer_iter;
2207}
2208
2209/* === */
2210
2211MERROR_RETVAL retrotile_alloc(
2212 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count,
2213 const char* tilemap_name, const char* tileset_name
2214) {
2215 struct RETROTILE_LAYER* layer_iter = NULL;
2216 MERROR_RETVAL retval = MERROR_OK;
2217 size_t tilemap_sz = 0;
2218 struct RETROTILE* tilemap = NULL;
2219 size_t i = 0;
2220
2221 tilemap_sz = sizeof( struct RETROTILE ) +
2222 (layers_count * sizeof( struct RETROTILE_LAYER )) +
2223 (layers_count * (w * h * sizeof( retroflat_tile_t ) ));
2224
2225#if RETROTILE_TRACE_LVL > 0
2226 debug_printf(
2227 RETROTILE_TRACE_LVL, "allocating new tilemap " SIZE_T_FMT "x" SIZE_T_FMT
2228 " tiles, " SIZE_T_FMT " layers (" SIZE_T_FMT " bytes)...",
2229 w, h, layers_count, tilemap_sz );
2230#endif /* RETROTILE_TRACE_LVL */
2231
2232 maug_malloc_test( *p_tilemap_h, 1, tilemap_sz );
2233
2234 maug_mlock( *p_tilemap_h, tilemap );
2235 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
2236
2237 maug_mzero( tilemap, tilemap_sz );
2238 tilemap->total_sz = tilemap_sz;
2239 tilemap->layers_count = layers_count;
2240 tilemap->tiles_w = w;
2241 tilemap->tiles_h = h;
2243 tilemap->sz = sizeof( struct RETROTILE );
2244
2245 maug_strncpy( tilemap->name, tilemap_name, RETROTILE_NAME_SZ_MAX );
2246
2247 maug_strncpy( tilemap->tileset, tileset_name, RETROTILE_NAME_SZ_MAX );
2248
2249 for( i = 0 ; layers_count > i ; i++ ) {
2250 layer_iter = retrotile_get_layer_p( tilemap, i );
2251 assert( NULL != layer_iter );
2252 layer_iter->total_sz = sizeof( struct RETROTILE_LAYER ) +
2253 (w * h * sizeof( retroflat_tile_t ));
2254 maug_cleanup_if_not_ok();
2255 layer_iter->sz = sizeof( struct RETROTILE_LAYER );
2256 }
2257
2258cleanup:
2259
2260 if( NULL != tilemap ) {
2261 maug_munlock( *p_tilemap_h, tilemap );
2262 }
2263
2264 return retval;
2265}
2266
2267/* === */
2268
2269void retrotile_format_asset_path(
2270 maug_path path_out, const char* afile,
2271 struct RETROTILE_PARSER* parser
2272) {
2273 /* Load the portrait. */
2274 maug_mzero( path_out, MAUG_PATH_SZ_MAX );
2275 maug_snprintf( path_out, MAUG_PATH_SZ_MAX - 1, "%s/%s",
2276 parser->dirname, afile );
2277}
2278
2279#else
2280
2281/* This is defined externally so custom token callbacks can reference it. */
2282
2283# define RETROTILE_PARSER_MSTATE_TABLE_CONST( name, idx, tokn, parent, m ) \
2284 extern MAUG_CONST uint8_t SEG_MCONST name;
2285
2286RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_CONST )
2287
2288# define RETROTILE_CLASS_TABLE_CONSTS( A, a, i ) \
2289 extern MAUG_CONST uint8_t SEG_MCONST RETROTILE_CLASS_ ## A;
2290
2291RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_CONSTS )
2292
2293
2294#endif /* RETROTIL_C */
2295 /* retrotile */
2297 /* maug_retroflt */
2299
2300#endif /* !RETROTIL_H */
2301
uint16_t MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition: merror.h:28
MERROR_RETVAL mfile_open_read(const maug_path filename, mfile_t *p_file)
Open a file and read it into memory or memory-map it.
#define MAUG_PATH_SZ_MAX
Maximum size allocated for asset paths.
Definition: mfile.h:38
#define MERROR_PREEMPT
Indicates MLISP_AST_NODE can be executed again on next step iter pass.
Definition: merror.h:67
MERROR_RETVAL mfile_assign_path(maug_path tgt, const maug_path src, uint8_t flags)
Copy a maug_path from one place to another, safely observing character limits, etc.
char maug_path[MAUG_PATH_SZ_MAX]
Path/name used to load an asset from disk or access other files.
Definition: mfile.h:138
int8_t RETROFLAT_COLOR
Defines an index in the platform-specific color-table.
Definition: retroflt.h:328
#define RETROFLAT_COLOR_TABLE(f)
This macro defines all colors supported by RetroFlat for primative operations, particularly using ret...
Definition: retroflt.h:309
#define RETROTILE_DS_FLAG_INIT_DATA
Flag for retrotile_gen_diamond_square_iter() indicating that passed RETROTILE_DATA_DS object should b...
Definition: retrotil.h:157
MERROR_RETVAL retrotile_gen_smooth_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Average the values in adjacent tiles over an already-generated tilemap.
MERROR_RETVAL retrotile_gen_borders_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Given a list of RETROTILE_DATA_BORDER structs, this will search for occurrences of RETROTILE_DATA_BOR...
MERROR_RETVAL retrotile_gen_diamond_square_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Generate tilemap terrain using diamond square algorithm.
MERROR_RETVAL retrotile_gen_voronoi_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Generate tilemap terrain using voronoi graph.
int retrotile_parse_prop_type(const char *token, size_t token_sz)
Convert a Tiled "type" field to an integer suitable for use with RETROTILE_PARSER::last_prop_type.
MERROR_RETVAL retrotile_parse_json_file(const maug_path dirname, const char *filename, MAUG_MHANDLE *p_tilemap_h, struct MDATA_VECTOR *p_tile_defs, mparser_wait_cb_t wait_cb, void *wait_data, mparser_parse_token_cb token_cb, void *token_cb_data, uint8_t passes, uint8_t flags)
Parse the JSON file at the given path into a heap-allocated tilemap with a RETROTILE struct header.
mfix_t retrotile_static_rotation_from_dir(const char *dir)
Convert a less-or-equal-to-two-character string to a direction in degrees.
#define RETROTILE_TILE_SCALE_DEFAULT
Default value for RETROTILE::tile_scale.
Definition: retrotil.h:35
#define RETROTILE_PROP_NAME_SZ_MAX
Maximum number of chars in a parsed property name.
Definition: retrotil.h:30
#define RETROTILE_TRACE_LVL
If defined, bring debug printf statements up to this level.
Definition: retrotil.h:40
int16_t retroflat_tile_t
Value for an individual tile in a RETROTILE_LAYER.
Definition: retroflt.h:19
#define RETROTILE_NAME_SZ_MAX
Maximum number of chars in a RETROTILE::name.
Definition: retrotil.h:25
A vector of uniformly-sized objects, stored contiguously.
Definition: mdata.h:108
#define mdata_vector_lock(v)
Lock the vector. This should be done when items from the vector are actively being referenced,...
Definition: mdata.h:372
#define mdata_vector_fill(v, ct_new, sz)
Allocate and mark the new slots as active.
Definition: mdata.h:460
#define mdata_vector_unlock(v)
Unlock the vector so items may be added and removed.
Definition: mdata.h:405
#define mdata_vector_ct(v)
Number of items of MDATA_VECTOR::item_sz bytes actively stored in this vector.
Definition: mdata.h:448
Definition: mfile.h:184
This is not currently used for anything, but is provided as a a convenience for game logic.
Definition: retrotil.h:210
Definition: retrotil.h:265
retroflat_tile_t mod_to[8]
If the center and outside match, use this mod-to.
Definition: retrotil.h:270
Internal data structure used by retrotile_gen_diamond_square_iter().
Definition: retrotil.h:248
int16_t sect_y
Starting Y of subsector in a given iteration.
Definition: retrotil.h:252
int16_t sect_w
Width of subsector in a given iteration.
Definition: retrotil.h:254
int16_t sect_w_half
Half of the width of subsector in a given iteration.
Definition: retrotil.h:258
int16_t sect_x
Starting X of subsector in a given iteration.
Definition: retrotil.h:250
int16_t sect_h
Height of subsector in a given iteration.
Definition: retrotil.h:256
int16_t sect_h_half
Half of the height of subsector in a given iteration.
Definition: retrotil.h:260
Definition: retrotil.h:198
size_t total_sz
Size of the layer in bytes (including this struct header).
Definition: retrotil.h:202
size_t sz
Size of this struct (useful for serializing).
Definition: retrotil.h:200
Definition: retrotil.h:312
mparser_parse_token_cb custom_token_cb
Callback to parse engine-specific custom tokens from the tilemap JSON. Should return MERROR_PREEMPT i...
Definition: retrotil.h:346
#define RETROTILE_PROP_TYPE_FILE
Value for RETROTILE_PARSER::last_prop_type indicating file path.
Definition: retrotil.h:139
#define RETROTILE_PARSER_MODE_DEFS
Value for RETROTILE_PARSER::mode indicating the parser is currently parsing tile definitions.
Definition: retrotil.h:78
#define RETROTILE_PROP_TYPE_STRING
Value for RETROTILE_PARSER::last_prop_type indicating string.
Definition: retrotil.h:133
char tilemap_name[RETROTILE_NAME_SZ_MAX+1]
The name to give to the new tilemap.
Definition: retrotil.h:329
#define RETROTILE_PROP_TYPE_INT
Value for RETROTILE_PARSER::last_prop_type indicating integer.
Definition: retrotil.h:145
char last_prop_name[RETROTILE_PROP_NAME_SZ_MAX+1]
The name of the last property key/value pair parsed.
Definition: retrotil.h:325
#define RETROTILE_PARSER_MODE_MAP
Value for RETROTILE_PARSER::mode indicating the parser is currently parsing a tilemap.
Definition: retrotil.h:71
uint8_t mode
Value indicating the current type of object being parsed into.
Definition: retrotil.h:319
size_t tileset_id_cur
Highest tileset ID on first pass and next ID to be assigned on second.
Definition: retrotil.h:336
#define RETROTILE_PROP_TYPE_OTHER
Value for RETROTILE_PARSER::last_prop_type indicating other type.
Definition: retrotil.h:127
#define RETROTILE_PARSER_FLAG_LITERAL_PATHS
Flag for RETROTILE_PARSER::flags indicating to use literal image asset paths.
Definition: retrotil.h:63
Definition: retrotil.h:164
mfix_t static_rotation
Field indicating how many degrees the tile should always be rotated before drawin on-screen....
Definition: retrotil.h:178
size_t sz
Size of this struct (useful for serializing).
Definition: retrotil.h:166
int8_t no_serial
Dummy field; do not serialize fields after this!
Definition: retrotil.h:182
A struct representing a tilemap.
Definition: retrotil.h:222
size_t tiles_h
Height of all layers of the tilemap in tiles.
Definition: retrotil.h:234
uint32_t total_sz
Size of the tilemap in bytes (including this struct header).
Definition: retrotil.h:228
size_t tileset_fgid
First GID in the accompanying tileset.
Definition: retrotil.h:232
float tile_scale
Amount by which to scale tiles (convenience property).
Definition: retrotil.h:241
size_t tiles_w
Width of all layers of the tilemap in tiles.
Definition: retrotil.h:236
uint32_t layers_count
Number of tile layers in this tilemap.
Definition: retrotil.h:230
size_t sz
Size of this struct (useful for serializing).
Definition: retrotil.h:224