2 #include <stdlib.h> //exit, malloc
3 #include <stdio.h> //print
4 #include <stdarg.h> //va_args
5 #include <string.h> //memset, str*
8 #include <unistd.h> //u8_* functions
9 #include <unitypes.h> //uint8_t as a char
10 #include <unistr.h> //u32_cpy
11 #include <unistdio.h> //ulc_fprintf
18 #define do_error(...) exit(-1)
19 #define XXH_PRIVATE_API
20 #include "../xxHash/xxhash.h"
21 #define STB_IMAGE_IMPLEMENTATION
22 #include "stb_image.h"
25 void ir_binout_init(struct ir_class_t
*);
27 /* Memory Allocation */
28 #define struct_alloc(_T) ((struct _T*) stack_alloc(&datapages, sizeof(struct _T)))
30 struct pagelist_t linkpages
, datapages
;
32 enum model_type
{ SS
};
33 /* Binaryout out structure definitions */
34 struct bin_img_info_t
{
37 int fwidth
; //map and frame width
38 int fheight
; //map and frame height
42 struct bin_ht_header_t
{
46 struct bin_ht_entry_t
{
50 struct bin_class_header_t
{
51 struct bin_ht_header_t child_ht
;
52 struct bin_ht_header_t rootset_ht
;
55 struct bin_set_header_t
{
56 struct bin_ht_header_t child_ht
;
57 long sdat_start
; //points to setdata_header
60 struct bin_setdata_header_t
{
61 struct bin_ht_header_t variant_ht
;
64 struct bin_model_header_t
{ //one for each framebox, currently
65 long facing_array_start
;
68 struct bin_frame_header_t
{
84 struct bin_pixel_node_t
{
85 struct bin_pixel_node_t
* next
;
86 struct bin_pixel_t data
;
93 struct bin_attachment_header_t
{
94 int num_attachment_lists
;
97 struct bin_attachment_t
{
101 /* Read out of the als, after first ir pass, resolves the
102 attach_pos of the src-set that created the attachment_list
103 to the header that describes the attachment_list */
104 struct bin_attachment_list_t
{
106 struct bin_attachment_t
** attachments
;
110 struct bin_attachment_list_t
**attachment_stack
, **asp
; //attachment_stack, attachment_stack_pointer
115 #define NAMEHASH(name, domain) (XXH32(name, u8_strlen(name), 0XCEED ) & domain)
117 /* Given a position and a size, checks if the bytes are null and returns
118 the file position to where it started. 1 if not null, 0 if null*/
119 /* TODO: Determine why fseeking file past end sets bytes to -1, and not 0 */
121 int bytes_null( int len
, int pos
)
123 { if(fgetc(binaryout
) > 0)
124 { fseek(binaryout
, pos
, SEEK_SET
);
128 fseek(binaryout
, pos
, SEEK_SET
);
131 /* Checks if the key at entrypos is the same as the parameter key. Returns
132 1 if so, 0 if not. */
134 int bin_keys_identical
139 fseek(binaryout
, entry_pos
, SEEK_SET
);
140 fscanf(binaryout
, "%u", &curr_key
);
146 long bin_traverse_class(ir_class
);
147 /* Takes root class and begins processing */
149 ir_binout_init(ir_class root_class
)
150 { binaryout
= fopen("binaryout", "w+");
151 asp
= attachment_stack
;
152 pagelist_init(datapages
, (size_t) SYS_PAGESIZE
);
153 pagelist_init(linkpages
, (size_t) SYS_PAGESIZE
);
154 bin_traverse_class(root_class
);
157 #define ENTRY_OCCUPIED() (bytes_null(sizeof(ht_entry->key), entry_pos))
158 #define WRITE_ENTRY() do { \
159 if(fseek(binaryout, entry_pos, SEEK_SET) == -1) wprintf("fseek failed with %s", strerror(errno)); \
160 fwrite(&ht_entry, sizeof ht_entry, 1, binaryout); \
162 #define LOOP_ENTRY(_HTSTART) (entry_pos = _HTSTART)
163 #define INC_ENTRY() do { \
164 entry_pos += sizeof(ht_entry); \
165 if(fseek(binaryout, entry_pos, SEEK_SET) == -1) eprintf("fseek failed with %s", strerror(errno)); \
167 #define HT_END(_HTEND) (entry_pos >= _HTEND) //just in case at last entry
169 /* TODO: Should overwrite be a default? */
173 struct bin_ht_entry_t
* ht_entry
,
176 { long entry_pos
, ht_end
;
178 ht_end
= ht_start
+ ht_size
;
179 entry_pos
= ht_start
+ sizeof(ht_entry
) * ht_entry
->key
;
180 fseek(binaryout
, entry_pos
, SEEK_SET
);
182 if (!ENTRY_OCCUPIED())
183 { uprintf("key not occupied\n");
186 while( ENTRY_OCCUPIED() )
188 { if(bin_keys_identical(entry_pos
, ht_entry
->key
))
191 { eprintf("error in hashtable insertion, keys are identical");
195 LOOP_ENTRY(ht_start
);
204 |--------------------|
206 |--------------------|
208 |--------------------|
210 |--------------------|
211 | classchild header |
215 long bin_traverse_set(ir_set
);
217 #define HT_INIT(_START, _SIZE, _INIT_ENTRIES) do { \
218 _SIZE = _INIT_ENTRIES ? (_INIT_ENTRIES * (sizeof(struct bin_ht_entry_t) << 1)): 0; \
219 _START = ftell(binaryout); \
220 fseek(binaryout, _SIZE, SEEK_CUR); \
226 struct bin_class_header_t class_header
;
227 struct bin_ht_entry_t ht_entry
;
228 long class_start
, classht_start
, classht_size
, rootsetht_start
, rootsetht_size
;
229 int num_csibs
, num_ssibs
;
232 class_start
= ftell(binaryout
);
233 class_name
= get_class_name(class);
235 num_csibs
= get_class_sibcount(class);
236 num_ssibs
= get_set_sibcount(get_class_root_set(class));
238 /* alloc space (before hash tables) for class header */
239 class_header
.namelen
= u8_strlen(class_name
);
240 fseek(binaryout
, class_start
+ sizeof(class_header
) + class_header
.namelen
,SEEK_SET
);
242 HT_INIT(classht_start
, classht_size
, num_csibs
);
243 HT_INIT(rootsetht_start
, rootsetht_size
, num_ssibs
);
245 /* TODO: Figure out generic way to output headers */
246 /* populate class header */
247 class_header
.child_ht
.entries
= num_csibs
;
248 class_header
.child_ht
.start
= classht_start
;
249 class_header
.rootset_ht
.entries
= num_ssibs
;
250 class_header
.rootset_ht
.start
= rootsetht_start
;
251 fseek(binaryout
, class_start
, SEEK_SET
); //seek back to where we allocated header
252 fwrite(&class_header
, sizeof(class_header
), 1, binaryout
);
253 fwrite(class_name
, class_header
.namelen
, 1, binaryout
);
255 /* Start populating root_set hash table */
256 for ( siter
= get_class_root_set(class); siter
!= NULL
; siter
= get_set_nextsib(siter
))
257 { fseek(binaryout
, 0, SEEK_END
);
258 ht_entry
.key
= NAMEHASH(get_set_name(siter
), num_ssibs
<< 1);
259 ht_entry
.value
= bin_traverse_set(siter
);
260 bin_insert_ht_entry(rootsetht_start
, rootsetht_size
, &ht_entry
, 0);
263 /* Start populating class child hash table */
264 for ( citer
= get_class_nextchild(class); citer
!= NULL
; citer
= get_class_nextsib(citer
))
265 { fseek(binaryout
, 0, SEEK_END
);
266 ht_entry
.key
= NAMEHASH(get_class_name(citer
), num_csibs
<< 1);
267 ht_entry
.value
= bin_traverse_class(citer
);
268 bin_insert_ht_entry(classht_start
, classht_size
, &ht_entry
, 0);
274 long bin_process_sdat( ir_set
);
277 |-----------------| |
279 |-----------------| |
280 ---| setchild ht |<--
281 | |-----------------|
282 |->| setchild header |
290 struct bin_set_header_t header
;
291 struct bin_ht_entry_t ht_entry
;
292 int num_child
, setname_len
;
293 long childht_start
, childht_size
, set_start
;
296 set_start
= ftell(binaryout
);
297 set_name
= get_set_name(set
);
299 /* alloc space for set header */
300 setname_len
= u8_strlen(set_name
);
301 fseek(binaryout
, sizeof(struct bin_set_header_t
) + setname_len
, SEEK_CUR
);
303 /* process the set data */
304 header
.sdat_start
= bin_process_sdat(set
);
306 /* Setup child hash table for current sets children */
307 num_child
= get_set_sibcount(get_set_nextchild(set
));
308 HT_INIT(childht_start
, childht_size
, num_child
);
310 /* populate header, write to file */
311 header
.child_ht
.entries
= num_child
;
312 header
.child_ht
.start
= childht_start
;
313 fseek(binaryout
, set_start
, SEEK_SET
);
314 fwrite(&header
, sizeof(struct bin_set_header_t
), 1, binaryout
);
315 fwrite(set_name
, setname_len
, 1, binaryout
);
317 for(iter
= get_set_nextchild(set
); iter
!= NULL
; iter
= get_set_nextsib(iter
))
318 { fseek(binaryout
, 0, SEEK_END
);
319 ht_entry
.key
= NAMEHASH(get_set_name(iter
), num_child
<< 1);
320 ht_entry
.value
= bin_traverse_set(iter
);
321 bin_insert_ht_entry(childht_start
, childht_size
, &ht_entry
, 0);
325 set_set_filepos(set
, set_start
);
333 | 1st variant data | -- variant == framebox
341 void bin_insert_links(int, struct bin_ht_header_t
*, long);
342 struct bin_pixel_node_t
* bin_find_default_pixel_list(ir_set
);
343 void bin_process_frameboxes(ir_set
, struct bin_ht_header_t
*, struct bin_pixel_node_t
*);
344 int bin_process_links(ir_set
, ir_setdata
);
346 /* Init the variant hash table for the set, process the sets links and add them to link_stack
347 and variant hash table, and then output the actual framedata */
351 { struct bin_setdata_header_t header
;
352 struct bin_attachment_list_t attachment_list
;
353 struct bin_pixel_node_t
*default_pixel_list
;
354 struct bin_ht_header_t ht_header
;
355 long varht_start
, varht_size
, sdat_start
;
356 int num_entries
, num_links
;
357 ir_setdata olink_head
;
359 sdat_start
= ftell(binaryout
);
361 /* Alloc position for sdat_header */
362 fseek(binaryout
, sizeof(struct bin_setdata_header_t
), SEEK_CUR
);
364 num_links
= bin_process_links(set
, olink_head
);
365 num_entries
= get_set_variants(set
) + num_links
;
367 HT_INIT(varht_start
, varht_size
, num_entries
);
369 /* Populate the sdat_header */
370 fseek(binaryout
, 0, sdat_start
);
371 header
.variant_ht
.start
= ht_header
.start
= varht_start
;
372 header
.variant_ht
.entries
= ht_header
.entries
= num_entries
;
373 attachment_list
.filepos
= header
.attach_pos
= ftell(binaryout
) + sizeof(varht_start
) + sizeof(num_entries
);
374 fwrite(&header
, sizeof(header
), 1, binaryout
);
375 fseek(binaryout
, 0, SEEK_END
);
377 /* insert the links that were processed into the variant hash table */
378 bin_insert_links(num_links
, &ht_header
, attachment_list
.filepos
);
379 /* Determine the default pixel list for all of the frameboxes */
380 default_pixel_list
= bin_find_default_pixel_list(set
);
381 /* Output each framebox, and insert it into the variant hash table */
382 bin_process_frameboxes(set
, &ht_header
, default_pixel_list
);
384 /* TODO: Convert the default pixel list to an attachment_list and then push the */
385 /* sdats attachment_list onto the attachment_stack so it can be procesed */
392 /* Adds a vlink onto the stack_alloc to be popped during the processing of the
393 sets variant hash table. If the vlink has a name, its a vlink to a single
394 variant. if the vlink doesnt have a name, its the vlink to an entire variant
395 hash table, and each variant (framebox) needs to be added */
401 { struct bin_plink_t
* plp
;
408 /* TODO: Macroize? or not worth? */
409 link_name
= get_link_name(vlink
);
412 { plp
= struct_alloc(bin_plink_t
);
413 plp
->src_pos
= 0; // TBD @ process_setdata
414 plp
->name
= link_name
;
415 plp
->trg_set
= trg_set
;
419 else // linking a variant hash table
420 for (fiter
= get_set_frameboxes(trg_set
); fiter
!= NULL
; fiter
= get_framebox_nextsib(fiter
))
421 { plp
= struct_alloc(bin_plink_t
);
422 plp
->src_pos
= 0; // TBD @ process_setdata
423 plp
->name
= get_framebox_name(fiter
);
424 plp
->trg_set
= trg_set
;
433 /* Adds an mlink to the stack_alloc, to be processed later */
439 { struct bin_plink_t
* plp
;
441 mlink_name
= get_link_name(mlink
);
442 plp
= struct_alloc(bin_plink_t
);
443 plp
->src_pos
= 0; //TBD after resolving the childlist | TODO: attach_pos?
444 if(mlink_name
) plp
->name
= mlink_name
;
445 plp
->trg_set
= trg_set
;
451 /* TODO: implement this */
452 /* Determine if olink is already part of the olink_list.
453 if it is, theres a cycle, return 1. Else return 0. */
457 ir_setdata olink_head
)
467 /* Given a set, determine the number of links it has and process each link and
468 then add them to stack_alloc, where they will be popped off and further processed. */
472 ir_setdata olink_head
475 ir_setdata liter
; //link iter
480 for(liter
= get_set_links(src_set
); liter
!= NULL
; liter
= get_link_nextsib(liter
))
481 { trg_set
= get_set_from_ref(get_link_ref(liter
));
482 switch (get_link_type(liter
)) {
484 if (olink_cycle(liter
, olink_head
)) //TODO: stack of olinks to iterate and check for cycles?
486 num_links
+= bin_process_vlink(liter
, trg_set
);
487 num_links
+= bin_process_mlink(liter
, trg_set
);
488 num_links
+= bin_process_links(trg_set
, liter
);
491 num_links
+= bin_process_vlink(liter
, trg_set
);
494 num_links
+= bin_process_mlink(liter
, trg_set
);
496 case ALINK
: //TODO: ?
503 /* Insert both mlinks and vlinks into the link stack, after determining their src_pos. Vlinks
504 have an additional requirement of being added into the variant hash table */
505 #define pop_linkp() (*(struct bin_plink_t**) pagelist_pop(&datapages, sizeof(struct bin_plink_t*)))
506 #define PUSH_PLINK(_LINK) (*(struct bin_plink_t**) stack_alloc(&linkpages, sizeof (_LINK)) = _LINK )
510 struct bin_ht_header_t
* ht
,
513 { struct bin_plink_t
* plp
;
514 struct bin_ht_entry_t ht_entry
;
517 /* Insert vlinks into hash table, put v/mlinks on link stack to be processed later */
518 for ( i
= 0; i
< num_links
; i
++)
522 plp
->trg_set
= plp
->trg_set
;
523 plp
->src_pos
= attach_pos
;
527 ht_entry
.key
= NAMEHASH(plp
->name
, ht
->entries
<< 1);
529 bin_insert_ht_entry(ht
->start
, ht
->entries
* sizeof(ht_entry
), &ht_entry
, 0);
530 plp
->src_pos
= ht_entry
.key
+ sizeof(ht_entry
.key
);
543 long bin_process_facing(ir_setdata
, apc_facing
, struct bin_pixel_node_t
*);
544 /* |-------------------|
546 |-------------------|
548 |-------------------|
549 | SWFACE framesheet |
550 |-------------------|
557 struct bin_pixel_node_t
* default_pixel_list
559 { struct bin_model_header_t header
;
560 long framebox_start
, index_pos
;
563 framebox_start
= ftell(binaryout
);
565 /* insert model header */
567 header
.facing_array_start
= framebox_start
+ sizeof(header
);
568 fwrite(&header
, sizeof(header
), 1, binaryout
);
570 /* Create the index array for framesheet of each direction */
571 for ( i
= SFACE
; i
< FACING_MAX
; i
++)
572 { fseek(binaryout
, 0, SEEK_END
);
573 index_pos
= bin_process_facing(framebox
, i
, default_pixel_list
); //TODO: finish process_direction
574 fseek(binaryout
, header
.facing_array_start
+ i
* sizeof(long), SEEK_SET
);
575 fwrite(&index_pos
, sizeof(long), 1, binaryout
);
578 return framebox_start
;
581 bin_process_frameboxes
583 struct bin_ht_header_t
* ht
,
584 struct bin_pixel_node_t
* default_pixel_list
586 { struct bin_ht_entry_t ht_entry
;
589 /* Insert variants into hash table to overwrite olink insertions*/
590 for ( fiter
= get_set_frameboxes(set
); fiter
!= NULL
; fiter
= get_framebox_nextsib(fiter
))
591 { fseek(binaryout
, 0, SEEK_END
);
592 ht_entry
.key
= NAMEHASH(get_framebox_name(fiter
), ht
->entries
<< 1);
593 ht_entry
.value
= bin_process_framebox(set
, fiter
, default_pixel_list
);
594 bin_insert_ht_entry(ht
->start
, ht
->entries
* sizeof(ht_entry
), &ht_entry
, 1);
598 /* Determine clipping based on image height/width and frame height/width */
600 void bin_set_img_info
601 ( struct bin_img_info_t
* img_info
,
604 { img_info
->fwidth
= get_framedata_width(framedata
);
605 img_info
->fheight
= get_framedata_height(framedata
);
606 img_info
->unaligned_height
= img_info
->height
% img_info
->fheight
;
607 img_info
->unaligned_width
= img_info
->width
% img_info
->fwidth
;
611 /* TODO: Implement this */
619 /* Combine the framesheet and mapsheet to create
621 /* |-------------------------|
622 | outputsheet header |
623 |-------------------------|
624 | pixel data for frame1 |
625 |-------------------------|
626 | op data for frame1 |
627 |-------------------------|
629 //TODO: processing direction sounds dumb, but cant call it process_framesheet because
630 // its actually the mapsheet and the framesheet. rename to output sheet?
631 /* THIS FUNCTION IS NOT DONE */
634 ( ir_setdata framebox
,
636 struct bin_pixel_node_t
* default_pixel_list
638 { struct bin_img_info_t mapsheet_info
, framesheet_info
;
639 int num_mapchannels
, num_framechannels
;
641 unsigned char* mapdata
, * framedata
;
643 facing_start
= ftell(binaryout
);
646 /* Set up data pointers to mapsheet and framesheet, as well as their image infos */
647 mapdata
= stbi_load(get_framedata_name(get_framebox_facing_mapdata(framebox
,SFACE
)), &mapsheet_info
.width
, &mapsheet_info
.width
, &num_framechannels
, 0);
648 framedata
= stbi_load(get_framedata_name(get_framebox_facing_framedata(framebox
,SFACE
)), &framesheet_info
.width
, &framesheet_info
.height
, &num_mapchannels
, 0);
649 bin_set_img_info(&framesheet_info
, get_framebox_facing_framedata(framebox
, SFACE
));
650 bin_set_img_info(&mapsheet_info
, get_framebox_facing_mapdata(framebox
, SFACE
));
652 /* TODO: output framesheet/direction header */
655 /* For each frame and map i in framesheet + mapsheet */
656 /* output frame data */
657 /* output op space for frames */
658 /* determine ops in map */
659 /* check if ops are acceptable */
671 /* Insert pixel(s) into the list, z sorted */
673 bin_insert_node_into_list
674 ( struct bin_pixel_node_t
* pixel_list_root
,
675 struct bin_pixel_node_t
* pixel_node
677 { struct bin_pixel_node_t
* head_node
, *prev_node
;
680 head_node
= pixel_list_root
->next
;
681 node_z
= pixel_node
->data
.z
;
683 if(head_node
== NULL
)
684 { head_node
= pixel_node
;
686 prev_node
= pixel_list_root
;
687 while(head_node
!= NULL
)
688 { if(node_z
> head_node
->data
.z
)
689 { prev_node
= head_node
;
690 head_node
= head_node
->next
;
692 else if (node_z
< head_node
->data
.z
|| node_z
== head_node
->data
.z
)
693 { prev_node
->next
= pixel_node
;
694 pixel_node
->next
= head_node
;
702 /* TODO: Finish this */
703 struct bin_pixel_node_t
*
705 ( unsigned char* data
,
711 { struct bin_pixel_node_t
* pixel_node
;
714 pixel_node
= struct_alloc(bin_pixel_node_t
);
717 { /* get ref from 4 bytes of data */
718 pixel_node
->data
.ref
= (int) data
;
719 /* bitshift by ? to get Z ordering */
722 pixel_node
->data
.x
= x
+ init_width
;
723 pixel_node
->data
.y
= y
+ init_width
;
727 { data
+= 4; //TODO: does this correctly increment past 0x00000000?
737 /* Returns the non null pixels of a single frame */
738 /* TODO: Finish this */
739 struct bin_pixel_node_t
*
740 bin_map_to_pixel_list
741 ( struct bin_img_info_t
* img_info
,
747 struct bin_pixel_node_t
* pixel_list
, * pixel_node
;
751 /* Check if frame clips */
752 if( img_info
->unaligned_width
)
754 if (img_info
->unaligned_height
)
758 for (y
= 0; y
< img_info
->fheight
; y
++)
759 { for ( x
= 0; x
< img_info
->fwidth
; x
++ )
760 { pixel_node
= bin_process_pixel(data
, x
, y
, init_height
, init_width
);
761 bin_insert_node_into_list(pixel_list
, pixel_node
);
769 int bin_pixel_list_len
770 ( struct bin_pixel_node_t
* pl
)
771 { struct bin_pixel_node_t
* plp
;
782 struct bin_pixel_node_t
*
783 bin_cmp_default_pixel_lists
784 ( struct bin_pixel_node_t
* pl1
,
785 struct bin_pixel_node_t
* pl2
787 { struct bin_pixel_node_t
* pl1p
, * pl2p
;
788 int i
, pl1_len
, pl2_len
;
792 pl1_len
= bin_pixel_list_len(pl1
);
793 pl2_len
= bin_pixel_list_len(pl2
);
795 if (pl1_len
> pl2_len
)
797 else if (pl1_len
< pl2_len
)
799 /* pl1 == pl2, make sure that all refs are the same */
800 /* TODO: what type of warning/error handling should occur here? */
801 for (i
= 0; i
< pl1_len
; i
++)
802 { if (pl1p
->data
.ref
!= pl2p
->data
.ref
)
803 eprintf("Error in determining default pixel list\n");
807 return pl1
; //doesnt matter which one you return
810 /* Find default framebox, based on the framebox with the most attachments*/
811 /* Search through first frame of S of each framebox */
812 struct bin_pixel_node_t
*
813 bin_find_default_pixel_list
816 struct bin_pixel_node_t
* default_pixel_list
, * curr_pixel_list
;
819 struct bin_img_info_t img_info
;
821 for (fiter
= get_set_frameboxes(set
); fiter
!= NULL
; fiter
= get_framebox_nextsib(fiter
))
822 { /* TODO: Stringify the frame name with .png? */
823 /* TODO: Add directory changing */
824 data
= stbi_load(get_framedata_name(get_framebox_facing_mapdata(fiter
, SFACE
)), &img_info
.width
, &img_info
.width
, &num_channels
, 0);
825 bin_set_img_info(&img_info
, get_framebox_facing_mapdata(fiter
, SFACE
));
826 curr_pixel_list
= bin_map_to_pixel_list(&img_info
, 0, 0, data
);
827 default_pixel_list
= bin_cmp_default_pixel_lists(curr_pixel_list
, default_pixel_list
);
832 return default_pixel_list
;