/*!@file \brief IR Memory Implementation \details Intermediary memory management \author Jordan Lavatai \date Aug 2016 ----------------------------------------------------------------------------*/ /* Standard */ #include //exit, malloc #include //print #include //va_args #include //uint64_t #include //memset, str* #include /* Unicode */ #include //u8_* functions #include //uint8_t as a char #include //u32_cpy #include //ulc_fprintf /* Local */ #include "print.h" #include "apc.h" #include "ir.h" #include "pagenode.h" #undef do_error #define do_error(...) exit(-1) #define XXH_PRIVATE_API #include "../xxHash/xxhash.h" /* Public */ int ir_init(void); void ir_quit(void); void ir_test(void); int ir_linker(void); int ir_condenser(void); /* Set data mem */ enum dtype { FSDAT, MSDAT, ADAT, LDAT, FBDAT }; struct ir_namelist_t; struct ir_namelist_t { struct ir_namelist_t* nextsib; uint8_t* name; }; struct ir_classld_t { struct ir_class_t* root_class; struct ir_namelist_t* namelist, * namelist_head; }; struct ir_setld_t { struct ir_classld_t* classld; long long ref; struct ir_namelist_t* namelist, * namelist_head; }; struct ir_setdata_header_t { enum dtype type; uint8_t* src_filename, * data_name; union ir_setdata_t* nextsib; }; struct ir_frameinfo_t { int facing, w, h; }; struct ir_framedata_t { struct ir_setdata_header_t header; struct ir_frameinfo_t frameinfo; }; struct ir_framebox_t { struct ir_setdata_header_t header; struct ir_framedata_t framesheets[FACING_MAX]; struct ir_framedata_t mapsheets[FACING_MAX]; }; struct ir_simplex_t { struct ir_setdata_header_t header; }; struct ir_link_t { struct ir_setdata_header_t header; struct ir_classld_t* classld; struct ir_setld_t* setld; enum ltype type; }; union ir_setdata_t { struct ir_setdata_header_t header; struct ir_framebox_t framebox; struct ir_framedata_t framesheet; struct ir_framedata_t mapsheet; struct ir_simplex_t audio; struct ir_link_t link; }; struct ir_class_t { struct ir_class_t* nextchild, * nextsib; struct ir_set_t* root_set; uint8_t* name; }; struct ir_set_t { struct ir_set_t* nextchild, * nextsib; uint32_t ref; uint8_t* name; struct ir_framebox_t* frameboxes; struct ir_simplex_t* audio; struct ir_link_t* links; long filepos; }; /* Functions */ static inline struct ir_framebox_t* ir_set_add_framebox(struct ir_set_t*,uint8_t*); static inline union ir_setdata_t* ir_framedata (enum dtype,const uint8_t*,apc_facing,int,int); static inline int bytes_identical(const uint8_t*,const uint8_t*); static inline int classnames_identical(const uint8_t*,const uint8_t*); static uint8_t* name_alloc(const uint8_t*); static uint8_t* classname_alloc(const uint8_t*); #define struct_clear(_S) (memset((_S), 0, sizeof(*(_S)))) #define REFHASH(ref) (XXH32(&ref, sizeof(uint32_t), 0xCEED) & 0xCFF) #define struct_alloc(_T) ((struct _T*) stack_alloc(&datapages, sizeof(struct _T))) extern //apc.c long sys_pagesize; static struct pagelist_t datapages, namepages, refhashpages; static struct ir_class_t root_class = { .name = (uint8_t*)"." }; /* Init */ int ir_init ( void ) { pagelist_init(datapages, (size_t)SYS_PAGESIZE); pagelist_init(namepages, (size_t)NAME_PAGESIZE); pagelist_init(refhashpages, (size_t)SYS_PAGESIZE); return 0; } /* Quit/Cleanup */ void ir_quit ( void ) { pagenode_free(datapages.root); pagenode_free(namepages.root); pagenode_free(refhashpages.root); } /* Link */ int ir_linker ( void ) { return 0; } /* Condense */ int ir_condenser ( void ) { return 0; } /* Return the class's name string */ uint8_t* ir_class_name ( struct ir_class_t* class ) { return class->name; } /* Return a pointer to the root class */ struct ir_class_t* ir_class_root ( void ) { return &root_class; } /* Add a subclass to a class Attempts to create a new subclass in the provided class, returning the class if it already exists */ struct ir_class_t* ir_class_addchild ( struct ir_class_t* class, const uint8_t* name ) { struct ir_class_t* iter; if (class->nextchild == NULL) { class->nextchild = struct_alloc(ir_class_t); struct_clear(class->nextchild); class->nextchild->name = classname_alloc(name); return class->nextchild; } iter = class->nextchild; if (iter->name == NULL) eprintf("Null name pointer in class %p\n", iter); if (name == NULL) eprintf("Null child added to class %s\n", iter->name); check: if (classnames_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_class_t); struct_clear(iter->nextsib); iter->nextsib->name = classname_alloc(name); return iter->nextsib; } /* Add a set to a class Attempts to create a new root set in the specified class, returning the set if it already exists */ struct ir_set_t* ir_class_addset ( struct ir_class_t* class, const uint8_t* name ) { struct ir_set_t* iter; if (class->root_set == NULL) { class->root_set = struct_alloc(ir_set_t); struct_clear(class->root_set); class->root_set->name = name_alloc(name); return class->root_set; } iter = class->root_set; if (iter->name == NULL) eprintf("Null name pointer in class %p\n", iter); if (name == NULL) eprintf("Null set added to class %U\n", iter->name); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_set_t); struct_clear(iter->nextsib); iter->nextsib->name = name_alloc(name); return iter->nextsib; } struct ir_set_t* ir_set_from_ref ( uint32_t ref ) { uint16_t hash; struct ir_set_t** iters; struct pagenode_t* iterp; iterp = refhashpages.root; hash = REFHASH(ref); do iters = ((struct ir_set_t**) iterp->root) + hash; while (*iters != NULL && (*iters)->ref != ref && (iterp = iterp->header.next) != NULL); return *iters; } /* Add a set to a set Attempts to create a new subset of the specified set, returning the child if it already exists */ struct ir_set_t* ir_set_addchild ( struct ir_set_t* set, const uint8_t* name ) { struct ir_set_t* iter; if (set->nextchild == NULL) { set->nextchild = struct_alloc(ir_set_t); struct_clear(set->nextchild); set->nextchild->name = name_alloc(name); return set->nextchild; } iter = set->nextchild; if (name == NULL) eprintf("Null child added to set %s\n", iter->name); if (iter->name == NULL) eprintf("Null name pointer in set %p\n", iter); check: if (bytes_identical(iter->name, name)) return iter; if (iter->nextsib != NULL) { iter = iter->nextsib; goto check; } iter->nextsib = struct_alloc(ir_set_t); struct_clear(iter->nextsib); iter->nextsib->name = name_alloc(name); return iter->nextsib; } /* Add a framebox to a set Attempts to create a new framebox of the specified set, returning the framebox if it already exists Name is not allocated, but assigned, unlike other "XXX_add" functions where name is duplicated into IR's internal array. */ static inline struct ir_framebox_t* ir_set_add_framebox ( struct ir_set_t* set, uint8_t* name ) { struct ir_framebox_t* iter; if (set->frameboxes == NULL) { set->frameboxes = struct_alloc(ir_framebox_t); struct_clear(set->frameboxes); set->frameboxes->header.data_name = name; return set->frameboxes; } iter = set->frameboxes; check: if (bytes_identical(iter->header.data_name, name)) return iter; if (iter->header.nextsib != NULL) { iter = (struct ir_framebox_t*) iter->header.nextsib; goto check; } iter->header.nextsib = (union ir_setdata_t*) struct_alloc(ir_framebox_t); struct_clear(iter->header.nextsib); iter->header.nextsib->header.data_name = name; return (struct ir_framebox_t*) (iter->header.nextsib); } /* Match two null-terminated bytestrings Return 1 if the two bytestrings are identical, else 0 */ static inline int bytes_identical ( const uint8_t* stra, const uint8_t* strb ) { int ca, cb; do { ca = *stra++; cb = *strb++; } while (ca && ca != '_' && ca == cb); return (ca == cb); } static inline int classnames_identical ( const uint8_t* stra, const uint8_t* strb ) { int ca, cb; do { ca = *stra++; cb = *strb++; } while (ca && ca == cb); return (ca == cb); } /* Assign Setdata to Set */ void ir_set_assign_data ( struct ir_set_t* set, union ir_setdata_t* setdata ) { struct ir_framebox_t* framebox; struct ir_simplex_t* simplex; switch (setdata->header.type) { case FSDAT: framebox = ir_set_add_framebox(set, setdata->header.data_name); if (framebox->framesheets[setdata->framesheet.frameinfo.facing].header.data_name != NULL) wprintf("Duplicate framesheet [%i] %s\n", setdata->framesheet.frameinfo.facing, setdata->header.data_name); framebox->framesheets[setdata->framesheet.frameinfo.facing] = setdata->framesheet; break; case MSDAT: framebox = ir_set_add_framebox(set, setdata->header.data_name); if (framebox->mapsheets[setdata->mapsheet.frameinfo.facing].header.data_name != NULL) wprintf("Duplicate mapsheet [%i] %s\n", setdata->mapsheet.frameinfo.facing, setdata->header.data_name); framebox->mapsheets[setdata->mapsheet.frameinfo.facing] = setdata->mapsheet; break; case ADAT: if (set->audio == NULL) { set->audio = (struct ir_simplex_t*) setdata; return; } simplex = set->audio; while (simplex->header.nextsib != NULL) if (bytes_identical(simplex->header.data_name, setdata->header.data_name)) { wprintf("Duplicate audio %s\n", setdata->header.data_name); *simplex = setdata->audio; //setdata is now a pointer to redundant, unused memory. return; } else simplex = (struct ir_simplex_t*) simplex->header.nextsib; setdata->audio.header.nextsib = (union ir_setdata_t*) set->audio; set->audio = (struct ir_simplex_t*) setdata; break; case LDAT: setdata->link.header.nextsib = (union ir_setdata_t*) set->links; set->links = (struct ir_link_t*) setdata; break; default: fprintf(stderr, "Unknown setdata type %x\n", setdata->header.type); exit(-1); } } void ir_set_assign_ref ( struct ir_set_t* set, uint32_t ref ) { uint16_t hash, oldhash; struct ir_set_t** iters; struct pagenode_t* iterp; uint32_t oldref; oldref = set->ref; oldhash = 0; hash = REFHASH(ref); iterp = refhashpages.root; check_depth: iters = ((struct ir_set_t**) iterp->root) + hash; if (*iters == NULL || *iters == set) *iters = set; else { if (iterp->header.next == NULL) pagelist_alloc(refhashpages); iterp = iterp->header.next; goto check_depth; } if (oldref != 0) { wprintf("Ref override: 0x%x -> 0x%x for set %s\n", oldref, ref, set->name); if (oldhash != 0) *iters = NULL; else { oldhash = hash; hash = REFHASH(oldref); goto check_depth; } } set->ref = ref; } void ir_data_assign_path ( union ir_setdata_t* setdata, const uint8_t* path ) { if (path == NULL) eprintf("Null path in data %s\n", setdata->header.data_name); if (setdata->header.src_filename != NULL) wprintf("Path override: %s -> %s for setdata %s\n", setdata->header.src_filename, path, setdata->header.data_name); setdata->header.src_filename = name_alloc(path); } union ir_setdata_t* ir_framesheet ( const uint8_t* name, apc_facing d, int width, int height ) { return ir_framedata(FSDAT, name, d, width, height); } union ir_setdata_t* ir_mapsheet ( const uint8_t* name, apc_facing d, int width, int height ) { return ir_framedata(MSDAT, name, d, width, height); } static inline union ir_setdata_t* ir_framedata ( enum dtype type, const uint8_t* name, apc_facing d, int width, int height ) { struct ir_framedata_t* framedata = struct_alloc(ir_framedata_t); struct_clear(framedata); if (name == NULL) eprintf("Null name in set allocation\n"); framedata->header.type = type; framedata->header.data_name = name_alloc(name); framedata->frameinfo.facing = d; framedata->frameinfo.w = width; framedata->frameinfo.h = height; return (union ir_setdata_t*) framedata; } union ir_setdata_t* ir_audio ( const uint8_t* name ) { struct ir_simplex_t* audio = struct_alloc(ir_simplex_t); struct_clear(audio); if (name == NULL) eprintf("Null audio\n"); audio->header.type = ADAT; audio->header.data_name = name_alloc(name); return (union ir_setdata_t*) audio; } /* Create classld that points to a class */ struct ir_classld_t* ir_classld_from_class ( struct ir_class_t* class ) { struct ir_classld_t* classld; if (class == NULL) eprintf("Null class in classld\n"); classld = struct_alloc(ir_classld_t); struct_clear(classld); classld->root_class = class; return classld; } struct ir_setld_t* ir_setld_from_ref ( uint32_t ref ) { struct ir_setld_t* setld; setld = struct_alloc(ir_setld_t); struct_clear(setld); setld->ref = ref; return setld; } struct ir_setld_t* ir_setld_from_classld ( struct ir_classld_t* classld, const uint8_t* name ) { struct ir_setld_t* setld; setld = struct_alloc(ir_setld_t); struct_clear(setld); setld->namelist = struct_alloc(ir_namelist_t); struct_clear(setld->namelist); setld->namelist_head = setld->namelist; setld->namelist_head->name = name_alloc(name); setld->classld = classld; return setld; } struct ir_setld_t* ir_setld_addchild ( struct ir_setld_t* setld, const uint8_t* name ) { if (setld->namelist == NULL) { setld->namelist = struct_alloc(ir_namelist_t); struct_clear(setld->namelist); setld->namelist_head = setld->namelist; } else { setld->namelist_head->nextsib = struct_alloc(ir_namelist_t); struct_clear(setld->namelist_head->nextsib); setld->namelist_head = setld->namelist_head->nextsib; } setld->namelist_head->name = name_alloc(name); return setld; } union ir_setdata_t* ir_link ( enum ltype link_type, struct ir_setld_t* setld, const uint8_t* name ) { struct ir_link_t* link; link = struct_alloc(ir_link_t); struct_clear(link); link->header.type = LDAT; link->type = link_type; link->classld = setld->classld; link->setld = setld; if (link_type != OLINK && name != NULL) link->header.data_name = name_alloc(name); return (union ir_setdata_t*) link; } static uint8_t* name_alloc ( const uint8_t* name_src ) { const uint8_t* iter; uint8_t* name; int head_mem; copy: name = (uint8_t*)namepages.head->header.head; iter = name_src; for (head_mem = PL_HEADMEM(namepages); *iter && *iter != '_' && *iter != '.' && head_mem; head_mem--) *(namepages.head->header.head)++ = *iter++; if (head_mem < 1) //not enough room { pagelist_alloc(namepages); goto copy; } *(namepages.head->header.head)++ = '\0'; return name; } static uint8_t* classname_alloc ( const uint8_t* name_src ) { const uint8_t* iter; uint8_t* name; int head_mem; copy: name = (uint8_t*)namepages.head->header.head; iter = name_src; for (head_mem = PL_HEADMEM(namepages); *iter && head_mem; head_mem--) *(namepages.head->header.head)++ = *iter++; if (head_mem < 1) //not enough room { pagelist_alloc(namepages); goto copy; } *(namepages.head->header.head)++ = '\0'; return name; } static void crawl_class(struct ir_class_t*); static void crawl_set(struct ir_set_t*,int); void ir_test(void) { uprintf("IR From Directory: %s\n",getcwd(NULL,255)); crawl_class(&root_class); if (root_class.root_set != NULL) crawl_set(root_class.root_set, 0); uprintf("starting binaryout \n"); ir_binout_init(&root_class); } static void crawl_class ( struct ir_class_t* class ) { struct ir_class_t* iter; for (iter = class->nextchild; iter != NULL; iter = iter->nextsib) { wprintf("Crawling class %U/\n", iter->name); if(chdir((char*)iter->name)) eprintf("CHDIR %U from %s\n",iter->name,getcwd(NULL,255)); crawl_class(iter); if (iter->root_set != NULL) crawl_set(iter->root_set, 0); uprintf("%U\\\n",iter->name); if (chdir("..")) eprintf("CHDIR ..\n"); wprintf("Finished crawling class %U/\n", iter->name); } } #define push_setp(setp) (*(struct ir_set_t**)stack_alloc(&datapages, sizeof(struct ir_set_t*)) = setp) #define pop_setp() (*(struct ir_set_t**)pagelist_pop(&datapages, sizeof(struct ir_set_t*))) static void crawl_set ( struct ir_set_t* set, int depth ) { struct ir_set_t* iter; int i; i = depth * 12; while (i--) putchar('.'); i = depth; for(iter = set; iter != NULL; iter = iter->nextchild) { uprintf("[%10U]", iter->name); push_setp(iter); i++; } putchar('\n'); while (--i >= depth) if (((iter = pop_setp())->nextsib) != NULL) crawl_set(iter->nextsib,i); }